Merge inbound to mozilla-central r=merge a=merge

This commit is contained in:
shindli 2017-12-08 00:12:14 +02:00
commit 0bed6b5d6d
92 changed files with 1657 additions and 1010 deletions

View File

@ -260,45 +260,46 @@ function runTest(testCase, cb) {
function testCopy(copyVal, targetValue, cb) {
info("Expecting copy of: " + targetValue);
waitForClipboard(targetValue, function() {
gURLBar.focus();
if (copyVal) {
let offsets = [];
while (true) {
let startBracket = copyVal.indexOf("<");
let endBracket = copyVal.indexOf(">");
if (startBracket == -1 && endBracket == -1) {
break;
}
if (startBracket > endBracket || startBracket == -1) {
offsets = [];
break;
}
offsets.push([startBracket, endBracket - 1]);
copyVal = copyVal.replace("<", "").replace(">", "");
}
if (offsets.length == 0 ||
copyVal != gURLBar.textValue) {
ok(false, "invalid copyVal: " + copyVal);
}
gURLBar.selectionStart = offsets[0][0];
gURLBar.selectionEnd = offsets[0][1];
if (offsets.length > 1) {
let sel = gURLBar.editor.selection;
let r0 = sel.getRangeAt(0);
let node0 = r0.startContainer;
sel.removeAllRanges();
offsets.map(function(startEnd) {
let range = r0.cloneRange();
range.setStart(node0, startEnd[0]);
range.setEnd(node0, startEnd[1]);
sel.addRange(range);
});
}
} else {
gURLBar.select();
}
gURLBar.focus();
if (copyVal) {
let offsets = [];
while (true) {
let startBracket = copyVal.indexOf("<");
let endBracket = copyVal.indexOf(">");
if (startBracket == -1 && endBracket == -1) {
break;
}
if (startBracket > endBracket || startBracket == -1) {
offsets = [];
break;
}
offsets.push([startBracket, endBracket - 1]);
copyVal = copyVal.replace("<", "").replace(">", "");
}
if (offsets.length == 0 ||
copyVal != gURLBar.textValue) {
ok(false, "invalid copyVal: " + copyVal);
}
gURLBar.selectionStart = offsets[0][0];
gURLBar.selectionEnd = offsets[0][1];
if (offsets.length > 1) {
let sel = gURLBar.editor.selection;
let r0 = sel.getRangeAt(0);
let node0 = r0.startContainer;
sel.removeAllRanges();
offsets.map(function(startEnd) {
let range = r0.cloneRange();
range.setStart(node0, startEnd[0]);
range.setEnd(node0, startEnd[1]);
sel.addRange(range);
});
}
} else {
gURLBar.select();
}
waitForClipboard(targetValue, function() {
goDoCommand("cmd_copy");
}, cb, cb);
}

View File

@ -495,10 +495,13 @@ this.PanelMultiView = class {
}
viewNode.panelMultiView = this.node;
this._setHeader(viewNode, viewNode.getAttribute("title") ||
(aAnchor && aAnchor.getAttribute("label")));
let reverse = !!aPreviousView;
if (!reverse) {
this._setHeader(viewNode, viewNode.getAttribute("title") ||
(aAnchor && aAnchor.getAttribute("label")));
}
let previousViewNode = aPreviousView || this._currentSubView;
// If the panelview to show is the same as the previous one, the 'ViewShowing'
// event has already been dispatched. Don't do it twice.

View File

@ -34,30 +34,14 @@ add_task(async function() {
ok(!copyButton.hasAttribute("disabled"), "Copy button is enabled when selecting");
copyButton.click();
await SimpleTest.promiseClipboardChange(testText, () => {
copyButton.click();
});
is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");
// check that the text was added to the clipboard
let clipboard = Services.clipboard;
let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
globalClipboard = clipboard.kGlobalClipboard;
transferable.init(null);
transferable.addDataFlavor("text/unicode");
clipboard.getData(transferable, globalClipboard);
let str = {}, strLength = {};
transferable.getTransferData("text/unicode", str, strLength);
let clipboardValue = "";
if (str.value) {
str.value.QueryInterface(Ci.nsISupportsString);
clipboardValue = str.value.data;
}
is(clipboardValue, testText, "Data was copied to the clipboard.");
});
});
registerCleanupFunction(function cleanup() {
CustomizableUI.reset();
Services.clipboard.emptyClipboard(globalClipboard);
});

View File

@ -32,30 +32,13 @@ add_task(async function() {
info("Menu panel was opened");
ok(!cutButton.hasAttribute("disabled"), "Cut button is enabled when selecting");
cutButton.click();
await SimpleTest.promiseClipboardChange(testText, () => {
cutButton.click();
});
is(gURLBar.value, "", "Selected text is removed from source when clicking on cut");
// check that the text was added to the clipboard
let clipboard = Services.clipboard;
let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
globalClipboard = clipboard.kGlobalClipboard;
transferable.init(null);
transferable.addDataFlavor("text/unicode");
clipboard.getData(transferable, globalClipboard);
let str = {}, strLength = {};
transferable.getTransferData("text/unicode", str, strLength);
let clipboardValue = "";
if (str.value) {
str.value.QueryInterface(Ci.nsISupportsString);
clipboardValue = str.value.data;
}
is(clipboardValue, testText, "Data was copied to the clipboard.");
});
});
registerCleanupFunction(function cleanup() {
CustomizableUI.reset();
Services.clipboard.emptyClipboard(globalClipboard);
});

View File

@ -32,7 +32,12 @@ add_task(function* () {
target: markupTagLine,
});
info(`Testing ${menuId} for ${clipboardData}`);
clipboard.copyString(clipboardData);
yield SimpleTest.promiseClipboardChange(clipboardData,
() => {
clipboard.copyString(clipboardData);
}
);
let onMutation = inspector.once("markupmutation");
allMenuItems.find(item => item.id === menuId).click();

View File

