Bug 1467970 - Unsupport cross docGroup adoption r=smaug
authorSean Feng <sefeng@mozilla.com>
Sat, 14 Sep 2019 00:09:44 +0000
changeset 495151 c8a2c27a1128c4800c1fefc9fafd06204e48ec2a
parent 495150 deba67add7d2d31d593cb0438bfff33be9760226
child 495152 e10a3c2499439232e95ecff9b90c3689c48d7ae2
push id36623
push usershindli@mozilla.com
push dateFri, 27 Sep 2019 04:28:18 +0000
treeherdermozilla-central@dcfdecc355c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1467970
milestone71.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 1467970 - Unsupport cross docGroup adoption r=smaug Differential Revision: https://phabricator.services.mozilla.com/D43135
accessible/tests/mochitest/aom/test_general.html
accessible/tests/mochitest/treeupdate/test_ariaowns.html
browser/base/content/browser.js
devtools/server/actors/inspector/walker.js
dom/base/DOMParser.cpp
dom/base/DOMParser.h
dom/base/nsNodeUtils.h
dom/bindings/test/test_dom_xrays.html
dom/tests/mochitest/general/test_focusrings.xul
dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
dom/webidl/DOMParser.webidl
js/xpconnect/tests/chrome/chrome.ini
js/xpconnect/tests/chrome/test_bug601803.xul
toolkit/content/customElements.js
--- a/accessible/tests/mochitest/aom/test_general.html
+++ b/accessible/tests/mochitest/aom/test_general.html
@@ -187,17 +187,17 @@
     }
 
     // Check if an AccessibleNode is properly cached.
     let node = ifrDoc.createElement("div");
     anode = node.accessibleNode;
     is(anode, node.accessibleNode, "an AccessibleNode is properly cached");
 
     // Adopting node to another document doesn't change .accessibleNode
-    let anotherDoc = document.implementation.createDocument("", "", null);
+    let anotherDoc = ifrDoc.implementation.createDocument("", "", null);
     let adopted_node = anotherDoc.adoptNode(node);
     is(anode, adopted_node.accessibleNode, "adopting node to another document doesn't change node.accessibleNode");
 
     const relationProps = ["activeDescendant", "details", "errorMessage"];
 
     for (const relationProp of relationProps) {
       testRelationProp(anode, node, relationProp);
     }
