Bug 1425104: Part 1b - Split matching logic for content scripts into MozDocumentMatcher base class. r?zombie draft
authorKris Maglione <maglione.k@gmail.com>
Wed, 18 Apr 2018 14:02:15 -0700
changeset 805619 466c39fc8af5170ea39d19d6d7aa13b8fe5cd838
parent 805618 5b08fd277ce5f750fbec6aaa239f3c7c9806e22a
child 805620 52562e8d57a8d2973cfaca9c350b6727996df39f
push id112718
push usermaglione.k@gmail.com
push dateFri, 08 Jun 2018 05:36:45 +0000
reviewerszombie
bugs1425104
milestone62.0a1
Bug 1425104: Part 1b - Split matching logic for content scripts into MozDocumentMatcher base class. r?zombie MozReview-Commit-ID: JAOWZcB4hZW
dom/bindings/Bindings.conf
dom/chrome-webidl/WebExtensionContentScript.webidl
toolkit/components/extensions/WebExtensionContentScript.h
toolkit/components/extensions/WebExtensionPolicy.cpp
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -574,16 +574,21 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::HTMLCanvasPrintState',
 },
 
 'MozChannel': {
     'nativeType': 'nsIChannel',
     'notflattened': True
 },
 
+'MozDocumentMatcher': {
+    'nativeType': 'mozilla::extensions::MozDocumentMatcher',
+    'headerFile': 'mozilla/extensions/WebExtensionContentScript.h',
+},
+
 'MozStorageAsyncStatementParams': {
     'headerFile': 'mozilla/storage/mozStorageAsyncStatementParams.h',
     'nativeType': 'mozilla::storage::AsyncStatementParams',
 },
 
 'MozStorageStatementParams': {
     'headerFile': 'mozilla/storage/mozStorageStatementParams.h',
     'nativeType': 'mozilla::storage::StatementParams',
--- a/dom/chrome-webidl/WebExtensionContentScript.webidl
+++ b/dom/chrome-webidl/WebExtensionContentScript.webidl
@@ -1,96 +1,53 @@
 /* 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/. */
 
 interface LoadInfo;
 interface URI;
 interface WindowProxy;
 
-/**
- * Describes the earliest point in the load cycle at which a script should
- * run.
- */
-enum ContentScriptRunAt {
-  /**
-   * The point in the load cycle just after the document element has been
-   * inserted, before any page scripts have been allowed to run.
-   */
-  "document_start",
-  /**
-   * The point after which the page DOM has fully loaded, but before all page
-   * resources have necessarily been loaded. Corresponds approximately to the
-   * DOMContentLoaded event.
-   */
-  "document_end",
-  /**
-   * The first point after the page and all of its resources has fully loaded
-   * when the event loop is idle, and can run scripts without delaying a paint
-   * event.
-   */
-  "document_idle",
-};
-
-[Constructor(WebExtensionPolicy extension, WebExtensionContentScriptInit options), ChromeOnly, Exposed=System]
-interface WebExtensionContentScript {
+[Constructor(MozDocumentMatcherInit options), ChromeOnly, Exposed=System]
+interface MozDocumentMatcher {
   /**
    * Returns true if the script's match and exclude patterns match the given
    * URI, without reference to attributes such as `allFrames`.
    */
   boolean matchesURI(URI uri);
 
   /**
-   * Returns true if the script matches the given URI and LoadInfo objects.
+   * Returns true if the the given URI and LoadInfo objects match.
    * This should be used to determine whether to begin pre-loading a content
    * script based on network events.
    */
   boolean matchesLoadInfo(URI uri, LoadInfo loadInfo);
 
   /**
-   * Returns true if the script matches the given window. This should be used
+   * Returns true if the given window matches. This should be used
    * to determine whether to run a script in a window at load time.
    */
   boolean matchesWindow(WindowProxy window);
 
   /**
-   * The policy object for the extension that this script belongs to.
-   */
-  [Constant]
-  readonly attribute WebExtensionPolicy extension;
-
-  /**
-   * If true, this script runs in all frames. If false, it only runs in
-   * top-level frames.
+   * If true, match all frames. If false, match only top-level frames.
    */
   [Constant]
   readonly attribute boolean allFrames;
 
   /**
    * If true, this (misleadingly-named, but inherited from Chrome) attribute
-   * causes the script to run in frames with URLs which inherit a principal
-   * that matches one of the match patterns, such as about:blank or
-   * about:srcdoc. If false, the script only runs in frames with an explicit
-   * matching URL.
+   * causes us to match frames with URLs which inherit a principal that
+   * matches one of the match patterns, such as about:blank or about:srcdoc.
+   * If false, we only match frames with an explicit matching URL.
    */
   [Constant]
   readonly attribute boolean matchAboutBlank;
 
   /**
-   * The earliest point in the load cycle at which this script should run. For
-   * static content scripts, in extensions which were present at browser
-   * startup, the browser makes every effort to make sure that the script runs
-   * no later than this point in the load cycle. For dynamic content scripts,
-   * and scripts from extensions installed during this session, the scripts
-   * may run at a later point.
-   */
-  [Constant]
-  readonly attribute ContentScriptRunAt runAt;
-
-  /**
    * The outer window ID of the frame in which to run the script, or 0 if it
    * should run in the top-level frame. Should only be used for
    * dynamically-injected scripts.
    */
   [Constant]
   readonly attribute unsigned long long? frameID;
 
   /**
@@ -119,45 +76,91 @@ interface WebExtensionContentScript {
   /**
    * A set of glob matchers for URLs in which this script should not run, even
    * if they match other include patterns or globs.
    */
   [Cached, Constant, Frozen]
   readonly attribute sequence<MatchGlob>? excludeGlobs;
 
   /**
+   * The policy object for the extension that this matcher belongs to.
+   */
+  [Constant]
+  readonly attribute WebExtensionPolicy? extension;
+};
+
+dictionary MozDocumentMatcherInit {
+  boolean allFrames = false;
+
+  boolean matchAboutBlank = false;
+
+  unsigned long long? frameID = null;
+
+  required MatchPatternSet matches;
+
+  MatchPatternSet? excludeMatches = null;
+
+  sequence<MatchGlob>? includeGlobs = null;
+
+  sequence<MatchGlob>? excludeGlobs = null;
+
+  boolean hasActiveTabPermission = false;
+};
+
+/**
+ * Describes the earliest point in the load cycle at which a script should
+ * run.
+ */
+enum ContentScriptRunAt {
+  /**
+   * The point in the load cycle just after the document element has been
+   * inserted, before any page scripts have been allowed to run.
+   */
+  "document_start",
+  /**
+   * The point after which the page DOM has fully loaded, but before all page
+   * resources have necessarily been loaded. Corresponds approximately to the
+   * DOMContentLoaded event.
+   */
+  "document_end",
+  /**
+   * The first point after the page and all of its resources has fully loaded
+   * when the event loop is idle, and can run scripts without delaying a paint
+   * event.
+   */
+  "document_idle",
+};
+
+[Constructor(WebExtensionPolicy extension, WebExtensionContentScriptInit options), ChromeOnly, Exposed=System]
+interface WebExtensionContentScript : MozDocumentMatcher {
+  /**
+   * The earliest point in the load cycle at which this script should run. For
+   * static content scripts, in extensions which were present at browser
+   * startup, the browser makes every effort to make sure that the script runs
+   * no later than this point in the load cycle. For dynamic content scripts,
+   * and scripts from extensions installed during this session, the scripts
+   * may run at a later point.
+   */
+  [Constant]
+  readonly attribute ContentScriptRunAt runAt;
+
+  /**
    * A set of paths, relative to the extension root, of CSS sheets to inject
    * into matching pages.
    */
   [Cached, Constant, Frozen]
   readonly attribute sequence<DOMString> cssPaths;
 
   /**
    * A set of paths, relative to the extension root, of JavaScript scripts to
    * execute in matching pages.
    */
   [Cached, Constant, Frozen]
   readonly attribute sequence<DOMString> jsPaths;
 };
 
-dictionary WebExtensionContentScriptInit {
-  boolean allFrames = false;
-
-  boolean matchAboutBlank = false;
-
+dictionary WebExtensionContentScriptInit : MozDocumentMatcherInit {
   ContentScriptRunAt runAt = "document_idle";
 
-  unsigned long long? frameID = null;
-
-  boolean hasActiveTabPermission = false;
-
-  required MatchPatternSet matches;
-
-  MatchPatternSet? excludeMatches = null;
-
-  sequence<MatchGlob>? includeGlobs = null;
-
-  sequence<MatchGlob>? excludeGlobs = null;
-
   sequence<DOMString> cssPaths = [];
 
   sequence<DOMString> jsPaths = [];
 };
--- a/toolkit/components/extensions/WebExtensionContentScript.h
+++ b/toolkit/components/extensions/WebExtensionContentScript.h
@@ -75,122 +75,145 @@ private:
 
   using Window = nsPIDOMWindowOuter*;
   using LoadInfo = nsILoadInfo*;
 
   const Variant<LoadInfo, Window> mObj;
 };
 
 
-class WebExtensionContentScript final : public nsISupports
-                                      , public nsWrapperCache
+class MozDocumentMatcher : public nsISupports
+                         , public nsWrapperCache
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebExtensionContentScript)
-
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MozDocumentMatcher)
 
   using MatchGlobArray = nsTArray<RefPtr<MatchGlob>>;
