[Pkg-mozext-commits] [greasemonkey] 01/43: [WIP] Move script injection into frame script for e10s.
David Prévot
taffit at moszumanska.debian.org
Sun Feb 22 21:56:08 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 1ccf7e45aa71217f2827e55511dcff737d7be4a3
Author: Ventero <ventero at ventero.de>
Date: Tue Sep 23 18:54:39 2014 +0200
[WIP] Move script injection into frame script for e10s.
---
components/greasemonkey.js | 82 ++++++++--------------------
content/browser.js | 7 ---
content/config.js | 12 +++--
content/framescript.js | 130 +++++++++++++++++++++++++++++++++++++++++++++
modules/ipcscript.js | 79 +++++++++++++++++++++++++++
modules/miscapis.js | 2 +
modules/sandbox.js | 2 +
modules/script.js | 25 ++++++---
8 files changed, 263 insertions(+), 76 deletions(-)
diff --git a/components/greasemonkey.js b/components/greasemonkey.js
index cb0625a..c08f23b 100644
--- a/components/greasemonkey.js
+++ b/components/greasemonkey.js
@@ -8,10 +8,9 @@ var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
-Cu.import("resource://greasemonkey/third-party/getChromeWinForContentWin.js");
+Cu.import("resource://greasemonkey/ipcscript.js");
Cu.import("resource://greasemonkey/menucommand.js");
Cu.import("resource://greasemonkey/prefmanager.js");
-Cu.import("resource://greasemonkey/sandbox.js");
Cu.import("resource://greasemonkey/sync.js");
Cu.import("resource://greasemonkey/util.js");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@@ -27,17 +26,8 @@ var gTmpDir = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("TmpD", Components.interfaces.nsIFile);
-var gStripUserPassRegexp = new RegExp('(://)([^:/]+)(:[^@/]+)?@');
-
/////////////////////// Component-global Helper Functions //////////////////////
-function contentLoad(aEvent) {
- var safeWin = aEvent.target.defaultView;
- safeWin.removeEventListener('DOMContentLoaded', contentLoad, true);
- safeWin.removeEventListener('load', contentLoad, true);
- GM_util.getService().runScripts('document-end', safeWin);
-}
-
function isTempScript(uri) {
if (uri.scheme != "file") return false;
var file = gFileProtocolHandler.getFileFromURLSpec(uri.spec);
@@ -58,6 +48,13 @@ function startup(aService) {
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(aService, 'document-element-inserted', false);
+ var messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
+ .getService(Ci.nsIMessageListenerManager);
+
+ messageManager.addMessageListener('greasemonkey:scripts-for-url',
+ aService.getScriptsForUrl.bind(aService));
+ messageManager.loadFrameScript("chrome://greasemonkey/content/framescript.js", true);
+
// Import this once, early, so that enqueued deletes can happen.
Cu.import("resource://greasemonkey/util/enqueueRemoveFile.js");
}
@@ -65,7 +62,6 @@ function startup(aService) {
/////////////////////////////////// Service ////////////////////////////////////
function service() {
- this.contentLoad = contentLoad;
this.filename = Components.stack.filename;
this.wrappedJSObject = this;
}
@@ -142,17 +138,6 @@ service.prototype.observe = function(aSubject, aTopic, aData) {
case 'profile-after-change':
startup(this);
break;
- case 'document-element-inserted':
- if (!GM_util.getEnabled()) break;
- var doc = aSubject;
- var win = doc && doc.defaultView;
- if (!doc || !win) break;
-
- win.addEventListener('DOMContentLoaded', contentLoad, true);
- win.addEventListener('load', contentLoad, true);
- this.runScripts('document-start', win);
-
- break;
}
};
@@ -171,60 +156,39 @@ service.prototype.__defineGetter__('config', function() {
return this._config;
});
-service.prototype.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');
+service.prototype.getScriptsForUrl = function(aMessage) {
+ var url = aMessage.data.url;
+ var when = aMessage.data.when;
+ var windowId = aMessage.data.windowId;
+ var browser = aMessage.target;
- if (!GM_util.getEnabled() || !GM_util.isGreasemonkeyable(url)) return;
+ if (!GM_util.getEnabled() || !url) return [];
+ if (!GM_util.isGreasemonkeyable(url)) return [];
if (GM_prefRoot.getValue('enableScriptRefreshing')) {
- this._config.updateModifiedScripts(aRunWhen, aWrappedContentWin);
+ this.config.updateModifiedScripts(when, url, windowId, browser);
}
var scripts = this.config.getMatchingScripts(function(script) {
try {
- return GM_util.scriptMatchesUrlAndRuns(script, url, aRunWhen);
+ return GM_util.scriptMatchesUrlAndRuns(script, url, when);
} catch (e) {
GM_util.logError(e, false, e.fileName, e.lineNumber);
// See #1692; Prevent failures like that from being so severe.
return false;
}
+ }).map(function(script) {
+ // Make the script serializable so it can be sent to the frame script.
+ return new IPCScript(script);
});
- if (scripts.length > 0) {
- this.injectScripts(scripts, url, aWrappedContentWin);
- }
-};
+
+ return scripts;
+}
service.prototype.ignoreNextScript = function() {
this._ignoreNextScript = true;
};
-service.prototype.injectScripts = function(
- scripts, url, wrappedContentWin
-) {
- try {
- wrappedContentWin.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 chromeWin = getChromeWinForContentWin(wrappedContentWin);
-
- for (var i = 0, script = null; script = scripts[i]; i++) {
- var sandbox = createSandbox(script, wrappedContentWin, url);
- runScriptInSandbox(script, sandbox);
- }
-};
-
//////////////////////////// Component Registration ////////////////////////////
var NSGetFactory = XPCOMUtils.generateNSGetFactory([service]);
diff --git a/content/browser.js b/content/browser.js
index aa42277..f34a564 100644
--- a/content/browser.js
+++ b/content/browser.js
@@ -45,9 +45,6 @@ GM_BrowserUI.chromeLoad = function(e) {
gBrowser.addEventListener("pageshow", GM_BrowserUI.pageshow, true);
var sidebar = document.getElementById("sidebar");
- var svc = GM_util.getService();
- sidebar.addEventListener(
- "DOMContentLoaded", GM_util.hitch(svc, svc.contentLoad), true);
sidebar.addEventListener("pagehide", GM_BrowserUI.pagehide, true);
sidebar.addEventListener("pageshow", GM_BrowserUI.pageshow, true);
@@ -56,10 +53,6 @@ GM_BrowserUI.chromeLoad = function(e) {
var safeWin = aEvent.target.defaultView;
var href = safeWin.location.href;
GM_BrowserUI.checkDisabledScriptNavigation(aEvent, safeWin, href);
- if (0 == href.indexOf('about:blank')) {
- // #1696: document-element-inserted doesn't see about:blank
- svc.contentLoad(aEvent);
- }
}, true);
document.getElementById("contentAreaContextMenu")
diff --git a/content/config.js b/content/config.js
index abce40c..cfdf93e 100644
--- a/content/config.js
+++ b/content/config.js
@@ -220,7 +220,9 @@ Config.prototype.getMatchingScripts = function(testFunc) {
return this._scripts.filter(testFunc);
};
-Config.prototype.updateModifiedScripts = function(aWhen, aSafeWin) {
+Config.prototype.updateModifiedScripts = function(
+ aWhen, aUrl, aWindowId, aBrowser
+) {
// Find any updated scripts or scripts with delayed injection
var scripts = this.getMatchingScripts(
function (script) {
@@ -236,11 +238,15 @@ Config.prototype.updateModifiedScripts = function(aWhen, aSafeWin) {
var parsedScript = scope.parse(
script.textContent, GM_util.uriFromUrl(script.downloadURL));
// TODO: Show PopupNotifications about parse error(s)?
- script.updateFromNewScript(parsedScript, aSafeWin);
+ script.updateFromNewScript(parsedScript, aUrl, aWindowId, aBrowser);
} else {
// We are already downloading dependencies for this script
// so add its window to the list
- script.pendingExec.push({'safeWin': aSafeWin});
+ script.pendingExec.push({
+ 'browser': aBrowser,
+ 'url': aUrl,
+ 'windowId': aWindowId
+ });
}
}
diff --git a/content/framescript.js b/content/framescript.js
new file mode 100644
index 0000000..6d7dd0a
--- /dev/null
+++ b/content/framescript.js
@@ -0,0 +1,130 @@
+let {utils: Cu, interfaces: Ci, classes: Cc} = Components;
+
+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 gStripUserPassRegexp = new RegExp('(://)([^:/]+)(:[^@/]+)?@');
+var gScriptRunners = {};
+
+function ScriptRunner(aWindow) {
+ this.window = aWindow;
+}
+
+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.
+ }
+
+ for (var i = 0, script = null; script = aScripts[i]; i++) {
+ var sandbox = createSandbox(script, this.window);
+ runScriptInSandbox(script, sandbox);
+ }
+}
+
+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;
+
+ // 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) {
+ if (!GM_util.getEnabled()) return;
+
+ var contentWin = aEvent.target.defaultView;
+ this.runScripts('document-end', contentWin);
+ },
+
+ 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);
+ }
+
+ 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]);
+ },
+
+ 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);
+
+addEventListener("DOMContentLoaded", observer.contentLoad.bind(observer));
+addEventListener("load", observer.contentLoad.bind(observer));
+
+addMessageListener("greasemonkey:inject-script",
+ observer.runDelayedScript.bind(observer));
diff --git a/modules/ipcscript.js b/modules/ipcscript.js
new file mode 100644
index 0000000..87f6794
--- /dev/null
+++ b/modules/ipcscript.js
@@ -0,0 +1,79 @@
+var EXPORTED_SYMBOLS = ['IPCScript'];
+
+Components.utils.import("resource://greasemonkey/extractMeta.js");
+Components.utils.import("resource://greasemonkey/util.js");
+
+function IPCScript(aScript) {
+ this.description = aScript.description;
+ this.excludes = aScript.excludes;
+ this.fileURL = aScript.fileURL;
+ this.grants = aScript.grants;
+ this.id = aScript.id;
+ this.includes = aScript.includes;
+ this.localized = aScript.localized;
+ this.matches = aScript.matches.map(function(m) { return m.pattern });
+ this.name = aScript.name;
+ this.namespace = aScript.namespace;
+ this.runAt = aScript.runAt;
+ this.textContent = aScript.textContent;
+ this.uuid = aScript.uuid;
+ this.version = aScript.version;
+ this.willUpdate = aScript.isRemoteUpdateAllowed();
+
+ this.requires = aScript.requires.map(function(req) {
+ return {
+ 'fileURL': req.fileURL,
+ 'textContent': req.textContent
+ }
+ });
+
+ this.resources = aScript.resources.map(function(res) {
+ return {
+ 'name': res.name,
+ 'mimetype': res.mimetype,
+ 'textContent': res.textContent
+ };
+ });
+};
+
+IPCScript.prototype.__defineGetter__('metaStr',
+function IPCScript_getMetaStr() {
+ if (!this._metaStr) {
+ this._metaStr = extractMeta(this.textContent);
+ }
+
+ return this._metaStr;
+});
+
+IPCScript.prototype.info = function() {
+ var resources = {};
+ for (var i = 0, r = null; r = this.resources[i]; i++) {
+ resources[r.name] = {
+ 'name': r.name,
+ 'mimetype': r.mimetype,
+ };
+ }
+
+ return {
+ 'uuid': this.uuid,
+ 'version': "unknown", // TODO
+ 'scriptMetaStr': this.metaStr,
+ 'scriptSource': this.textContent,
+ 'scriptWillUpdate': this.willUpdate,
+ 'script': {
+ 'description': this.desciption,
+ 'excludes': this.excludes,
+ // 'icon': ??? source URL?
+ 'includes': this.includes,
+ 'localizedDescription': this.localized.description,
+ 'localizedName': this.localized.name,
+ 'matches': this.matches,
+ 'name': this.name,
+ 'namespace': this.namespace,
+ // 'requires': ??? source URL?
+ 'resources': resources,
+ 'run-at': this.runAt,
+ 'version': this.version
+ }
+ }
+};
diff --git a/modules/miscapis.js b/modules/miscapis.js
index c4056b2..e5b99d9 100644
--- a/modules/miscapis.js
+++ b/modules/miscapis.js
@@ -300,6 +300,8 @@ function GM_openInTab(safeContentWin, url, aLoadInBackground) {
// Get the chrome window currently corresponding to the content window, as
// this might have changed since the script was injected (e.g. by moving
// the tab to a different window).
+ // TODO: sendAsyncMessage("...", {url}), look for browser containing message.target,
+ // add tab to that browser
var chromeWin = getChromeWinForContentWin(safeContentWin);
var browser = chromeWin.gBrowser;
var currentTab = browser.tabs[
diff --git a/modules/sandbox.js b/modules/sandbox.js
index 060aacc..d19dbe2 100644
--- a/modules/sandbox.js
+++ b/modules/sandbox.js
@@ -34,6 +34,7 @@ function createSandbox(aScript, aContentWin, aUrl) {
'wantXrays': false,
});
// GM_info is always provided.
+ // TODO: lazy getter? XPCOMUtils.defineLazyGetter
Components.utils.evalInSandbox(
'const GM_info = ' + uneval(aScript.info()), contentSandbox);
// Alias unsafeWindow for compatibility.
@@ -116,6 +117,7 @@ function createSandbox(aScript, aContentWin, aUrl) {
'contentStartRequest');
}
+ // TODO: lazy getter?
Components.utils.evalInSandbox(
'const GM_info = ' + uneval(aScript.info()), sandbox);
diff --git a/modules/script.js b/modules/script.js
index 3766cfe..29cf98b 100644
--- a/modules/script.js
+++ b/modules/script.js
@@ -4,6 +4,7 @@ Components.utils.import('resource://gre/modules/AddonManager.jsm');
Components.utils.import('resource://greasemonkey/GM_notification.js');
Components.utils.import('resource://greasemonkey/constants.js');
Components.utils.import('resource://greasemonkey/extractMeta.js');
+Components.utils.import('resource://greasemonkey/ipcscript.js');
Components.utils.import('resource://greasemonkey/miscapis.js');
Components.utils.import("resource://greasemonkey/parseScript.js");
Components.utils.import('resource://greasemonkey/prefmanager.js');
@@ -584,7 +585,7 @@ Script.prototype.isRemoteUpdateAllowed = function(aForced) {
}
};
-Script.prototype.updateFromNewScript = function(newScript, safeWin) {
+Script.prototype.updateFromNewScript = function(newScript, url, windowId, browser) {
// Keep a _copy_ of the old script ID, so we can eventually pass it up
// to the Add-ons manager UI, to update this script's old entry.
var oldScriptId = '' + this.id;
@@ -636,8 +637,12 @@ Script.prototype.updateFromNewScript = function(newScript, safeWin) {
this.id + "\nNot running at document-start; waiting for update ...",
true);
this.pendingExec.push('document-start update');
- } else {
- if (safeWin) this.pendingExec.push({'safeWin': safeWin});
+ } else if (windowId) {
+ this.pendingExec.push({
+ 'browser': browser,
+ 'url': url,
+ 'windowId': windowId
+ });
}
// Re-download dependencies.
@@ -704,11 +709,17 @@ Script.prototype.updateFromNewScript = function(newScript, safeWin) {
true);
continue;
}
- if (pendingExec.safeWin.closed) continue;
- var url = pendingExec.safeWin.location.href;
- var shouldRun = GM_util.scriptMatchesUrlAndRuns(this, url, this.runAt);
+
+ var shouldRun = GM_util.scriptMatchesUrlAndRuns(
+ this, pendingExec.url, this.runAt);
+
if (shouldRun) {
- GM_util.getService().injectScripts([this], url, pendingExec.safeWin);
+ pendingExec.browser.messageManager.sendAsyncMessage(
+ "greasemonkey:inject-script",
+ {
+ windowId: pendingExec.windowId,
+ script: new IPCScript(this)
+ });
}
}
--
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