Bug 1472491: Part 2a - Split matching logic for content scripts into MozDocumentMatcher base class. r=zombie
authorKris Maglione <maglione.k@gmail.com>
Wed, 18 Apr 2018 14:02:15 -0700
changeset 431451 5479607a771acc685552de2b7f968c0c64cc2358
parent 431450 823dbd89637cf9c0847de7957932220bcc6905a0
child 431452 761dcb3fa4e90fe6e500671ca8123c6dae96103e
push id34443
push usercsabou@mozilla.com
push dateWed, 15 Aug 2018 00:53:32 +0000
treeherdermozilla-central@b80906e2fbc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerszombie
bugs1472491
milestone63.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 1472491: Part 2a - 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
@@ -576,16 +576,21 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::HTMLCanvasPrintState',
 },
 
 'MozChannel': {
     'nativeType': 'nsIChannel',
     'notflattened': True
 },
 
+'MozDocumentMatcher': {
+    'nativeType': 'mozilla::extensions::MozDocumentMatcher',
+    'headerFile': 'mozilla/extensions/WebExtensionContentScript.h',
+},
+
 'MozSharedMap': {
     'nativeType': 'mozilla::dom::ipc::SharedMap',
 },
 
 'MozWritableSharedMap': {
     'headerFile': 'mozilla/dom/ipc/SharedMap.h',
     'nativeType': 'mozilla::dom::ipc::WritableSharedMap',
 },
--- 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 MozDocumentMatcher_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+JSObject*
 WebExtensionContentScript::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
 {
   return WebExtensionContentScript_Binding::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)