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

--HG--
extra : rebase_source : 28ce713a6450c223f9b2089e6c6e8c78284ef8af
This commit is contained in:
Kris Maglione 2017-03-16 16:47:35 -07:00
parent 250fd89a21
commit 8d2a5ab915
4 changed files with 93 additions and 0 deletions

View File

@ -75,6 +75,8 @@
#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"
@ -2744,6 +2746,13 @@ nsDocument::InitCSP(nsIChannel* aChannel)
return NS_OK;
}
already_AddRefed<nsIParser>
nsDocument::CreatorParserOrNull()
{
nsCOMPtr<nsIParser> parser = mParser;
return parser.forget();
}
void
nsDocument::StopDocumentLoad()
{
@ -10514,6 +10523,78 @@ nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
}
}
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()
{

View File

@ -537,6 +537,8 @@ public:
virtual void ApplySettingsFromCSP(bool aSpeculative) override;
virtual already_AddRefed<nsIParser> CreatorParserOrNull() override;
/**
* Set the principal responsible for this document.
*/

View File

@ -15,6 +15,7 @@
#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"
@ -359,6 +360,8 @@ public:
*/
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.
@ -2784,6 +2787,9 @@ public:
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

View File

@ -379,6 +379,10 @@ partial interface Document {
[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;