Files
tubestation/toolkit/components/extensions/ExtensionPolicyService.cpp
Nika Layzell 44d99735f2 Bug 1906734 - Switch WebExtension resources to use only static protocol flags, r=extension-reviewers,necko-reviewers,kershaw,robwu
Previously, the WebExtension protocol used dynamic protocol flags which
were based on the WebExtension policy in order to enforce things such as
availability in private browsing and the accessibility of certain
resources.

Since the shift to MV3, these checks have required more complex checks
than what was possible to specify with protocol flags, which required
the addition of WEBEXT_URI_WEB_ACCESSIBLE - a security flag which would
trigger further checks with the EPS to determine if the URI can be
loaded.

This was somewhat inefficient, as fetching the URI flags would require
looking up the policy each time dynamic flags were looked up, as well as
when policy specifics were being checked after loading flags. In
addition, it lead to a number of flags which were very specific to
extension protocols.

This patch changes extensions to no longer have dynamic flags, instead
specifying the static `URI_IS_WEBEXTENSION_RESOURCE` security flag. When
this flag is specified, security checks are made by querying the
ExtensionPolicyService to ask if the load should be permitted, combining
the specific security checks for Extension resources into a simpler
code-path, and avoids redundant checks.

Differential Revision: https://phabricator.services.mozilla.com/D216076
2024-07-17 16:02:34 +00:00

