Bug 1793995 - Part 1: Move immutable parts of WebExtensionPolicy to a threadsafe core type, r=kmag

This threadsafe core type also acts as a weak reference to the main-thread
WebExtensionPolicy when needed. This will be used when information about a
WebExtension is needed to be accessible off-main-thread in the future.

Differential Revision: https://phabricator.services.mozilla.com/D158879
This commit is contained in:
Nika Layzell 2022-10-13 21:46:58 +00:00
parent 5388c4586e
commit 88e501708c
4 changed files with 193 additions and 96 deletions

View file

@ -513,17 +513,20 @@ nsresult ContentPrincipal::GetSiteIdentifier(SiteIdentifier& aSite) {
}
WebExtensionPolicy* ContentPrincipal::AddonPolicy() {
AssertIsOnMainThread();
if (!mAddon.isSome()) {
NS_ENSURE_TRUE(mURI, nullptr);
if (mURI->SchemeIs("moz-extension")) {
mAddon.emplace(EPS().GetByURL(mURI.get()));
} else {
mAddon.emplace(nullptr);
}
WebExtensionPolicy* policy =
mURI->SchemeIs("moz-extension") ? EPS().GetByURL(mURI.get()) : nullptr;
mAddon.emplace(policy ? policy->Core() : nullptr);
}
return mAddon.value();
if (extensions::WebExtensionPolicyCore* policy = mAddon.ref()) {
return policy->GetMainThreadPolicy();
}
return nullptr;
}
NS_IMETHODIMP

View file

@ -76,7 +76,7 @@ class ContentPrincipal final : public BasePrincipal {
private:
const nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mDomain;
Maybe<WeakPtr<extensions::WebExtensionPolicy>> mAddon;
Maybe<RefPtr<extensions::WebExtensionPolicyCore>> mAddon;
};
} // namespace mozilla

View file

