Bug 1708238 - Stop relying on messagemanager in ExtensionPolicyService r=robwu,geckoview-reviewers,agi

Patch mostly by Nika Layzell; test, some tweaks (and all bugs) by me.

Differential Revision: https://phabricator.services.mozilla.com/D114060
This commit is contained in:
Tomislav Jovanovic 2021-05-10 16:55:09 +00:00
parent 611ced6f1e
commit c5fa3a1d8d
6 changed files with 91 additions and 112 deletions

View File

@ -500,6 +500,7 @@ function createBrowser() {
browser.setAttribute("maychangeremoteness", "true"); browser.setAttribute("maychangeremoteness", "true");
browser.setAttribute("remote", "true"); browser.setAttribute("remote", "true");
browser.setAttribute("remoteType", E10SUtils.DEFAULT_REMOTE_TYPE); browser.setAttribute("remoteType", E10SUtils.DEFAULT_REMOTE_TYPE);
browser.setAttribute("messagemanagergroup", "browsers");
return browser; return browser;
} }

View File

@ -1172,6 +1172,7 @@ class HiddenXULWindow {
const browser = chromeDoc.createXULElement("browser"); const browser = chromeDoc.createXULElement("browser");
browser.setAttribute("type", "content"); browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true"); browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("messagemanagergroup", "webext-browsers");
for (const [name, value] of Object.entries(xulAttributes)) { for (const [name, value] of Object.entries(xulAttributes)) {
if (value != null) { if (value != null) {

View File

@ -15,18 +15,17 @@
#include "mozilla/Services.h" #include "mozilla/Services.h"
#include "mozilla/SimpleEnumerator.h" #include "mozilla/SimpleEnumerator.h"
#include "mozilla/StaticPrefs_extensions.h" #include "mozilla/StaticPrefs_extensions.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentFrameMessageManager.h"
#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h" #include "mozilla/dom/Promise-inl.h"
#include "mozIExtensionProcessScript.h" #include "mozIExtensionProcessScript.h"
#include "nsDocShell.h"
#include "nsEscape.h" #include "nsEscape.h"
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
#include "nsIChannel.h" #include "nsIChannel.h"
#include "nsIContentPolicy.h" #include "nsIContentPolicy.h"
#include "nsIDocShell.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "nsGlobalWindowOuter.h" #include "nsGlobalWindowOuter.h"
#include "nsILoadInfo.h" #include "nsILoadInfo.h"
@ -43,7 +42,6 @@ namespace mozilla {
using namespace extensions; using namespace extensions;
using dom::AutoJSAPI; using dom::AutoJSAPI;
using dom::ContentFrameMessageManager;
using dom::Document; using dom::Document;
using dom::Promise; using dom::Promise;
@ -207,7 +205,6 @@ ExtensionPolicyService::CollectReports(nsIHandleReportCallback* aHandleReport,
void ExtensionPolicyService::RegisterObservers() { void ExtensionPolicyService::RegisterObservers() {
mObs->AddObserver(this, kDocElementInserted, false); mObs->AddObserver(this, kDocElementInserted, false);
mObs->AddObserver(this, "tab-content-frameloader-created", false);
if (XRE_IsContentProcess()) { if (XRE_IsContentProcess()) {
mObs->AddObserver(this, "http-on-opening-request", false); mObs->AddObserver(this, "http-on-opening-request", false);
mObs->AddObserver(this, "document-on-opening-request", false); mObs->AddObserver(this, "document-on-opening-request", false);
@ -218,7 +215,6 @@ void ExtensionPolicyService::RegisterObservers() {
void ExtensionPolicyService::UnregisterObservers() { void ExtensionPolicyService::UnregisterObservers() {
mObs->RemoveObserver(this, kDocElementInserted); mObs->RemoveObserver(this, kDocElementInserted);
mObs->RemoveObserver(this, "tab-content-frameloader-created");
if (XRE_IsContentProcess()) { if (XRE_IsContentProcess()) {
mObs->RemoveObserver(this, "http-on-opening-request"); mObs->RemoveObserver(this, "http-on-opening-request");
mObs->RemoveObserver(this, "document-on-opening-request"); mObs->RemoveObserver(this, "document-on-opening-request");
@ -241,13 +237,6 @@ nsresult ExtensionPolicyService::Observe(nsISupports* aSubject,
if (chan) { if (chan) {
CheckRequest(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.Insert(mm);
mm->AddSystemEventListener(u"unload"_ns, this, false, false);
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
const nsCString converted = NS_ConvertUTF16toUTF8(aData); const nsCString converted = NS_ConvertUTF16toUTF8(aData);
const char* pref = converted.get(); const char* pref = converted.get();
@ -258,28 +247,6 @@ nsresult ExtensionPolicyService::Observe(nsISupports* aSubject,
return NS_OK; return NS_OK;
} }
nsresult ExtensionPolicyService::HandleEvent(dom::Event* aEvent) {
RefPtr<ContentFrameMessageManager> mm = do_QueryObject(aEvent->GetTarget());
MOZ_ASSERT(mm);
if (mm) {
mMessageManagers.Remove(mm);
}
return NS_OK;
}
nsresult ForEachDocShell(
nsIDocShell* aDocShell,
const std::function<nsresult(nsIDocShell*)>& aCallback) {
nsTArray<RefPtr<nsIDocShell>> docShells;
MOZ_TRY(aDocShell->GetAllDocShellsInSubtree(
nsIDocShell::typeContent, nsIDocShell::ENUMERATE_FORWARDS, docShells));
for (auto& docShell : docShells) {
MOZ_TRY(aCallback(docShell));
}
return NS_OK;
}
already_AddRefed<Promise> ExtensionPolicyService::ExecuteContentScript( already_AddRefed<Promise> ExtensionPolicyService::ExecuteContentScript(
nsPIDOMWindowInner* aWindow, WebExtensionContentScript& aScript) { nsPIDOMWindowInner* aWindow, WebExtensionContentScript& aScript) {
if (!aWindow->IsCurrentInnerWindow()) { if (!aWindow->IsCurrentInnerWindow()) {
@ -307,68 +274,88 @@ RefPtr<Promise> ExtensionPolicyService::ExecuteContentScripts(
return 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();
return group == u"browsers"_ns || group == u"webext-browsers"_ns;
}
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( nsresult ExtensionPolicyService::InjectContentScripts(
WebExtensionPolicy* aExtension) { WebExtensionPolicy* aExtension) {
AutoJSAPI jsapi; AutoJSAPI jsapi;
MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope())); MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
for (ContentFrameMessageManager* mm : mMessageManagers) { auto contentBCs = GetAllInProcessContentBCs();
nsCOMPtr<nsIDocShell> docShell = mm->GetDocShell(IgnoreErrors()); for (dom::BrowsingContext* bc : contentBCs) {
NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED); auto* win = bc->GetDOMWindow();
auto result = if (bc->Top()->IsDiscarded() || !win || !win->GetDocumentURI()) {
ForEachDocShell(docShell, [&](nsIDocShell* aDocShell) -> nsresult { continue;
nsCOMPtr<nsPIDOMWindowOuter> win = aDocShell->GetWindow(); }
if (!win->GetDocumentURI()) { DocInfo docInfo(win);
return NS_OK;
}
DocInfo docInfo(win);
using RunAt = dom::ContentScriptRunAt; using RunAt = dom::ContentScriptRunAt;
namespace RunAtValues = dom::ContentScriptRunAtValues; namespace RunAtValues = dom::ContentScriptRunAtValues;
using Scripts = AutoTArray<RefPtr<WebExtensionContentScript>, 8>; using Scripts = AutoTArray<RefPtr<WebExtensionContentScript>, 8>;
Scripts scripts[RunAtValues::Count]; Scripts scripts[RunAtValues::Count];
auto GetScripts = [&](RunAt aRunAt) -> Scripts&& { auto GetScripts = [&](RunAt aRunAt) -> Scripts&& {
static_assert(sizeof(aRunAt) == 1, "Our cast is wrong"); static_assert(sizeof(aRunAt) == 1, "Our cast is wrong");
return std::move(scripts[uint8_t(aRunAt)]); return std::move(scripts[uint8_t(aRunAt)]);
}; };
for (const auto& script : aExtension->ContentScripts()) { for (const auto& script : aExtension->ContentScripts()) {
if (script->Matches(docInfo)) { if (script->Matches(docInfo)) {
GetScripts(script->RunAt()).AppendElement(script); GetScripts(script->RunAt()).AppendElement(script);
} }
} }
nsCOMPtr<nsPIDOMWindowInner> inner = win->GetCurrentInnerWindow(); nsCOMPtr<nsPIDOMWindowInner> inner = win->GetCurrentInnerWindow();
MOZ_TRY(ExecuteContentScripts(jsapi.cx(), inner, MOZ_TRY(ExecuteContentScripts(jsapi.cx(), inner,
GetScripts(RunAt::Document_start)) GetScripts(RunAt::Document_start))
->ThenWithCycleCollectedArgs( ->ThenWithCycleCollectedArgs(
[](JSContext* aCx, JS::HandleValue aValue, [](JSContext* aCx, JS::HandleValue aValue,
ExtensionPolicyService* aSelf, ExtensionPolicyService* aSelf,
nsPIDOMWindowInner* aInner, Scripts&& aScripts) { nsPIDOMWindowInner* aInner, Scripts&& aScripts) {
return aSelf return aSelf->ExecuteContentScripts(aCx, aInner, aScripts)
->ExecuteContentScripts(aCx, aInner, aScripts) .forget();
.forget(); },
}, this, inner, GetScripts(RunAt::Document_end))
this, inner, GetScripts(RunAt::Document_end)) .andThen([&](auto aPromise) {
.andThen([&](auto aPromise) { return aPromise->ThenWithCycleCollectedArgs(
return aPromise->ThenWithCycleCollectedArgs( [](JSContext* aCx, JS::HandleValue aValue,
[](JSContext* aCx, JS::HandleValue aValue, ExtensionPolicyService* aSelf,
ExtensionPolicyService* aSelf, nsPIDOMWindowInner* aInner, Scripts&& aScripts) {
nsPIDOMWindowInner* aInner, Scripts&& aScripts) { return aSelf
return aSelf ->ExecuteContentScripts(aCx, aInner, aScripts)
->ExecuteContentScripts(aCx, aInner, aScripts) .forget();
.forget(); },
}, this, inner, GetScripts(RunAt::Document_idle));
this, inner, GetScripts(RunAt::Document_idle)); }));
}));
return NS_OK;
});
MOZ_TRY(result);
} }
return NS_OK; return NS_OK;
} }
@ -438,20 +425,7 @@ static bool CheckParentFrames(nsPIDOMWindowOuter* aWindow,
void ExtensionPolicyService::CheckDocument(Document* aDocument) { void ExtensionPolicyService::CheckDocument(Document* aDocument) {
nsCOMPtr<nsPIDOMWindowOuter> win = aDocument->GetWindow(); nsCOMPtr<nsPIDOMWindowOuter> win = aDocument->GetWindow();
if (win) { if (win) {
nsIDocShell* docShell = win->GetDocShell(); if (!IsTabOrExtensionBrowser(win->GetBrowsingContext())) {
RefPtr<ContentFrameMessageManager> mm = docShell->GetMessageManager();
nsString group = win->GetBrowsingContext()->Top()->GetMessageManagerGroup();
// Currently, we use frame scripts to select specific kinds of browsers
// where we want to run content scripts.
if ((!mm || !mMessageManagers.Contains(mm)) &&
// With Fission, OOP iframes don't have a frame message manager, so we
// use the browser's MessageManagerGroup attribute to decide if content
// scripts should run. The "browsers" group includes iframes from tabs,
// and the "webext-browsers" group includes custom browsers for
// extension popups/sidebars and xpcshell tests.
!group.EqualsLiteral("browsers") &&
!group.EqualsLiteral("webext-browsers")) {
return; return;
} }
@ -627,7 +601,6 @@ NS_IMPL_CYCLE_COLLECTION(ExtensionPolicyService, mExtensions, mExtensionHosts,
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtensionPolicyService) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtensionPolicyService)
NS_INTERFACE_MAP_ENTRY(nsIAddonPolicyService) NS_INTERFACE_MAP_ENTRY(nsIAddonPolicyService)
NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter) NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAddonPolicyService) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAddonPolicyService)
NS_INTERFACE_MAP_END NS_INTERFACE_MAP_END

View File

@ -13,7 +13,6 @@
#include "nsHashKeys.h" #include "nsHashKeys.h"
#include "nsIAddonPolicyService.h" #include "nsIAddonPolicyService.h"
#include "nsAtom.h" #include "nsAtom.h"
#include "nsIDOMEventListener.h"
#include "nsIMemoryReporter.h" #include "nsIMemoryReporter.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsIObserverService.h" #include "nsIObserverService.h"
@ -30,7 +29,6 @@ class nsIPIDOMWindowOuter;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
class ContentFrameMessageManager;
class Promise; class Promise;
} // namespace dom } // namespace dom
namespace extensions { namespace extensions {
@ -44,7 +42,6 @@ using extensions::WebExtensionPolicy;
class ExtensionPolicyService final : public nsIAddonPolicyService, class ExtensionPolicyService final : public nsIAddonPolicyService,
public nsIObserver, public nsIObserver,
public nsIDOMEventListener,
public nsIMemoryReporter { public nsIMemoryReporter {
public: public:
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ExtensionPolicyService, NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ExtensionPolicyService,
@ -52,7 +49,6 @@ class ExtensionPolicyService final : public nsIAddonPolicyService,
NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIADDONPOLICYSERVICE NS_DECL_NSIADDONPOLICYSERVICE
NS_DECL_NSIOBSERVER NS_DECL_NSIOBSERVER
NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_NSIMEMORYREPORTER NS_DECL_NSIMEMORYREPORTER
static ExtensionPolicyService& GetSingleton(); static ExtensionPolicyService& GetSingleton();
@ -114,8 +110,6 @@ class ExtensionPolicyService final : public nsIAddonPolicyService,
nsRefPtrHashtable<nsPtrHashKey<const nsAtom>, WebExtensionPolicy> mExtensions; nsRefPtrHashtable<nsPtrHashKey<const nsAtom>, WebExtensionPolicy> mExtensions;
nsRefPtrHashtable<nsCStringHashKey, WebExtensionPolicy> mExtensionHosts; nsRefPtrHashtable<nsCStringHashKey, WebExtensionPolicy> mExtensionHosts;
nsTHashSet<RefPtr<dom::ContentFrameMessageManager>> mMessageManagers;
nsRefPtrHashtable<nsPtrHashKey<const extensions::DocumentObserver>, nsRefPtrHashtable<nsPtrHashKey<const extensions::DocumentObserver>,
extensions::DocumentObserver> extensions::DocumentObserver>
mObservers; mObservers;

View File

@ -11,7 +11,7 @@
add_task(async function test_content_script_cross_origin_frame() { add_task(async function test_content_script_cross_origin_frame() {
const extension = ExtensionTestUtils.loadExtension({ const extensionData = {
manifest: { manifest: {
content_scripts: [{ content_scripts: [{
matches: ["http://example.net/*/file_sample.html"], matches: ["http://example.net/*/file_sample.html"],
@ -84,17 +84,26 @@ add_task(async function test_content_script_cross_origin_frame() {
port.postMessage(7); port.postMessage(7);
}, },
}, },
}); };
await extension.startup(); info("Load first extension");
let ext1 = ExtensionTestUtils.loadExtension(extensionData);
await ext1.startup();
info("Load a page, test content scripts in new frame with extension loaded");
let base = "http://example.org/tests/toolkit/components/extensions/test"; let base = "http://example.org/tests/toolkit/components/extensions/test";
let win = window.open(`${base}/mochitest/file_with_xorigin_frame.html`); let win = window.open(`${base}/mochitest/file_with_xorigin_frame.html`);
await extension.awaitFinish(); await ext1.awaitFinish();
win.close(); await ext1.unload();
await extension.unload(); info("Load second extension, test content scripts in existing frame");
let ext2 = ExtensionTestUtils.loadExtension(extensionData);
await ext2.startup();
await ext2.awaitFinish();
win.close();
await ext2.unload();
}); });
</script> </script>

View File

@ -2379,6 +2379,7 @@ class InlineOptionsBrowser extends HTMLElement {
let browser = document.createXULElement("browser"); let browser = document.createXULElement("browser");
browser.setAttribute("type", "content"); browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true"); browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("messagemanagergroup", "webext-browsers");
browser.setAttribute("id", "addon-inline-options"); browser.setAttribute("id", "addon-inline-options");
browser.setAttribute("transparent", "true"); browser.setAttribute("transparent", "true");
browser.setAttribute("forcemessagemanager", "true"); browser.setAttribute("forcemessagemanager", "true");