@ -43,7 +43,12 @@ add_task(function* () {
function* testPasteOuterHTMLMenu() {
info("Testing that 'Paste Outer HTML' menu item works.");
clipboard.copyString("this was pasted (outerHTML)");
yield SimpleTest.promiseClipboardChange("this was pasted (outerHTML)",
() => {
clipboard.copyString("this was pasted (outerHTML)");
});
let outerHTMLSelector = "#paste-area h1";
let nodeFront = yield getNodeFront(outerHTMLSelector, inspector);
@ -68,7 +73,11 @@ add_task(function* () {
function* testPasteInnerHTMLMenu() {
info("Testing that 'Paste Inner HTML' menu item works.");
clipboard.copyString("this was pasted (innerHTML)");
yield SimpleTest.promiseClipboardChange("this was pasted (innerHTML)",
() => {
clipboard.copyString("this was pasted (innerHTML)");
});
let innerHTMLSelector = "#paste-area .inner";
let getInnerHTML = () => testActor.getProperty(innerHTMLSelector,
"innerHTML");
@ -107,7 +116,11 @@ add_task(function* () {
target: markupTagLine,
});
info(`Testing ${menuId} for ${clipboardData}`);
clipboard.copyString(clipboardData);
yield SimpleTest.promiseClipboardChange(clipboardData,
() => {
clipboard.copyString(clipboardData);
});
let onMutation = inspector.once("markupmutation");
allMenuItems.find(item => item.id === menuId).click();

View File

@ -4,8 +4,11 @@ subsuite = devtools
support-files =
autocomplete.html
browser_styleeditor_cmd_edit.html
bug_1405342_serviceworker_iframes.html
four.html
head.js
iframe_with_service_worker.html
iframe_service_worker.js
import.css
import.html
import2.css
@ -70,6 +73,7 @@ support-files =
[browser_styleeditor_bug_740541_iframes.js]
[browser_styleeditor_bug_851132_middle_click.js]
[browser_styleeditor_bug_870339.js]
[browser_styleeditor_bug_1405342_serviceworker_iframes.js]
[browser_styleeditor_cmd_edit.js]
[browser_styleeditor_enabled.js]
[browser_styleeditor_fetch-from-cache.js]

View File

@ -0,0 +1,23 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that sheets inside cross origin iframes, served from a service worker
// are correctly fetched via the service worker in the stylesheet editor.
add_task(async function () {
const TEST_URL = "https://test1.example.com/browser/devtools/client/styleeditor/test/bug_1405342_serviceworker_iframes.html";
let { ui } = await openStyleEditorForURL(TEST_URL);
if (ui.editors.length != 1) {
info("Stylesheet isn't available immediately, waiting for it");
await ui.once("editor-added");
}
is(ui.editors.length, 1, "Got the iframe stylesheet");
await ui.selectStyleSheet(ui.editors[0].styleSheet);
let editor = await ui.editors[0].getSourceEditor();
let text = editor.sourceEditor.getText();
is(text, "* { color: green; }",
"stylesheet content is the one served by the service worker");
});

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Bug 1405342</title>
</head>
<body>
<iframe src="https://test2.example.com/browser/devtools/client/styleeditor/test/iframe_with_service_worker.html"><iframe>
</body>
</html>

View File

@ -0,0 +1,12 @@
"use strict";
self.onfetch = function (event) {
if (event.request.url.includes("sheet.css")) {
return event.respondWith(new Response("* { color: green; }"));
}
return null;
};
self.onactivate = function (event) {
event.waitUntil(self.clients.claim());
};

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<meta charset="utf-8">
Iframe loading a stylesheet via a service worker
<script>
"use strict";
function waitForActive(swr) {
let sw = swr.installing || swr.waiting || swr.active;
return new Promise(resolve => {
if (sw.state === "activated") {
resolve(swr);
return;
}
sw.addEventListener("statechange", function onStateChange(evt) {
if (sw.state === "activated") {
sw.removeEventListener("statechange", onStateChange);
resolve(swr);
}
});
});
}
navigator.serviceWorker.register("iframe_service_worker.js", {scope: "."})
.then(registration => waitForActive(registration))
.then(() => {
let link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("type", "text/css");
link.setAttribute("href", "sheet.css");
document.documentElement.appendChild(link);
});
</script>

View File

@ -10,7 +10,6 @@
add_task(function* () {
const TEST_URI = "data:text/html;charset=utf8,<p>hello world, bug 916997";
let clipboardValue = "";
yield loadTab(TEST_URI);
let hud = yield openConsole();
@ -47,26 +46,21 @@ add_task(function* () {
let selection = hud.iframeWindow.getSelection() + "";
info("selection '" + selection + "'");
waitForClipboard((str) => {
clipboardValue = str;
return str.indexOf("bug916997a") > -1 && str.indexOf("bug916997b") > -1;
},
let clipboardValue = yield SimpleTest.promiseClipboardChange(str => {
return str.indexOf("bug916997a") > -1 && str.indexOf("bug916997b") > -1;
},
() => {
goDoCommand("cmd_copy");
},
() => {
info("clipboard value '" + clipboardValue + "'");
let lines = clipboardValue.trim().split("\n");
is(hud.outputNode.children.length, 2, "number of messages");
is(lines.length, hud.outputNode.children.length, "number of lines");
isnot(lines[0].indexOf("bug916997a"), -1,
"first message text includes 'bug916997a'");
isnot(lines[1].indexOf("bug916997b"), -1,
"second message text includes 'bug916997b'");
is(lines[0].indexOf("bug916997b"), -1,
"first message text does not include 'bug916997b'");
},
() => {
info("last clipboard value: '" + clipboardValue + "'");
});
}
);
let lines = clipboardValue.trim().split("\n");
is(hud.outputNode.children.length, 2, "number of messages");
is(lines.length, hud.outputNode.children.length, "number of lines");
isnot(lines[0].indexOf("bug916997a"), -1,
"first message text includes 'bug916997a'");
isnot(lines[1].indexOf("bug916997b"), -1,
"second message text includes 'bug916997b'");
is(lines[0].indexOf("bug916997b"), -1,
"first message text does not include 'bug916997b'");
});

View File

@ -140,7 +140,7 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
* Window of target
*/
get window() {
return this._window || this.parentActor.window;
return this.parentActor.window;
},
/**
@ -150,6 +150,14 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
return this.window.document;
},
/**
* StyleSheet's window.
*/
get ownerWindow() {
// eslint-disable-next-line mozilla/use-ownerGlobal
return this.ownerDocument.defaultView;
},
get ownerNode() {
return this.rawSheet.ownerNode;
},
@ -202,18 +210,31 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
}
},
initialize: function (styleSheet, parentActor, window) {
initialize: function (styleSheet, parentActor) {
protocol.Actor.prototype.initialize.call(this, null);
this.rawSheet = styleSheet;
this.parentActor = parentActor;
this.conn = this.parentActor.conn;
this._window = window;
// text and index are unknown until source load
this.text = null;
this._styleSheetIndex = -1;
// When the style is imported, `styleSheet.ownerNode` is null,
// so retrieve the topmost parent style sheet which has an ownerNode
let parentStyleSheet = styleSheet;
while (parentStyleSheet.parentStyleSheet) {
parentStyleSheet = parentStyleSheet.parentStyleSheet;
}
// When the style is injected via nsIDOMWindowUtils.loadSheet, even
// the parent style sheet has no owner, so default back to tab actor
// document
if (parentStyleSheet.ownerNode) {
this.ownerDocument = parentStyleSheet.ownerNode.ownerDocument;
} else {
this.ownerDocument = parentActor.window;
}
},
/**
@ -417,8 +438,8 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
let excludedProtocolsRe = /^(chrome|file|resource|moz-extension):\/\//;
if (!excludedProtocolsRe.test(this.href)) {
// Stylesheets using other protocols should use the content principal.
options.window = this.window;
options.principal = this.document.nodePrincipal;
options.window = this.ownerWindow;
options.principal = this.ownerDocument.nodePrincipal;
}
let result;

View File

@ -589,7 +589,18 @@ function mainThreadFetch(urlIn, aOptions = { loadFromCache: true,
// the input unmodified. Essentially we try to decode the data as UTF-8
// and if that fails, we use the locale specific default encoding. This is
// the best we can do if the source does not provide charset info.
let charset = bomCharset || channel.contentCharset || aOptions.charset || "UTF-8";
let charset = bomCharset;
if (!charset) {
try {
charset = channel.contentCharset;
} catch (e) {
// Accessing `contentCharset` on content served by a service worker in
// non-e10s may throw.
}
}
if (!charset) {
charset = aOptions.charset || "UTF-8";
}
let unicodeSource = NetworkHelper.convertToUnicode(source, charset);
deferred.resolve({

View File

@ -29,7 +29,7 @@ function getLoadContext() {
.QueryInterface(Ci.nsILoadContext);
}
function testCopyPaste (isXHTML) {
async function testCopyPaste (isXHTML) {
var suppressUnicodeCheckIfHidden = !!isXHTML;
var suppressHTMLCheck = !!isXHTML;
@ -45,8 +45,9 @@ function testCopyPaste (isXHTML) {
var textarea = SpecialPowers.wrap(document.getElementById('input'));
function copySelectionToClipboard(suppressUnicodeCheck) {
documentViewer.copySelection();
async function copySelectionToClipboard(suppressUnicodeCheck) {
await SimpleTest.promiseClipboardChange(() => true,
() => { documentViewer.copySelection(); });
if (!suppressUnicodeCheck)
ok(clipboard.hasDataMatchingFlavors(["text/unicode"], 1,1), "check text/unicode");
if (!suppressHTMLCheck)
@ -54,16 +55,15 @@ function testCopyPaste (isXHTML) {
}
function clear(node, suppressUnicodeCheck) {
textarea.blur();
clipboard.emptyClipboard(1);
var sel = window.getSelection();
sel.removeAllRanges();
}
function copyToClipboard(node, suppressUnicodeCheck) {
async function copyToClipboard(node, suppressUnicodeCheck) {
clear();
var r = document.createRange();
r.selectNode(node);
window.getSelection().addRange(r);
copySelectionToClipboard(suppressUnicodeCheck);
await copySelectionToClipboard(suppressUnicodeCheck);
}
function addRange(startNode,startIndex,endNode,endIndex) {
var sel = window.getSelection();
@ -72,15 +72,15 @@ function testCopyPaste (isXHTML) {
r.setEnd(endNode,endIndex)
sel.addRange(r);
}
function copyRangeToClipboard(startNode,startIndex,endNode,endIndex,suppressUnicodeCheck) {
async function copyRangeToClipboard(startNode,startIndex,endNode,endIndex,suppressUnicodeCheck) {
clear();
addRange(startNode,startIndex,endNode,endIndex);
copySelectionToClipboard(suppressUnicodeCheck);
await copySelectionToClipboard(suppressUnicodeCheck);
}
function copyChildrenToClipboard(id) {
async function copyChildrenToClipboard(id) {
clear();
window.getSelection().selectAllChildren(document.getElementById(id));
copySelectionToClipboard();
await copySelectionToClipboard();
}
function getClipboardData(mime) {
var transferable = SpecialPowers.Cc['@mozilla.org/widget/transferable;1']
@ -132,15 +132,8 @@ function testCopyPaste (isXHTML) {
var value = document.getElementById(id).innerHTML;
is(value, expected, id + ".innerHTML");
}
function testEmptyChildren(id) {
copyChildrenToClipboard(id);
testSelectionToString("");
testClipboardValue("text/unicode", null);
testClipboardValue("text/html", null);
testPasteText("");
}
copyChildrenToClipboard("draggable");
await copyChildrenToClipboard("draggable");
testSelectionToString("This is a draggable bit of text.");
testClipboardValue("text/unicode",
"This is a draggable bit of text.");
@ -148,25 +141,25 @@ function testCopyPaste (isXHTML) {
"<div id=\"draggable\" title=\"title to have a long HTML line\">This is a <em>draggable</em> bit of text.</div>");
testPasteText("This is a draggable bit of text.");
copyChildrenToClipboard("alist");
await copyChildrenToClipboard("alist");
testSelectionToString(" bla\n\n foo\n bar\n\n");
testClipboardValue("text/unicode", " bla\n\n foo\n bar\n\n");
testHtmlClipboardValue("text/html", "<div id=\"alist\">\n bla\n <ul>\n <li>foo</li>\n \n <li>bar</li>\n </ul>\n </div>");
testPasteText(" bla\n\n foo\n bar\n\n");
copyChildrenToClipboard("blist");
await copyChildrenToClipboard("blist");
testSelectionToString(" mozilla\n\n foo\n bar\n\n");
testClipboardValue("text/unicode", " mozilla\n\n foo\n bar\n\n");
testHtmlClipboardValue("text/html", "<div id=\"blist\">\n mozilla\n <ol>\n <li>foo</li>\n \n <li>bar</li>\n </ol>\n </div>");
testPasteText(" mozilla\n\n foo\n bar\n\n");
copyChildrenToClipboard("clist");
await copyChildrenToClipboard("clist");
testSelectionToString(" mzla\n\n foo\n bazzinga!\n bar\n\n");
testClipboardValue("text/unicode", " mzla\n\n foo\n bazzinga!\n bar\n\n");
testHtmlClipboardValue("text/html", "<div id=\"clist\">\n mzla\n <ul>\n <li>foo<ul>\n <li>bazzinga!</li>\n </ul></li>\n \n <li>bar</li>\n </ul>\n </div>");
testPasteText(" mzla\n\n foo\n bazzinga!\n bar\n\n");
copyChildrenToClipboard("div4");
await copyChildrenToClipboard("div4");
testSelectionToString(" Tt t t ");
testClipboardValue("text/unicode", " Tt t t ");
if (isXHTML) {
@ -179,7 +172,7 @@ function testCopyPaste (isXHTML) {
}
testPasteText(" Tt t t ");
copyChildrenToClipboard("div5");
await copyChildrenToClipboard("div5");
testSelectionToString(" T ");
testClipboardValue("text/unicode", " T ");
if (isXHTML) {
@ -192,7 +185,7 @@ function testCopyPaste (isXHTML) {
}
testPasteText(" T ");
copyRangeToClipboard($("div6").childNodes[0],0, $("div6").childNodes[1],1,suppressUnicodeCheckIfHidden);
await copyRangeToClipboard($("div6").childNodes[0],0, $("div6").childNodes[1],1,suppressUnicodeCheckIfHidden);
testSelectionToString("");
// START Disabled due to bug 564688
if (false) {
@ -202,7 +195,7 @@ if (false) {
// END Disabled due to bug 564688
testInnerHTML("div6", "div6");
copyRangeToClipboard($("div7").childNodes[0],0, $("div7").childNodes[0],4,suppressUnicodeCheckIfHidden);
await copyRangeToClipboard($("div7").childNodes[0],0, $("div7").childNodes[0],4,suppressUnicodeCheckIfHidden);
testSelectionToString("");
// START Disabled due to bug 564688
if (false) {
@ -212,7 +205,7 @@ if (false) {
// END Disabled due to bug 564688
testInnerHTML("div7", "div7");
copyRangeToClipboard($("div8").childNodes[0],0, $("div8").childNodes[0],4,suppressUnicodeCheckIfHidden);
await copyRangeToClipboard($("div8").childNodes[0],0, $("div8").childNodes[0],4,suppressUnicodeCheckIfHidden);
testSelectionToString("");
// START Disabled due to bug 564688
if (false) {
@ -222,23 +215,23 @@ if (false) {
// END Disabled due to bug 564688
testInnerHTML("div8", "div8");
copyRangeToClipboard($("div9").childNodes[0],0, $("div9").childNodes[0],4,suppressUnicodeCheckIfHidden);
await copyRangeToClipboard($("div9").childNodes[0],0, $("div9").childNodes[0],4,suppressUnicodeCheckIfHidden);
testSelectionToString("div9");
testClipboardValue("text/unicode", "div9");
testHtmlClipboardValue("text/html", "div9");
testInnerHTML("div9", "div9");
copyToClipboard($("div10"), suppressUnicodeCheckIfHidden);
await copyToClipboard($("div10"), suppressUnicodeCheckIfHidden);
testSelectionToString("");
testInnerHTML("div10", "div10");
copyToClipboard($("div10").firstChild, suppressUnicodeCheckIfHidden);
await copyToClipboard($("div10").firstChild, suppressUnicodeCheckIfHidden);
testSelectionToString("");
copyRangeToClipboard($("div10").childNodes[0],0, $("div10").childNodes[0],1,suppressUnicodeCheckIfHidden);
await copyRangeToClipboard($("div10").childNodes[0],0, $("div10").childNodes[0],1,suppressUnicodeCheckIfHidden);
testSelectionToString("");
copyRangeToClipboard($("div10").childNodes[1],0, $("div10").childNodes[1],1,suppressUnicodeCheckIfHidden);
await copyRangeToClipboard($("div10").childNodes[1],0, $("div10").childNodes[1],1,suppressUnicodeCheckIfHidden);
testSelectionToString("");
if (!isXHTML) {
@ -257,7 +250,7 @@ if (false) {
r.setStart(ul, 1); // before the space inside the UL
r.setEnd(parent, 2); // after the UL
sel.addRange(r);
copySelectionToClipboard(true);
await copySelectionToClipboard(true);
testPasteHTML('contentEditable1', 'Copy1then Paste');
// with text end node
@ -274,7 +267,7 @@ if (false) {
r.setStart(parent.childNodes[1], 0); // the start of "Copy..."
r.setEnd(parent, 2);
sel.addRange(r);
copySelectionToClipboard(true);
await copySelectionToClipboard(true);
testPasteHTML('contentEditable2', 'Copy2then Paste');
// with text end node and non-empty start
@ -291,7 +284,7 @@ if (false) {
r.setStart(parent.childNodes[1], 0); // the start of "Copy..."
r.setEnd(parent, 2);
sel.addRange(r);
copySelectionToClipboard(true);
await copySelectionToClipboard(true);
testPasteHTML('contentEditable3', '<ul id="ul3"><li>\n<br></li></ul>Copy3then Paste');
// with elements of different depth
@ -308,7 +301,7 @@ if (false) {
r.setStart(div1.childNodes[1], 0); // the start of "after"
r.setEnd(parent, 1);
sel.addRange(r);
copySelectionToClipboard(true);
await copySelectionToClipboard(true);
testPasteHTML('contentEditable4', '<div id="div1s"><div id="div1se1">before</div></div><div id="div1s">after</div>');
// with elements of different depth, and a text node at the end
@ -325,7 +318,7 @@ if (false) {
r.setStart(div1.childNodes[1], 0); // the start of "after"
r.setEnd(parent, 1);
sel.addRange(r);
copySelectionToClipboard(true);
await copySelectionToClipboard(true);
testPasteHTML('contentEditable5', '<div id="div2s"><div id="div2se1">before</div></div><div id="div2s">after</div>');
// crash test for bug 1127835
@ -347,7 +340,7 @@ if (false) {
r.setStart(e2, 1);
r.setEnd(t3, 0);
sel.addRange(r);
copySelectionToClipboard(true);
await copySelectionToClipboard(true);
testPasteHTML('contentEditable6', '<span id="1127835crash1"></span><div id="1127835crash2"><div>\n</div></div><br>');
}
@ -356,8 +349,8 @@ if (false) {
var val = "1\n 2\n 3";
textarea.value=val;
textarea.select();
textarea.editor.copy();
await SimpleTest.promiseClipboardChange(() => true,
() => { textarea.editor.copy(); });
textarea.value="";
textarea.editor.paste(1);
is(textarea.value, val);
@ -365,7 +358,7 @@ if (false) {
// ============ NOSCRIPT should not be copied
copyChildrenToClipboard("div13");
await copyChildrenToClipboard("div13");
testSelectionToString("__");
testClipboardValue("text/unicode", "__");
testHtmlClipboardValue("text/html", "<div id=\"div13\">__</div>");
@ -373,13 +366,13 @@ if (false) {
// ============ converting cell boundaries to tabs in tables
copyToClipboard($("tr1"));
await copyToClipboard($("tr1"));
testClipboardValue("text/unicode", "foo\tbar");
if (!isXHTML) {
// ============ spanning multiple rows
copyRangeToClipboard($("tr2"),0,$("tr3"),0);
await copyRangeToClipboard($("tr2"),0,$("tr3"),0);
testClipboardValue("text/unicode", "1\t2\n3\t4\n");
testHtmlClipboardValue("text/html", '<table><tbody><tr id="tr2"><tr id="tr2"><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr><tr id="tr3"></tr></tr></tbody></table>');
@ -388,25 +381,27 @@ if (false) {
clear();
addRange($("tr2"),0,$("tr2"),2);
addRange($("tr3"),0,$("tr3"),2);
copySelectionToClipboard();
await copySelectionToClipboard();
testClipboardValue("text/unicode", "1\t2\n5\t6");
testHtmlClipboardValue("text/html", '<table><tbody><tr id="tr2"><td>1</td><td>2</td></tr><tr id="tr3"><td>5</td><td>6</td></tr></tbody></table>');
}
// ============ manipulating Selection in oncopy
copyRangeToClipboard($("div11").childNodes[0],0, $("div11").childNodes[1],2);
await copyRangeToClipboard($("div11").childNodes[0],0, $("div11").childNodes[1],2);
testClipboardValue("text/unicode", "Xdiv11");
testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>11</p></div>");
setTimeout(function(){testSelectionToString("div11")},0);
setTimeout(function(){
copyRangeToClipboard($("div12").childNodes[0],0, $("div12").childNodes[1],2);
testClipboardValue("text/unicode", "Xdiv12");
testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>12</p></div>");
setTimeout(function(){
testSelectionToString("div12");
setTimeout(SimpleTest.finish,0);
},0);
},0);
await new Promise(resolve => { setTimeout(resolve, 0); });
testSelectionToString("div11");
await new Promise(resolve => { setTimeout(resolve, 0); });
await copyRangeToClipboard($("div12").childNodes[0],0, $("div12").childNodes[1],2);
testClipboardValue("text/unicode", "Xdiv12");
testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>12</p></div>");
await new Promise(resolve => { setTimeout(resolve, 0); });
testSelectionToString("div12");
await new Promise(resolve => { setTimeout(resolve, 0); });
}

View File

@ -6,6 +6,7 @@
<title>Test for copy/paste</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="copypaste.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
@ -18,7 +19,11 @@
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(() => testCopyPaste(false));
addLoadEvent(() => {
add_task(async function test_copyhtml() {
await testCopyPaste(false);
});
});
</script>
</pre>

View File

@ -16,6 +16,7 @@ This test is different from test_copypaste.html in two ways:
<title>Test for copy/paste with XHTML</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="copypaste.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
@ -32,7 +33,11 @@ function modifySelectionDiv12() {
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(() => testCopyPaste(true));
addLoadEvent(() => {
add_task(async function test_copyhtml() {
await testCopyPaste(true);
});
});
]]>
</script>

View File

@ -19,35 +19,18 @@ function runTest() {
let desc = document.querySelector("description");
window.getSelection().selectAllChildren(desc);
let webnav = window.
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation);
webnav.
QueryInterface(Ci.nsIDocShell).
contentViewer.
QueryInterface(Ci.nsIContentViewerEdit).
copySelection();
let mime = "text/unicode";
let whichClipboard = Ci.nsIClipboard.kGlobalClipboard;
let clipboard = Cc["@mozilla.org/widget/clipboard;1"].
getService(Ci.nsIClipboard);
ok(clipboard.hasDataMatchingFlavors([mime], 1, whichClipboard),
"Clipboard should have text/unicode");
let transferable = Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
transferable.init(webnav.QueryInterface(Ci.nsILoadContext));
transferable.addDataFlavor(mime);
clipboard.getData(transferable, whichClipboard);
var data = {};
transferable.getTransferData(mime, data, {});
is(data.value.QueryInterface(Ci.nsISupportsString).data,
"\n hello\n world\n ",
"Paste is not HTML, so it should not be pretty printed");
SimpleTest.finish();
let expected = "\n hello\n world\n ";
SimpleTest.waitForClipboard(expected, function() {
let webnav = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation);
webnav.QueryInterface(Ci.nsIDocShell)
.contentViewer
.QueryInterface(Ci.nsIContentViewerEdit)
.copySelection();
}, function() {
ok(true, "Paste is not HTML, so it should not be pretty printed");
SimpleTest.finish();
});
}
]]></script>

View File

@ -24,10 +24,12 @@ var focusScript;
var createEmbededFrame = false;
var testSelectionChange = false;
function copyToClipboard(str) {
function copyToClipboard(str, callback) {
gTextarea.value = str;
SpecialPowers.wrap(gTextarea).editor.selectAll();
SpecialPowers.wrap(gTextarea).editor.copy();
SimpleTest.waitForClipboard(() => true, () => {
SpecialPowers.wrap(gTextarea).editor.copy();
}, callback, () => { ok(false, "clipboard copy failed"); });
}
function getScriptForGetContent() {
@ -212,36 +214,37 @@ function testSelectAll(e) {
function testCopy1(e) {
// Right now we're at "selectall" state, so we can test copy commnad by
// calling doCommand
copyToClipboard("");
let setup = function() {
doCommand("copy");
};
copyToClipboard("", () => {
let setup = function() {
doCommand("copy");
};
let nextTest = function(success) {
ok(success, "copy command works" + stateMeaning);
SimpleTest.executeSoon(function() { testPaste1(e); });
};
let nextTest = function(success) {
ok(success, "copy command works" + stateMeaning);
SimpleTest.executeSoon(function() { testPaste1(e); });
};
let success = function() {
nextTest(true);
}
let success = function() {
nextTest(true);
}
let fail = function() {
nextTest(false);
}
let fail = function() {
nextTest(false);
}
let compareData = defaultData;
SimpleTest.waitForClipboard(compareData, setup, success, fail);
let compareData = defaultData;
SimpleTest.waitForClipboard(compareData, setup, success, fail);
});
}
function testPaste1(e) {
// Next test paste command, first we copy to global clipboard in parent side.
// Then paste it to child side.
copyToClipboard(pasteData);
doCommand('selectall');
doCommand("paste");
SimpleTest.executeSoon(function() { testPaste2(e); });
copyToClipboard(pasteData, () => {
doCommand('selectall');
doCommand("paste");
SimpleTest.executeSoon(function() { testPaste2(e); });
});
}
function testPaste2(e) {
@ -264,40 +267,42 @@ function testPaste2(e) {
function testCut1(e) {
// Clean clipboard first
copyToClipboard("");
let setup = function() {
doCommand("selectall");
doCommand("cut");
};
copyToClipboard("", () => {
let setup = function() {
doCommand("selectall");
doCommand("cut");
};
let nextTest = function(success) {
if (state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) {
// Something weird when we doCommand with content editable element in OOP.
todo(false, "cut function works" + stateMeaning);
} else {
ok(success, "cut function works" + stateMeaning);
let nextTest = function(success) {
if (state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) {
// Something weird when we doCommand with content editable element in OOP.
todo(false, "cut function works" + stateMeaning);
} else {
ok(success, "cut function works" + stateMeaning);
}
SimpleTest.executeSoon(function() { testCut2(e); });
};
let success = function() {
nextTest(true);
}
SimpleTest.executeSoon(function() { testCut2(e); });
};
let success = function() {
nextTest(true);
}
let fail = function() {
nextTest(false);
}
let fail = function() {
nextTest(false);
}
let compareData = pasteData;
let compareData = pasteData;
// Something weird when we doCommand with content editable element in OOP.
// Always true in this case
// Normal div case cannot cut, always true as well.
if ((state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) ||
state == 4) {
compareData = function() { return true; }
}
// Something weird when we doCommand with content editable element in OOP.
// Always true in this case
// Normal div case cannot cut, always true as well.
if ((state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) ||
state == 4) {
compareData = function() { return true; }
}
SimpleTest.waitForClipboard(compareData, setup, success, fail);
SimpleTest.waitForClipboard(compareData, setup, success, fail);
});
}
function testCut2(e) {

View File

@ -1013,8 +1013,7 @@ WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
gfx::IntRect pictureRegion =
mVideoInfo.ScaledImageRect(videoWidth, videoHeight);
LayersBackend backend = GetCompositorBackendType(mKnowsCompositor);
if (backend != LayersBackend::LAYERS_D3D11 || !mIMFUsable) {
if (!mKnowsCompositor || !mKnowsCompositor->SupportsD3D11() || !mIMFUsable) {
RefPtr<VideoData> v =
VideoData::CreateAndCopyData(mVideoInfo,
mImageContainer,

View File

@ -90,7 +90,7 @@ ModuleLoadRequest::ModuleLoaded()
LOG(("ScriptLoadRequest (%p): Module loaded", this));
mModuleScript = mLoader->GetFetchedModule(mURI);
if (!mModuleScript || mModuleScript->IsErrored()) {
if (!mModuleScript || mModuleScript->HasParseError()) {
ModuleErrored();
return;
}
@ -104,7 +104,7 @@ ModuleLoadRequest::ModuleErrored()
LOG(("ScriptLoadRequest (%p): Module errored", this));
mLoader->CheckModuleDependenciesLoaded(this);
MOZ_ASSERT(!mModuleScript || mModuleScript->IsErrored());
MOZ_ASSERT(!mModuleScript || mModuleScript->HasParseError());
CancelImports();
SetReady();

View File

@ -22,7 +22,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
tmp->UnlinkModuleRecord();
tmp->mError.setUndefined();
tmp->mParseError.setUndefined();
tmp->mErrorToRethrow.setUndefined();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript)
@ -31,7 +32,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mError)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript)
@ -44,7 +46,8 @@ ModuleScript::ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL)
MOZ_ASSERT(mLoader);
MOZ_ASSERT(mBaseURL);
MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(mError.isUndefined());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
}
void
@ -70,7 +73,8 @@ void
ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord)
{
MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(mError.isUndefined());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
mModuleRecord = aModuleRecord;
@ -81,37 +85,24 @@ ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord)
}
void
ModuleScript::SetPreInstantiationError(const JS::Value& aError)
ModuleScript::SetParseError(const JS::Value& aError)
{
MOZ_ASSERT(!aError.isUndefined());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
UnlinkModuleRecord();
mError = aError;
mParseError = aError;
HoldJSObjects(this);
}
bool
ModuleScript::IsErrored() const
void
ModuleScript::SetErrorToRethrow(const JS::Value& aError)
{
if (!mModuleRecord) {
MOZ_ASSERT(!mError.isUndefined());
return true;
}
MOZ_ASSERT(!aError.isUndefined());
MOZ_ASSERT(!HasErrorToRethrow());
return JS::IsModuleErrored(mModuleRecord);
}
JS::Value
ModuleScript::Error() const
{
MOZ_ASSERT(IsErrored());
if (!mModuleRecord) {
return mError;
}
return JS::GetModuleError(mModuleRecord);
mErrorToRethrow = aError;
}
} // dom namespace

View File

@ -23,7 +23,8 @@ class ModuleScript final : public nsISupports
RefPtr<ScriptLoader> mLoader;
nsCOMPtr<nsIURI> mBaseURL;
JS::Heap<JSObject*> mModuleRecord;
JS::Heap<JS::Value> mError;
JS::Heap<JS::Value> mParseError;
JS::Heap<JS::Value> mErrorToRethrow;
~ModuleScript();
@ -35,14 +36,16 @@ public:
nsIURI* aBaseURL);
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
void SetPreInstantiationError(const JS::Value& aError);
void SetParseError(const JS::Value& aError);
void SetErrorToRethrow(const JS::Value& aError);
ScriptLoader* Loader() const { return mLoader; }
JSObject* ModuleRecord() const { return mModuleRecord; }
nsIURI* BaseURL() const { return mBaseURL; }
bool IsErrored() const;
JS::Value Error() const;
JS::Value ParseError() const { return mParseError; }
JS::Value ErrorToRethrow() const { return mErrorToRethrow; }
bool HasParseError() const { return !mParseError.isUndefined(); }
bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); }
void UnlinkModuleRecord();
};

View File

@ -444,7 +444,7 @@ ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest)
return rv;
}
if (!aRequest->mModuleScript->IsErrored()) {
if (!aRequest->mModuleScript->HasParseError()) {
StartFetchingModuleDependencies(aRequest);
}
@ -518,7 +518,7 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
return NS_ERROR_FAILURE;
}
moduleScript->SetPreInstantiationError(error);
moduleScript->SetParseError(error);
aRequest->ModuleErrored();
return NS_OK;
}
@ -530,8 +530,6 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
nsCOMArray<nsIURI> urls;
rv = ResolveRequestedModules(aRequest, urls);
if (NS_FAILED(rv)) {
// ResolveRequestedModules sets pre-instanitation error on failure.
MOZ_ASSERT(moduleScript->IsErrored());
aRequest->ModuleErrored();
return NS_OK;
}
@ -574,7 +572,7 @@ HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
return NS_ERROR_OUT_OF_MEMORY;
}
aScript->SetPreInstantiationError(error);
aScript->SetParseError(error);
return NS_OK;
}
@ -675,7 +673,6 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>& aUrls)
}
// Let url be the result of resolving a module specifier given module script and requested.
ModuleScript* ms = aRequest->mModuleScript;
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier);
if (!uri) {
uint32_t lineNumber = 0;
@ -702,7 +699,7 @@ void
ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest)
{
MOZ_ASSERT(aRequest->mModuleScript);
MOZ_ASSERT(!aRequest->mModuleScript->IsErrored());
MOZ_ASSERT(!aRequest->mModuleScript->HasParseError());
MOZ_ASSERT(!aRequest->IsReadyToRun());
LOG(("ScriptLoadRequest (%p): Start fetching module dependencies", aRequest));
@ -814,7 +811,7 @@ HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp)
ModuleScript* ms = script->Loader()->GetFetchedModule(uri);
MOZ_ASSERT(ms, "Resolved module not found in module map");
MOZ_ASSERT(!ms->IsErrored());
MOZ_ASSERT(!ms->HasParseError());
*vp = JS::ObjectValue(*ms->ModuleRecord());
return true;
@ -844,22 +841,20 @@ ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest)
LOG(("ScriptLoadRequest (%p): Check dependencies loaded", aRequest));
RefPtr<ModuleScript> moduleScript = aRequest->mModuleScript;
if (moduleScript && !moduleScript->IsErrored()) {
for (auto childRequest : aRequest->mImports) {
ModuleScript* childScript = childRequest->mModuleScript;
if (!childScript) {
aRequest->mModuleScript = nullptr;
LOG(("ScriptLoadRequest (%p): %p failed (load error)", aRequest, childScript));
return;
} else if (childScript->IsErrored()) {
moduleScript->SetPreInstantiationError(childScript->Error());
LOG(("ScriptLoadRequest (%p): %p failed (error)", aRequest, childScript));
return;
}
if (!moduleScript || moduleScript->HasParseError()) {
return;
}
for (auto childRequest : aRequest->mImports) {
ModuleScript* childScript = childRequest->mModuleScript;
if (!childScript) {
aRequest->mModuleScript = nullptr;
LOG(("ScriptLoadRequest (%p): %p failed (load error)", aRequest, childRequest.get()));
return;
}
}
LOG(("ScriptLoadRequest (%p): all ok", aRequest));
LOG(("ScriptLoadRequest (%p): all ok", aRequest));
}
void
@ -867,7 +862,7 @@ ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest)
{
if (aRequest->IsTopLevel()) {
ModuleScript* moduleScript = aRequest->mModuleScript;
if (moduleScript && !moduleScript->IsErrored()) {
if (moduleScript && !moduleScript->HasErrorToRethrow()) {
if (!InstantiateModuleTree(aRequest)) {
aRequest->mModuleScript = nullptr;
}
@ -881,6 +876,28 @@ ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest)
}
}
JS::Value
ScriptLoader::FindFirstParseError(ModuleLoadRequest* aRequest)
{
MOZ_ASSERT(aRequest);
ModuleScript* moduleScript = aRequest->mModuleScript;
MOZ_ASSERT(moduleScript);
if (moduleScript->HasParseError()) {
return moduleScript->ParseError();
}
for (ModuleLoadRequest* childRequest : aRequest->mImports) {
JS::Value error = FindFirstParseError(childRequest);
if (!error.isUndefined()) {
return error;
}
}
return JS::UndefinedValue();
}
bool
ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest)
{
@ -893,6 +910,14 @@ ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest)
ModuleScript* moduleScript = aRequest->mModuleScript;
MOZ_ASSERT(moduleScript);
JS::Value parseError = FindFirstParseError(aRequest);
if (!parseError.isUndefined()) {
LOG(("ScriptLoadRequest (%p): found parse error", aRequest));
moduleScript->SetErrorToRethrow(parseError);
return true;
}
MOZ_ASSERT(moduleScript->ModuleRecord());
nsAutoMicroTask mt;
@ -915,7 +940,7 @@ ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest)
return false;
}
MOZ_ASSERT(!exception.isUndefined());
// Ignore the exception. It will be recorded in the module record.
moduleScript->SetErrorToRethrow(exception);
}
return true;
@ -1582,7 +1607,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement)
} else {
AddDeferRequest(request);
}
if (!modReq->mModuleScript->IsErrored()) {
if (!modReq->mModuleScript->HasParseError()) {
StartFetchingModuleDependencies(modReq);
}
return false;
@ -2210,9 +2235,9 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
MOZ_ASSERT(!request->mOffThreadToken);
ModuleScript* moduleScript = request->mModuleScript;
if (moduleScript->IsErrored()) {
LOG(("ScriptLoadRequest (%p): module is errored", aRequest));
JS::Rooted<JS::Value> error(cx, moduleScript->Error());
if (moduleScript->HasErrorToRethrow()) {
LOG(("ScriptLoadRequest (%p): module has error to rethrow", aRequest));
JS::Rooted<JS::Value> error(cx, moduleScript->ErrorToRethrow());
JS_SetPendingException(cx, error);
return NS_OK; // An error is reported by AutoEntryScript.
}

View File

@ -488,6 +488,7 @@ private:
void CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest);
void ProcessLoadedModuleTree(ModuleLoadRequest* aRequest);
bool InstantiateModuleTree(ModuleLoadRequest* aRequest);
JS::Value FindFirstParseError(ModuleLoadRequest* aRequest);
void StartFetchingModuleDependencies(ModuleLoadRequest* aRequest);
RefPtr<mozilla::GenericPromise>

View File

@ -1,14 +1,15 @@
<!DOCTYPE html>
<head>
<title>DOMWindowCreated not visible in content</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<body onload="ok(true, 'Test finished'); SimpleTest.finish();">
<p id="display"></p>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
window.addEventListener("DOMWindowCreated", function() { ok(false, "DOMWindowCreated should not have fired"); }, false);
window.addEventListener("DOMDocElementInserted", function() { ok(false, "DOMDocElementInserted should not have fired"); }, false);
<iframe src="data:text/plain,Hi"></iframe>
<!DOCTYPE html>
<head>
<title>DOMWindowCreated not visible in content</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<body onload="ok(true, 'Test finished'); SimpleTest.finish();">
<p id="display"></p>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
window.addEventListener("DOMWindowCreated", function() { ok(false, "DOMWindowCreated should not have fired"); }, false);
window.addEventListener("DOMDocElementInserted", function() { ok(false, "DOMDocElementInserted should not have fired"); }, false);
</script>
<iframe src="data:text/plain,Hi"></iframe>

View File

@ -5,6 +5,7 @@
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
@ -19,84 +20,60 @@
<pre id="test">
<script class="testbody" type="text/javascript">
var content = document.getElementById("content");
var contentInput = document.getElementById("content-input");
var clipboardInitialValue = "empty";
// Test that clearing and reading the clipboard works. A random number
// is used to make sure that leftover clipboard values from a previous
// test run don't cause a false-positive test.
var cb_text = "empty_" + Math.random();
setClipboardText(cb_text);
is(getClipboardText(), cb_text, "set/get clipboard text failed");
var cachedCutData, cachedCopyData, cachedPasteData;
// A list of test functions to run. Before each test function is run, the
// clipboard is initialized to clipboardInitialValue, and the contents of
// div#content are set as the window's selection.
var testFunctions = [
test_dom_oncopy,
test_dom_oncut,
test_dom_onpaste,
test_dom_oncopy_abort,
test_input_oncopy,
test_input_oncut,
test_input_onpaste,
test_input_oncopy_abort,
test_input_oncut_abort,
test_input_onpaste_abort,
test_input_cut_dataTransfer,
test_input_cut_abort_dataTransfer,
test_input_copy_dataTransfer,
test_input_paste_dataTransfer,
test_input_paste_abort_dataTransfer,
test_input_copypaste_dataTransfer_multiple,
test_input_copy_button_dataTransfer,
test_eventspref_disabled,
test_input_cut_disallowed_types_dataTransfer,
test_image_dataTransfer,
];
// Before each test function is run, the clipboard is initialized
// to clipboardInitialValue, and the contents of div#content are
// set as the window's selection.
function doTests()
{
add_task(async function initialize_for_tests() {
await SimpleTest.promiseFocus();
await new Promise(resolve => {
SpecialPowers.pushPrefEnv({
// NOTE: These tests operate under the assumption that the protected mode of
// DataTransfer is enabled.
"set": [["dom.events.dataTransfer.protected.enabled", true]]
}, resolve);
});
// Test that clearing and reading the clipboard works. A random number
// is used to make sure that leftover clipboard values from a previous
// test run don't cause a false-positive test.
var cb_text = "empty_" + Math.random();
await putOnClipboard(cb_text, () => { setClipboardText(cb_text) },
"initial set/get clipboard text");
});
async function reset() {
// Init clipboard
setClipboardText(clipboardInitialValue);
await putOnClipboard(clipboardInitialValue,
() => { setClipboardText(clipboardInitialValue) },
"reset clipboard");
// Reset value of contentInput.
contentInput.value = "INPUT TEXT";
if (testFunctions.length) {
let func = testFunctions.shift();
let result = func();
if (result instanceof Promise) {
result.then(doTests);
}
else {
doTests();
}
}
else {
// Check if the cached clipboard data can be accessed or modified
// and whether it modifies the real clipboard
checkCachedDataTransfer(cachedCutData, "cut");
checkCachedDataTransfer(cachedCopyData, "copy");
checkCachedDataTransfer(cachedPasteData, "paste");
checkSyntheticEvents();
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
function getClipboardText() {
return SpecialPowers.getClipboardData("text/unicode");
}
async function putOnClipboard(expected, operationFn, desc, type) {
await SimpleTest.promiseClipboardChange(expected, operationFn, type);
ok(true, desc);
}
async function wontPutOnClipboard(expected, operationFn, desc, type) {
await SimpleTest.promiseClipboardChange(null, operationFn, type, 300, true);
ok(SpecialPowers.getClipboardData(type || "text/unicode")
.includes("waitForClipboard-known-value"), desc + " data");
}
function setClipboardText(text) {
var helper = SpecialPowers.Cc["@mozilla.org/widget/clipboardhelper;1"]
@ -116,25 +93,29 @@ function selectContentInput() {
contentInput.focus();
}
function test_dom_oncopy() {
add_task(async function test_dom_oncopy() {
await reset();
// Setup an oncopy event handler, fire copy. Ensure that the event
// handler was called, and the clipboard contents have set to CONTENT TEXT.
// Test firing oncopy event on ctrl-c:
selectContentDiv();
var oncopy_fired = false;
content.oncopy = function() { oncopy_fired = true; };
try {
synthesizeKey("c", {accelKey: 1});
await putOnClipboard("CONTENT TEXT", () => {
synthesizeKey("c", {accelKey: 1});
}, "copy on DOM element set clipboard correctly");
ok(oncopy_fired, "copy event firing on DOM element");
is(getClipboardText(), "CONTENT TEXT",
"copy on DOM element set clipboard correctly");
} finally {
content.oncopy = null;
}
}
});
add_task(async function test_dom_oncut() {
await reset();
function test_dom_oncut() {
// Setup an oncut event handler, fire cut. Ensure that the event handler
// was called. The <div> doesn't handle a cut, so ensure that the
// clipboard text is clipboardInitialValue, NOT "CONTENT TEXT".
@ -142,17 +123,18 @@ function test_dom_oncut() {
var oncut_fired = false;
content.oncut = function() { oncut_fired = true; };
try {
synthesizeKey("x", {accelKey: 1});
await wontPutOnClipboard(clipboardInitialValue, () => {
synthesizeKey("x", {accelKey: 1});
}, "cut on DOM element set clipboard correctly");
ok(oncut_fired, "cut event firing on DOM element")
is(getClipboardText(), clipboardInitialValue,
"cut on DOM element did not modify clipboard");
} finally {
content.oncut = null;
}
}
});
add_task(async function test_dom_onpaste() {
await reset();
function test_dom_onpaste() {
// Setup an onpaste event handler, fire paste. Ensure that the event
// handler was called.
selectContentDiv();
@ -164,10 +146,11 @@ function test_dom_onpaste() {
} finally {
content.onpaste = null;
}
}
});
add_task(async function test_dom_oncopy_abort() {
await reset();
function test_dom_oncopy_abort() {
// Setup an oncopy event handler that aborts the copy, and fire the copy
// event. Ensure that the event handler was fired, and the clipboard
// contents have not been modified.
@ -175,17 +158,18 @@ function test_dom_oncopy_abort() {
var oncopy_fired = false;
content.oncopy = function() { oncopy_fired = true; return false; };
try {
synthesizeKey("c", {accelKey: 1});
await wontPutOnClipboard(clipboardInitialValue, () => {
synthesizeKey("c", {accelKey: 1});
}, "aborted copy on DOM element did not modify clipboard");
ok(oncopy_fired, "copy event (to-be-cancelled) firing on DOM element");
is(getClipboardText(), clipboardInitialValue,
"aborted copy on DOM element did not modify clipboard");
} finally {
content.oncopy = null;
}
}
});
add_task(async function test_input_oncopy() {
await reset();
function test_input_oncopy() {
// Setup an oncopy event handler, fire copy. Ensure that the event
// handler was called, and the clipboard contents have been set to 'PUT TE',
// which is the part that is selected below.
@ -196,17 +180,18 @@ function test_input_oncopy() {
var oncopy_fired = false;
contentInput.oncopy = function() { oncopy_fired = true; };
try {
synthesizeKey("c", {accelKey: 1});
await putOnClipboard("PUT TE", () => {
synthesizeKey("c", {accelKey: 1});
}, "copy on plaintext editor set clipboard correctly");
ok(oncopy_fired, "copy event firing on plaintext editor");
is(getClipboardText(), "PUT TE",
"copy on plaintext editor set clipboard correctly");
} finally {
contentInput.oncopy = null;
}
}
});
add_task(async function test_input_oncut() {
await reset();
function test_input_oncut() {
// Setup an oncut event handler, and fire cut. Ensure that the event
// handler was fired, the clipboard contains the INPUT TEXT, and
// that the input itself is empty.
@ -214,19 +199,20 @@ function test_input_oncut() {
var oncut_fired = false;
contentInput.oncut = function() { oncut_fired = true; };
try {
synthesizeKey("x", {accelKey: 1});
await putOnClipboard("INPUT TEXT", () => {
synthesizeKey("x", {accelKey: 1});
}, "cut on plaintext editor set clipboard correctly");
ok(oncut_fired, "cut event firing on plaintext editor");
is(getClipboardText(), "INPUT TEXT",
"cut on plaintext editor set clipboard correctly");
is(contentInput.value, "",
"cut on plaintext editor emptied editor");
} finally {
contentInput.oncut = null;
}
}
});
add_task(async function test_input_onpaste() {
await reset();
function test_input_onpaste() {
// Setup an onpaste event handler, and fire paste. Ensure that the event
// handler was fired, the clipboard contents didn't change, and that the
// input value did change (ie. paste succeeded).
@ -243,27 +229,29 @@ function test_input_onpaste() {
} finally {
contentInput.onpaste = null;
}
}
});
add_task(async function test_input_oncopy_abort() {
await reset();
function test_input_oncopy_abort() {
// Setup an oncopy event handler, fire copy. Ensure that the event
// handler was called, and that the clipboard value did NOT change.
selectContentInput();
var oncopy_fired = false;
contentInput.oncopy = function() { oncopy_fired = true; return false; };
try {
synthesizeKey("c", {accelKey: 1});
await wontPutOnClipboard(clipboardInitialValue, () => {
synthesizeKey("c", {accelKey: 1});
}, "aborted copy on plaintext editor did not modify clipboard");
ok(oncopy_fired, "copy event (to-be-cancelled) firing on plaintext editor");
is(getClipboardText(), clipboardInitialValue,
"aborted copy on plaintext editor did not modify clipboard");
} finally {
contentInput.oncopy = null;
}
}
});
add_task(async function test_input_oncut_abort() {
await reset();
function test_input_oncut_abort() {
// Setup an oncut event handler, and fire cut. Ensure that the event
// handler was fired, the clipboard contains the INPUT TEXT, and
// that the input itself is empty.
@ -271,19 +259,20 @@ function test_input_oncut_abort() {
var oncut_fired = false;
contentInput.oncut = function() { oncut_fired = true; return false; };
try {
synthesizeKey("x", {accelKey: 1});
await wontPutOnClipboard(clipboardInitialValue, () => {
synthesizeKey("x", {accelKey: 1});
}, "aborted cut on plaintext editor did not modify clipboard");
ok(oncut_fired, "cut event (to-be-cancelled) firing on plaintext editor");
is(getClipboardText(), clipboardInitialValue,
"aborted cut on plaintext editor did not modify clipboard.");
is(contentInput.value, "INPUT TEXT",
"aborted cut on plaintext editor did not modify editor contents");
} finally {
contentInput.oncut = null;
}
}
});
add_task(async function test_input_onpaste_abort() {
await reset();
function test_input_onpaste_abort() {
// Setup an onpaste event handler, and fire paste. Ensure that the event
// handler was fired, the clipboard contents didn't change, and that the
// input value did change (ie. paste succeeded).
@ -301,10 +290,11 @@ function test_input_onpaste_abort() {
} finally {
contentInput.onpaste = null;
}
}
});
add_task(async function test_input_cut_dataTransfer() {
await reset();
function test_input_cut_dataTransfer() {
// Cut using event.dataTransfer. The event is not cancelled so the default
// cut should occur
selectContentInput();
@ -318,18 +308,19 @@ function test_input_cut_dataTransfer() {
cachedCutData = event.clipboardData;
};
try {
synthesizeKey("x", {accelKey: 1});
is(getClipboardText(), "INPUT TEXT",
"cut using dataTransfer on plaintext editor set clipboard correctly");
await putOnClipboard("INPUT TEXT", () => {
synthesizeKey("x", {accelKey: 1});
}, "cut using dataTransfer on plaintext editor set clipboard correctly");
is(contentInput.value, "",
"cut using dataTransfer on plaintext editor cleared input");
} finally {
contentInput.oncut = null;
}
}
});
add_task(async function test_input_cut_abort_dataTransfer() {
await reset();
function test_input_cut_abort_dataTransfer() {
// Cut using event.dataTransfer but cancel the event. The data should be
// put on the clipboard but since we don't modify the input value, the input
// should have the same value.
@ -339,18 +330,19 @@ function test_input_cut_abort_dataTransfer() {
return false;
};
try {
synthesizeKey("x", {accelKey: 1});
is(getClipboardText(), "Cut dataTransfer text",
"aborted cut using dataTransfer on plaintext editor set clipboard correctly");
await putOnClipboard("Cut dataTransfer text", () => {
synthesizeKey("x", {accelKey: 1});
}, "aborted cut using dataTransfer on plaintext editor set clipboard correctly");
is(contentInput.value, "INPUT TEXT",
"aborted cut using dataTransfer on plaintext editor did not modify input");
} finally {
contentInput.oncut = null;
}
}
});
add_task(async function test_input_copy_dataTransfer() {
await reset();
function test_input_copy_dataTransfer() {
// Copy using event.dataTransfer
selectContentInput();
contentInput.oncopy = function(event) {
@ -363,18 +355,19 @@ function test_input_copy_dataTransfer() {
cachedCopyData = event.clipboardData;
};
try {
synthesizeKey("c", {accelKey: 1});
is(getClipboardText(), "INPUT TEXT",
"copy using dataTransfer on plaintext editor set clipboard correctly");
await putOnClipboard("INPUT TEXT", () => {
synthesizeKey("c", {accelKey: 1});
}, "copy using dataTransfer on plaintext editor set clipboard correctly");
is(contentInput.value, "INPUT TEXT",
"copy using dataTransfer on plaintext editor did not modify input");
} finally {
contentInput.oncopy = null;
}
}
});
add_task(async function test_input_copy_abort_dataTransfer() {
await reset();
function test_input_copy_abort_dataTransfer() {
// Copy using event.dataTransfer but cancel the event.
selectContentInput();
contentInput.oncopy = function(event) {
@ -382,18 +375,19 @@ function test_input_copy_abort_dataTransfer() {
return false;
};
try {
synthesizeKey("x", {accelKey: 1});
is(getClipboardText(), "Copy dataTransfer text",
"aborted copy using dataTransfer on plaintext editor set clipboard correctly");
await putOnClipboard("Copy dataTransfer text", () => {
synthesizeKey("c", {accelKey: 1});
}, "aborted copy using dataTransfer on plaintext editor set clipboard correctly");
is(contentInput.value, "INPUT TEXT",
"aborted copy using dataTransfer on plaintext editor did not modify input");
} finally {
contentInput.oncopy = null;
}
}
});
add_task(async function test_input_paste_dataTransfer() {
await reset();
function test_input_paste_dataTransfer() {
// Paste using event.dataTransfer
selectContentInput();
contentInput.onpaste = function(event) {
@ -413,10 +407,11 @@ function test_input_paste_dataTransfer() {
} finally {
contentInput.onpaste = null;
}
}
});
add_task(async function test_input_paste_abort_dataTransfer() {
await reset();
function test_input_paste_abort_dataTransfer() {
// Paste using event.dataTransfer but cancel the event
selectContentInput();
contentInput.onpaste = function(event) {
@ -433,9 +428,11 @@ function test_input_paste_abort_dataTransfer() {
} finally {
contentInput.onpaste = null;
}
}
});
add_task(async function test_input_copypaste_dataTransfer_multiple() {
await reset();
function test_input_copypaste_dataTransfer_multiple() {
// Cut several types of data and paste it again
contentInput.value = "This is a line of text";
contentInput.oncopy = function(event) {
@ -463,14 +460,15 @@ function test_input_copypaste_dataTransfer_multiple() {
try {
selectContentInput();
synthesizeKey("c", {accelKey: 1});
await putOnClipboard("would be a phrase", () => {
synthesizeKey("c", {accelKey: 1});
}, "copy multiple types text");
}
finally {
contentInput.oncopy = null;
}
is(getClipboardText(), "would be a phrase", "copy multiple types text");
contentInput.setSelectionRange(5, 14);
contentInput.onpaste = function(event) {
@ -506,9 +504,11 @@ function test_input_copypaste_dataTransfer_multiple() {
} finally {
contentInput.onpaste = null;
}
}
});
add_task(async function test_input_copy_button_dataTransfer() {
await reset();
function test_input_copy_button_dataTransfer() {
// Copy using event.dataTransfer when a button is focused.
var button = document.getElementById("button");
button.focus();
@ -519,67 +519,72 @@ function test_input_copy_button_dataTransfer() {
try {
// copy should not occur here because buttons don't have any controller
// for the copy command
synthesizeKey("c", {accelKey: 1});
is(getClipboardText(), clipboardInitialValue,
"copy using dataTransfer on plaintext editor set clipboard correctly for button");
await wontPutOnClipboard(clipboardInitialValue, () => {
synthesizeKey("c", {accelKey: 1});
}, "copy using dataTransfer on plaintext editor set clipboard correctly for button");
selectContentDiv();
synthesizeKey("c", {accelKey: 1});
is(getClipboardText(), "CONTENT TEXT",
"copy using dataTransfer with selection on plaintext editor set clipboard correctly for button");
await putOnClipboard("CONTENT TEXT", () => {
synthesizeKey("c", {accelKey: 1});
}, "copy using dataTransfer with selection on plaintext editor set clipboard correctly for button");
} finally {
document.documentElement.oncopy = null;
}
}
});
add_task(async function test_eventspref_disabled() {
await reset();
function test_eventspref_disabled() {
// Disable clipboard events
return new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [['dom.event.clipboardevents.enabled', false]]}, doPrefDisabledTest);
function doPrefDisabledTest() {
var event_fired = false;
contentInput.oncut = function() { event_fired = true; };
contentInput.oncopy = function() { event_fired = true; };
contentInput.onpaste = function() { event_fired = true; };
try {
selectContentInput();
contentInput.setSelectionRange(1, 4);
synthesizeKey("x", {accelKey: 1});
is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled");
is(getClipboardText(), "NPU", "cut changed clipboard when preference is disabled");
ok(!event_fired, "cut event did not fire when preference is disabled")
event_fired = false;
contentInput.setSelectionRange(3, 6);
synthesizeKey("c", {accelKey: 1});
is(getClipboardText(), "TEX", "copy changed clipboard when preference is disabled");
ok(!event_fired, "copy event did not fire when preference is disabled")
event_fired = false;
contentInput.setSelectionRange(0, 2);
synthesizeKey("v", {accelKey: 1});
is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled");
ok(!event_fired, "paste event did not fire when preference is disabled")
} finally {
contentInput.oncut = null;
contentInput.oncopy = null;
contentInput.onpaste = null;
}
SpecialPowers.popPrefEnv(resolve);
}
await new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [['dom.event.clipboardevents.enabled', false]]}, resolve);
});
}
var event_fired = false;
contentInput.oncut = function() { event_fired = true; };
contentInput.oncopy = function() { event_fired = true; };
contentInput.onpaste = function() { event_fired = true; };
try {
selectContentInput();
contentInput.setSelectionRange(1, 4);
await putOnClipboard("NPU", () => {
synthesizeKey("x", {accelKey: 1});
}, "cut changed clipboard when preference is disabled");
is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled");
ok(!event_fired, "cut event did not fire when preference is disabled")
event_fired = false;
contentInput.setSelectionRange(3, 6);
await putOnClipboard("TEX", () => {
synthesizeKey("c", {accelKey: 1});
}, "copy changed clipboard when preference is disabled");
ok(!event_fired, "copy event did not fire when preference is disabled")
event_fired = false;
contentInput.setSelectionRange(0, 2);
synthesizeKey("v", {accelKey: 1});
is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled");
ok(!event_fired, "paste event did not fire when preference is disabled")
} finally {
contentInput.oncut = null;
contentInput.oncopy = null;
contentInput.onpaste = null;
}
await new Promise(resolve => {
SpecialPowers.popPrefEnv(resolve);
});
});
let expectedData = [];
// Check to make that synthetic events do not change the clipboard
function checkSyntheticEvents()
{
add_task(async function test_synthetic_events() {
await reset();
let syntheticSpot = document.getElementById("syntheticSpot");
setClipboardText(clipboardInitialValue);
// No dataType specified
let event = new ClipboardEvent("cut", { data: "something" });
@ -623,10 +628,9 @@ function checkSyntheticEvents()
syntheticSpot.dispatchEvent(event);
ok(expectedData.eventFired, "paste event fired");
compareSynthetic(event, "after");
}
});
function compareSynthetic(event, eventtype)
{
function compareSynthetic(event, eventtype) {
let step = (eventtype == "cut" || eventtype == "copy" || eventtype == "paste") ? "during" : eventtype;
if (step == "during") {
is(eventtype, expectedData.type, "synthetic " + eventtype + " event fired");
@ -652,11 +656,11 @@ function compareSynthetic(event, eventtype)
}
}
function checkCachedDataTransfer(cd, eventtype)
{
async function checkCachedDataTransfer(cd, eventtype) {
var testprefix = "cached " + eventtype + " dataTransfer";
setClipboardText("Some Clipboard Text");
await putOnClipboard("Some Clipboard Text", () => { setClipboardText("Some Clipboard Text") },
"change clipboard outside of event");
var oldtext = cd.getData("text/plain");
ok(!oldtext, "clipboard get using " + testprefix);
@ -676,7 +680,19 @@ function checkCachedDataTransfer(cd, eventtype)
is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix);
}
function test_input_cut_disallowed_types_dataTransfer() {
add_task(async function test_modify_datatransfer_outofevent() {
await reset();
// Check if the cached clipboard data can be accessed or modified
// and whether it modifies the real clipboard
await checkCachedDataTransfer(cachedCutData, "cut");
await checkCachedDataTransfer(cachedCopyData, "copy");
await checkCachedDataTransfer(cachedPasteData, "paste");
});
add_task(async function test_input_cut_disallowed_types_dataTransfer() {
await reset();
selectContentInput();
let oncutExecuted = false;
contentInput.oncut = function(event) {
@ -700,23 +716,29 @@ function test_input_cut_disallowed_types_dataTransfer() {
};
try {
synthesizeKey("x", {accelKey: 1});
await putOnClipboard("INPUT TEXT", () => {
synthesizeKey("x", {accelKey: 1});
}, "The oncut handler should have been executed data");
ok(oncutExecuted, "The oncut handler should have been executed");
} finally {
contentInput.oncut = null;
}
}
});
// Try copying an image to the clipboard and make sure that it looks correct when pasting it.
function test_image_dataTransfer() {
add_task(async function test_image_dataTransfer() {
await reset();
// cmd_copyImageContents errors on Android (bug 1299578).
if (navigator.userAgent.includes("Android")) {
return;
}
// Copy the image's data to the clipboard
SpecialPowers.setCommandNode(window, document.getElementById("image"));
SpecialPowers.doCommand(window, "cmd_copyImageContents");
await putOnClipboard("", () => {
SpecialPowers.setCommandNode(window, document.getElementById("image"));
SpecialPowers.doCommand(window, "cmd_copyImageContents");
}, "copy changed clipboard when preference is disabled");
let onpasteCalled = false;
document.onpaste = function(event) {
@ -744,14 +766,6 @@ function test_image_dataTransfer() {
} finally {
document.onpaste = null;
}
}
SimpleTest.waitForFocus(() => {
SpecialPowers.pushPrefEnv({
// NOTE: These tests operate under the assumption that the protected mode of
// DataTransfer is enabled.
"set": [["dom.events.dataTransfer.protected.enabled", true]]
}, doTests);
});
</script>

View File

@ -2483,7 +2483,7 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
nsresult rv = aLoadInfo->mResolvedScriptURI->GetSpec(scriptSpec);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(aLoadInfo->mPrincipal);
MOZ_DIAGNOSTIC_ASSERT(aLoadInfo->mPrincipal && aLoadInfo->mLoadingPrincipal);
WorkerDomainInfo* domainInfo;
if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo)) {
@ -2491,9 +2491,9 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
if (data->mScriptSpec == scriptSpec &&
data->mName == aName &&
// We want to be sure that the window's principal subsumes the
// SharedWorker's principal and vice versa.
aLoadInfo->mPrincipal->Subsumes(data->mWorkerPrivate->GetPrincipal()) &&
data->mWorkerPrivate->GetPrincipal()->Subsumes(aLoadInfo->mPrincipal)) {
// SharedWorker's loading principal and vice versa.
aLoadInfo->mLoadingPrincipal->Subsumes(data->mWorkerPrivate->GetLoadingPrincipal()) &&
data->mWorkerPrivate->GetLoadingPrincipal()->Subsumes(aLoadInfo->mLoadingPrincipal)) {
workerPrivate = data->mWorkerPrivate;
break;
}

View File

@ -1083,6 +1083,20 @@ private:
principal = parentWorker->GetPrincipal();
}
#ifdef DEBUG
if (IsMainWorkerScript()) {
nsCOMPtr<nsIPrincipal> loadingPrincipal =
mWorkerPrivate->GetLoadingPrincipal();
// if we are not in a ServiceWorker, and the principal is not null, then the
// loading principal must subsume the worker principal if it is not a
// nullPrincipal (sandbox).
MOZ_ASSERT(!loadingPrincipal ||
loadingPrincipal->GetIsNullPrincipal() ||
principal->GetIsNullPrincipal() ||
loadingPrincipal->Subsumes(principal));
}
#endif
// We don't mute the main worker script becase we've already done
// same-origin checks on them so we should be able to see their errors.
// Note that for data: url, where we allow it through the same-origin check
@ -1185,7 +1199,7 @@ private:
// Store the channel info if needed.
mWorkerPrivate->InitChannelInfo(channel);
// Our final channel principal should match the original principal
// Our final channel principal should match the loading principal
// in terms of the origin.
MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->FinalChannelPrincipalIsValid(channel));
@ -1838,8 +1852,10 @@ public:
// before doing anything else. Normally we do this in the WorkerPrivate
// Constructor, but we can't do so off the main thread when creating
// a nested worker. So do it here instead.
mLoadInfo.mPrincipal = mWorkerPrivate->GetPrincipal();
MOZ_ASSERT(mLoadInfo.mPrincipal);
mLoadInfo.mLoadingPrincipal = mWorkerPrivate->GetPrincipal();
MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mLoadingPrincipal);
mLoadInfo.mPrincipal = mLoadInfo.mLoadingPrincipal;
// Figure out our base URI.
nsCOMPtr<nsIURI> baseURI = mWorkerPrivate->GetBaseURI();
@ -1852,7 +1868,7 @@ public:
nsCOMPtr<nsIChannel> channel;
mResult =
scriptloader::ChannelFromScriptURLMainThread(mLoadInfo.mPrincipal,
scriptloader::ChannelFromScriptURLMainThread(mLoadInfo.mLoadingPrincipal,
baseURI, parentDoc,
mLoadInfo.mLoadGroup,
mScriptURL,

View File

@ -1894,7 +1894,7 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
info.mEvalAllowed = true;
info.mReportCSPViolations = false;
WorkerPrivate::OverrideLoadInfoLoadGroup(info);
WorkerPrivate::OverrideLoadInfoLoadGroup(info, info.mPrincipal);
rv = info.SetPrincipalOnMainThread(info.mPrincipal, info.mLoadGroup);
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -1830,6 +1830,9 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
MOZ_ASSERT(!mPrincipal);
aOther.mPrincipal.swap(mPrincipal);
// mLoadingPrincipal can be null if this is a ServiceWorker.
aOther.mLoadingPrincipal.swap(mLoadingPrincipal);
MOZ_ASSERT(!mScriptContext);
aOther.mScriptContext.swap(mScriptContext);
@ -1926,7 +1929,7 @@ WorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
MOZ_DIAGNOSTIC_ASSERT(aLoadGroupOut);
// Initial triggering principal should be set
MOZ_DIAGNOSTIC_ASSERT(mPrincipal);
MOZ_DIAGNOSTIC_ASSERT(mLoadingPrincipal);
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
MOZ_DIAGNOSTIC_ASSERT(ssm);
@ -1940,14 +1943,14 @@ WorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(channelLoadGroup);
// If the load principal is the system principal then the channel
// If the loading principal is the system principal then the channel
// principal must also be the system principal (we do not allow chrome
// code to create workers with non-chrome scripts, and if we ever decide
// to change this we need to make sure we don't always set
// mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
// this channel principal must be same origin with the load principal (we
// check again here in case redirects changed the location of the script).
if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
if (nsContentUtils::IsSystemPrincipal(mLoadingPrincipal)) {
if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
nsCOMPtr<nsIURI> finalURI;
rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
@ -1965,7 +1968,7 @@ WorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
if (isResource) {
// Assign the system principal to the resource:// worker only if it
// was loaded from code using the system principal.
channelPrincipal = mPrincipal;
channelPrincipal = mLoadingPrincipal;
} else {
return NS_ERROR_DOM_BAD_URI;
}
@ -2099,7 +2102,7 @@ WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
{
static const uint32_t kDoomedCount = 10;
static const uint32_t kDoomedCount = 11;
nsTArray<nsCOMPtr<nsISupports>> doomed(kDoomedCount);
SwapToISupportsArray(mWindow, doomed);
@ -2107,6 +2110,7 @@ WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
SwapToISupportsArray(mBaseURI, doomed);
SwapToISupportsArray(mResolvedScriptURI, doomed);
SwapToISupportsArray(mPrincipal, doomed);
SwapToISupportsArray(mLoadingPrincipal, doomed);
SwapToISupportsArray(mChannel, doomed);
SwapToISupportsArray(mCSP, doomed);
SwapToISupportsArray(mLoadGroup, doomed);
@ -4845,7 +4849,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
// loader will refuse to run any script that does not also have the system
// principal.
if (isChrome) {
rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal));
rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mLoadingPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
loadInfo.mPrincipalIsSystem = true;
@ -4895,11 +4899,11 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
loadInfo.mLoadGroup = document->GetDocumentLoadGroup();
NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE);
// Use the document's NodePrincipal as our principal if we're not being
// Use the document's NodePrincipal as loading principal if we're not being
// called from chrome.
if (!loadInfo.mPrincipal) {
loadInfo.mPrincipal = document->NodePrincipal();
NS_ENSURE_TRUE(loadInfo.mPrincipal, NS_ERROR_FAILURE);
if (!loadInfo.mLoadingPrincipal) {
loadInfo.mLoadingPrincipal = document->NodePrincipal();
NS_ENSURE_TRUE(loadInfo.mLoadingPrincipal, NS_ERROR_FAILURE);
// We use the document's base domain to limit the number of workers
// each domain can create. For sandboxed documents, we use the domain
@ -4919,18 +4923,18 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
NS_ENSURE_SUCCESS(rv, rv);
} else {
// No unsandboxed ancestor, use our GUID.
rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
NS_ENSURE_SUCCESS(rv, rv);
}
} else {
// Document creating the worker is not sandboxed.
rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
NS_ENSURE_SUCCESS(rv, rv);
}
}
NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
loadInfo.mPrincipal),
loadInfo.mLoadingPrincipal),
NS_ERROR_FAILURE);
nsCOMPtr<nsIPermissionManager> permMgr =
@ -4938,8 +4942,8 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
NS_ENSURE_SUCCESS(rv, rv);
uint32_t perm;
rv = permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal, "systemXHR",
&perm);
rv = permMgr->TestPermissionFromPrincipal(loadInfo.mLoadingPrincipal,
"systemXHR", &perm);
NS_ENSURE_SUCCESS(rv, rv);
loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
@ -4994,19 +4998,20 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
loadInfo.mOriginAttributes = OriginAttributes();
}
MOZ_ASSERT(loadInfo.mPrincipal);
MOZ_ASSERT(loadInfo.mLoadingPrincipal);
MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) {
OverrideLoadInfoLoadGroup(loadInfo);
OverrideLoadInfoLoadGroup(loadInfo, loadInfo.mLoadingPrincipal);
}
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
loadInfo.mPrincipal));
loadInfo.mLoadingPrincipal));
// Top level workers' main script use the document charset for the script
// uri encoding.
bool useDefaultEncoding = false;
rv = ChannelFromScriptURLMainThread(loadInfo.mPrincipal, loadInfo.mBaseURI,
rv = ChannelFromScriptURLMainThread(loadInfo.mLoadingPrincipal,
loadInfo.mBaseURI,
document, loadInfo.mLoadGroup,
aScriptURL,
ContentPolicyType(aWorkerType),
@ -5022,6 +5027,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
NS_ENSURE_SUCCESS(rv, rv);
}
MOZ_DIAGNOSTIC_ASSERT(loadInfo.mLoadingPrincipal);
MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());
aLoadInfo->StealFrom(loadInfo);
@ -5030,13 +5036,15 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
// static
void
WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo)
WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
nsIPrincipal* aPrincipal)
{
MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
MOZ_ASSERT(aLoadInfo.mPrincipal == aPrincipal /* service workers */ ||
aLoadInfo.mLoadingPrincipal == aPrincipal /* any other worker type */);
aLoadInfo.mInterfaceRequestor =
new WorkerLoadInfo::InterfaceRequestor(aLoadInfo.mPrincipal,
aLoadInfo.mLoadGroup);
new WorkerLoadInfo::InterfaceRequestor(aPrincipal, aLoadInfo.mLoadGroup);
aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);
// NOTE: this defaults the load context to:
@ -5052,8 +5060,7 @@ WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo)
aLoadInfo.mLoadGroup = loadGroup.forget();
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup,
aLoadInfo.mPrincipal));
MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup, aPrincipal));
}
void

