Bug 1333990: Part 2d - Add a utility to block HTML parsing until sandbox scripts are ready. r=hsivonen,billm
authorKris Maglione <maglione.k@gmail.com>
Thu, 16 Mar 2017 16:47:35 -0700
changeset 348214 fce9fb688fe01cd5bcf9997be7ecc6bbca38dbf7
parent 348213 3ce64ea6ccdc400400e0dca38573cfc2a93be05a
child 348215 dc7501462ffff389ef10ac910c41140bad0ae575
push id39092
push userkwierso@gmail.com
push dateFri, 17 Mar 2017 18:14:05 +0000
treeherderautoland@88576fd417e7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen, billm
bugs1333990
milestone55.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 1333990: Part 2d - Add a utility to block HTML parsing until sandbox scripts are ready. r=hsivonen,billm In order to asynchronously load content scripts that need to run very early in the page load cycle, before any ordinary page scripts, we need to be able to block parsing from the document-element-inserted listener. Since the script loader operates by returning promises, blocking on promise resolution is the simplest way to achieve this. MozReview-Commit-ID: CTWlyrP6dqG
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
dom/webidl/Document.webidl
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -70,16 +70,18 @@
 #include "nsIFrame.h"
 #include "nsITabChild.h"
 
 #include "nsRange.h"
 #include "nsIDOMText.h"
 #include "nsIDOMComment.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/NodeIterator.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/TreeWalker.h"
 
 #include "nsIServiceManager.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "imgLoader.h"
 
 #include "nsCanvasFrame.h"
 #include "nsContentCID.h"
@@ -2739,16 +2741,23 @@ nsDocument::InitCSP(nsIChannel* aChannel
       // stop!  ERROR page!
       aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
     }
   }
   ApplySettingsFromCSP(false);
   return NS_OK;
 }
 
+already_AddRefed<nsIParser>
+nsDocument::CreatorParserOrNull()
+{
+  nsCOMPtr<nsIParser> parser = mParser;
+  return parser.forget();
+}
+
 void
 nsDocument::StopDocumentLoad()
 {
   if (mParser) {
     mParserAborted = true;
     mParser->Terminate();
   }
 }
@@ -10509,16 +10518,88 @@ nsIDocument::ObsoleteSheet(const nsAStri
     return;
   }
   res = CSSLoader()->ObsoleteSheet(uri);
   if (NS_FAILED(res)) {
     rv.Throw(res);
   }
 }
 