-  using RunAtEnum = dom::ContentScriptRunAt;
 
-  static already_AddRefed<WebExtensionContentScript>
+  static already_AddRefed<MozDocumentMatcher>
   Constructor(dom::GlobalObject& aGlobal,
-              WebExtensionPolicy& aExtension,
-              const ContentScriptInit& aInit,
+              const dom::MozDocumentMatcherInit& aInit,
               ErrorResult& aRv);
 
-
   bool Matches(const DocInfo& aDoc) const;
   bool MatchesURI(const URLInfo& aURL) const;
 
   bool MatchesLoadInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo) const
   {
     return Matches({aURL, aLoadInfo});
   }
   bool MatchesWindow(nsPIDOMWindowOuter* aWindow) const
   {
     return Matches(aWindow);
   }
 
 
+  WebExtensionPolicy* GetExtension() { return mExtension; }
+
   WebExtensionPolicy* Extension() { return mExtension; }
   const WebExtensionPolicy* Extension() const { return mExtension; }
 
   bool AllFrames() const { return mAllFrames; }
   bool MatchAboutBlank() const { return mMatchAboutBlank; }
-  RunAtEnum RunAt() const { return mRunAt; }
-
-  Nullable<uint64_t> GetFrameID() const { return mFrameID; }
 
   MatchPatternSet* Matches() { return mMatches; }
   const MatchPatternSet* GetMatches() const { return mMatches; }
 
   MatchPatternSet* GetExcludeMatches() { return mExcludeMatches; }
   const MatchPatternSet* GetExcludeMatches() const { return mExcludeMatches; }
 
   void GetIncludeGlobs(Nullable<MatchGlobArray>& aGlobs)
   {
     ToNullable(mExcludeGlobs, aGlobs);
   }
   void GetExcludeGlobs(Nullable<MatchGlobArray>& aGlobs)
   {
     ToNullable(mExcludeGlobs, aGlobs);
   }
 
