[Pkg-mozext-commits] [greasemonkey] 11/43: Sloppily migrate document-element-inserted observer into a JSM.

David Prévot taffit at moszumanska.debian.org
Sun Feb 22 21:56:10 UTC 2015


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository greasemonkey.

commit f82a7e813a3280f89cba16c60ccb340b688b468a
Author: Anthony Lieuallen <arantius at gmail.com>
Date:   Fri Oct 31 15:29:34 2014 -0400

    Sloppily migrate document-element-inserted observer into a JSM.
    
    As suggested: http://bugzil.la/1048164#c27
---
 content/framescript.js     | 196 ++++----------------------------------
 modules/contentObserver.js | 230 +++++++++++++++++++++++++++++++++++++++++++++
 modules/sandbox.js         |  35 ++++---
 3 files changed, 263 insertions(+), 198 deletions(-)

diff --git a/content/framescript.js b/content/framescript.js
index a1c0678..fa9ada0 100644
--- a/content/framescript.js
+++ b/content/framescript.js
@@ -5,17 +5,16 @@ var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 
-Cu.import("resource://gre/modules/Services.jsm");
 
-Cu.import('resource://greasemonkey/GM_setClipboard.js');
-Cu.import("resource://greasemonkey/ipcscript.js");
-Cu.import("resource://greasemonkey/miscapis.js");
-Cu.import("resource://greasemonkey/sandbox.js");
-Cu.import('resource://greasemonkey/util.js');
+//var gScriptRunners = {};
 
+// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ //
 
