16

Situation : I am writing a chrome extension that works on any page.

Problem Question : I can not load jQuery into Facebook and I would like to understand what is happening.

Hypotheses : Facebook possess some ultra advanced tech that somehow detects both :

  1. When jQuery is loaded via a chrome extension in an ostensibly separate JSVM execution context, the Facebook megamind somehow knows about this ostensibly separate JSVM execution context, and blocks it.
  2. that jQuery is loaded via script.src and blocks it (when I used the Google CDN which serves over HTTPS instead of the jQuery one which doesn't method 2 works, but is not sufficient for answer).

DATA

How do I know jQuery is not loading?

I j to bring up the console in Chrome. When I do :

    > jQuery
    >> ReferenceError : jQuery is not defined.
    > $('body')
    >> Error : Tried to get element "body" but it is not present on the page.

How do I attempt to load jQuery in facebook?

Method 1 (required but fails):

Via the following code in the manifest.json file :

"content_scripts"         :   [
                                  {
                                    "matches"   : ["<all_urls>"],
                                    "js"        : [ 
                                                    "javascript/jq/jquery-1.9.1.min.js",                                            
                                                    "javascript/jq/non-standard.js"
                                                  ],
                                    "all_frames": true // (or false, same failure)
                                  } 
                              ]

Method 2 (works, but insufficent):

Via the method described in this SO answer (load jQuery into console), modified to permit the correct protocol :

    var jq = document.createElement('script');
    jq.src = "//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js";
    document.getElementsByTagName('head')[0].appendChild(jq);
    jQuery.noConflict();

Summary

Hypothesis 1 seems very unlikely, because over-riding the separate execution contexts of a web browser would be a major security vulnerability (break that sandbox), and not likely to be sanctioned. Therefore, I am probably being paranoid and overlooking the obvious, which hopefully one of you will see.

Appendix (other relevant code)

All of non-standard.js :

    $.fn.in_groups_of = function( countPerGroup ) {
        var groups = [], offset = 0, $group;
        while ( ($group = this.slice( offset, (countPerGroup + offset) )).length ) {
            groups.push( $group );
            offset += countPerGroup;
        }
        return groups;
    };

More of manifest.json :

"manifest_version"        :   2,
"permissions"             :   [
                                  "http://*/",
                                  "https://*/",
                                  "tabs",
                                  "storage",
                                  "unlimitedStorage"
                              ],
7
  • So your extension is working on any other site than Facebook? Commented Mar 4, 2013 at 4:58
  • 1
    @SimonBoudrias Great question. Actually no. I only checked on Stackoverflow (but it seems they load their own jQuery, so I got a false positive). My extension does not work on Google, either. Good point! Perhaps I need to change the title of question, though that could be too distracting now. Commented Mar 4, 2013 at 5:01
  • 3
    That little $ function they implemented is funny, it just pretends like it does something but really its just a deceiving error message. Presumably they are trying to horde of people trying to dig into facebook's guts.
    – brysgo
    Commented Mar 4, 2013 at 5:04
  • 1
    Ok so, this has nothing to do with Facebook megamind or anything. It's only that you're not loading your scripts correctly. Also, make sure to prevent conflict with other jquery instances by calling noConflict. BTW I'm not sure how content scripts work, but it may be worth trying using only one concatenated script. Commented Mar 4, 2013 at 5:06
  • 1
    @SimonBoudrias do you think it could be that the execution context for the console is different to the execution context for the content script injected? Commented Mar 4, 2013 at 5:07

3 Answers 3

46

The Chrome console does not appear to have access to the content script's execution context.

Wrong, it does. You need to look at the correct place:

Animation of how-to get access to the execution environment of the Chromne extension

The previous screencast shows that the Console tab of the Chrome developer tools has two dropdown boxes at the bottom, which can be used to change the execution environment for the developer tools' console.
The left side can be used to change the frame context (top frame, so iframe, ...), and the right side can be used to change the script context (page, content script, ...).

3
  • 4
    That is the most awesome answer I have ever seen. It needs to be upvoted 10,000 times for the on-topic animated gif that completely answers the question and taught me like 5 new things. How did you do that gif? Commented Mar 4, 2013 at 12:31
  • 4
    @CrisStringfellow I'm using Byzanz. See this answer on Ask Ubuntu for my shell script which launches the tool. The generated GIFs have a reasonable quality and size. For instance, have a look at this animation. It has a duration of 40 seconds and it's only 3.7Mb.
    – Rob W
    Commented Mar 4, 2013 at 13:50
  • In 2019, the DevTools Console options bar is different. There's now just a single consolidated "JavaScript contexts" dropdown list with nested entries, and it lists extensions not by "chrome-extension://[ID]" but by extension name.
    – Jacob C.
    Commented Feb 15, 2019 at 22:15
2

The Answer

It seems my 'experimental method' was flawed. The assumption about the Chrome console's omniscience is incorrect. The Chrome console does not appear to have access to the content script's execution context. So although console was reporting that jQuery did not have access to the page, it actually did, from the content script's execution context.

This was verified by adding a content script, test.js, to manifest.json :

"content_scripts"         :   [
                                  {
                                    "matches"   : ["<all_urls>"],
                                    "js"        : [
                                                    "javascript/jq/jquery-1.9.1.min.js",
                                                    "javascript/jq/non-standard.js",
                                                    "javascript/test.js" // <-- add
                                                  ],

The content of test.js is :

    var jtest = $('body');
    alert(jtest);
    alert(jtest.text());

Now whatever page I navigate to, the two alert boxes pop up as expected.

It works!

0

You may know all of these by now, but I think someone still finds these useful.

In a Chrome extension,

You have some "worlds of scripts":

  • Original page scripts: the scripts on the page itself.
  • Content scripts: you write those script, and they run on the page
  • Popup scripts: they run on the popup, if you have a popup page.
  • Background scripts: run on the global background page.

Google does a excellent job on documentation, so tons of document about all of those scripts https://developer.chrome.com/extensions.

But in your case, just note that: page scripts and content script live in separate worlds, they do share the DOM and some Chrome native objects, but they don't share variables (or objects) they create.

For this case, if you have jQuery in your content scripts, you will have $ and jQuery ready to use in your content scripts. You can use it to query the DOM (although some jQuery events may not work as expected). But on the page, you will not have $ and jQuery, (you might have $, but it is not jQuery ^^).

Your method 2 above, it actually injects jQuery into the page, that jQuery will become page script. And you cannot use $ or jQuery in your content scripts.

If you use both method at once, you can have jQuery 1 in your page scripts, and jQuery 2 in your content script, and they are 2 different jQuery instances. It might cause confusion, but I do it all the times.

Not the answer you're looking for? Browse other questions tagged or ask your own question.