+  Nullable<uint64_t> GetFrameID() const { return mFrameID; }
+
+
+  WebExtensionPolicy* GetParentObject() const { return mExtension; }
+  virtual JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
+
+protected:
+  friend class WebExtensionPolicy;
+
+  virtual ~MozDocumentMatcher() = default;
+
+  MozDocumentMatcher(const dom::MozDocumentMatcherInit& aInit,
+                     ErrorResult& aRv);
+
+  RefPtr<WebExtensionPolicy> mExtension;
+
+  bool mHasActiveTabPermission;
+  bool mRestricted;
+
+  RefPtr<MatchPatternSet> mMatches;
+  RefPtr<MatchPatternSet> mExcludeMatches;
+
+  Nullable<MatchGlobSet> mIncludeGlobs;
+  Nullable<MatchGlobSet> mExcludeGlobs;
+
+
+  bool mAllFrames;
+  Nullable<uint64_t> mFrameID;
+  bool mMatchAboutBlank;
+
+private:
+  template <typename T, typename U>
+  void
+  ToNullable(const Nullable<T>& aInput, Nullable<U>& aOutput)
+  {
+    if (aInput.IsNull()) {
+      aOutput.SetNull();
+    } else {
+      aOutput.SetValue(aInput.Value());
+    }
+  }
+};
+
+class WebExtensionContentScript final : public MozDocumentMatcher
+{
+public:
+
+  using RunAtEnum = dom::ContentScriptRunAt;
+
+  static already_AddRefed<WebExtensionContentScript>
+  Constructor(dom::GlobalObject& aGlobal,
+              WebExtensionPolicy& aExtension,
+              const ContentScriptInit& aInit,
+              ErrorResult& aRv);
+
+  RunAtEnum RunAt() const { return mRunAt; }
+
   void GetCssPaths(nsTArray<nsString>& aPaths) const
   {
     aPaths.AppendElements(mCssPaths);
   }
   void GetJsPaths(nsTArray<nsString>& aPaths) const
   {
     aPaths.AppendElements(mJsPaths);
   }
 
-
-  WebExtensionPolicy* GetParentObject() const { return mExtension; }
-
   virtual JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
 
 protected:
   friend class WebExtensionPolicy;
 
   virtual ~WebExtensionContentScript() = default;
 
   WebExtensionContentScript(WebExtensionPolicy& aExtension,
                             const ContentScriptInit& aInit,
                             ErrorResult& aRv);
 
 private:
-  RefPtr<WebExtensionPolicy> mExtension;
-
-  bool mHasActiveTabPermission;
-  bool mRestricted;
-
-  RefPtr<MatchPatternSet> mMatches;
-  RefPtr<MatchPatternSet> mExcludeMatches;
-
-  Nullable<MatchGlobSet> mIncludeGlobs;
-  Nullable<MatchGlobSet> mExcludeGlobs;
-
   nsTArray<nsString> mCssPaths;
   nsTArray<nsString> mJsPaths;
 
   RunAtEnum mRunAt;
-
-  bool mAllFrames;
-  Nullable<uint64_t> mFrameID;
-  bool mMatchAboutBlank;
-
-  template <typename T, typename U>
-  void
-  ToNullable(const Nullable<T>& aInput, Nullable<U>& aOutput)
-  {
-    if (aInput.IsNull()) {
-      aOutput.SetNull();
-    } else {
-      aOutput.SetValue(aInput.Value());
-    }
-  }
 };
 
 } // namespace extensions
 } // namespace mozilla
 
 #endif // mozilla_extensions_WebExtensionContentScript_h