773 lines
25 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* 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/DocumentObserver.h"
#include "mozilla/extensions/WebExtensionContentScript.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/Services.h"
#include "mozilla/SimpleEnumerator.h"
#include "mozilla/StaticPrefs_extensions.h"
#include "mozilla/Try.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h"
#include "mozIExtensionProcessScript.h"
#include "nsEscape.h"
#include "nsGkAtoms.h"
#include "nsHashKeys.h"
#include "nsIChannel.h"
#include "nsIContentPolicy.h"
#include "mozilla/dom/Document.h"
#include "nsGlobalWindowInner.h"
#include "nsILoadInfo.h"
#include "nsIXULRuntime.h"
#include "nsImportModule.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
#include "nsPIDOMWindow.h"
#include "nsXULAppAPI.h"
#include "nsQueryObject.h"
namespace mozilla {
using namespace extensions;
using dom::AutoJSAPI;
using dom::Document;
using dom::Promise;
#define DEFAULT_CSP_PREF \
"extensions.webextensions.default-content-security-policy"
#define DEFAULT_DEFAULT_CSP "script-src 'self' 'wasm-unsafe-eval';"
#define DEFAULT_CSP_PREF_V3 \
"extensions.webextensions.default-content-security-policy.v3"
#define DEFAULT_DEFAULT_CSP_V3 "script-src 'self'; upgrade-insecure-requests;"
#define RESTRICTED_DOMAINS_PREF "extensions.webextensions.restrictedDomains"
#define QUARANTINED_DOMAINS_PREF "extensions.quarantinedDomains.list"
#define QUARANTINED_DOMAINS_ENABLED "extensions.quarantinedDomains.enabled"
#define OBS_TOPIC_PRELOAD_SCRIPT "web-extension-preload-content-script"
#define OBS_TOPIC_LOAD_SCRIPT "web-extension-load-content-script"
static const char kDocElementInserted[] = "initial-document-element-inserted";
/*****************************************************************************
* ExtensionPolicyService
*****************************************************************************/
using CoreByHostMap = nsTHashMap<nsCStringASCIICaseInsensitiveHashKey,
RefPtr<extensions::WebExtensionPolicyCore>>;
static StaticRWLock sEPSLock;
static StaticAutoPtr<CoreByHostMap> sCoreByHost MOZ_GUARDED_BY(sEPSLock);
static StaticRefPtr<AtomSet> sRestrictedDomains MOZ_GUARDED_BY(sEPSLock);
static StaticRefPtr<AtomSet> sQuarantinedDomains MOZ_GUARDED_BY(sEPSLock);
/* static */
mozIExtensionProcessScript& ExtensionPolicyService::ProcessScript() {
static nsCOMPtr<mozIExtensionProcessScript> sProcessScript;
MOZ_ASSERT(NS_IsMainThread());
if (MOZ_UNLIKELY(!sProcessScript)) {
sProcessScript = do_ImportESModule(
"resource://gre/modules/ExtensionProcessScript.sys.mjs",
"ExtensionProcessScript");
ClearOnShutdown(&sProcessScript);
}
return *sProcessScript;
}
/* static */ ExtensionPolicyService& ExtensionPolicyService::GetSingleton() {
MOZ_ASSERT(NS_IsMainThread());
static RefPtr<ExtensionPolicyService> sExtensionPolicyService;
if (MOZ_UNLIKELY(!sExtensionPolicyService)) {
sExtensionPolicyService = new ExtensionPolicyService();
RegisterWeakMemoryReporter(sExtensionPolicyService);
ClearOnShutdown(&sExtensionPolicyService);
}
return *sExtensionPolicyService.get();
}
/* static */
RefPtr<extensions::WebExtensionPolicyCore>
ExtensionPolicyService::GetCoreByHost(const nsACString& aHost) {
StaticAutoReadLock lock(sEPSLock);
return sCoreByHost ? sCoreByHost->Get(aHost) : nullptr;
}
/* static */
RefPtr<extensions::WebExtensionPolicyCore> ExtensionPolicyService::GetCoreByURL(
const URLInfo& aURL) {
if (aURL.Scheme() == nsGkAtoms::moz_extension) {
return GetCoreByHost(aURL.Host());
}
return nullptr;
}
ExtensionPolicyService::ExtensionPolicyService() {
mObs = services::GetObserverService();
MOZ_RELEASE_ASSERT(mObs);
mDefaultCSP.SetIsVoid(true);
mDefaultCSPV3.SetIsVoid(true);
RegisterObservers();
{
StaticAutoWriteLock lock(sEPSLock);
MOZ_DIAGNOSTIC_ASSERT(!sCoreByHost,
"ExtensionPolicyService created twice?");
sCoreByHost = new CoreByHostMap();
}
UpdateRestrictedDomains();
UpdateQuarantinedDomains();
}
ExtensionPolicyService::~ExtensionPolicyService() {
UnregisterWeakMemoryReporter(this);
{
StaticAutoWriteLock lock(sEPSLock);
sCoreByHost = nullptr;
sRestrictedDomains = nullptr;
sQuarantinedDomains = nullptr;
}
}
bool ExtensionPolicyService::UseRemoteExtensions() const {
static Maybe<bool> sRemoteExtensions;
if (MOZ_UNLIKELY(sRemoteExtensions.isNothing())) {
sRemoteExtensions = Some(StaticPrefs::extensions_webextensions_remote());
}
return sRemoteExtensions.value() && BrowserTabsRemoteAutostart();
}
bool ExtensionPolicyService::IsExtensionProcess() const {
bool isRemote = UseRemoteExtensions();
if (isRemote && XRE_IsContentProcess()) {
auto& remoteType = dom::ContentChild::GetSingleton()->GetRemoteType();
return remoteType == EXTENSION_REMOTE_TYPE;
}
return !isRemote && XRE_IsParentProcess();
}
bool ExtensionPolicyService::GetQuarantinedDomainsEnabled() const {
StaticAutoReadLock lock(sEPSLock);
return sQuarantinedDomains != nullptr;
}
WebExtensionPolicy* ExtensionPolicyService::GetByURL(const URLInfo& aURL) {
if (aURL.Scheme() == nsGkAtoms::moz_extension) {
return GetByHost(aURL.Host());
}
return nullptr;
}
WebExtensionPolicy* ExtensionPolicyService::GetByHost(
const nsACString& aHost) const {
AssertIsOnMainThread();
RefPtr<WebExtensionPolicyCore> core = GetCoreByHost(aHost);
return core ? core->GetMainThreadPolicy() : nullptr;
}
void ExtensionPolicyService::GetAll(
nsTArray<RefPtr<WebExtensionPolicy>>& aResult) {
AppendToArray(aResult, mExtensions.Values());
}
bool ExtensionPolicyService::RegisterExtension(WebExtensionPolicy& aPolicy) {
bool ok =
(!GetByID(aPolicy.Id()) && !GetByHost(aPolicy.MozExtensionHostname()));
MOZ_ASSERT(ok);
if (!ok) {
return false;
}
mExtensions.InsertOrUpdate(aPolicy.Id(), RefPtr{&aPolicy});
{
StaticAutoWriteLock lock(sEPSLock);
sCoreByHost->InsertOrUpdate(aPolicy.MozExtensionHostname(), aPolicy.Core());
}
return true;
}
bool ExtensionPolicyService::UnregisterExtension(WebExtensionPolicy& aPolicy) {
bool ok = (GetByID(aPolicy.Id()) == &aPolicy &&
GetByHost(aPolicy.MozExtensionHostname()) == &aPolicy);
MOZ_ASSERT(ok);
if (!ok) {
return false;
}
mExtensions.Remove(aPolicy.Id());
{
StaticAutoWriteLock lock(sEPSLock);
sCoreByHost->Remove(aPolicy.MozExtensionHostname());
}
return true;
}
bool ExtensionPolicyService::RegisterObserver(DocumentObserver& aObserver) {
bool inserted = false;
mObservers.LookupOrInsertWith(&aObserver, [&] {
inserted = true;
return RefPtr{&aObserver};
});
return inserted;
}
bool ExtensionPolicyService::UnregisterObserver(DocumentObserver& aObserver) {
return mObservers.Remove(&aObserver);
}
/*****************************************************************************
* nsIMemoryReporter
*****************************************************************************/
NS_IMETHODIMP
ExtensionPolicyService::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) {
for (const auto& ext : mExtensions.Values()) {
nsAtomCString id(ext->Id());
NS_ConvertUTF16toUTF8 name(ext->Name());
name.ReplaceSubstring("\"", "");
name.ReplaceSubstring("\\", "");
nsString url;
MOZ_TRY_VAR(url, ext->GetURL(u""_ns));
nsPrintfCString desc("Extension(id=%s, name=\"%s\", baseURL=%s)", id.get(),
name.get(), NS_ConvertUTF16toUTF8(url).get());
desc.ReplaceChar('/', '\\');
nsCString path("extensions/");
path.Append(desc);
aHandleReport->Callback(""_ns, path, KIND_NONHEAP, UNITS_COUNT, 1,
"WebExtensions that are active in this session"_ns,
aData);
}
return NS_OK;
}
/*****************************************************************************
* Content script management
*****************************************************************************/
void ExtensionPolicyService::RegisterObservers() {
mObs->AddObserver(this, kDocElementInserted, false);
if (XRE_IsContentProcess()) {
mObs->AddObserver(this, "http-on-opening-request", false);
mObs->AddObserver(this, "document-on-opening-request", false);
}
Preferences::AddStrongObserver(this, DEFAULT_CSP_PREF);
Preferences::AddStrongObserver(this, DEFAULT_CSP_PREF_V3);
Preferences::AddStrongObserver(this, RESTRICTED_DOMAINS_PREF);
Preferences::AddStrongObserver(this, QUARANTINED_DOMAINS_PREF);
Preferences::AddStrongObserver(this, QUARANTINED_DOMAINS_ENABLED);
}
void ExtensionPolicyService::UnregisterObservers() {
mObs->RemoveObserver(this, kDocElementInserted);
if (XRE_IsContentProcess()) {
mObs->RemoveObserver(this, "http-on-opening-request");
mObs->RemoveObserver(this, "document-on-opening-request");
}
Preferences::RemoveObserver(this, DEFAULT_CSP_PREF);
Preferences::RemoveObserver(this, DEFAULT_CSP_PREF_V3);
Preferences::RemoveObserver(this, RESTRICTED_DOMAINS_PREF);
Preferences::RemoveObserver(this, QUARANTINED_DOMAINS_PREF);
Preferences::RemoveObserver(this, QUARANTINED_DOMAINS_ENABLED);
}
nsresult ExtensionPolicyService::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData) {
if (!strcmp(aTopic, kDocElementInserted)) {
nsCOMPtr<Document> doc = do_QueryInterface(aSubject);
if (doc) {
CheckDocument(doc);
}
} else if (!strcmp(aTopic, "http-on-opening-request") ||
!strcmp(aTopic, "document-on-opening-request")) {
nsCOMPtr<nsIChannel> chan = do_QueryInterface(aSubject);
if (chan) {
CheckRequest(chan);
}
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
const nsCString converted = NS_ConvertUTF16toUTF8(aData);
const char* pref = converted.get();
if (!strcmp(pref, DEFAULT_CSP_PREF)) {
mDefaultCSP.SetIsVoid(true);
} else if (!strcmp(pref, DEFAULT_CSP_PREF_V3)) {
mDefaultCSPV3.SetIsVoid(true);
} else if (!strcmp(pref, RESTRICTED_DOMAINS_PREF)) {
UpdateRestrictedDomains();
} else if (!strcmp(pref, QUARANTINED_DOMAINS_PREF) ||
!strcmp(pref, QUARANTINED_DOMAINS_ENABLED)) {
UpdateQuarantinedDomains();
}
}
return NS_OK;
}
already_AddRefed<Promise> ExtensionPolicyService::ExecuteContentScript(
nsPIDOMWindowInner* aWindow, WebExtensionContentScript& aScript) {
if (!aWindow->IsCurrentInnerWindow()) {
return nullptr;
}
RefPtr<Promise> promise;
ProcessScript().LoadContentScript(&aScript, aWindow, getter_AddRefs(promise));
return promise.forget();
}
RefPtr<Promise> ExtensionPolicyService::ExecuteContentScripts(
JSContext* aCx, nsPIDOMWindowInner* aWindow,
const nsTArray<RefPtr<WebExtensionContentScript>>& aScripts) {
AutoTArray<RefPtr<Promise>, 8> promises;
for (auto& script : aScripts) {
if (RefPtr<Promise> promise = ExecuteContentScript(aWindow, *script)) {
promises.AppendElement(std::move(promise));
}
}
RefPtr<Promise> promise = Promise::All(aCx, promises, IgnoreErrors());
MOZ_RELEASE_ASSERT(promise);
return promise;
}
// Use browser's MessageManagerGroup to decide if we care about it, to inject
// extension APIs or content scripts. Tabs use "browsers", and all custom
// extension browsers use "webext-browsers", including popups & sidebars,
// background & options pages, and xpcshell tests.
static bool IsTabOrExtensionBrowser(dom::BrowsingContext* aBC) {
const auto& group = aBC->Top()->GetMessageManagerGroup();
bool rv = group == u"browsers"_ns || group == u"webext-browsers"_ns;
#ifdef MOZ_THUNDERBIRD
// ...unless it's Thunderbird, which has extra groups for unrelated reasons.
rv = rv || group == u"single-site"_ns || group == u"single-page"_ns;
#endif
return rv;
}
static nsTArray<RefPtr<dom::BrowsingContext>> GetAllInProcessContentBCs() {
nsTArray<RefPtr<dom::BrowsingContext>> contentBCs;
nsTArray<RefPtr<dom::BrowsingContextGroup>> groups;
dom::BrowsingContextGroup::GetAllGroups(groups);
for (const auto& group : groups) {
for (const auto& toplevel : group->Toplevels()) {
if (!toplevel->IsContent() || toplevel->IsDiscarded() ||
!IsTabOrExtensionBrowser(toplevel)) {
continue;
}
toplevel->PreOrderWalk([&](dom::BrowsingContext* aContext) {
contentBCs.AppendElement(aContext);
});
}
}
return contentBCs;
}
nsresult ExtensionPolicyService::InjectContentScripts(
WebExtensionPolicy* aExtension) {
AutoJSAPI jsapi;
MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
auto contentBCs = GetAllInProcessContentBCs();
for (dom::BrowsingContext* bc : contentBCs) {
auto* win = bc->GetDOMWindow();
if (bc->Top()->IsDiscarded() || !win || !win->GetDocumentURI()) {
continue;
}
DocInfo docInfo(win);
using RunAt = dom::ContentScriptRunAt;
using Scripts = AutoTArray<RefPtr<WebExtensionContentScript>, 8>;
Scripts scripts[ContiguousEnumSize<RunAt>::value];
auto GetScripts = [&](RunAt aRunAt) -> Scripts&& {
static_assert(sizeof(aRunAt) == 1, "Our cast is wrong");
return std::move(scripts[uint8_t(aRunAt)]);
};
for (const auto& script : aExtension->ContentScripts()) {
if (script->Matches(docInfo)) {
GetScripts(script->RunAt()).AppendElement(script);
}
}
nsCOMPtr<nsPIDOMWindowInner> inner = win->GetCurrentInnerWindow();
MOZ_TRY(ExecuteContentScripts(jsapi.cx(), inner,
GetScripts(RunAt::Document_start))
->ThenWithCycleCollectedArgs(
[](JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv, ExtensionPolicyService* aSelf,
nsPIDOMWindowInner* aInner, Scripts&& aScripts) {
return aSelf->ExecuteContentScripts(aCx, aInner, aScripts)
.forget();
},
this, inner, GetScripts(RunAt::Document_end))
.andThen([&](auto aPromise) {
return aPromise->ThenWithCycleCollectedArgs(
[](JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv, ExtensionPolicyService* aSelf,
nsPIDOMWindowInner* aInner, Scripts&& aScripts) {
return aSelf
->ExecuteContentScripts(aCx, aInner, aScripts)
.forget();
},
this, inner, GetScripts(RunAt::Document_idle));
}));
}
return NS_OK;
}
// Checks a request for matching content scripts, and begins pre-loading them
// if necessary.
void ExtensionPolicyService::CheckRequest(nsIChannel* aChannel) {
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
auto loadType = loadInfo->GetExternalContentPolicyType();
if (loadType != ExtContentPolicy::TYPE_DOCUMENT &&
loadType != ExtContentPolicy::TYPE_SUBDOCUMENT) {
return;
}
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(aChannel->GetURI(getter_AddRefs(uri)))) {
return;
}
CheckContentScripts({uri.get(), loadInfo}, true);
}
static bool CheckParentFrames(nsPIDOMWindowOuter* aWindow,
WebExtensionPolicy& aPolicy) {
nsCOMPtr<nsIURI> aboutAddons;
if (NS_FAILED(NS_NewURI(getter_AddRefs(aboutAddons), "about:addons"))) {
return false;
}
nsCOMPtr<nsIURI> htmlAboutAddons;
if (NS_FAILED(
NS_NewURI(getter_AddRefs(htmlAboutAddons),
"chrome://mozapps/content/extensions/aboutaddons.html"))) {
return false;
}
dom::WindowContext* wc = aWindow->GetCurrentInnerWindow()->GetWindowContext();
while ((wc = wc->GetParentWindowContext())) {
if (!wc->IsInProcess()) {
return false;
}
nsGlobalWindowInner* win = wc->GetInnerWindow();
auto* principal = BasePrincipal::Cast(win->GetPrincipal());
if (principal->IsSystemPrincipal()) {
// The add-on manager is a special case, since it contains extension
// options pages in same-type <browser> frames.
nsIURI* uri = win->GetDocumentURI();
bool equals;
if ((NS_SUCCEEDED(uri->Equals(aboutAddons, &equals)) && equals) ||
(NS_SUCCEEDED(uri->Equals(htmlAboutAddons, &equals)) && equals)) {
return true;
}
}
if (principal->AddonPolicy() != &aPolicy) {
return false;
}
}
return true;
}
// Checks a document, just after the document element has been inserted, for
// matching content scripts or extension principals, and loads them if
// necessary.
void ExtensionPolicyService::CheckDocument(Document* aDocument) {
nsCOMPtr<nsPIDOMWindowOuter> win = aDocument->GetWindow();
if (win) {
if (!IsTabOrExtensionBrowser(win->GetBrowsingContext())) {
return;
}
if (win->GetDocumentURI()) {
CheckContentScripts(win.get(), false);
}
nsIPrincipal* principal = aDocument->NodePrincipal();
RefPtr<WebExtensionPolicy> policy =
BasePrincipal::Cast(principal)->AddonPolicy();
if (policy) {
bool privileged = IsExtensionProcess() && CheckParentFrames(win, *policy);
ProcessScript().InitExtensionDocument(policy, aDocument, privileged);
}
}
}
void ExtensionPolicyService::CheckContentScripts(const DocInfo& aDocInfo,
bool aIsPreload) {
nsCOMPtr<nsPIDOMWindowInner> win;
if (!aIsPreload) {
win = aDocInfo.GetWindow()->GetCurrentInnerWindow();
}
nsTArray<RefPtr<WebExtensionContentScript>> scriptsToLoad;
for (RefPtr<WebExtensionPolicy> policy : mExtensions.Values()) {
for (auto& script : policy->ContentScripts()) {
if (script->Matches(aDocInfo)) {
if (aIsPreload) {
ProcessScript().PreloadContentScript(script);
} else {
// Collect the content scripts to load instead of loading them
// right away (to prevent a loaded content script from being
// able to invalidate the iterator by triggering a call to
// policy->UnregisterContentScript while we are still iterating
// over all its content scripts). See Bug 1593240.
scriptsToLoad.AppendElement(script);
}
}
}
for (auto& script : scriptsToLoad) {
if (!win->IsCurrentInnerWindow()) {
break;
}
RefPtr<Promise> promise;
ProcessScript().LoadContentScript(script, win, getter_AddRefs(promise));
}
scriptsToLoad.ClearAndRetainStorage();
}
for (RefPtr<DocumentObserver> observer : mObservers.Values()) {
for (auto& matcher : observer->Matchers()) {
if (matcher->Matches(aDocInfo)) {
if (aIsPreload) {
observer->NotifyMatch(*matcher, aDocInfo.GetLoadInfo());
} else {
observer->NotifyMatch(*matcher, aDocInfo.GetWindow());
}
}
}
}
}
/* static */
RefPtr<AtomSet> ExtensionPolicyService::RestrictedDomains() {
StaticAutoReadLock lock(sEPSLock);
return sRestrictedDomains;
}
/* static */
RefPtr<AtomSet> ExtensionPolicyService::QuarantinedDomains() {
StaticAutoReadLock lock(sEPSLock);
return sQuarantinedDomains;
}
void ExtensionPolicyService::UpdateRestrictedDomains() {
nsAutoCString eltsString;
Unused << Preferences::GetCString(RESTRICTED_DOMAINS_PREF, eltsString);
AutoTArray<nsString, 32> elts;
for (const nsACString& elt : eltsString.Split(',')) {
elts.AppendElement(NS_ConvertUTF8toUTF16(elt));
elts.LastElement().StripWhitespace();
}
RefPtr<AtomSet> atomSet = new AtomSet(elts);
StaticAutoWriteLock lock(sEPSLock);
sRestrictedDomains = atomSet;
}
void ExtensionPolicyService::UpdateQuarantinedDomains() {
if (!Preferences::GetBool(QUARANTINED_DOMAINS_ENABLED)) {
StaticAutoWriteLock lock(sEPSLock);
sQuarantinedDomains = nullptr;
return;
}
nsAutoCString eltsString;
AutoTArray<nsString, 32> elts;
if (NS_SUCCEEDED(
Preferences::GetCString(QUARANTINED_DOMAINS_PREF, eltsString))) {
for (const nsACString& elt : eltsString.Split(',')) {
elts.AppendElement(NS_ConvertUTF8toUTF16(elt));
elts.LastElement().StripWhitespace();
}
}
RefPtr<AtomSet> atomSet = new AtomSet(elts);
StaticAutoWriteLock lock(sEPSLock);
sQuarantinedDomains = atomSet;
}
/*****************************************************************************
* nsIAddonPolicyService
*****************************************************************************/
nsresult ExtensionPolicyService::GetDefaultCSP(nsAString& aDefaultCSP) {
if (mDefaultCSP.IsVoid()) {
nsresult rv = Preferences::GetString(DEFAULT_CSP_PREF, mDefaultCSP);
if (NS_FAILED(rv)) {
mDefaultCSP.AssignLiteral(DEFAULT_DEFAULT_CSP);
}
mDefaultCSP.SetIsVoid(false);
}
aDefaultCSP.Assign(mDefaultCSP);
return NS_OK;
}
nsresult ExtensionPolicyService::GetDefaultCSPV3(nsAString& aDefaultCSP) {
if (mDefaultCSPV3.IsVoid()) {
nsresult rv = Preferences::GetString(DEFAULT_CSP_PREF_V3, mDefaultCSPV3);
if (NS_FAILED(rv)) {
mDefaultCSPV3.AssignLiteral(DEFAULT_DEFAULT_CSP_V3);
}
mDefaultCSPV3.SetIsVoid(false);
}
aDefaultCSP.Assign(mDefaultCSPV3);
return NS_OK;
}
nsresult ExtensionPolicyService::GetBaseCSP(const nsAString& aAddonId,
nsAString& aResult) {
if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
policy->GetBaseCSP(aResult);
return NS_OK;
}
return NS_ERROR_INVALID_ARG;
}
nsresult ExtensionPolicyService::GetExtensionPageCSP(const nsAString& aAddonId,
nsAString& aResult) {
if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
policy->GetExtensionPageCSP(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::GetExtensionName(const nsAString& aAddonId,
nsAString& aName) {
if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
aName.Assign(policy->Name());
return NS_OK;
}
return NS_ERROR_INVALID_ARG;
}
nsresult ExtensionPolicyService::SourceMayLoadExtensionURI(
nsIURI* aSourceURI, nsIURI* aExtensionURI, bool aFromPrivateWindow,
bool* aResult) {
URLInfo source(aSourceURI);
URLInfo url(aExtensionURI);
if (RefPtr<WebExtensionPolicyCore> policy = GetCoreByURL(url)) {
*aResult = (!aFromPrivateWindow || policy->PrivateBrowsingAllowed()) &&
policy->SourceMayAccessPath(source, url.FilePath());
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, mObservers)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtensionPolicyService)
NS_INTERFACE_MAP_ENTRY(nsIAddonPolicyService)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAddonPolicyService)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ExtensionPolicyService)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ExtensionPolicyService)
} // namespace mozilla