Bug 1412173 Part 1: Implement the Document ignore-opens-during-unload counter required by spec.
MozReview-Commit-ID: IWfT5c0H3t
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1593,17 +1593,18 @@ nsIDocument::nsIDocument()
mInSyncOperationCount(0),
mBlockDOMContentLoaded(0),
mUseCounters(0),
mChildDocumentUseCounters(0),
mNotifiedPageForUseCounter(0),
mIncCounters(),
mUserHasInteracted(false),
mServoRestyleRootDirtyBits(0),
- mThrowOnDynamicMarkupInsertionCounter(0)
+ mThrowOnDynamicMarkupInsertionCounter(0),
+ mIgnoreOpensDuringUnloadCounter(0)
{
SetIsInDocument();
for (auto& cnt : mIncCounters) {
cnt = 0;
}
}
nsDocument::nsDocument(const char* aContentType)
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -3212,16 +3212,32 @@ public:
}
void DecrementThrowOnDynamicMarkupInsertionCounter()
{
MOZ_ASSERT(mThrowOnDynamicMarkupInsertionCounter);
--mThrowOnDynamicMarkupInsertionCounter;
}
+ bool ShouldIgnoreOpens() const
+ {
+ return mIgnoreOpensDuringUnloadCounter;
+ }
+
+ void IncrementIgnoreOpensDuringUnloadCounter()
+ {
+ ++mIgnoreOpensDuringUnloadCounter;
+ }
+
+ void DecrementIgnoreOpensDuringUnloadCounter()
+ {
+ MOZ_ASSERT(mIgnoreOpensDuringUnloadCounter);
+ --mIgnoreOpensDuringUnloadCounter;
+ }
+
virtual bool AllowPaymentRequest() const = 0;
virtual void SetAllowPaymentRequest(bool aAllowPaymentRequest) = 0;
protected:
bool GetUseCounter(mozilla::UseCounter aUseCounter)
{
return mUseCounters[aUseCounter];
}
@@ -3763,16 +3779,19 @@ protected:
// root corresponds to.
nsCOMPtr<nsINode> mServoRestyleRoot;
uint32_t mServoRestyleRootDirtyBits;
// Used in conjunction with the create-an-element-for-the-token algorithm to
// prevent custom element constructors from being able to use document.open(),
// document.close(), and document.write() when they are invoked by the parser.
uint32_t mThrowOnDynamicMarkupInsertionCounter;
+
+ // Count of unload/beforeunload/pagehide operations in progress.
+ uint32_t mIgnoreOpensDuringUnloadCounter;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
/**
* mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
* event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
* object is deleted.
@@ -3836,16 +3855,33 @@ class MOZ_RAII AutoSetThrowOnDynamicMark
~AutoSetThrowOnDynamicMarkupInsertionCounter() {
mDocument->DecrementThrowOnDynamicMarkupInsertionCounter();
}
private:
nsIDocument* mDocument;
};
+class MOZ_RAII IgnoreOpensDuringUnload final
+{
+public:
+ explicit IgnoreOpensDuringUnload(nsIDocument* aDoc)
+ : mDoc(aDoc)
+ {
+ mDoc->IncrementIgnoreOpensDuringUnloadCounter();
+ }
+
+ ~IgnoreOpensDuringUnload()
+ {
+ mDoc->DecrementIgnoreOpensDuringUnloadCounter();
+ }
+private:
+ nsIDocument* mDoc;
+};
+
// XXX These belong somewhere else
nsresult
NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false);
nsresult
NS_NewXMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false,
bool aIsPlainDocument = false);
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1529,16 +1529,23 @@ nsHTMLDocument::Open(JSContext* cx,
// invoked."
// Note that aborting a parser leaves the parser "active" with its
// insertion point "not undefined". We track this using mParserAborted,
// because aborting a parser nulls out mParser.
nsCOMPtr<nsIDocument> ret = this;
return ret.forget();
}
+ // Implement Step 6 of:
+ // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps
+ if (ShouldIgnoreOpens()) {
+ nsCOMPtr<nsIDocument> ret = this;
+ return ret.forget();
+ }
+
// No calling document.open() without a script global object
if (!mScriptGlobalObject) {
nsCOMPtr<nsIDocument> ret = this;
return ret.forget();
}
nsPIDOMWindowOuter* outer = GetWindow();
if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
@@ -1942,16 +1949,22 @@ nsHTMLDocument::WriteCommon(JSContext *c
if (mParserAborted) {
// Hixie says aborting the parser doesn't undefine the insertion point.
// However, since we null out mParser in that case, we track the
// theoretically defined insertion point using mParserAborted.
return NS_OK;
}
+ // Implement Step 4.1 of:
+ // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
+ if (ShouldIgnoreOpens()) {
+ return NS_OK;
+ }
+
nsresult rv = NS_OK;
void *key = GenerateParserKey();
if (mParser && !mParser->IsInsertionPointDefined()) {
if (mIgnoreDestructiveWritesCounter) {
// Instead of implying a call to document.open(), ignore the call.
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("DOM Events"), this,
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1184,16 +1184,23 @@ nsDocumentViewer::PermitUnloadInternal(b
if (!window) {
// This is odd, but not fatal
NS_WARNING("window not set for document!");
return NS_OK;
}
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
+ // https://html.spec.whatwg.org/multipage/browsing-the-web.html#prompt-to-unload-a-document
+ // Create an RAII object on mDocument that will increment the
+ // should-ignore-opens-during-unload counter on initialization
+ // and decrement it again when it goes out of score (regardless
+ // of how we exit this function).
+ IgnoreOpensDuringUnload ignoreOpens(mDocument);
+
// Now, fire an BeforeUnload event to the document and see if it's ok
// to unload...
nsIPresShell* shell = mDocument->GetShell();
nsPresContext* presContext = nullptr;
if (shell) {
presContext = shell->GetPresContext();
}
RefPtr<BeforeUnloadEvent> event =
@@ -1392,16 +1399,22 @@ nsDocumentViewer::PageHide(bool aIsUnloa
nsPIDOMWindowOuter* window = mDocument->GetWindow();
if (!window) {
// Fail if no window is available...
NS_WARNING("window not set for document!");
return NS_ERROR_NULL_POINTER;
}
+ // https://html.spec.whatwg.org/multipage/browsing-the-web.html#unload-a-document
+ // Create an RAII object on mDocument that will increment the
+ // should-ignore-opens-during-unload counter on initialization
+ // and decrement it again when it goes out of scope.
+ IgnoreOpensDuringUnload ignoreOpens(mDocument);
+
// Now, fire an Unload event to the document...
nsEventStatus status = nsEventStatus_eIgnore;
WidgetEvent event(true, eUnload);
event.mFlags.mBubbles = false;
// XXX Dispatching to |window|, but using |document| as the target.
event.mTarget = mDocument;
// Never permit popups from the unload handler, no matter how we get