@ -3,6 +3,7 @@
* 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 "MainThreadUtils.h"
#include "mozilla/ExtensionPolicyService.h"
#include "mozilla/extensions/DocumentObserver.h"
#include "mozilla/extensions/WebExtensionContentScript.h"
@ -18,6 +19,7 @@
#include "nsGlobalWindowInner.h"
#include "nsIObserver.h"
#include "nsISubstitutingProtocolHandler.h"
#include "nsLiteralString.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
@ -173,6 +175,53 @@ NS_IMPL_CYCLE_COLLECTION(WebAccessibleResource)
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAccessibleResource)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAccessibleResource)
/*****************************************************************************
* WebExtensionPolicyCore
*****************************************************************************/
WebExtensionPolicyCore::WebExtensionPolicyCore(WebExtensionPolicy* aPolicy,
const WebExtensionInit& aInit,
ErrorResult& aRv)
: mPolicy(aPolicy),
mId(NS_AtomizeMainThread(aInit.mId)),
mName(aInit.mName),
mType(NS_AtomizeMainThread(aInit.mType)),
mManifestVersion(aInit.mManifestVersion),
mExtensionPageCSP(aInit.mExtensionPageCSP),
mIsPrivileged(aInit.mIsPrivileged),
mTemporarilyInstalled(aInit.mTemporarilyInstalled),
mBackgroundWorkerScript(aInit.mBackgroundWorkerScript) {
// 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);
// Initialize the base CSP and extension page CSP
if (mManifestVersion < 3) {
nsresult rv = Preferences::GetString(BASE_CSP_PREF_V2, mBaseCSP);
if (NS_FAILED(rv)) {
mBaseCSP = NS_LITERAL_STRING_FROM_CSTRING(DEFAULT_BASE_CSP_V2);
}
} else {
nsresult rv = Preferences::GetString(BASE_CSP_PREF_V3, mBaseCSP);
if (NS_FAILED(rv)) {
mBaseCSP = NS_LITERAL_STRING_FROM_CSTRING(DEFAULT_BASE_CSP_V3);
}
}
if (mExtensionPageCSP.IsVoid()) {
if (mManifestVersion < 3) {
EPS().GetDefaultCSP(mExtensionPageCSP);
} else {
EPS().GetDefaultCSPV3(mExtensionPageCSP);
}
}
nsresult rv = NS_NewURI(getter_AddRefs(mBaseURI), aInit.mBaseURL);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
/*****************************************************************************
* WebExtensionPolicy
*****************************************************************************/
@ -180,22 +229,16 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAccessibleResource)
WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
const WebExtensionInit& aInit,
ErrorResult& aRv)
: mId(NS_AtomizeMainThread(aInit.mId)),
mName(aInit.mName),
mType(NS_AtomizeMainThread(aInit.mType)),
mManifestVersion(aInit.mManifestVersion),
mExtensionPageCSP(aInit.mExtensionPageCSP),
: mCore(new WebExtensionPolicyCore(this, aInit, aRv)),
mLocalizeCallback(aInit.mLocalizeCallback),
mIsPrivileged(aInit.mIsPrivileged),
mTemporarilyInstalled(aInit.mTemporarilyInstalled),
mPermissions(new AtomSet(aInit.mPermissions)) {
if (aRv.Failed()) {
return;
}
MatchPatternOptions options;
options.mRestrictSchemes = !HasPermission(nsGkAtoms::mozillaAddons);
// 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);
mHostPermissions = ParseMatches(aGlobal, aInit.mAllowedOrigins, options,
ErrorBehavior::CreateEmptyPattern, aRv);
if (aRv.Failed()) {
@ -207,20 +250,6 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
aInit.mBackgroundScripts.Value());
}
if (!aInit.mBackgroundWorkerScript.IsEmpty()) {
mBackgroundWorkerScript.Assign(aInit.mBackgroundWorkerScript);
}
InitializeBaseCSP();
if (mExtensionPageCSP.IsVoid()) {
if (mManifestVersion < 3) {
EPS().GetDefaultCSP(mExtensionPageCSP);
} else {
EPS().GetDefaultCSPV3(mExtensionPageCSP);
}
}
mWebAccessibleResources.SetCapacity(aInit.mWebAccessibleResources.Length());
for (const auto& resourceInit : aInit.mWebAccessibleResources) {
RefPtr<WebAccessibleResource> resource =
@ -251,11 +280,6 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
if (aInit.mReadyPromise.WasPassed()) {
mReadyPromise = &aInit.mReadyPromise.Value();
}
nsresult rv = NS_NewURI(getter_AddRefs(mBaseURI), aInit.mBaseURL);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
already_AddRefed<WebExtensionPolicy> WebExtensionPolicy::Constructor(
@ -268,22 +292,6 @@ already_AddRefed<WebExtensionPolicy> WebExtensionPolicy::Constructor(
return policy.forget();
}
void WebExtensionPolicy::InitializeBaseCSP() {
if (mManifestVersion < 3) {
nsresult rv = Preferences::GetString(BASE_CSP_PREF_V2, mBaseCSP);
if (NS_FAILED(rv)) {
mBaseCSP.AssignLiteral(DEFAULT_BASE_CSP_V2);
}
return;
}
// Version 3 or higher.
nsresult rv = Preferences::GetString(BASE_CSP_PREF_V3, mBaseCSP);
if (NS_FAILED(rv)) {
mBaseCSP.AssignLiteral(DEFAULT_BASE_CSP_V3);
}
}
/* static */
void WebExtensionPolicy::GetActiveExtensions(
dom::GlobalObject& aGlobal,
@ -334,7 +342,7 @@ bool WebExtensionPolicy::Enable() {
mBrowsingContextGroup = group->MakeKeepAlivePtr();
}
Unused << Proto()->SetSubstitution(MozExtensionHostname(), mBaseURI);
Unused << Proto()->SetSubstitution(MozExtensionHostname(), BaseURI());
mActive = true;
return true;
@ -372,7 +380,7 @@ void WebExtensionPolicy::GetURL(const nsAString& aPath, nsAString& aResult,
Result<nsString, nsresult> WebExtensionPolicy::GetURL(
const nsAString& aPath) const {
nsPrintfCString spec("%s://%s/", kProto, mHostname.get());
nsPrintfCString spec("%s://%s/", kProto, MozExtensionHostname().get());
nsCOMPtr<nsIURI> uri;
MOZ_TRY(NS_NewURI(getter_AddRefs(uri), spec));
@ -444,17 +452,17 @@ bool WebExtensionPolicy::BackgroundServiceWorkerEnabled(GlobalObject& aGlobal) {
bool WebExtensionPolicy::SourceMayAccessPath(const URLInfo& aURI,
const nsACString& aPath) const {
if (aURI.Scheme() == nsGkAtoms::moz_extension &&
mHostname.Equals(aURI.Host())) {
MozExtensionHostname().Equals(aURI.Host())) {
// An extension can always access it's own paths.
return true;
}
// Bug 1786564 Static themes need to allow access to theme resources.
if (mType == nsGkAtoms::theme) {
if (Type() == nsGkAtoms::theme) {
WebExtensionPolicy* policy = EPS().GetByHost(aURI.Host());
return policy != nullptr;
}
if (mManifestVersion < 3) {
if (ManifestVersion() < 3) {
return IsWebAccessiblePath(aPath);
}
for (const auto& resource : mWebAccessibleResources) {
@ -633,9 +641,28 @@ uint64_t WebExtensionPolicy::GetBrowsingContextGroupId(ErrorResult& aRv) {
return 0;
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(
WebExtensionPolicy, mParent, mBrowsingContextGroup, mLocalizeCallback,
mHostPermissions, mWebAccessibleResources, mContentScripts)
WebExtensionPolicy::~WebExtensionPolicy() { mCore->ClearPolicyWeakRef(); }
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WebExtensionPolicy)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebExtensionPolicy)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContextGroup)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalizeCallback)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHostPermissions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebAccessibleResources)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContentScripts)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
AssertIsOnMainThread();
tmp->mCore->ClearPolicyWeakRef();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WebExtensionPolicy)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContextGroup)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalizeCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHostPermissions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebAccessibleResources)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentScripts)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionPolicy)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY

