--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -154,16 +154,50 @@ Script.prototype = {
if (this.options.cssCode) {
let url = "data:text/css;charset=utf-8," + encodeURIComponent(this.options.cssCode);
urls.push(url);
}
return urls;
},
+ matchesLoadInfo(uri, loadInfo) {
+ if (!this.matchesURI(uri)) {
+ return false;
+ }
+
+ if (!this.options.all_frames && !loadInfo.isTopLevelLoad) {
+ return false;
+ }
+
+ return true;
+ },
+
+ matchesURI(uri) {
+ if (!(this.matches_.matches(uri) || this.matches_host_.matchesIgnoringPath(uri))) {
+ return false;
+ }
+
+ if (this.exclude_matches_.matches(uri)) {
+ return false;
+ }
+
+ if (this.options.include_globs != null) {
+ if (!this.include_globs_.matches(uri.spec)) {
+ return false;
+ }
+ }
+
+ if (this.exclude_globs_.matches(uri.spec)) {
+ return false;
+ }
+
+ return true;
+ },
+
matches(window) {
let uri = window.document.documentURIObject;
let principal = window.document.nodePrincipal;
// If mozAddonManager is present on this page, don't allow
// content scripts.
if (window.navigator.mozAddonManager !== undefined) {
return false;
@@ -186,31 +220,17 @@ Script.prototype = {
// Documents from data: URIs also inherit the principal.
if (Services.netUtils.URIChainHasFlags(uri, Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT)) {
if (!this.match_about_blank) {
return false;
}
uri = principal.URI;
}
- if (!(this.matches_.matches(uri) || this.matches_host_.matchesIgnoringPath(uri))) {
- return false;
- }
-
- if (this.exclude_matches_.matches(uri)) {
- return false;
- }
-
- if (this.options.include_globs != null) {
- if (!this.include_globs_.matches(uri.spec)) {
- return false;
- }
- }
-
- if (this.exclude_globs_.matches(uri.spec)) {
+ if (!this.matchesURI(uri)) {
return false;
}
if (this.options.frame_id != null) {
if (WebNavigationFrames.getFrameId(window) != this.options.frame_id) {
return false;
}
} else if (!this.options.all_frames && window.top != window) {
@@ -506,22 +526,28 @@ DocumentManager = {
// Map[windowId -> Map[extensionId -> ContentScriptContextChild]]
contentScriptWindows: new Map(),
// Map[windowId -> ContentScriptContextChild]
extensionPageWindows: new Map(),
init() {
+ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+ Services.obs.addObserver(this, "http-on-opening-request", false);
+ }
Services.obs.addObserver(this, "content-document-global-created", false);
Services.obs.addObserver(this, "document-element-inserted", false);
Services.obs.addObserver(this, "inner-window-destroyed", false);
},
uninit() {
+ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+ Services.obs.removeObserver(this, "http-on-opening-request");
+ }
Services.obs.removeObserver(this, "content-document-global-created");
Services.obs.removeObserver(this, "document-element-inserted");
Services.obs.removeObserver(this, "inner-window-destroyed");
},
getWindowState(contentWindow) {
let readyState = contentWindow.document.readyState;
if (readyState == "complete") {
@@ -551,17 +577,17 @@ DocumentManager = {
DocumentManager.getExtensionPageContext(extension, window);
} else if (apiLevel == FULL_PRIVILEGES) {
ExtensionChild.createExtensionContext(extension, window);
}
}
}
},
- observe: function(subject, topic, data) {
+ observe(subject, topic, data) {
// For some types of documents (about:blank), we only see the first
// notification, for others (data: URIs) we only observe the second.
if (topic == "content-document-global-created" || topic == "document-element-inserted") {
let document = subject;
let window = document && document.defaultView;
if (topic == "content-document-global-created") {
window = subject;
@@ -608,20 +634,29 @@ DocumentManager = {
// Close any existent iframe extension page context for the destroyed window.
if (this.extensionPageWindows.has(windowId)) {
let context = this.extensionPageWindows.get(windowId);
context.close();
this.extensionPageWindows.delete(windowId);
}
ExtensionChild.destroyExtensionContext(windowId);
+ } else if (topic === "http-on-opening-request") {
+ let {loadInfo} = subject.QueryInterface(Ci.nsIChannel);
+ if (loadInfo) {
+ let {externalContentPolicyType: type} = loadInfo;
+ if (type === Ci.nsIContentPolicy.TYPE_DOCUMENT ||
+ type === Ci.nsIContentPolicy.TYPE_SUBDOCUMENT) {
+ this.preloadScripts(subject.URI, loadInfo);
+ }
+ }
}
},
- handleEvent: function(event) {
+ handleEvent(event) {
let window = event.currentTarget;
if (event.target != window.document) {
// We use capturing listeners so we have precedence over content script
// listeners, but only care about events targeted to the element we're
// listening on.
return;
}
window.removeEventListener(event.type, this, true);
@@ -770,16 +805,26 @@ DocumentManager = {
MessageChannel.abortResponses({extensionId});
this.extensionCount--;
if (this.extensionCount == 0) {
this.uninit();
}
},
+ preloadScripts(uri, loadInfo) {
+ for (let extension of ExtensionManager.extensions.values()) {
+ for (let script of extension.scripts) {
+ if (script.matchesLoadInfo(uri, loadInfo)) {
+ script.compileScripts();
+ }
+ }
+ }
+ },
+
trigger(when, window) {
if (when === "document_start") {
for (let extension of ExtensionManager.extensions.values()) {
for (let script of extension.scripts) {
if (script.matches(window)) {
let context = this.getContentScriptContext(extension, window);
context.addScript(script, when);
}