Bug 1590526 - Temporarily allow node adoption across different docGroups for the content/content case r=smaug,zombie

As web extensions rely on this node adoption between content to content
documents, we want to continue allowing this capability to work for now.

Differential Revision: https://phabricator.services.mozilla.com/D50348

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Sean Feng 2019-10-24 20:56:43 +00:00
parent 78e89d3ba7
commit a269703b7c
6 changed files with 155 additions and 7 deletions

View File

@ -295,3 +295,5 @@ skip-if = os == 'mac' # Fails when windows are randomly opened in fullscreen mod
skip-if = (verify && (os == 'mac'))
tags = fullscreen
[browser_ext_contentscript_animate.js]
[browser_ext_contentscript_cross_docGroup_adoption.js]
[browser_ext_contentscript_cross_docGroup_adoption_xhr.js]

View File

@ -0,0 +1,58 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(async function test_cross_docGroup_adoption() {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"http://example.com/"
);
let extension = ExtensionTestUtils.loadExtension({
manifest: {
content_scripts: [
{
matches: ["http://example.com/"],
js: ["content-script.js"],
},
],
},
files: {
"current.html": "<html>data</html>",
"content-script.js": function() {
let iframe = document.createElement("iframe");
iframe.src = browser.extension.getURL("current.html");
document.body.appendChild(iframe);
iframe.addEventListener(
"load",
() => {
let parser = new DOMParser();
let bold = parser.parseFromString(
"<b>NodeAdopted</b>",
"text/html"
);
let doc = iframe.contentDocument;
let node = document.adoptNode(bold.documentElement);
doc.replaceChild(node, doc.documentElement);
const expected =
"<html><head></head><body><b>NodeAdopted</b></body></html>";
browser.test.assertEq(expected, doc.documentElement.outerHTML);
browser.test.notifyPass("nodeAdopted");
},
{ once: true }
);
},
},
});
await extension.startup();
await extension.awaitFinish("nodeAdopted");
await extension.unload();
BrowserTestUtils.removeTab(tab);
});

View File

@ -0,0 +1,51 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(async function test_cross_docGroup_adoption() {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"http://example.com/"
);
let extension = ExtensionTestUtils.loadExtension({
manifest: {
content_scripts: [
{
matches: ["http://example.com/"],
js: ["content-script.js"],
},
],
},
files: {
"blank.html": "<html>data</html>",
"content-script.js": function() {
let xhr = new XMLHttpRequest();
xhr.responseType = "document";
xhr.open("GET", browser.extension.getURL("blank.html"));
xhr.onload = function() {
let doc = xhr.response;
try {
let node = doc.body.cloneNode(true);
document.body.appendChild(node);
browser.test.notifyPass("nodeAdopted");
} catch (SecurityError) {
browser.test.assertTrue(
false,
"The above node adoption should not fail"
);
}
};
xhr.send();
},
},
});
await extension.startup();
await extension.awaitFinish("nodeAdopted");
await extension.unload();
BrowserTestUtils.removeTab(tab);
});

View File

@ -13,6 +13,7 @@
#include "mozilla/dom/Document.h"
#include "js/TypeDecls.h"
#include "nsCOMArray.h"
#include "nsContentUtils.h"
struct CharacterDataChangeInfo;
template <class E>
@ -206,13 +207,20 @@ class nsNodeUtils {
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;
mozilla::dom::Document* afterAdoptDoc =
aNewNodeInfoManager->GetDocument();
mozilla::dom::Document* beforeAdoptDoc = aNode->OwnerDoc();
if (afterAdoptDoc && beforeAdoptDoc &&
(afterAdoptDoc->GetDocGroup() != beforeAdoptDoc->GetDocGroup())) {
// This is a temporary solution for Bug 1590526 to only limit
// the restriction to chrome level documents because web extensions
// rely on content to content node adoption.
if (nsContentUtils::IsChromeDoc(afterAdoptDoc) ||
nsContentUtils::IsChromeDoc(beforeAdoptDoc)) {
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
}
}

View File

@ -6,6 +6,7 @@ skip-if = os == 'android'
support-files = test_offsets.css test_offsets.js
skip-if = (os == "mac" && debug) #leaks Bug 1571583
[test_spacetopagedown.html]
[test_nodeAdoption_chrome_boundary.xul]
[test_focusrings.xul]
support-files = file_focusrings.html
skip-if = toolkit == 'android' #TIMED_OUT

View File

@ -0,0 +1,28 @@
<?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"?>
<window title="Cross chrome and content node adoption test"
onload="setTimeout(runTest, 0);"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<browser xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="content" type="content" src="about:blank"/>
<script>
SimpleTest.waitForExplicitFinish();
function runTest()
{
let browserElement = document.getElementById("content");
try {
document.adoptNode(browserElement.contentDocument.documentElement);
SimpleTest.ok(false, "Cross chrome and content node adoption should fail");
} catch (SecurityError) {
SimpleTest.ok(true, "Cross chrome and content node adoption fails as expected");
}
SimpleTest.finish();
}
</script>
</window>