View File

@ -650,6 +650,13 @@ public:
return mLoadInfo.mPrincipal;
}
nsIPrincipal*
GetLoadingPrincipal() const
{
AssertIsOnMainThread();
return mLoadInfo.mLoadingPrincipal;
}
const nsAString& Origin() const
{
return mLoadInfo.mOrigin;
@ -1112,8 +1119,11 @@ public:
LoadGroupBehavior aLoadGroupBehavior, WorkerType aWorkerType,
WorkerLoadInfo* aLoadInfo);
// The passed principal must be the Worker principal in case of a
// ServiceWorker and the loading principal for any other type.
static void
OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo);
OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
nsIPrincipal* aPrincipal);
bool
IsDebuggerRegistered()

View File

@ -203,7 +203,14 @@ struct WorkerLoadInfo
// All of these should be released in WorkerPrivateParent::ForgetMainThreadObjects.
nsCOMPtr<nsIURI> mBaseURI;
nsCOMPtr<nsIURI> mResolvedScriptURI;
// This is the principal of the global (parent worker or a window) loading
// the worker. It can be null if we are executing a ServiceWorker, otherwise,
// except for data: URL, it must subsumes the worker principal.
// If we load a data: URL, mPrincipal will be a null principal.
nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIScriptContext> mScriptContext;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
nsCOMPtr<nsIContentSecurityPolicy> mCSP;