View file

@ -6,6 +6,7 @@
#ifndef mozilla_extensions_WebExtensionPolicy_h
#define mozilla_extensions_WebExtensionPolicy_h
#include "MainThreadUtils.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/Nullable.h"
@ -70,9 +71,80 @@ class WebAccessibleResource final : public nsISupports {
RefPtr<AtomSet> mExtensionIDs;
};
class WebExtensionPolicy final : public nsISupports,
public nsWrapperCache,
public SupportsWeakPtr {
/// The thread-safe component of the WebExtensionPolicy.
///
/// Acts as a weak reference to the base WebExtensionPolicy.
class WebExtensionPolicyCore final {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebExtensionPolicyCore)
nsAtom* Id() const { return mId; }
const nsCString& MozExtensionHostname() const { return mHostname; }
nsIURI* BaseURI() const { return mBaseURI; }
bool IsPrivileged() { return mIsPrivileged; }
bool TemporarilyInstalled() { return mTemporarilyInstalled; }
const nsString& Name() const { return mName; }
nsAtom* Type() const { return mType; }
uint32_t ManifestVersion() const { return mManifestVersion; }
const nsString& ExtensionPageCSP() const { return mExtensionPageCSP; }
const nsString& BaseCSP() const { return mBaseCSP; }
const nsString& BackgroundWorkerScript() const {
return mBackgroundWorkerScript;
}
// Try to get a reference to the cycle-collected main-thread-only
// WebExtensionPolicy instance.
//
// Will return nullptr if the policy has already been unlinked or destroyed.
WebExtensionPolicy* GetMainThreadPolicy() const
MOZ_REQUIRES(sMainThreadCapability) {
return mPolicy;
}
private:
friend class WebExtensionPolicy;
WebExtensionPolicyCore(WebExtensionPolicy* aPolicy,
const WebExtensionInit& aInit, ErrorResult& aRv);
~WebExtensionPolicyCore() = default;
void ClearPolicyWeakRef() MOZ_REQUIRES(sMainThreadCapability) {
mPolicy = nullptr;
}
// Unless otherwise guarded by a capability, all members on
// WebExtensionPolicyCore should be immutable and threadsafe.
WebExtensionPolicy* MOZ_NON_OWNING_REF mPolicy
MOZ_GUARDED_BY(sMainThreadCapability);
const RefPtr<nsAtom> mId;
/* const */ nsCString mHostname;
/* const */ nsCOMPtr<nsIURI> mBaseURI;
const nsString mName;
const RefPtr<nsAtom> mType;
const uint32_t mManifestVersion;
/* const */ nsString mExtensionPageCSP;
/* const */ nsString mBaseCSP;
const bool mIsPrivileged;
const bool mTemporarilyInstalled;
const nsString mBackgroundWorkerScript;
};
class WebExtensionPolicy final : public nsISupports, public nsWrapperCache {
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WebExtensionPolicy)
@ -83,21 +155,26 @@ class WebExtensionPolicy final : public nsISupports,
dom::GlobalObject& aGlobal, const WebExtensionInit& aInit,
ErrorResult& aRv);
nsAtom* Id() const { return mId; }
void GetId(nsAString& aId) const { aId = nsDependentAtomString(mId); };
WebExtensionPolicyCore* Core() const { return mCore; }
const nsCString& MozExtensionHostname() const { return mHostname; }
nsAtom* Id() const { return mCore->Id(); }
void GetId(nsAString& aId) const { aId = nsDependentAtomString(Id()); };
const nsCString& MozExtensionHostname() const {
return mCore->MozExtensionHostname();
}
void GetMozExtensionHostname(nsACString& aHostname) const {
aHostname = MozExtensionHostname();
}
nsIURI* BaseURI() const { return mCore->BaseURI(); }
void GetBaseURL(nsACString& aBaseURL) const {
MOZ_ALWAYS_SUCCEEDS(mBaseURI->GetSpec(aBaseURL));
MOZ_ALWAYS_SUCCEEDS(mCore->BaseURI()->GetSpec(aBaseURL));
}
bool IsPrivileged() { return mIsPrivileged; }
bool IsPrivileged() { return mCore->IsPrivileged(); }
bool TemporarilyInstalled() { return mTemporarilyInstalled; }
bool TemporarilyInstalled() { return mCore->TemporarilyInstalled(); }
void GetURL(const nsAString& aPath, nsAString& aURL, ErrorResult& aRv) const;
@ -141,21 +218,21 @@ class WebExtensionPolicy final : public nsISupports,
MOZ_CAN_RUN_SCRIPT
void Localize(const nsAString& aInput, nsString& aResult) const;
const nsString& Name() const { return mName; }
void GetName(nsAString& aName) const { aName = mName; }
const nsString& Name() const { return mCore->Name(); }
void GetName(nsAString& aName) const { aName = Name(); }
nsAtom* Type() const { return mType; }
nsAtom* Type() const { return mCore->Type(); }
void GetType(nsAString& aType) const {
aType = nsDependentAtomString(mType);
aType = nsDependentAtomString(Type());
};
uint32_t ManifestVersion() const { return mManifestVersion; }
uint32_t ManifestVersion() const { return mCore->ManifestVersion(); }
const nsString& ExtensionPageCSP() const { return mExtensionPageCSP; }
void GetExtensionPageCSP(nsAString& aCSP) const { aCSP = mExtensionPageCSP; }
const nsString& ExtensionPageCSP() const { return mCore->ExtensionPageCSP(); }
void GetExtensionPageCSP(nsAString& aCSP) const { aCSP = ExtensionPageCSP(); }
const nsString& BaseCSP() const { return mBaseCSP; }
void GetBaseCSP(nsAString& aCSP) const { aCSP = mBaseCSP; }
const nsString& BaseCSP() const { return mCore->BaseCSP(); }
void GetBaseCSP(nsAString& aCSP) const { aCSP = BaseCSP(); }
already_AddRefed<MatchPatternSet> AllowedOrigins() {
return do_AddRef(mHostPermissions);
@ -187,12 +264,15 @@ class WebExtensionPolicy final : public nsISupports,
JS::MutableHandle<JSObject*> aResult) const;
dom::Promise* ReadyPromise() const { return mReadyPromise; }
const nsString& BackgroundWorkerScript() const {
return mCore->BackgroundWorkerScript();
}
void GetBackgroundWorker(nsString& aScriptURL) const {
aScriptURL.Assign(mBackgroundWorkerScript);
aScriptURL.Assign(BackgroundWorkerScript());
}
bool IsManifestBackgroundWorker(const nsAString& aWorkerScriptURL) const {
return mBackgroundWorkerScript.Equals(aWorkerScriptURL);
return BackgroundWorkerScript().Equals(aWorkerScriptURL);
}
uint64_t GetBrowsingContextGroupId() const;
@ -225,7 +305,7 @@ class WebExtensionPolicy final : public nsISupports,
JS::Handle<JSObject*> aGivenProto) override;
protected:
virtual ~WebExtensionPolicy() = default;
~WebExtensionPolicy();
private:
WebExtensionPolicy(dom::GlobalObject& aGlobal, const WebExtensionInit& aInit,
@ -233,19 +313,10 @@ class WebExtensionPolicy final : public nsISupports,
bool Enable();
bool Disable();
void InitializeBaseCSP();
nsCOMPtr<nsISupports> mParent;
RefPtr<nsAtom> mId;
nsCString mHostname;
nsCOMPtr<nsIURI> mBaseURI;
nsString mName;
RefPtr<nsAtom> mType;
uint32_t mManifestVersion = 2;
nsString mExtensionPageCSP;
nsString mBaseCSP;
RefPtr<WebExtensionPolicyCore> mCore;
dom::BrowsingContextGroup::KeepAlivePtr mBrowsingContextGroup;
@ -253,14 +324,10 @@ class WebExtensionPolicy final : public nsISupports,
RefPtr<WebExtensionLocalizeCallback> mLocalizeCallback;
bool mIsPrivileged;
bool mTemporarilyInstalled;
RefPtr<AtomSet> mPermissions;
RefPtr<MatchPatternSet> mHostPermissions;
dom::Nullable<nsTArray<nsString>> mBackgroundScripts;
nsString mBackgroundWorkerScript;
nsTArray<RefPtr<WebAccessibleResource>> mWebAccessibleResources;
nsTArray<RefPtr<WebExtensionContentScript>> mContentScripts;