--- a/accessible/tests/mochitest/treeupdate/test_ariaowns.html
+++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html
@@ -591,17 +591,17 @@
         new invokerChecker(EVENT_REORDER, () => {
           return getNode("t9_container").contentDocument;
         }),
       ];
 
       this.invoke = () => {
         // trigger a tree update.
         let doc = getNode("t9_container").contentDocument;
-        doc.body.appendChild(document.createElement("p"));
+        doc.body.appendChild(doc.createElement("p"));
       };
 
       this.finalCheck = () => {
         var tree =
           { INTERNAL_FRAME: [
             { DOCUMENT: [
               { PARAGRAPH: [] },
               { SECTION: [] },
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -9410,20 +9410,20 @@ TabModalPromptBox.prototype = {
         "focus-tab-by-prompt",
         Services.perms.ALLOW_ACTION
       );
     }
     onCloseCallback.apply(this, args);
   },
 
   appendPrompt(args, onCloseCallback) {
-    let newPrompt = new TabModalPrompt(window);
+    let browser = this.browser;
+    let newPrompt = new TabModalPrompt(browser.ownerGlobal);
     this.prompts.set(newPrompt.element, newPrompt);
 
-    let browser = this.browser;
     browser.parentNode.insertBefore(
       newPrompt.element,
       browser.nextElementSibling
     );
     browser.setAttribute("tabmodalPromptShowing", true);
 
     let prompts = this.listPrompts();
     if (prompts.length > 1) {
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -1640,18 +1640,30 @@ var WalkerActor = protocol.ActorClassWit
    * @param {NodeActor} node The node.
    * @param {string} value The piece of HTML content.
    */
   setOuterHTML: function(node, value) {
     if (isNodeDead(node)) {
       return;
     }
 
-    const parsedDOM = new DOMParser().parseFromString(value, "text/html");
     const rawNode = node.rawNode;
+    const doc = nodeDocument(rawNode);
+    const win = doc.defaultView;
+    let parser;
+    if (!win) {
+      throw new Error("The window object shouldn't be null");
+    } else {
+      // We create DOMParser under window object because we want a content
+      // DOMParser, which means all the DOM objects created by this DOMParser
+      // will be in the same DocGroup as rawNode.parentNode. Then the newly
+      // created nodes can be adopted into rawNode.parentNode.
+      parser = new win.DOMParser();
+    }
+    const parsedDOM = parser.parseFromString(value, "text/html");
     const parentNode = rawNode.parentNode;
 
     // Special case for head and body.  Setting document.body.outerHTML
     // creates an extra <head> tag, and document.head.outerHTML creates
     // an extra <body>.  So instead we will call replaceChild with the
     // parsed DOM, assuming that they aren't trying to set both tags at once.
     if (rawNode.tagName === "BODY") {
       if (parsedDOM.head.innerHTML === "") {
--- a/dom/base/DOMParser.cpp
+++ b/dom/base/DOMParser.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/DOMParser.h"
 
 #include "nsNetUtil.h"
 #include "nsDOMString.h"
 #include "MainThreadUtils.h"
+#include "SystemPrincipal.h"
 #include "nsIStreamListener.h"
 #include "nsStringStream.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCRT.h"
 #include "nsStreamUtils.h"
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
@@ -98,16 +99,32 @@ already_AddRefed<Document> DOMParser::Pa
     aRv.Throw(rv);
     return nullptr;
   }
 
   return ParseFromStream(stream, NS_LITERAL_STRING("UTF-8"), utf8str.Length(),
                          aType, aRv);
 }
 
+already_AddRefed<Document> DOMParser::ParseFromSafeString(const nsAString& aStr,
+                                                          SupportedType aType,
+                                                          ErrorResult& aRv) {
+  // Since we disable cross docGroup node adoption, it is safe to create
+  // new document with the system principal, then the new document will be
+  // placed in the same docGroup as the chrome document.
+  nsCOMPtr<nsIPrincipal> docPrincipal = mPrincipal;
+  if (!nsContentUtils::IsSystemPrincipal(mPrincipal)) {
+    mPrincipal = SystemPrincipal::Create();
+  }
+
+  RefPtr<Document> ret = ParseFromString(aStr, aType, aRv);
+  mPrincipal = docPrincipal;
+  return ret.forget();
+}
+
 already_AddRefed<Document> DOMParser::ParseFromBuffer(const Uint8Array& aBuf,
                                                       SupportedType aType,
                                                       ErrorResult& aRv) {
   aBuf.ComputeLengthAndData();
   return ParseFromBuffer(MakeSpan(aBuf.Data(), aBuf.Length()), aType, aRv);
 }
 
 already_AddRefed<Document> DOMParser::ParseFromBuffer(Span<const uint8_t> aBuf,
--- a/dom/base/DOMParser.h
+++ b/dom/base/DOMParser.h
@@ -32,16 +32,20 @@ class DOMParser final : public nsISuppor
   // WebIDL API
   static already_AddRefed<DOMParser> Constructor(const GlobalObject& aOwner,
                                                  mozilla::ErrorResult& rv);
 
   already_AddRefed<Document> ParseFromString(const nsAString& aStr,
                                              SupportedType aType,
                                              ErrorResult& aRv);
 
+  // ChromeOnly API
+  already_AddRefed<Document> ParseFromSafeString(const nsAString& aStr,
+                                                 SupportedType aType,
+                                                 ErrorResult& aRv);
   // Sequence converts to Span, so we can use this overload for both
   // the Sequence case and our internal uses.
   already_AddRefed<Document> ParseFromBuffer(Span<const uint8_t> aBuf,
                                              SupportedType aType,
                                              ErrorResult& aRv);
 
   already_AddRefed<Document> ParseFromBuffer(const Uint8Array& aBuf,
                                              SupportedType aType,
--- a/dom/base/nsNodeUtils.h
+++ b/dom/base/nsNodeUtils.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsNodeUtils_h___
 #define nsNodeUtils_h___
 
 #include "mozilla/Maybe.h"
 #include "nsIContent.h"  // for use in inline function (ParentChainChanged)
 #include "nsIMutationObserver.h"  // for use in inline function (ParentChainChanged)
+#include "mozilla/dom/Document.h"
 #include "js/TypeDecls.h"
 #include "nsCOMArray.h"
 
 struct CharacterDataChangeInfo;
 template <class E>
 class nsCOMArray;
 class nsCycleCollectionTraversalCallback;
 namespace mozilla {
@@ -199,16 +200,27 @@ class nsNodeUtils {
    * @param aNodesWithProperties All nodes (from amongst aNode and its
    *                             descendants) with properties.
    * @param aError The error, if any.
    */
   static void Adopt(nsINode* aNode, nsNodeInfoManager* aNewNodeInfoManager,
                     JS::Handle<JSObject*> aReparentScope,
                     nsCOMArray<nsINode>& aNodesWithProperties,
                     mozilla::ErrorResult& aError) {
+    if (aNode && aNewNodeInfoManager) {
+      mozilla::dom::Document* newDoc = aNewNodeInfoManager->GetDocument();
+      mozilla::dom::Document* oldDoc = aNode->OwnerDoc();
+      if (newDoc && oldDoc &&
+          (oldDoc->GetDocGroup() != newDoc->GetDocGroup())) {
+        MOZ_ASSERT(false, "Cross docGroup adoption is not allowed");
+        aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+        return;
+      }
+    }
+
     // Just need to store the return value of CloneAndAdopt in a
     // temporary nsCOMPtr to make sure we release it.
     nsCOMPtr<nsINode> node =
         CloneAndAdopt(aNode, false, true, aNewNodeInfoManager, aReparentScope,
                       &aNodesWithProperties, nullptr, aError);
 
     nsMutationGuard::DidMutate();
   }
--- a/dom/bindings/test/test_dom_xrays.html
+++ b/dom/bindings/test/test_dom_xrays.html
@@ -289,17 +289,17 @@ function test() {
   isnot(typeof elem.ELEMENT_NODE, "undefined",
         "Should see constant property on prototype objects");
   is(Object.getOwnPropertyDescriptor(elem, "ELEMENT_NODE"), undefined,
      "Shouldn't see constant property on instances");
   isnot(typeof Object.getOwnPropertyDescriptor(win.Node.prototype, "ELEMENT_NODE"), "undefined",
         "Should see constant property on prototype objects");
 
   // Adopting nodes should not lose expandos.
-  elem = doc.createElement("span");
+  elem = document.createElement("span");
   elem.expando = 5;
   is(elem.expando, 5, "We just set this property");
   document.adoptNode(elem);
   is(elem.wrappedJSObject, undefined, "Shouldn't be an Xray anymore");
   is(elem.expando, 5, "Expando should not get lost");
 
   // Instanceof tests
   var img = doc.createElement("img");
--- a/dom/tests/mochitest/general/test_focusrings.xul
+++ b/dom/tests/mochitest/general/test_focusrings.xul
@@ -116,17 +116,17 @@ var htmlElements = [
 var htmlElementsMacPrefSet = [
   "<button id='elem' class='canfocus'>Button</button>",
   "<input id='elem' class='canfocus'>",
   "<input id='elem' type='button' class='canfocus'>",
   "<input id='elem' type='checkbox' class='canfocus'>",
 ];
 
 function createElement(str) {
-  let doc = new DOMParser().parseFromString(`<html><body>${str}</body></html>`, "text/html");
+  let doc = new DOMParser().parseFromSafeString(`<html><body>${str}</body></html>`, "text/html");
   return doc.body.firstChild;
 }
 
 function testHTMLElements(list, isMac, expectedNoRingsOnWin)
 {
   var childwin = frames[0];
   var childdoc = childwin.document;
   var container = childdoc.getElementById("container");
--- a/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
+++ b/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
@@ -12,17 +12,17 @@
   <script type="application/javascript">
   <![CDATA[
     SimpleTest.waitForExplicitFinish();
 
     var gXULDOMParser = new DOMParser();
     gXULDOMParser.forceEnableXULXBL();
 
     function parseXULToFragment(str) {
-      let doc = gXULDOMParser.parseFromString(`
+      let doc = gXULDOMParser.parseFromSafeString(`
         <box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">${str}</box>`,
         "application/xml");
       // We use a range here so that we don't access the inner DOM elements from
       // JavaScript before they are imported and inserted into a document.
       let range = doc.createRange();
       range.selectNodeContents(doc.firstChild);
       return range.extractContents();
     }
--- a/dom/webidl/DOMParser.webidl
+++ b/dom/webidl/DOMParser.webidl
@@ -20,16 +20,19 @@ enum SupportedType {
 
 interface DOMParser {
   [Throws]
   constructor();
 
   [NewObject, Throws]
   Document parseFromString(DOMString str, SupportedType type);
 
+  [NewObject, ChromeOnly, Throws]
+  Document parseFromSafeString(DOMString str, SupportedType type);
+
   // Mozilla-specific stuff
   [NewObject, Throws, ChromeOnly]
   Document parseFromBuffer(sequence<octet> buf, SupportedType type);
   [NewObject, Throws, ChromeOnly]
   Document parseFromBuffer(Uint8Array buf, SupportedType type);
   [NewObject, Throws, ChromeOnly]
   Document parseFromStream(InputStream stream, DOMString? charset,
                            long contentLength, SupportedType type);
--- a/js/xpconnect/tests/chrome/chrome.ini
+++ b/js/xpconnect/tests/chrome/chrome.ini
@@ -39,17 +39,16 @@ support-files =
 [test_bug448587.xul]
 [test_bug484459.xul]
 skip-if = os == 'win' || os == 'mac' || (os == 'linux' && !debug) # bug 1131110, 1255284
 [test_bug500931.xul]
 [test_bug503926.xul]
 [test_bug533596.xul]
 [test_bug571849.xul]
 [test_bug596580.xul]
-[test_bug601803.xul]
 [test_bug610390.xul]
 [test_bug614757.xul]
 [test_bug616992.xul]
 [test_bug618176.xul]
 [test_bug654370.xul]
 [test_bug658560.xul]
 [test_bug658909.xul]
 [test_bug664689.xul]
deleted file mode 100644
--- a/js/xpconnect/tests/chrome/test_bug601803.xul
+++ /dev/null
@@ -1,37 +0,0 @@
-<?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=500931
--->
-<window title="Mozilla Bug 601803"
-  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <script 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=601803"
-     target="_blank">Mozilla Bug 601803</a>
-
-  <!-- test code goes here -->
-  <script type="application/javascript"><![CDATA[
-
-  /** Test for Bug 601803 **/
-
-  function go() {
-    var ifr = document.getElementById('ifr');
-    var elem = document.createElementNS("http://www.w3.org/1999/xhtml","html:div");
-    elem.appendChild(document.createTextNode("hello, world"));
-    elem.expando = 42;
-    ifr.contentDocument.body.appendChild(elem);
-    is(elem.wrappedJSObject.expando, 42, "expando is preserved");
-    SimpleTest.finish();
-  }
-
-  SimpleTest.waitForExplicitFinish();
-
-  ]]></script>
-  <iframe type="content" src="about:blank" onload="go()" id="ifr" />
-  </body>
-</window>
--- a/toolkit/content/customElements.js
+++ b/toolkit/content/customElements.js
@@ -481,17 +481,17 @@
        * @param {string[]} [entities]
        *        An array of DTD URLs containing entity definitions.
        *
        * @return {DocumentFragment} `DocumentFragment` instance containing
        *         the corresponding element tree, including element nodes
        *         but excluding any text node.
        */
       static parseXULToFragment(str, entities = []) {
-        let doc = gXULDOMParser.parseFromString(
+        let doc = gXULDOMParser.parseFromSafeString(
           `
       ${
         entities.length
           ? `<!DOCTYPE bindings [
         ${entities.reduce((preamble, url, index) => {
           return (
             preamble +
             `<!ENTITY % _dtd-${index} SYSTEM "${url}">