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 466117 3a212aece7e4ae048615644fa52699ceeb1d9b88
parent 466116 6070d963e387db57215201580c75b78de9766c75
child 466118 1d112c578b34f040b662e5d602bb2663f6191259
push id1
push userpvanderbeken@mozilla.com
push dateThu, 28 Mar 2019 13:34:35 +0000
reviewersnika
bugs1523982
milestone68.0a1
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;