-var gStripUserPassRegexp = new RegExp('(://)([^:/]+)(:[^@/]+)?@');
-var gScriptRunners = {};
+// For every frame/process, make sure the content observer is running.
+
+Cu.import('resource://greasemonkey/contentObserver.js');
+
+// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ //
 
 function ScriptRunner(aWindow, aUrl) {
   this.menuCommands = [];
@@ -24,31 +23,6 @@ function ScriptRunner(aWindow, aUrl) {
   this.url = aUrl;
 }
 
-ScriptRunner.prototype.injectScripts = function(aScripts) {
-  try {
-    this.window.QueryInterface(Ci.nsIDOMChromeWindow);
-    // Never ever inject scripts into a chrome context window.
-    return;
-  } catch(e) {
-    // Ignore, it's good if we can't QI to a chrome window.
-  }
-
-  var winIsTop = true;
-  try {
-    this.window.QueryInterface(Ci.nsIDOMWindow);
-    if (this.window.frameElement) winIsTop = false;
-  } catch (e) {
-    // Ignore non-DOM-windows.
-    dump('Could not QI this.window to nsIDOMWindow at\n'
-        + this.url + ' ?!\n');
-  }
-
-  for (var i = 0, script = null; script = aScripts[i]; i++) {
-    if (script.noframes && !winIsTop) continue;
-    var sandbox = createSandbox(script, this);
-    runScriptInSandbox(script, sandbox);
-  }
-}
 
 ScriptRunner.prototype.openInTab = function(aUrl, aInBackground) {
   var response = sendSyncMessage('greasemonkey:open-in-tab', {
@@ -57,7 +31,8 @@ ScriptRunner.prototype.openInTab = function(aUrl, aInBackground) {
   });
 
   return response ? response[0] : null;
-}
+};
+
 
 ScriptRunner.prototype.registeredMenuCommand = function(aCommand) {
   var length = this.menuCommands.push(aCommand);
@@ -69,158 +44,19 @@ ScriptRunner.prototype.registeredMenuCommand = function(aCommand) {
     name: aCommand.name,
     windowId: aCommand.contentWindowId
   });
-}
-
-var observer = {
-  observe: function(aSubject, aTopic, aData) {
-    if (!GM_util.getEnabled()) return;
-
-    switch (aTopic) {
-      case 'document-element-inserted':
-        var doc = aSubject;
-        var win = doc && doc.defaultView;
-
-        if (!doc || !win) break;
-
-        // Listen for load event (which unlike DOMContentLoaded can't be done
-        // globally), as some documents (e.g. images) don't fire DCL.
-        win.addEventListener("load", contentLoad, true);
-
-        // TODO:
-        // Sometimes we get this notification twice with different windows but
-        // identical documentURI/location.href. In one of those cases, the call
-        // to sendSyncMessage will throw, and I can't find a way to detect which
-        // notification is the correct one. if (win !== content) would also
-        // exclude iframes.
-        this.runScripts('document-start', win);
-        break;
-      case 'inner-window-destroyed':
-        // Make sure we don't keep any window references around
-        var windowId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-        delete gScriptRunners[windowId];
-        break;
-      default:
-        dump("received unknown topic: " + aTopic + "\n");
-    }
-  },
-
-  contentLoad: function(aEvent) {
-    var contentWin = aEvent.target.defaultView;
-    contentWin.removeEventListener("load", contentLoad, true);
-
-    if (!GM_util.getEnabled()) return;
-    this.runScripts('document-end', contentWin);
-  },
-
-  pagehide: function(aEvent) {
-    var contentWin = aEvent.target.defaultView;
-    var windowId = GM_util.windowId(contentWin);
-    if (!windowId || !gScriptRunners[windowId]) return;
-
-    // Small optimization: only send a notification if there's a menu command
-    // for this window.
-    if (!gScriptRunners[windowId].menuCommands.length) return;
-
-    if (aEvent.persisted) {
-      sendAsyncMessage("greasemonkey:toggle-menu-commands", {
-        frozen: true,
-        windowId: windowId
-      });
-    } else {
-      sendAsyncMessage("greasemonkey:clear-menu-commands", {
-        windowId: windowId
-      });
-    }
-  },
-
-  pageshow: function(aEvent) {
-    var contentWin = aEvent.target.defaultView;
-    var windowId = GM_util.windowId(contentWin);
-    if (!windowId || !gScriptRunners[windowId]) return;
-
-    if (!gScriptRunners[windowId].menuCommands.length) return;
-
-    sendAsyncMessage("greasemonkey:toggle-menu-commands", {
-      frozen: false,
-      windowId: windowId
-    });
-  },
-
-  runScripts: function(aRunWhen, aWrappedContentWin) {
-    // See #1970
-    // When content does (e.g.) history.replacestate() in an inline script,
-    // the location.href changes between document-start and document-end time.
-    // But the content can call replacestate() much later, too.  The only way to
-    // be consistent is to ignore it.  Luckily, the  document.documentURI does
-    // _not_ change, so always use it when deciding whether to run scripts.
-    var url = aWrappedContentWin.document.documentURI;
-    // But ( #1631 ) ignore user/pass in the URL.
-    url = url.replace(gStripUserPassRegexp, '$1');
-
-    if (!GM_util.isGreasemonkeyable(url)) return;
-
-    var windowId = GM_util.windowId(aWrappedContentWin);
-    if (gScriptRunners[windowId]) {
-      // Update the window in case it changed, see the comment in observe().
-      gScriptRunners[windowId].window = aWrappedContentWin;
-    } else {
-      gScriptRunners[windowId] = new ScriptRunner(aWrappedContentWin, url);
-    }
-
-    var response = sendSyncMessage('greasemonkey:scripts-for-url', {
-      'url': url,
-      'when': aRunWhen,
-      'windowId': windowId
-    });
-
-    if (!response || !response[0]) return;
-    var scripts = response[0].map(this.createScriptFromObject);
-    gScriptRunners[windowId].injectScripts(scripts);
-  },
-
-  runDelayedScript: function(aMessage) {
-    var windowId = aMessage.data.windowId;
-    if (!gScriptRunners[windowId]) return;
-
-    var script = this.createScriptFromObject(aMessage.data.script);
-    gScriptRunners[windowId].injectScripts([script]);
-  },
-
-  runMenuCommand: function(aMessage) {
-    var windowId = aMessage.data.windowId;
-    if (!gScriptRunners[windowId]) return;
-
-    var index = aMessage.data.index;
-    var command = gScriptRunners[windowId].menuCommands[index];
-    if (!command || !command.commandFunc) return;
-
-    // Ensure |this| is set to the sandbox object inside the command function.
-    command.commandFunc.call(null);
-  },
-
-  createScriptFromObject: function(aObject) {
-    var script = Object.create(IPCScript.prototype);
-    // TODO: better way for this? Object.create needs property descriptors.
-    for (var key in aObject)
-      script[key] = aObject[key];
-
-    return script;
-  }
-}
+};
 
