Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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,
#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 (NS_WARN_IF(!aWindow) || !aWindow->IsCurrentInnerWindow()) {
return nullptr;
}
RefPtr<Promise> promise;
ProcessScript().LoadContentScript(&aScript, aWindow, getter_AddRefs(promise));
return promise.forget();
}
already_AddRefed<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());
Unused << NS_WARN_IF(!promise);
return promise.forget();
}
// 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;
}
void ExtensionPolicyService::InjectContentScripts(
WebExtensionPolicy* aExtension, ErrorResult& aRv) {
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();
RefPtr<Promise> nextPromise = ExecuteContentScripts(
jsapi.cx(), inner, GetScripts(RunAt::Document_start));
// Throw an UnknownError when the first ExecuteContentScripts call
// for document_start content scripts fails to return a non-null Promise,
// the other two ExecuteContentScripts calls from the promise chain
// that follows will also log a similar execption and the promise chain
// rejected right away.
// NOTE: ExecuteContentScripts will be returning a nullptr if the call to
if (!nextPromise) {
aRv.ThrowUnknownError(
"The execution of document_start content scripts failed for an "
"unknown reason");
return;
}
auto result =
nextPromise
->ThenWithCycleCollectedArgs(
[](JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv, ExtensionPolicyService* aSelf,
nsPIDOMWindowInner* aInner, Scripts&& aScripts) {
RefPtr<Promise> newPromise =
aSelf->ExecuteContentScripts(aCx, aInner, aScripts);
if (NS_WARN_IF(!newPromise)) {
aRv.ThrowUnknownError(
"The execution of document_end content scripts failed "
"for an unknown reason");
}
return newPromise.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) {
RefPtr<Promise> newPromise =
aSelf->ExecuteContentScripts(aCx, aInner, aScripts);
if (NS_WARN_IF(!newPromise)) {
aRv.ThrowUnknownError(
"The execution of document_end content scripts "
"failed "
"for an unknown reason");
}
return newPromise.forget();
},
this, inner, GetScripts(RunAt::Document_idle));
});
if (result.isErr()) {
aRv.ThrowUnknownError(
"The execution of document_end and document_idle content scripts "
"failed for an unknown reason");
return;
}
}
}
// 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
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