Bug 1443925 - Part 9: Allow checking webextension permissions from off-main-thread through WebExtensionPolicyCore, r=extension-reviewers,kmag

This requires migrating some members from WebExtensionPolicy to
WebExtensionPolicyCore. The mHostPermissions member could not be fully
transferred, as the WebIDL reflector needs to be cached for
WebExtensionPolicy.allowedOrigins, however the threadsafe core is shared.

Differential Revision: https://phabricator.services.mozilla.com/D163039
This commit is contained in:
Nika Layzell 2022-12-02 00:53:52 +00:00
parent c3e1048e3e
commit 5bd9a5e6a1
2 changed files with 68 additions and 20 deletions

View File

@ -184,7 +184,8 @@ WebExtensionPolicyCore::WebExtensionPolicyCore(GlobalObject& aGlobal,
mExtensionPageCSP(aInit.mExtensionPageCSP),
mIsPrivileged(aInit.mIsPrivileged),
mTemporarilyInstalled(aInit.mTemporarilyInstalled),
mBackgroundWorkerScript(aInit.mBackgroundWorkerScript) {
mBackgroundWorkerScript(aInit.mBackgroundWorkerScript),
mPermissions(new AtomSet(aInit.mPermissions)) {
// In practice this is not necessary, but in tests where the uuid
// passed in is not lowercased various tests can fail.
ToLowerCase(aInit.mMozExtensionHostname, mHostname);
@ -251,6 +252,20 @@ bool WebExtensionPolicyCore::SourceMayAccessPath(
return false;
}
bool WebExtensionPolicyCore::CanAccessURI(const URLInfo& aURI, bool aExplicit,
bool aCheckRestricted,
bool aAllowFilePermission) const {
if (aCheckRestricted && WebExtensionPolicy::IsRestrictedURI(aURI)) {
return false;
}
if (!aAllowFilePermission && aURI.Scheme() == nsGkAtoms::file) {
return false;
}
AutoReadLock lock(mLock);
return mHostPermissions && mHostPermissions->Matches(aURI, aExplicit);
}
/*****************************************************************************
* WebExtensionPolicy
*****************************************************************************/
@ -259,8 +274,7 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
const WebExtensionInit& aInit,
ErrorResult& aRv)
: mCore(new WebExtensionPolicyCore(aGlobal, this, aInit, aRv)),
mLocalizeCallback(aInit.mLocalizeCallback),
mPermissions(new AtomSet(aInit.mPermissions)) {
mLocalizeCallback(aInit.mLocalizeCallback) {
if (aRv.Failed()) {
return;
}
@ -268,11 +282,15 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
MatchPatternOptions options;
options.mRestrictSchemes = !HasPermission(nsGkAtoms::mozillaAddons);
mHostPermissions = ParseMatches(aGlobal, aInit.mAllowedOrigins, options,
ErrorBehavior::CreateEmptyPattern, aRv);
// Set host permissions with SetAllowedOrigins to make sure the copy in core
// and WebExtensionPolicy stay in sync.
RefPtr<MatchPatternSet> hostPermissions =
ParseMatches(aGlobal, aInit.mAllowedOrigins, options,
ErrorBehavior::CreateEmptyPattern, aRv);
if (aRv.Failed()) {
return;
}
SetAllowedOrigins(*hostPermissions);
if (!aInit.mBackgroundScripts.IsNull()) {
mBackgroundScripts.SetValue().AppendElements(
@ -438,12 +456,13 @@ void WebExtensionPolicy::UnregisterContentScript(
WebExtensionPolicy_Binding::ClearCachedContentScriptsValue(this);
}
bool WebExtensionPolicy::CanAccessURI(const URLInfo& aURI, bool aExplicit,
bool aCheckRestricted,
bool aAllowFilePermission) const {
return (!aCheckRestricted || !IsRestrictedURI(aURI)) && mHostPermissions &&
mHostPermissions->Matches(aURI, aExplicit) &&
(aURI.Scheme() != nsGkAtoms::file || aAllowFilePermission);
void WebExtensionPolicy::SetAllowedOrigins(MatchPatternSet& aAllowedOrigins) {
// Make sure to keep the version in `WebExtensionPolicy` (which can be exposed
// back to script using AllowedOrigins()), and the version in
// `WebExtensionPolicyCore` (which is threadsafe) in sync.
AutoWriteLock lock(mCore->mLock);
mHostPermissions = &aAllowedOrigins;
mCore->mHostPermissions = aAllowedOrigins.Core();
}
void WebExtensionPolicy::InjectContentScripts(ErrorResult& aRv) {

View File

@ -7,6 +7,7 @@
#define mozilla_extensions_WebExtensionPolicy_h
#include "MainThreadUtils.h"
#include "mozilla/RWLock.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/Nullable.h"
@ -111,6 +112,27 @@ class WebExtensionPolicyCore final {
bool SourceMayAccessPath(const URLInfo& aURI, const nsACString& aPath) const;
bool HasPermission(const nsAtom* aPermission) const {
AutoReadLock lock(mLock);
return mPermissions->Contains(aPermission);
}
void GetPermissions(nsTArray<nsString>& aResult) const MOZ_EXCLUDES(mLock) {
AutoReadLock lock(mLock);
return mPermissions->Get(aResult);
}
void SetPermissions(const nsTArray<nsString>& aPermissions)
MOZ_EXCLUDES(mLock) {
RefPtr<AtomSet> newPermissions = new AtomSet(aPermissions);
AutoWriteLock lock(mLock);
mPermissions = std::move(newPermissions);
}
bool CanAccessURI(const URLInfo& aURI, bool aExplicit = false,
bool aCheckRestricted = true,
bool aAllowFilePermission = false) const;
// Try to get a reference to the cycle-collected main-thread-only
// WebExtensionPolicy instance.
//
@ -155,6 +177,10 @@ class WebExtensionPolicyCore final {
const nsString mBackgroundWorkerScript;
/* const */ nsTArray<RefPtr<WebAccessibleResource>> mWebAccessibleResources;
mutable RWLock mLock{"WebExtensionPolicyCore"};
RefPtr<AtomSet> mPermissions MOZ_GUARDED_BY(mLock);
RefPtr<MatchPatternSetCore> mHostPermissions MOZ_GUARDED_BY(mLock);
};
class WebExtensionPolicy final : public nsISupports, public nsWrapperCache {
@ -203,7 +229,10 @@ class WebExtensionPolicy final : public nsISupports, public nsWrapperCache {
bool CanAccessURI(const URLInfo& aURI, bool aExplicit = false,
bool aCheckRestricted = true,
bool aAllowFilePermission = false) const;
bool aAllowFilePermission = false) const {
return mCore->CanAccessURI(aURI, aExplicit, aCheckRestricted,
aAllowFilePermission);
}
bool IsWebAccessiblePath(const nsACString& aPath) const {
return mCore->IsWebAccessiblePath(aPath);
@ -214,10 +243,11 @@ class WebExtensionPolicy final : public nsISupports, public nsWrapperCache {
}
bool HasPermission(const nsAtom* aPermission) const {
return mPermissions->Contains(aPermission);
return mCore->HasPermission(aPermission);
}
bool HasPermission(const nsAString& aPermission) const {
return mPermissions->Contains(aPermission);
RefPtr<nsAtom> atom = NS_AtomizeMainThread(aPermission);
return HasPermission(atom);
}
static bool IsRestrictedDoc(const DocInfo& aDoc);
@ -247,15 +277,13 @@ class WebExtensionPolicy final : public nsISupports, public nsWrapperCache {
already_AddRefed<MatchPatternSet> AllowedOrigins() {
return do_AddRef(mHostPermissions);
}
void SetAllowedOrigins(MatchPatternSet& aAllowedOrigins) {
mHostPermissions = &aAllowedOrigins;
}
void SetAllowedOrigins(MatchPatternSet& aAllowedOrigins);
void GetPermissions(nsTArray<nsString>& aResult) const {
mPermissions->Get(aResult);
mCore->GetPermissions(aResult);
}
void SetPermissions(const nsTArray<nsString>& aPermissions) {
mPermissions = new AtomSet(aPermissions);
mCore->SetPermissions(aPermissions);
}
void GetContentScripts(ScriptArray& aScripts) const;
@ -334,7 +362,8 @@ class WebExtensionPolicy final : public nsISupports, public nsWrapperCache {
RefPtr<WebExtensionLocalizeCallback> mLocalizeCallback;
RefPtr<AtomSet> mPermissions;
// NOTE: This is a mirror of the object in `mCore`, except with the
// non-threadsafe wrapper.
RefPtr<MatchPatternSet> mHostPermissions;
dom::Nullable<nsTArray<nsString>> mBackgroundScripts;