+class UnblockParsingPromiseHandler final : public PromiseNativeHandler
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise)
+    : mDocument(aDocument)
+    , mPromise(aPromise)
+  {
+    nsCOMPtr<nsIParser> parser = mDocument->CreatorParserOrNull();
+    if (parser) {
+      parser->BlockParser();
+    } else {
+      mDocument = nullptr;
+    }
+  }
+
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    MaybeUnblockParser();
+
+    mPromise->MaybeResolve(aCx, aValue);
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    MaybeUnblockParser();
+
+    mPromise->MaybeReject(aCx, aValue);
+  }
+
+protected:
+  virtual ~UnblockParsingPromiseHandler()
+  {
+    MaybeUnblockParser();
+  }
+
+private:
+  void MaybeUnblockParser() {
+    if (mDocument) {
+      nsCOMPtr<nsIParser> parser = mDocument->CreatorParserOrNull();
+      if (parser) {
+        parser->UnblockParser();
+        parser->ContinueInterruptedParsingAsync();
+      }
+      mDocument = nullptr;
+    }
+  }
+
+  RefPtr<nsIDocument> mDocument;
+  RefPtr<Promise> mPromise;
+};
+
+NS_IMPL_ISUPPORTS0(UnblockParsingPromiseHandler)
+
+already_AddRefed<Promise>
+nsIDocument::BlockParsing(OwningNonNull<Promise> aPromise,
+                          ErrorResult& aRv)
+{
+  RefPtr<Promise> resultPromise = Promise::Create(aPromise->GetParentObject(), aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  RefPtr<PromiseNativeHandler> promiseHandler = new UnblockParsingPromiseHandler(this, resultPromise);
+  aPromise->AppendNativeHandler(promiseHandler);
+
+  return resultPromise.forget();
+}
+
 already_AddRefed<nsIURI>
 nsIDocument::GetMozDocumentURIIfNotForErrorPages()
 {
   if (mFailedChannel) {
     nsCOMPtr<nsIURI> failedURI;
     if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) {
       return failedURI.forget();
     }
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -532,16 +532,18 @@ public:
   virtual void SetDocumentURI(nsIURI* aURI) override;
 
   virtual void SetChromeXHRDocURI(nsIURI* aURI) override;
 
   virtual void SetChromeXHRDocBaseURI(nsIURI* aURI) override;
 
   virtual void ApplySettingsFromCSP(bool aSpeculative) override;
 
+  virtual already_AddRefed<nsIParser> CreatorParserOrNull() override;
+
   /**
    * Set the principal responsible for this document.
    */
   virtual void SetPrincipal(nsIPrincipal *aPrincipal) override;
 
   /**
    * Get the Content-Type of this document.
    */
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -10,16 +10,17 @@
 #include "nsAutoPtr.h"                   // for member
 #include "nsCOMArray.h"                  // for member
 #include "nsCompatibility.h"             // for member
 #include "nsCOMPtr.h"                    // for member
 #include "nsGkAtoms.h"                   // for static class members
 #include "nsIDocumentObserver.h"         // for typedef (nsUpdateType)
 #include "nsILoadGroup.h"                // for member (in nsCOMPtr)
 #include "nsINode.h"                     // for base class
+#include "nsIParser.h"
 #include "nsIScriptGlobalObject.h"       // for member (in nsCOMPtr)
 #include "nsIServiceManager.h"
 #include "nsIUUIDGenerator.h"
 #include "nsPIDOMWindow.h"               // for use in inline functions
 #include "nsPropertyTable.h"             // for member
 #include "nsDataHashtable.h"             // for member
 #include "nsURIHashKey.h"                // for member
 #include "mozilla/net/ReferrerPolicy.h"  // for member
@@ -354,16 +355,18 @@ public:
    */
   virtual void SetChromeXHRDocBaseURI(nsIURI* aURI) = 0;
 
   /**
    * Set referrer policy and upgrade-insecure-requests flags
    */
   virtual void ApplySettingsFromCSP(bool aSpeculative) = 0;
 
+  virtual already_AddRefed<nsIParser> CreatorParserOrNull() = 0;
+
   /**
    * Return the referrer policy of the document. Return "default" if there's no
    * valid meta referrer tag found in the document.
    */
   ReferrerPolicyEnum GetReferrerPolicy() const
   {
     return mReferrerPolicy;
   }
@@ -2779,16 +2782,19 @@ public:
   {
     return mStyleSheetChangeEventsEnabled;
   }
 
   void ObsoleteSheet(nsIURI *aSheetURI, mozilla::ErrorResult& rv);
 
   void ObsoleteSheet(const nsAString& aSheetURI, mozilla::ErrorResult& rv);
 
+  already_AddRefed<mozilla::dom::Promise> BlockParsing(mozilla::OwningNonNull<mozilla::dom::Promise> aPromise,
+                                                       mozilla::ErrorResult& aRv);
+
   already_AddRefed<nsIURI> GetMozDocumentURIIfNotForErrorPages();
 
   // ParentNode
   nsIHTMLCollection* Children();
   uint32_t ChildElementCount();
 
   virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
   virtual mozilla::dom::SVGDocument* AsSVGDocument() { return nullptr; }
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -374,16 +374,20 @@ partial interface Document {
   void obsoleteSheet(DOMString sheetURI);
 
   [ChromeOnly] readonly attribute nsIDocShell? docShell;
 
   [ChromeOnly] readonly attribute DOMString contentLanguage;
 
   [ChromeOnly] readonly attribute nsILoadGroup? documentLoadGroup;
 
+  // Blocks the initial document parser until the given promise is settled.
+  [ChromeOnly, Throws]
+  Promise<any> blockParsing(Promise<any> promise);
+
   // like documentURI, except that for error pages, it returns the URI we were
   // trying to load when we hit an error, rather than the error page's own URI.
   [ChromeOnly] readonly attribute URI? mozDocumentURIIfNotForErrorPages;
 };
 
 // Extension to give chrome JS the ability to determine when a document was
 // created to satisfy an iframe with srcdoc attribute.
 partial interface Document {