Bug 1455680 - Notify document-element-inserted in XUL documents;r=bz draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Fri, 20 Apr 2018 13:55:51 -0700
changeset 785946 28fe9b6a4bcbb6ecf8966a0c059d867bf66285bf
parent 783746 5ded36cb383d3ccafd9b6c231c5120dcdae196a2
push id107362
push userbgrinstead@mozilla.com
push dateFri, 20 Apr 2018 20:56:17 +0000
reviewersbz
bugs1455680
milestone61.0a1
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
dom/base/nsContentSink.cpp
dom/base/test/chrome/chrome.ini
dom/base/test/chrome/file_document-element-inserted-inner.xul
dom/base/test/chrome/file_document-element-inserted.xul
dom/base/test/chrome/test_document-element-inserted.xul
dom/xml/nsXMLContentSink.cpp
dom/xslt/xslt/txMozillaXMLOutput.cpp
dom/xul/XULDocument.cpp
--- 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"