Bug 1472491: Part 2b - Add MozDocumentObserver class to notify on new pattern-matched documents. r=zombie draft
authorKris Maglione <maglione.k@gmail.com>
Tue, 31 Jul 2018 21:50:34 -0700
changeset 828379 5d0ba95f24c7e611b63a70bfce52fe2b6391e1d2
parent 828378 b01d05b015b7921bd980bbe469581c21a7737408
child 828380 b4cc2b8c8396e2bf8dcfbdea8dfc0895db014495
push id118679
push usermaglione.k@gmail.com
push dateFri, 10 Aug 2018 21:19:41 +0000
reviewerszombie
bugs1472491
milestone63.0a1
Bug 1472491: Part 2b - Add MozDocumentObserver class to notify on new pattern-matched documents. r=zombie MozReview-Commit-ID: 29CsJ2mya36
dom/bindings/Bindings.conf
dom/chrome-webidl/MozDocumentObserver.webidl
dom/chrome-webidl/moz.build
toolkit/components/extensions/DocumentObserver.h
toolkit/components/extensions/ExtensionPolicyService.cpp
toolkit/components/extensions/ExtensionPolicyService.h
toolkit/components/extensions/WebExtensionContentScript.h
toolkit/components/extensions/WebExtensionPolicy.cpp
toolkit/components/extensions/moz.build
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -581,16 +581,20 @@ DOMInterfaces = {
     'notflattened': True
 },
 
 'MozDocumentMatcher': {
     'nativeType': 'mozilla::extensions::MozDocumentMatcher',
     'headerFile': 'mozilla/extensions/WebExtensionContentScript.h',
 },
 
+'MozDocumentObserver': {
+    'nativeType': 'mozilla::extensions::DocumentObserver',
+},
+
 'MozSharedMap': {
     'nativeType': 'mozilla::dom::ipc::SharedMap',
 },
 
 'MozWritableSharedMap': {
     'headerFile': 'mozilla/dom/ipc/SharedMap.h',
     'nativeType': 'mozilla::dom::ipc::WritableSharedMap',
 },
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/MozDocumentObserver.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+callback interface MozDocumentCallback {
+  void onNewDocument(MozDocumentMatcher matcher, WindowProxy window);
+  void onPreloadDocument(MozDocumentMatcher matcher, LoadInfo loadInfo);
+};
+
+[ChromeOnly, Constructor(MozDocumentCallback callbacks), Exposed=System]
+interface MozDocumentObserver {
+  [Throws]
+  void observe(sequence<MozDocumentMatcher> matchers);
+  void disconnect();
+};
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -32,16 +32,17 @@ PREPROCESSED_WEBIDL_FILES = [
 WEBIDL_FILES = [
     'ChannelWrapper.webidl',
     'DominatorTree.webidl',
     'HeapSnapshot.webidl',
     'InspectorUtils.webidl',
     'MatchGlob.webidl',
     'MatchPattern.webidl',
     'MessageManager.webidl',
+    'MozDocumentObserver.webidl',
     'MozSharedMap.webidl',
     'MozStorageAsyncStatementParams.webidl',
     'MozStorageStatementParams.webidl',
     'MozStorageStatementRow.webidl',
     'PrecompiledScript.webidl',
     'PromiseDebugging.webidl',
     'StructuredCloneHolder.webidl',
     'WebExtensionContentScript.webidl',
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/DocumentObserver.h
@@ -0,0 +1,61 @@
+/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_extensions_DocumentObserver_h
+#define mozilla_extensions_DocumentObserver_h
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/MozDocumentObserverBinding.h"
+
+#include "mozilla/extensions/WebExtensionContentScript.h"
+
+class nsILoadInfo;
+class nsPIDOMWindowOuter;
+
+namespace mozilla {
+namespace extensions {
+
+class DocumentObserver final : public nsISupports
+                             , public nsWrapperCache
+{
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DocumentObserver)
+
+  static already_AddRefed<DocumentObserver>
+  Constructor(dom::GlobalObject& aGlobal,
+              dom::MozDocumentCallback& aCallbacks,
+              ErrorResult& aRv);
+
+  void Observe(const dom::Sequence<OwningNonNull<MozDocumentMatcher>>& matchers, ErrorResult& aRv);
+
+  void Disconnect();
+
+  const nsTArray<RefPtr<MozDocumentMatcher>>& Matchers() const { return mMatchers; }
+
+  void NotifyMatch(MozDocumentMatcher& aMatcher, nsPIDOMWindowOuter* aWindow);
+  void NotifyMatch(MozDocumentMatcher& aMatcher, nsILoadInfo* aLoadInfo);
+
+  nsISupports* GetParentObject() const { return mParent; }
+  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+protected:
+  virtual ~DocumentObserver() = default;
+
+private:
+  explicit DocumentObserver(nsISupports* aParent, dom::MozDocumentCallback& aCallbacks)
+    : mParent(aParent)
+    , mCallbacks(&aCallbacks)
+  {}
+
+  nsCOMPtr<nsISupports> mParent;
+  RefPtr<dom::MozDocumentCallback> mCallbacks;
+  nsTArray<RefPtr<MozDocumentMatcher>> mMatchers;
+};
+
+} // namespace extensions
+} // namespace mozilla
+
+#endif // mozilla_extensions_DocumentObserver_h
+
--- a/toolkit/components/extensions/ExtensionPolicyService.cpp
+++ b/toolkit/components/extensions/ExtensionPolicyService.cpp
@@ -1,14 +1,15 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ExtensionPolicyService.h"
+#include "mozilla/extensions/DocumentObserver.h"
 #include "mozilla/extensions/WebExtensionContentScript.h"
 #include "mozilla/extensions/WebExtensionPolicy.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/Services.h"
 #include "mozilla/dom/ContentChild.h"
@@ -151,16 +152,38 @@ ExtensionPolicyService::UnregisterExtens
     return false;
   }
 
   mExtensions.Remove(aPolicy.Id());
   mExtensionHosts.Remove(aPolicy.MozExtensionHostname());
   return true;
 }
 