View File

@ -4,6 +4,7 @@
<style src="/tests/SimpleTest/test.css" type="text/css"></style>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script class="testbody" type="application/javascript">
@ -19,7 +20,7 @@ function getLoadContext() {
.QueryInterface(Ci.nsILoadContext);
}
function runTest() {
async function runTest() {
var pasteCount = 0;
var pasteFunc = function (event) { pasteCount++; };
@ -114,7 +115,7 @@ function runTest() {
return trans;
}
function copyToClipBoard(s,asHTML,target_id) {
async function copyToClipBoard(s,asHTML,target_id) {
var e = document.getElementById('i2');
var doc = e.contentDocument;
if (asHTML) {
@ -134,11 +135,14 @@ function runTest() {
range.selectNode(doc.getElementById(target_id));
selection.addRange(range);
}
SpecialPowers.wrap(doc).execCommand("copy", false, null);
await SimpleTest.promiseClipboardChange(() => true,
() => { SpecialPowers.wrap(doc).execCommand("copy", false, null); });
return e;
}
copyToClipBoard('<span>Hello</span><span>Kitty</span>', true);
await copyToClipBoard('<span>Hello</span><span>Kitty</span>', true);
var trans = getTransferableFromClipboard(true);
pasteInto(trans, '');
verifyContent('<span>Hello</span><span>Kitty</span>');
@ -150,7 +154,7 @@ function runTest() {
//pasteInto(trans, '');
//verifyContent('<span>Hello</span><span>Kitty</span>');
copyToClipBoard("<dl><dd>Hello Kitty</dd></dl><span>Hello</span><span>Kitty</span>", true);
await copyToClipBoard("<dl><dd>Hello Kitty</dd></dl><span>Hello</span><span>Kitty</span>", true);
trans = getTransferableFromClipboard(true);
pasteInto(trans, '<ol><li id="paste_here">X</li></ol>',"paste_here");
verifyContent('<ol><li id="paste_here">X<dl><dd>Hello Kitty</dd></dl><span>Hello</span><span>Kitty</span></li></ol>');
@ -159,11 +163,11 @@ function runTest() {
// The following test doesn't do what I expected, because the special handling
// of IsList nodes in nsHTMLEditor::InsertHTMLWithContext simply removes
// non-list/item children. See bug 481177.
// copyToClipBoard("<ol><li>Hello Kitty</li><span>Hello</span></ol>", true);
// await copyToClipBoard("<ol><li>Hello Kitty</li><span>Hello</span></ol>", true);
// pasteInto('<ol><li id="paste_here">X</li></ol>',"paste_here");
// verifyContent('<ol><li id="paste_here">X</li><li>Hello Kitty</li><span>Hello</span></ol>');
copyToClipBoard("<pre>Kitty</pre><span>Hello</span>", true);
await copyToClipBoard("<pre>Kitty</pre><span>Hello</span>", true);
trans = getTransferableFromClipboard(true);
pasteInto(trans, '<pre id="paste_here">Hello </pre>',"paste_here");
verifyContent('<pre id="paste_here">Hello Kitty<span>Hello</span></pre>');
@ -171,17 +175,20 @@ function runTest() {
// test that we can preventDefault pastes
pasteFunc = function (event) { event.preventDefault(); return false; };
copyToClipBoard("<pre>Kitty</pre><span>Hello</span>", true);
await copyToClipBoard("<pre>Kitty</pre><span>Hello</span>", true);
trans = getTransferableFromClipboard(true);
pasteInto(trans, '<pre id="paste_here">Hello </pre>',"paste_here");
verifyContent('<pre id="paste_here">Hello </pre>');
is(pasteCount, 0, "paste event was triggered");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);
addLoadEvent(() => {
add_task(async function test_copy() {
await runTest();
});
});
</script>
</head>
<body>

View File

@ -44,8 +44,10 @@ function testTab(prefix, callback) {
);
}
SimpleTest.waitForExplicitFinish();
testTab("", function() {
testTab("foo", function() {
SimpleTest.finish();
});
});

View File

@ -36,8 +36,11 @@ SimpleTest.waitForFocus(function() {
r.setEnd(div.firstChild, 9);
sel.addRange(r);
SimpleTest.waitForClipboard(
function compare(value) {
SimpleTest.waitForClipboard(() => true,
function setup() {
synthesizeKey("C", {accelKey: true});
},
function onSuccess() {
var theEdit = document.getElementById("editable");
sel.collapse(theEdit.firstChild, 2);
@ -45,12 +48,7 @@ SimpleTest.waitForFocus(function() {
is(theEdit.innerHTML,
"ABCopy this",
"unexpected HTML for test");
return true;
},
function setup() {
synthesizeKey("C", {accelKey: true});
},
function onSuccess() {
SimpleTest.finish();
},
function onFailure() {

View File

@ -36,8 +36,11 @@ SimpleTest.waitForFocus(function() {
r.setEnd(div.firstChild, 9);
sel.addRange(r);
SimpleTest.waitForClipboard(
function compare(value) {
SimpleTest.waitForClipboard(() => true,
function setup() {
synthesizeKey("C", {accelKey: true});
},
function onSuccess() {
var theEdit = document.getElementById("editable");
sel.collapse(theEdit.firstChild, 2);
@ -45,12 +48,6 @@ SimpleTest.waitForFocus(function() {
is(theEdit.innerHTML,
"ABCopy this",
"unexpected HTML for test");
return true;
},
function setup() {
synthesizeKey("C", {accelKey: true});
},
function onSuccess() {
SimpleTest.finish();
},
function onFailure() {

View File

@ -36,8 +36,11 @@ SimpleTest.waitForFocus(function() {
r.setEnd(div.firstChild, 9);
sel.addRange(r);
SimpleTest.waitForClipboard(
function compare(value) {
SimpleTest.waitForClipboard(() => true,
function setup() {
synthesizeKey("C", {accelKey: true});
},
function onSuccess() {
var theEdit = document.getElementById("editable");
sel.collapse(theEdit.firstChild, 2);
@ -45,12 +48,6 @@ SimpleTest.waitForFocus(function() {
is(theEdit.innerHTML,
"AB<blockquote type=\"cite\">Copy this</blockquote>",
"unexpected HTML for test");
return true;
},
function setup() {
synthesizeKey("C", {accelKey: true});
},
function onSuccess() {
SimpleTest.finish();
},
function onFailure() {

View File

@ -36,8 +36,11 @@ SimpleTest.waitForFocus(function() {
r.setEnd(div.firstChild, 9);
sel.addRange(r);
SimpleTest.waitForClipboard(
function compare(value) {
SimpleTest.waitForClipboard(() => true,
function setup() {
synthesizeKey("C", {accelKey: true});
},
function onSuccess() {
var theEdit = document.getElementById("editable");
sel.collapse(theEdit.firstChild, 1);
@ -45,12 +48,7 @@ SimpleTest.waitForFocus(function() {
is(theEdit.innerHTML,
"<span>AB<blockquote type=\"cite\">Copy this</blockquote></span>",
"unexpected HTML for test");
return true;
},
function setup() {
synthesizeKey("C", {accelKey: true});
},
function onSuccess() {
SimpleTest.finish();
},
function onFailure() {

View File

@ -14,6 +14,7 @@
#include "Filters.h"
#include <vector>
#include "CaptureCommandList.h"
#include "FilterNodeCapture.h"
namespace mozilla {
namespace gfx {
@ -246,7 +247,11 @@ public:
virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
{
aDT->DrawFilter(mFilter, mSourceRect, mDestPoint, mOptions);
RefPtr<FilterNode> filter = mFilter;
if (mFilter->GetBackendType() == FilterBackend::FILTER_BACKEND_CAPTURE) {
filter = static_cast<FilterNodeCapture*>(filter.get())->Validate(aDT);
}
aDT->DrawFilter(filter, mSourceRect, mDestPoint, mOptions);
}
static const bool AffectsSnapshot = true;

View File

@ -8,6 +8,7 @@
#include "DrawCommand.h"
#include "gfxPlatform.h"
#include "SourceSurfaceCapture.h"
#include "FilterNodeCapture.h"
namespace mozilla {
namespace gfx {
@ -418,5 +419,15 @@ DrawTargetCaptureImpl::CreateSimilarRasterTarget(const IntSize& aSize, SurfaceFo
return mRefDT->CreateSimilarDrawTarget(aSize, aFormat);
}
already_AddRefed<FilterNode>
DrawTargetCaptureImpl::CreateFilter(FilterType aType)
{
if (mRefDT->GetBackendType() == BackendType::DIRECT2D1_1) {
return MakeRefPtr<FilterNodeCapture>(aType).forget();
} else {
return mRefDT->CreateFilter(aType);
}
}
} // namespace gfx
} // namespace mozilla

View File

@ -143,10 +143,7 @@ public:
{
return mRefDT->CreateGradientStops(aStops, aNumStops, aExtendMode);
}
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override
{
return mRefDT->CreateFilter(aType);
}
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
void ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform);

View File

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "FilterNodeCapture.h"
namespace mozilla {
namespace gfx {
struct Setter
{
Setter(FilterNode* aNode, DrawTarget* aDT, bool aInputsChanged)
: mNode{aNode}, mIndex{0}, mDT{aDT}, mInputsChanged{aInputsChanged} {}
template<typename T>
void match(T& aValue) { mNode->SetAttribute(mIndex, aValue); }
FilterNode* mNode;
uint32_t mIndex;
DrawTarget* mDT;
bool mInputsChanged;
};
template<>
void
Setter::match<std::vector<Float>>(std::vector<Float>& aValue)
{
mNode->SetAttribute(mIndex, aValue.data(), aValue.size());
}
template<>
void
Setter::match<RefPtr<SourceSurface>>(RefPtr<SourceSurface>& aSurface)
{
if (!mInputsChanged) {
return;
}
mNode->SetInput(mIndex, aSurface);
}
template<>
void
Setter::match<RefPtr<FilterNode>>(RefPtr<FilterNode>& aNode)
{
RefPtr<FilterNode> node = aNode;
if (node->GetBackendType() == FilterBackend::FILTER_BACKEND_CAPTURE) {
FilterNodeCapture* captureNode = static_cast<FilterNodeCapture*>(node.get());
node = captureNode->Validate(mDT);
}
if (!mInputsChanged) {
return;
}
mNode->SetInput(mIndex, node);
}
RefPtr<FilterNode>
FilterNodeCapture::Validate(DrawTarget* aDT)
{
if (!mFilterNodeInternal) {
mFilterNodeInternal = aDT->CreateFilter(mType);
}
if (!mFilterNodeInternal) {
return nullptr;
}
Setter setter(mFilterNodeInternal, aDT, mInputsChanged);
for (auto attribute : mAttributes)
{
setter.mIndex = attribute.first;
// Variant's matching doesn't seem to compile to terribly efficient code,
// this is probably fine since this happens on the paint thread, if ever
// needed it would be fairly simple to write something more optimized.
attribute.second.match(setter);
}
mAttributes.clear();
for (auto input : mInputs) {
setter.mIndex = input.first;
input.second.match(setter);
}
mInputsChanged = false;
return mFilterNodeInternal.get();
}
void
FilterNodeCapture::SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize)
{
std::vector<Float> vec(aSize);
memcpy(vec.data(), aValues, sizeof(Float) * aSize);
AttributeValue att(std::move(vec));
auto result = mAttributes.insert({ aIndex, att });
if (!result.second) {
result.first->second = att;
}
}
}
}

105
gfx/2d/FilterNodeCapture.h Normal file
View File

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef MOZILLA_GFX_FILTERNODECAPTURE_H_
#define MOZILLA_GFX_FILTERNODECAPTURE_H_
#include "2D.h"
#include "Filters.h"
#include <unordered_map>
#include <vector>
#include "mozilla/Variant.h"
namespace mozilla {
namespace gfx {
class FilterNodeCapture final : public FilterNode
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeCapture, override)
explicit FilterNodeCapture(FilterType aType)
: mType{aType}
, mInputsChanged{true}
{
}
virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_CAPTURE; }
RefPtr<FilterNode> Validate(DrawTarget* aDT);
template<typename T, typename C>
void Replace(uint32_t aIndex, const T& aValue, C& aContainer)
{
// This replace function avoids generating the hash twice.
auto result = aContainer.insert({ aIndex, typename C::mapped_type(aValue) });
if (!result.second) {
result.first->second = typename C::mapped_type(aValue);
}
}
virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override
{
mInputsChanged = true;
Replace(aIndex, RefPtr<SourceSurface>(aSurface), mInputs);
}
virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override
{
mInputsChanged = true;
Replace(aIndex, RefPtr<FilterNode>(aFilter), mInputs);
}
using AttributeValue = Variant<
uint32_t,
Float,
Point,
Matrix5x4,
Point3D,
Size,
IntSize,
Color,
Rect,
IntRect,
bool,
std::vector<Float>,
IntPoint,
Matrix
>;
virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, Float aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const Point &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const Point3D &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const Size &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const IntSize &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const Color &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const Rect &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, bool aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const Float *aValues, uint32_t aSize) override;
virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue) override { Replace(aIndex, aValue, mAttributes); }
virtual void SetAttribute(uint32_t aIndex, const Matrix &aValue) override { Replace(aIndex, aValue, mAttributes); }
private:
FilterType mType;
RefPtr<FilterNode> mFilterNodeInternal;
// This only contains attributes that were set since the last validation.
std::unordered_map<uint32_t, AttributeValue> mAttributes;
// This always contains all inputs, so that Validate may be called on input
// filter nodes.
std::unordered_map<uint32_t, Variant<RefPtr<SourceSurface>, RefPtr<FilterNode>>> mInputs;
// This is true if SetInput was called since the last validation.
bool mInputsChanged;
};
}
}
#endif

View File

@ -22,7 +22,8 @@ class SourceSurface;
enum FilterBackend {
FILTER_BACKEND_SOFTWARE = 0,
FILTER_BACKEND_DIRECT2D1_1,
FILTER_BACKEND_RECORDING
FILTER_BACKEND_RECORDING,
FILTER_BACKEND_CAPTURE
};
enum TransformFilterAtts

View File

@ -167,6 +167,7 @@ UNIFIED_SOURCES += [
'DrawTargetRecording.cpp',
'DrawTargetTiled.cpp',
'DrawTargetWrapAndRecord.cpp',
'FilterNodeCapture.cpp',
'FilterNodeSoftware.cpp',
'FilterProcessing.cpp',
'FilterProcessingScalar.cpp',

View File

@ -5,11 +5,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "IMFYCbCrImage.h"
#include "mozilla/gfx/DeviceManagerDx.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/layers/TextureD3D11.h"
#include "mozilla/layers/CompositableClient.h"
#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/gfx/DeviceManagerDx.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/layers/TextureClient.h"
#include "d3d9.h"
@ -135,7 +136,9 @@ IMFYCbCrImage::GetD3D11TextureData(Data aData, gfx::IntSize aSize)
CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM,
aData.mYSize.width, aData.mYSize.height, 1, 1);
if (device == gfx::DeviceManagerDx::Get()->GetCompositorDevice()) {
// WebRender requests keyed mutex
if (device == gfx::DeviceManagerDx::Get()->GetCompositorDevice() &&
!gfxVars::UseWebRender()) {
newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
} else {
newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
@ -222,8 +225,7 @@ IMFYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder)
gfx::DeviceManagerDx::Get()->GetCompositorDevice();
}
LayersBackend backend = aForwarder->GetCompositorBackendType();
if (!device || backend != LayersBackend::LAYERS_D3D11) {
if (!device || !aForwarder->SupportsD3D11()) {
return nullptr;
}
return GetD3D11TextureClient(aForwarder);

View File

@ -1169,7 +1169,8 @@ TextureClient::CreateFromSurface(KnowsCompositor* aAllocator,
int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
if (layersBackend == LayersBackend::LAYERS_D3D11 &&
if ((layersBackend == LayersBackend::LAYERS_D3D11 ||
layersBackend == LayersBackend::LAYERS_WR) &&
(moz2DBackend == gfx::BackendType::DIRECT2D ||
moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
(!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&

View File

@ -930,8 +930,8 @@ NewImageChannel(nsIChannel** aResult,
return NS_OK;
}
static uint32_t
SecondsFromPRTime(PRTime prTime)
/* static */ uint32_t
imgCacheEntry::SecondsFromPRTime(PRTime prTime)
{
return uint32_t(int64_t(prTime) / int64_t(PR_USEC_PER_SEC));
}
@ -1881,13 +1881,11 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry,
{
LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
bool hasExpired;
uint32_t expirationTime = aEntry->GetExpiryTime();
if (expirationTime <= SecondsFromPRTime(PR_Now())) {
hasExpired = true;
} else {
hasExpired = false;
}
// If the expiration time is zero, then the request has not gotten far enough
// to know when it will expire.
uint32_t expiryTime = aEntry->GetExpiryTime();
bool hasExpired = expiryTime != 0 &&
expiryTime <= imgCacheEntry::SecondsFromPRTime(PR_Now());
nsresult rv;
@ -1904,7 +1902,8 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry,
if (NS_SUCCEEDED(rv)) {
// nsIFile uses millisec, NSPR usec
fileLastMod *= 1000;
hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
hasExpired =
imgCacheEntry::SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
}
}
}

View File

@ -41,6 +41,8 @@ class ImageURL;
class imgCacheEntry
{
public:
static uint32_t SecondsFromPRTime(PRTime prTime);
imgCacheEntry(imgLoader* loader, imgRequest* request,
bool aForcePrincipalCheck);
~imgCacheEntry();

View File

@ -33,6 +33,7 @@
#include "nsContentUtils.h"
#include "plstr.h" // PL_strcasestr(...)
#include "prtime.h" // for PR_Now
#include "nsNetUtil.h"
#include "nsIProtocolHandler.h"
#include "imgIRequest.h"
@ -637,17 +638,21 @@ imgRequest::SetCacheValidation(imgCacheEntry* aCacheEntry, nsIRequest* aRequest)
{
/* get the expires info */
if (aCacheEntry) {
nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aRequest));
if (cacheChannel) {
// Expiration time defaults to 0. We set the expiration time on our
// entry if it hasn't been set yet.
if (aCacheEntry->GetExpiryTime() == 0) {
uint32_t expiration = 0;
/* get the expiration time from the caching channel's token */
if (NS_SUCCEEDED(cacheChannel->GetCacheTokenExpirationTime(&expiration))) {
// Expiration time defaults to 0. We set the expiration time on our
// entry if it hasn't been set yet.
if (aCacheEntry->GetExpiryTime() == 0) {
aCacheEntry->SetExpiryTime(expiration);
}
nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aRequest));
if (cacheChannel) {
/* get the expiration time from the caching channel's token */
cacheChannel->GetCacheTokenExpirationTime(&expiration);
}
if (expiration == 0) {
// If the channel doesn't support caching, then ensure this expires the
// next time it is used.
expiration = imgCacheEntry::SecondsFromPRTime(PR_Now()) - 1;
}
aCacheEntry->SetExpiryTime(expiration);
}
// Determine whether the cache entry must be revalidated when we try to use

View File

@ -68,10 +68,16 @@ function ModuleGetExportedNames(exportStarSet = [])
function ModuleSetStatus(module, newStatus)
{
assert(newStatus >= MODULE_STATUS_ERRORED && newStatus <= MODULE_STATUS_EVALUATED,
assert(newStatus >= MODULE_STATUS_UNINSTANTIATED &&
newStatus <= MODULE_STATUS_EVALUATED_ERROR,
"Bad new module status in ModuleSetStatus");
if (newStatus !== MODULE_STATUS_ERRORED)
assert(newStatus > module.status, "New module status inconsistent with current status");
// Note that under OOM conditions we can fail the module instantiation
// process even after modules have been marked as instantiated.
assert((module.status <= MODULE_STATUS_INSTANTIATED &&
newStatus === MODULE_STATUS_UNINSTANTIATED) ||
newStatus > module.status,
"New module status inconsistent with current status");
UnsafeSetReservedSlot(module, MODULE_OBJECT_STATUS_SLOT, newStatus);
}
@ -81,20 +87,15 @@ function ModuleSetStatus(module, newStatus)
// Returns an object describing the location of the resolved export or
// indicating a failure.
//
// On success this returns: { resolved: true, module, bindingName }
// On success this returns a resolved binding record: { module, bindingName }
//
// There are three failure cases:
// There are two failure cases:
//
// - The resolution failure can be blamed on a particular module.
// Returns: { resolved: false, module, ambiguous: false }
// - If no definition was found or the request is found to be circular, *null*
// is returned.
//
// - No culprit can be determined and the resolution failure was due to star
// export ambiguity.
// Returns: { resolved: false, module: null, ambiguous: true }
//
// - No culprit can be determined and the resolution failure was not due to
// star export ambiguity.
// Returns: { resolved: false, module: null, ambiguous: false }
// - If the request is found to be ambiguous, the string `"ambiguous"` is
// returned.
//
function ModuleResolveExport(exportName, resolveSet = [])
{
@ -107,53 +108,47 @@ function ModuleResolveExport(exportName, resolveSet = [])
let module = this;
// Step 2
assert(module.status !== MODULE_STATUS_ERRORED, "Bad module state in ResolveExport");
// Step 3
for (let i = 0; i < resolveSet.length; i++) {
let r = resolveSet[i];
if (r.module === module && r.exportName === exportName) {
// This is a circular import request.
return {resolved: false, module: null, ambiguous: false};
return null;
}
}
// Step 4
// Step 3
_DefineDataProperty(resolveSet, resolveSet.length, {module, exportName});
// Step 5
// Step 4
let localExportEntries = module.localExportEntries;
for (let i = 0; i < localExportEntries.length; i++) {
let e = localExportEntries[i];
if (exportName === e.exportName)
return {resolved: true, module, bindingName: e.localName};
return {module, bindingName: e.localName};
}
// Step 6
// Step 5
let indirectExportEntries = module.indirectExportEntries;
for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i];
if (exportName === e.exportName) {
let importedModule = CallModuleResolveHook(module, e.moduleRequest,
MODULE_STATUS_UNINSTANTIATED);
let resolution = callFunction(importedModule.resolveExport, importedModule, e.importName,
resolveSet);
if (!resolution.resolved && !resolution.module)
resolution.module = module;
return resolution;
return callFunction(importedModule.resolveExport, importedModule, e.importName,
resolveSet);
}
}
// Step 7
// Step 6
if (exportName === "default") {
// A default export cannot be provided by an export *.
return {resolved: false, module: null, ambiguous: false};
return null;
}
// Step 8
// Step 7
let starResolution = null;
// Step 9
// Step 8
let starExportEntries = module.starExportEntries;
for (let i = 0; i < starExportEntries.length; i++) {
let e = starExportEntries[i];
@ -161,26 +156,30 @@ function ModuleResolveExport(exportName, resolveSet = [])
MODULE_STATUS_UNINSTANTIATED);
let resolution = callFunction(importedModule.resolveExport, importedModule, exportName,
resolveSet);
if (!resolution.resolved && (resolution.module || resolution.ambiguous))
if (resolution === "ambiguous")
return resolution;
if (resolution.resolved) {
if (resolution !== null) {
if (starResolution === null) {
starResolution = resolution;
} else {
if (resolution.module !== starResolution.module ||
resolution.bindingName !== starResolution.bindingName)
{
return {resolved: false, module: null, ambiguous: true};
return "ambiguous";
}
}
}
}
// Step 10
if (starResolution !== null)
return starResolution;
// Step 9
return starResolution;
}
return {resolved: false, module: null, ambiguous: false};
function IsResolvedBinding(resolution)
{
assert(resolution === "ambiguous" || typeof resolution === "object",
"Bad module resolution result");
return typeof resolution === "object" && resolution !== null;
}
// 15.2.1.18 GetModuleNamespace(module)
@ -189,12 +188,12 @@ function GetModuleNamespace(module)
// Step 1
assert(IsModule(module), "GetModuleNamespace called with non-module");
// Step 2
// Steps 2-3
assert(module.status !== MODULE_STATUS_UNINSTANTIATED &&
module.status !== MODULE_STATUS_ERRORED,
module.status !== MODULE_STATUS_EVALUATED_ERROR,
"Bad module state in GetModuleNamespace");
// Step 3
// Step 4
let namespace = module.namespace;
// Step 3
@ -204,7 +203,7 @@ function GetModuleNamespace(module)
for (let i = 0; i < exportedNames.length; i++) {
let name = exportedNames[i];
let resolution = callFunction(module.resolveExport, module, name);
if (resolution.resolved)
if (IsResolvedBinding(resolution))
_DefineDataProperty(unambiguousNames, unambiguousNames.length, name);
}
namespace = ModuleNamespaceCreate(module, unambiguousNames);
@ -226,7 +225,7 @@ function ModuleNamespaceCreate(module, exports)
for (let i = 0; i < exports.length; i++) {
let name = exports[i];
let binding = callFunction(module.resolveExport, module, name);
assert(binding.resolved, "Failed to resolve binding");
assert(IsResolvedBinding(binding), "Failed to resolve binding");
AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName);
}
@ -237,11 +236,6 @@ function GetModuleEnvironment(module)
{
assert(IsModule(module), "Non-module passed to GetModuleEnvironment");
// Check for a previous failed attempt to instantiate this module. This can
// only happen due to a bug in the module loader.
if (module.status === MODULE_STATUS_ERRORED)
ThrowInternalError(JSMSG_MODULE_INSTANTIATE_FAILED, module.status);
assert(module.status >= MODULE_STATUS_INSTANTIATING,
"Attempt to access module environement before instantation");
@ -252,19 +246,6 @@ function GetModuleEnvironment(module)
return env;
}
function RecordModuleError(module, error)
{
// Set the module's status to 'errored' to indicate a failed module
// instantiation and record the exception. The environment slot is also
// reset to 'undefined'.
assert(IsObject(module) && IsModule(module), "Non-module passed to RecordModuleError");
ModuleSetStatus(module, MODULE_STATUS_ERRORED);
UnsafeSetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT, error);
UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined);
}
function CountArrayValues(array, value)
{
let count = 0;
@ -284,6 +265,16 @@ function ArrayContains(array, value)
return false;
}
function HandleModuleInstantiationFailure(module)
{
// Reset the module to the "uninstantiated" state. Don't reset the
// environment slot as the environment object will be required by any
// possible future instantiation attempt.
ModuleSetStatus(module, MODULE_STATUS_UNINSTANTIATED);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, undefined);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, undefined);
}
// 15.2.1.16.4 ModuleInstantiate()
function ModuleInstantiate()
{
@ -305,37 +296,29 @@ function ModuleInstantiate()
// Steps 4-5
try {
InnerModuleDeclarationInstantiation(module, stack, 0);
InnerModuleInstantiation(module, stack, 0);
} catch (error) {
for (let i = 0; i < stack.length; i++) {
let m = stack[i];
assert(m.status === MODULE_STATUS_INSTANTIATING ||
m.status === MODULE_STATUS_ERRORED,
"Bad module status after failed instantiation");
RecordModuleError(m, error);
assert(m.status === MODULE_STATUS_INSTANTIATING,
"Expected instantiating status during failed instantiation");
HandleModuleInstantiationFailure(m);
}
if (stack.length === 0 &&
typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined")
{
// This can happen due to OOM when appending to the stack or
// over-recursion errors.
RecordModuleError(module, error);
}
// Handle OOM when appending to the stack or over-recursion errors.
if (stack.length === 0)
HandleModuleInstantiationFailure(module);
assert(module.status === MODULE_STATUS_ERRORED,
"Bad module status after failed instantiation");
assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error),
"Module has different error set after failed instantiation");
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
"Expected uninstantiated status after failed instantiation");
throw error;
}
// Step 6
assert(module.status == MODULE_STATUS_INSTANTIATED ||
module.status == MODULE_STATUS_EVALUATED,
assert(module.status === MODULE_STATUS_INSTANTIATED ||
module.status === MODULE_STATUS_EVALUATED ||
module.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad module status after successful instantiation");
// Step 7
@ -347,8 +330,8 @@ function ModuleInstantiate()
}
_SetCanonicalName(ModuleInstantiate, "ModuleInstantiate");
// 15.2.1.16.4.1 InnerModuleDeclarationInstantiation(module, stack, index)
function InnerModuleDeclarationInstantiation(module, stack, index)
// 15.2.1.16.4.1 InnerModuleInstantiation(module, stack, index)
function InnerModuleInstantiation(module, stack, index)
{
// Step 1
// TODO: Support module records other than source text module records.
@ -356,42 +339,40 @@ function InnerModuleDeclarationInstantiation(module, stack, index)
// Step 2
if (module.status === MODULE_STATUS_INSTANTIATING ||
module.status === MODULE_STATUS_INSTANTIATED ||
module.status === MODULE_STATUS_EVALUATED)
module.status === MODULE_STATUS_EVALUATED ||
module.status === MODULE_STATUS_EVALUATED_ERROR)
{
return index;
}
// Step 3
if (module.status === MODULE_STATUS_ERRORED)
throw module.error;
// Step 4
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
"Bad module status in ModuleDeclarationInstantiation");
// Steps 5
// Steps 4
ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING);
// Step 6-8
// Step 5-7
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
index++;
// Step 9
// Step 8
_DefineDataProperty(stack, stack.length, module);
// Step 10
// Step 9
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i].moduleSpecifier;
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_ERRORED);
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED);
index = InnerModuleDeclarationInstantiation(requiredModule, stack, index);
index = InnerModuleInstantiation(requiredModule, stack, index);
assert(requiredModule.status === MODULE_STATUS_INSTANTIATING ||
requiredModule.status === MODULE_STATUS_INSTANTIATED ||
requiredModule.status === MODULE_STATUS_EVALUATED,
"Bad required module status after InnerModuleDeclarationInstantiation");
requiredModule.status === MODULE_STATUS_EVALUATED ||
requiredModule.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad required module status after InnerModuleInstantiation");
assert((requiredModule.status === MODULE_STATUS_INSTANTIATING) ===
ArrayContains(stack, requiredModule),
@ -407,16 +388,16 @@ function InnerModuleDeclarationInstantiation(module, stack, index)
}
}
// Step 11
// Step 10
ModuleDeclarationEnvironmentSetup(module);
// Steps 12-13
// Steps 11-12
assert(CountArrayValues(stack, module) === 1,
"Current module should appear exactly once in the stack");
assert(module.dfsAncestorIndex <= module.dfsIndex,
"Bad DFS ancestor index");
// Step 14
// Step 13
if (module.dfsAncestorIndex === module.dfsIndex) {
let requiredModule;
do {
@ -437,11 +418,9 @@ function ModuleDeclarationEnvironmentSetup(module)
for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i];
let resolution = callFunction(module.resolveExport, module, e.exportName);
assert(resolution.resolved || resolution.module,
"Unexpected failure to resolve export in ModuleDeclarationEnvironmentSetup");
if (!resolution.resolved) {
return ResolutionError(resolution, "indirectExport", e.exportName,
e.lineNumber, e.columnNumber);
if (!IsResolvedBinding(resolution)) {
ThrowResolutionError(module, resolution, "indirectExport", e.exportName,
e.lineNumber, e.columnNumber);
}
}
@ -461,12 +440,9 @@ function ModuleDeclarationEnvironmentSetup(module)
} else {
let resolution = callFunction(importedModule.resolveExport, importedModule,
imp.importName);
if (!resolution.resolved && !resolution.module)
resolution.module = module;
if (!resolution.resolved) {
return ResolutionError(resolution, "import", imp.importName,
imp.lineNumber, imp.columnNumber);
if (!IsResolvedBinding(resolution)) {
ThrowResolutionError(module, resolution, "import", imp.importName,
imp.lineNumber, imp.columnNumber);
}
CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
@ -476,38 +452,58 @@ function ModuleDeclarationEnvironmentSetup(module)
InstantiateModuleFunctionDeclarations(module);
}
// 15.2.1.16.4.3 ResolutionError(module)
function ResolutionError(resolution, kind, name, line, column)
function ThrowResolutionError(module, resolution, kind, name, line, column)
{
let module = resolution.module;
assert(module !== null,
"Null module passed to ResolutionError");
assert(module.status === MODULE_STATUS_UNINSTANTIATED ||
module.status === MODULE_STATUS_INSTANTIATING,
"Unexpected module status in ResolutionError");
assert(module.status === MODULE_STATUS_INSTANTIATING,
"Unexpected module status in ThrowResolutionError");
assert(kind === "import" || kind === "indirectExport",
"Unexpected kind in ResolutionError");
"Unexpected kind in ThrowResolutionError");
assert(line > 0,
"Line number should be present for all imports and indirect exports");
let ambiguous = resolution === "ambiguous";
let errorNumber;
if (kind === "import") {
errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_IMPORT
: JSMSG_MISSING_IMPORT;
} else {
errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT
: JSMSG_MISSING_INDIRECT_EXPORT;
}
if (kind === "import")
errorNumber = ambiguous ? JSMSG_AMBIGUOUS_IMPORT : JSMSG_MISSING_IMPORT;
else
errorNumber = ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT : JSMSG_MISSING_INDIRECT_EXPORT;
let message = GetErrorMessage(errorNumber) + ": " + name;
let error = CreateModuleSyntaxError(module, line, column, message);
RecordModuleError(module, error);
throw error;
}
function GetModuleEvaluationError(module)
{
assert(IsObject(module) && IsModule(module),
"Non-module passed to GetModuleEvaluationError");
assert(module.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad module status in GetModuleEvaluationError");
return UnsafeGetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT);
}
function RecordModuleEvaluationError(module, error)
{
// Set the module's EvaluationError slot to indicate a failed module
// evaluation.
assert(IsObject(module) && IsModule(module),
"Non-module passed to RecordModuleEvaluationError");
if (module.status === MODULE_STATUS_EVALUATED_ERROR) {
// It would be nice to assert that |error| is the same as the one we
// previously recorded, but that's not always true in the case of out of
// memory and over-recursion errors.
return;
}
ModuleSetStatus(module, MODULE_STATUS_EVALUATED_ERROR);
UnsafeSetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT, error);
}
// 15.2.1.16.5 ModuleEvaluate()
function ModuleEvaluate()
{
@ -518,9 +514,9 @@ function ModuleEvaluate()
let module = this;
// Step 2
if (module.status !== MODULE_STATUS_ERRORED &&
module.status !== MODULE_STATUS_INSTANTIATED &&
module.status !== MODULE_STATUS_EVALUATED)
if (module.status !== MODULE_STATUS_INSTANTIATED &&
module.status !== MODULE_STATUS_EVALUATED &&
module.status !== MODULE_STATUS_EVALUATED_ERROR)
{
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
}
@ -534,30 +530,22 @@ function ModuleEvaluate()
} catch (error) {
for (let i = 0; i < stack.length; i++) {
let m = stack[i];
assert(m.status === MODULE_STATUS_EVALUATING,
"Bad module status after failed evaluation");
RecordModuleError(m, error);
RecordModuleEvaluationError(m, error);
}
if (stack.length === 0 &&
typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined")
{
// This can happen due to OOM when appending to the stack or
// over-recursion errors.
RecordModuleError(module, error);
}
// Handle OOM when appending to the stack or over-recursion errors.
if (stack.length === 0)
RecordModuleEvaluationError(module, error);
assert(module.status === MODULE_STATUS_ERRORED,
assert(module.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad module status after failed evaluation");
assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error),
"Module has different error set after failed evaluation");
throw error;
}
assert(module.status == MODULE_STATUS_EVALUATED,
assert(module.status === MODULE_STATUS_EVALUATED,
"Bad module status after successful evaluation");
assert(stack.length === 0,
"Stack should be empty after successful evaluation");
@ -573,19 +561,19 @@ function InnerModuleEvaluation(module, stack, index)
// TODO: Support module records other than source text module records.
// Step 2
if (module.status === MODULE_STATUS_EVALUATING ||
module.status === MODULE_STATUS_EVALUATED)
{
if (module.status === MODULE_STATUS_EVALUATED_ERROR)
throw GetModuleEvaluationError(module);
if (module.status === MODULE_STATUS_EVALUATED)
return index;
}
// Step 3
if (module.status === MODULE_STATUS_ERRORED)
throw module.error;
if (module.status === MODULE_STATUS_EVALUATING)
return index;
// Step 4
assert(module.status === MODULE_STATUS_INSTANTIATED,
"Bad module status in ModuleEvaluation");
"Bad module status in InnerModuleEvaluation");
// Step 5
ModuleSetStatus(module, MODULE_STATUS_EVALUATING);
@ -607,8 +595,8 @@ function InnerModuleEvaluation(module, stack, index)
index = InnerModuleEvaluation(requiredModule, stack, index);
assert(requiredModule.status == MODULE_STATUS_EVALUATING ||
requiredModule.status == MODULE_STATUS_EVALUATED,
assert(requiredModule.status === MODULE_STATUS_EVALUATING ||
requiredModule.status === MODULE_STATUS_EVALUATED,
"Bad module status after InnerModuleEvaluation");
assert((requiredModule.status === MODULE_STATUS_EVALUATING) ===

View File

@ -22,10 +22,10 @@
using namespace js;
using namespace js::frontend;
static_assert(MODULE_STATUS_ERRORED < MODULE_STATUS_UNINSTANTIATED &&
MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED &&
MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED,
MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED &&
MODULE_STATUS_EVALUATED < MODULE_STATUS_EVALUATED_ERROR,
"Module statuses are ordered incorrectly");
template<typename T, Value ValueGetter(const T* obj)>
@ -342,12 +342,12 @@ IndirectBindingMap::trace(JSTracer* trc)
}
bool
IndirectBindingMap::putNew(JSContext* cx, HandleId name,
HandleModuleEnvironmentObject environment, HandleId localName)
IndirectBindingMap::put(JSContext* cx, HandleId name,
HandleModuleEnvironmentObject environment, HandleId localName)
{
RootedShape shape(cx, environment->lookup(cx, localName));
MOZ_ASSERT(shape);
if (!map_.putNew(name, Binding(environment, shape))) {
if (!map_.put(name, Binding(environment, shape))) {
ReportOutOfMemory(cx);
return false;
}
@ -428,7 +428,7 @@ ModuleNamespaceObject::addBinding(JSContext* cx, HandleAtom exportedName,
RootedModuleEnvironmentObject environment(cx, &targetModule->initialEnvironment());
RootedId exportedNameId(cx, AtomToId(exportedName));
RootedId localNameId(cx, AtomToId(localName));
return bindings().putNew(cx, exportedNameId, environment, localNameId);
return bindings().put(cx, exportedNameId, environment, localNameId);
}
const char ModuleNamespaceObject::ProxyHandler::family = 0;
@ -799,7 +799,7 @@ ModuleObject::initialEnvironment() const
ModuleEnvironmentObject*
ModuleObject::environment() const
{
MOZ_ASSERT(status() != MODULE_STATUS_ERRORED);
MOZ_ASSERT(!hadEvaluationError());
// According to the spec the environment record is created during
// instantiation, but we create it earlier than that.
@ -845,7 +845,7 @@ void
ModuleObject::init(HandleScript script)
{
initReservedSlot(ScriptSlot, PrivateValue(script));
initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_ERRORED));
initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
}
void
@ -961,7 +961,8 @@ ModuleObject::script() const
static inline void
AssertValidModuleStatus(ModuleStatus status)
{
MOZ_ASSERT(status >= MODULE_STATUS_ERRORED && status <= MODULE_STATUS_EVALUATED);
MOZ_ASSERT(status >= MODULE_STATUS_UNINSTANTIATED &&
status <= MODULE_STATUS_EVALUATED_ERROR);
}
ModuleStatus
@ -972,11 +973,17 @@ ModuleObject::status() const
return status;
}
Value
ModuleObject::error() const
bool
ModuleObject::hadEvaluationError() const
{
MOZ_ASSERT(status() == MODULE_STATUS_ERRORED);
return getReservedSlot(ErrorSlot);
return status() == MODULE_STATUS_EVALUATED_ERROR;
}
Value
ModuleObject::evaluationError() const
{
MOZ_ASSERT(hadEvaluationError());
return getReservedSlot(EvaluationErrorSlot);
}
Value
@ -1137,7 +1144,7 @@ ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self)
DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, error, ErrorSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, EvaluationErrorSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot)
@ -1152,7 +1159,7 @@ GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global)
static const JSPropertySpec protoAccessors[] = {
JS_PSG("namespace", ModuleObject_namespace_Getter, 0),
JS_PSG("status", ModuleObject_statusGetter, 0),
JS_PSG("error", ModuleObject_errorGetter, 0),
JS_PSG("evaluationError", ModuleObject_evaluationErrorGetter, 0),
JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),

View File

@ -133,8 +133,8 @@ class IndirectBindingMap
void trace(JSTracer* trc);
bool putNew(JSContext* cx, HandleId name,
HandleModuleEnvironmentObject environment, HandleId localName);
bool put(JSContext* cx, HandleId name,
HandleModuleEnvironmentObject environment, HandleId localName);
size_t count() const {
return map_.count();
@ -254,7 +254,7 @@ class ModuleObject : public NativeObject
EnvironmentSlot,
NamespaceSlot,
StatusSlot,
ErrorSlot,
EvaluationErrorSlot,
HostDefinedSlot,
RequestedModulesSlot,
ImportEntriesSlot,
@ -272,8 +272,8 @@ class ModuleObject : public NativeObject
"EnvironmentSlot must match self-hosting define");
static_assert(StatusSlot == MODULE_OBJECT_STATUS_SLOT,
"StatusSlot must match self-hosting define");
static_assert(ErrorSlot == MODULE_OBJECT_ERROR_SLOT,
"ErrorSlot must match self-hosting define");
static_assert(EvaluationErrorSlot == MODULE_OBJECT_EVALUATION_ERROR_SLOT,
"EvaluationErrorSlot must match self-hosting define");
static_assert(DFSIndexSlot == MODULE_OBJECT_DFS_INDEX_SLOT,
"DFSIndexSlot must match self-hosting define");
static_assert(DFSAncestorIndexSlot == MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
@ -303,7 +303,8 @@ class ModuleObject : public NativeObject
ModuleEnvironmentObject* environment() const;
ModuleNamespaceObject* namespace_();
ModuleStatus status() const;
Value error() const;
bool hadEvaluationError() const;
Value evaluationError() const;
Value hostDefinedField() const;
ArrayObject& requestedModules() const;
ArrayObject& importEntries() const;

View File

@ -95,16 +95,16 @@
#define MODULE_OBJECT_ENVIRONMENT_SLOT 1
#define MODULE_OBJECT_STATUS_SLOT 3
#define MODULE_OBJECT_ERROR_SLOT 4
#define MODULE_OBJECT_EVALUATION_ERROR_SLOT 4
#define MODULE_OBJECT_DFS_INDEX_SLOT 13
#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 14
#define MODULE_STATUS_ERRORED 0
#define MODULE_STATUS_UNINSTANTIATED 1
#define MODULE_STATUS_INSTANTIATING 2
#define MODULE_STATUS_INSTANTIATED 3
#define MODULE_STATUS_EVALUATING 4
#define MODULE_STATUS_EVALUATED 5
#define MODULE_STATUS_UNINSTANTIATED 0
#define MODULE_STATUS_INSTANTIATING 1
#define MODULE_STATUS_INSTANTIATED 2
#define MODULE_STATUS_EVALUATING 3
#define MODULE_STATUS_EVALUATED 4
#define MODULE_STATUS_EVALUATED_ERROR 5
#define STRING_GENERICS_CHAR_AT 0
#define STRING_GENERICS_CHAR_CODE_AT 1

View File

@ -4322,7 +4322,7 @@ GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp)
}
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
if (module->status() == MODULE_STATUS_ERRORED) {
if (module->hadEvaluationError()) {
JS_ReportErrorASCII(cx, "Module environment unavailable");
return false;
}
@ -4365,7 +4365,7 @@ GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp)
}
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
if (module->status() == MODULE_STATUS_ERRORED) {
if (module->hadEvaluationError()) {
JS_ReportErrorASCII(cx, "Module environment unavailable");
return false;
}

View File

@ -1,9 +1,8 @@
// This tests that attempting to perform ModuleDeclarationInstantation a second
// time after a failure re-throws the same error.
// This tests that module instantiation can succeed when executed a second
// time after a failure.
//
// The first attempt fails becuase module 'a' is not available. The second
// attempt fails because of the previous failure (it would otherwise succeed as
// 'a' is now available).
// attempt succeeds as 'a' is now available.
//
// This test exercises the path where the previously instantiated module is
// encountered as an import.
@ -28,12 +27,9 @@ let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
threw = false;
let e2;
try {
d.declarationInstantiation();
} catch (exc) {
threw = true;
e2 = exc;
}
assertEq(threw, true);
assertEq(e1, e2);
assertEq(threw, false);

View File

@ -0,0 +1,18 @@
// Test re-instantiation module after failure.
load(libdir + "asserts.js");
load(libdir + "dummyModuleResolveHook.js");
moduleRepo["good"] = parseModule(`export let x`);
moduleRepo["y1"] = parseModule(`export let y`);
moduleRepo["y2"] = parseModule(`export let y`);
moduleRepo["bad"] = parseModule(`export* from "y1"; export* from "y2";`);
moduleRepo["a"] = parseModule(`import* as ns from "good"; import {y} from "bad";`);
let b = moduleRepo["b"] = parseModule(`import "a";`);
let c = moduleRepo["c"] = parseModule(`import "a";`);
assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError);
assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError);

View File

@ -0,0 +1,8 @@
if (!('stackTest' in this))
quit();
let a = parseModule(`throw new Error`);
a.declarationInstantiation();
stackTest(function() {
a.evaluation();
});

View File

@ -0,0 +1,15 @@
load(libdir + "asserts.js");
load(libdir + "dummyModuleResolveHook.js");
moduleRepo["a"] = parseModule(`throw undefined`);
let b = moduleRepo["b"] = parseModule(`import "a";`);
let c = moduleRepo["c"] = parseModule(`import "a";`);
b.declarationInstantiation();
c.declarationInstantiation();
let count = 0;
try { b.evaluation() } catch (e) { count++; }
try { c.evaluation() } catch (e) { count++; }
assertEq(count, 2);

View File

@ -0,0 +1,18 @@
// Test re-instantiation module after failure.
load(libdir + "asserts.js");
load(libdir + "dummyModuleResolveHook.js");
moduleRepo["good"] = parseModule(`export let x`);
moduleRepo["y1"] = parseModule(`export let y`);
moduleRepo["y2"] = parseModule(`export let y`);
moduleRepo["bad"] = parseModule(`export* from "y1"; export* from "y2";`);
moduleRepo["a"] = parseModule(`import {x} from "good"; import {y} from "bad";`);
let b = moduleRepo["b"] = parseModule(`import "a";`);
let c = moduleRepo["c"] = parseModule(`import "a";`);
assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError);
assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError);

View File

@ -583,7 +583,6 @@ MSG_DEF(JSMSG_MISSING_IMPORT, 0, JSEXN_SYNTAXERR, "import not found")
MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 0, JSEXN_SYNTAXERR, "ambiguous import")
MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found for namespace")
MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
MSG_DEF(JSMSG_MODULE_INSTANTIATE_FAILED, 0, JSEXN_INTERNALERR, "attempt to re-instantiate module after failure")
MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
// Promise

View File

@ -4948,20 +4948,6 @@ JS::GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue value,
*columnNumber = requested.columnNumber();
}
JS_PUBLIC_API(bool)
JS::IsModuleErrored(JSObject* moduleArg)
{
AssertHeapIsIdle();
return moduleArg->as<ModuleObject>().status() == MODULE_STATUS_ERRORED;
}
JS_PUBLIC_API(JS::Value)
JS::GetModuleError(JSObject* moduleArg)
{
AssertHeapIsIdle();
return moduleArg->as<ModuleObject>().error();
}
JS_PUBLIC_API(JSObject*)
JS_New(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
{

View File

@ -4267,12 +4267,6 @@ extern JS_PUBLIC_API(void)
GetRequestedModuleSourcePos(JSContext* cx, JS::HandleValue requestedModuleObject,
uint32_t* lineNumber, uint32_t* columnNumber);
extern JS_PUBLIC_API(bool)
IsModuleErrored(JSObject* moduleRecord);
extern JS_PUBLIC_API(JS::Value)
GetModuleError(JSObject* moduleRecord);
} /* namespace JS */
extern JS_PUBLIC_API(bool)

View File

@ -493,10 +493,8 @@ ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importNam
RootedId importNameId(cx, AtomToId(importName));
RootedId localNameId(cx, AtomToId(localName));
RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
if (!importBindings().putNew(cx, importNameId, env, localNameId)) {
ReportOutOfMemory(cx);
if (!importBindings().put(cx, importNameId, env, localNameId))
return false;
}
return true;
}

View File

@ -2072,7 +2072,6 @@ intrinsic_CreateNamespaceBinding(JSContext* cx, unsigned argc, Value* vp)
// the slot directly.
RootedShape shape(cx, environment->lookup(cx, name));
MOZ_ASSERT(shape);
MOZ_ASSERT(environment->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL));
environment->setSlot(shape->slot(), args[2]);
args.rval().setUndefined();
return true;

View File

@ -794,7 +794,7 @@ js::ReshapeForAllocKind(JSContext* cx, Shape* shape, TaggedProto proto,
for (unsigned i = 0; i < ids.length(); i++) {
id = ids[i];
Rooted<UnownedBaseShape*> nbase(cx, GetBaseShapeForNewShape(cx, newShape, id));
UnownedBaseShape* nbase = GetBaseShapeForNewShape(cx, newShape, id);
if (!nbase)
return nullptr;
@ -949,11 +949,6 @@ NativeObject::putDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id
} else {
// Updating the last property in a non-dictionary-mode object. Find an
// alternate shared child of the last property's previous shape.
StackBaseShape base(obj->lastProperty()->base());
UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return nullptr;
MOZ_ASSERT(shape == obj->lastProperty());
@ -1053,11 +1048,6 @@ NativeObject::putAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleI
} else {
// Updating the last property in a non-dictionary-mode object. Find an
// alternate shared child of the last property's previous shape.
StackBaseShape base(obj->lastProperty()->base());
UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return nullptr;
MOZ_ASSERT(shape == obj->lastProperty());

View File

@ -1748,7 +1748,7 @@ RenderExport(WasmRenderContext& c, AstExport& export_,
break;
}
case DefinitionKind::Global: {
if (!c.buffer.append("global"))
if (!c.buffer.append("global "))
return false;
if (!RenderRef(c, export_.ref()))
return false;

View File

@ -122,6 +122,7 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsStyleContext* aContext)
, mIsOffset(false)
, mHFlip(false)
, mVFlip(false)
, mPositionedOffset(0)
, mAnchorType(MenuPopupAnchorType_Node)
{
// the preference name is backwards here. True means that the 'top' level is
@ -576,7 +577,8 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
}
// finally, if the popup just opened, send a popupshown event
if (mIsOpenChanged) {
bool openChanged = mIsOpenChanged;
if (openChanged) {
mIsOpenChanged = false;
// Make sure the current selection in a menulist is visible.
@ -605,7 +607,7 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
if (needCallback && !mReflowCallbackData.mPosted) {
pc->PresShell()->PostReflowCallback(this);
mReflowCallbackData.MarkPosted(aAnchor, aSizedToPopup);
mReflowCallbackData.MarkPosted(aAnchor, aSizedToPopup, openChanged);
}
}
@ -717,6 +719,7 @@ nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent,
mVFlip = false;
mHFlip = false;
mAlignmentOffset = 0;
mPositionedOffset = 0;
mAnchorType = aAnchorType;
@ -863,6 +866,7 @@ nsMenuPopupFrame::InitializePopupAtScreen(nsIContent* aTriggerContent,
mIsContextMenu = aIsContextMenu;
mAdjustOffsetForContextMenu = aIsContextMenu;
mAnchorType = MenuPopupAnchorType_Point;
mPositionedOffset = 0;
}
void
@ -887,6 +891,7 @@ nsMenuPopupFrame::InitializePopupWithAnchorAlign(nsIContent* aAnchorContent,
mPopupState = ePopupShowing;
mAdjustOffsetForContextMenu = false;
mFlip = FlipType_Default;
mPositionedOffset = 0;
// this popup opening function is provided for backwards compatibility
// only. It accepts either coordinates or an anchor and alignment value
@ -1158,17 +1163,24 @@ nsMenuPopupFrame::AdjustPositionForAnchorAlign(nsRect& anchorRect,
MOZ_ASSERT(popupAlign == POPUPALIGNMENT_TOPLEFT ||
popupAlign == POPUPALIGNMENT_TOPRIGHT);
nsIFrame* selectedItemFrame = GetSelectedItemForAlignment();
if (selectedItemFrame) {
int32_t scrolly = 0;
nsIScrollableFrame *scrollframe = do_QueryFrame(nsBox::GetChildXULBox(this));
if (scrollframe) {
scrolly = scrollframe->GetScrollPosition().y;
}
// Only adjust the popup if it just opened, otherwise the popup will move around if its gets
// resized or the selection changed. Cache the value in mPositionedOffset and use that instead
// for any future calculations.
if (mIsOpenChanged || mReflowCallbackData.mIsOpenChanged) {
nsIFrame* selectedItemFrame = GetSelectedItemForAlignment();
if (selectedItemFrame) {
int32_t scrolly = 0;
nsIScrollableFrame *scrollframe = do_QueryFrame(nsBox::GetChildXULBox(this));
if (scrollframe) {
scrolly = scrollframe->GetScrollPosition().y;
}
pnt.y -= originalAnchorRect.height + selectedItemFrame->GetRect().y - scrolly;
mPositionedOffset = originalAnchorRect.height + selectedItemFrame->GetRect().y - scrolly;
}
}
}
pnt.y -= mPositionedOffset;
}
// Flipping horizontally is allowed as long as the popup is above or below
// the anchor. This will happen if both the anchor and alignment are top or
@ -1355,6 +1367,7 @@ nsMenuPopupFrame::FlipOrResize(nscoord& aScreenPoint, nscoord aSize,
if (popupSize <= 0 || aSize < popupSize) {
popupSize = aSize;
}
return std::min(popupSize, aScreenEnd - aScreenPoint);
}

View File

@ -613,19 +613,22 @@ protected:
mAnchor(nullptr),
mSizedToPopup(false)
{}
void MarkPosted(nsIFrame* aAnchor, bool aSizedToPopup) {
void MarkPosted(nsIFrame* aAnchor, bool aSizedToPopup, bool aIsOpenChanged) {
mPosted = true;
mAnchor = aAnchor;
mSizedToPopup = aSizedToPopup;
mIsOpenChanged = aIsOpenChanged;
}
void Clear() {
mPosted = false;
mAnchor = nullptr;
mSizedToPopup = false;
mIsOpenChanged = false;
}
bool mPosted;
nsIFrame* mAnchor;
bool mSizedToPopup;
bool mIsOpenChanged;
};
ReflowCallbackData mReflowCallbackData;
@ -650,6 +653,11 @@ protected:
bool mHFlip;
bool mVFlip;
// When POPUPPOSITION_SELECTION is used, this indicates the vertical offset that the
// original selected item was. This needs to be used in case the popup gets changed
// so that we can keep the popup at the same vertical offset.
nscoord mPositionedOffset;
// How the popup is anchored.
MenuPopupAnchorType mAnchorType;

View File

@ -7,9 +7,9 @@
#include "HttpLog.h"
#include "nsHttpBasicAuth.h"
#include "plbase64.h"
#include "plstr.h"
#include "nsString.h"
#include "mozilla/Base64.h"
namespace mozilla {
namespace net {
@ -87,21 +87,21 @@ nsHttpBasicAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
bool isBasicAuth = !PL_strncasecmp(challenge, "basic", 5);
NS_ENSURE_TRUE(isBasicAuth, NS_ERROR_UNEXPECTED);
// we work with ASCII around here
// we work with UTF-8 around here
nsAutoCString userpass;
LossyCopyUTF16toASCII(user, userpass);
CopyUTF16toUTF8(user, userpass);
userpass.Append(':'); // always send a ':' (see bug 129565)
if (password)
LossyAppendUTF16toASCII(password, userpass);
if (password) {
AppendUTF16toUTF8(password, userpass);
}
// plbase64.h provides this worst-case output buffer size calculation.
// use calloc, since PL_Base64Encode does not null terminate.
*creds = (char *) calloc(6 + ((userpass.Length() + 2)/3)*4 + 1, 1);
if (!*creds)
return NS_ERROR_OUT_OF_MEMORY;
nsAutoCString authString;
nsresult rv = Base64Encode(userpass, authString);
NS_ENSURE_SUCCESS(rv, rv);
memcpy(*creds, "Basic ", 6);
PL_Base64Encode(userpass.get(), userpass.Length(), *creds + 6);
authString.InsertLiteral("Basic ", 0);
*creds = ToNewCString(authString);
return NS_OK;
}

View File

@ -23,6 +23,7 @@ const FLAG_BOGUS_USER = 1 << 2;
const FLAG_PREVIOUS_FAILED = 1 << 3;
const CROSS_ORIGIN = 1 << 4;
const FLAG_NO_REALM = 1 << 5;
const FLAG_NON_ASCII_USER_PASSWORD = 1 << 6;
const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2;
const nsIAuthInformation = Components.interfaces.nsIAuthInformation;
@ -75,14 +76,19 @@ AuthPrompt1.prototype = {
if (this.flags & FLAG_RETURN_FALSE)
return false;
if (this.flags & FLAG_BOGUS_USER)
if (this.flags & FLAG_BOGUS_USER) {
this.user = "foo\nbar";
} else if (this.flags & FLAG_NON_ASCII_USER_PASSWORD) {
this.user = "é";
}
user.value = this.user;
if (this.flags & FLAG_WRONG_PASSWORD) {
pw.value = this.pass + ".wrong";
// Now clear the flag to avoid an infinite loop
this.flags &= ~FLAG_WRONG_PASSWORD;
} else if (this.flags & FLAG_NON_ASCII_USER_PASSWORD) {
pw.value = "é";
} else {
pw.value = this.pass;
}
@ -140,7 +146,7 @@ AuthPrompt2.prototype = {
if (isNTLM)
expectedFlags |= nsIAuthInformation.NEED_DOMAIN;
const kAllKnownFlags = 63; // Don't fail test for newly added flags
const kAllKnownFlags = 127; // Don't fail test for newly added flags
do_check_eq(expectedFlags, authInfo.flags & kAllKnownFlags);
var expectedScheme = isNTLM ? "ntlm" : isDigest ? "digest" : "basic";
@ -157,8 +163,11 @@ AuthPrompt2.prototype = {
return false;
}
if (this.flags & FLAG_BOGUS_USER)
if (this.flags & FLAG_BOGUS_USER) {
this.user = "foo\nbar";
} else if (this.flags & FLAG_NON_ASCII_USER_PASSWORD) {
this.user = "é";
}
authInfo.username = this.user;
if (this.flags & FLAG_WRONG_PASSWORD) {
@ -166,6 +175,8 @@ AuthPrompt2.prototype = {
this.flags |= FLAG_PREVIOUS_FAILED;
// Now clear the flag to avoid an infinite loop
this.flags &= ~FLAG_WRONG_PASSWORD;
} else if (this.flags & FLAG_NON_ASCII_USER_PASSWORD) {
authInfo.password = "é";
} else {
authInfo.password = this.pass;
this.flags &= ~FLAG_PREVIOUS_FAILED;
@ -305,7 +316,7 @@ function makeChan(url, loadingUrl) {
var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1,
test_prompt1CrossOrigin, test_prompt2CrossOrigin,
test_returnfalse2, test_wrongpw2, test_prompt2, test_ntlm,
test_basicrealm, test_digest_noauth, test_digest,
test_basicrealm, test_nonascii, test_digest_noauth, test_digest,
test_digest_bogus_user, test_short_digest, test_large_realm,
test_large_domain];
@ -319,6 +330,7 @@ function run_test() {
httpserv.registerPathHandler("/auth", authHandler);
httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple);
httpserv.registerPathHandler("/auth/realm", authRealm);
httpserv.registerPathHandler("/auth/non_ascii", authNonascii);
httpserv.registerPathHandler("/auth/digest", authDigest);
httpserv.registerPathHandler("/auth/short_digest", authShortDigest);
httpserv.registerPathHandler("/largeRealm", largeRealm);
@ -438,6 +450,16 @@ function test_basicrealm() {
do_test_pending();
}
function test_nonascii() {
var chan = makeChan(URL + "/auth/non_ascii", URL);
chan.notificationCallbacks = new Requestor(FLAG_NON_ASCII_USER_PASSWORD, 2);
listener.expectedCode = 200; // OK
chan.asyncOpen2(listener);
do_test_pending();
}
function test_digest_noauth() {
var chan = makeChan(URL + "/auth/digest", URL);
@ -527,6 +549,32 @@ function authRealm(metadata, response) {
response.bodyOutputStream.write(body, body.length);
}
// /auth/nonAscii
function authNonascii(metadata, response) {
// btoa("é:é"), but that function is not available here
var expectedHeader = "Basic w6k6w6k=";
var body;
if (metadata.hasHeader("Authorization") &&
metadata.getHeader("Authorization") == expectedHeader)
{
response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
body = "success";
}
else
{
// didn't know é:é, failure
response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
body = "failed";
}
response.bodyOutputStream.write(body, body.length);
}
//
// Digest functions
//

View File

@ -1 +1 @@
NSPR_4_18_BETA2
NSPR_4_18_BETA3

View File

@ -10,4 +10,3 @@
*/
#error "Do not include this header file."

View File

@ -1064,11 +1064,6 @@ void PR_HPUX10xInit(shl_t handle, int loading)
void _PR_Fini(void)
{
/* We disable the cleanup code on Mac OSX, see bug 1399746.
* The .dylib containing NSPR can get unloaded, and _PR_Fini called,
* and other code calling NSPR functions can get executed afterwards.
*/
#ifndef DARWIN
void *thred;
int rv;
@ -1100,7 +1095,6 @@ void _PR_Fini(void)
pt_book.keyCreated = PR_FALSE;
/* TODO: free other resources used by NSPR */
/* _pr_initialized = PR_FALSE; */
#endif
} /* _PR_Fini */
PR_IMPLEMENT(PRStatus) PR_Cleanup(void)

View File

@ -4,3 +4,4 @@ add_WOW64_flags_to_allowed_registry_read_flags.patch
change_USER_NON_ADMIN_to_blacklist.patch
consult_PermissionsService_for_file_access.patch
allow_flash_temporary_files.patch
use_STARTF_FORCEOFFFEEDBACK_flag.patch

View File

@ -0,0 +1,32 @@
# HG changeset patch
# User Bob Owen <bobowencode@gmail.com>
# Date 1512580728 0
# Wed Dec 06 17:18:48 2017 +0000
# Node ID e56684aa96bd963d4b94ed69b99a0359fa0479ae
# Parent 785572419acc82b2cbdcd2e24ca59fdbf5d7255f
Use STARTF_FORCEOFFFEEDBACK flag when starting Windows child processes to prevent app starting cursor. r=jimm
diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.cc b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
--- a/security/sandbox/chromium/sandbox/win/src/broker_services.cc
+++ b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
@@ -346,16 +346,20 @@ ResultCode BrokerServicesBase::SpawnTarg
base::win::ScopedHandle job;
result = policy_base->MakeJobObject(&job);
if (SBOX_ALL_OK != result)
return result;
// Initialize the startup information from the policy.
base::win::StartupInformation startup_info;
+
+ // We don't want any child processes causing the IDC_APPSTARTING cursor.
+ startup_info.startup_info()->dwFlags |= STARTF_FORCEOFFFEEDBACK;
+
// The liftime of |mitigations|, |inherit_handle_list| and
// |child_process_creation| have to be at least as long as
// |startup_info| because |UpdateProcThreadAttribute| requires that
// its |lpValue| parameter persist until |DeleteProcThreadAttributeList| is
// called; StartupInformation's destructor makes such a call.
DWORD64 mitigations;
std::vector<HANDLE> inherited_handle_list;
DWORD child_process_creation = PROCESS_CREATION_CHILD_PROCESS_RESTRICTED;

View File

@ -351,6 +351,10 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
// Initialize the startup information from the policy.
base::win::StartupInformation startup_info;
// We don't want any child processes causing the IDC_APPSTARTING cursor.
startup_info.startup_info()->dwFlags |= STARTF_FORCEOFFFEEDBACK;
// The liftime of |mitigations|, |inherit_handle_list| and
// |child_process_creation| have to be at least as long as
// |startup_info| because |UpdateProcThreadAttribute| requires that

View File

@ -137,7 +137,10 @@ test-verify-wpt:
# do not run on ccov; see also the enable_code_coverage transform
linux64-ccov/.*: []
default: built-projects
tier: 3
tier:
by-test-platform:
windows10-64-asan.*: 3
default: 2
mozharness:
extra-options:
- --verify

View File

@ -46,41 +46,47 @@ function starttest() {
},
function () {
check = true;
is(check, true, "waitForClipboard should work");
manipulateElements();
},
function () {
check = false;
is(check, false, "waitForClipboard should work");
manipulateElements();
}
);
is(check, true, "waitForClipboard should work");
//use helper functions
var div1 = createEl('div', {'id': 'somediv', 'display': 'block'}, "I am a div");
document.body.appendChild(div1);
var divObj = this.getElement('somediv');
is(divObj, div1, 'createEl did not create element as expected');
is($('somediv'), divObj, '$ helper did not get element as expected');
is(computedStyle(divObj, 'display'), 'block', 'computedStyle did not get right display value');
document.body.removeChild(div1);
/* note: expectChildProcessCrash is not being tested here, as it causes wildly variable
* run times. It is currently being tested in:
* dom/plugins/test/test_hanging.html and dom/plugins/test/test_crashing.html
*/
function manipulateElements()
{
var div1 = createEl('div', {'id': 'somediv', 'display': 'block'}, "I am a div");
document.body.appendChild(div1);
var divObj = this.getElement('somediv');
is(divObj, div1, 'createEl did not create element as expected');
is($('somediv'), divObj, '$ helper did not get element as expected');
is(computedStyle(divObj, 'display'), 'block', 'computedStyle did not get right display value');
document.body.removeChild(div1);
//note: this also adds a short wait period
SimpleTest.executeSoon(
function () {
//finish() calls a slew of SimpleTest functions
SimpleTest.finish();
//call this after finish so we can make sure it works and doesn't hang our process
var endTime = new Date();
info("Profile::SimpleTestRunTime: " + (endTime-startTime));
//expect and throw exception here. Otherwise, any code that follows the throw call will never be executed
SimpleTest.expectUncaughtException();
//make sure we catch this error
throw "i am an uncaught exception"
}
);
/* note: expectChildProcessCrash is not being tested here, as it causes wildly variable
* run times. It is currently being tested in:
* dom/plugins/test/test_hanging.html and dom/plugins/test/test_crashing.html
*/
//note: this also adds a short wait period
SimpleTest.executeSoon(
function () {
//finish() calls a slew of SimpleTest functions
SimpleTest.finish();
//call this after finish so we can make sure it works and doesn't hang our process
var endTime = new Date();
info("Profile::SimpleTestRunTime: " + (endTime-startTime));
//expect and throw exception here. Otherwise, any code that follows the throw call will never be executed
SimpleTest.expectUncaughtException();
//make sure we catch this error
throw "i am an uncaught exception"
}
);
}
}
);
};

View File

@ -926,8 +926,6 @@ SimpleTest.waitForFocus = function (callback, targetWindow, expectBlankPage) {
}
};
SimpleTest.waitForClipboard_polls = 0;
/*
* Polls the clipboard waiting for the expected value. A known value different than
* the expected value is put on the clipboard first (and also polled for) so we
@ -937,7 +935,7 @@ SimpleTest.waitForClipboard_polls = 0;
*
* @param aExpectedStringOrValidatorFn
* The string value that is expected to be on the clipboard or a
* validator function getting cripboard data and returning a bool.
* validator function getting expected clipboard data and returning a bool.
* @param aSetupFn
* A function responsible for setting the clipboard to the expected value,
* called after the known value setting succeeds.
@ -956,19 +954,25 @@ SimpleTest.waitForClipboard_polls = 0;
* aExpectedStringOrValidatorFn must be null, as it won't be used.
* Defaults to false.
*/
SimpleTest.__waitForClipboardMonotonicCounter = 0;
SimpleTest.__defineGetter__("_waitForClipboardMonotonicCounter", function () {
return SimpleTest.__waitForClipboardMonotonicCounter++;
});
SimpleTest.waitForClipboard = function(aExpectedStringOrValidatorFn, aSetupFn,
aSuccessFn, aFailureFn, aFlavor, aTimeout, aExpectFailure) {
var requestedFlavor = aFlavor || "text/unicode";
let promise = SimpleTest.promiseClipboardChange(aExpectedStringOrValidatorFn, aSetupFn,
aFlavor, aTimeout, aExpectFailure);
promise.then(aSuccessFn).catch(aFailureFn);
}
/**
* Promise-oriented version of waitForClipboard.
*/
SimpleTest.promiseClipboardChange = async function(aExpectedStringOrValidatorFn, aSetupFn,
aFlavor, aTimeout, aExpectFailure) {
let requestedFlavor = aFlavor || "text/unicode";
// The known value we put on the clipboard before running aSetupFn
var initialVal = SimpleTest._waitForClipboardMonotonicCounter +
"-waitForClipboard-known-value";
let initialVal = "waitForClipboard-known-value-" + Math.random();
let preExpectedVal = initialVal;
var inputValidatorFn;
let inputValidatorFn;
if (aExpectFailure) {
// If we expect failure, the aExpectedStringOrValidatorFn should be null
if (aExpectedStringOrValidatorFn !== null) {
@ -985,55 +989,44 @@ SimpleTest.waitForClipboard = function(aExpectedStringOrValidatorFn, aSetupFn,
: aExpectedStringOrValidatorFn;
}
var maxPolls = aTimeout ? aTimeout / 100 : 50;
let maxPolls = aTimeout ? aTimeout / 100 : 50;
// reset for the next use
function reset() {
SimpleTest.waitForClipboard_polls = 0;
}
async function putAndVerify(operationFn, validatorFn, flavor) {
operationFn();
var lastValue;
function wait(validatorFn, successFn, failureFn, flavor) {
if (SimpleTest.waitForClipboard_polls == 0) {
lastValue = undefined;
let data;
for (let i = 0; i < maxPolls; i++) {
data = SpecialPowers.getClipboardData(flavor);
if (validatorFn(data)) {
// Don't show the success message when waiting for preExpectedVal
if (preExpectedVal) {
preExpectedVal = null;
} else {
SimpleTest.ok(!aExpectFailure, "Clipboard has the given value: '" + data + "'");
}
return data;
}
// Wait 100ms and check again.
await new Promise(resolve => { SimpleTest._originalSetTimeout.apply(window, [resolve, 100]); });
}
if (++SimpleTest.waitForClipboard_polls > maxPolls) {
// Log the failure.
SimpleTest.ok(aExpectFailure, "Timed out while polling clipboard for pasted data");
dump("Got this value: " + lastValue);
reset();
failureFn();
return;
}
var data = SpecialPowers.getClipboardData(flavor);
if (validatorFn(data)) {
// Don't show the success message when waiting for preExpectedVal
if (preExpectedVal)
preExpectedVal = null;
else
SimpleTest.ok(!aExpectFailure, "Clipboard has the given value");
reset();
successFn();
} else {
lastValue = data;
SimpleTest._originalSetTimeout.apply(window, [function() { return wait(validatorFn, successFn, failureFn, flavor); }, 100]);
SimpleTest.ok(aExpectFailure, "Timed out while polling clipboard for pasted data, got: " + data);
if (!aExpectFailure) {
throw "failed";
}
}
// First we wait for a known value different from the expected one.
var preExpectedVal = initialVal;
SpecialPowers.clipboardCopyString(preExpectedVal);
wait(function(aData) { return aData == preExpectedVal; },
function() {
// Call the original setup fn
aSetupFn();
wait(inputValidatorFn, aSuccessFn, aFailureFn, requestedFlavor);
}, aFailureFn, "text/unicode");
await putAndVerify(function() { SpecialPowers.clipboardCopyString(preExpectedVal); },
function(aData) { return aData == preExpectedVal; },
"text/unicode");
return await putAndVerify(aSetupFn, inputValidatorFn, requestedFlavor);
}
/**
* Wait for a condition for a while (actually up to 3s here).
*
@ -1082,7 +1075,7 @@ SimpleTest.executeSoon = function(aFunc) {
return SpecialPowers.executeSoon(aFunc, window);
}
setTimeout(aFunc, 0);
return null; // Avoid warning.
return null; // Avoid warning.
};
SimpleTest.registerCleanupFunction = function(aFunc) {

View File

@ -540,14 +540,18 @@ class DeviceManagerADB(DeviceManager):
localDir = '/'.join(localDir.rstrip('/').split('/')[:-1])
cmd = ["pull", remoteDir, localDir]
proc = self._runCmd(cmd)
if proc.returncode != 0:
# Raise a DMError when the device is missing, but not when the
# directory is empty or missing.
output = ''.join(proc.output)
if ("no devices/emulators found" in output or
("pulled" not in output and
"does not exist" not in output)):
raise DMError("getDirectory() failed to pull %s: %s" %
(remoteDir, proc.output))
if copyRequired:
try:
dir_util.copy_tree(localDir, originalLocal)
mozfile.remove(tempParent)
except:
self._logger.error("getDirectory() failed after %s" % str(cmd))
self._logger.error("rc=%d out=%s" % (proc.returncode, str(proc.output)))
raise
dir_util.copy_tree(localDir, originalLocal)
mozfile.remove(tempParent)
def validateFile(self, remoteFile, localFile):
md5Remote = self._getRemoteHash(remoteFile)

View File

@ -286741,6 +286741,11 @@
{}
]
],
"workers/support/iframe_sw_dataUrl.html": [
[
{}
]
],
"workers/support/name-as-accidental-global.js": [
[
{}
@ -352264,6 +352269,12 @@
{}
]
],
"workers/SharedWorker_dataUrl.html": [
[
"/workers/SharedWorker_dataUrl.html",
{}
]
],
"workers/WorkerGlobalScope_ErrorEvent_colno.htm": [
[
"/workers/WorkerGlobalScope_ErrorEvent_colno.htm",
@ -581861,6 +581872,10 @@
"d74620a63e7d911ef60b995cabf6b360c2c46a4f",
"testharness"
],
"workers/SharedWorker_dataUrl.html": [
"f65d3caef416775c970a554448cebdf02580428f",
"testharness"
],
"workers/WorkerGlobalScope_ErrorEvent_colno.htm": [
"8ceb41543f928c918010000d638099faeb674980",
"testharness"
@ -582865,6 +582880,10 @@
"b0e679dd7720701364abeaca6870d94db5d7ee74",
"support"
],
"workers/support/iframe_sw_dataUrl.html": [
"2cd66112b612e7b861af00af5ccc26cc7c5a76f8",
"support"
],
"workers/support/name-as-accidental-global.js": [
"530670268fae610b60066773ee475743b8498b53",
"support"

View File

@ -0,0 +1,3 @@
[instantiation-error-1.html]
[Test that missing exports lead to SyntaxError events on window and load events on script, and that exceptions are remembered]
expected: FAIL

View File

@ -0,0 +1,3 @@
[instantiation-error-2.html]
[Test that missing exports lead to SyntaxError events on window and load events on script, and that exceptions are remembered]
expected: FAIL

View File

@ -1,3 +1,3 @@
[instantiation-error-3.html]
type: testharness
disabled: true
[Test that unresolvable cycles lead to SyntaxError events on window and load events on script, and that exceptions are remembered]
expected: FAIL

View File

@ -0,0 +1,3 @@
[instantiation-error-4.html]
[Test that loading a graph in which a module is already errored results in that module's error.]
expected: FAIL

View File

@ -0,0 +1,3 @@
[instantiation-error-5.html]
[Test that loading a graph in which a module is already errored results in that module's error.]
expected: FAIL

View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<title>Shared Worker: Data URL cross-origin checks</title>
<script src="/common/get-host-info.sub.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
</body>
<script>
function dirname(path) {
return path.replace(/\/[^\/]*$/, '/');
}
promise_test(t => {
return new Promise(function(resolve) {
let count = 0;
onmessage = e => {
assert_equals(e.data, 1);
if (++count == 2) {
resolve(true);
}
};
let iframeA = document.createElement('iframe');
document.body.appendChild(iframeA);
iframeA.src = get_host_info().HTTP_REMOTE_ORIGIN +
dirname(location.pathname) +
"support/iframe_sw_dataUrl.html";
let iframeB = document.createElement('iframe');
document.body.appendChild(iframeB);
iframeB.src = get_host_info().HTTPS_REMOTE_ORIGIN +
dirname(location.pathname) +
"support/iframe_sw_dataUrl.html";
});
}, 'Data URL not shared by cross-origin SharedWorkers');
promise_test(t => {
return new Promise(function(resolve) {
let count = 0;
onmessage = e => {
assert_equals(e.data, ++count);
if (count == 2) {
resolve(true);
}
};
let iframeA = document.createElement('iframe');
document.body.appendChild(iframeA);
iframeA.src = get_host_info().HTTP_ORIGIN +
dirname(location.pathname) +
"support/iframe_sw_dataUrl.html";
let iframeB = document.createElement('iframe');
document.body.appendChild(iframeB);
iframeB.src = get_host_info().HTTP_ORIGIN +
dirname(location.pathname) +
"support/iframe_sw_dataUrl.html";
});
}, 'Data URLs shared by same-origin SharedWorkers');
</script>
</html>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<title>Iframe for Shared Worker: Data URL cross-origin checks</title>
<body>
<script>
let worker = new SharedWorker('data:text/javascript,let conns=0; onconnect = e => { e.ports[0].postMessage(++conns); }');
worker.port.onmessage = e => {
parent.postMessage(e.data, '*');
}
</script>
</body>
</html>

View File

@ -50,6 +50,19 @@ function popupShown()
ok(isWithinHalfPixel(menulistlabel.getBoundingClientRect().top,
mitemlabel.getBoundingClientRect().top),
"Labels vertically aligned for index " + index);
// Store the current value and reset it afterwards.
let current = menulist.selectedIndex;
// Cycle through the items to ensure that the popup doesn't move when the selection changes.
for (let i = 0; i < menulist.itemCount; i++) {
menulist.selectedIndex = i;
let newpopuprect = menulist.menupopup.getBoundingClientRect();
is(newpopuprect.x, popuprect.x, "Popup remained horizontally for index " + i + " starting at " + current);
is(newpopuprect.y, popuprect.y, "Popup remained vertically for index " + i + " starting at " + current);
}
menulist.selectedIndex = current;
}
else {
let marginTop = parseFloat(getComputedStyle(menulist.menupopup).marginTop);
@ -73,14 +86,16 @@ function popupHidden()
]]>
</script>
<hbox align="center" pack="center" style="margin-top: 100px;">
<hbox align="center" pack="center" style="margin-top: 140px;">
<menulist id="menulist" onpopupshown="popupShown();" onpopuphidden="popupHidden();">
<menupopup>
<menupopup style="max-height: 90px;">
<menuitem label="One"/>
<menuitem label="Two"/>
<menuitem label="Three"/>
<menuitem label="Four"/>
<menuitem label="Five"/>
<menuitem label="Six"/>
<menuitem label="Seven"/>
</menupopup>
</menulist>
</hbox>

View File

@ -254,36 +254,41 @@ nsAppShell::Init()
{
LSPAnnotate();
mLastNativeEventScheduled = TimeStamp::NowLoRes();
mozilla::ipc::windows::InitUIThread();
sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(kTaskbarButtonEventId);
NS_ASSERTION(sTaskbarButtonCreatedMsg, "Could not register taskbar button creation message");
WNDCLASSW wc;
HINSTANCE module = GetModuleHandle(nullptr);
// The hidden message window is used for interrupting the processing of native
// events, so that we can process gecko events. Therefore, we only need it if
// we are processing native events.
if (XRE_UseNativeEventProcessing()) {
mLastNativeEventScheduled = TimeStamp::NowLoRes();
const wchar_t *const kWindowClass = L"nsAppShell:EventWindowClass";
if (!GetClassInfoW(module, kWindowClass, &wc)) {
wc.style = 0;
wc.lpfnWndProc = EventWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = module;
wc.hIcon = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = (HBRUSH) nullptr;
wc.lpszMenuName = (LPCWSTR) nullptr;
wc.lpszClassName = kWindowClass;
RegisterClassW(&wc);
WNDCLASSW wc;
HINSTANCE module = GetModuleHandle(nullptr);
const wchar_t *const kWindowClass = L"nsAppShell:EventWindowClass";
if (!GetClassInfoW(module, kWindowClass, &wc)) {
wc.style = 0;
wc.lpfnWndProc = EventWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = module;
wc.hIcon = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = (HBRUSH) nullptr;
wc.lpszMenuName = (LPCWSTR) nullptr;
wc.lpszClassName = kWindowClass;
RegisterClassW(&wc);
}
mEventWnd = CreateWindowW(kWindowClass, L"nsAppShell:EventWindow",
0, 0, 0, 10, 10, HWND_MESSAGE, nullptr, module,
nullptr);
NS_ENSURE_STATE(mEventWnd);
}
mEventWnd = CreateWindowW(kWindowClass, L"nsAppShell:EventWindow",
0, 0, 0, 10, 10, HWND_MESSAGE, nullptr, module,
nullptr);
NS_ENSURE_STATE(mEventWnd);
if (XRE_IsParentProcess()) {
ScreenManager& screenManager = ScreenManager::GetSingleton();
if (gfxPlatform::IsHeadless()) {
@ -383,6 +388,9 @@ nsAppShell::DoProcessMoreGeckoEvents()
void
nsAppShell::ScheduleNativeEventCallback()
{
MOZ_ASSERT(mEventWnd,
"We should have created mEventWnd in Init, if this is called.");
// Post a message to the hidden message window
NS_ADDREF_THIS(); // will be released when the event is processed
{