--- a/toolkit/components/build/nsToolkitCompsCID.h
+++ b/toolkit/components/build/nsToolkitCompsCID.h
@@ -181,8 +181,14 @@
#define NS_APPLICATION_REPUTATION_SERVICE_CID \
{ 0x8576c950, 0xf4a2, 0x11e2, { 0xb7, 0x78, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
#define NS_ADDONCONTENTPOLICY_CID \
{ 0xc26a8241, 0xecf4, 0x4aed, { 0x9f, 0x3c, 0xf1, 0xf5, 0xc7, 0x13, 0xb9, 0xa5 } }
#define NS_ADDON_PATH_SERVICE_CID \
{ 0xa39f39d0, 0xdfb6, 0x11e3, { 0x8b, 0x68, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
+
+#define NS_ADDON_POLICY_SERVICE_CID \
+{ 0x562de129, 0x8338, 0x482c, { 0xbb, 0x96, 0xa1, 0xff, 0x09, 0xee, 0x53, 0xcc } }
+
+#define NS_ADDON_POLICY_SERVICE_CONTRACTID \
+ "@mozilla.org/addons/policy-service;1"
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -32,16 +32,17 @@
#include "nsUrlClassifierPrefixSet.h"
#include "nsBrowserStatusFilter.h"
#include "mozilla/FinalizationWitnessService.h"
#include "mozilla/NativeOSFileInternals.h"
#include "mozilla/AddonContentPolicy.h"
#include "mozilla/AddonManagerStartup.h"
#include "mozilla/AddonPathService.h"
+#include "mozilla/ExtensionPolicyService.h"
#if defined(XP_WIN)
#include "NativeFileWatcherWin.h"
#else
#include "NativeFileWatcherNotSupported.h"
#endif // (XP_WIN)
#include "nsWebRequestListener.h"
@@ -121,16 +122,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateP
#endif
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(FinalizationWitnessService, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(AddonContentPolicy)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonPathService, AddonPathService::GetInstance)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonManagerStartup, AddonManagerStartup::GetInstance)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ExtensionPolicyService, ExtensionPolicyService::GetInstance)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebRequestListener)
NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID);
#if defined(MOZ_HAS_PERFSTATS)
NS_DEFINE_NAMED_CID(NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID);
#endif // defined (MOZ_HAS_PERFSTATS)
@@ -156,16 +158,17 @@ NS_DEFINE_NAMED_CID(NS_BROWSERSTATUSFILT
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID);
#endif
NS_DEFINE_NAMED_CID(FINALIZATIONWITNESSSERVICE_CID);
NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERNALS_SERVICE_CID);
NS_DEFINE_NAMED_CID(NS_ADDONCONTENTPOLICY_CID);
NS_DEFINE_NAMED_CID(NS_ADDON_PATH_SERVICE_CID);
NS_DEFINE_NAMED_CID(NS_ADDON_MANAGER_STARTUP_CID);
+NS_DEFINE_NAMED_CID(NS_ADDON_POLICY_SERVICE_CID);
NS_DEFINE_NAMED_CID(NATIVE_FILEWATCHER_SERVICE_CID);
NS_DEFINE_NAMED_CID(NS_WEBREQUESTLISTENER_CID);
static const Module::CIDEntry kToolkitCIDs[] = {
{ &kNS_TOOLKIT_APPSTARTUP_CID, false, nullptr, nsAppStartupConstructor },
#if defined(MOZ_HAS_TERMINATOR)
{ &kNS_TOOLKIT_TERMINATOR_CID, false, nullptr, nsTerminatorConstructor },
#endif
@@ -191,16 +194,17 @@ static const Module::CIDEntry kToolkitCI
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
{ &kNS_UPDATEPROCESSOR_CID, false, nullptr, nsUpdateProcessorConstructor },
#endif
{ &kFINALIZATIONWITNESSSERVICE_CID, false, nullptr, FinalizationWitnessServiceConstructor },
{ &kNATIVE_OSFILE_INTERNALS_SERVICE_CID, false, nullptr, NativeOSFileInternalsServiceConstructor },
{ &kNS_ADDONCONTENTPOLICY_CID, false, nullptr, AddonContentPolicyConstructor },
{ &kNS_ADDON_PATH_SERVICE_CID, false, nullptr, AddonPathServiceConstructor },
{ &kNS_ADDON_MANAGER_STARTUP_CID, false, nullptr, AddonManagerStartupConstructor },
+ { &kNS_ADDON_POLICY_SERVICE_CID, false, nullptr, ExtensionPolicyServiceConstructor },
{ &kNATIVE_FILEWATCHER_SERVICE_CID, false, nullptr, NativeFileWatcherServiceConstructor },
{ &kNS_WEBREQUESTLISTENER_CID, false, nullptr, nsWebRequestListenerConstructor },
{ nullptr }
};
static const Module::ContractIDEntry kToolkitContracts[] = {
{ NS_APPSTARTUP_CONTRACTID, &kNS_TOOLKIT_APPSTARTUP_CID },
#if defined(MOZ_HAS_TERMINATOR)
@@ -229,16 +233,17 @@ static const Module::ContractIDEntry kTo
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
{ NS_UPDATEPROCESSOR_CONTRACTID, &kNS_UPDATEPROCESSOR_CID },
#endif
{ FINALIZATIONWITNESSSERVICE_CONTRACTID, &kFINALIZATIONWITNESSSERVICE_CID },
{ NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID, &kNATIVE_OSFILE_INTERNALS_SERVICE_CID },
{ NS_ADDONCONTENTPOLICY_CONTRACTID, &kNS_ADDONCONTENTPOLICY_CID },
{ NS_ADDONPATHSERVICE_CONTRACTID, &kNS_ADDON_PATH_SERVICE_CID },
{ NS_ADDONMANAGERSTARTUP_CONTRACTID, &kNS_ADDON_MANAGER_STARTUP_CID },
+ { NS_ADDON_POLICY_SERVICE_CONTRACTID, &kNS_ADDON_POLICY_SERVICE_CID },
{ NATIVE_FILEWATCHER_SERVICE_CONTRACTID, &kNATIVE_FILEWATCHER_SERVICE_CID },
{ NS_WEBREQUESTLISTENER_CONTRACTID, &kNS_WEBREQUESTLISTENER_CID },
{ nullptr }
};
static const mozilla::Module::CategoryEntry kToolkitCategories[] = {
{ "content-policy", NS_ADDONCONTENTPOLICY_CONTRACTID, NS_ADDONCONTENTPOLICY_CONTRACTID },
{ nullptr }
--- a/toolkit/components/extensions/.eslintrc.js
+++ b/toolkit/components/extensions/.eslintrc.js
@@ -8,16 +8,17 @@ module.exports = {
"Cr": true,
"Cu": true,
"TextDecoder": false,
"TextEncoder": false,
"MatchGlob": false,
"MatchPattern": true,
"MatchPatternSet": false,
+ "WebExtensionPolicy": false,
// Specific to WebExtensions:
"AppConstants": true,
"Extension": true,
"ExtensionAPI": true,
"ExtensionManagement": true,
"ExtensionUtils": true,
"extensions": true,
"getContainerForCookieStoreId": true,
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -752,16 +752,19 @@ this.Extension = class extends Extension
}
let origins = permissions.origins.map(
origin => new MatchPattern(origin, {ignorePath: true}).pattern);
this.whiteListedHosts = new MatchPatternSet(
this.whiteListedHosts.patterns
.filter(host => !origins.includes(host.pattern)));
+
+ this.policy.permissions = Array.from(this.permissions);
+ this.policy.allowedOrigins = this.whiteListedHosts;
});
/* eslint-enable mozilla/balanced-listeners */
}
static generateXPI(data) {
return ExtensionTestCommon.generateXPI(data);
}
@@ -1018,17 +1021,17 @@ this.Extension = class extends Extension
Management.emit("ready", this);
this.emit("ready");
} catch (e) {
dump(`Extension error: ${e.message} ${e.filename || e.fileName}:${e.lineNumber} :: ${e.stack || new Error().stack}\n`);
Cu.reportError(e);
if (this.started) {
this.started = false;
- ExtensionManagement.shutdownExtension(this.uuid);
+ ExtensionManagement.shutdownExtension(this);
}
this.cleanupGeneratedFile();
throw e;
}
this.startupPromise = null;
@@ -1075,17 +1078,17 @@ this.Extension = class extends Extension
}
let data = Services.ppmm.initialProcessData;
data["Extension:Extensions"] = data["Extension:Extensions"].filter(e => e.id !== this.id);
Services.ppmm.removeMessageListener(this.MESSAGE_EMIT_EVENT, this);
if (!this.manifest) {
- ExtensionManagement.shutdownExtension(this.uuid);
+ ExtensionManagement.shutdownExtension(this);
this.cleanupGeneratedFile();
return;
}
GlobalManager.uninit(this);
for (let obj of this.onShutdown) {
@@ -1100,17 +1103,17 @@ this.Extension = class extends Extension
Management.emit("shutdown", this);
this.emit("shutdown");
Services.ppmm.broadcastAsyncMessage("Extension:Shutdown", {id: this.id});
MessageChannel.abortResponses({extensionId: this.id});
- ExtensionManagement.shutdownExtension(this.uuid);
+ ExtensionManagement.shutdownExtension(this);
return this.cleanupGeneratedFile();
}
observe(subject, topic, data) {
if (topic === "xpcom-shutdown") {
this.cleanupGeneratedFile();
}
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -522,16 +522,21 @@ class BrowserExtensionContent extends Ev
if (permissions.origins.length > 0) {
let origins = permissions.origins.map(
origin => new MatchPattern(origin, {ignorePath: true}).pattern);
this.whiteListedHosts = new MatchPatternSet(
this.whiteListedHosts.patterns
.filter(host => !origins.includes(host.pattern)));
}
+
+ if (this.policy) {
+ this.policy.permissions = Array.from(this.permissions);
+ this.policy.allowedOrigins = this.whiteListedHosts;
+ }
});
/* eslint-enable mozilla/balanced-listeners */
ExtensionManager.extensions.set(this.id, this);
}
shutdown() {
ExtensionManager.extensions.delete(this.id);
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -11,31 +11,25 @@ const Cc = Components.classes;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
"resource:///modules/E10SUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
- "resource://gre/modules/ExtensionUtils.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionUtils.getConsole());
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;
-var ExtensionManagement;
-
/*
* This file should be kept short and simple since it's loaded even
* when no extensions are running.
*/
var APIs = {
apis: new Map(),
@@ -60,147 +54,61 @@ function getURLForExtension(id, path = "
let uuid = UUIDMap.get(id, false);
if (!uuid) {
Cu.reportError(`Called getURLForExtension on unmapped extension ${id}`);
return null;
}
return `moz-extension://${uuid}/${path}`;
}
-// This object manages various platform-level issues related to
-// moz-extension:// URIs. It lives here so that it can be used in both
-// the parent and child processes.
-//
-// moz-extension URIs have the form moz-extension://uuid/path. Each
-// extension has its own UUID, unique to the machine it's installed
-// on. This is easier and more secure than using the extension ID,
-// since it makes it slightly harder to fingerprint for extensions if
-// each user uses different URIs for the extension.
-var Service = {
- initialized: false,
-
- // Map[uuid -> extension].
- // extension can be an Extension (parent process) or BrowserExtensionContent (child process).
- uuidMap: new Map(),
-
- init() {
- let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService);
- aps = aps.wrappedJSObject;
- this.aps = aps;
- aps.setExtensionURILoadCallback(this.extensionURILoadableByAnyone.bind(this));
- aps.setExtensionURIToAddonIdCallback(this.extensionURIToAddonID.bind(this));
- },
-
- // Called when a new extension is loaded.
- startupExtension(uuid, uri, extension) {
- if (!this.initialized) {
- this.initialized = true;
- this.init();
- }
-
- // Create the moz-extension://uuid mapping.
- let handler = Services.io.getProtocolHandler("moz-extension");
- handler.QueryInterface(Ci.nsISubstitutingProtocolHandler);
- handler.setSubstitution(uuid, uri);
-
- this.uuidMap.set(uuid, extension);
- this.aps.setAddonHasPermissionCallback(extension.id, extension.hasPermission.bind(extension));
- this.aps.setAddonLoadURICallback(extension.id, this.checkAddonMayLoad.bind(this, extension));
- this.aps.setAddonLocalizeCallback(extension.id, extension.localize.bind(extension));
- this.aps.setAddonCSP(extension.id, extension.manifest.content_security_policy);
- this.aps.setBackgroundPageUrlCallback(uuid, this.generateBackgroundPageUrl.bind(this, extension));
- },
-
- // Called when an extension is unloaded.
- shutdownExtension(uuid) {
- let extension = this.uuidMap.get(uuid);
- if (!extension) {
- return;
- }
- this.uuidMap.delete(uuid);
- this.aps.setAddonHasPermissionCallback(extension.id, null);
- this.aps.setAddonLoadURICallback(extension.id, null);
- this.aps.setAddonLocalizeCallback(extension.id, null);
- this.aps.setAddonCSP(extension.id, null);
- this.aps.setBackgroundPageUrlCallback(uuid, null);
-
- let handler = Services.io.getProtocolHandler("moz-extension");
- handler.QueryInterface(Ci.nsISubstitutingProtocolHandler);
- handler.setSubstitution(uuid, null);
- },
-
- // Return true if the given URI can be loaded from arbitrary web
- // content. The manifest.json |web_accessible_resources| directive
- // determines this.
- extensionURILoadableByAnyone(uri) {
- let uuid = uri.host;
- let extension = this.uuidMap.get(uuid);
- if (!extension || !extension.webAccessibleResources) {
- return false;
- }
-
- let path = uri.QueryInterface(Ci.nsIURL).filePath;
- return extension.webAccessibleResources.some(res => res.matches(path));
- },
-
- // Checks whether a given extension can load this URI (typically via
- // an XML HTTP request). The manifest.json |permissions| directive
- // determines this.
- checkAddonMayLoad(extension, uri, explicit = false) {
- return extension.whiteListedHosts.matchesIgnoringPath(uri, explicit);
- },
-
- generateBackgroundPageUrl(extension) {
- let background_scripts = (extension.manifest.background &&
- extension.manifest.background.scripts);
-
- if (!background_scripts) {
- return;
- }
-
- let html = "<!DOCTYPE html>\n<html>\n<body>\n";
- for (let script of background_scripts) {
- script = script.replace(/"/g, """);
- html += `<script src="${script}"></script>\n`;
- }
- html += "</body>\n</html>\n";
-
- return "data:text/html;charset=utf-8," + encodeURIComponent(html);
- },
-
- // Finds the add-on ID associated with a given moz-extension:// URI.
- // This is used to set the addonId on the for the nsIPrincipal
- // attached to the URI.
- extensionURIToAddonID(uri) {
- let uuid = uri.host;
- let extension = this.uuidMap.get(uuid);
- return extension ? extension.id : undefined;
- },
-};
-
let cacheInvalidated = 0;
function onCacheInvalidate() {
cacheInvalidated++;
}
Services.obs.addObserver(onCacheInvalidate, "startupcache-invalidate");
-ExtensionManagement = {
+var ExtensionManagement = {
get cacheInvalidated() {
return cacheInvalidated;
},
get isExtensionProcess() {
if (this.useRemoteWebExtensions) {
return appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE;
}
return isParentProcess;
},
- startupExtension: Service.startupExtension.bind(Service),
- shutdownExtension: Service.shutdownExtension.bind(Service),
+ // Called when a new extension is loaded.
+ startupExtension(uuid, uri, extension) {
+ let policy = new WebExtensionPolicy({
+ id: extension.id,
+ mozExtensionHostname: uuid,
+ baseURL: uri.spec,
+
+ permissions: Array.from(extension.permissions || []),
+ allowedOrigins: extension.whiteListedHosts,
+ webAccessibleResources: extension.webAccessibleResources || [],
+
+ contentSecurityPolicy: extension.manifest.content_security_policy,
+
+ localizeCallback: extension.localize.bind(extension),
+
+ backgroundScripts: (extension.manifest.background &&
+ extension.manifest.background.scripts),
+ });
+
+ extension.policy = policy;
+ policy.active = true;
+ },
+
+ // Called when an extension is unloaded.
+ shutdownExtension(extension) {
+ extension.policy.active = false;
+ },
registerAPI: APIs.register.bind(APIs),
unregisterAPI: APIs.unregister.bind(APIs),
getURLForExtension,
APIs,
};
--- a/toolkit/components/extensions/ExtensionPolicyService.cpp
+++ b/toolkit/components/extensions/ExtensionPolicyService.cpp
@@ -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 "mozilla/ExtensionPolicyService.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
+
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
+#include "nsEscape.h"
#include "nsGkAtoms.h"
namespace mozilla {
using namespace extensions;
#define DEFAULT_BASE_CSP \
"script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; " \
@@ -104,18 +106,115 @@ ExtensionPolicyService::DefaultCSP(nsASt
nsresult rv;
rv = Preferences::GetString("extensions.webextensions.default-content-security-policy", &aDefaultCSP);
if (NS_FAILED(rv)) {
aDefaultCSP.AssignLiteral(DEFAULT_DEFAULT_CSP);
}
}
+
+/*****************************************************************************
+ * nsIAddonPolicyService
+ *****************************************************************************/
+
+nsresult
+ExtensionPolicyService::GetBaseCSP(nsAString& aBaseCSP)
+{
+ BaseCSP(aBaseCSP);
+ return NS_OK;
+}
+
+nsresult
+ExtensionPolicyService::GetDefaultCSP(nsAString& aDefaultCSP)
+{
+ DefaultCSP(aDefaultCSP);
+ return NS_OK;
+}
+
+nsresult
+ExtensionPolicyService::GetAddonCSP(const nsAString& aAddonId,
+ nsAString& aResult)
+{
+ if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
+ policy->GetContentSecurityPolicy(aResult);
+ return NS_OK;
+ }
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult
+ExtensionPolicyService::GetGeneratedBackgroundPageUrl(const nsACString& aHostname,
+ nsACString& aResult)
+{
+ if (WebExtensionPolicy* policy = GetByHost(aHostname)) {
+ nsAutoCString url("data:text/html,");
+
+ nsCString html = policy->BackgroundPageHTML();
+ nsAutoCString escaped;
+
+ url.Append(NS_EscapeURL(html, esc_Minimal, escaped));
+
+ aResult = url;
+ return NS_OK;
+ }
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult
+ExtensionPolicyService::AddonHasPermission(const nsAString& aAddonId,
+ const nsAString& aPerm,
+ bool* aResult)
+{
+ if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
+ *aResult = policy->HasPermission(aPerm);
+ return NS_OK;
+ }
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult
+ExtensionPolicyService::AddonMayLoadURI(const nsAString& aAddonId,
+ nsIURI* aURI,
+ bool aExplicit,
+ bool* aResult)
+{
+ if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
+ *aResult = policy->CanAccessURI(aURI, aExplicit);
+ return NS_OK;
+ }
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult
+ExtensionPolicyService::ExtensionURILoadableByAnyone(nsIURI* aURI, bool* aResult)
+{
+ URLInfo url(aURI);
+ if (WebExtensionPolicy* policy = GetByURL(url)) {
+ *aResult = policy->IsPathWebAccessible(url.Path());
+ return NS_OK;
+ }
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult
+ExtensionPolicyService::ExtensionURIToAddonId(nsIURI* aURI, nsAString& aResult)
+{
+ if (WebExtensionPolicy* policy = GetByURL(aURI)) {
+ policy->GetId(aResult);
+ } else {
+ aResult.SetIsVoid(true);
+ }
+ return NS_OK;
+}
+
+
NS_IMPL_CYCLE_COLLECTION(ExtensionPolicyService, mExtensions, mExtensionHosts)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtensionPolicyService)
+ NS_INTERFACE_MAP_ENTRY(nsIAddonPolicyService)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ExtensionPolicyService)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ExtensionPolicyService)
} // namespace mozilla
--- a/toolkit/components/extensions/ExtensionPolicyService.h
+++ b/toolkit/components/extensions/ExtensionPolicyService.h
@@ -5,33 +5,41 @@
#ifndef mozilla_ExtensionPolicyService_h
#define mozilla_ExtensionPolicyService_h
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"
+#include "nsIAddonPolicyService.h"
#include "nsIAtom.h"
#include "nsISupports.h"
#include "nsPointerHashKeys.h"
#include "nsRefPtrHashtable.h"
namespace mozilla {
using extensions::WebExtensionPolicy;
-class ExtensionPolicyService final : public nsISupports
+class ExtensionPolicyService final : public nsIAddonPolicyService
{
public:
NS_DECL_CYCLE_COLLECTION_CLASS(ExtensionPolicyService)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIADDONPOLICYSERVICE
static ExtensionPolicyService& GetSingleton();
+ static already_AddRefed<ExtensionPolicyService> GetInstance()
+ {
+ RefPtr<ExtensionPolicyService> service = &GetSingleton();
+ return service.forget();
+ }
+
WebExtensionPolicy*
GetByID(const nsIAtom* aAddonId)
{
return mExtensions.GetWeak(aAddonId);
}
WebExtensionPolicy* GetByID(const nsAString& aAddonId)
{
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -552,17 +552,17 @@ class StubExtension {
if (isContentProcess) {
let uri = Services.io.newURI(this.data.resourceURL);
ExtensionManagement.startupExtension(this.uuid, uri, this);
}
}
shutdown() {
if (isContentProcess) {
- ExtensionManagement.shutdownExtension(this.uuid);
+ ExtensionManagement.shutdownExtension(this);
}
if (this._realExtension) {
this._realExtension.shutdown();
}
}
// Lazily create the real extension object when needed.
get realExtension() {
--- a/toolkit/components/extensions/test/xpcshell/test_csp_custom_policies.js
+++ b/toolkit/components/extensions/test/xpcshell/test_csp_custom_policies.js
@@ -2,37 +2,55 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
Cu.import("resource://gre/modules/Preferences.jsm");
const ADDON_ID = "test@web.extension";
const aps = Cc["@mozilla.org/addons/policy-service;1"]
- .getService(Ci.nsIAddonPolicyService).wrappedJSObject;
+ .getService(Ci.nsIAddonPolicyService);
+
+let policy = null;
+
+function setAddonCSP(csp) {
+ if (policy) {
+ policy.active = false;
+ }
+
+ policy = new WebExtensionPolicy({
+ id: ADDON_ID,
+ mozExtensionHostname: ADDON_ID,
+ baseURL: "file:///",
+
+ allowedOrigins: new MatchPatternSet([]),
+ localizeCallback() {},
+
+ contentSecurityPolicy: csp,
+ });
+
+ policy.active = true;
+}
do_register_cleanup(() => {
- aps.setAddonCSP(ADDON_ID, null);
+ policy.active = false;
});
add_task(function* test_addon_csp() {
equal(aps.baseCSP, Preferences.get("extensions.webextensions.base-content-security-policy"),
"Expected base CSP value");
equal(aps.defaultCSP, Preferences.get("extensions.webextensions.default-content-security-policy"),
"Expected default CSP value");
- equal(aps.getAddonCSP(ADDON_ID), aps.defaultCSP,
- "CSP for unknown add-on ID should be the default CSP");
-
const CUSTOM_POLICY = "script-src: 'self' https://xpcshell.test.custom.csp; object-src: 'none'";
- aps.setAddonCSP(ADDON_ID, CUSTOM_POLICY);
+ setAddonCSP(CUSTOM_POLICY);
equal(aps.getAddonCSP(ADDON_ID), CUSTOM_POLICY, "CSP should point to add-on's custom policy");
- aps.setAddonCSP(ADDON_ID, null);
+ setAddonCSP(null);
equal(aps.getAddonCSP(ADDON_ID), aps.defaultCSP,
"CSP should revert to default when set to null");
});
--- a/toolkit/components/extensions/test/xpcshell/test_locale_converter.js
+++ b/toolkit/components/extensions/test/xpcshell/test_locale_converter.js
@@ -18,32 +18,32 @@ function StringStream(string) {
stream.data = string;
return stream;
}
// Initialize the policy service with a stub localizer for our
// add-on ID.
add_task(async function init() {
- const aps = Cc["@mozilla.org/addons/policy-service;1"]
- .getService(Ci.nsIAddonPolicyService).wrappedJSObject;
+ let policy = new WebExtensionPolicy({
+ id: ADDON_ID,
+ mozExtensionHostname: UUID,
+ baseURL: "file:///",
- let oldCallback = aps.setExtensionURIToAddonIdCallback(uri => {
- if (uri.host == UUID) {
- return ADDON_ID;
- }
+ allowedOrigins: new MatchPatternSet([]),
+
+ localizeCallback(string) {
+ return string.replace(/__MSG_(.*?)__/g, "<localized-$1>");
+ },
});
- aps.setAddonLocalizeCallback(ADDON_ID, string => {
- return string.replace(/__MSG_(.*?)__/g, "<localized-$1>");
- });
+ policy.active = true;
do_register_cleanup(() => {
- aps.setExtensionURIToAddonIdCallback(oldCallback);
- aps.setAddonLocalizeCallback(ADDON_ID, null);
+ policy.active = false;
});
});
// Test that the synchronous converter works as expected with a
// simple string.
add_task(async function testSynchronousConvert() {
let stream = StringStream("Foo __MSG_xxx__ bar __MSG_yyy__ baz");
--- a/toolkit/components/utils/simpleServices.js
+++ b/toolkit/components/utils/simpleServices.js
@@ -12,224 +12,33 @@
"use strict";
const Cc = Components.classes;
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cr = Components.results;
+/* globals WebExtensionPolicy */
+
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
-function AddonPolicyService() {
- this.wrappedJSObject = this;
- this.cspStrings = new Map();
- this.backgroundPageUrlCallbacks = new Map();
- this.checkHasPermissionCallbacks = new Map();
- this.mayLoadURICallbacks = new Map();
- this.localizeCallbacks = new Map();
-
- XPCOMUtils.defineLazyPreferenceGetter(
- this, "baseCSP", "extensions.webextensions.base-content-security-policy",
- "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; " +
- "object-src 'self' https://* moz-extension: blob: filesystem:;");
-
- XPCOMUtils.defineLazyPreferenceGetter(
- this, "defaultCSP", "extensions.webextensions.default-content-security-policy",
- "script-src 'self'; object-src 'self';");
-}
-
-AddonPolicyService.prototype = {
- classID: Components.ID("{89560ed3-72e3-498d-a0e8-ffe50334d7c5}"),
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonPolicyService]),
-
- /**
- * Returns the content security policy which applies to documents belonging
- * to the extension with the given ID. This may be either a custom policy,
- * if one was supplied, or the default policy if one was not.
- */
- getAddonCSP(aAddonId) {
- let csp = this.cspStrings.get(aAddonId);
- return csp || this.defaultCSP;
- },
-
- /**
- * Returns the generated background page as a data-URI, if any. If the addon
- * does not have an auto-generated background page, an empty string is
- * returned.
- */
- getGeneratedBackgroundPageUrl(aAddonId) {
- let cb = this.backgroundPageUrlCallbacks.get(aAddonId);
- return cb && cb(aAddonId) || "";
- },
-
- /*
- * Invokes a callback (if any) associated with the addon to determine whether
- * the addon is granted the |aPerm| API permission.
- *
- * @see nsIAddonPolicyService.addonHasPermission
- */
- addonHasPermission(aAddonId, aPerm) {
- let cb = this.checkHasPermissionCallbacks.get(aAddonId);
- return cb ? cb(aPerm) : false;
- },
-
- /*
- * Invokes a callback (if any) associated with the addon to determine whether
- * unprivileged code running within the addon is allowed to perform loads from
- * the given URI.
- *
- * @see nsIAddonPolicyService.addonMayLoadURI
- */
- addonMayLoadURI(aAddonId, aURI, aExplicit = false) {
- let cb = this.mayLoadURICallbacks.get(aAddonId);
- return cb ? cb(aURI, aExplicit) : false;
- },
-
- /*
- * Invokes a callback (if any) associated with the addon to loclaize a
- * resource belonging to that add-on.
- */
- localizeAddonString(aAddonId, aString) {
- let cb = this.localizeCallbacks.get(aAddonId);
- return cb ? cb(aString) : aString;
- },
-
- /*
- * Invokes a callback (if any) to determine if an extension URI should be
- * web-accessible.
- *
- * @see nsIAddonPolicyService.extensionURILoadableByAnyone
- */
- extensionURILoadableByAnyone(aURI) {
- if (aURI.scheme != "moz-extension") {
- throw new TypeError("non-extension URI passed");
- }
-
- let cb = this.extensionURILoadCallback;
- return cb ? cb(aURI) : false;
- },
-
- /*
- * Maps an extension URI to an addon ID.
- *
- * @see nsIAddonPolicyService.extensionURIToAddonId
- */
- extensionURIToAddonId(aURI) {
- if (aURI.scheme != "moz-extension") {
- throw new TypeError("non-extension URI passed");
- }
-
- let cb = this.extensionURIToAddonIdCallback;
- if (!cb) {
- throw new Error("no callback set to map extension URIs to addon Ids");
- }
- return cb(aURI);
- },
-
- /*
- * Sets the callbacks used in addonHasPermission above. Not accessible over
- * XPCOM - callers should use .wrappedJSObject on the service to call it
- * directly.
- */
- setAddonHasPermissionCallback(aAddonId, aCallback) {
- if (aCallback) {
- this.checkHasPermissionCallbacks.set(aAddonId, aCallback);
- } else {
- this.checkHasPermissionCallbacks.delete(aAddonId);
- }
- },
-
- /*
- * Sets the callbacks used in addonMayLoadURI above. Not accessible over
- * XPCOM - callers should use .wrappedJSObject on the service to call it
- * directly.
- */
- setAddonLoadURICallback(aAddonId, aCallback) {
- if (aCallback) {
- this.mayLoadURICallbacks.set(aAddonId, aCallback);
- } else {
- this.mayLoadURICallbacks.delete(aAddonId);
- }
- },
-
- /*
- * Sets the custom CSP string to be used for the add-on. Not accessible over
- * XPCOM - callers should use .wrappedJSObject on the service to call it
- * directly.
- */
- setAddonCSP(aAddonId, aCSPString) {
- if (aCSPString) {
- this.cspStrings.set(aAddonId, aCSPString);
- } else {
- this.cspStrings.delete(aAddonId);
- }
- },
-
- /**
- * Set the callback that generates a data-URL for the background page.
- */
- setBackgroundPageUrlCallback(aAddonId, aCallback) {
- if (aCallback) {
- this.backgroundPageUrlCallbacks.set(aAddonId, aCallback);
- } else {
- this.backgroundPageUrlCallbacks.delete(aAddonId);
- }
- },
-
- /*
- * Sets the callbacks used by the stream converter service to localize
- * add-on resources.
- */
- setAddonLocalizeCallback(aAddonId, aCallback) {
- if (aCallback) {
- this.localizeCallbacks.set(aAddonId, aCallback);
- } else {
- this.localizeCallbacks.delete(aAddonId);
- }
- },
-
- /*
- * Sets the callback used in extensionURILoadableByAnyone above. Not
- * accessible over XPCOM - callers should use .wrappedJSObject on the
- * service to call it directly.
- */
- setExtensionURILoadCallback(aCallback) {
- var old = this.extensionURILoadCallback;
- this.extensionURILoadCallback = aCallback;
- return old;
- },
-
- /*
- * Sets the callback used in extensionURIToAddonId above. Not accessible over
- * XPCOM - callers should use .wrappedJSObject on the service to call it
- * directly.
- */
- setExtensionURIToAddonIdCallback(aCallback) {
- var old = this.extensionURIToAddonIdCallback;
- this.extensionURIToAddonIdCallback = aCallback;
- return old;
- }
-};
-
/*
* This class provides a stream filter for locale messages in CSS files served
* by the moz-extension: protocol handler.
*
* See SubstituteChannel in netwerk/protocol/res/ExtensionProtocolHandler.cpp
* for usage.
*/
function AddonLocalizationConverter() {
- this.aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService)
- .wrappedJSObject;
}
AddonLocalizationConverter.prototype = {
classID: Components.ID("{ded150e3-c92e-4077-a396-0dba9953e39f}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamConverter]),
FROM_TYPE: "application/vnd.mozilla.webext.unlocalized",
TO_TYPE: "text/css",
@@ -241,71 +50,70 @@ AddonLocalizationConverter.prototype = {
}
if (aToType != this.TO_TYPE) {
throw Components.Exception("Invalid aToType value", Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller.caller);
}
},
// aContext must be a nsIURI object for a valid moz-extension: URL.
- getAddonId(aContext) {
+ getAddon(aContext) {
// In this case, we want the add-on ID even if the URL is web accessible,
// so check the root rather than the exact path.
let uri = Services.io.newURI("/", null, aContext);
- let id = this.aps.extensionURIToAddonId(uri);
- if (id == undefined) {
+ let addon = WebExtensionPolicy.getByURI(uri);
+ if (!addon) {
throw new Components.Exception("Invalid context", Cr.NS_ERROR_INVALID_ARG);
}
- return id;
+ return addon;
},
- convertToStream(aAddonId, aString) {
+ convertToStream(aAddon, aString) {
let stream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
- stream.data = this.aps.localizeAddonString(aAddonId, aString);
+ stream.data = aAddon.localize(aString);
return stream;
},
convert(aStream, aFromType, aToType, aContext) {
this.checkTypes(aFromType, aToType);
- let addonId = this.getAddonId(aContext);
+ let addon = this.getAddon(aContext);
let string = (
aStream.available() ?
NetUtil.readInputStreamToString(aStream, aStream.available()) : ""
);
- return this.convertToStream(addonId, string);
+ return this.convertToStream(addon, string);
},
asyncConvertData(aFromType, aToType, aListener, aContext) {
this.checkTypes(aFromType, aToType);
- this.addonId = this.getAddonId(aContext);
+ this.addon = this.getAddon(aContext);
this.listener = aListener;
},
onStartRequest(aRequest, aContext) {
this.parts = [];
},
onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount) {
this.parts.push(NetUtil.readInputStreamToString(aInputStream, aCount));
},
onStopRequest(aRequest, aContext, aStatusCode) {
try {
this.listener.onStartRequest(aRequest, null);
if (Components.isSuccessCode(aStatusCode)) {
let string = this.parts.join("");
- let stream = this.convertToStream(this.addonId, string);
+ let stream = this.convertToStream(this.addon, string);
this.listener.onDataAvailable(aRequest, null, stream, 0, stream.data.length);
}
} catch (e) {
aStatusCode = e.result || Cr.NS_ERROR_FAILURE;
}
this.listener.onStopRequest(aRequest, null, aStatusCode);
},
};
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AddonPolicyService,
- AddonLocalizationConverter]);
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AddonLocalizationConverter]);
--- a/toolkit/components/utils/utils.manifest
+++ b/toolkit/components/utils/utils.manifest
@@ -1,6 +1,4 @@
component {dfd07380-6083-11e4-9803-0800200c9a66} simpleServices.js
contract @mozilla.org/addons/remote-tag-service;1 {dfd07380-6083-11e4-9803-0800200c9a66}
-component {89560ed3-72e3-498d-a0e8-ffe50334d7c5} simpleServices.js
-contract @mozilla.org/addons/policy-service;1 {89560ed3-72e3-498d-a0e8-ffe50334d7c5}
component {ded150e3-c92e-4077-a396-0dba9953e39f} simpleServices.js
contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.webext.unlocalized&to=text/css {ded150e3-c92e-4077-a396-0dba9953e39f}