Bug 1368102: Part 8 - Move extension page matching into C++. r?billm,mixedpuppy
Bill, can you please review the WebIDL change, and Shane the rest?
MozReview-Commit-ID: 6N3sGrAsHzs
--- a/dom/webidl/WebExtensionPolicy.webidl
+++ b/dom/webidl/WebExtensionPolicy.webidl
@@ -72,16 +72,19 @@ interface WebExtensionPolicy {
* Only one extension policy with a given ID or hostname may be active at a
* time. Attempting to activate a policy while a conflicting policy is
* active will raise an error.
*/
[Affects=Everything, SetterThrows]
attribute boolean active;
+ static readonly attribute boolean isExtensionProcess;
+
+
/**
* Returns true if the extension has cross-origin access to the given URI.
*/
boolean canAccessURI(URI uri);
/**
* Returns true if the extension currently has the given permission.
*/
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -17,19 +17,16 @@ Cu.import("resource://gre/modules/XPCOMU
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
"resource:///modules/E10SUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "UUIDMap", () => {
let {UUIDMap} = Cu.import("resource://gre/modules/Extension.jsm", {});
return UUIDMap;
});
-const {appinfo} = Services;
-const isParentProcess = appinfo.processType === appinfo.PROCESS_TYPE_DEFAULT;
-
/*
* This file should be kept short and simple since it's loaded even
* when no extensions are running.
*/
function parseScriptOptions(options) {
return {
allFrames: options.all_frames,
@@ -83,20 +80,17 @@ function onCacheInvalidate() {
Services.obs.addObserver(onCacheInvalidate, "startupcache-invalidate");
var ExtensionManagement = {
get cacheInvalidated() {
return cacheInvalidated;
},
get isExtensionProcess() {
- if (this.useRemoteWebExtensions) {
- return appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE;
- }
- return isParentProcess;
+ return WebExtensionPolicy.isExtensionProcess;
},
// Called when a new extension is loaded.
startupExtension(uuid, uri, extension) {
let policy = new WebExtensionPolicy({
id: extension.id,
mozExtensionHostname: uuid,
baseURL: uri.spec,
--- a/toolkit/components/extensions/ExtensionPolicyService.cpp
+++ b/toolkit/components/extensions/ExtensionPolicyService.cpp
@@ -4,21 +4,24 @@
#include "mozilla/ExtensionPolicyService.h"
#include "mozilla/extensions/WebExtensionContentScript.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
#include "mozIExtensionProcessScript.h"
#include "nsEscape.h"
#include "nsGkAtoms.h"
#include "nsIChannel.h"
#include "nsIContentPolicy.h"
+#include "nsIDOMDocument.h"
#include "nsIDocument.h"
#include "nsILoadInfo.h"
#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
#include "nsXULAppAPI.h"
namespace mozilla {
@@ -48,16 +51,18 @@ ProcessScript()
}
return *sProcessScript;
}
/*****************************************************************************
* ExtensionPolicyService
*****************************************************************************/
+/* static */ bool ExtensionPolicyService::sRemoteExtensions;
+
/* static */ ExtensionPolicyService&
ExtensionPolicyService::GetSingleton()
{
static RefPtr<ExtensionPolicyService> sExtensionPolicyService;
if (MOZ_UNLIKELY(!sExtensionPolicyService)) {
sExtensionPolicyService = new ExtensionPolicyService();
ClearOnShutdown(&sExtensionPolicyService);
@@ -65,20 +70,33 @@ ExtensionPolicyService::GetSingleton()
return *sExtensionPolicyService.get();
}
ExtensionPolicyService::ExtensionPolicyService()
{
mObs = services::GetObserverService();
MOZ_RELEASE_ASSERT(mObs);
+ Preferences::AddBoolVarCache(&sRemoteExtensions, "extensions.webextensions.remote", false);
+
RegisterObservers();
}
+bool
+ExtensionPolicyService::IsExtensionProcess() const
+{
+ if (sRemoteExtensions) {
+ auto& remoteType = dom::ContentChild::GetSingleton()->GetRemoteType();
+ return remoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE);
+ }
+ return XRE_IsParentProcess();
+}
+
+
WebExtensionPolicy*
ExtensionPolicyService::GetByURL(const URLInfo& aURL)
{
if (aURL.Scheme() == nsGkAtoms::moz_extension) {
return GetByHost(aURL.Host());
}
return nullptr;
}
@@ -221,16 +239,27 @@ ExtensionPolicyService::CheckRequest(nsI
// matching content scripts or extension principals, and loads them if
// necessary.
void
ExtensionPolicyService::CheckDocument(nsIDocument* aDocument)
{
nsCOMPtr<nsPIDOMWindowOuter> win = aDocument->GetWindow();
if (win) {
CheckContentScripts(win.get(), false);
+
+ nsIPrincipal* principal = aDocument->NodePrincipal();
+
+ nsAutoString addonId;
+ Unused << principal->GetAddonId(addonId);
+
+ RefPtr<WebExtensionPolicy> policy = GetByID(addonId);
+ if (policy) {
+ nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDocument);
+ ProcessScript().InitExtensionDocument(policy, doc);
+ }
}
}
// Checks for loads of about:blank into new window globals, and loads any
// matching content scripts. about:blank loads do not trigger document element
// inserted events, so they're the only load type that are special cased this
// way.
void
--- a/toolkit/components/extensions/ExtensionPolicyService.h
+++ b/toolkit/components/extensions/ExtensionPolicyService.h
@@ -71,16 +71,18 @@ public:
void GetAll(nsTArray<RefPtr<WebExtensionPolicy>>& aResult);
bool RegisterExtension(WebExtensionPolicy& aPolicy);
bool UnregisterExtension(WebExtensionPolicy& aPolicy);
void BaseCSP(nsAString& aDefaultCSP) const;
void DefaultCSP(nsAString& aDefaultCSP) const;
+ bool IsExtensionProcess() const;
+
protected:
virtual ~ExtensionPolicyService() = default;
private:
ExtensionPolicyService();
void RegisterObservers();
void UnregisterObservers();
@@ -90,13 +92,15 @@ private:
void CheckWindow(nsPIDOMWindowOuter* aWindow);
void CheckContentScripts(const DocInfo& aDocInfo, bool aIsPreload);
nsRefPtrHashtable<nsPtrHashKey<const nsIAtom>, WebExtensionPolicy> mExtensions;
nsRefPtrHashtable<nsCStringHashKey, WebExtensionPolicy> mExtensionHosts;
nsCOMPtr<nsIObserverService> mObs;
+
+ static bool sRemoteExtensions;
};
} // namespace mozilla
#endif // mozilla_ExtensionPolicyService_h
--- a/toolkit/components/extensions/WebExtensionPolicy.cpp
+++ b/toolkit/components/extensions/WebExtensionPolicy.cpp
@@ -236,16 +236,22 @@ WebExtensionPolicy::GetURL(const nsAStri
nsCOMPtr<nsIURI> uri;
NS_TRY(NS_NewURI(getter_AddRefs(uri), spec));
NS_TRY(uri->Resolve(NS_ConvertUTF16toUTF8(aPath), spec));
return NS_ConvertUTF8toUTF16(spec);
}
+/* static */ bool
+WebExtensionPolicy::IsExtensionProcess(GlobalObject& aGlobal)
+{
+ return EPS().IsExtensionProcess();
+}
+
nsCString
WebExtensionPolicy::BackgroundPageHTML() const
{
nsAutoCString result;
if (mBackgroundScripts.IsNull()) {
result.SetIsVoid(true);
return result;
--- a/toolkit/components/extensions/WebExtensionPolicy.h
+++ b/toolkit/components/extensions/WebExtensionPolicy.h
@@ -126,16 +126,19 @@ public:
static already_AddRefed<WebExtensionPolicy>
GetByHostname(dom::GlobalObject& aGlobal, const nsACString& aHostname);
static already_AddRefed<WebExtensionPolicy>
GetByURI(dom::GlobalObject& aGlobal, nsIURI* aURI);
+ static bool IsExtensionProcess(dom::GlobalObject& aGlobal);
+
+
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
protected:
virtual ~WebExtensionPolicy() = default;
private:
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -10,18 +10,16 @@
* after startup, in *every* browser process live outside of this file.
*/
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
- "resource://gre/modules/ExtensionManagement.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
"resource://gre/modules/MessageChannel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionChild",
"resource://gre/modules/ExtensionChild.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionContent",
"resource://gre/modules/ExtensionContent.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPageChild",
@@ -88,19 +86,16 @@ class ExtensionGlobal {
MessageChannel.addListener(global, "Extension:Capture", this);
MessageChannel.addListener(global, "Extension:DetectLanguage", this);
MessageChannel.addListener(global, "Extension:Execute", this);
MessageChannel.addListener(global, "WebNavigation:GetFrame", this);
MessageChannel.addListener(global, "WebNavigation:GetAllFrames", this);
}
- uninit() {
- }
-
get messageFilterStrict() {
return {
innerWindowID: getInnerWindowID(this.global.content),
};
}
receiveMessage({target, messageName, recipient, data}) {
switch (messageName) {
@@ -138,27 +133,19 @@ DocumentManager = {
globals: new Map(),
// Initialize listeners that we need regardless of whether extensions are
// enabled.
earlyInit() {
Services.obs.addObserver(this, "tab-content-frameloader-created"); // eslint-disable-line mozilla/balanced-listeners
},
- // Initialize listeners that we need when any extension is enabled.
- init() {
- Services.obs.addObserver(this, "document-element-inserted");
- },
- uninit() {
- Services.obs.removeObserver(this, "document-element-inserted");
- },
-
extensionProcessInitialized: false,
initExtensionProcess() {
- if (this.extensionProcessInitialized || !ExtensionManagement.isExtensionProcess) {
+ if (this.extensionProcessInitialized || !WebExtensionPolicy.isExtensionProcess) {
return;
}
this.extensionProcessInitialized = true;
for (let global of this.globals.keys()) {
ExtensionPageChild.init(global);
}
},
@@ -167,69 +154,39 @@ DocumentManager = {
// into.
initGlobal(global) {
// Note: {once: true} does not work as expected here.
global.addEventListener("unload", event => { // eslint-disable-line mozilla/balanced-listeners
this.uninitGlobal(global);
});
this.globals.set(global, new ExtensionGlobal(global));
- this.initExtensionProcess();
- if (this.extensionProcessInitialized && ExtensionManagement.isExtensionProcess) {
+ if (this.extensionProcessInitialized && WebExtensionPolicy.isExtensionProcess) {
ExtensionPageChild.init(global);
}
},
uninitGlobal(global) {
if (this.extensionProcessInitialized) {
ExtensionPageChild.uninit(global);
}
- this.globals.get(global).uninit();
this.globals.delete(global);
},
initExtension(extension) {
- if (this.extensionCount === 0) {
- this.init();
- this.initExtensionProcess();
- }
- this.extensionCount++;
+ this.initExtensionProcess();
this.injectExtensionScripts(extension);
},
- uninitExtension(extension) {
- this.extensionCount--;
- if (this.extensionCount === 0) {
- this.uninit();
- }
- },
-
- extensionCount: 0,
// Listeners
- observers: {
- "document-element-inserted"(document) {
- let window = document.defaultView;
- if (!document.location || !window ||
- // Make sure we only load into frames that belong to tabs, or other
- // special areas that we want to load content scripts into.
- !this.globals.has(getMessageManager(window))) {
- return;
- }
-
- this.loadInto(window);
- },
-
- "tab-content-frameloader-created"(global) {
- this.initGlobal(global);
- },
- },
-
observe(subject, topic, data) {
- this.observers[topic].call(this, subject, topic, data);
+ if (topic == "tab-content-frameloader-created") {
+ this.initGlobal(subject);
+ }
},
// Script loading
injectExtensionScripts(extension) {
for (let window of this.enumerateWindows()) {
for (let script of extension.contentScripts) {
if (script.matchesWindow(window)) {
@@ -276,29 +233,19 @@ DocumentManager = {
if (principal.addonId !== addonId) {
return false;
}
}
return true;
},
- loadInto(window) {
- let {addonId} = Cu.getObjectPrincipal(window);
- if (!addonId) {
- return;
- }
-
- let policy = WebExtensionPolicy.getByID(addonId);
- if (!policy) {
- throw new Error(`No registered extension for ID ${addonId}`);
- }
-
+ loadInto(policy, window) {
let extension = extensions.get(policy);
- if (this.checkParentFrames(window, addonId) && ExtensionManagement.isExtensionProcess) {
+ if (this.checkParentFrames(window, policy.id) && WebExtensionPolicy.isExtensionProcess) {
// We're in a top-level extension frame, or a sub-frame thereof,
// in the extension process. Inject the full extension page API.
ExtensionPageChild.initExtensionContext(extension, window);
} else {
// We're in a content sub-frame or not in the extension process.
// Only inject a minimal content script API.
ExtensionContent.initExtensionContext(extension, window);
}
@@ -390,18 +337,16 @@ ExtensionManager = {
case "Extension:Shutdown": {
let policy = WebExtensionPolicy.getByID(data.id);
if (extensions.has(policy)) {
extensions.get(policy).shutdown();
}
- DocumentManager.uninitExtension(policy);
-
if (isContentProcess) {
policy.active = false;
}
break;
}
case "Extension:FlushJarCache": {
ExtensionUtils.flushJarCache(data.path);
@@ -425,16 +370,22 @@ function ExtensionProcessScript() {
}
ExtensionProcessScript.singleton = null;
ExtensionProcessScript.prototype = {
classID: Components.ID("{21f9819e-4cdf-49f9-85a0-850af91a5058}"),
QueryInterface: XPCOMUtils.generateQI([Ci.mozIExtensionProcessScript]),
+ initExtensionDocument(policy, doc) {
+ if (DocumentManager.globals.has(getMessageManager(doc.defaultView))) {
+ DocumentManager.loadInto(policy, doc.defaultView);
+ }
+ },
+
preloadContentScript(contentScript) {
contentScripts.get(contentScript).preload();
},
loadContentScript(contentScript, window) {
if (DocumentManager.globals.has(getMessageManager(window))) {
contentScripts.get(contentScript).injectInto(window);
}
--- a/toolkit/components/extensions/moz.build
+++ b/toolkit/components/extensions/moz.build
@@ -82,8 +82,10 @@ MOCHITEST_MANIFESTS += [
'test/mochitest/mochitest.ini'
]
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
XPCSHELL_TESTS_MANIFESTS += [
'test/xpcshell/native_messaging.ini',
'test/xpcshell/xpcshell-remote.ini',
'test/xpcshell/xpcshell.ini',
]
+
+include('/ipc/chromium/chromium-config.mozbuild')
--- a/toolkit/components/extensions/mozIExtensionProcessScript.idl
+++ b/toolkit/components/extensions/mozIExtensionProcessScript.idl
@@ -1,16 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIDOMWindowProxy;
+interface nsIDOMDocument;
[scriptable,uuid(6b09dc51-6caa-4ca7-9d6d-30c87258a630)]
interface mozIExtensionProcessScript : nsISupports
{
void preloadContentScript(in nsISupports contentScript);
void loadContentScript(in nsISupports contentScript, in mozIDOMWindowProxy window);
+ void initExtensionDocument(in nsISupports extension, in nsIDOMDocument doc);
};