The basic idea is to make non-initial about:blank fire document-element-inserted notifications just like every other document. We then ensure that there's a notification (initial-document-element-inserted) that only gets fired once per window for documents that are in a window. This notification is what webextensions use to inject into the document. The old setup which injected into about:blank when its global is created gets removed in favor of injecting the same way as into every other document. The changes to Document.cpp are fixing a bug in the "block the parser" stuff webextensions do. For about:blank, the blocking happens at a point when the parser really has nothing else to parse (since it's parsing the empty string). So the blocking is a no-op. But we do want to prevent DOMContentLoaded firing, because otherwise the "end of document" scripts could run before we finish doing the "beginning of document" work in webextensions. So we want to make sure we block DOMContentLoaded, not just the load event. Differential Revision: https://phabricator.services.mozilla.com/D19892
616 lines
20 KiB
C++
616 lines
20 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/ClearOnShutdown.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/ResultExtensions.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/SimpleEnumerator.h"
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/dom/ContentFrameMessageManager.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/Promise-inl.h"
|
|
#include "mozIExtensionProcessScript.h"
|
|
#include "nsDocShell.h"
|
|
#include "nsEscape.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsIChannel.h"
|
|
#include "nsIContentPolicy.h"
|
|
#include "nsIDocShell.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "nsGlobalWindowOuter.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::ContentFrameMessageManager;
|
|
using dom::Document;
|
|
using dom::Promise;
|
|
|
|
#define DEFAULT_BASE_CSP \
|
|
"script-src 'self' https://* moz-extension: blob: filesystem: " \
|
|
"'unsafe-eval' 'unsafe-inline'; " \
|
|
"object-src 'self' https://* moz-extension: blob: filesystem:;"
|
|
|
|
#define DEFAULT_DEFAULT_CSP "script-src 'self'; object-src 'self';"
|
|
|
|
#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";
|
|
|
|
static mozIExtensionProcessScript& ProcessScript() {
|
|
static nsCOMPtr<mozIExtensionProcessScript> sProcessScript;
|
|
|
|
if (MOZ_UNLIKELY(!sProcessScript)) {
|
|
nsCOMPtr<mozIExtensionProcessScriptJSM> jsm =
|
|
do_ImportModule("resource://gre/modules/ExtensionProcessScript.jsm");
|
|
MOZ_RELEASE_ASSERT(jsm);
|
|
|
|
Unused << jsm->GetExtensionProcessScript(getter_AddRefs(sProcessScript));
|
|
MOZ_RELEASE_ASSERT(sProcessScript);
|
|
ClearOnShutdown(&sProcessScript);
|
|
}
|
|
return *sProcessScript;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ExtensionPolicyService
|
|
*****************************************************************************/
|
|
|
|
/* static */ bool ExtensionPolicyService::sRemoteExtensions;
|
|
|
|
/* static */ ExtensionPolicyService& ExtensionPolicyService::GetSingleton() {
|
|
static RefPtr<ExtensionPolicyService> sExtensionPolicyService;
|
|
|
|
if (MOZ_UNLIKELY(!sExtensionPolicyService)) {
|
|
sExtensionPolicyService = new ExtensionPolicyService();
|
|
RegisterWeakMemoryReporter(sExtensionPolicyService);
|
|
ClearOnShutdown(&sExtensionPolicyService);
|
|
}
|
|
return *sExtensionPolicyService.get();
|
|
}
|
|
|
|
ExtensionPolicyService::ExtensionPolicyService() {
|
|
mObs = services::GetObserverService();
|
|
MOZ_RELEASE_ASSERT(mObs);
|
|
|
|
Preferences::AddBoolVarCache(&sRemoteExtensions,
|
|
"extensions.webextensions.remote", false);
|
|
|
|
RegisterObservers();
|
|
}
|
|
|
|
ExtensionPolicyService::~ExtensionPolicyService() {
|
|
UnregisterWeakMemoryReporter(this);
|
|
}
|
|
|
|
bool ExtensionPolicyService::UseRemoteExtensions() const {
|
|
return sRemoteExtensions && BrowserTabsRemoteAutostart();
|
|
}
|
|
|
|
bool ExtensionPolicyService::IsExtensionProcess() const {
|
|
bool isRemote = UseRemoteExtensions();
|
|
|
|
if (isRemote && XRE_IsContentProcess()) {
|
|
auto& remoteType = dom::ContentChild::GetSingleton()->GetRemoteType();
|
|
return remoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE);
|
|
}
|
|
return !isRemote && XRE_IsParentProcess();
|
|
}
|
|
|
|
WebExtensionPolicy* ExtensionPolicyService::GetByURL(const URLInfo& aURL) {
|
|
if (aURL.Scheme() == nsGkAtoms::moz_extension) {
|
|
return GetByHost(aURL.Host());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void ExtensionPolicyService::GetAll(
|
|
nsTArray<RefPtr<WebExtensionPolicy>>& aResult) {
|
|
for (auto iter = mExtensions.Iter(); !iter.Done(); iter.Next()) {
|
|
aResult.AppendElement(iter.Data());
|
|
}
|
|
}
|
|
|
|
bool ExtensionPolicyService::RegisterExtension(WebExtensionPolicy& aPolicy) {
|
|
bool ok =
|
|
(!GetByID(aPolicy.Id()) && !GetByHost(aPolicy.MozExtensionHostname()));
|
|
MOZ_ASSERT(ok);
|
|
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
|
|
mExtensions.Put(aPolicy.Id(), &aPolicy);
|
|
mExtensionHosts.Put(aPolicy.MozExtensionHostname(), &aPolicy);
|
|
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());
|
|
mExtensionHosts.Remove(aPolicy.MozExtensionHostname());
|
|
return true;
|
|
}
|
|
|
|
bool ExtensionPolicyService::RegisterObserver(DocumentObserver& aObserver) {
|
|
if (mObservers.GetWeak(&aObserver)) {
|
|
return false;
|
|
}
|
|
|
|
mObservers.Put(&aObserver, &aObserver);
|
|
return true;
|
|
}
|
|
|
|
bool ExtensionPolicyService::UnregisterObserver(DocumentObserver& aObserver) {
|
|
if (!mObservers.GetWeak(&aObserver)) {
|
|
return false;
|
|
}
|
|
|
|
mObservers.Remove(&aObserver);
|
|
return true;
|
|
}
|
|
|
|
void ExtensionPolicyService::BaseCSP(nsAString& aBaseCSP) const {
|
|
nsresult rv;
|
|
|
|
rv = Preferences::GetString(
|
|
"extensions.webextensions.base-content-security-policy", aBaseCSP);
|
|
if (NS_FAILED(rv)) {
|
|
aBaseCSP.AssignLiteral(DEFAULT_BASE_CSP);
|
|
}
|
|
}
|
|
|
|
void ExtensionPolicyService::DefaultCSP(nsAString& aDefaultCSP) const {
|
|
nsresult rv;
|
|
|
|
rv = Preferences::GetString(
|
|
"extensions.webextensions.default-content-security-policy", aDefaultCSP);
|
|
if (NS_FAILED(rv)) {
|
|
aDefaultCSP.AssignLiteral(DEFAULT_DEFAULT_CSP);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* nsIMemoryReporter
|
|
*****************************************************************************/
|
|
|
|
NS_IMETHODIMP
|
|
ExtensionPolicyService::CollectReports(nsIHandleReportCallback* aHandleReport,
|
|
nsISupports* aData, bool aAnonymize) {
|
|
for (auto iter = mExtensions.Iter(); !iter.Done(); iter.Next()) {
|
|
auto& ext = iter.Data();
|
|
|
|
nsAtomCString id(ext->Id());
|
|
|
|
NS_ConvertUTF16toUTF8 name(ext->Name());
|
|
name.ReplaceSubstring("\"", "");
|
|
name.ReplaceSubstring("\\", "");
|
|
|
|
nsString url;
|
|
MOZ_TRY_VAR(url, ext->GetURL(NS_LITERAL_STRING("")));
|
|
|
|
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(
|
|
EmptyCString(), path, KIND_NONHEAP, UNITS_COUNT, 1,
|
|
NS_LITERAL_CSTRING("WebExtensions that are active in this session"),
|
|
aData);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Content script management
|
|
*****************************************************************************/
|
|
|
|
void ExtensionPolicyService::RegisterObservers() {
|
|
mObs->AddObserver(this, kDocElementInserted, false);
|
|
mObs->AddObserver(this, "tab-content-frameloader-created", false);
|
|
if (XRE_IsContentProcess()) {
|
|
mObs->AddObserver(this, "http-on-opening-request", false);
|
|
}
|
|
}
|
|
|
|
void ExtensionPolicyService::UnregisterObservers() {
|
|
mObs->RemoveObserver(this, kDocElementInserted);
|
|
mObs->RemoveObserver(this, "tab-content-frameloader-created");
|
|
if (XRE_IsContentProcess()) {
|
|
mObs->RemoveObserver(this, "http-on-opening-request");
|
|
}
|
|
}
|
|
|
|
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")) {
|
|
nsCOMPtr<nsIChannel> chan = do_QueryInterface(aSubject);
|
|
if (chan) {
|
|
CheckRequest(chan);
|
|
}
|
|
} else if (!strcmp(aTopic, "tab-content-frameloader-created")) {
|
|
RefPtr<ContentFrameMessageManager> mm = do_QueryObject(aSubject);
|
|
NS_ENSURE_TRUE(mm, NS_ERROR_UNEXPECTED);
|
|
|
|
mMessageManagers.PutEntry(mm);
|
|
|
|
mm->AddSystemEventListener(NS_LITERAL_STRING("unload"), this, false, false);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult ExtensionPolicyService::HandleEvent(dom::Event* aEvent) {
|
|
RefPtr<ContentFrameMessageManager> mm = do_QueryObject(aEvent->GetTarget());
|
|
MOZ_ASSERT(mm);
|
|
if (mm) {
|
|
mMessageManagers.RemoveEntry(mm);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult ForEachDocShell(
|
|
nsIDocShell* aDocShell,
|
|
const std::function<nsresult(nsIDocShell*)>& aCallback) {
|
|
nsCOMPtr<nsISimpleEnumerator> iter;
|
|
MOZ_TRY(aDocShell->GetDocShellEnumerator(nsIDocShell::typeContent,
|
|
nsIDocShell::ENUMERATE_FORWARDS,
|
|
getter_AddRefs(iter)));
|
|
|
|
for (auto& docShell : SimpleEnumerator<nsIDocShell>(iter)) {
|
|
MOZ_TRY(aCallback(docShell));
|
|
}
|
|
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;
|
|
}
|
|
|
|
nsresult ExtensionPolicyService::InjectContentScripts(
|
|
WebExtensionPolicy* aExtension) {
|
|
AutoJSAPI jsapi;
|
|
MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
|
|
|
|
for (auto iter = mMessageManagers.ConstIter(); !iter.Done(); iter.Next()) {
|
|
ContentFrameMessageManager* mm = iter.Get()->GetKey();
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = mm->GetDocShell(IgnoreErrors());
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
|
|
|
|
auto result =
|
|
ForEachDocShell(docShell, [&](nsIDocShell* aDocShell) -> nsresult {
|
|
nsCOMPtr<nsPIDOMWindowOuter> win = aDocShell->GetWindow();
|
|
if (!win->GetDocumentURI()) {
|
|
return NS_OK;
|
|
}
|
|
DocInfo docInfo(win);
|
|
|
|
using RunAt = dom::ContentScriptRunAt;
|
|
using Scripts = AutoTArray<RefPtr<WebExtensionContentScript>, 8>;
|
|
|
|
constexpr uint8_t n = uint8_t(RunAt::EndGuard_);
|
|
Scripts scripts[n];
|
|
|
|
auto GetScripts = [&](RunAt aRunAt) -> Scripts&& {
|
|
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::HandleValue aValue,
|
|
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::HandleValue aValue,
|
|
ExtensionPolicyService* aSelf,
|
|
nsPIDOMWindowInner* aInner, Scripts&& aScripts) {
|
|
return aSelf
|
|
->ExecuteContentScripts(aCx, aInner, aScripts)
|
|
.forget();
|
|
},
|
|
this, inner, GetScripts(RunAt::Document_idle));
|
|
}));
|
|
|
|
return NS_OK;
|
|
});
|
|
MOZ_TRY(result);
|
|
}
|
|
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 != nsIContentPolicy::TYPE_DOCUMENT &&
|
|
loadType != nsIContentPolicy::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;
|
|
}
|
|
|
|
auto* piWin = aWindow;
|
|
while ((piWin = piWin->GetScriptableParentOrNull())) {
|
|
auto* win = nsGlobalWindowOuter::Cast(piWin);
|
|
|
|
auto* principal = BasePrincipal::Cast(win->GetPrincipal());
|
|
if (nsContentUtils::IsSystemPrincipal(principal)) {
|
|
// The add-on manager is a special case, since it contains extension
|
|
// options pages in same-type <browser> frames.
|
|
bool equals;
|
|
if (NS_SUCCEEDED(win->GetDocumentURI()->Equals(aboutAddons, &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) {
|
|
nsIDocShell* docShell = win->GetDocShell();
|
|
RefPtr<ContentFrameMessageManager> mm = docShell->GetMessageManager();
|
|
if (!mm || !mMessageManagers.Contains(mm)) {
|
|
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();
|
|
}
|
|
|
|
for (auto iter = mExtensions.Iter(); !iter.Done(); iter.Next()) {
|
|
RefPtr<WebExtensionPolicy> policy = iter.Data();
|
|
|
|
for (auto& script : policy->ContentScripts()) {
|
|
if (script->Matches(aDocInfo)) {
|
|
if (aIsPreload) {
|
|
ProcessScript().PreloadContentScript(script);
|
|
} else {
|
|
if (!win->IsCurrentInnerWindow()) {
|
|
break;
|
|
}
|
|
RefPtr<Promise> promise;
|
|
ProcessScript().LoadContentScript(script, win,
|
|
getter_AddRefs(promise));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto iter = mObservers.Iter(); !iter.Done(); iter.Next()) {
|
|
RefPtr<DocumentObserver> observer = iter.Data();
|
|
|
|
for (auto& matcher : observer->Matchers()) {
|
|
if (matcher->Matches(aDocInfo)) {
|
|
if (aIsPreload) {
|
|
observer->NotifyMatch(*matcher, aDocInfo.GetLoadInfo());
|
|
} else {
|
|
observer->NotifyMatch(*matcher, aDocInfo.GetWindow());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* 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::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::ExtensionURILoadableByAnyone(nsIURI* aURI,
|
|
bool* aResult) {
|
|
URLInfo url(aURI);
|
|
if (WebExtensionPolicy* policy = GetByURL(url)) {
|
|
*aResult = policy->IsPathWebAccessible(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, mExtensionHosts,
|
|
mObservers)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtensionPolicyService)
|
|
NS_INTERFACE_MAP_ENTRY(nsIAddonPolicyService)
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
|
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
|
|
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
|