-var observerService = Cc['@mozilla.org/observer-service;1']
-    .getService(Ci.nsIObserverService);
-observerService.addObserver(observer, 'document-element-inserted', false);
-observerService.addObserver(observer, 'inner-window-destroyed', false);
-
-// Used for DOMContentLoaded here and load in observer.observe.
-var contentLoad = observer.contentLoad.bind(observer);
-addEventListener("DOMContentLoaded", contentLoad, true);
+// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ //
 
+/*
 addEventListener("pagehide", observer.pagehide.bind(observer));
 addEventListener("pageshow", observer.pageshow.bind(observer));
+*/
+
 
+/*
 addMessageListener("greasemonkey:inject-script",
     observer.runDelayedScript.bind(observer));
 addMessageListener("greasemonkey:menu-command-clicked",
     observer.runMenuCommand.bind(observer));
+*/
diff --git a/modules/contentObserver.js b/modules/contentObserver.js
new file mode 100644
index 0000000..848b3f4
--- /dev/null
+++ b/modules/contentObserver.js
@@ -0,0 +1,230 @@
+var EXPORTED_SYMBOLS = ['contentObserver'];
+
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+Cu.import('resource://greasemonkey/GM_setClipboard.js');
+Cu.import("resource://greasemonkey/ipcscript.js");
+Cu.import("resource://greasemonkey/miscapis.js");
+Cu.import("resource://greasemonkey/sandbox.js");
+Cu.import('resource://greasemonkey/util.js');
+
+// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ //
+
+var gStripUserPassRegexp = new RegExp('(://)([^:/]+)(:[^@/]+)?@');
+
+// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ //
+
+function ContentObserver() {
+}
+
+
+ContentObserver.prototype.QueryInterface = XPCOMUtils.generateQI([
+    Ci.nsIObserver]);
+
+
+ContentObserver.prototype.createScriptFromObject = function(aObject) {
+  var script = Object.create(IPCScript.prototype);
+  // TODO: better way for this? Object.create needs property descriptors.
+  for (var key in aObject) {
+    script[key] = aObject[key];
+  }
+  return script;
+};
+
+
+ContentObserver.prototype.contentLoad = function(aEvent) {
+  // Now that we've seen any first load event, stop listening for any more.
+  aEvent.target.removeEventListener("DOMContentLoaded", gContentLoad, true);
+  aEvent.target.removeEventListener("load", gContentLoad, true);
+
+  var contentWin = aEvent.target.defaultView;
+  this.runScripts('document-end', contentWin);
+};
+
+
+ContentObserver.prototype.observe = function(aSubject, aTopic, aData) {
+  if (!GM_util.getEnabled()) return;
+
+  switch (aTopic) {
+    case 'document-element-inserted':
+      if (!GM_util.getEnabled()) return;
+
+      var doc = aSubject;
+      var url = doc.documentURI;
+      if (!GM_util.isGreasemonkeyable(url)) return;
+
+      var win = doc && doc.defaultView;
+      if (!doc || !win) break;
+
+      // Listen for whichever kind of load event arrives first.
+      win.addEventListener("DOMContentLoaded", gContentLoad, true);
+      win.addEventListener("load", gContentLoad, true);
+
+      // TODO:
+      // Sometimes we get this notification twice with different windows but
+      // identical documentURI/location.href. In one of those cases, the call
+      // to sendSyncMessage will throw, and I can't find a way to detect which
+      // notification is the correct one. if (win !== content) would also
+      // exclude iframes.
+      this.runScripts('document-start', win);
+      break;
+    default:
+      dump("received unknown topic: " + aTopic + "\n");
+  }
+};
+
+
+ContentObserver.prototype.pagehide = function(aEvent) {
+  var contentWin = aEvent.target.defaultView;
+  var windowId = GM_util.windowId(contentWin);
+  // TODO: Fix.
+  /*
+  if (!windowId || !gScriptRunners[windowId]) return;
+
+  // Small optimization: only send a notification if there's a menu command
+  // for this window.
+  if (!gScriptRunners[windowId].menuCommands.length) return;
+
+  if (aEvent.persisted) {
+    sendAsyncMessage("greasemonkey:toggle-menu-commands", {
+      frozen: true,
+      windowId: windowId
+    });
+  } else {
+    sendAsyncMessage("greasemonkey:clear-menu-commands", {
+      windowId: windowId
+    });
+  }
+  */
+};
+
+
+ContentObserver.prototype.pageshow = function(aEvent) {
+  var contentWin = aEvent.target.defaultView;
+  var windowId = GM_util.windowId(contentWin);
+  // TODO: Fix.
+  /*
+  if (!windowId || !gScriptRunners[windowId]) return;
+
+  if (!gScriptRunners[windowId].menuCommands.length) return;
+
+  sendAsyncMessage("greasemonkey:toggle-menu-commands", {
+    frozen: false,
+    windowId: windowId
+  });
+  */
+};
+
+
+ContentObserver.prototype.runDelayedScript = function(aMessage) {
+  var windowId = aMessage.data.windowId;
+  if (!gScriptRunners[windowId]) return;
+
+  var script = this.createScriptFromObject(aMessage.data.script);
+  gScriptRunners[windowId].injectScripts([script]);
+};
+
+
+ContentObserver.prototype.runMenuCommand = function(aMessage) {
+  var windowId = aMessage.data.windowId;
+  if (!gScriptRunners[windowId]) return;
+
+  var index = aMessage.data.index;
+  var command = gScriptRunners[windowId].menuCommands[index];
+  if (!command || !command.commandFunc) return;
+
+  // Ensure |this| is set to the sandbox object inside the command function.
+  command.commandFunc.call(null);
+};
+
+
+ContentObserver.prototype.runScripts = function(aRunWhen, aContentWin) {
+  try {
+    this.window.QueryInterface(Ci.nsIDOMChromeWindow);
+    // Never ever inject scripts into a chrome context window.
+    return;
+  } catch(e) {
+    // Ignore, it's good if we can't QI to a chrome window.
+  }
+
+  // See #1970
+  // When content does (e.g.) history.replacestate() in an inline script,
+  // the location.href changes between document-start and document-end time.
+  // But the content can call replacestate() much later, too.  The only way to
+  // be consistent is to ignore it.  Luckily, the  document.documentURI does
+  // _not_ change, so always use it when deciding whether to run scripts.
+  var url = aContentWin.document.documentURI;
+  // But ( #1631 ) ignore user/pass in the URL.
+  url = url.replace(gStripUserPassRegexp, '$1');
+
+  if (!GM_util.isGreasemonkeyable(url)) return;
+
+  var windowId = GM_util.windowId(aContentWin);
+  // TODO: Fix.
+  /*
+  if (gScriptRunners[windowId]) {
+    // Update the window in case it changed, see the comment in observe().
+    gScriptRunners[windowId].window = aContentWin;
+  } else {
+    gScriptRunners[windowId] = new ScriptRunner(aContentWin, url);
+  }
+  */
+
+  var response = this.windowMessageManager(aContentWin).sendSyncMessage(
+    'greasemonkey:scripts-for-url', {
+      'url': url,
+      'when': aRunWhen,
+      'windowId': windowId
+    });
+  if (!response || !response[0]) return;
+
+  var scripts = response[0].map(this.createScriptFromObject);
+  var winIsTop = this.windowIsTop(aContentWin);
+  for (var i = 0, script = null; script = scripts[i]; i++) {
+    if (script.noframes && !winIsTop) continue;
+    var sandbox = createSandbox(script, url, aContentWin);
+    runScriptInSandbox(script, sandbox);
+  }
+};
+
+
+ContentObserver.prototype.windowIsTop = function(aContentWin) {
+  try {
+    aContentWin.QueryInterface(Ci.nsIDOMWindow);
+    if (aContentWin.frameElement) return false;
+  } catch (e) {
+    var url = 'unknown';
+    try {
+      url = aContentWin.location.href;
+    } catch (e) { }
+    // Ignore non-DOM-windows.
+    dump('Could not QI this.window to nsIDOMWindow at\n' + url + ' ?!\n');
+  }
+  return true;
+};
+
+ContentObserver.prototype.windowMessageManager = function(aContentWin) {
+  return aContentWin.QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIWebNavigation)
+      .QueryInterface(Ci.nsIDocShell)
+      .QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIContentFrameMessageManager);
+};
+
+// \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ // \\ //
+
+// Since observers are process-global, create this observer singleton in the
+// process-global JSM scope.
+var contentObserver = new ContentObserver();
+Services.obs.addObserver(contentObserver, 'document-element-inserted', false);
+
+// This single global function reference can easily be both attached as
+// an event listener, and then removed again.
+var gContentLoad = contentObserver.contentLoad.bind(contentObserver);
diff --git a/modules/sandbox.js b/modules/sandbox.js
index 56e1548..f36dea9 100644
--- a/modules/sandbox.js
+++ b/modules/sandbox.js
@@ -24,18 +24,15 @@ function alert(msg) {
       .alert(null, "Greasemonkey alert", msg);
 }
 
