Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switching off `@grant none` breaks my Greasemonkey script?

I'm running this script on the Facebook homepage. It gets all the conversations in the dock in the bottom and gets their __FB_TOKENs.

// ==UserScript==
// @name MyScript
// @namespace MyNameSpance
// @include /https?://(www.)?facebook.com(/.*)?/
// @require http://code.jquery.com/jquery-2.1.0.min.js
// @version 0.0.0
// @grant none
// ==/UserScript==
(function () {
  // Don't run on frames or iframes
  if (window.top != window.self) {
    return ;
  }

  window.addEventListener('load', function () {

    var element = $('.fbNubGroup.clearfix.videoCallEnabled');
    console.log('SCRIPT ELEMENT: ', element); // is displayed on the console

    var children = element.children();
    console.log('SCRIPT CHILDREN: ', children); // is displayed on the console

    for (var i = 0; i < children.length; i++) {
      var child = $(children[i]);
      console.log('SCRIPT CHILD: ', child); // is displayed on the console

      /*
      child :
        Object [div.someClasses]
          + 0: div.someClasses
            + __FB_TOKEN: [ 267 ]
      */

      console.log('SCRIPT CHILD[0]: ', child[0]); // is displayed on the console
      console.log('SCRIPT CHILD[0].__FB_TOKEN:', child[0].__FB_TOKEN); // UNDEFINED

      var key = child[0].__FB_TOKEN[0];
      console.log('SCRIPT KEY: ', key); // not displayed
    }
  }, false);
}) ();


With @grant none, it works as expected and I get:

grant_none


However, if I change @grant none to @grant GM_xmlhttpRequest, the script does not work anymore. It gives:

grant GM_xmlhttpRequest

and throws an exception on the child[0].__FB_TOKEN line.


I don't understand why, because the CHILD[0] did not change:

child

Why is changing the @grant none breaking the script?

like image 239
AlixB Avatar asked Feb 13 '23 07:02

AlixB


1 Answers

When you switch from @grant none to @grant GM_xmlhttpRequest, Greasemonkey switches the sandbox back on, as a side-effect.
(Frankly, all GM developers and scripts should always run with the sandbox on anyway -- with extremely rare exceptions. This avoids "Time bomb" coding problems and head-scratchers like this question.)

JavaScript objects, arrays, variables, and functions cannot cross the sandbox. But unsafeWindow is provided to the Greasemonkey scope as a limited workaround.

Also important for this question:

  1. Greasemonkey (and Firefox) just shut down some of the previously possible solutions using unsafeWindow.
  2. jQuery is a special case and is extra difficult to use to bring non-DOM objects across the sandbox.

Solution:

  1. Since __FB_TOKEN is an array, you must use unsafeWindow to get it across the sandbox.
  2. Trying to use jQuery in this case gives an unacceptably high headache-to-results ratio; so use plain DOM methods.

Putting it all together the script becomes:

// ==UserScript==
// @name        _MyScript
// @namespace   MyNameSpace
// @include     /https?://(www.)?facebook.com(/.*)?/
// @require     http://code.jquery.com/jquery-2.1.0.min.js
// @version     0.0.0
// @grant       GM_xmlhttpRequest
// ==/UserScript==
(function () {
    // Don't run on frames or iframes
    if (window.top != window.self) {
        return;
    }
    window.addEventListener ('load', function () {
        /*--- This unsafeWindow is key. After that, don't use any jQuery
            while trying to get at javascript objects like __FB_TOKEN.
        */
        var element = unsafeWindow.document.querySelector (
            '.fbNubGroup.clearfix.videoCallEnabled'
        );
        /*-- Used console.info to make it easier to spot messages buried
            in all the Facebook console crud.
        */
        console.info ('SCRIPT ELEMENT: ', element);

        var children = element.children;
        console.info ('SCRIPT CHILDREN: ', children);

        for (var i = 0; i < children.length; i++) {
            var child = children[i];
            console.info ('SCRIPT CHILD: ', child);

            /*
            child :
              Object [div.someClasses]
                + 0: div.someClasses
                  + __FB_TOKEN: [ 267 ]
            */
            console.info ('SCRIPT CHILD: ', child);
            console.info ('SCRIPT CHILD.__FB_TOKEN:', child.__FB_TOKEN);

            var key = child.__FB_TOKEN[0];
            console.info ('SCRIPT KEY: ', key);
        }
    }, false);
} ) ();
like image 54
Brock Adams Avatar answered Feb 14 '23 22:02

Brock Adams