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("remote", "true");
browser.setAttribute("remoteType", E10SUtils.DEFAULT_REMOTE_TYPE);
browser.setAttribute("messagemanagergroup", "browsers");
return browser;
}

View File

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

View File

@ -15,18 +15,17 @@
#include "mozilla/Services.h"
#include "mozilla/SimpleEnumerator.h"
#include "mozilla/StaticPrefs_extensions.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowsingContextGroup.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"
@ -43,7 +42,6 @@ namespace mozilla {
using namespace extensions;
using dom::AutoJSAPI;
using dom::ContentFrameMessageManager;
using dom::Document;
using dom::Promise;
@ -207,7 +205,6 @@ ExtensionPolicyService::CollectReports(nsIHandleReportCallback* aHandleReport,
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);
mObs->AddObserver(this, "document-on-opening-request", false);
@ -218,7 +215,6 @@ void ExtensionPolicyService::RegisterObservers() {
void ExtensionPolicyService::UnregisterObservers() {
mObs->RemoveObserver(this, kDocElementInserted);
mObs->RemoveObserver(this, "tab-content-frameloader-created");
if (XRE_IsContentProcess()) {
mObs->RemoveObserver(this, "http-on-opening-request");
mObs->RemoveObserver(this, "document-on-opening-request");
@ -241,13 +237,6 @@ nsresult ExtensionPolicyService::Observe(nsISupports* 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.Insert(mm);
mm->AddSystemEventListener(u"unload"_ns, this, false, false);
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
const nsCString converted = NS_ConvertUTF16toUTF8(aData);
const char* pref = converted.get();
@ -258,28 +247,6 @@ nsresult ExtensionPolicyService::Observe(nsISupports* aSubject,
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(
nsPIDOMWindowInner* aWindow, WebExtensionContentScript& aScript) {
if (!aWindow->IsCurrentInnerWindow()) {
@ -307,68 +274,88 @@ RefPtr<Promise> ExtensionPolicyService::ExecuteContentScripts(
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(
WebExtensionPolicy* aExtension) {
AutoJSAPI jsapi;
MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
for (ContentFrameMessageManager* mm : mMessageManagers) {
nsCOMPtr<nsIDocShell> docShell = mm->GetDocShell(IgnoreErrors());
NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
auto contentBCs = GetAllInProcessContentBCs();
for (dom::BrowsingContext* bc : contentBCs) {
auto* win = bc->GetDOMWindow();
auto result =
ForEachDocShell(docShell, [&](nsIDocShell* aDocShell) -> nsresult {
nsCOMPtr<nsPIDOMWindowOuter> win = aDocShell->GetWindow();
if (!win->GetDocumentURI()) {
return NS_OK;
}
DocInfo docInfo(win);
if (bc->Top()->IsDiscarded() || !win || !win->GetDocumentURI()) {
continue;
}
DocInfo docInfo(win);
using RunAt = dom::ContentScriptRunAt;
namespace RunAtValues = dom::ContentScriptRunAtValues;
using Scripts = AutoTArray<RefPtr<WebExtensionContentScript>, 8>;
using RunAt = dom::ContentScriptRunAt;
namespace RunAtValues = dom::ContentScriptRunAtValues;
using Scripts = AutoTArray<RefPtr<WebExtensionContentScript>, 8>;
Scripts scripts[RunAtValues::Count];
Scripts scripts[RunAtValues::Count];
auto GetScripts = [&](RunAt aRunAt) -> Scripts&& {
static_assert(sizeof(aRunAt) == 1, "Our cast is wrong");
return std::move(scripts[uint8_t(aRunAt)]);
};
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);
}
}
for (const auto& script : aExtension->ContentScripts()) {
if (script->Matches(docInfo)) {
GetScripts(script->RunAt()).AppendElement(script);
}
}
nsCOMPtr<nsPIDOMWindowInner> inner = win->GetCurrentInnerWindow();
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);
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;
}
@ -438,20 +425,7 @@ static bool CheckParentFrames(nsPIDOMWindowOuter* aWindow,
void ExtensionPolicyService::CheckDocument(Document* aDocument) {
nsCOMPtr<nsPIDOMWindowOuter> win = aDocument->GetWindow();
if (win) {
nsIDocShell* docShell = win->GetDocShell();
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")) {
if (!IsTabOrExtensionBrowser(win->GetBrowsingContext())) {
return;
}
@ -627,7 +601,6 @@ NS_IMPL_CYCLE_COLLECTION(ExtensionPolicyService, mExtensions, mExtensionHosts,
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

View File

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

View File

@ -11,7 +11,7 @@
add_task(async function test_content_script_cross_origin_frame() {
const extension = ExtensionTestUtils.loadExtension({
const extensionData = {
manifest: {
content_scripts: [{
matches: ["http://example.net/*/file_sample.html"],
@ -84,17 +84,26 @@ add_task(async function test_content_script_cross_origin_frame() {
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 win = window.open(`${base}/mochitest/file_with_xorigin_frame.html`);
await extension.awaitFinish();
win.close();
await ext1.awaitFinish();
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>

View File

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