-function createSandbox(aScript, aScriptRunner) {
-  var contentWin = aScriptRunner.window;
-  var url = aScriptRunner.url;
-
+function createSandbox(aScript, aUrl, aWindow) {
   if (GM_util.inArray(aScript.grants, 'none')) {
     // If there is an explicit none grant, use a plain unwrapped sandbox
     // with no other content.
     var contentSandbox = new Components.utils.Sandbox(
-        contentWin,
+        aWindow,
         {
           'sandboxName': aScript.id,
-          'sandboxPrototype': contentWin,
+          'sandboxPrototype': aWindow,
           'wantXrays': false,
         });
     // GM_info is always provided.
@@ -56,10 +53,10 @@ function createSandbox(aScript, aScriptRunner) {
   }
 
   var sandbox = new Components.utils.Sandbox(
-      [contentWin],
+      [aWindow],
       {
         'sandboxName': aScript.id,
-        'sandboxPrototype': contentWin,
+        'sandboxPrototype': aWindow,
         'wantXrays': true,
       });
 
@@ -75,15 +72,16 @@ function createSandbox(aScript, aScriptRunner) {
   sandbox.exportFunction = Cu.exportFunction;
 
   if (GM_util.inArray(aScript.grants, 'GM_addStyle')) {
-    sandbox.GM_addStyle = GM_util.hitch(null, GM_addStyle, contentWin.document);
+    sandbox.GM_addStyle = GM_util.hitch(null, GM_addStyle, aWindow.document);
   }
   if (GM_util.inArray(aScript.grants, 'GM_log')) {
     sandbox.GM_log = GM_util.hitch(new GM_ScriptLogger(aScript), 'log');
   }
-  if (GM_util.inArray(aScript.grants, 'GM_registerMenuCommand')) {
-    var gmrmc = GM_util.hitch(null, registerMenuCommand, aScriptRunner);
-    sandbox.GM_registerMenuCommand = gmrmc;
-  }
+  // TODO: Fix.
+//  if (GM_util.inArray(aScript.grants, 'GM_registerMenuCommand')) {
+//    var gmrmc = GM_util.hitch(null, registerMenuCommand, aScriptRunner);
+//    sandbox.GM_registerMenuCommand = gmrmc;
+//  }
 
   var scriptStorage = new GM_ScriptStorage(aScript);
   if (GM_util.inArray(aScript.grants, 'GM_deleteValue')) {
@@ -111,13 +109,14 @@ function createSandbox(aScript, aScriptRunner) {
   if (GM_util.inArray(aScript.grants, 'GM_listValues')) {
     sandbox.GM_listValues = GM_util.hitch(scriptStorage, 'listValues');
   }
-  if (GM_util.inArray(aScript.grants, 'GM_openInTab')) {
-    sandbox.GM_openInTab = GM_util.hitch(
-        null, GM_openInTab, aScriptRunner);
-  }
+  // TODO: Fix.
+//  if (GM_util.inArray(aScript.grants, 'GM_openInTab')) {
+//    sandbox.GM_openInTab = GM_util.hitch(
+//        null, GM_openInTab, aScriptRunner);
+//  }
   if (GM_util.inArray(aScript.grants, 'GM_xmlhttpRequest')) {
     sandbox.GM_xmlhttpRequest = GM_util.hitch(
-        new GM_xmlhttpRequester(contentWin, aScriptRunner.url, sandbox),
+        new GM_xmlhttpRequester(aWindow, aUrl, sandbox),
         'contentStartRequest');
   }
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/greasemonkey.git



More information about the Pkg-mozext-commits mailing list