Bug 1455680 - Notify document-element-inserted in XUL documents;r=bz
We'll want to use this event to inject scripts before other scripts run
in XUL documents. It already fires in HTML documents.
MozReview-Commit-ID: 7FW0R8r9o9G
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -1640,16 +1640,18 @@ nsContentSink::WillBuildModelImpl()
DoProcessLinkHeader();
}
}
/* static */
void
nsContentSink::NotifyDocElementCreated(nsIDocument* aDoc)
{
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
observerService->
NotifyObservers(domDoc, "document-element-inserted",
EmptyString().get());
}
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -60,16 +60,20 @@ support-files = ../file_bug357450.js
[test_bug1063837.xul]
[test_bug1139964.xul]
[test_bug1209621.xul]
[test_bug1346936.html]
[test_cpows.xul]
[test_getElementsWithGrid.html]
[test_custom_element_content.xul]
[test_custom_element_ep.xul]
+[test_document-element-inserted.xul]
+support-files =
+ file_document-element-inserted.xul
+ file_document-element-inserted-inner.xul
[test_domparsing.xul]
[test_fileconstructor.xul]
[test_nsITextInputProcessor.xul]
[test_node_localize.xul]
[test_permission_isHandlingUserInput.xul]
support-files = ../dummy.html
[test_range_getClientRectsAndTexts.html]
[test_title.xul]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_document-element-inserted-inner.xul
@@ -0,0 +1,1 @@
+<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'></window>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_document-element-inserted.xul
@@ -0,0 +1,3 @@
+<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'>
+ <iframe src='file_document-element-inserted-inner.xul'></iframe>
+</window>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/test_document-element-inserted.xul
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1411707
+-->
+<window title="Mozilla Bug 1411707"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1411707"
+ target="_blank">Mozilla Bug 1411707</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ const OUTER_URL = "chrome://mochitests/content/chrome/dom/base/test/chrome/file_document-element-inserted.xul";
+ const INNER_URL = "chrome://mochitests/content/chrome/dom/base/test/chrome/file_document-element-inserted-inner.xul";
+
+ async function waitForEvent(url) {
+ return new Promise(resolve => {
+ SpecialPowers.addObserver(function inserted(document) {
+ is(document.documentURI, url, "Correct URL");
+ is(document.readyState, "uninitialized", "Correct readyState");
+ SpecialPowers.removeObserver(inserted, "document-element-inserted");
+ resolve();
+ }, "document-element-inserted");
+ })
+ }
+
+ // Load a XUL document that also has an iframe to a subdocument, and
+ // expect both events to fire with the docs in the correct state.
+ async function testEvents() {
+ info(`Waiting for events after loading ${OUTER_URL}`);
+ let win = window.openDialog(OUTER_URL, null, "chrome,dialog=no,all");
+ await waitForEvent(OUTER_URL);
+ await waitForEvent(INNER_URL);
+ win.close();
+ }
+
+ (async function() {
+ // Test the same document twice to make to make sure we are
+ // firing properly when loading the protype document.
+ await testEvents();
+ await testEvents();
+ SimpleTest.finish();
+ })();
+ ]]></script>
+</window>
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -14,16 +14,17 @@
#include "nsIDocShell.h"
#include "nsIStyleSheetLinkingElement.h"
#include "nsHTMLParts.h"
#include "nsCRT.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/css/Loader.h"
#include "nsGkAtoms.h"
#include "nsContentUtils.h"
+#include "nsDocElementCreatedNotificationRunner.h"
#include "nsIScriptContext.h"
#include "nsNameSpaceManager.h"
#include "nsIServiceManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsIContentViewer.h"
#include "prtime.h"
#include "mozilla/Logging.h"
#include "nsRect.h"
@@ -1045,17 +1046,18 @@ nsXMLContentSink::HandleStartElement(con
}
if (IsMonolithicContainer(nodeInfo)) {
mInMonolithicContainer++;
}
if (!mXSLTProcessor) {
if (content == mDocElement) {
- NotifyDocElementCreated(mDocument);
+ nsContentUtils::AddScriptRunner(
+ new nsDocElementCreatedNotificationRunner(mDocument));
if (aInterruptable && NS_SUCCEEDED(result) && mParser && !mParser->IsParserEnabled()) {
return NS_ERROR_HTMLPARSER_BLOCK;
}
} else if (!mCurrentHead) {
// This isn't the root and we're not inside an XHTML <head>.
// Might need to start layout
MaybeStartLayout(false);
--- a/dom/xslt/xslt/txMozillaXMLOutput.cpp
+++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp
@@ -27,16 +27,17 @@
#include "nsIDocumentTransformer.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/css/Loader.h"
#include "mozilla/dom/DocumentType.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ScriptLoader.h"
#include "mozilla/Encoding.h"
#include "nsContentUtils.h"
+#include "nsDocElementCreatedNotificationRunner.h"
#include "txXMLUtils.h"
#include "nsContentSink.h"
#include "nsINode.h"
#include "nsContentCreatorFunctions.h"
#include "nsError.h"
#include "nsIFrame.h"
#include <algorithm>
#include "nsTextNode.h"
@@ -573,17 +574,18 @@ txMozillaXMLOutput::closePrevious(bool a
NS_ENSURE_SUCCESS(rv, rv);
}
rv = mCurrentNode->AppendChildTo(mOpenedElement, true);
NS_ENSURE_SUCCESS(rv, rv);
if (currentIsDoc) {
mRootContentCreated = true;
- nsContentSink::NotifyDocElementCreated(mDocument);
+ nsContentUtils::AddScriptRunner(
+ new nsDocElementCreatedNotificationRunner(mDocument));
}
mCurrentNode = mOpenedElement;
mOpenedElement = nullptr;
}
else if (aFlushText && !mText.IsEmpty()) {
// Text can't appear in the root of a document
if (mDocument == mCurrentNode) {
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -35,16 +35,17 @@
#include "nsITimer.h"
#include "nsDocShell.h"
#include "nsGkAtoms.h"
#include "nsXMLContentSink.h"
#include "nsXULContentSink.h"
#include "nsXULContentUtils.h"
#include "nsIXULOverlayProvider.h"
#include "nsIStringEnumerator.h"
+#include "nsDocElementCreatedNotificationRunner.h"
#include "nsNetUtil.h"
#include "nsParserCIID.h"
#include "nsPIBoxObject.h"
#include "mozilla/dom/BoxObject.h"
#include "nsString.h"
#include "nsPIDOMWindow.h"
#include "nsPIWindowRoot.h"
#include "nsXULCommandDispatcher.h"
@@ -2020,16 +2021,19 @@ XULDocument::PrepareToWalk()
if (NS_FAILED(rv)) return rv;
rv = AppendChildTo(root, false);
if (NS_FAILED(rv)) return rv;
// Block onload until we've finished building the complete
// document content model.
BlockOnload();
+
+ nsContentUtils::AddScriptRunner(
+ new nsDocElementCreatedNotificationRunner(this));
}
// There'd better not be anything on the context stack at this
// point! This is the basis case for our "induction" in
// ResumeWalk(), below, which'll assume that there's always a
// content element on the context stack if either 1) we're in the
// "master" document, or 2) we're in an overlay, and we've got
// more than one prototype element (the single, root "overlay"