Bug 1523982 - Part 1: Add filtering to control which globals are permitted to create actors, r=nika
authorJohn Dai <jdai@mozilla.com>
Tue, 26 Mar 2019 15:19:47 +0000
changeset 466113 3a212aece7e4ae048615644fa52699ceeb1d9b88
parent 466112 6070d963e387db57215201580c75b78de9766c75
child 466114 1d112c578b34f040b662e5d602bb2663f6191259
push id81453
push userjdai@mozilla.com
push dateTue, 26 Mar 2019 16:52:49 +0000
treeherderautoland@54f0ce4913e0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1523982
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1523982 - Part 1: Add filtering to control which globals are permitted to create actors, r=nika Differential Revision: https://phabricator.services.mozilla.com/D24203
dom/chrome-webidl/ChromeUtils.webidl
dom/ipc/JSWindowActorService.cpp
dom/ipc/JSWindowActorService.h
dom/ipc/PContent.ipdl
dom/ipc/WindowGlobalChild.cpp
dom/ipc/WindowGlobalParent.cpp
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -601,16 +601,25 @@ dictionary WindowActorOptions {
 
   /**
    * If this is set to `true`, allow this actor to be created for window
    * globals loaded in chrome browsing contexts, such as those used to load the
    * tabbrowser.
    */
   boolean includeChrome = false;
 
+  /**
+   * An array of URL match patterns (as accepted by the MatchPattern
+   * class in MatchPattern.webidl) which restrict which pages the actor
+   * may be instantiated for. If this is defined, only documents URL which match
+   * are allowed to have the given actor created for them. Other
+   * documents will fail to have their actor constructed, returning nullptr.
+   **/
+  sequence<DOMString> matches;
+
   /** This fields are used for configuring individual sides of the actor. */
   required WindowActorSidedOptions parent;
   required WindowActorChildOptions child;
 };
 
 dictionary WindowActorSidedOptions {
   /** The module path which should be loaded for the actor on this side. */
   required ByteString moduleURI;
--- a/dom/ipc/JSWindowActorService.cpp
+++ b/dom/ipc/JSWindowActorService.cpp
@@ -7,16 +7,18 @@
 
 #include "mozilla/dom/JSWindowActorService.h"
 #include "mozilla/dom/ChromeUtilsBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/StaticPtr.h"
 #include "mozJSComponentLoader.h"
+#include "mozilla/extensions/WebExtensionContentScript.h"
+#include "mozilla/Logging.h"
 
 namespace mozilla {
 namespace dom {
 namespace {
 StaticRefPtr<JSWindowActorService> gJSWindowActorService;
 }
 
 /**
@@ -114,50 +116,53 @@ class JSWindowActorProtocol final : publ
     Optional<bool> mPassive;
   };
 
   struct ChildSide : public Sided {
     nsTArray<EventDecl> mEvents;
     nsTArray<nsCString> mObservers;
   };
 
-  const nsAString& Name() const { return mName; }
-  bool AllFrames() const { return mAllFrames; }
-  bool IncludeChrome() const { return mIncludeChrome; }
   const ParentSide& Parent() const { return mParent; }
   const ChildSide& Child() const { return mChild; }
 
   void RegisterListenersFor(EventTarget* aRoot);
   void UnregisterListenersFor(EventTarget* aRoot);
   void AddObservers();
   void RemoveObservers();
+  bool Matches(BrowsingContext* aBrowsingContext, nsIURI* aURI);
 
  private:
   explicit JSWindowActorProtocol(const nsAString& aName) : mName(aName) {}
-
+  extensions::MatchPatternSet* GetURIMatcher();
   ~JSWindowActorProtocol() = default;
 
   nsString mName;
   bool mAllFrames = false;
   bool mIncludeChrome = false;
+  nsTArray<nsString> mMatches;
+
   ParentSide mParent;
   ChildSide mChild;
+
+  RefPtr<extensions::MatchPatternSet> mURIMatcher;
 };
 
 NS_IMPL_ISUPPORTS(JSWindowActorProtocol, nsIObserver, nsIDOMEventListener);
 
 /* static */ already_AddRefed<JSWindowActorProtocol>
 JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
   MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
 
   RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aInfo.name());
   // Content processes cannot load chrome browsing contexts, so this flag is
   // irrelevant and not propagated.
   proto->mIncludeChrome = false;
   proto->mAllFrames = aInfo.allFrames();
+  proto->mMatches = aInfo.matches();
   proto->mChild.mModuleURI.Assign(aInfo.url());
 
   proto->mChild.mEvents.SetCapacity(aInfo.events().Length());
   for (auto& ipc : aInfo.events()) {
     auto* event = proto->mChild.mEvents.AppendElement();
     event->mName.Assign(ipc.name());
     event->mFlags.mCapture = ipc.capture();
     event->mFlags.mInSystemGroup = ipc.systemGroup();
@@ -172,16 +177,17 @@ JSWindowActorProtocol::FromIPC(const JSW
 }
 
 JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
   MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
 
   JSWindowActorInfo info;
   info.name() = mName;
   info.allFrames() = mAllFrames;
+  info.matches() = mMatches;
   info.url() = mChild.mModuleURI;
 
   info.events().SetCapacity(mChild.mEvents.Length());
   for (auto& event : mChild.mEvents) {
     auto* ipc = info.events().AppendElement();
     ipc->name().Assign(event.mName);
     ipc->capture() = event.mFlags.mCapture;
     ipc->systemGroup() = event.mFlags.mInSystemGroup;
@@ -200,16 +206,21 @@ JSWindowActorProtocol::FromWebIDLOptions
                                          const WindowActorOptions& aOptions,
                                          ErrorResult& aRv) {
   MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
 
   RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aName);
   proto->mAllFrames = aOptions.mAllFrames;
   proto->mIncludeChrome = aOptions.mIncludeChrome;
 
+  if (aOptions.mMatches.WasPassed()) {
+    MOZ_ASSERT(aOptions.mMatches.Value().Length());
+    proto->mMatches = aOptions.mMatches.Value();
+  }
+
   proto->mParent.mModuleURI = aOptions.mParent.mModuleURI;
   proto->mChild.mModuleURI = aOptions.mChild.mModuleURI;
 
   // For each event declared in the source dictionary, initialize the
   // corresponding envent declaration entry in the JSWindowActorProtocol.
   if (aOptions.mChild.mEvents.WasPassed()) {
     auto& entries = aOptions.mChild.mEvents.Value().Entries();
     proto->mChild.mEvents.SetCapacity(entries.Length());
@@ -377,16 +388,62 @@ void JSWindowActorProtocol::AddObservers
 
 void JSWindowActorProtocol::RemoveObservers() {
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   for (auto& topic : mChild.mObservers) {
     os->RemoveObserver(this, topic.get());
   }
 }
 
+extensions::MatchPatternSet* JSWindowActorProtocol::GetURIMatcher() {
+  // If we've already created the pattern set, return it.
+  if (mURIMatcher || mMatches.IsEmpty()) {
+    return mURIMatcher;
+  }
+
+  // Constructing the MatchPatternSet requires a JS environment to be run in.
+  // We can construct it here in the JSM scope, as we will be keeping it around.
+  AutoJSAPI jsapi;
+  MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
+  GlobalObject global(jsapi.cx(), xpc::PrivilegedJunkScope());
+
+  nsTArray<OwningStringOrMatchPattern> patterns;
+  patterns.SetCapacity(mMatches.Length());
+  for (nsString& s : mMatches) {
+    auto* entry = patterns.AppendElement();
+    entry->SetAsString() = s;
+  }
+
+  MatchPatternOptions matchPatternOptions;
+  // Make MatchPattern's mSchemes create properly.
+  matchPatternOptions.mRestrictSchemes = false;
+  mURIMatcher = extensions::MatchPatternSet::Constructor(
+      global, patterns, matchPatternOptions, IgnoreErrors());
+  return mURIMatcher;
+}
+
+bool JSWindowActorProtocol::Matches(BrowsingContext* aBrowsingContext,
+                                    nsIURI* aURI) {
+  if (!mAllFrames && aBrowsingContext->GetParent()) {
+    return false;
+  }
+
+  if (!mIncludeChrome && !aBrowsingContext->IsContent()) {
+    return false;
+  }
+
+  if (extensions::MatchPatternSet* uriMatcher = GetURIMatcher()) {
+    if (!uriMatcher->Matches(aURI)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 JSWindowActorService::JSWindowActorService() { MOZ_ASSERT(NS_IsMainThread()); }
 
 JSWindowActorService::~JSWindowActorService() { MOZ_ASSERT(NS_IsMainThread()); }
 
 /* static */
 already_AddRefed<JSWindowActorService> JSWindowActorService::GetSingleton() {
   MOZ_ASSERT(NS_IsMainThread());
   if (!gJSWindowActorService) {
@@ -484,23 +541,22 @@ void JSWindowActorService::GetJSWindowAc
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(XRE_IsParentProcess());
 
   for (auto iter = mDescriptors.ConstIter(); !iter.Done(); iter.Next()) {
     aInfos.AppendElement(iter.Data()->ToIPC());
   }
 }
 
-void JSWindowActorService::ConstructActor(const nsAString& aName,
-                                          bool aParentSide,
-                                          BrowsingContext* aBrowsingContext,
-                                          JS::MutableHandleObject aActor,
-                                          ErrorResult& aRv) {
+void JSWindowActorService::ConstructActor(
+    const nsAString& aName, bool aParentSide, BrowsingContext* aBrowsingContext,
+    nsIURI* aURI, JS::MutableHandleObject aActor, ErrorResult& aRv) {
   MOZ_ASSERT_IF(aParentSide, XRE_IsParentProcess());
-
+  MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
+  MOZ_ASSERT(aURI, "Must have URI!");
   // Constructing an actor requires a running script, so push an AutoEntryScript
   // onto the stack.
   AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSWindowActor construction");
   JSContext* cx = aes.cx();
 
   // Load our descriptor
   RefPtr<JSWindowActorProtocol> proto = mDescriptors.Get(aName);
   if (!proto) {
@@ -510,24 +566,19 @@ void JSWindowActorService::ConstructActo
 
   const JSWindowActorProtocol::Sided* side;
   if (aParentSide) {
     side = &proto->Parent();
   } else {
     side = &proto->Child();
   }
 
-  // Check if our current BrowsingContext matches the requirements for this
-  // actor to load.
-  if (!proto->AllFrames() && aBrowsingContext->GetParent()) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return;
-  }
-
-  if (!proto->IncludeChrome() && !aBrowsingContext->IsContent()) {
+  // Check if our current BrowsingContext and URI matches the requirements for
+  // this actor to load.
+  if (!proto->Matches(aBrowsingContext, aURI)) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
 
   // Load the module using mozJSComponentLoader.
   RefPtr<mozJSComponentLoader> loader = mozJSComponentLoader::Get();
   MOZ_ASSERT(loader);
 
--- a/dom/ipc/JSWindowActorService.h
+++ b/dom/ipc/JSWindowActorService.h
@@ -35,17 +35,17 @@ class JSWindowActorService final {
   // Get the named of Window Actor and the child's WindowActorOptions
   // from mDescriptors to JSWindowActorInfos.
   void GetJSWindowActorInfos(nsTArray<JSWindowActorInfo>& aInfos);
 
   // Load the module for the named Window Actor and contruct it.
   // This method will not initialize the actor or set its manager,
   // which is handled by callers.
   void ConstructActor(const nsAString& aName, bool aParentSide,
-                      BrowsingContext* aBrowsingContext,
+                      BrowsingContext* aBrowsingContext, nsIURI* aURI,
                       JS::MutableHandleObject aActor, ErrorResult& aRv);
 
   void ReceiveMessage(nsISupports* aActor, JS::RootedObject& aObj,
                       const nsString& aMessageName,
                       ipc::StructuredCloneData& aData);
 
   // Register or unregister a WindowRoot object from this JSWindowActorService.
   void RegisterWindowRoot(EventTarget* aRoot);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -237,16 +237,17 @@ struct JSWindowActorEventDecl
 struct JSWindowActorInfo
 {
   nsString name;
   bool allFrames;
   nsCString url;
 
   JSWindowActorEventDecl[] events;
   nsCString[] observers;
+  nsString[] matches;
 };
 
 struct GMPAPITags
 {
     nsCString api;
     nsCString[] tags;
 };
 
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -162,17 +162,17 @@ already_AddRefed<JSWindowActorChild> Win
   // JSWindowActorService to trigger construction.
   RefPtr<JSWindowActorService> actorSvc = JSWindowActorService::GetSingleton();
   if (!actorSvc) {
     return nullptr;
   }
 
   JS::RootedObject obj(RootingCx());
   actorSvc->ConstructActor(aName, /* aChildSide */ false, mBrowsingContext,
-                           &obj, aRv);
+                           mWindowGlobal->GetDocumentURI(), &obj, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   // Unwrap our actor to a JSWindowActorChild object.
   RefPtr<JSWindowActorChild> actor;
   if (NS_FAILED(UNWRAP_OBJECT(JSWindowActorChild, &obj, actor))) {
     return nullptr;
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -205,17 +205,17 @@ already_AddRefed<JSWindowActorParent> Wi
   // JSWindowActorService to trigger construction
   RefPtr<JSWindowActorService> actorSvc = JSWindowActorService::GetSingleton();
   if (!actorSvc) {
     return nullptr;
   }
 
   JS::RootedObject obj(RootingCx());
   actorSvc->ConstructActor(aName, /* aParentSide */ true, mBrowsingContext,
-                           &obj, aRv);
+                           mDocumentURI, &obj, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   // Unwrap our actor to a JSWindowActorParent object.
   RefPtr<JSWindowActorParent> actor;
   if (NS_FAILED(UNWRAP_OBJECT(JSWindowActorParent, &obj, actor))) {
     return nullptr;