From 63fd1048130db63608a0ad8a396a4dde5dfcc7ee Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Tue, 24 Mar 2015 15:29:16 +0100 Subject: [PATCH] Bug 1126014 - DomainPolicy support for e10s. r=mrbkap --- caps/DomainPolicy.cpp | 118 ++++++++++- caps/DomainPolicy.h | 31 ++- caps/nsIDomainPolicy.idl | 22 +- caps/nsIScriptSecurityManager.idl | 25 ++- caps/nsScriptSecurityManager.cpp | 30 ++- dom/ipc/ContentChild.cpp | 93 ++++++++- dom/ipc/ContentChild.h | 5 + dom/ipc/ContentParent.cpp | 11 +- dom/ipc/ContentParent.h | 3 +- dom/ipc/PContent.ipdl | 14 +- dom/ipc/moz.build | 1 + dom/ipc/tests/browser.ini | 6 + dom/ipc/tests/browser_domainPolicy.js | 240 ++++++++++++++++++++++ dom/ipc/tests/file_disableScript.html | 11 + dom/ipc/tests/file_domainPolicy_base.html | 8 + 15 files changed, 600 insertions(+), 18 deletions(-) create mode 100644 dom/ipc/tests/browser.ini create mode 100644 dom/ipc/tests/browser_domainPolicy.js create mode 100644 dom/ipc/tests/file_disableScript.html create mode 100644 dom/ipc/tests/file_domainPolicy_base.html diff --git a/caps/DomainPolicy.cpp b/caps/DomainPolicy.cpp index ec0230cbe3b..a2ec3e34f68 100644 --- a/caps/DomainPolicy.cpp +++ b/caps/DomainPolicy.cpp @@ -5,17 +5,50 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DomainPolicy.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/unused.h" +#include "nsIMessageManager.h" #include "nsScriptSecurityManager.h" namespace mozilla { +using namespace ipc; +using namespace dom; + NS_IMPL_ISUPPORTS(DomainPolicy, nsIDomainPolicy) -DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet()) - , mSuperBlacklist(new DomainSet()) - , mWhitelist(new DomainSet()) - , mSuperWhitelist(new DomainSet()) -{} +static nsresult +BroadcastDomainSetChange(DomainSetType aSetType, DomainSetChangeType aChangeType, + nsIURI* aDomain = nullptr) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default, + "DomainPolicy should only be exposed to the chrome process."); + + nsTArray parents; + ContentParent::GetAll(parents); + if (!parents.Length()) { + return NS_OK; + } + + OptionalURIParams uri; + SerializeURI(aDomain, uri); + + for (uint32_t i = 0; i < parents.Length(); i++) { + unused << parents[i]->SendDomainSetChanged(aSetType, aChangeType, uri); + } + return NS_OK; +} + +DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet(BLACKLIST)) + , mSuperBlacklist(new DomainSet(SUPER_BLACKLIST)) + , mWhitelist(new DomainSet(WHITELIST)) + , mSuperWhitelist(new DomainSet(SUPER_WHITELIST)) +{ + if (XRE_GetProcessType() == GeckoProcessType_Default) { + BroadcastDomainSetChange(NO_TYPE, ACTIVATE_POLICY); + } +} DomainPolicy::~DomainPolicy() { @@ -75,10 +108,47 @@ DomainPolicy::Deactivate() mSuperWhitelist = nullptr; // Inform the SSM. - nsScriptSecurityManager::GetScriptSecurityManager()->DeactivateDomainPolicy(); + nsScriptSecurityManager* ssm = nsScriptSecurityManager::GetScriptSecurityManager(); + if (ssm) { + ssm->DeactivateDomainPolicy(); + } + if (XRE_GetProcessType() == GeckoProcessType_Default) { + BroadcastDomainSetChange(NO_TYPE, DEACTIVATE_POLICY); + } return NS_OK; } +void +DomainPolicy::CloneDomainPolicy(DomainPolicyClone* aClone) +{ + aClone->active() = true; + static_cast(mBlacklist.get())->CloneSet(&aClone->blacklist()); + static_cast(mSuperBlacklist.get())->CloneSet(&aClone->superBlacklist()); + static_cast(mWhitelist.get())->CloneSet(&aClone->whitelist()); + static_cast(mSuperWhitelist.get())->CloneSet(&aClone->superWhitelist()); +} + +static +void +CopyURIs(const InfallibleTArray& aDomains, nsIDomainSet* aSet) +{ + for (uint32_t i = 0; i < aDomains.Length(); i++) { + nsCOMPtr uri = DeserializeURI(aDomains[i]); + aSet->Add(uri); + } +} + +void +DomainPolicy::ApplyClone(DomainPolicyClone* aClone) +{ + nsCOMPtr list; + + CopyURIs(aClone->blacklist(), mBlacklist); + CopyURIs(aClone->whitelist(), mWhitelist); + CopyURIs(aClone->superBlacklist(), mSuperBlacklist); + CopyURIs(aClone->superWhitelist(), mSuperWhitelist); +} + static already_AddRefed GetCanonicalClone(nsIURI* aURI) { @@ -100,6 +170,9 @@ DomainSet::Add(nsIURI* aDomain) nsCOMPtr clone = GetCanonicalClone(aDomain); NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE); mHashTable.PutEntry(clone); + if (XRE_GetProcessType() == GeckoProcessType_Default) + return BroadcastDomainSetChange(mType, ADD_DOMAIN, aDomain); + return NS_OK; } @@ -109,6 +182,9 @@ DomainSet::Remove(nsIURI* aDomain) nsCOMPtr clone = GetCanonicalClone(aDomain); NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE); mHashTable.RemoveEntry(clone); + if (XRE_GetProcessType() == GeckoProcessType_Default) + return BroadcastDomainSetChange(mType, REMOVE_DOMAIN, aDomain); + return NS_OK; } @@ -116,6 +192,9 @@ NS_IMETHODIMP DomainSet::Clear() { mHashTable.Clear(); + if (XRE_GetProcessType() == GeckoProcessType_Default) + return BroadcastDomainSetChange(mType, CLEAR_DOMAINS); + return NS_OK; } @@ -160,4 +239,31 @@ DomainSet::ContainsSuperDomain(nsIURI* aDomain, bool* aContains) } +NS_IMETHODIMP +DomainSet::GetType(uint32_t* aType) +{ + *aType = mType; + return NS_OK; +} + +static +PLDHashOperator +DomainEnumerator(nsURIHashKey* aEntry, void* aUserArg) +{ + InfallibleTArray* uris = static_cast*>(aUserArg); + nsIURI* key = aEntry->GetKey(); + + URIParams uri; + SerializeURI(key, uri); + + uris->AppendElement(uri); + return PL_DHASH_NEXT; +} + +void +DomainSet::CloneSet(InfallibleTArray* aDomains) +{ + mHashTable.EnumerateEntries(DomainEnumerator, aDomains); +} + } /* namespace mozilla */ diff --git a/caps/DomainPolicy.h b/caps/DomainPolicy.h index 43c1726d009..eea89d476fb 100644 --- a/caps/DomainPolicy.h +++ b/caps/DomainPolicy.h @@ -13,6 +13,30 @@ namespace mozilla { +namespace dom { +class nsIContentParent; +}; + +namespace ipc { +class URIParams; +}; + +enum DomainSetChangeType{ + ACTIVATE_POLICY, + DEACTIVATE_POLICY, + ADD_DOMAIN, + REMOVE_DOMAIN, + CLEAR_DOMAINS +}; + +enum DomainSetType{ + NO_TYPE, + BLACKLIST, + SUPER_BLACKLIST, + WHITELIST, + SUPER_WHITELIST +}; + class DomainPolicy : public nsIDomainPolicy { public: @@ -35,11 +59,16 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIDOMAINSET - DomainSet() {} + explicit DomainSet(DomainSetType aType) + : mType(aType) + {} + + void CloneSet(InfallibleTArray* aDomains); protected: virtual ~DomainSet() {} nsTHashtable mHashTable; + DomainSetType mType; }; } /* namespace mozilla */ diff --git a/caps/nsIDomainPolicy.idl b/caps/nsIDomainPolicy.idl index e77c45681ca..54b0de4dbf9 100644 --- a/caps/nsIDomainPolicy.idl +++ b/caps/nsIDomainPolicy.idl @@ -8,6 +8,16 @@ interface nsIURI; interface nsIDomainSet; +%{ C++ +namespace mozilla { +namespace dom { +class DomainPolicyClone; +} +} +%} + +[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone); + /* * When a domain policy is instantiated by invoking activateDomainPolicy() on * nsIScriptSecurityManager, these domain sets are consulted when each new @@ -20,7 +30,7 @@ interface nsIDomainSet; * When deactivate() is invoked, the domain sets are emptied, and the * nsIDomainPolicy ceases to have any effect on the system. */ -[scriptable, builtinclass, uuid(27b10f54-f34b-42b7-8594-4348d3ad7953)] +[scriptable, builtinclass, uuid(82b24a20-6701-4d40-a0f9-f5dc7321b555)] interface nsIDomainPolicy : nsISupports { readonly attribute nsIDomainSet blacklist; @@ -29,11 +39,19 @@ interface nsIDomainPolicy : nsISupports readonly attribute nsIDomainSet superWhitelist; void deactivate(); + + [noscript, notxpcom] void cloneDomainPolicy(in DomainPolicyClonePtr aClone); + [noscript, notxpcom] void applyClone(in DomainPolicyClonePtr aClone); }; -[scriptable, builtinclass, uuid(946a01ff-6525-4007-a2c2-447ebe1875d3)] +[scriptable, builtinclass, uuid(665c981b-0a0f-4229-ac06-a826e02d4f69)] interface nsIDomainSet : nsISupports { + /* + * The type of the set. See: DomainSetType + */ + [noscript] readonly attribute uint32_t type; + /* * Add a domain to the set. No-op if it already exists. */ diff --git a/caps/nsIScriptSecurityManager.idl b/caps/nsIScriptSecurityManager.idl index 33503a33ef0..76465abc70e 100644 --- a/caps/nsIScriptSecurityManager.idl +++ b/caps/nsIScriptSecurityManager.idl @@ -14,12 +14,19 @@ interface nsILoadContext; %{ C++ #include "jspubtd.h" + +namespace mozilla { +namespace dom { +class DomainPolicyClone; +} +} %} [ptr] native JSContextPtr(JSContext); [ptr] native JSObjectPtr(JSObject); +[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone); -[scriptable, uuid(f649959d-dae3-4027-83fd-5b7f8c8a8815)] +[scriptable, uuid(ba602ca6-dc7a-457e-a57a-ee5b343fd863)] interface nsIScriptSecurityManager : nsISupports { /** @@ -240,6 +247,22 @@ interface nsIScriptSecurityManager : nsISupports nsIDomainPolicy activateDomainPolicy(); readonly attribute boolean domainPolicyActive; + /** + * Only the parent process can directly access domain policies, child + * processes only have a read-only mirror to the one in the parent. + * For child processes the mirror is updated via messages + * and ContentChild will hold the DomainPolicy by calling + * ActivateDomainPolicyInternal directly. New consumer to this + * function should not be addded. + */ + [noscript] nsIDomainPolicy activateDomainPolicyInternal(); + + /** + * This function is for internal use only. Every time a child process is spawned, we + * must clone any active domain policies in the parent to the new child. + */ + [noscript, notxpcom] void cloneDomainPolicy(in DomainPolicyClonePtr aClone); + /** * Query mechanism for the above policy. * diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 5cef6cb6ad8..1ec72d7eca9 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -1310,9 +1310,14 @@ static StaticRefPtr gScriptSecMan; nsScriptSecurityManager::~nsScriptSecurityManager(void) { Preferences::RemoveObservers(this, kObservedPrefs); - if (mDomainPolicy) + if (mDomainPolicy) { mDomainPolicy->Deactivate(); - MOZ_ASSERT(!mDomainPolicy); + } + // ContentChild might hold a reference to the domain policy, + // and it might release it only after the security manager is + // gone. But we can still assert this for the main process. + MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_Default, + !mDomainPolicy); } void @@ -1527,6 +1532,16 @@ nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv) NS_IMETHODIMP nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv) +{ + if (XRE_GetProcessType() != GeckoProcessType_Default) { + return NS_ERROR_SERVICE_NOT_AVAILABLE; + } + + return ActivateDomainPolicyInternal(aRv); +} + +NS_IMETHODIMP +nsScriptSecurityManager::ActivateDomainPolicyInternal(nsIDomainPolicy** aRv) { // We only allow one domain policy at a time. The holder of the previous // policy must explicitly deactivate it first. @@ -1548,6 +1563,17 @@ nsScriptSecurityManager::DeactivateDomainPolicy() mDomainPolicy = nullptr; } +void +nsScriptSecurityManager::CloneDomainPolicy(DomainPolicyClone* aClone) +{ + MOZ_ASSERT(aClone); + if (mDomainPolicy) { + mDomainPolicy->CloneDomainPolicy(aClone); + } else { + aClone->active() = false; + } +} + NS_IMETHODIMP nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv) { diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 63161f033b1..0fbd709416c 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -28,6 +28,7 @@ #include "mozilla/docshell/OfflineCacheUpdateChild.h" #include "mozilla/dom/ContentBridgeChild.h" #include "mozilla/dom/ContentBridgeParent.h" +#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/DOMStorageIPC.h" #include "mozilla/dom/ExternalHelperAppChild.h" #include "mozilla/dom/PCrashReporterChild.h" @@ -110,8 +111,9 @@ #include "mozilla/dom/PMemoryReportRequestChild.h" #include "mozilla/dom/PCycleCollectWithLogsChild.h" -#ifdef MOZ_PERMISSIONS #include "nsIScriptSecurityManager.h" + +#ifdef MOZ_PERMISSIONS #include "nsPermission.h" #include "nsPermissionManager.h" #endif @@ -168,6 +170,7 @@ #include "nsIPrincipal.h" #include "nsDeviceStorage.h" #include "AudioChannelService.h" +#include "DomainPolicy.h" #include "mozilla/dom/DataStoreService.h" #include "mozilla/dom/telephony/PTelephonyChild.h" #include "mozilla/dom/time/DateCacheCleaner.h" @@ -786,9 +789,21 @@ ContentChild::InitXPCOM() bool isOffline; ClipboardCapabilities clipboardCaps; - SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries, &clipboardCaps); + DomainPolicyClone domainPolicy; + + SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries, &clipboardCaps, &domainPolicy); RecvSetOffline(isOffline); + if (domainPolicy.active()) { + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + MOZ_ASSERT(ssm); + ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy)); + if (!mPolicy) { + MOZ_CRASH("Failed to activate domain policy."); + } + mPolicy->ApplyClone(&domainPolicy); + } + nsCOMPtr clipboard(do_GetService("@mozilla.org/widget/clipboard;1")); if (nsCOMPtr clipboardProxy = do_QueryInterface(clipboard)) { clipboardProxy->SetCapabilities(clipboardCaps); @@ -2595,9 +2610,83 @@ ContentChild::RecvAssociatePluginId(const uint32_t& aPluginId, return true; } +bool +ContentChild::RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType, + const OptionalURIParams& aDomain) +{ + if (aChangeType == ACTIVATE_POLICY) { + if (mPolicy) { + return true; + } + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + MOZ_ASSERT(ssm); + ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy)); + return !!mPolicy; + } else if (!mPolicy) { + MOZ_ASSERT_UNREACHABLE("If the domain policy is not active yet," + " the first message should be ACTIVATE_POLICY"); + return false; + } + + NS_ENSURE_TRUE(mPolicy, false); + + if (aChangeType == DEACTIVATE_POLICY) { + mPolicy->Deactivate(); + mPolicy = nullptr; + return true; + } + + nsCOMPtr set; + switch(aSetType) { + case BLACKLIST: + mPolicy->GetBlacklist(getter_AddRefs(set)); + break; + case SUPER_BLACKLIST: + mPolicy->GetSuperBlacklist(getter_AddRefs(set)); + break; + case WHITELIST: + mPolicy->GetWhitelist(getter_AddRefs(set)); + break; + case SUPER_WHITELIST: + mPolicy->GetSuperWhitelist(getter_AddRefs(set)); + break; + default: + NS_NOTREACHED("Unexpected setType"); + return false; + } + + MOZ_ASSERT(set); + + nsCOMPtr uri = DeserializeURI(aDomain); + + switch(aChangeType) { + case ADD_DOMAIN: + NS_ENSURE_TRUE(uri, false); + set->Add(uri); + break; + case REMOVE_DOMAIN: + NS_ENSURE_TRUE(uri, false); + set->Remove(uri); + break; + case CLEAR_DOMAINS: + set->Clear(); + break; + default: + NS_NOTREACHED("Unexpected changeType"); + return false; + } + + return true; +} + bool ContentChild::RecvShutdown() { + if (mPolicy) { + mPolicy->Deactivate(); + mPolicy = nullptr; + } + nsCOMPtr os = services::GetObserverService(); if (os) { os->NotifyObservers(this, "content-child-shutdown", nullptr); diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 501ae51ffab..1947cfab5c5 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -24,6 +24,7 @@ class nsIDOMBlob; class nsIObserver; struct ResourceMapping; struct OverrideMapping; +class nsIDomainPolicy; namespace mozilla { class RemoteSpellcheckEngineChild; @@ -384,6 +385,8 @@ public: nsTArray&& aThreadNameFilters) override; virtual bool RecvStopProfiler() override; virtual bool RecvGetProfile(nsCString* aProfile) override; + virtual bool RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType, + const OptionalURIParams& aDomain) override; virtual bool RecvShutdown() override; #ifdef ANDROID @@ -481,6 +484,8 @@ private: static ContentChild* sSingleton; + nsCOMPtr mPolicy; + DISALLOW_EVIL_CONSTRUCTORS(ContentChild); }; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index be28a00a720..a71cbd58117 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2782,8 +2782,9 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid, bool isOffline; InfallibleTArray unusedDictionaries; ClipboardCapabilities clipboardCaps; + DomainPolicyClone domainPolicy; RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries, - &clipboardCaps); + &clipboardCaps, &domainPolicy); mozilla::unused << content->SendSetOffline(isOffline); MOZ_ASSERT(!clipboardCaps.supportsSelectionClipboard() && !clipboardCaps.supportsFindClipboard(), @@ -3119,7 +3120,8 @@ ContentParent::RecvGetProcessAttributes(ContentParentId* aCpId, bool ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline, InfallibleTArray* dictionaries, - ClipboardCapabilities* clipboardCaps) + ClipboardCapabilities* clipboardCaps, + DomainPolicyClone* domainPolicy) { nsCOMPtr io(do_GetIOService()); MOZ_ASSERT(io, "No IO service?"); @@ -3140,6 +3142,11 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline, rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard()); MOZ_ASSERT(NS_SUCCEEDED(rv)); + // Let's copy the domain policy from the parent to the child (if it's active). + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + NS_ENSURE_TRUE(ssm, false); + ssm->CloneDomainPolicy(domainPolicy); + return true; } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 67c0f5547eb..95756dfb917 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -511,7 +511,8 @@ private: bool* aIsForBrowser) override; virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline, InfallibleTArray* dictionaries, - ClipboardCapabilities* clipboardCaps) + ClipboardCapabilities* clipboardCaps, + DomainPolicyClone* domainPolicy) override; virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 8a290781917..26354d71fae 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -346,6 +346,15 @@ union OptionalContentId void_t; }; +struct DomainPolicyClone +{ + bool active; + URIParams[] blacklist; + URIParams[] whitelist; + URIParams[] superBlacklist; + URIParams[] superWhitelist; +}; + prio(normal upto urgent) sync protocol PContent { parent spawns PPluginModule; @@ -552,6 +561,8 @@ child: NuwaFreeze(); + async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams aDomain); + /** * Notify the child to shutdown. The child will in turn call FinishShutdown * and let the parent close the channel. @@ -586,7 +597,8 @@ parent: returns (ContentParentId cpId, bool isForApp, bool isForBrowser); sync GetXPCOMProcessAttributes() returns (bool isOffline, nsString[] dictionaries, - ClipboardCapabilities clipboardCaps); + ClipboardCapabilities clipboardCaps, + DomainPolicyClone domainPolicy); sync CreateChildProcess(IPCTabContext context, ProcessPriority priority, diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build index 473fb9f4910..29def19955c 100644 --- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -154,6 +154,7 @@ for var in ('MOZ_PERMISSIONS', 'MOZ_CHILD_PERMISSIONS'): JAR_MANIFESTS += ['jar.mn'] +BROWSER_CHROME_MANIFESTS += ['tests/browser.ini'] MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini'] MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] diff --git a/dom/ipc/tests/browser.ini b/dom/ipc/tests/browser.ini new file mode 100644 index 00000000000..f3d8ce14013 --- /dev/null +++ b/dom/ipc/tests/browser.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + file_disableScript.html + file_domainPolicy_base.html + +[browser_domainPolicy.js] diff --git a/dom/ipc/tests/browser_domainPolicy.js b/dom/ipc/tests/browser_domainPolicy.js new file mode 100644 index 00000000000..df06b8bc0eb --- /dev/null +++ b/dom/ipc/tests/browser_domainPolicy.js @@ -0,0 +1,240 @@ +var policy; // To make sure we never leave up an activated domain policy after a failed test, let's make this global. +function activateDomainPolicy() { + const ssm = Services.scriptSecurityManager; + policy = ssm.activateDomainPolicy(); +} + +function deactivateDomainPolicy() { + if (policy) { + policy.deactivate(); + policy = null; + } +} + +function* test_domainPolicy() { + + XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); + let deferred = Promise.defer(); + let currentTask = deferred.promise; + SpecialPowers.pushPrefEnv( + {set: [["dom.ipc.browser_frames.oop_by_default", false], + ["browser.pagethumbnails.capturing_disabled", false], + ["dom.mozBrowserFramesEnabled", false]]}, + () => { return deferred.resolve()}); + yield currentTask; + + // Create tab + let tab; + + // Init test + function initProcess() { + tab = gBrowser.addTab(); + gBrowser.selectedTab = tab; + + let initPromise = ContentTask.spawn(tab.linkedBrowser, null, function() { + Cu.import("resource://gre/modules/PromiseUtils.jsm"); + function loadBase() { + let deferred = PromiseUtils.defer(); + let listener = (event) => { + removeEventListener("DOMDocElementInserted", listener, true); + let listener2 = (event) => { + content.removeEventListener('load', listener2); + deferred.resolve(); + } + content.addEventListener('load', listener2); + }; + addEventListener("DOMDocElementInserted", listener, true); + return deferred.promise; + } + + return loadBase(); + }); + tab.linkedBrowser.loadURI("http://mochi.test:8888/browser/dom/ipc/tests/file_domainPolicy_base.html"); + return initPromise; + } + + // We use ContentTask for the tests, but we also want to pass some data and some helper functions too. + // To do that, we serialize an input object via JSON |ipcArgs| and some shared helper functions |initUtils| + // and eval them in the content process. + var ipcArgs = {}; + function initUtils(obj) { + obj.checkScriptEnabled = function(win, expectEnabled) { + win.wrappedJSObject.gFiredOnclick = false; + win.document.body.dispatchEvent(new win.Event('click')); + return { passed: win.wrappedJSObject.gFiredOnclick == expectEnabled, + msg: `Checking script-enabled for ${win.name} (${win.location})`}; + } + + obj.navigateFrame = function(ifr, src) { + let deferred = PromiseUtils.defer(); + function onload() { + ifr.removeEventListener('load', onload); + deferred.resolve(); + } + ifr.addEventListener('load', onload, false); + ifr.setAttribute('src', src); + return deferred.promise; + } + }; + + function runTest(test) { + return ContentTask.spawn(tab.linkedBrowser, + 'ipcArgs = ' + JSON.stringify(ipcArgs) + '; (' + initUtils.toSource() + ')(utils)', test); + } + + function checkAndCleanup(result) { + result = [].concat(result); + for (var i in result) + ok(result[i].passed, result[i].msg); + gBrowser.removeTab(tab); + deactivateDomainPolicy(); + ipcArgs = {}; + } + + function testDomain(domain) { + ipcArgs.domain = domain; + return (aUtils) => { + Cu.import("resource://gre/modules/PromiseUtils.jsm"); + var ipcArgs; + var utils = {}; + eval(aUtils); + + let path = '/browser/dom/ipc/tests/file_disableScript.html'; + let deferred = PromiseUtils.defer(); + var rootFrame = content.document.getElementById('root'); + utils.navigateFrame(rootFrame, ipcArgs.domain + path).then(() => { + deferred.resolve(utils.checkScriptEnabled(rootFrame.contentWindow, false)); + }); + return deferred.promise; + } + } + + info("Testing simple blacklist policy"); + + info("Creating child process first, activating domainPolicy after"); + currentTask = initProcess(); + yield currentTask; + activateDomainPolicy(); + var bl = policy.blacklist; + bl.add(Services.io.newURI('http://example.com', null, null)); + currentTask = runTest(testDomain("http://example.com")); + checkAndCleanup(yield currentTask); + + info("Activating domainPolicy first, creating child process after"); + activateDomainPolicy(); + var bl = policy.blacklist; + bl.add(Services.io.newURI('http://example.com', null, null)); + currentTask = initProcess(); + yield currentTask; + currentTask = runTest(testDomain("http://example.com")); + checkAndCleanup(yield currentTask); + + function testList(expectEnabled, list) { + ipcArgs.expectEnabled = expectEnabled; + ipcArgs.list = list; + return (aUtils) => { + Cu.import("resource://gre/modules/PromiseUtils.jsm"); + var ipcArgs; + var utils = {}; + eval(aUtils); + + var results = []; + var testListInternal = function(expectEnabled, list, idx) { + idx = idx || 0; + let deferred = PromiseUtils.defer(); + let path = '/browser/dom/ipc/tests/file_disableScript.html'; + let target = list[idx] + path; + var rootFrame = content.document.getElementById('root'); + utils.navigateFrame(rootFrame, target).then(function() { + results.push(utils.checkScriptEnabled(rootFrame.contentWindow, expectEnabled)); + if (idx == list.length - 1) + deferred.resolve(results); + else + testListInternal(expectEnabled, list, idx + 1).then(function(retArg) { deferred.resolve(retArg); }); + }); + return deferred.promise; + } + return testListInternal(ipcArgs.expectEnabled, ipcArgs.list); + } + } + + let testPolicy = { + exceptions: ['http://test1.example.com', 'http://example.com'], + superExceptions: ['http://test2.example.org', 'https://test1.example.com'], + exempt: ['http://test1.example.com', 'http://example.com', + 'http://test2.example.org', 'http://sub1.test2.example.org', + 'https://sub1.test1.example.com'], + notExempt: ['http://test2.example.com', 'http://sub1.test1.example.com', + 'http://www.example.com', 'https://test2.example.com', + 'https://example.com', 'http://test1.example.org'], + }; + + function activate(isBlack, exceptions, superExceptions) { + activateDomainPolicy(); + let set = isBlack ? policy.blacklist : policy.whitelist; + let superSet = isBlack ? policy.superBlacklist : policy.superWhitelist; + for (var e of exceptions) + set.add(makeURI(e)); + for (var e of superExceptions) + superSet.add(makeURI(e)); + }; + + info("Testing Blacklist-style Domain Policy"); + info("Activating domainPolicy first, creating child process after"); + activate(true, testPolicy.exceptions, testPolicy.superExceptions); + currentTask = initProcess(); + yield currentTask; + let results = []; + currentTask = runTest(testList(true, testPolicy.notExempt)); + results = results.concat(yield currentTask); + currentTask = runTest(testList(false, testPolicy.exempt)); + results = results.concat(yield currentTask); + checkAndCleanup(results); + + info("Creating child process first, activating domainPolicy after"); + currentTask = initProcess(); + yield currentTask; + activate(true, testPolicy.exceptions, testPolicy.superExceptions); + results = []; + currentTask = runTest(testList(true, testPolicy.notExempt)); + results = results.concat(yield currentTask); + currentTask = runTest(testList(false, testPolicy.exempt)); + results = results.concat(yield currentTask); + checkAndCleanup(results); + + info("Testing Whitelist-style Domain Policy"); + deferred = Promise.defer(); + currentTask = deferred.promise; + SpecialPowers.pushPrefEnv({set:[["javascript.enabled", false]]}, () => { return deferred.resolve()}); + yield currentTask; + + info("Activating domainPolicy first, creating child process after"); + activate(false, testPolicy.exceptions, testPolicy.superExceptions); + currentTask = initProcess(); + yield currentTask; + results = []; + currentTask = runTest(testList(false, testPolicy.notExempt)); + results = results.concat(yield currentTask); + currentTask = runTest(testList(true, testPolicy.exempt)); + results = results.concat(yield currentTask); + checkAndCleanup(results); + + info("Creating child process first, activating domainPolicy after"); + currentTask = initProcess(); + yield currentTask; + activate(false, testPolicy.exceptions, testPolicy.superExceptions); + results = []; + currentTask = runTest(testList(false, testPolicy.notExempt)); + results = results.concat(yield currentTask); + currentTask = runTest(testList(true, testPolicy.exempt)); + results = results.concat(yield currentTask); + checkAndCleanup(results); + finish(); +} + + +add_task(test_domainPolicy); + +registerCleanupFunction(()=>{ + deactivateDomainPolicy(); +}) \ No newline at end of file diff --git a/dom/ipc/tests/file_disableScript.html b/dom/ipc/tests/file_disableScript.html new file mode 100644 index 00000000000..f4888cd586b --- /dev/null +++ b/dom/ipc/tests/file_disableScript.html @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/dom/ipc/tests/file_domainPolicy_base.html b/dom/ipc/tests/file_domainPolicy_base.html new file mode 100644 index 00000000000..6e3ec7aec4f --- /dev/null +++ b/dom/ipc/tests/file_domainPolicy_base.html @@ -0,0 +1,8 @@ + + + + + +