+bool
+ExtensionPolicyService::RegisterObserver(DocumentObserver& aObserver)
+{
+  if (mObservers.GetWeak(&aObserver)) {
+    return false;
+  }
+
+  mObservers.Put(&aObserver, &aObserver);
+  return true;
+}
+
+bool
+ExtensionPolicyService::UnregisterObserver(DocumentObserver& aObserver)
+{
+  if (!mObservers.GetWeak(&aObserver)) {
+    return false;
+  }
+
+  mObservers.Remove(&aObserver);
+  return true;
+}
+
 
 void
 ExtensionPolicyService::BaseCSP(nsAString& aBaseCSP) const
 {
   nsresult rv;
 
   rv = Preferences::GetString("extensions.webextensions.base-content-security-policy", aBaseCSP);
   if (NS_FAILED(rv)) {
@@ -346,16 +369,30 @@ ExtensionPolicyService::CheckContentScri
         if (aIsPreload) {
           ProcessScript().PreloadContentScript(script);
         } else {
           ProcessScript().LoadContentScript(script, aDocInfo.GetWindow());
         }
       }
     }
   }
+
+  for (auto iter = mObservers.Iter(); !iter.Done(); iter.Next()) {
+    RefPtr<DocumentObserver> observer = iter.Data();
+
+    for (auto& matcher : observer->Matchers()) {
+      if (matcher->Matches(aDocInfo)) {
+        if (aIsPreload) {
+          observer->NotifyMatch(*matcher, aDocInfo.GetLoadInfo());
+        } else {
+          observer->NotifyMatch(*matcher, aDocInfo.GetWindow());
+        }
+      }
+    }
+  }
 }
 
 
 /*****************************************************************************
  * nsIAddonPolicyService
  *****************************************************************************/
 
 nsresult
--- a/toolkit/components/extensions/ExtensionPolicyService.h
+++ b/toolkit/components/extensions/ExtensionPolicyService.h
@@ -23,16 +23,17 @@
 class nsIChannel;
 class nsIObserverService;
 class nsIDocument;
 class nsIPIDOMWindowOuter;
 
 namespace mozilla {
 namespace extensions {
   class DocInfo;
+  class DocumentObserver;
 }
 
 using extensions::DocInfo;
 using extensions::WebExtensionPolicy;
 
 class ExtensionPolicyService final : public nsIAddonPolicyService
                                    , public nsIObserver
                                    , public nsIMemoryReporter
@@ -71,16 +72,19 @@ public:
     return mExtensionHosts.GetWeak(aHost);
   }
 
   void GetAll(nsTArray<RefPtr<WebExtensionPolicy>>& aResult);
 
   bool RegisterExtension(WebExtensionPolicy& aPolicy);
   bool UnregisterExtension(WebExtensionPolicy& aPolicy);
 
+  bool RegisterObserver(extensions::DocumentObserver& aPolicy);
+  bool UnregisterObserver(extensions::DocumentObserver& aPolicy);
+
   void BaseCSP(nsAString& aDefaultCSP) const;
   void DefaultCSP(nsAString& aDefaultCSP) const;
 
   bool UseRemoteExtensions() const;
   bool IsExtensionProcess() const;
 
 protected:
   virtual ~ExtensionPolicyService();
