Talk:Location hack: Difference between revisions

From GreaseSpot Wiki
Jump to navigationJump to search
Cacycle (talk | contribs)
polling
m Reverted edits by 46.17.96.204 (talk) to last revision by Arantius
 
(22 intermediate revisions by 10 users not shown)
Line 21: Line 21:
[[User:Arantius|Arantius]] has removed the following passage that I had added:
[[User:Arantius|Arantius]] has removed the following passage that I had added:


: Whoops, true that I missed adding a comment there.  But it was because I created [[Reading Content Globals]] to contain this topic (and added it to the "see also" section).  It works consistently, and well, and does not involve polling.  [[User:Arantius|Arantius]] 13:17, 27 September 2010 (UTC)
: It is possible that the location.href code is excuted (much) later than the readout code in the following lines. This breaks the location hack. (Observed in Firefox 3.6.8)
: It is possible that the location.href code is excuted (much) later than the readout code in the following lines. This breaks the location hack. (Observed in Firefox 3.6.8)


This happened with Firefox 3.6.8 and broke the Wikipedia editor [http://en.wikipedia.org/wiki/User:Cacycle/wikEd wikEd]. The only work around was to poll the location hack element after having set location.href. Independent of the poll delay time the results arrives usually with the 1st or 2nd poll. I have to apply the location hack during the page loading period before the load event fires. Event triggering instead of polling was not possible because during the page loading none of the tested events fired (e.g. click) (Arantius also reverted that remark [http://wiki.greasespot.net/index.php?title=Generate_Click_Events&curid=1943&diff=5592&oldid=5591]). Please see the current working code below. [[User:Cacycle|Cacycle]] 22:54, 3 September 2010 (UTC)
This happened with Firefox 3.6.8 and broke the Wikipedia editor [http://en.wikipedia.org/wiki/User:Cacycle/wikEd wikEd]. The only work around was to poll the location hack element after having set location.href. Independent of the poll delay time the results arrives usually with the 1st or 2nd poll. I have to apply the location hack during the page loading period before the load event fires. Event triggering instead of polling was not possible because during the page loading none of the tested events fired (e.g. click) (Arantius also reverted that remark [http://wiki.greasespot.net/index.php?title=Generate_Click_Events&curid=1943&diff=5592&oldid=5591]). Please see the current working code and testcase below. [[User:Cacycle|Cacycle]] 00:53, 5 September 2010 (UTC)


<pre>
<pre>// ==UserScript==
//
// @name        location hack polling
// WikEdGetGlobal: parse global variables into hash, uses Greasemonkey 'location hack' (code copied to wikEdDiff.js)
// @include    *wiki*
//
// ==/UserScript==


window.WikEdGetGlobal = function(globalNameArray) {
window.Polling = function() {
  if (globalNode.value != '') {
    GM_log(globalNode.value);
  }
  else {
    GM_log('polling');
    setTimeout(Polling, 10);
  }
}


// work around sandboxing using Greasemonkey location hack
window.globalNode = document.createElement('textarea');
    if ( (wikEdGreasemonkey == true) && (typeof(JSON) == 'object') ) {
globalNode.id = 'globalNode'
        var scheduled = 0;
document.body.appendChild(globalNode);
        for (var i = 0; i < globalNameArray.length; i ++) {
location.href = 'javascript:document.getElementById(\'globalNode\').value = skin; void(0);';
            var globalName = globalNameArray[i];
Polling();
 
</pre>
// create single use textarea for text exchange
            var globalNode = document.createElement('textarea');
            globalNode.id = 'wikEdGetGlobalNode' + wikEdGetGlobalNodeId;
            globalNode.style.display = 'none';
            wikEdGetGlobalNodeRefs[wikEdGetGlobalNodeId] = globalNode;
            document.body.appendChild(globalNode);
 
// set location href to execute code in global context
            location.href = 'javascript:'
            + 'var node = document.getElementById(\'wikEdGetGlobalNode' + wikEdGetGlobalNodeId + '\');'
            + 'if (typeof(' + globalName + ') != \'undefined\') {'
            + '  node.value = JSON.stringify( { \'' + globalName + '\': ' + globalName + ' } );'
            + '}'
            + 'else {'
            + '  node.value = \'\\n\''
            + '}'
            + 'void(0);';
            scheduled ++;
            wikEdGetGlobalNodeId ++;
        }


// start polling for the arrival of asynchronous results
== Alternate solution sketch for reading page scope variables ==
        if (scheduled > 0) {
            WikEdGetGlobalPolling();
        }
    }
    return;
};


While I don't know why Arantius removed the location hack helper (even the tersest change message would be useful), I can guess that it wasn't a dependable solution.


//
I think some asynchronous solution might be more backwards-and-forwards compatible and dependable, even though the API is less friendly to javascript beginners, and the code size explodes. Maybe something like this (untested):
// WikEdGetGlobalPolling: poll location hack DOM elements for asynchronously arriving content
//  in Firefox 3.6.8 usually the 1st or 2nd poll independently of delay time


window.WikEdGetGlobalPolling = function() {
// Query page javascript for the identifier "name", and call callback(value),
    var scheduled = 0;
// when found, or undefined, if not found or some error occurred. This works
    for (var i = 0; i < wikEdGetGlobalNodeId; i ++) {
// only for values that can be JSON serialized -- numbers, strings, booleans,
      var node = wikEdGetGlobalNodeRefs[i];
// null, or nested structures like Arrays and Objects that only contain above
      if (node != null) {
// mentioned types of data.
          scheduled ++;
function queryContentVar(name, callback) {
          var nodeValue = node.value;
  // makes a random 20-char lowercase id
          if (nodeValue != '') {
  function random() {
              if (nodeValue != '\n') {
    var rand = '';
                    var obj = JSON.parse(nodeValue);
    while (rand.length < 20)
                    for (var key in obj) {
      rand += String.fromCharCode(97 + Math.random() * 26 | 0);
                        if (obj.hasOwnProperty(key) == true) {
    return rand;
                          wikEdWikiGlobals[key] = obj[key];
  }
                        }
                    }
  // Creates a mostly anonymous <meta> tag in the <head> section, giving it a
                }
  // secret id we'll use for messaging back and forth with content space, and
                document.body.removeChild(node);
  // fires pageInit() in the page scope (to listen and respond to queries from
                wikEdGetGlobalNodeRefs[i] = undefined;
  // us about variables) and registers reply() for answers from it at this end.
                scheduled --;
  function privInit() {
            }
    var node = document.createElement('meta'), secret,
        }
        head = document.getElementsByTagName('head')[0];
    }
    node.id = secret = random();
    node.addEventListener(id, reply, false);
    head.appendChild(node);
    location.href = 'javascript:('+ pageInit +')('+ JSON.stringify(secret) +')';
    return node;
  }
  // Grabs the meta tag, anonymizes it and listens for incoming queries from us.
  function pageInit(secret) {
    // Replaces given <meta content="variable.name"> with <meta content="value">
    // (after JSON encoding it) and then alerts the caller to grab the response.
    function reply() {
      var name = node.getAttribute('content'),
          mesg = document.createEvent('Events'),
          val;
      try {
        val = eval(node.getAttribute('content'));
        val = JSON.stringify(val);
      } catch(e) { val = ''; }
      node.setAttribute('content', val);
      mesg.initEvent(secret, true, false);
      node.dispatchEvent(mesg);
    }
    var node = document.getElementById(secret);
    node.addEventListener(secret + '?', reply, false);
    node.removeAttribute('id');
  }
  var self = queryContentVar,
      node = self.node = self.node || privInit(),
        id = self.secret = self.secret || node.id,
      mesg = document.createEvent('Events');
  mesg.initEvent(secret + '?', true, false);
  node.setAttribute('content', name);
  self.callback = callback;
  node.dispatchEvent(mesg);
  // Picks up the response from content space, decodes it, passes it on to given
  // response callback and resets everything (just in case) to handle new calls.
  function reply() {
    var cb = self.callback,
        is = node.getAttribute('content');
    if (cb) cb(is === '' ? undefined : JSON.parse(is));
    node.removeAttribute('name');
    delete self.callback;
  }
}


// no longer waiting for nodes
    if (scheduled == 0) {
        wikEdGetGlobalNodeId = 0;
    }


// schedule next poll (ms)
--[[User:Ecmanaut|Ecmanaut]] 23:58, 26 September 2010 (UTC)
    else {
        window.setTimeout(WikEdGetGlobalPolling, 10);
    }
    return;
}
</pre>

Latest revision as of 13:22, 22 March 2012

GM_eval vs. eval(s, unsafeWindow) vs. LocationHack

GM_eval() will not be implemented.

  1. The second argument of eval was removed.
  2. Running eval() in chrome scope is a very dangerous thing to do.
  3. I'm not confident that it actually worked to solve the problems that the location hack does. (I'm confident that it does not, on FF 3.0.13 on Linux. Perhaps it did in older versions, before it was removed.)

Location Hack Broken

-Removed-

Fromp: You are confused about what the location hack is supposed to accomplish. It's for letting a user script call into a function (for example) defined in the page. Your example which I just removed was doing the opposite. As written that example should never have worked. Arantius 12:41, 16 September 2009 (EDT)

Polling

Arantius has removed the following passage that I had added:

Whoops, true that I missed adding a comment there. But it was because I created Reading Content Globals to contain this topic (and added it to the "see also" section). It works consistently, and well, and does not involve polling. Arantius 13:17, 27 September 2010 (UTC)
It is possible that the location.href code is excuted (much) later than the readout code in the following lines. This breaks the location hack. (Observed in Firefox 3.6.8)

This happened with Firefox 3.6.8 and broke the Wikipedia editor wikEd. The only work around was to poll the location hack element after having set location.href. Independent of the poll delay time the results arrives usually with the 1st or 2nd poll. I have to apply the location hack during the page loading period before the load event fires. Event triggering instead of polling was not possible because during the page loading none of the tested events fired (e.g. click) (Arantius also reverted that remark [1]). Please see the current working code and testcase below. Cacycle 00:53, 5 September 2010 (UTC)

// ==UserScript==
// @name        location hack polling
// @include     *wiki*
// ==/UserScript==

window.Polling = function() {
  if (globalNode.value != '') {
    GM_log(globalNode.value);
  }
  else {
    GM_log('polling');
    setTimeout(Polling, 10);
  }
}

window.globalNode = document.createElement('textarea');
globalNode.id = 'globalNode'
document.body.appendChild(globalNode);
location.href = 'javascript:document.getElementById(\'globalNode\').value = skin; void(0);';
Polling();

Alternate solution sketch for reading page scope variables

While I don't know why Arantius removed the location hack helper (even the tersest change message would be useful), I can guess that it wasn't a dependable solution.

I think some asynchronous solution might be more backwards-and-forwards compatible and dependable, even though the API is less friendly to javascript beginners, and the code size explodes. Maybe something like this (untested):

// Query page javascript for the identifier "name", and call callback(value),
// when found, or undefined, if not found or some error occurred. This works
// only for values that can be JSON serialized -- numbers, strings, booleans,
// null, or nested structures like Arrays and Objects that only contain above
// mentioned types of data.
function queryContentVar(name, callback) {
  // makes a random 20-char lowercase id
  function random() {
    var rand = ;
    while (rand.length < 20)
      rand += String.fromCharCode(97 + Math.random() * 26 | 0);
    return rand;
  }

  // Creates a mostly anonymous <meta> tag in the <head> section, giving it a
  // secret id we'll use for messaging back and forth with content space, and
  // fires pageInit() in the page scope (to listen and respond to queries from
  // us about variables) and registers reply() for answers from it at this end.
  function privInit() {
    var node = document.createElement('meta'), secret,
        head = document.getElementsByTagName('head')[0];
    node.id = secret = random();
    node.addEventListener(id, reply, false);
    head.appendChild(node);
    location.href = 'javascript:('+ pageInit +')('+ JSON.stringify(secret) +')';
    return node;
  }

  // Grabs the meta tag, anonymizes it and listens for incoming queries from us.
  function pageInit(secret) {
    // Replaces given <meta content="variable.name"> with <meta content="value">
    // (after JSON encoding it) and then alerts the caller to grab the response.
    function reply() {
      var name = node.getAttribute('content'),
          mesg = document.createEvent('Events'),
          val;
      try {
        val = eval(node.getAttribute('content'));
        val = JSON.stringify(val);
      } catch(e) { val = ; }

      node.setAttribute('content', val);
      mesg.initEvent(secret, true, false);
      node.dispatchEvent(mesg);
    }

    var node = document.getElementById(secret);
    node.addEventListener(secret + '?', reply, false);
    node.removeAttribute('id');
  }

  var self = queryContentVar,
      node = self.node = self.node || privInit(),
        id = self.secret = self.secret || node.id,
      mesg = document.createEvent('Events');

  mesg.initEvent(secret + '?', true, false);
  node.setAttribute('content', name);
  self.callback = callback;
  node.dispatchEvent(mesg);

  // Picks up the response from content space, decodes it, passes it on to given
  // response callback and resets everything (just in case) to handle new calls.
  function reply() {
    var cb = self.callback,
        is = node.getAttribute('content');
    if (cb) cb(is ===  ? undefined : JSON.parse(is));
    node.removeAttribute('name');
    delete self.callback;
  }
}


--Ecmanaut 23:58, 26 September 2010 (UTC)