--- a/toolkit/components/extensions/WebExtensionPolicy.cpp
+++ b/toolkit/components/extensions/WebExtensionPolicy.cpp
@@ -423,58 +423,77 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionPolicy)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionPolicy)
 
 
 /*****************************************************************************
- * WebExtensionContentScript
+ * WebExtensionContentScript / MozDocumentMatcher
  *****************************************************************************/
 
+/* static */ already_AddRefed<MozDocumentMatcher>
+MozDocumentMatcher::Constructor(GlobalObject& aGlobal,
+                                const dom::MozDocumentMatcherInit& aInit,
+                                ErrorResult& aRv)
+{
+  RefPtr<MozDocumentMatcher> matcher = new MozDocumentMatcher(aInit, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+  return matcher.forget();
+}
+
 /* static */ already_AddRefed<WebExtensionContentScript>
 WebExtensionContentScript::Constructor(GlobalObject& aGlobal,
                                        WebExtensionPolicy& aExtension,
                                        const ContentScriptInit& aInit,
                                        ErrorResult& aRv)
 {
   RefPtr<WebExtensionContentScript> script = new WebExtensionContentScript(aExtension, aInit, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   return script.forget();
 }
 
-WebExtensionContentScript::WebExtensionContentScript(WebExtensionPolicy& aExtension,
-                                                     const ContentScriptInit& aInit,
-                                                     ErrorResult& aRv)
-  : mExtension(&aExtension)
-  , mHasActiveTabPermission(aInit.mHasActiveTabPermission)
-  , mRestricted(!aExtension.HasPermission(nsGkAtoms::mozillaAddons))
+MozDocumentMatcher::MozDocumentMatcher(const dom::MozDocumentMatcherInit& aInit,
+                                       ErrorResult& aRv)
+  : mHasActiveTabPermission(aInit.mHasActiveTabPermission)
+  , mRestricted(false)
   , mMatches(aInit.mMatches)
   , mExcludeMatches(aInit.mExcludeMatches)
-  , mCssPaths(aInit.mCssPaths)
-  , mJsPaths(aInit.mJsPaths)
-  , mRunAt(aInit.mRunAt)
   , mAllFrames(aInit.mAllFrames)
   , mFrameID(aInit.mFrameID)
   , mMatchAboutBlank(aInit.mMatchAboutBlank)
 {
   if (!aInit.mIncludeGlobs.IsNull()) {
     mIncludeGlobs.SetValue().AppendElements(aInit.mIncludeGlobs.Value());
   }
 
   if (!aInit.mExcludeGlobs.IsNull()) {
     mExcludeGlobs.SetValue().AppendElements(aInit.mExcludeGlobs.Value());
   }
 }
 
+WebExtensionContentScript::WebExtensionContentScript(WebExtensionPolicy& aExtension,
+                                                     const ContentScriptInit& aInit,
+                                                     ErrorResult& aRv)
+  : MozDocumentMatcher(aInit, aRv)
+  , mCssPaths(aInit.mCssPaths)
+  , mJsPaths(aInit.mJsPaths)
+  , mRunAt(aInit.mRunAt)
+{
+  mExtension = &aExtension;
+  mRestricted = !aExtension.HasPermission(nsGkAtoms::mozillaAddons);
+}
+
 bool
-WebExtensionContentScript::Matches(const DocInfo& aDoc) const
+MozDocumentMatcher::Matches(const DocInfo& aDoc) const
 {
   if (!mFrameID.IsNull()) {
     if (aDoc.FrameID() != mFrameID.Value()) {
       return false;
     }
   } else {
     if (!mAllFrames && !aDoc.IsTopLevel()) {
       return false;
@@ -503,17 +522,17 @@ WebExtensionContentScript::Matches(const
       MatchPattern::MatchesAllURLs(urlinfo)) {
     return true;
   }
 
   return MatchesURI(urlinfo);
 }
 
 bool
-WebExtensionContentScript::MatchesURI(const URLInfo& aURL) const
+MozDocumentMatcher::MatchesURI(const URLInfo& aURL) const
 {
   if (!mMatches->Matches(aURL)) {
     return false;
   }
 
   if (mExcludeMatches && mExcludeMatches->Matches(aURL)) {
     return false;
   }
@@ -530,34 +549,40 @@ WebExtensionContentScript::MatchesURI(co
     return false;
   }
 
   return true;
 }
 
 
 JSObject*
+MozDocumentMatcher::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
+{
+  return MozDocumentMatcherBinding::Wrap(aCx, this, aGivenProto);
+}
+
+JSObject*
 WebExtensionContentScript::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
 {
   return WebExtensionContentScriptBinding::Wrap(aCx, this, aGivenProto);
 }
 
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionContentScript,
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MozDocumentMatcher,
                                       mMatches, mExcludeMatches,
                                       mIncludeGlobs, mExcludeGlobs,
                                       mExtension)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionContentScript)
+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(WebExtensionContentScript)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionContentScript)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MozDocumentMatcher)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MozDocumentMatcher)
 
 
 /*****************************************************************************
  * DocInfo
  *****************************************************************************/
 
 DocInfo::DocInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo)
   : mURL(aURL)