@@ -95,16 +99,19 @@ private:
   void CheckDocument(nsIDocument* aDocument);
   void CheckWindow(nsPIDOMWindowOuter* aWindow);
 
   void CheckContentScripts(const DocInfo& aDocInfo, bool aIsPreload);
 
   nsRefPtrHashtable<nsPtrHashKey<const nsAtom>, WebExtensionPolicy> mExtensions;
   nsRefPtrHashtable<nsCStringHashKey, WebExtensionPolicy> mExtensionHosts;
 
+  nsRefPtrHashtable<nsPtrHashKey<const extensions::DocumentObserver>,
+                    extensions::DocumentObserver> mObservers;
+
   nsCOMPtr<nsIObserverService> mObs;
 
   static bool sRemoteExtensions;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ExtensionPolicyService_h
--- a/toolkit/components/extensions/WebExtensionContentScript.h
+++ b/toolkit/components/extensions/WebExtensionContentScript.h
@@ -57,16 +57,24 @@ public:
   nsPIDOMWindowOuter* GetWindow() const
   {
     if (mObj.is<Window>()) {
       return mObj.as<Window>();
     }
     return nullptr;
   }
 
+  nsILoadInfo* GetLoadInfo() const
+  {
+    if (mObj.is<LoadInfo>()) {
+      return mObj.as<LoadInfo>();
+    }
+    return nullptr;
+  }
+
 private:
   void SetURL(const URLInfo& aURL);
 
   const URLInfo mURL;
   mutable Maybe<const URLInfo> mPrincipalURL;
 
   mutable Maybe<bool> mIsTopLevel;
 
--- a/toolkit/components/extensions/WebExtensionPolicy.cpp
+++ b/toolkit/components/extensions/WebExtensionPolicy.cpp
@@ -1,14 +1,15 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ExtensionPolicyService.h"
+#include "mozilla/extensions/DocumentObserver.h"
 #include "mozilla/extensions/WebExtensionContentScript.h"
 #include "mozilla/extensions/WebExtensionPolicy.h"
 
 #include "mozilla/AddonManagerWebAPI.h"
 #include "mozilla/ResultExtensions.h"
 #include "nsEscape.h"
 #include "nsIDocShell.h"
 #include "nsIObserver.h"
@@ -574,16 +575,84 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Mo
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MozDocumentMatcher)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MozDocumentMatcher)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MozDocumentMatcher)
 
+/*****************************************************************************
+ * MozDocumentObserver
+ *****************************************************************************/
+
+/* static */ already_AddRefed<DocumentObserver>
+DocumentObserver::Constructor(GlobalObject& aGlobal,
+                              dom::MozDocumentCallback& aCallbacks,
+                              ErrorResult& aRv)
+{
+  RefPtr<DocumentObserver> matcher = new DocumentObserver(aGlobal.GetAsSupports(), aCallbacks);
+  return matcher.forget();
+}
+
+
+void
+DocumentObserver::Observe(const dom::Sequence<OwningNonNull<MozDocumentMatcher>>& matchers, ErrorResult& aRv)
+{
+  if (!EPS().RegisterObserver(*this)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+  mMatchers.Clear();
+  for (auto& matcher : matchers) {
+    if (!mMatchers.AppendElement(matcher, fallible)) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+  }
+}
+
+void
+DocumentObserver::Disconnect()
+{
+  Unused << EPS().UnregisterObserver(*this);
+}
+
+
+void
+DocumentObserver::NotifyMatch(MozDocumentMatcher& aMatcher, nsPIDOMWindowOuter* aWindow)
+{
+  IgnoredErrorResult rv;
+  mCallbacks->OnNewDocument(aMatcher, aWindow, rv);
+}
+
+void
+DocumentObserver::NotifyMatch(MozDocumentMatcher& aMatcher, nsILoadInfo* aLoadInfo)
+{
+  IgnoredErrorResult rv;
+  mCallbacks->OnPreloadDocument(aMatcher, aLoadInfo, rv);
+}
+
+
+JSObject*
+DocumentObserver::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
+{
+  return MozDocumentObserver_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DocumentObserver, mCallbacks, mMatchers, mParent)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentObserver)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DocumentObserver)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DocumentObserver)
 
 /*****************************************************************************
  * DocInfo
  *****************************************************************************/
 
 DocInfo::DocInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo)
   : mURL(aURL)
   , mObj(AsVariant(aLoadInfo))
--- a/toolkit/components/extensions/moz.build
+++ b/toolkit/components/extensions/moz.build
@@ -54,16 +54,17 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'webextensions'
 
 EXPORTS.mozilla = [
     'ExtensionPolicyService.h',
 ]
 
 EXPORTS.mozilla.extensions = [
+    'DocumentObserver.h',
     'MatchGlob.h',
     'MatchPattern.h',
     'WebExtensionContentScript.h',
     'WebExtensionPolicy.h',
 ]
 
 UNIFIED_SOURCES += [
     'ExtensionPolicyService.cpp',