diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/tests/mochitest/general | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/tests/mochitest/general')
114 files changed, 10148 insertions, 0 deletions
diff --git a/dom/tests/mochitest/general/497633.html b/dom/tests/mochitest/general/497633.html new file mode 100644 index 000000000..ab65fb7e3 --- /dev/null +++ b/dom/tests/mochitest/general/497633.html @@ -0,0 +1,24 @@ +<html> +<head> +</head> +<body> +<script> +var t = 0; +function doe() { +setTimeout(function() { + if (t == 1) { + window.close(); + window.opener.done(); + } + else { + window.frames[0].location.reload(); + t++; + } +}, 300); +} +</script> + +<iframe src="data:application/xhtml+xml;charset=utf-8,%3Chtml%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml%22%3E%0A%0A%3Ciframe%2F%3E%0A%3Cframeset%20onblur%3D%22window.frameElement.parentNode.removeChild(window.frameElement)%22%20id%3D%22frame%22%2F%3E%0A%0A%3Cscript%3E%0Afunction%20doe(i)%7B%0Adocument.getElementById('frame').focus()%3B%0Adocument.getElementsByTagName('*')%5B1%5D.focus()%3B%0A%7D%0Atop.opener.SimpleTest.waitForFocus(function%20()%20setTimeout(doe%2C%20100)%2C%20top)%3B%0A%3C%2Fscript%3E%0A%3C%2Fhtml%3E" onload="doe()" id="content"></iframe> +</body> +</html> + diff --git a/dom/tests/mochitest/general/chrome.ini b/dom/tests/mochitest/general/chrome.ini new file mode 100644 index 000000000..b46b49c8c --- /dev/null +++ b/dom/tests/mochitest/general/chrome.ini @@ -0,0 +1,9 @@ +[DEFAULT] +skip-if = os == 'android' + +[test_idleapi_permissions.html] +[test_innerScreen.xul] +[test_offsets.css] +[test_offsets.js] +[test_offsets.xul] +[test_spacetopagedown.html] diff --git a/dom/tests/mochitest/general/fail.png b/dom/tests/mochitest/general/fail.png Binary files differnew file mode 100644 index 000000000..db812bd7d --- /dev/null +++ b/dom/tests/mochitest/general/fail.png diff --git a/dom/tests/mochitest/general/file_bug628069.html b/dom/tests/mochitest/general/file_bug628069.html new file mode 100644 index 000000000..9fd28888c --- /dev/null +++ b/dom/tests/mochitest/general/file_bug628069.html @@ -0,0 +1,16 @@ +<html> +<body onhashchange='hashchange(event)' onload='load()'> + +<script> +function hashchange(e) { + (opener || parent).childHashchange(e); +} + +function load() { + (opener || parent).childLoad(); +} +</script> + +Just a shell of a page. +</body> +</html> diff --git a/dom/tests/mochitest/general/file_clonewrapper.html b/dom/tests/mochitest/general/file_clonewrapper.html new file mode 100644 index 000000000..811147d78 --- /dev/null +++ b/dom/tests/mochitest/general/file_clonewrapper.html @@ -0,0 +1,36 @@ +<!doctype html> +<html> +<head> +<script type="application/javascript"> + + function waitForMessage() { + return new Promise(function(resolve) { + window.addEventListener('message', function l(evt) { + window.removeEventListener('message', l); + resolve(evt.data); + }); + }); + } + + // Set up the objects for cloning. + function setup() { + window.testObject = { myNumber: 42, + myString: "hello", + myImageData: new ImageData(10, 10) }; + } + + // Called by the chrome parent window. + function tryToClone(obj, shouldSucceed, message) { + var success = false; + try { window.postMessage(obj, '*'); success = true; } + catch (e) { message = message + ' (threw: ' + e.message + ')'; } + is(success, shouldSucceed, message); + return (success && shouldSucceed) ? waitForMessage() : Promise.resolve(); + } + +</script> +</head> +<body onload="setup()"> +<input id="fileinput" type="file"></input> +</body> +</html> diff --git a/dom/tests/mochitest/general/file_domWindowUtils_scrollbarSize.html b/dom/tests/mochitest/general/file_domWindowUtils_scrollbarSize.html new file mode 100644 index 000000000..1959fc4f6 --- /dev/null +++ b/dom/tests/mochitest/general/file_domWindowUtils_scrollbarSize.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<body style='width: 100000px; overflow: hidden;'></body> + <div id="float" style="float: left; overflow: scroll;"> + <div style="width: 200px;"></div> + </div> +</html> diff --git a/dom/tests/mochitest/general/file_frameElementWrapping.html b/dom/tests/mochitest/general/file_frameElementWrapping.html new file mode 100644 index 000000000..44237f7e0 --- /dev/null +++ b/dom/tests/mochitest/general/file_frameElementWrapping.html @@ -0,0 +1,32 @@ +<html> + <script> + function check(elt, expectAccess, prop) { + var access = false; + try { + elt[prop]; + access = true; + } + catch (e) {} + return access === expectAccess; + } + + function sendMessage(success, sameOrigin, prop) { + var result = success ? 'PASS' : 'FAIL'; + var message; + if (sameOrigin) + message = 'Can access |' + prop + '| if same origin'; + else + message = 'Cannot access |' + prop + '| if not same origin'; + parent.postMessage(result + ',' + message, '*'); + } + + var sameOrigin = location.host !== 'example.org'; + var pass = check(frameElement, sameOrigin, 'src'); + if (!pass) { + sendMessage(false, sameOrigin, 'src'); + } else { + pass = check(parent.location, sameOrigin, 'href'); + sendMessage(pass, sameOrigin, 'href'); + } + </script> +</html> diff --git a/dom/tests/mochitest/general/file_interfaces.xml b/dom/tests/mochitest/general/file_interfaces.xml new file mode 100644 index 000000000..8b5aa5bfa --- /dev/null +++ b/dom/tests/mochitest/general/file_interfaces.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<bindings id="xbltestBindings" xmlns="http://www.mozilla.org/xbl"> + <binding id="xbltest"> + <content>PASS</content> + <implementation> + <constructor> + var win = XPCNativeWrapper.unwrap(window); + var SpecialPowers = win.SpecialPowers; + var is = win.is; + var todo_is = win.todo_is; + var ok = win.ok; + var legacyMozPrefixedInterfaces = win.legacyMozPrefixedInterfaces; + var createInterfaceMap = win.createInterfaceMap; + eval(win.runTest.toString()); + runTest(true); + win.SimpleTest.finish(); + </constructor> + </implementation> + </binding> +</bindings> diff --git a/dom/tests/mochitest/general/file_moving_nodeList.html b/dom/tests/mochitest/general/file_moving_nodeList.html new file mode 100644 index 000000000..2456c6e68 --- /dev/null +++ b/dom/tests/mochitest/general/file_moving_nodeList.html @@ -0,0 +1,31 @@ +<html> + <head> + <script> + document.childNodes.expando = "foo"; + + function getNodeList() { + return document.childNodes; + } + function getOptions() { + return document.createElement("select").options; + } + + function tryToUseNodeList(nodeList, ok) { + function expectException(op, reason) { + try { + var result = op(); + ok(false, "should have thrown an exception, got: " + result); + } catch (e) { + ok(/Permission denied/.test(e.toString()), reason); + } + } + + expectException(function() { nodeList.length = 2; }, "should not be able to set attributes"); + expectException(function() { nodeList.item(0); }, "should not have access to any functions"); + expectException(function() { nodeList.foo = "foo"; }, "should not be able to add expandos"); + } + </script> + </head> + <body> + </body> +</html> diff --git a/dom/tests/mochitest/general/file_moving_xhr.html b/dom/tests/mochitest/general/file_moving_xhr.html new file mode 100644 index 000000000..ee09c2bd1 --- /dev/null +++ b/dom/tests/mochitest/general/file_moving_xhr.html @@ -0,0 +1,28 @@ +<html> + <head> + <script> + function createXHR() { + var xhr = new XMLHttpRequest(); + xhr.expando = "foo"; + return xhr; + } + + function tryToUseXHR(xhr, ok) { + function expectException(op, reason) { + try { + var result = op(); + ok(false, "should have thrown an exception, got: " + result); + } catch (e) { + ok(/Permission denied/.test(e.toString()), reason); + } + } + + expectException(function() { xhr.open(); }, "should not have access to any functions"); + expectException(function() { xhr.foo = "foo"; }, "should not be able to add expandos"); + expectException(function() { xhr.withCredentials = true; }, "should not be able to set attributes"); + } + </script> + </head> + <body> + </body> +</html> diff --git a/dom/tests/mochitest/general/file_showModalDialog.html b/dom/tests/mochitest/general/file_showModalDialog.html new file mode 100644 index 000000000..1cae0b1c0 --- /dev/null +++ b/dom/tests/mochitest/general/file_showModalDialog.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<script> + function go() { + is(SpecialPowers.wrap(window).location.toString(), location.toString(), "sanity"); + ok("returnValue" in window && "dialogArguments" in window, "We are modal"); + var iwin = document.getElementById('ifr').contentWindow; + is(SpecialPowers.Cu.getClassName(iwin, /* aUnwrap = */ true), "Window", "Descendant frames should not be modal"); + + if (location.origin != "http://mochi.test:8888") { + is(window.dialogArguments, undefined, + "dialogArguments should be undefined cross-origin: " + location.origin); + } + + window.returnValue = "rv: " + window.dialogArguments; + + // Allow for testing navigations in series. + if (location.search == "") { + window.close(); + } else { + var origins = location.search.split('?')[1].split(','); + var newsearch = '?' + origins.splice(1).join(','); + var newurl = location.toString().replace(location.origin, origins[0]) + .replace(location.search, newsearch); + location = newurl; + } + + } +</script> +</head> +<body onload="opener.postMessage('dosetup', '*');"> +<iframe id="ifr"></iframe> +</body> +</html> diff --git a/dom/tests/mochitest/general/frameSelectEvents.html b/dom/tests/mochitest/general/frameSelectEvents.html new file mode 100644 index 000000000..c53bdb5e7 --- /dev/null +++ b/dom/tests/mochitest/general/frameSelectEvents.html @@ -0,0 +1,467 @@ +<!doctype html> +<html> + <head> + <title>Testing Selection Events</title> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + </head> + + <body> + <div id="normal"> + <span id="inner">A bunch of text in a span inside of a div which should be selected</span> + </div> + + <div id="ce"> + This is a random block of text + </div> + + <input type="text" id="input" value="XXXXXXXXXXXXXXXXXXX" width="200"> <br> + + <textarea id="textarea" width="200">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</textarea> + + <script> + // Call the testing methods from the parent window + var is = parent.is; + var ok = parent.ok; + + // spin() spins the event loop for two cycles, giving time for + // selectionchange events to be fired, and handled by our listeners. + function spin() { + return new Promise(function(a) { + parent.SimpleTest.executeSoon(function() { + parent.SimpleTest.executeSoon(a) + }); + }); + } + + // The main test + parent.add_task(function *() { + yield spin(); + + var selectstart = 0; + var selectionchange = 0; + var inputSelectionchange = 0; + var textareaSelectionchange = 0; + + var cancel = false; + var selectstartTarget = null; + + function* UpdateSelectEventsOntextControlsPref(aEnabled) { + yield SpecialPowers.pushPrefEnv({'set': [['dom.select_events.textcontrols.enabled', aEnabled]]}); + } + yield* UpdateSelectEventsOntextControlsPref(false); + + document.addEventListener('selectstart', function(aEvent) { + console.log("originaltarget", aEvent.originalTarget, "new", selectstartTarget); + is(aEvent.originalTarget, selectstartTarget, + "The original target of selectstart"); + selectstartTarget = null; + + console.log(selectstart); + selectstart++; + + if (cancel) { + aEvent.preventDefault(); + } + }); + document.addEventListener('selectionchange', function(aEvent) { + is(aEvent.originalTarget, document, + "The original target of selectionchange should be the document"); + console.log(selectionchange); + selectionchange++; + }); + + function elt(aId) { return document.getElementById(aId); } + function reset() { + selectstart = 0; + selectionchange = 0; + inputSelectionchange = 0; + textareaSelectionchange = 0; + cancel = false; + } + + elt("input").addEventListener('selectionchange', function(aEvent) { + is (aEvent.originalTarget, elt("input"), + "The original target of selectionchange should be the input"); + console.log(inputSelectionchange); + inputSelectionchange++; + }); + elt("textarea").addEventListener('selectionchange', function(aEvent) { + is (aEvent.originalTarget, elt("textarea"), + "The original target of selectionchange should be the textarea"); + console.log(textareaSelectionchange); + textareaSelectionchange++; + }); + function* mouseAction(aElement, aOffset, aType, + aSelStart, aSelChng, aISelChng, aTSelChng, + aYOffset) + { + if (aType == "click") { // You can simulate a click event by sending undefined + aType = undefined; + } + if (!aYOffset) { + aYOffset = 10; + } + synthesizeMouse(aElement, aOffset, aYOffset, { type: aType }); + yield spin(); + + is(selectstart, aSelStart, + "SelStart Mouse Action (" + aOffset + " - " + aType + ")"); + is(selectionchange, aSelChng, + "SelChng Mouse Action (" + aOffset + " - " + aType + ")"); + is(inputSelectionchange, aISelChng || 0, + "ISelChng Mouse Action (" + aOffset + " - " + aType + ")"); + is(textareaSelectionchange, aTSelChng || 0, + "TSelChng Mouse Action (" + aOffset + " - " + aType + ")"); + reset(); + } + + function* keyAction(aKey, aShift, aAccel, + aSelStart, aSelChng, aISelChng, aTSelChng) + { + synthesizeKey(aKey, { shiftKey: aShift, accelKey: aAccel }); + yield spin(); + is(selectstart, aSelStart, + "SelStart Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")"); + is(selectionchange, aSelChng, + "SelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")"); + is(inputSelectionchange, aISelChng || 0, + "ISelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")"); + is(textareaSelectionchange, aTSelChng || 0, + "TSelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")"); + reset(); + } + + function* contentEditableAction(aElement, aContentEditable, + aSelStart, aSelChng, + aISelChng, aTSelChng) + { + aElement.setAttribute("contenteditable", + aContentEditable ? "true" : "false"); + yield spin(); + + is(selectstart, aSelStart, + "SelStart ContentEditable Action"); + is(selectionchange, aSelChng, + "SelStart ContentEditable Action"); + is(inputSelectionchange, aISelChng || 0, + "SelStart ContentEditable Action"); + is(textareaSelectionchange, aTSelChng || 0, + "SelStart ContentEditable Action"); + reset(); + } + + var selection = document.getSelection(); + function isCollapsed() { + is(selection.isCollapsed, true, "Selection is collapsed"); + } + function isNotCollapsed() { + is(selection.isCollapsed, false, "Selection is not collapsed"); + } + + // Make sure setting the element to contentEditable doesn't cause any selectionchange events + yield* contentEditableAction(elt("ce"), true, 0, 0, 0, 0); + + // Make sure setting the element to not be contentEditable doesn't cause any selectionchange events + yield* contentEditableAction(elt("ce"), false, 0, 0, 0, 0); + + // Now make the div contentEditable and proceed with the test + yield* contentEditableAction(elt("ce"), true, 0, 0, 0, 0); + + // Focus the contenteditable text + yield* mouseAction(elt("ce"), 100, "click", 0, 1); + isCollapsed(); + + // Move the selection to the right, this should only fire selectstart once + selectstartTarget = elt("ce").firstChild; + yield* keyAction("VK_RIGHT", true, false, 1, 1); + isNotCollapsed(); + yield* keyAction("VK_RIGHT", true, false, 0, 1); + isNotCollapsed(); + + // Move it back so that the selection is empty again + yield* keyAction("VK_LEFT", true, false, 0, 1); + isNotCollapsed(); + yield* keyAction("VK_LEFT", true, false, 0, 1); + isCollapsed(); + + // Going from empty to non-empty should fire selectstart again + selectstartTarget = elt("ce").firstChild; + yield* keyAction("VK_LEFT", true, false, 1, 1); + isNotCollapsed(); + + function* mouseMoves(aElement, aTarget) { + // Select a region + yield* mouseAction(aElement, 50, "mousedown", 0, 1); + isCollapsed(); + + selectstartTarget = aTarget; + yield* mouseAction(aElement, 100, "mousemove", 1, 1); + isNotCollapsed(); + + // Moving it more shouldn't trigger a start (move back to empty) + yield* mouseAction(aElement, 75, "mousemove", 0, 1); + isNotCollapsed(); + yield* mouseAction(aElement, 50, "mousemove", 0, 1); + isCollapsed(); + + // Wiggling the mouse a little such that it doesn't select any + // characters shouldn't trigger a selection + yield* mouseAction(aElement, 50, "mousemove", 0, 0, 0, 0, 11); + isCollapsed(); + + // Moving the mouse again from an empty selection should trigger a + // selectstart + selectstartTarget = aTarget; + yield* mouseAction(aElement, 25, "mousemove", 1, 1); + isNotCollapsed(); + + // Releasing the mouse shouldn't do anything + yield* mouseAction(aElement, 25, "mouseup", 0, 0); + isNotCollapsed(); + + // And neither should moving your mouse around when the mouse + // button isn't pressed + yield* mouseAction(aElement, 50, "mousemove", 0, 0); + isNotCollapsed(); + + // Clicking in an random location should move the selection, but not perform a + // selectstart + yield* mouseAction(aElement, 50, "click", 0, 1); + isCollapsed(); + + // Clicking there again should do nothing + yield* mouseAction(aElement, 50, "click", 0, 0); + isCollapsed(); + + // Selecting a region, and canceling the selectstart should mean that the + // selection remains collapsed + yield* mouseAction(aElement, 75, "mousedown", 0, 1); + isCollapsed(); + cancel = true; + selectstartTarget = aTarget; + yield* mouseAction(aElement, 100, "mousemove", 1, 1); + isCollapsed(); + yield* mouseAction(aElement, 100, "mouseup", 0, 0); + isCollapsed(); + } + + // Should work both on normal + yield* mouseMoves(elt("inner"), elt("inner").firstChild); + // and contenteditable fields + yield* mouseMoves(elt("ce"), elt("ce").firstChild); + // and fields with elements in them + yield* mouseMoves(elt("normal"), elt("inner").firstChild); + + yield* mouseAction(elt("inner"), 50, "click", 0, 1); + isCollapsed(); + + reset(); + // Select all should fire both selectstart and change + selectstartTarget = document.body; + yield* keyAction("A", false, true, 1, 1); + isNotCollapsed(); + + // Clear the selection + yield* mouseAction(elt("inner"), 50, "click", 0, 1); + isCollapsed(); + + // Even if we already have a selection + yield* mouseAction(elt("inner"), 75, "mousedown", 0, 1); + isCollapsed(); + selectstartTarget = elt("inner").firstChild; + yield* mouseAction(elt("inner"), 100, "mousemove", 1, 1); + isNotCollapsed(); + yield* mouseAction(elt("inner"), 100, "mouseup", 0, 0); + isNotCollapsed(); + + selectstartTarget = document.body; + yield* keyAction("A", false, true, 1, 1); + isNotCollapsed(); + + // Clear the selection + yield* mouseAction(elt("inner"), 50, "click", 0, 1); + isCollapsed(); + + // Make sure that a synthesized selection change doesn't fire selectstart + var s = document.getSelection(); + s.removeAllRanges(); + yield spin(); + is(selectstart, 0, "Synthesized range removals shouldn't fire selectstart"); + is(selectionchange, 1, "Synthesized range removals should change selectionchange"); + reset(); + isCollapsed(); + + var range = document.createRange(); + range.selectNode(elt("inner")); + s.addRange(range); + yield spin(); + is(selectstart, 0, "Synthesized range additions shouldn't fire selectstart"); + is(selectionchange, 1, "Synthesized range additions should change selectionchange"); + reset(); + isNotCollapsed(); + + // Change the range, without replacing + range.selectNode(elt("ce")); + yield spin(); + is(selectstart, 0, "Synthesized range mutations shouldn't fire selectstart"); + is(selectionchange, 1, "Synthesized range mutations should change selectionchange"); + reset(); + isNotCollapsed(); + + // Remove the range + s.removeAllRanges(); + yield spin(); + is(selectstart, 0, "Synthesized range removal"); + is(selectionchange, 1, "Synthesized range removal"); + reset(); + isCollapsed(); + + + /* + Selection events mouse move on input type=text + */ + + // Select a region + + // Without the dom.select_events.textcontrols.enabled pref, + // pressing the mouse shouldn't do anything. + yield* mouseAction(elt("input"), 50, "mousedown", 0, 1, 0, 0); + + // Releasing the mouse shouldn't do anything + yield* mouseAction(elt("input"), 50, "mouseup", 0, 0, 0, 0); + + yield* UpdateSelectEventsOntextControlsPref(true); + + yield* mouseAction(elt("input"), 50, "mousedown", 0, 0, 0, 0); + + selectstartTarget = elt("input"); + yield* mouseAction(elt("input"), 100, "mousemove", 1, 0, 1, 0); + + // Moving it more shouldn't trigger a start (move back to empty) + yield* mouseAction(elt("input"), 75, "mousemove", 0, 0, 1, 0); + yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 1, 0); + + // Wiggling the mouse a little such that it doesn't select any + // characters shouldn't trigger a selection + yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 0, 0, 11); + + // Moving the mouse again from an empty selection should trigger a + // selectstart + selectstartTarget = elt("input"); + yield* mouseAction(elt("input"), 25, "mousemove", 1, 0, 1, 0); + + // Releasing the mouse shouldn't do anything + yield* mouseAction(elt("input"), 25, "mouseup", 0, 0, 0, 0); + + // And neither should moving your mouse around when the mouse + // button isn't pressed + yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 0, 0); + + // Clicking in an random location should move the selection, but + // not perform a selectstart + yield* mouseAction(elt("input"), 50, "click", 0, 0, 1, 0); + + // Clicking there again should do nothing + yield* mouseAction(elt("input"), 50, "click", 0, 0, 0, 0); + + // Selecting a region, and canceling the selectstart should mean that the + // selection remains collapsed + yield* mouseAction(elt("input"), 75, "mousedown", 0, 0, 1, 0); + cancel = true; + selectstartTarget = elt("input"); + yield* mouseAction(elt("input"), 100, "mousemove", 1, 0, 1, 0); + yield* mouseAction(elt("input"), 100, "mouseup", 0, 0, 0, 0); + + + yield* UpdateSelectEventsOntextControlsPref(false); + + // Without the dom.select_events.textcontrols.enabled pref, + // pressing the mouse shouldn't do anything. + // XXX For some reason we fire 2 selectchange events on the body + // when switching from the input to the text area. + yield* mouseAction(elt("textarea"), 50, "mousedown", 0, 2, 0, 0); + + // Releasing the mouse shouldn't do anything + yield* mouseAction(elt("textarea"), 50, "mouseup", 0, 0, 0, 0); + + yield* UpdateSelectEventsOntextControlsPref(true); + + // Select a region + yield* mouseAction(elt("textarea"), 50, "mousedown", 0, 0, 0, 0); + + selectstartTarget = elt("textarea"); + yield* mouseAction(elt("textarea"), 100, "mousemove", 1, 0, 0, 1); + + // Moving it more shouldn't trigger a start (move back to empty) + yield* mouseAction(elt("textarea"), 75, "mousemove", 0, 0, 0, 1); + yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 1); + + // Wiggling the mouse a little such that it doesn't select any + // characters shouldn't trigger a selection + yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 0, 11); + + // Moving the mouse again from an empty selection should trigger a + // selectstart + selectstartTarget = elt("textarea"); + yield* mouseAction(elt("textarea"), 25, "mousemove", 1, 0, 0, 1); + + // Releasing the mouse shouldn't do anything + yield* mouseAction(elt("textarea"), 25, "mouseup", 0, 0, 0, 0); + + // And neither should moving your mouse around when the mouse + // button isn't pressed + yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 0); + + // Clicking in an random location should move the selection, but not perform a + // selectstart + yield* mouseAction(elt("textarea"), 50, "click", 0, 0, 0, 1); + + // Clicking there again should do nothing + yield* mouseAction(elt("textarea"), 50, "click", 0, 0, 0, 0); + + // Selecting a region, and canceling the selectstart should mean that the + // selection remains collapsed + yield* mouseAction(elt("textarea"), 75, "mousedown", 0, 0, 0, 1); + cancel = true; + selectstartTarget = elt("textarea"); + yield* mouseAction(elt("textarea"), 100, "mousemove", 1, 0, 0, 1); + yield* mouseAction(elt("textarea"), 100, "mouseup", 0, 0, 0, 0); + + // Marking the input and textarea as display: none and then as visible again + // shouldn't trigger any changes, although the nodes will be re-framed + elt("input").setAttribute("style", "display: none;"); + yield spin(); + is(selectstart, 0, "idn - ss 1"); + is(selectionchange, 0, "idn - sc 1"); + is(inputSelectionchange, 0, "idn - isc 1"); + is(textareaSelectionchange, 0, "idn - tsc 1"); + reset(); + + elt("input").setAttribute("style", ""); + yield spin(); + is(selectstart, 0, "idn - ss 2"); + is(selectionchange, 0, "idn - sc 2"); + is(inputSelectionchange, 0, "idn - isc 2"); + is(textareaSelectionchange, 0, "idn - tsc 2"); + reset(); + + elt("textarea").setAttribute("style", "display: none;"); + yield spin(); + is(selectstart, 0, "tdn - ss 1"); + is(selectionchange, 0, "tdn - sc 1"); + is(inputSelectionchange, 0, "tdn - isc 1"); + is(textareaSelectionchange, 0, "tdn - tsc 1"); + reset(); + + elt("textarea").setAttribute("style", ""); + yield spin(); + is(selectstart, 0, "tdn - ss 2"); + is(selectionchange, 0, "tdn - sc 2"); + is(inputSelectionchange, 0, "tdn - isc 2"); + is(textareaSelectionchange, 0, "tdn - tsc 2"); + reset(); + }); + </script> + </body> +</html> diff --git a/dom/tests/mochitest/general/frameStorageAllowed.html b/dom/tests/mochitest/general/frameStorageAllowed.html new file mode 100644 index 000000000..62f4ac4b1 --- /dev/null +++ b/dom/tests/mochitest/general/frameStorageAllowed.html @@ -0,0 +1,22 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>frame for storage allowed test</title> + +<script type="text/javascript" src="https://example.com/tests/dom/tests/mochitest/general/storagePermissionsUtils.js"></script> +<script type="text/javascript"> + + task(function* () { + // We should be able to access storage + yield storageAllowed(); + + // We should be able to run a web worker which can access storage + yield runWorker("workerStorageAllowed.js"); + }); + +</script> + +</head> + +<body> +</body> +</html> diff --git a/dom/tests/mochitest/general/frameStorageChrome.html b/dom/tests/mochitest/general/frameStorageChrome.html new file mode 100644 index 000000000..7a4733139 --- /dev/null +++ b/dom/tests/mochitest/general/frameStorageChrome.html @@ -0,0 +1,20 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>frame for storage allowed test</title> + +<script type="text/javascript" src="https://example.com/tests/dom/tests/mochitest/general/storagePermissionsUtils.js"></script> +<script type="text/javascript"> + + task(function* () { + // We just check if we can access storage as chrome using special powers! + var params = new URLSearchParams(location.search.substr(1)); + yield chromePower(params.get('allowed') == 'yes', params.get('blockSessionStorage') == 'yes'); + }); + +</script> + +</head> + +<body> +</body> +</html> diff --git a/dom/tests/mochitest/general/frameStorageNullprincipal.sjs b/dom/tests/mochitest/general/frameStorageNullprincipal.sjs new file mode 100644 index 000000000..4f58a296f --- /dev/null +++ b/dom/tests/mochitest/general/frameStorageNullprincipal.sjs @@ -0,0 +1,33 @@ +// This is a sjs file which reads in frameStoragePrevented.html, and writes it out as a data: URI, which this page redirects to. +// This produces a URI with the null principal, which should be unable to access storage. +// We append the #nullprincipal hash to the end of the data: URI to tell the script that it shouldn't try to spawn a webworker, +// as it won't be allowed to, as it has a null principal. + +function handleRequest(request, response) { + // Get the nsIFile for frameStoragePrevented.html + var file; + getObjectState("SERVER_ROOT", function(serverRoot) { + file = serverRoot.getFile("/tests/dom/tests/mochitest/general/frameStoragePrevented.html"); + }); + + // Set up the file streams to read in the file as UTF-8 + let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]. + createInstance(Components.interfaces.nsIFileInputStream); + fstream.init(file, -1, 0, 0); + let cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Components.interfaces.nsIConverterInputStream); + cstream.init(fstream, "UTF-8", 0, 0); + + // Read in the file, and concatenate it onto the data string + let data = ""; + let str = {}; + let read = 0; + do { + read = cstream.readString(0xffffffff, str); + data += str.value; + } while (read != 0); + + // Write out the file as a data: URI, and redirect to it + response.setStatusLine('1.1', 302, 'Found'); + response.setHeader('Location', 'data:text/html,' + encodeURIComponent(data) + "#nullprincipal"); +} diff --git a/dom/tests/mochitest/general/frameStoragePrevented.html b/dom/tests/mochitest/general/frameStoragePrevented.html new file mode 100644 index 000000000..3554d6082 --- /dev/null +++ b/dom/tests/mochitest/general/frameStoragePrevented.html @@ -0,0 +1,47 @@ + +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>frame for storage prevented test</title> + +<script type="text/javascript" src="https://example.com/tests/dom/tests/mochitest/general/storagePermissionsUtils.js"></script> +<script type="text/javascript;version=1.7"> + + task(function* () { + // We shouldn't be able to access storage + yield storagePrevented(); + + // This hash of the URI is set to #nullprincipal by the test if the current page has a null principal, + // and thus attempting to create a dedicated worker will throw + if (location.hash == "#nullprincipal") { + function createWorker() { + return new Promise((resolve, reject) => { + var w; + try { + w = new Worker("workerStoragePrevented.js"); + } catch (e) { + ok(true, "Running workers was prevented"); + resolve(); + } + + w.onerror = function() { + ok(true, "Running workers was prevented"); + resolve(); + } + }); + } + + yield createWorker(); + return; + } + + // Try to run a worker, which shouldn't be able to access storage + yield runWorker("workerStoragePrevented.js"); + }); + +</script> + +</head> + +<body> +</body> +</html> diff --git a/dom/tests/mochitest/general/historyframes.html b/dom/tests/mochitest/general/historyframes.html new file mode 100644 index 000000000..def5fd171 --- /dev/null +++ b/dom/tests/mochitest/general/historyframes.html @@ -0,0 +1,156 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602256 +--> +<head> + <title>Test for Bug 602256</title> +</head> +<body onload="SimpleTest.executeSoon(run_test)"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a> +<div id="content"> + <iframe id="iframe" src="data:text/html,<p%20id='text'>Start</p>"></iframe> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 602256 **/ + +var testWin = window.opener ? window.opener : window.parent; + +var SimpleTest = testWin.SimpleTest; +function is() { testWin.is.apply(testWin, arguments); } + +var gFrame = null; + +var gState = null; + +window.addEventListener("popstate", function(aEvent) { + gState = aEvent.state; +}, false); + +function waitForLoad() { + function listener() { + gFrame.removeEventListener("load", listener, false); + SimpleTest.executeSoon(continue_test); + } + + gFrame.addEventListener("load", listener, false); +} + +function loadContent(aURL) { + waitForLoad(); + + gFrame.src = aURL; +} + +function getURL() { + return gFrame.contentDocument.documentURI; +} + +function getContent() { + return gFrame.contentDocument.getElementById("text").textContent; +} + +var START = "data:text/html,<p%20id='text'>Start</p>"; +var URL1 = "data:text/html,<p%20id='text'>Test1</p>"; +var URL2 = "data:text/html,<p%20id='text'>Test2</p>"; + +function run_test() { + window.history.pushState("START", window.location); + + gFrame = document.getElementById("iframe"); + + continue_test(); +} + +function* test_body() +{ + yield* test_basic_inner_navigation(); + yield* test_state_navigation(); +} + +var gTestContinuation = null; + +function continue_test() { + if (!gTestContinuation) { + gTestContinuation = test_body(); + } + var ret = gTestContinuation.next(); + if (ret.done) { + testWin.done(); + } +} + +function* test_basic_inner_navigation() { + // Navigate the inner frame a few times + yield loadContent(URL1); + is(getURL(), URL1, "URL should be correct"); + is(getContent(), "Test1", "Page should be correct"); + + yield loadContent(URL2); + + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + // Test that history is working + window.history.back(); + yield waitForLoad(); + is(getURL(), URL1, "URL should be correct"); + is(getContent(), "Test1", "Page should be correct"); + + window.history.forward(); + yield waitForLoad(); + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); +} + +function* test_state_navigation() { + window.history.pushState("STATE1", window.location); + + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.history.pushState("STATE2", window.location); + + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.history.back(); + + is(gState, "STATE1", "State should be correct"); + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.history.forward(); + + is(gState, "STATE2", "State should be correct"); + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.history.back(); + window.history.back(); + + is(gState, "START", "State should be correct"); + is(getURL(), URL2, "URL should be correct"); + is(getContent(), "Test2", "Page should be correct"); + + window.history.back(); + is(gState, "START", "State should be correct"); + yield waitForLoad(); + + is(getURL(), URL1, "URL should be correct"); + is(getContent(), "Test1", "Page should be correct"); + + window.history.back(); + is(gState, "START", "State should be correct after going back twice"); + yield waitForLoad(); + + is(gState, "START", "State should be correct"); + is(getURL(), START, "URL should be correct"); + is(getContent(), "Start", "Page should be correct"); +} +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/image_100.png b/dom/tests/mochitest/general/image_100.png Binary files differnew file mode 100644 index 000000000..df421453c --- /dev/null +++ b/dom/tests/mochitest/general/image_100.png diff --git a/dom/tests/mochitest/general/image_200.png b/dom/tests/mochitest/general/image_200.png Binary files differnew file mode 100644 index 000000000..6f76d4438 --- /dev/null +++ b/dom/tests/mochitest/general/image_200.png diff --git a/dom/tests/mochitest/general/image_50.png b/dom/tests/mochitest/general/image_50.png Binary files differnew file mode 100644 index 000000000..144a2f0b9 --- /dev/null +++ b/dom/tests/mochitest/general/image_50.png diff --git a/dom/tests/mochitest/general/mochitest.ini b/dom/tests/mochitest/general/mochitest.ini new file mode 100644 index 000000000..d59527801 --- /dev/null +++ b/dom/tests/mochitest/general/mochitest.ini @@ -0,0 +1,128 @@ +[DEFAULT] +support-files = + 497633.html + fail.png + file_bug628069.html + file_clonewrapper.html + file_domWindowUtils_scrollbarSize.html + file_frameElementWrapping.html + file_interfaces.xml + file_moving_nodeList.html + file_moving_xhr.html + file_showModalDialog.html + historyframes.html + image_50.png + image_100.png + image_200.png + pass.apng + performance_timeline_main_test.html + resource_timing_iframe.html + resource_timing_main_test.html + resource_timing_cross_origin.html + res0.resource + res1.resource + res1.resource^headers^ + res2.resource + res2.resource^headers^ + res3.resource + res3.resource^headers^ + res4.resource + res4.resource^headers^ + res5.resource + res5.resource^headers^ + res6.resource + res6.resource^headers^ + res7.resource + res7.resource^headers^ + res8.resource + res8.resource^headers^ + resource_timing.js + navigation_timing.html + test_bug1012662_common.js + frameStorageAllowed.html + frameStoragePrevented.html + frameStorageChrome.html + frameStorageNullprincipal.sjs + workerStorageAllowed.js + workerStoragePrevented.js + storagePermissionsUtils.js + frameSelectEvents.html + !/image/test/mochitest/big.png + !/image/test/mochitest/blue.png + !/image/test/mochitest/clear.png + !/image/test/mochitest/damon.jpg + !/image/test/mochitest/over.png + !/image/test/mochitest/red.png + !/dom/base/test/file_empty.html + +[test_497898.html] +skip-if = toolkit == 'android' +[test_bug504220.html] +[test_bug628069_1.html] +[test_bug628069_2.html] +[test_bug631440.html] +[test_bug653364.html] +[test_bug861217.html] +[test_bug1012662_editor.html] +skip-if = (toolkit == 'android') # Disabled on Android, see bug 1230231 +subsuite = clipboard +[test_bug1012662_noeditor.html] +subsuite = clipboard +skip-if = (toolkit == 'android') # Disabled on Android, see bug 1230231 +[test_bug1161721.html] +[test_bug1170911.html] +subsuite = clipboard +[test_bug1208217.html] +[test_bug1313753.html] +[test_clientRects.html] +[test_clipboard_disallowed.html] +[test_clipboard_events.html] +subsuite = clipboard +[test_consoleAPI.html] +[test_contentViewer_overrideDPPX.html] +[test_DOMMatrix.html] +[test_domWindowUtils.html] +[test_domWindowUtils_scrollbarSize.html] +[test_domWindowUtils_scrollXY.html] +[test_donottrack.html] +[test_focus_legend_noparent.html] +[test_focusrings.xul] +skip-if = toolkit == 'android' #TIMED_OUT +[test_for_of.html] +[test_framedhistoryframes.html] +[test_frameElementWrapping.html] +[test_img_mutations.html] +[test_interfaces.html] +[test_navigation_timing.html] +[test_network_events.html] +skip-if = true +# Disable this test until bug 795711 is fixed. +[test_offsets.html] +support-files = test_offsets.js +[test_outerHTML.html] +[test_outerHTML.xhtml] +[test_paste_selection.html] +[test_performance_now.html] +[test_performance_timeline.html] +[test_picture_apng.html] +[test_picture_mutations.html] +[test_pointerPreserves3D.html] +[test_pointerPreserves3DClip.html] +[test_resource_timing.html] +[test_resource_timing_cross_origin.html] +[test_resource_timing_frameset.html] +[test_selectevents.html] +skip-if = toolkit == 'android' # bug 1230232 - Mouse doesn't select in the same way +[test_showModalDialog.html] +skip-if = e10s || toolkit == 'android' #Don't run modal tests on Android +[test_showModalDialog_e10s.html] +run-if = e10s +[test_storagePermissionsAccept.html] +[test_storagePermissionsLimitForeign.html] +[test_storagePermissionsReject.html] +[test_storagePermissionsRejectForeign.html] +[test_stylesheetPI.html] +[test_vibrator.html] +[test_WebKitCSSMatrix.html] +[test_windowedhistoryframes.html] +[test_windowProperties.html] diff --git a/dom/tests/mochitest/general/navigation_timing.html b/dom/tests/mochitest/general/navigation_timing.html new file mode 100644 index 000000000..44474468e --- /dev/null +++ b/dom/tests/mochitest/general/navigation_timing.html @@ -0,0 +1,100 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!DOCTYPE html> +<html> +<head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + +var timingParams = [ + "navigationStart", + "unloadEventStart", + "unloadEventEnd", + "redirectStart", + "redirectEnd", + "fetchStart", + "domainLookupStart", + "domainLookupEnd", + "connectStart", + "connectEnd", + "requestStart", + "responseStart", + "responseEnd", + "domLoading", + "domInteractive", + "domContentLoadedEventStart", + "domContentLoadedEventEnd", + "domComplete", + "loadEventStart", + "loadEventEnd" + ]; + +function is(received, expected, message) { + window.opener.is(received, expected, message); +} + +function isnot(received, notExpected, message) { + window.opener.isnot(received, notExpected, message); +} + +window.onload = function() { + if (location.href.indexOf("_blank") != -1) { + test_blank(); + return; + } + + if (location.href.indexOf("_self") != -1) { + test_self(); + return; + } +} + +function checkTimingValues(expectedValues) { + for (var name of timingParams) { + if (name in expectedValues) { + is(window.performance.timing[name], expectedValues[name], name+" should be "+expectedValues[name]); + } else { + isnot(window.performance.timing[name], 0, name+" should not be 0"); + } + } +} + +function test_blank() { + // We set a timeout to make sure this is run after onload is called + setTimeout(function(){ + // When loading the page in _blank, unloadEvent and redirect timestamps should be 0 + var expectedValues = { "unloadEventStart": 0, "unloadEventEnd": 0, "redirectStart": 0, "redirectEnd": 0 }; + checkTimingValues(expectedValues); + + // change location in order to test a _self load + window.location.href = "navigation_timing.html?x=1#_self"; + }, 0); +} + +function test_self() { + // We set a timeout to make sure this is run after onload is called + setTimeout(function(){ + // When simply loading in _self, redirect timestamps should be 0 (unloadEventStart/End != 0) + var expectedValues = { "redirectStart": 0, "redirectEnd": 0 }; + checkTimingValues(expectedValues); + + window.opener.finishTests(); + }, 0); +} + +</script> + +</script> +</head> +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1099092" + title="Navigation timing"> + Bug #1099092 - Navigation Timing has incorrect values when page is load via link with target=_blank attribute + </a> + <p id="display"></p> +</body> +</html> diff --git a/dom/tests/mochitest/general/pass.apng b/dom/tests/mochitest/general/pass.apng Binary files differnew file mode 100644 index 000000000..6e78a9eef --- /dev/null +++ b/dom/tests/mochitest/general/pass.apng diff --git a/dom/tests/mochitest/general/performance_timeline_main_test.html b/dom/tests/mochitest/general/performance_timeline_main_test.html new file mode 100644 index 000000000..81456d44d --- /dev/null +++ b/dom/tests/mochitest/general/performance_timeline_main_test.html @@ -0,0 +1,101 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!DOCTYPE html> +<html> +<head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + +function ok(cond, message) { + window.opener.ok(cond, message) +} + +function is(received, expected, message) { + window.opener.is(received, expected, message); +} + +function isnot(received, notExpected, message) { + window.opener.isnot(received, notExpected, message); +} + +var receivedBufferFullEvents = 0; +window.performance.onresourcetimingbufferfull = () => { + receivedBufferFullEvents++; +} + +window.onload = () => { + // Here, we should have 4 entries (1 css, 3 png) since the image was loaded. + var nEntries = window.performance.getEntries().length; + ok(nEntries >= 4, "Performance.getEntries() returned wrong number of entries."); + + window.performance.setResourceTimingBufferSize(5); + window.performance.mark("test-start"); + window.performance.mark("test-end"); + + // The URI should be the address of a resource will be loaded later to be used on getEntriesByName. + window.performance.measure("http://mochi.test:8888/tests/dom/tests/mochitest/general/test-data2.json", + "test-start", "test-end"); + + is(window.performance.getEntries().length, nEntries + 3, "User Timing APIs should never be affected by setResourceTimingBufferSize."); + is(window.performance.getEntriesByType("resource").length, 4, "The number of PerformanceResourceTiming should be 4."); + is(window.performance.getEntriesByType("mark").length, 2, "The number of PerformanceMark entries should be 2."); + is(window.performance.getEntriesByType("measure").length, 1, "The number of PerformanceMeasure entries should be 1."); + + is(receivedBufferFullEvents, 0, "onresourcetimingbufferfull should never be called."); + + makeXhr("test-data2.json", firstCheck); +} + +function makeXhr(aUrl, aCallback) { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onload = aCallback; + xmlhttp.open("get", aUrl, true); + xmlhttp.send(); +} + +function firstCheck() { + is(window.performance.getEntriesByType("resource").length, 5, "The number of PerformanceResourceTiming should be 5."); + is(receivedBufferFullEvents, 1, "onresourcetimingbufferfull should be called once."); + makeXhr("test-data2.json", secondCheck); +} + +function secondCheck() { + is(window.performance.getEntriesByType("resource").length, 5, "The number of PerformanceResourceTiming should be 5."); + is(receivedBufferFullEvents, 1, "onresourcetimingbufferfull should never be called since the last call."); + checkOrder(window.performance.getEntries(), "All PerformanceEntry"); + checkOrder(window.performance.getEntriesByType("resource"), "PerformanceResourceTiming"); + checkOrder(window.performance.getEntriesByType("mark"), "PerformanceMark"); + checkOrder(window.performance.getEntriesByType("measure"), "PerformanceMeasure"); + + is(window.performance.getEntriesByName("http://mochi.test:8888/tests/dom/tests/mochitest/general/test-data2.json").length, 2, + "Both PerformanceMeasure and XMLHttpRequest resource should be included."); + checkOrder(window.performance.getEntriesByName("http://mochi.test:8888/tests/dom/tests/mochitest/general/test-data2.json"), + "Entry with performance.getEntrieByName()"); + window.opener.finishTests(); +} + +function checkOrder(entries, name) { + for (var i = 0; i < entries.length - 1; i++) { + ok(entries[i].startTime <= entries[i + 1].startTime, name + " should be sorted by startTime."); + } +} + +</script> +</head> +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1158731" + title="Buffer for Performance APIs (Resource Timing, User Timing) should be separeted"> + Bug #1158731 - Buffer for Performance APIs (Resource Timing, User Timing) should be separeted + </a> + <p id="display"></p> + <div id="content"> + <img src="http://mochi.test:8888/tests/image/test/mochitest/over.png"> + <object data="http://mochi.test:8888/tests/image/test/mochitest/clear.png" type="image/png"></object> + <embed src="http://mochi.test:8888/tests/image/test/mochitest/green.png" type="image/png"/> + </div> +</body> +</html> diff --git a/dom/tests/mochitest/general/res0.resource b/dom/tests/mochitest/general/res0.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res0.resource diff --git a/dom/tests/mochitest/general/res1.resource b/dom/tests/mochitest/general/res1.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res1.resource diff --git a/dom/tests/mochitest/general/res1.resource^headers^ b/dom/tests/mochitest/general/res1.resource^headers^ new file mode 100644 index 000000000..2e5d8ea17 --- /dev/null +++ b/dom/tests/mochitest/general/res1.resource^headers^ @@ -0,0 +1,2 @@ +HTTP 200 +Timing-Allow-Origin: * diff --git a/dom/tests/mochitest/general/res2.resource b/dom/tests/mochitest/general/res2.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res2.resource diff --git a/dom/tests/mochitest/general/res2.resource^headers^ b/dom/tests/mochitest/general/res2.resource^headers^ new file mode 100644 index 000000000..f19c20d3e --- /dev/null +++ b/dom/tests/mochitest/general/res2.resource^headers^ @@ -0,0 +1,2 @@ +HTTP 302 Moved +Location: http://test2.example.com/tests/image/test/mochitest/red.png diff --git a/dom/tests/mochitest/general/res3.resource b/dom/tests/mochitest/general/res3.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res3.resource diff --git a/dom/tests/mochitest/general/res3.resource^headers^ b/dom/tests/mochitest/general/res3.resource^headers^ new file mode 100644 index 000000000..391a44222 --- /dev/null +++ b/dom/tests/mochitest/general/res3.resource^headers^ @@ -0,0 +1,2 @@ +HTTP 200 +Timing-Allow-Origin: http://mochi.test:8888 diff --git a/dom/tests/mochitest/general/res4.resource b/dom/tests/mochitest/general/res4.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res4.resource diff --git a/dom/tests/mochitest/general/res4.resource^headers^ b/dom/tests/mochitest/general/res4.resource^headers^ new file mode 100644 index 000000000..fbf4a9de7 --- /dev/null +++ b/dom/tests/mochitest/general/res4.resource^headers^ @@ -0,0 +1,3 @@ +HTTP 302 Moved +Timing-Allow-Origin: * +Location: http://mochi.test:8888/tests/dom/tests/mochitest/general/res1.resource diff --git a/dom/tests/mochitest/general/res5.resource b/dom/tests/mochitest/general/res5.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res5.resource diff --git a/dom/tests/mochitest/general/res5.resource^headers^ b/dom/tests/mochitest/general/res5.resource^headers^ new file mode 100644 index 000000000..1be24a555 --- /dev/null +++ b/dom/tests/mochitest/general/res5.resource^headers^ @@ -0,0 +1,2 @@ +HTTP 200 +Timing-Allow-Origin: http://mochi.test:8889 diff --git a/dom/tests/mochitest/general/res6.resource b/dom/tests/mochitest/general/res6.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res6.resource diff --git a/dom/tests/mochitest/general/res6.resource^headers^ b/dom/tests/mochitest/general/res6.resource^headers^ new file mode 100644 index 000000000..4a570ae67 --- /dev/null +++ b/dom/tests/mochitest/general/res6.resource^headers^ @@ -0,0 +1,2 @@ +HTTP 200 +Timing-Allow-Origin: diff --git a/dom/tests/mochitest/general/res7.resource b/dom/tests/mochitest/general/res7.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res7.resource diff --git a/dom/tests/mochitest/general/res7.resource^headers^ b/dom/tests/mochitest/general/res7.resource^headers^ new file mode 100644 index 000000000..d9bbf9462 --- /dev/null +++ b/dom/tests/mochitest/general/res7.resource^headers^ @@ -0,0 +1,2 @@ +HTTP 200 +Timing-Allow-Origin: http://mochi.test:8888 http://test1.com diff --git a/dom/tests/mochitest/general/res8.resource b/dom/tests/mochitest/general/res8.resource new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/res8.resource diff --git a/dom/tests/mochitest/general/res8.resource^headers^ b/dom/tests/mochitest/general/res8.resource^headers^ new file mode 100644 index 000000000..21f490fd3 --- /dev/null +++ b/dom/tests/mochitest/general/res8.resource^headers^ @@ -0,0 +1,2 @@ +HTTP 302 Moved +Location: http://test1.example.com/tests/dom/tests/mochitest/general/res4.resource diff --git a/dom/tests/mochitest/general/resource_timing.js b/dom/tests/mochitest/general/resource_timing.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/tests/mochitest/general/resource_timing.js diff --git a/dom/tests/mochitest/general/resource_timing_cross_origin.html b/dom/tests/mochitest/general/resource_timing_cross_origin.html new file mode 100644 index 000000000..c5b906280 --- /dev/null +++ b/dom/tests/mochitest/general/resource_timing_cross_origin.html @@ -0,0 +1,187 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!DOCTYPE HTML> +<html> +<head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + +function ok(cond, message) { + window.opener.ok(cond, message) +} + +function is(received, expected, message) { + window.opener.is(received, expected, message); +} + +function isnot(received, notExpected, message) { + window.opener.isnot(received, notExpected, message); +} + +var bufferFullCounter = 0; +const expectedBufferFullEvents = 0; + +const properties = ["startTime", "redirectStart", "redirectEnd", "fetchStart", + "domainLookupStart", "domainLookupEnd", "connectStart", + "connectEnd", "requestStart", "responseStart", "responseEnd"]; + +window.onload = function() { + ok(!!window.performance, "Performance object should exist"); + ok(!!window.performance.getEntries, "Performance.getEntries() should exist"); + ok(!!window.performance.getEntriesByName, "Performance.getEntriesByName() should exist"); + ok(!!window.performance.getEntriesByType, "Performance.getEntriesByType() should exist"); + + window.performance.onresourcetimingbufferfull = function() { + bufferFullCounter += 1; + } + + makeXhr("http://mochi.test:8888/tests/dom/tests/mochitest/general/test-data.json", firstCheck); +}; + +function firstCheck() { + var entries = window.performance.getEntriesByName("http://mochi.test:8888/tests/dom/tests/mochitest/general/test-data.json"); + ok(!!entries[0], "same origin test-data.json is missing from entries"); + checkSameOrigin(entries[0]); + + var entries = window.performance.getEntriesByName("http://mochi.test:8888/tests/dom/tests/mochitest/general/res0.resource"); + ok(!!entries[0], "same origin res0.resource is missing from entries"); + checkSameOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res0.resource"); + ok(!!entries[0], "cross origin res0.resource is missing from entries"); + checkCrossOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res1.resource"); + ok(!!entries[0], "res1.resource is missing from entries"); + checkSameOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res2.resource"); + ok(!!entries[0], "redirected res2.resource is missing from entries"); + checkRedirectedCrossOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res3.resource"); + ok(!!entries[0], "cross origin res3.resource is missing from entries"); + checkSameOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res4.resource"); + ok(!!entries[0], "redirected res4.resource is missing from entries"); + checkRedirectedSameOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res5.resource"); + ok(!!entries[0], "cross origin res5.resource is missing from entries"); + checkCrossOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res6.resource"); + ok(!!entries[0], "cross origin res6.resource is missing from entries"); + checkCrossOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res7.resource"); + ok(!!entries[0], "cross origin res7.resource is missing from entries"); + checkCrossOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res8.resource"); + ok(!!entries[0], "redirected res8.resource is missing from entries"); + checkRedirectCrossOriginResourceSameOrigin(entries[0]); + + entries = window.performance.getEntriesByName("http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing.js"); + ok(!!entries[0], "same origin resource_timing.js is missing from entries"); + checkSameOrigin(entries[0]); + + is(bufferFullCounter, expectedBufferFullEvents, "Buffer full was called"); + finishTests(); +} + +function checkEntry(entry, checks) { + // If the entry is undefined, we return early so we don't get a JS error + if (entry == undefined) + return; + + for (var j = 1; j < properties.length; ++j) { + var prop = properties[j]; + if (checks[prop] != undefined) { + is(entry[prop], checks[prop], "Wrong value for prop " + prop + " for resource " + entry.name); + } else { + isnot(entry[prop], 0, "Wrong value for prop " + prop + " for resource " + entry.name); + } + } +} + +// No redirects have occured so RedirectStart/End are 0 +function checkSameOrigin(entry) { + const checks = { "redirectStart": 0, "redirectEnd": 0 }; + checkEntry(entry, checks); +} + +// This is a cross-origin resource that doesn't pass the check +// All of these attributes are 0. No redirects +function checkCrossOrigin(entry) { + const checks = { "redirectStart": 0, "redirectEnd": 0, + "domainLookupStart": 0, "domainLookupEnd": 0, + "connectStart": 0, "connectEnd": 0, + "requestStart": 0, "responseStart": 0 }; + checkEntry(entry, checks); +} + +// A cross-origin redirect has occured. RedirectStart/End and the rest of the +// attributes are 0. +function checkRedirectedCrossOrigin(entry) { + const checks = { "redirectStart": 0, "redirectEnd": 0, + "domainLookupStart": 0, "domainLookupEnd": 0, + "connectStart": 0, "connectEnd": 0, + "requestStart": 0, "responseStart": 0 }; + checkEntry(entry, checks); +} + +// The redirect is to the same origin as the initial document, +// so no entries are 0. +function checkRedirectedSameOrigin(entry) { + const checks = { }; + checkEntry(entry, checks); +} + +// The final entry passes the timing-allow-check, +// but one of the redirects does not. redirectStart/End are 0. +function checkRedirectCrossOriginResourceSameOrigin(entry) { + const checks = { "redirectStart": 0, "redirectEnd": 0 }; + checkEntry(entry, checks); +} + +function makeXhr(aUrl, aCallback) { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onload = aCallback; + xmlhttp.open("get", aUrl, true); + xmlhttp.send(); +} + +function finishTests() { + window.opener.finishTests(); +} + +</script> + +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=822480" + title="Add resource timing API."> + Bug #822480 - Add in the Resource Timing API + </a> + <p id="display"></p> + <div id="content"> + <object data="http://mochi.test:8888/tests/dom/tests/mochitest/general/res0.resource"> <!-- same origin, no header --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res0.resource"> <!-- cross origin, no header --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res1.resource"> <!-- cross origin, Timing-Allow-Origin: * header --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res2.resource"> <!-- cross origin redirect to test2.example.com, no header --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res3.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8888 header --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res4.resource"> <!-- cross origin redirect to mochi.test:8888/.../res1.resource, Timing-Allow-Origin: * --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res5.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8889 --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res6.resource"> <!-- cross origin, Timing-Allow-Origin: "" (empty string) --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res7.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8888 http://test1.com header --> + <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res8.resource"> <!-- double cross origin redirect --> + <script type="text/javascript" src="http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing.js"></script> <!-- same origin script --> + </div> +</body> + +</html> diff --git a/dom/tests/mochitest/general/resource_timing_iframe.html b/dom/tests/mochitest/general/resource_timing_iframe.html new file mode 100644 index 000000000..8d789758b --- /dev/null +++ b/dom/tests/mochitest/general/resource_timing_iframe.html @@ -0,0 +1,49 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!-- + This file is a sub-test file for the Resource Timing and Performance Timeline + APIs. + These tests are focused on the iframe corner case. + The first step is to check that the image from this document was added as + an entry to this window.performance object. + The second step is to check that this iframe was not added as an entry to its + own window.performance object. + As a final step, we do a double checking: no ifrmes were added as entries + to this window.performance object. +--> + +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Bug 822480 - Add in the Resource Timing API</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<script> +function doTest() { + window.parent.ok(!!window.performance.getEntriesByName( + "http://mochi.test:8888/tests/image/test/mochitest/damon.jpg").length, + "http://mochi.test:8888/tests/image/test/mochitest/damon.jpg should be a valid entry name"); + window.parent.ok(!window.performance.getEntriesByName( + "http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html").length, + "This iframe should NOT contain itself as an entry"); + + // Check that there are no iframes added as entries + var resources = window.performance.getEntriesByType("resource"); + for (var i = 0 ; i < resources.length; i++) { + var entry = resources[i]; + if (entry.initiatorType === "iframe") { + ok(false, "unexpected iframe " + entry.name); + } + } + + window.parent.iframeTestsCompleted(); +} +</script> +<body onLoad="doTest()"> + <img src="http://mochi.test:8888/tests/image/test/mochitest/damon.jpg"/> +</body> +</html> diff --git a/dom/tests/mochitest/general/resource_timing_main_test.html b/dom/tests/mochitest/general/resource_timing_main_test.html new file mode 100644 index 000000000..864b02984 --- /dev/null +++ b/dom/tests/mochitest/general/resource_timing_main_test.html @@ -0,0 +1,288 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!-- + This file contains test for the Resource Timing and Performance Timeline APIs. + The test starts by checking that all the entries were added to the performance + object. + The next step is to check that the "performance" object and its "getEntries()" + methods are available. We check all the 3 methods: getEntries, + getEntriesByName() and getEntriesByType(). + + As a next step, we check that the entries contain the correct information + ("checkEntries()" method). + The test checks that the entries contain all the required members, that the + timings are sorted properly and that the entries were returned in + chronological order with respect to startTime. In "checkEntries()", it is also + checked if the order of the entries is the expected order (the expected order + is hard-coded here). + The last test from the "checkEntries()" method will verify the iframe case: + the iframe must be added as an entry to this window's performance object, + while the image from the iframe should not be added here. + + Next tests will check the Performance API extensions introduced by the + resource timing: window.performance.setResourceTimingBufferSize(1) and + window.performance.clearResourceTimings(); + + The last tests will verify that the xhr resources are also added as entries + to our performance object. + + Meanwhile, the iframe from the page will get loaded + (resource_timing_iframe.html). + The iframe contains a second script that will do some tests, as well, plus + an image - its own resource. + The script from the iframe will check that the iframe itself was not added + as an entry (to itself). Also, it will check that its image was added as + entry to the iframe's performance object. + The last check is a double check: check that no subdocuments were added as + entries for this iframe's performance object. + The parent's (this window) "ok_wrapper()" method will be called once the tests + are completed. +--> + +<!DOCTYPE html> +<html> +<head> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + +var mainWindowTestsDone = false; +var iframeTestsDone = false; + +function ok(cond, message) { + window.opener.ok(cond, message) +} + +function is(received, expected, message) { + window.opener.is(received, expected, message); +} + +function isnot(received, notExpected, message) { + window.opener.isnot(received, notExpected, message); +} + +var bufferFullCounter = 0; +const expectedBufferFullEvents = 1; + +window.onload = function() { + ok(!!window.performance, "Performance object should exist"); + ok(!!window.performance.getEntries, "Performance.getEntries() should exist"); + ok(!!window.performance.getEntriesByName, "Performance.getEntriesByName() should exist"); + ok(!!window.performance.getEntriesByType, "Performance.getEntriesByType() should exist"); + + window.performance.onresourcetimingbufferfull = function() { + bufferFullCounter += 1; + } + + // Here, we should have 5 entries (1 css, 3 png, 1 html) since the image was loaded. + is(window.performance.getEntriesByType("resource").length, 5, "Performance.getEntriesByType() returned wrong number of entries."); + + checkStringify(window.performance.getEntriesByType("resource")[0]); + + ok(!!window.performance.getEntriesByType("resource").length, + "Performance.getEntriesByType() should return some results"); + ok(!!window.performance.getEntriesByName("http://mochi.test:8888/tests/image/test/mochitest/blue.png").length, + "Performance.getEntriesByName() should return some results"); + + // Checks that two calls for "getEntriesByType()" return a different array with the same + // entries. + isnot(window.performance.getEntriesByType("resource"), window.performance.getEntriesByType("resource"), + "getEntriesByType() should return a different array object every time."); + ok(function (array1, array2) { + if (array1.length != array2.length) { + return false; + } + for (var i = 0 ; i < array1.length ; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + return true; + }(window.performance.getEntriesByType("resource"), window.performance.getEntriesByType("resource")), + "The arrays should have the same entries."); + + checkEntries(window.performance.getEntriesByType("resource")); + + window.performance.setResourceTimingBufferSize(1); + is(window.performance.getEntriesByType("resource").length, 5, "No entries should be " + + "removed when setResourceTimingBufferSize is called."); + + window.performance.setResourceTimingBufferSize(4); + is(window.performance.getEntriesByType("resource").length, 5, "No entries should be " + + "removed when setResourceTimingBufferSize is called."); + + window.performance.setResourceTimingBufferSize(1); + window.performance.clearResourceTimings(); + is(window.performance.getEntriesByType("resource").length, 0, "All the entries should " + + "be removed when when clearResourceTimings is being called."); + + makeXhr("test-data.json", firstCheck); +} + +function checkStringify(entry) { + var object = JSON.parse(JSON.stringify(entry)); + var keys = ["initiatorType","redirectStart","redirectEnd","fetchStart", + "domainLookupStart","domainLookupEnd","connectStart","connectEnd", + "secureConnectionStart","requestStart","responseStart","responseEnd", + "name","entryType","startTime","duration"]; + for (var i in keys) { + ok(keys[i] in object, "The serialization should contain key: "+keys[i]); + } +} + +function checkEntries(anEntryList) { + // Check that all the entries have all the properties. + for (var i = 0 ; i < anEntryList.length ; i++) { + var entry = anEntryList[i]; + + ok(!!entry, "PerformanceEntry should not be null"); + ok(!!entry.name, "PerformanceEntry.name should be valid."); + ok(entry.startTime > 0, "PerformanceEntry.startTime should be grater than 0"); + + // The entries list should be in chronological order with respect to startTime + if (i > 0) { + ok(anEntryList[i - 1].startTime <= anEntryList[i].startTime, + "Entries list should be in chronological order with respect to startTime."); + } + + // Check that each entry has all the properties and that the timings were + // returned in the expected order. + if ("initiatorType" in entry) { + ok("redirectStart" in entry, "PerformanceEntry.redirectStart should be part of PerformanceEntry"); + ok("redirectEnd" in entry, "PerformanceEntry.redirectEnd should be part of PerformanceEntry"); + ok("fetchStart" in entry, "PerformanceEntry.fetchStart should be part of PerformanceEntry"); + ok("domainLookupStart" in entry, "PerformanceEntry.domainLookupStart should be part of PerformanceEntry"); + ok("domainLookupEnd" in entry, "PerformanceEntry.domainLookupEnd should be part of PerformanceEntry"); + ok("connectStart" in entry, "PerformanceEntry.connectStart should be part of PerformanceEntry"); + ok("connectEnd" in entry, "PerformanceEntry.connectEnd should be part of PerformanceEntry"); + ok("secureConnectionStart" in entry, "PerformanceEntry.secureConnectionStart should be part of PerformanceEntry"); + ok("requestStart" in entry, "PerformanceEntry.requestStart should be part of PerformanceEntry"); + ok("responseStart" in entry, "PerformanceEntry.responseStart should be part of PerformanceEntry"); + ok("responseEnd" in entry, "PerformanceEntry.responseEnd should be part of PerformanceEntry"); + + // Check that timings are in proper order + sequence = ['startTime', 'redirectStart', 'redirectEnd', 'fetchStart', + 'domainLookupStart', 'domainLookupEnd', 'connectStart', + 'connectEnd', 'requestStart', 'responseStart', 'responseEnd']; + for (var j = 1; j < sequence.length; ++j) { + var prop = sequence[j]; + var prevProp = sequence[j-1]; + if (prop == 'redirectStart' && entry[prop] == 0) + continue; + if (prop == 'redirectEnd' && entry[prop] == 0) + continue; + ok(entry[prevProp] <= entry[prop], + ['Expected ', prevProp, ' to happen before ', prop, + ', got ', prevProp, ' = ', entry[prevProp], + ', ', prop, ' = ', entry[prop]].join('')); + } + } + } + + // Check that the entries have the expected initiator type. We can't check + // the order (the order might depend on the platform the tests are running). + allResources = { + "http://mochi.test:8888/tests/SimpleTest/test.css" : "link", + "http://mochi.test:8888/tests/image/test/mochitest/blue.png" : "img", + "http://mochi.test:8888/tests/image/test/mochitest/red.png" : "object", + "http://mochi.test:8888/tests/image/test/mochitest/big.png" : "embed", + "http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing_iframe.html" : "iframe"}; + + for (resourceName in allResources) { + // Check that we have a resource with the specific name. + namedEntries = window.performance.getEntriesByName(resourceName); + ok (!!namedEntries && (namedEntries.length == 1), + "An entry with the name '" + resourceName + "' should be available"); + + if (!namedEntries.length) { + continue; + } + + // Double check for the entry name. + is (namedEntries[0].name, resourceName, "The resource name is invalid"); + + // Check the initiator type. + is (namedEntries[0].initiatorType, allResources[resourceName], + "The initiator type for " + resourceName + " is invalid"); + } + + // Check that the iframe's image was NOT added as an entry to this window's performance entry. + ok(!window.performance.getEntriesByName("http://mochi.test:8888/tests/image/test/mochitest/damon.jpg").length, + "http://mochi.test:8888/tests/image/test/mochitest/damon.jpg should be a valid entry name"); +} + +function firstCheck() { + is(window.performance.getEntriesByType("resource").length, 1, "The first xhr entry was not added."); + is(window.performance.getEntriesByType("resource")[0].initiatorType, "xmlhttprequest", + "The initiatorType is incorrect for this entry"); + makeXhr("test-data2.json", secondCheck); +} + +function secondCheck() { + // Since the buffer max size was set to '1', 'peformance.getEntriesByType()' should + // return only '1' entry (first xhr results). + is(window.performance.getEntriesByType("resource").length, 1, "The second xhr entry should not be " + + "returned since the buffer size was set to 1."); + isnot(window.performance.getEntriesByType("resource")[0].name, "http://mochi.test:8888/tests/dom/tests/mochitest/general/test-data2.json", + "We returned the second xhr instead of the first one"); + finishTest(); +} + +function finishTest() { + // Check if all the tests are completed. + if (iframeTestsDone) { + is(bufferFullCounter, expectedBufferFullEvents, "onresourcetimingbufferfull called a wrong number of times"); + window.opener.finishTests(); + } else { + mainWindowTestsDone = true; + } +} + +function makeXhr(aUrl, aCallback) { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onload = aCallback; + xmlhttp.open("get", aUrl, true); + xmlhttp.send(); +} + +function checkArraysHaveSameElementsInSameOrder(array1, array2) { + if (array1.length != array2.length) { + return false; + } + for (var i = 0 ; i < array1.length ; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + return true; +} + +function iframeTestsCompleted() { + if (mainWindowTestsDone) { + is(bufferFullCounter, expectedBufferFullEvents, "onresourcetimingbufferfull called a wrong number of times"); + window.opener.finishTests(); + } + else { + iframeTestsDone = true; + } +} + +</script> +</head> +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=822480" + title="Add resource timing API."> + Bug #822480 - Add in the Resource Timing API + </a> + <p id="display"></p> + <div id="content"> + <img src="http://mochi.test:8888/tests/image/test/mochitest/blue.png"> + <object data="http://mochi.test:8888/tests/image/test/mochitest/red.png" type="image/png"></object> + <embed src="http://mochi.test:8888/tests/image/test/mochitest/big.png" type="image/png"/> + <iframe sandbox="allow-same-origin allow-scripts" id="if_2" src="resource_timing_iframe.html" height="10" width="10"></iframe> + </div> +</body> +</html> diff --git a/dom/tests/mochitest/general/storagePermissionsUtils.js b/dom/tests/mochitest/general/storagePermissionsUtils.js new file mode 100644 index 000000000..4d1609d9f --- /dev/null +++ b/dom/tests/mochitest/general/storagePermissionsUtils.js @@ -0,0 +1,237 @@ +const BEHAVIOR_ACCEPT = 0; +const BEHAVIOR_REJECT_FOREIGN = 1; +const BEHAVIOR_REJECT = 2; +const BEHAVIOR_LIMIT_FOREIGN = 3; + +const kPrefName = "network.cookie.cookieBehavior"; + +// Check if we are in frame, and declare ok and finishTest appropriately +const inFrame = ("" + location).match(/frame/); +if (inFrame) { + ok = function(a, message) { + if (!a) { + parent.postMessage("FAILURE: " + message, "http://mochi.test:8888"); + } else { + parent.postMessage(message, "http://mochi.test:8888"); + } + }; + + finishTest = function() { + parent.postMessage("done", "http://mochi.test:8888"); + }; +} else { + finishTest = function() { + SimpleTest.finish(); + }; +} + +function setCookieBehavior(behavior) { + return new Promise((resolve, reject) => { + SpecialPowers.pushPrefEnv({"set": [[kPrefName, behavior]]}, resolve); + }); +} + +function runIFrame(url) { + return new Promise((resolve, reject) => { + function onMessage(e) { + if (e.data == "done") { + resolve(); + window.removeEventListener('message', onMessage); + return; + } + + ok(!e.data.match(/^FAILURE/), e.data + " (IFRAME = " + url + ")"); + } + window.addEventListener('message', onMessage, false); + + document.querySelector('iframe').src = url; + }); +} + +function runWorker(url) { + return new Promise((resolve, reject) => { + var worker = new Worker(url); + worker.addEventListener('message', function(e) { + if (e.data == "done") { + resolve(); + return; + } + + ok(!e.data.match(/^FAILURE/), e.data + " (WORKER = " + url + ")"); + }); + }); +} + +function chromePower(allowed, blockSessionStorage) { + + // localStorage is affected by storage policy. + try { + SpecialPowers.wrap(window).localStorage.getItem("X"); + ok(allowed, "getting localStorage from chrome didn't throw"); + } catch (e) { + ok(!allowed, "getting localStorage from chrome threw"); + } + + + // sessionStorage is not. See bug 1183968. + try { + SpecialPowers.wrap(window).sessionStorage.getItem("X"); + ok(!blockSessionStorage, "getting sessionStorage from chrome didn't throw"); + } catch (e) { + ok(blockSessionStorage, "getting sessionStorage from chrome threw"); + } + + // indexedDB is affected by storage policy. + try { + SpecialPowers.wrap(window).indexedDB; + ok(allowed, "getting indexedDB from chrome didn't throw"); + } catch (e) { + ok(!allowed, "getting indexedDB from chrome threw"); + } + + // Same with caches, along with the additional https-only requirement. + try { + var shouldResolve = allowed && location.protocol == "https:"; + var promise = SpecialPowers.wrap(window).caches.keys(); + ok(true, "getting caches from chrome should never throw"); + return new Promise((resolve, reject) => { + promise.then(function() { + ok(shouldResolve, "The promise was resolved for chrome"); + resolve(); + }, function(e) { + ok(!shouldResolve, "The promise was rejected for chrome: " + e); + resolve(); + }); + }); + } catch (e) { + ok(false, "getting caches from chrome threw"); + } +} + +function storageAllowed() { + try { + localStorage.getItem("X"); + ok(true, "getting localStorage didn't throw"); + } catch (e) { + ok(false, "getting localStorage should not throw"); + } + + try { + sessionStorage.getItem("X"); + ok(true, "getting sessionStorage didn't throw"); + } catch (e) { + ok(false, "getting sessionStorage should not throw"); + } + + try { + indexedDB; + ok(true, "getting indexedDB didn't throw"); + } catch (e) { + ok(false, "getting indexedDB should not throw"); + } + + try { + var promise = caches.keys(); + ok(true, "getting caches didn't throw"); + + return new Promise((resolve, reject) => { + promise.then(function() { + ok(location.protocol == "https:", "The promise was not rejected"); + resolve(); + }, function() { + ok(location.protocol != "https:", "The promise should not have been rejected"); + resolve(); + }); + }); + } catch (e) { + ok(false, "getting caches should not have thrown"); + return Promise.resolve(); + } +} + +function storagePrevented() { + try { + localStorage.getItem("X"); + ok(false, "getting localStorage should have thrown"); + } catch (e) { + ok(true, "getting localStorage threw"); + } + + if (location.hash == "#thirdparty") { + // No matter what the user's preferences are, we don't block + // sessionStorage in 3rd-party iframes. We do block them everywhere + // else however. + try { + sessionStorage.getItem("X"); + ok(true, "getting sessionStorage didn't throw"); + } catch (e) { + ok(false, "getting sessionStorage should not have thrown"); + } + } else { + try { + sessionStorage.getItem("X"); + ok(false, "getting sessionStorage should have thrown"); + } catch (e) { + ok(true, "getting sessionStorage threw"); + } + } + + try { + indexedDB; + ok(false, "getting indexedDB should have thrown"); + } catch (e) { + ok(true, "getting indexedDB threw"); + } + + try { + var promise = caches.keys(); + ok(true, "getting caches didn't throw"); + + return new Promise((resolve, reject) => { + promise.then(function() { + ok(false, "The promise should have rejected"); + resolve(); + }, function() { + ok(true, "The promise was rejected"); + resolve(); + }); + }); + } catch (e) { + ok(false, "getting caches should not have thrown"); + + return Promise.resolve(); + } +} + +function task(fn) { + if (!inFrame) { + SimpleTest.waitForExplicitFinish(); + } + + var gen = fn(); + + function next_step(val, e) { + var it; + try { + if (typeof e !== "undefined") { + it = gen.throw(e); + } else { + it = gen.next(val); + } + } catch (e) { + ok(false, "An error was thrown while stepping: " + e); + ok(false, "Stack: " + e.stack); + finishTest(); + } + + if (it.done) { + finishTest(); + return; + } + it.value.then(next_step, (e) => next_step(null, e)); + } + + next_step(); +} + +var thirdparty = "https://example.com/tests/dom/tests/mochitest/general/"; diff --git a/dom/tests/mochitest/general/test-data.json b/dom/tests/mochitest/general/test-data.json new file mode 100644 index 000000000..7bd0cdaf3 --- /dev/null +++ b/dom/tests/mochitest/general/test-data.json @@ -0,0 +1 @@ +{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] } diff --git a/dom/tests/mochitest/general/test-data2.json b/dom/tests/mochitest/general/test-data2.json new file mode 100644 index 000000000..7bd0cdaf3 --- /dev/null +++ b/dom/tests/mochitest/general/test-data2.json @@ -0,0 +1 @@ +{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] } diff --git a/dom/tests/mochitest/general/test_497898.html b/dom/tests/mochitest/general/test_497898.html new file mode 100644 index 000000000..fdd95e126 --- /dev/null +++ b/dom/tests/mochitest/general/test_497898.html @@ -0,0 +1,29 @@ +<html> +<head> +<title>Crash [@ nsFocusManager::SendFocusOrBlurEvent] after switching focus to a different window in this case</title> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +<script> +SimpleTest.waitForExplicitFinish(); + +function done() +{ + is("passed", "passed", "test passed without crashing"); + SimpleTest.finish(); +} + +function switchFocus() +{ + setTimeout(() => window.open('497633.html', '_new', 'width=300,height=300'), 0); +} +</script> +</head> +<body> +<iframe src="data:text/html;charset=utf-8,%3Chtml%3E%0A%3Chead%3E%3C/head%3E%0A%3Cbody%3E%0A%3Cbutton%20id%3D%22a%22%20onfocus%3D%22parent.switchFocus%28%29%22%20onblur%3D%22window.frameElement.parentNode.removeChild%28window.frameElement%29%22%3ESwitching%20focus%20to%20a%20different%20program%20should%20not%20crash%20Mozilla%3C/button%3E%0A%3Cscript%3E%0Adocument.getElementById%28%27a%27%29.focus%28%29%3B%0A%3C/script%3E%0A%3C/body%3E%0A%3C/html%3E"></iframe> + +<p id="display"></p> +<div id="content" style="display: none"></div> + +</body> +</html> + diff --git a/dom/tests/mochitest/general/test_DOMMatrix.html b/dom/tests/mochitest/general/test_DOMMatrix.html new file mode 100644 index 000000000..14dfac38a --- /dev/null +++ b/dom/tests/mochitest/general/test_DOMMatrix.html @@ -0,0 +1,728 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test DOMMatrix behavior</title> + <scriptsrc="/MochiKit/packed.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> +<script> +function createMatrix(a, b, c, d, e, f) +{ + var m = new DOMMatrix(); + m.a = a; + m.b = b; + m.c = c; + m.d = d; + m.e = e; + m.f = f; + return m; +} + +function create3DMatrix(a, b, c, d, e, f) +{ + var m = new DOMMatrix(); + m.a = a; + m.b = b; + m.c = c; + m.d = d; + m.e = e; + m.f = f; + m.m13 = 0; + return m; +} + +function cmpMatrix(a, b, msg) +{ + if (Array.isArray(a)) + a = new DOMMatrix(a); + if (Array.isArray(b)) + b = new DOMMatrix(b); + + ok(CompareDOMMatrix(a, b), + msg + " - got " + formatMatrix(a) + + ", expected " + formatMatrix(b)); +} + +function roughCmpMatrix(a, b, msg) +{ + if (Array.isArray(a)) + a = new DOMMatrix(a); + if (Array.isArray(b)) + b = new DOMMatrix(b); + + ok(RoughCompareDOMMatrix(a, b), + msg + " - got " + formatMatrix(a) + + ", expected " + formatMatrix(b)); +} + +function formatMatrix(m) +{ + m = new DOMMatrix(m); + + if (m.is2D) + return "(" + [m.a, m.b, m.c, m.d, m.e, m.f].join(', ') + ")"; + else + return "(" + [m.m11, m.m12, m.m13, m.m14, + m.m21, m.m22, m.m23, m.m24, + m.m31, m.m32, m.m33, m.m34, + m.m41, m.m42, m.m43, m.m44,].join(', ') + ")"; +} + +function CompareMatrix(dm, m) +{ + var ma = m.toFloat32Array(); + for (var x = 0; x < ma.length; x++) { + if (Math.abs(ma[x] - dm.m[x]) > 0.000001) + return false; + } + + return true; +} + +function CompareDOMMatrix(dm1, dm2) +{ + var m1 = dm1.toFloat32Array(); + var m2 = dm2.toFloat32Array(); + + if (m1.length != m2.length) + return false; + + for (var x = 0; x < m1.length; x++) { + if (Math.abs(m1[x] - m2[x]) > 0.000001) + return false; + } + + return true; +} + +function RoughCompareDOMMatrix(dm1, dm2) +{ + var m1 = dm1.toFloat32Array(); + var m2 = dm2.toFloat32Array(); + + if (m1.length != m2.length) + return false; + + const tolerance = 1 / 65535; + for (var x = 0; x < m1.length; x++) { + if (Math.abs(m1[x] - m2[x]) > tolerance) + return false; + } + + return true; +} + +SimpleTest.waitForExplicitFinish(); + +function main() +{ + var tests = [ + testCreateMatrix, + testMultiply, + testInverse, + testTranslate, + testScale, + testScaleNonUniform, + testRotate, + testRotateFromVector, + testFlipX, + testFlipY, + testSkewX, + testSkewY, + testMultiplyInPlace, + testInverseInPlace, + testTranslateInPlace, + testScaleInPlace, + testScaleNonUniformInPlace, + testRotateInPlace, + testRotateFromVectorInPlace, + testSkewXInPlace, + testSkewYInPlace, + testCreateMatrix3D, + testMultiply3D, + testInverse3D, + testTranslate3D, + testScale3D, + test3D, + testParsing, + testStringify + ]; + for (var i = 0; i < tests.length; i++) { + try{ + tests[i](); + } catch (e) { + ok(false, "uncaught exception in test " + i + ": " + e.name); + } + } + SimpleTest.finish(); +} + +function testCreateMatrix() +{ + var m = new DOMMatrix(); + + // Should be initialised to identity + cmpMatrix(m, [1, 0, 0, 1, 0, 0], + "DOMMatrix should produce identity matrix"); + + m = new DOMMatrix([1,2,3,4,5,6]); + cmpMatrix(m, [1,2,3,4,5,6], + "DOMMatrix should produce the same matrix"); + ok(m.is2D, "Failed to mark matrix as 2D."); + + m = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]); + cmpMatrix(m, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], + "DOMMatrix should produce the same matrix"); + ok(!m.is2D, "Failed to mark matrix as 3D."); + + var n = new DOMMatrix(m.toFloat32Array()); + cmpMatrix(n, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], + "DOMMatrix should produce the same matrix with float32array constructor"); + ok(!n.is2D, "Failed to mark matrix as 3D."); + + var n = new DOMMatrix(m.toFloat64Array()); + cmpMatrix(n, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], + "DOMMatrix should produce the same matrix with float64array constructor"); + ok(!n.is2D, "Failed to mark matrix as 3D."); + + var exn = null; + try { + m = new DOMMatrix([0]); + } catch (e) { + exn = e; + } + ok(exn, "did throw exception with bad DOMMatrix constructor with 1 parameter"); + + exn = null; + try { + m = new DOMMatrix([1,2,3,4,5,6,7,8,9]); + } catch (e) { + exn = e; + } + ok(exn, "did throw exception with bad DOMMatrix constructor with 9 parameters"); + + exn = null; + try { + m = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]); + } catch (e) { + exn = e; + } + ok(exn, "did throw exception with bad DOMMatrix constructor with 17 parameters"); +} + +// DOMMatrix multiply(in DOMMatrix secondMatrix); +function testMultiply() +{ + var m1 = createMatrix(1, 0, 0, 1, 50, 90); + var m2 = createMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0); + var m3 = createMatrix(1, 0, 0, 1, 130, 160); + var result = m1.multiply(m2).multiply(m3); + roughCmpMatrix(result, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203], + "Unexpected result after multiplying matrices"); + + // Check orig matrices are unchanged + cmpMatrix(m1, [1, 0, 0, 1, 50, 90], "Matrix changed after multiplication"); + roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0], + "Matrix changed after multiplication"); + cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication"); +} + +// DOMMatrix inverse() raises(SVGException); +function testInverse() +{ + // Test inversion + var m = createMatrix(2, 0, 0, 4, 110, -50); + roughCmpMatrix(m.inverse(), [0.5, 0, 0, 0.25, -55, 12.5], + "Unexpected result after inverting matrix"); + + // Test non-invertable + m = createMatrix(0, 0, 1, 0, 0, 0); + m = m.inverse(); + ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a); + ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D."); +} + +// DOMMatrix translate(in float x, in float y); +function testTranslate() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.translate(100, -50), [2, 0, 0, 1, 320, 50], + "Unexpected result after translate"); +} + +// DOMMatrix scale(in float scaleFactor); +function testScale() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.scale(0.5), [1, 0, 0, 0.5, 120, 100], + "Unexpected result after scale"); +} + +// DOMMatrix scaleNonUniform(in float scaleFactorX, in float scaleFactorY); +function testScaleNonUniform() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.scaleNonUniform(0.5, -3), [1, 0, 0, -3, 120, 100], + "Unexpected result after scaleNonUniform"); +} + +// DOMMatrix rotate(in float angle); +function testRotate() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.rotate(45), + [2*Math.cos(Math.PI/4), Math.sin(Math.PI/4), + 2*-Math.sin(Math.PI/4), Math.cos(Math.PI/4), + 120, 100], + "Unexpected result after rotate"); +} + +// DOMMatrix rotateFromVector(in float x, in float y) raises(SVGException); +function testRotateFromVector() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + // Make a 150 degree angle + var result = m.rotateFromVector(-2, 1.1547); + roughCmpMatrix(result, + [2*Math.cos(5*Math.PI/6), Math.sin(5*Math.PI/6), + 2*-Math.sin(5*Math.PI/6), Math.cos(5*Math.PI/6), + 120, 100], + "Unexpected result after rotateFromVector"); + + // Test bad input (1) + var exn = null; + try { + m.rotateFromVector(1, 0); + } catch (e) { + exn = e; + } + is(exn, null, "did not throw exception with zero coord for rotateFromVector"); + + // Test bad input (2) + exn = null; + try { + m.rotateFromVector(0, 1); + } catch (e) { + exn = e; + } + is(exn, null, "did not throw exception with zero coord for rotateFromVector"); +} + +// DOMMatrix flipX(); +function testFlipX() +{ + var m = createMatrix(1, 2, 3, 4, 5, 6); + cmpMatrix(m.flipX(), [-1, -2, 3, 4, 5, 6], "Unexpected result after flipX"); +} + +// DOMMatrix flipY(); +function testFlipY() +{ + var m = createMatrix(1, 2, 3, 4, 5, 6); + cmpMatrix(m.flipY(), [1, 2, -3, -4, 5, 6], "Unexpected result after flipY"); +} + +// DOMMatrix skewX(in float angle); +function testSkewX() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.skewX(30), [2, 0, 2*Math.tan(Math.PI/6), 1, 120, 100], + "Unexpected result after skewX"); +} + +// DOMMatrix skewY(in float angle); +function testSkewY() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.skewY(30), [2, Math.tan(Math.PI/6), 0, 1, 120, 100], + "Unexpected result after skewY"); +} + +// DOMMatrix multiply(in DOMMatrix secondMatrix); +function testMultiplyInPlace() +{ + var m1 = createMatrix(1, 0, 0, 1, 50, 90); + var m2 = createMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0); + var m3 = createMatrix(1, 0, 0, 1, 130, 160); + m1.multiplySelf(m2).multiplySelf(m3); + roughCmpMatrix(m1, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203], + "Unexpected result after multiplying matrices"); + + // Check orig matrices are unchanged + roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0], + "Matrix changed after multiplication"); + cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication"); +} + +// DOMMatrix inverse() raises(SVGException); +function testInverseInPlace() +{ + // Test inversion + var m = createMatrix(2, 0, 0, 4, 110, -50); + m.invertSelf(); + roughCmpMatrix(m, [0.5, 0, 0, 0.25, -55, 12.5], + "Unexpected result after inverting matrix"); + + // Test non-invertable + m = createMatrix(0, 0, 1, 0, 0, 0); + m.invertSelf(); + ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a); + ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D."); +} + +// DOMMatrix translate(in float x, in float y); +function testTranslateInPlace() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + m.translateSelf(100, -50) + roughCmpMatrix(m, [2, 0, 0, 1, 320, 50], + "Unexpected result after translate"); +} + +// DOMMatrix scale(in float scaleFactor); +function testScaleInPlace() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + m.scaleSelf(0.5); + roughCmpMatrix(m, [1, 0, 0, 0.5, 120, 100], + "Unexpected result after scale"); +} + +// DOMMatrix scaleNonUniform(in float scaleFactorX, in float scaleFactorY); +function testScaleNonUniformInPlace() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + m.scaleNonUniformSelf(0.5, -3); + roughCmpMatrix(m, [1, 0, 0, -3, 120, 100], + "Unexpected result after scaleNonUniform"); +} + +// DOMMatrix rotate(in float angle); +function testRotateInPlace() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + m.rotateSelf(45); + roughCmpMatrix(m, + [2*Math.cos(Math.PI/4), Math.sin(Math.PI/4), + 2*-Math.sin(Math.PI/4), Math.cos(Math.PI/4), + 120, 100], + "Unexpected result after rotate"); +} + +// DOMMatrix rotateFromVector(in float x, in float y) raises(SVGException); +function testRotateFromVectorInPlace() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + // Make a 150 degree angle + m.rotateFromVectorSelf(-2, 1.1547); + roughCmpMatrix(m, + [2*Math.cos(5*Math.PI/6), Math.sin(5*Math.PI/6), + 2*-Math.sin(5*Math.PI/6), Math.cos(5*Math.PI/6), + 120, 100], + "Unexpected result after rotateFromVector"); + + // Test bad input (1) + try { + m.rotateFromVectorSelf(1, 0); + ok(true, "did not throw exception with zero coord for rotateFromVector"); + } catch (e) { + ok(false, + "Got unexpected exception " + e + ", expected NotSupportedError"); + } + + // Test bad input (2) + try { + m.rotateFromVectorSelf(0, 1); + ok(true, "did not throw exception with zero coord for rotateFromVector"); + } catch (e) { } +} + +// DOMMatrix skewX(in float angle); +function testSkewXInPlace() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + m.skewXSelf(30); + roughCmpMatrix(m, [2, 0, 2*Math.tan(Math.PI/6), 1, 120, 100], + "Unexpected result after skewX"); +} + +// DOMMatrix skewY(in float angle); +function testSkewYInPlace() +{ + var m = createMatrix(2, 0, 0, 1, 120, 100); + m.skewYSelf(30); + roughCmpMatrix(m, [2, Math.tan(Math.PI/6), 0, 1, 120, 100], + "Unexpected result after skewY"); +} + +function testCreateMatrix3D() +{ + var m = new DOMMatrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + + // Should be initialised to identity + cmpMatrix(m, [1, 0, 0, 1, 0, 0], + "DOMMatrix should produce identity matrix"); + is(m.is2D, true, "should not produce 3d matrix"); + + m = new DOMMatrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]); + + // Should be initialised to identity + cmpMatrix(m, [1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + "DOMMatrix should produce identity matrix"); + is(m.is2D, false, "should produce 3d matrix"); +} + +// DOMMatrix multiply(in DOMMatrix secondMatrix); +function testMultiply3D() +{ + var m1 = createMatrix(1, 0, 0, 1, 50, 90); + var m2 = create3DMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0); + var m3 = create3DMatrix(1, 0, 0, 1, 130, 160); + var result = m1.multiply(m2).multiply(m3); + ok(m1.is2D == true, "should produce 3d matrix"); + roughCmpMatrix(result, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203], + "Unexpected result after multiplying matrices"); + + // Check orig matrices are unchanged + cmpMatrix(m1, [1, 0, 0, 1, 50, 90], "Matrix changed after multiplication"); + roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0], + "Matrix changed after multiplication"); + cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication"); +} + +// DOMMatrix inverse() raises(SVGException); +function testInverse3D() +{ + // Test inversion + var m = create3DMatrix(2, 0, 0, 4, 110, -50); + roughCmpMatrix(m.inverse(), [0.5, 0, 0, 0.25, -55, 12.5], + "Unexpected result after inverting matrix"); + + // Test non-invertable + m = createMatrix(0, 0, 1, 0, 0, 0); + m = m.inverse(); + ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a); + ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D."); +} + +// DOMMatrix translate(in float x, in float y); +function testTranslate3D() +{ + var m = create3DMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.translate(100, -50), [2, 0, 0, 1, 320, 50], + "Unexpected result after translate"); +} + +// DOMMatrix scale(in float scaleFactor); +function testScale3D() +{ + var m = create3DMatrix(2, 0, 0, 1, 120, 100); + roughCmpMatrix(m.scale(0.5), [1, 0, 0, 0.5, 120, 100], + "Unexpected result after scale"); +} + +function Matrix3D() { + this.m = new Float32Array([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]); +} + +Matrix3D.prototype = { + translate: function(x, y, z, result) { + result = result || new Matrix3D(); + var m = result.m; + + m[0] = 1; + m[1] = 0; + m[2] = 0; + m[3] = x; + + m[4] = 0; + m[5] = 1; + m[6] = 0; + m[7] = y; + + m[8] = 0; + m[9] = 0; + m[10] = 1; + m[11] = z; + + m[12] = 0; + m[13] = 0; + m[14] = 0; + m[15] = 1; + + return result; + }, + inverse: function(matrix, result) { + result = result || new Matrix3D(); + var m = matrix.m, r = result.m; + + r[0] = m[5]*m[10]*m[15] - m[5]*m[14]*m[11] - m[6]*m[9]*m[15] + m[6]*m[13]*m[11] + m[7]*m[9]*m[14] - m[7]*m[13]*m[10]; + r[1] = -m[1]*m[10]*m[15] + m[1]*m[14]*m[11] + m[2]*m[9]*m[15] - m[2]*m[13]*m[11] - m[3]*m[9]*m[14] + m[3]*m[13]*m[10]; + r[2] = m[1]*m[6]*m[15] - m[1]*m[14]*m[7] - m[2]*m[5]*m[15] + m[2]*m[13]*m[7] + m[3]*m[5]*m[14] - m[3]*m[13]*m[6]; + r[3] = -m[1]*m[6]*m[11] + m[1]*m[10]*m[7] + m[2]*m[5]*m[11] - m[2]*m[9]*m[7] - m[3]*m[5]*m[10] + m[3]*m[9]*m[6]; + + r[4] = -m[4]*m[10]*m[15] + m[4]*m[14]*m[11] + m[6]*m[8]*m[15] - m[6]*m[12]*m[11] - m[7]*m[8]*m[14] + m[7]*m[12]*m[10]; + r[5] = m[0]*m[10]*m[15] - m[0]*m[14]*m[11] - m[2]*m[8]*m[15] + m[2]*m[12]*m[11] + m[3]*m[8]*m[14] - m[3]*m[12]*m[10]; + r[6] = -m[0]*m[6]*m[15] + m[0]*m[14]*m[7] + m[2]*m[4]*m[15] - m[2]*m[12]*m[7] - m[3]*m[4]*m[14] + m[3]*m[12]*m[6]; + r[7] = m[0]*m[6]*m[11] - m[0]*m[10]*m[7] - m[2]*m[4]*m[11] + m[2]*m[8]*m[7] + m[3]*m[4]*m[10] - m[3]*m[8]*m[6]; + + r[8] = m[4]*m[9]*m[15] - m[4]*m[13]*m[11] - m[5]*m[8]*m[15] + m[5]*m[12]*m[11] + m[7]*m[8]*m[13] - m[7]*m[12]*m[9]; + r[9] = -m[0]*m[9]*m[15] + m[0]*m[13]*m[11] + m[1]*m[8]*m[15] - m[1]*m[12]*m[11] - m[3]*m[8]*m[13] + m[3]*m[12]*m[9]; + r[10] = m[0]*m[5]*m[15] - m[0]*m[13]*m[7] - m[1]*m[4]*m[15] + m[1]*m[12]*m[7] + m[3]*m[4]*m[13] - m[3]*m[12]*m[5]; + r[11] = -m[0]*m[5]*m[11] + m[0]*m[9]*m[7] + m[1]*m[4]*m[11] - m[1]*m[8]*m[7] - m[3]*m[4]*m[9] + m[3]*m[8]*m[5]; + + r[12] = -m[4]*m[9]*m[14] + m[4]*m[13]*m[10] + m[5]*m[8]*m[14] - m[5]*m[12]*m[10] - m[6]*m[8]*m[13] + m[6]*m[12]*m[9]; + r[13] = m[0]*m[9]*m[14] - m[0]*m[13]*m[10] - m[1]*m[8]*m[14] + m[1]*m[12]*m[10] + m[2]*m[8]*m[13] - m[2]*m[12]*m[9]; + r[14] = -m[0]*m[5]*m[14] + m[0]*m[13]*m[6] + m[1]*m[4]*m[14] - m[1]*m[12]*m[6] - m[2]*m[4]*m[13] + m[2]*m[12]*m[5]; + r[15] = m[0]*m[5]*m[10] - m[0]*m[9]*m[6] - m[1]*m[4]*m[10] + m[1]*m[8]*m[6] + m[2]*m[4]*m[9] - m[2]*m[8]*m[5]; + + var det = m[0]*r[0] + m[1]*r[4] + m[2]*r[8] + m[3]*r[12]; + for (var i = 0; i < 16; i++) r[i] /= det; + return result; + }, + multiply: function(left, result) { + result = result || new Matrix3D(); + var a = this.m, b = left.m, r = result.m; + + r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12]; + r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13]; + r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14]; + r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15]; + + r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12]; + r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13]; + r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14]; + r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15]; + + r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12]; + r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13]; + r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14]; + r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15]; + + r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12]; + r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13]; + r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14]; + r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15]; + + return result; + }, + scale: function(x, y, z, result) { + result = result || new Matrix3D(); + var m = result.m; + + m[0] = x; + m[1] = 0; + m[2] = 0; + m[3] = 0; + + m[4] = 0; + m[5] = y; + m[6] = 0; + m[7] = 0; + + m[8] = 0; + m[9] = 0; + m[10] = z; + m[11] = 0; + + m[12] = 0; + m[13] = 0; + m[14] = 0; + m[15] = 1; + + return result; + }, + rotate: function(a, x, y, z, result) { + result = result || new Matrix3D(); + var m = result.m; + + var d = Math.sqrt(x*x + y*y + z*z); + a *= Math.PI / 180; x /= d; y /= d; z /= d; + var c = Math.cos(a), s = Math.sin(a), t = 1 - c; + + m[0] = x * x * t + c; + m[1] = x * y * t - z * s; + m[2] = x * z * t + y * s; + m[3] = 0; + + m[4] = y * x * t + z * s; + m[5] = y * y * t + c; + m[6] = y * z * t - x * s; + m[7] = 0; + + m[8] = z * x * t - y * s; + m[9] = z * y * t + x * s; + m[10] = z * z * t + c; + m[11] = 0; + + m[12] = 0; + m[13] = 0; + m[14] = 0; + m[15] = 1; + + return result; + }, + swap: function(result) { + result = result || new Matrix3D(); + for (var x = 0; x < 16; x++) + result.m[x] = this.m[Math.floor(x/4) + (x%4)*4]; + + return result; + } +}; + + +function test3D() +{ + var m = new DOMMatrix() + var m2 = new Matrix3D(); + + m.translateSelf(2,3,4).scaleNonUniformSelf(1.2, 2.3, 3.4, 0, 0, 0); + m2 = m2.multiply(m2.translate(2,3,4)).multiply(m2.scale(1.2, 2.3, 3.4)).swap(); + + ok(CompareMatrix(m2, m), "translate + scale in 3d didn't match, expected: " + formatMatrix(m2.m) + ", got: " + formatMatrix(m)); + + m.invertSelf(); + m2 = new Matrix3D(); + m2 = m2.multiply(m2.translate(2,3,4)).multiply(m2.scale(1.2, 2.3, 3.4)); + m2 = m2.inverse(m2).swap(); + ok(CompareMatrix(m2, m), "translate + scale in inverted 3d didn't match, expected: " + formatMatrix(m2.m) + ", got: " + formatMatrix(m)); +} + +function testParsing() +{ + var m = new DOMMatrix("translate(10, 20) scale(.5, 2) rotate(45)"); + var m2 = new DOMMatrix(); + m2.translateSelf(10, 20).scaleNonUniformSelf(.5,2).rotateSelf(45); + ok(CompareDOMMatrix(m2, m), "string parsing didn't match"); + + m = new DOMMatrix(); + m.setMatrixValue("translate(10, 20) scale(.5, 2) rotate(45)"); + ok(CompareDOMMatrix(m2, m), "string parsing didn't match"); +} + + +function testStringify() {
+ var m = new DOMMatrix();
+ var s = "" + m;
+ ok(s == "matrix" + formatMatrix(m), "stringifier 1 produced wrong result: " + s);
+ m.a = 100;
+ s = "" + m;
+ ok(s == "matrix" + formatMatrix(m), "stringifier 2 produced wrong result: " + s);
+ m.m43 = 200;
+ s = "" + m;
+ ok(s == "matrix3d" + formatMatrix(m), "stringifier 3 produced wrong result:" + s);
+} + +window.addEventListener("load", main, false); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_WebKitCSSMatrix.html b/dom/tests/mochitest/general/test_WebKitCSSMatrix.html new file mode 100644 index 000000000..3ce0bac66 --- /dev/null +++ b/dom/tests/mochitest/general/test_WebKitCSSMatrix.html @@ -0,0 +1,336 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Test for WebKitCSSMatrix</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> +function RoughCompareMatrix(dm1, dm2) +{ + var m1 = dm1.toFloat32Array(); + var m2 = dm2.toFloat32Array(); + + if (m1.length != m2.length) { + return false; + } + + const tolerance = 1 / 65535; + for (var x = 0; x < m1.length; x++) { + if (Math.abs(m1[x] - m2[x]) > tolerance) { + return false; + } + } + + return true; +} + +function CompareMatrix(dm1, dm2) +{ + var m1 = dm1.toFloat32Array(); + var m2 = dm2.toFloat32Array(); + + if (m1.length != m2.length) { + return false; + } + + for (var x = 0; x < m1.length; x++) { + if (m1[x] != m2[x]) { + return false; + } + } + + return true; +} + +test(function() { + var m = new WebKitCSSMatrix(); + + assert_equals(m.m11, 1, "m11 should be 1"); + assert_equals(m.m22, 1, "m22 should be 1"); + assert_equals(m.m33, 1, "m33 should be 1"); + assert_equals(m.m44, 1, "m44 should be 1"); + assert_equals(m.m12, 0, "m12 should be 0"); + assert_equals(m.m13, 0, "m13 should be 0"); + assert_equals(m.m14, 0, "m14 should be 0"); + assert_equals(m.m21, 0, "m21 should be 0"); + assert_equals(m.m23, 0, "m23 should be 0"); + assert_equals(m.m24, 0, "m24 should be 0"); + assert_equals(m.m31, 0, "m31 should be 0"); + assert_equals(m.m32, 0, "m32 should be 0"); + assert_equals(m.m34, 0, "m34 should be 0"); + assert_equals(m.m41, 0, "m41 should be 0"); + assert_equals(m.m42, 0, "m42 should be 0"); + assert_equals(m.m43, 0, "m43 should be 0"); +}, "Test constructor with no arguments."); + +test(function() { + var m = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + assert_equals(m.m11, 1, "m11 should be 1"); + assert_equals(m.m12, 2, "m12 should be 2"); + assert_equals(m.m21, 3, "m21 should be 3"); + assert_equals(m.m22, 4, "m22 should be 4"); + assert_equals(m.m41, 5, "m41 should be 5"); + assert_equals(m.m42, 6, "m42 should be 6"); +}, "Test constructor with transform list."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new WebKitCSSMatrix(m1); + + assert_true(RoughCompareMatrix(m1, m2), "Matrix should be equal."); +}, "Test constructor with other matrix."); + +test(function() { + var m = new WebKitCSSMatrix(); + var mr = m.setMatrixValue("matrix(1,2,3,4,5,6)"); + + assert_equals(m.m11, 1, "m11 should be 1"); + assert_equals(m.m12, 2, "m12 should be 2"); + assert_equals(m.m21, 3, "m21 should be 3"); + assert_equals(m.m22, 4, "m22 should be 4"); + assert_equals(m.m41, 5, "m41 should be 5"); + assert_equals(m.m42, 6, "m42 should be 6"); + + assert_equals(m, mr, "Return value of setMatrixValue should be the same matrix."); +}, "Test setMatrixValue."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(6,5,4,3,2,1)"); + var m4 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.multiply(m3); + var m2r = m2.multiply(m3); + + assert_true(RoughCompareMatrix(m1r, m2r), "multiply should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m4), "Multiply should not mutate original matrix."); +}, "Test multiply."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.inverse(); + var m2r = m2.inverse(); + + assert_true(RoughCompareMatrix(m1r, m2r), "inverse should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "inverse should not mutate original matrix."); +}, "Test inverse."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.translate(2, 3, 4); + var m2r = m2.translate(2, 3, 4); + + assert_true(RoughCompareMatrix(m1r, m2r), "translate should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "translate should not mutate original matrix."); +}, "Test inverse."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2); + var m2r = m2.scaleNonUniform(2, 2, 1); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with 1 argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2, 3); + var m2r = m2.scaleNonUniform(2, 3, 1); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with 2 arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2, 3, 4); + var m2r = m2.scaleNonUniform(2, 3, 4); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with 3 arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(undefined, 3, 4); + var m2r = m2.scaleNonUniform(1, 3, 4); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with undefined scaleX argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2, undefined, 4); + var m2r = m2.scaleNonUniform(2, 2, 4); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with undefined scaleY argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.scale(2, 3, undefined); + var m2r = m2.scaleNonUniform(2, 3, 1); + + assert_true(RoughCompareMatrix(m1r, m2r), "scale should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "scale should not mutate original matrix."); +}, "Test scale with undefined scaleZ argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(2); + var m2r = m2.rotateAxisAngle(0, 0, 1, 2); // Rotate around unit vector on z-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with 1 argument."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(2, 3); + var m2r = m2.rotateAxisAngle(0, 1, 0, 3); // Rotate around unit vector on x-axis. + m2r = m2r.rotateAxisAngle(1, 0, 0, 2); // Rotate around unit vector on y-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with 2 arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(2, 3, 4); + var m2r = m2.rotateAxisAngle(0, 0, 1, 4); // Rotate around unit vector on z-axis. + m2r = m2r.rotateAxisAngle(0, 1, 0, 3); // Rotate around unit vector on y-axis. + m2r = m2r.rotateAxisAngle(1, 0, 0, 2); // Rotate around unit vector on x-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with 3 arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(2, undefined, undefined); + var m2r = m2.rotateAxisAngle(0, 0, 1, 2); // Rotate around unit vector on z-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with rotY and rotZ as undefined."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotate(undefined, 3, 4); + var m2r = m2.rotateAxisAngle(0, 0, 1, 4); // Rotate around unit vector on z-axis. + m2r = m2r.rotateAxisAngle(0, 1, 0, 3); // Rotate around unit vector on y-axis. + + assert_true(RoughCompareMatrix(m1r, m2r)); + assert_true(CompareMatrix(m1, m3), "rotate should not mutate original matrix."); +}, "Test rotate with rotX as undefined."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.rotateAxisAngle(2, 3, 4, 5); + var m2r = m2.rotateAxisAngle(2, 3, 4, 5); + + assert_true(RoughCompareMatrix(m1r, m2r), "rotateAxisAngle should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "rotateAxisAngle should not mutate original matrix."); +}, "Test rotateAxisAngle."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.skewX(2); + var m2r = m2.skewX(2); + + assert_true(RoughCompareMatrix(m1r, m2r), "skewX should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "skewX should not mutate original matrix."); +}, "Test skewX."); + +test(function() { + var m1 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + var m2 = new DOMMatrix("matrix(1,2,3,4,5,6)"); + var m3 = new WebKitCSSMatrix("matrix(1,2,3,4,5,6)"); + + var m1r = m1.skewY(2); + var m2r = m2.skewY(2); + + assert_true(RoughCompareMatrix(m1r, m2r), "skewY should return the same result as DOMMatrixReadOnly."); + assert_true(CompareMatrix(m1, m3), "skewY should not mutate original matrix."); +}, "Test skewY."); + +test(function() { + var m = new WebKitCSSMatrix("matrix(1,0,0,0,0,0)"); + assert_throws("NotSupportedError", function() { m.inverse(); }, "Inverting an invertible matrix should throw.") +}, "Test that inverting an invertible matrix throws."); + +test(function() { + var m1 = new WebKitCSSMatrix("translate(10px, 10px)"); + var m2 = new DOMMatrix(); + m2.translateSelf(10, 10); + assert_true(RoughCompareMatrix(m1, m2), "translate in constructor should result in translated matrix"); + + assert_throws("SyntaxError", function() { new WebKitCSSMatrix("translate(10em, 10em)"); }, "Transform function may not contain relative units.") + assert_throws("SyntaxError", function() { new WebKitCSSMatrix("translate(10%, 10%)"); }, "Transform function may not contain percentage.") +}, "Test constructor with translate"); + +test(function() { + assert_throws("SyntaxError", function() { new WebKitCSSMatrix("initial"); }, "initial is not a valid constructor argument.") + assert_throws("SyntaxError", function() { new WebKitCSSMatrix("inherit"); }, "inherit is not a valid constructor arugment.") +}, "Test invalid constructor arguments."); + +test(function() { + var m1 = new WebKitCSSMatrix(); + m1 = m1.rotateAxisAngle(0, 0, 1, 45); + + var m2 = new WebKitCSSMatrix(); + m2 = m2.rotateAxisAngle(0, 0, 3, 45); + + assert_true(RoughCompareMatrix(m1, m2), "rotateAxisAngle should normalize vector to unit vector."); +}, "Test normalization of vector for rotateAxisAngle"); +</script> diff --git a/dom/tests/mochitest/general/test_bug1012662_common.js b/dom/tests/mochitest/general/test_bug1012662_common.js new file mode 100644 index 000000000..0d1ba2963 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug1012662_common.js @@ -0,0 +1,341 @@ +/** Test for Bug 1012662 **/ +// This test is called from both test_bug1012662_editor.html and test_bug1012662_noeditor.html +// This is to test that the code works both in the presence of a contentEditable node, and in the absense of one +var WATCH_TIMEOUT = 300; + +// Some global variables to make the debug messages easier to track down +var gTestN0 = 0, gTestN1 = 0, gTestN2 = 0; +function testLoc() { return " " + gTestN0 + " - " + gTestN1 + " - " + gTestN2; } + +// Listen for cut & copy events +var gCopyCount = 0, gCutCount = 0; +document.addEventListener('copy', function() { + gCopyCount++; +}); +document.addEventListener('cut', function() { + gCutCount++; +}); + + +// Helper methods +function selectNode(aSelector, aCb) { + var dn = document.querySelector(aSelector); + var range = document.createRange(); + range.selectNodeContents(dn); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + if (aCb) { + aCb(); + } +} + +function selectInputNode(aSelector, aCb) { + var dn = document.querySelector(aSelector); + synthesizeMouse(dn, 10, 10, {}); + SimpleTest.executeSoon(function() { + synthesizeKey("A", {accelKey: true}); + SimpleTest.executeSoon(aCb); + }); +} + +// Callback functions for attaching to the button +function execCut(aShouldSucceed) { + var cb = function(e) { + e.preventDefault(); + document.removeEventListener('keydown', cb); + + is(aShouldSucceed, document.execCommand('cut'), "Keydown caused cut invocation" + testLoc()); + }; + return cb; +} +function execCopy(aShouldSucceed) { + var cb = function(e) { + e.preventDefault(); + document.removeEventListener('keydown', cb); + + is(aShouldSucceed, document.execCommand('copy'), "Keydown caused copy invocation" + testLoc()); + }; + return cb; +} + +// The basic test set. Tries to cut/copy everything +function cutCopyAll(aDoCut, aDoCopy, aDone, aNegate, aClipOverride, aJustClipboardNegate) { + var execCommandAlwaysSucceed = !!(aClipOverride || aJustClipboardNegate); + + function waitForClipboard(aCond, aSetup, aNext, aNegateOne) { + if (aClipOverride) { + aCond = aClipOverride; + aNegateOne = false; + } + if (aNegate || aNegateOne || aJustClipboardNegate) { + SimpleTest.waitForClipboard(null, aSetup, aNext, aNext, "text/unicode", WATCH_TIMEOUT, true); + } else { + SimpleTest.waitForClipboard(aCond, aSetup, aNext, aNext); + } + } + + function validateCutCopy(aExpectedCut, aExpectedCopy) { + if (aNegate) { + aExpectedCut = aExpectedCopy = 0; + } // When we are negating - we always expect callbacks not to be run + + is(aExpectedCut, gCutCount, + (aExpectedCut > 0 ? "Expect cut callback to run" : "Expect cut callback not to run") + testLoc()); + is(aExpectedCopy, gCopyCount, + (aExpectedCopy > 0 ? "Expect copy callback to run" : "Expect copy callback not to run") + testLoc()); + gCutCount = gCopyCount = 0; + } + + function step(n) { + function nextStep() { step(n + 1); } + + document.querySelector('span').textContent = 'span text'; + document.querySelector('input[type=text]').value = 'text text'; + document.querySelector('input[type=password]').value = 'password text'; + document.querySelector('textarea').value = 'textarea text'; + + var contentEditableNode = document.querySelector('div[contentEditable=true]'); + if (contentEditableNode) { + contentEditableNode.textContent = 'contenteditable text'; + } + + gTestN2 = n; + switch (n) { + case 0: + // copy on readonly selection + selectNode('span'); + waitForClipboard("span text", function() { + aDoCopy(true); + }, nextStep); + return; + + case 1: + validateCutCopy(0, 1); + + // cut on readonly selection + selectNode('span'); + + waitForClipboard("span text", function() { + aDoCut(execCommandAlwaysSucceed); + }, nextStep, true); + return; + + case 2: + validateCutCopy(1, 0); + + // copy on textbox selection + selectInputNode('input[type=text]', nextStep); + return; + + case 3: + waitForClipboard("text text", function() { + selectInputNode('input[type=text]', function() { aDoCopy(true); }); + }, nextStep); + return; + + case 4: + validateCutCopy(0, 1); + + // cut on textbox selection + selectInputNode('input[type=text]', nextStep); + return; + + case 5: + waitForClipboard("text text", function() { + aDoCut(true); + }, nextStep); + return; + + case 6: + validateCutCopy(1, 0); + + // copy on password selection + selectInputNode('input[type=password]', nextStep); + return; + + case 7: + waitForClipboard(null, function() { + aDoCopy(execCommandAlwaysSucceed); + }, nextStep, true); + return; + + case 8: + validateCutCopy(0, 1); + + // cut on password selection + selectInputNode('input[type=password]', nextStep); + return; + + case 9: + waitForClipboard(null, function() { + aDoCut(execCommandAlwaysSucceed); + }, nextStep, true); + return; + + case 10: + validateCutCopy(1, 0); + + // copy on textarea selection + selectInputNode('textarea', nextStep); + return; + + case 11: + waitForClipboard("textarea text", function() { + aDoCopy(true); + }, nextStep); + return; + + case 12: + validateCutCopy(0, 1); + + // cut on password selection + selectInputNode('textarea', nextStep); + return; + + case 13: + waitForClipboard("textarea text", function() { + aDoCut(true); + }, nextStep); + return; + + case 14: + validateCutCopy(1, 0); + + // copy on no selection + document.querySelector('textarea').blur(); + + waitForClipboard(null, function() { + aDoCopy(true); + }, nextStep, true); + return; + + case 15: + validateCutCopy(0, 1); + + // cut on no selection + waitForClipboard(null, function() { + aDoCut(execCommandAlwaysSucceed); + }, nextStep, true); + return; + + case 16: + validateCutCopy(1, 0); + + if (!document.querySelector('div[contentEditable=true]')) { + // We're done! (no contentEditable node!) + step(-1); + return; + } + break; + + case 17: + // copy on contenteditable selection + waitForClipboard("contenteditable text", function() { + selectNode('div[contentEditable=true]', function() { + aDoCopy(true); + }); + }, nextStep); + return; + + case 18: + validateCutCopy(0, 1); + break; + + case 19: + // cut on contenteditable selection + waitForClipboard("contenteditable text", function() { + selectNode('div[contentEditable=true]', function() { + aDoCut(true); + }); + }, nextStep); + return; + + case 20: + validateCutCopy(1, 0); + break; + + default: + aDone(); + return; + } + + SimpleTest.executeSoon(function() { step(n + 1); }); + } + + step(0); +} + +function allMechanisms(aCb, aClipOverride, aNegateAll) { + function testStep(n) { + gTestN1 = n; + switch (n) { + case 0: + // Keyboard issued + cutCopyAll(function docut(aSucc) { + synthesizeKey("X", {accelKey: true}); + }, function docopy(aSucc) { + synthesizeKey("C", {accelKey: true}); + }, function done() { testStep(n + 1); }, false, aClipOverride, aNegateAll); + return; + + case 1: + // Button issued + cutCopyAll(function docut(aSucc) { + document.addEventListener('keydown', execCut(aSucc)); + synthesizeKey("Q", {}); + }, function docopy(aSucc) { + document.addEventListener('keydown', execCopy(aSucc)); + synthesizeKey("Q", {}); + }, function done() { testStep(n + 1); }, false, aClipOverride, aNegateAll); + return; + + case 2: + // Button issued + cutCopyAll(function doCut(aSucc) { + is(false, document.execCommand('cut'), "Can't directly execCommand not in a user callback"); + }, function doCopy(aSucc) { + is(false, document.execCommand('copy'), "Can't directly execCommand not in a user callback"); + }, function done() { testStep(n + 1); }, true, aClipOverride, aNegateAll); + return; + + default: + aCb(); + return; + } + + SimpleTest.executeSoon(function() { testStep(n + 1); }); + } + testStep(0); +} + +// Run the tests +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestLongerTimeout(5); // On the emulator - this times out occasionally +SimpleTest.waitForFocus(function() { + function justCancel(aEvent) { + aEvent.preventDefault(); + } + + function override(aEvent) { + aEvent.clipboardData.setData('text/plain', 'overridden'); + aEvent.preventDefault(); + } + + allMechanisms(function() { + gTestN0 = 1; + document.addEventListener('cut', override); + document.addEventListener('copy', override); + + allMechanisms(function() { + gTestN0 = 2; + document.removeEventListener('cut', override); + document.removeEventListener('copy', override); + document.addEventListener('cut', justCancel); + document.addEventListener('copy', justCancel); + + allMechanisms(function() { + SimpleTest.finish(); + }, null, true); + }, 'overridden'); + }); +}); diff --git a/dom/tests/mochitest/general/test_bug1012662_editor.html b/dom/tests/mochitest/general/test_bug1012662_editor.html new file mode 100644 index 000000000..c77adcfc7 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug1012662_editor.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1012662 +--> +<head> + <title>Test for Bug 1012662</title> + <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/WindowSnapshot.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1012662">Mozilla Bug 1012662</a> +<p id="display"></p> + +<div id="content"> + <span>span text</span> + <input type="text" value="text text"> + <input type="password" value="password text"> + <textarea>textarea text</textarea> + <div contentEditable="true">contenteditable text</div> +</div> + +<pre id="test"> +<script type="application/javascript" src="test_bug1012662_common.js"></script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug1012662_noeditor.html b/dom/tests/mochitest/general/test_bug1012662_noeditor.html new file mode 100644 index 000000000..a3c2be71e --- /dev/null +++ b/dom/tests/mochitest/general/test_bug1012662_noeditor.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1012662 +--> +<head> + <title>Test for Bug 1012662</title> + <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/WindowSnapshot.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1012662">Mozilla Bug 1012662</a> +<p id="display"></p> + +<div id="content"> + <span>span text</span> + <input type="text" value="text text"> + <input type="password" value="password text"> + <textarea>textarea text</textarea> +</div> + +<pre id="test"> +<script type="application/javascript" src="test_bug1012662_common.js"></script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug1161721.html b/dom/tests/mochitest/general/test_bug1161721.html new file mode 100644 index 000000000..3e59dd81b --- /dev/null +++ b/dom/tests/mochitest/general/test_bug1161721.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1161721 +--> +<head> + <title>Test for Bug 1161721</title> + <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/WindowSnapshot.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161721">Mozilla Bug 1161721</a> +<p id="display"></p> + +<div id="content"> +</div> + +<pre id="test"> + <script type="application/javascript"> + ok(!document.queryCommandSupported("paste"), "Paste isn't supported in non-privilged JavaScript"); + ok(document.queryCommandSupported("copy"), "Copy is supported in non-privilged JavaScript"); + ok(document.queryCommandSupported("cut"), "Cut is supported in non-privilged JavaScript"); + + ok(SpecialPowers.wrap(document).queryCommandSupported("paste"), "Paste is supported in privilged JavaScript"); + ok(SpecialPowers.wrap(document).queryCommandSupported("copy"), "Copy is supported in privilged JavaScript"); + ok(SpecialPowers.wrap(document).queryCommandSupported("cut"), "Cut is supported in privilged JavaScript"); + </script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug1170911.html b/dom/tests/mochitest/general/test_bug1170911.html new file mode 100644 index 000000000..e739a0f32 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug1170911.html @@ -0,0 +1,90 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1012662 +--> +<head> + <title>Test for Bug 1170911</title> + <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/WindowSnapshot.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1170911">Mozilla Bug 1170911</a> +<p id="display"></p> + +<div id="content"> + <textarea>textarea text</textarea> +</div> + +<pre id="test"> +<script> +const TEXTAREA = document.querySelector('textarea'); +const TEXTAREA_VALUE = TEXTAREA.value; + +function doTest() { + is(document.queryCommandSupported("copy"), false, + "Copy support should have been disabled"); + is(document.queryCommandSupported("cut"), false, + "Cut support should have been disabled"); + + document.addEventListener("keydown", tryCopy); + synthesizeKey("Q", {}); +} + +function tryCopy(evt) { + evt.preventDefault(); + document.removeEventListener("keydown", tryCopy); + TEXTAREA.setSelectionRange(0, TEXTAREA_VALUE.length); + TEXTAREA.focus(); + + SimpleTest.waitForClipboard(null, function () { + is(document.queryCommandEnabled("copy"), false, + "Copy should not be allowed when dom.allow_cut_copy is off"); + is(document.execCommand("copy"), false, + "Copy should not be executed when dom.allow_cut_copy is off"); + is(TEXTAREA.value, TEXTAREA_VALUE, + "Content in the textarea shouldn't be changed"); + TEXTAREA.value = TEXTAREA_VALUE; + }, + /* success fn */ SimpleTest.finish, + /* failure fn */ function () { + document.addEventListener("keydown", tryCut); + synthesizeKey("Q", {}); + }, + /* flavor */ undefined, + /* timeout */ undefined, + /* expect failure */ true); +} + +function tryCut(evt) { + evt.preventDefault(); + document.removeEventListener("keydown", tryCut); + TEXTAREA.setSelectionRange(0, TEXTAREA_VALUE.length); + TEXTAREA.focus(); + + SimpleTest.waitForClipboard(null, function () { + is(document.queryCommandEnabled("cut"), false, + "Cut should not be allowed when dom.allow_cut_copy is off"); + is(document.execCommand("cut"), false, + "Cut should not be executed when dom.allow_cut_copy is off"); + is(TEXTAREA.value, TEXTAREA_VALUE, + "Content in the textarea shouldn't be changed"); + }, + /* success fn */ SimpleTest.finish, + /* failure fn */ SimpleTest.finish, + /* flavor */ undefined, + /* timeout */ undefined, + /* expect failure */ true); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(() => { + SpecialPowers.pushPrefEnv({"set": [["dom.allow_cut_copy", false]]}, doTest); +}); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug1208217.html b/dom/tests/mochitest/general/test_bug1208217.html new file mode 100644 index 000000000..3fdd74549 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug1208217.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1208217 +--> +<head> + <title>Test for Bug 1208217</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> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1208217">Mozilla Bug 1208217</a> +<script type="application/javascript"> + + add_task(function*() { + let pasteCount = 0; + document.addEventListener('paste', function() { + pasteCount++; + }); + + is(pasteCount, 0); + + synthesizeKey("V", {accelKey: true}); + + is(pasteCount, 1); + }); + +</script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug1313753.html b/dom/tests/mochitest/general/test_bug1313753.html new file mode 100644 index 000000000..85a0080b7 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug1313753.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Test for bug 1313753</title> +<script type="application/javascript" + src="/tests/SimpleTest/SimpleTest.js"></script> +<div id="log"></div> +<script> +function runTest() { + // Change visible region of |closure| element. + document.getElementById("target").classList.add("rotate"); + window.setTimeout(function() { + var target = document.getElementById("target"); + var bounds = target.getBoundingClientRect(); + var x = bounds.x + bounds.width / 2; + var y = bounds.y + bounds.height / 2; + is(document.elementFromPoint(x, y).id, target.id, + "it should be |target| element if visible regions of closure is correct"); + SimpleTest.finish(); + }, 0); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(runTest); +</script> + +<style> +.panel { + transform: rotateX(-150deg); + backface-visibility: hidden; + transform-origin: 0px 0px; + position: absolute; + display: block; + width: 100px; + height: 100px; + background-color: green; +} +#closure .rotate { + transform: rotateX(0deg); +} +#closure { + perspective: 100px; + width: 200px; + z-index: 1; +} +#outer { + height: 400px; + width: 200px; +} +</style> +<div id="outer"> + <div id="closure"> + <div style="transform-style: preserve-3d;"> + <div style="transform-style: preserve-3d; background-color: blue;"> + <ul> + <li><div id="target" class="panel"></div> + </li> + </ul> + </div> + </div> + </div> +</div> diff --git a/dom/tests/mochitest/general/test_bug504220.html b/dom/tests/mochitest/general/test_bug504220.html new file mode 100644 index 000000000..a88180c89 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug504220.html @@ -0,0 +1,66 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=504220 +--> +<head> + <title>Test for Bug 504220</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body onload="run_test();"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=504220">Mozilla Bug 504220</a> +<p id="display"></p> +<div id="content"> + <iframe id="frame" style="height:100px; width:100px; border:0"></iframe> + <div id="status" style="display: none"></div> +</div> +<pre id="test"> +<script type="application/javascript;version=1.7"> + +/** Test for Bug 504220 **/ + +function run_test() { + ok("onhashchange" in document.body, + "document.body should contain 'onhashchange'."); + + ok("onhashchange" in window, "window should contain 'onhashchange'."); + + // window.onhashchange should mirror document.body.onhashchange. + var func = function() { return 42; }; + document.body.onhashchange = func; + is(window.onhashchange, func); + + // Likewise, document.body.hashchange should mirror window.onhashchange + numEvents = 0; + var func2 = function() { numEvents++; gGen.next() }; + window.onhashchange = func2; + is(document.body.onhashchange, func2); + + SimpleTest.waitForExplicitFinish(); + + function waitForHashchange() { + // Change the document's hash. If we've been running this test manually, + // the hash might already be "#foo", so we need to check in order to be + // sure we trigger a hashchange. + if (location.hash != "#foo") + location.hash = "#foo"; + else + location.hash = "#bar"; + + yield undefined; + + is(numEvents, 1, "Exactly one hashchange should have been fired."); + SimpleTest.finish(); + yield undefined; + } + + var gGen = waitForHashchange(); + gGen.next(); +} + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug628069_1.html b/dom/tests/mochitest/general/test_bug628069_1.html new file mode 100644 index 000000000..57690269a --- /dev/null +++ b/dom/tests/mochitest/general/test_bug628069_1.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=628069 +--> +<head> + <title>Test for Bug 628069</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=628069">Mozilla Bug 628069</a> +<p id="display"></p> +<div id="content"> + <iframe id="frame" style="height:100px; width:100px; border:0"></iframe> + <div id="status" style="display: none"></div> +</div> +<pre id="test"> +<script type="application/javascript;version=1.7"> + +/** Test for Bug 628069 **/ + +SimpleTest.waitForExplicitFinish(); + +popup = window.open('file_bug628069.html'); + +// Control flows into childLoad, once the popup loads. + +gOrigURL = null; +function childLoad() { + gOrigURL = popup.location + ''; + + popup.location.hash = '#hash'; + + // This should trigger a hashchange, so control should flow down to + // childHashchange. +} + +function childHashchange(e) { + is(e.oldURL, gOrigURL, 'event.oldURL'); + is(e.newURL, gOrigURL + '#hash', 'event.newURL'); + is(e.isTrusted, true, 'Hashchange event should be trusted.'); + popup.close(); + SimpleTest.finish(); +} + +</script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug628069_2.html b/dom/tests/mochitest/general/test_bug628069_2.html new file mode 100644 index 000000000..13ad88db9 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug628069_2.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=628069 +--> +<head> + <title>Test for Bug 628069</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=628069">Mozilla Bug 628069</a> +<p id="display"></p> +<div id="content"> + <iframe id="frame" style="height:100px; width:100px; border:0"></iframe> + <div id="status" style="display: none"></div> +</div> +<pre id="test"> +<script type="application/javascript;version=1.7"> + +/** Test for Bug 628069 **/ + +gotHashChange = 0; +document.addEventListener("hashChange", function(e) { + gotHashChange = 1; + is(e.oldURL, "oldURL"); + is(e.newURL, "newURL"); + is(e.isTrusted, false, "Hashchange event shouldn't be trusted."); +}, true); + +let hc = new HashChangeEvent("hashChange", { bubbles: true, + cancelable: false, + oldURL: "oldURL", + newURL: "newURL" }); +document.documentElement.dispatchEvent(hc); +is(gotHashChange, 1, 'Document received hashchange event.'); + +</script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug631440.html b/dom/tests/mochitest/general/test_bug631440.html new file mode 100644 index 000000000..7142bb5cf --- /dev/null +++ b/dom/tests/mochitest/general/test_bug631440.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=631440 +--> +<head> + <title>Test for Bug 631440</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=631440">Mozilla Bug 631440</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +var w = window.open("about:blank"); +w.addEventListener("load", function() { + w.close(); + SimpleTest.finish(); +}, false); + +try { + w.history.pushState(null, "title", "pushState.html"); + ok(false, "Should have thrown a security exception"); +} +catch (e) { + ok(true, "Should have thrown a security exception"); +} + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug653364.html b/dom/tests/mochitest/general/test_bug653364.html new file mode 100644 index 000000000..e77d6d180 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug653364.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=653364 +--> +<head> + <title>Test for Bug 653364</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=653364">Mozilla Bug 653364</a> +<p id="display"></p> +<div id="content"> + <iframe id="frame" style="height:100px; width:100px; border:0"></iframe> + <div id="status" style="display: none"></div> +</div> +<pre id="test"> +<script type="application/javascript;version=1.7"> + +/** Test for Bug 653364 **/ + +gotPopState = 0; +document.addEventListener("popState", function(e) { + gotPopState = 1; + is(e.state.foo, 'bar', "PopState event should have state we set."); + is(e.isTrusted, false, "PopState event shouldn't be trusted."); +}, true); + +let ps = new PopStateEvent("popState", { bubbles: true, + cancelable: false, + state: {'foo': 'bar'} }); +document.documentElement.dispatchEvent(ps); +is(gotPopState, 1, 'Document received PopState event.'); + +</script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_bug861217.html b/dom/tests/mochitest/general/test_bug861217.html new file mode 100644 index 000000000..157487d92 --- /dev/null +++ b/dom/tests/mochitest/general/test_bug861217.html @@ -0,0 +1,115 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=861217 +--> +<head> + <title>Test for Bug 861217</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> +<body onload="runTest()"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=861217">Mozilla Bug 861217</a> +<p id="display"></p> +<div id="content"> + <table border="0" cellpadding="0" cellspacing="0" style="table-layout: fixed; width: 50px"> + <tbody> + <tr> + <td id="tableCell1" style="overflow: hidden"><div style="width: 100px; height: 100px; background-color: DodgerBlue">1</div></td> + </tr> + <tr> + <td id="tableCell2" style="overflow: hidden"><div style="margin-top: 5px; margin-left: 7px; width: 100px; height: 100px; background-color: SkyBlue">2</div></td> + </tr> + <tr> + <td id="tableCell3" style="overflow: hidden"><div style="display: inline-block; margin-right: 8px; margin-bottom: 10px; width: 100px; height: 100px; background-color: Khaki">3</div></td> + </tr> + <tr> + <td id="tableCell4" style="overflow: hidden"><div style="display: inline-block; margin-right: 3px; margin-left: 1px; box-sizing: border-box; width: 100px; height: 100px; border-left: 6px solid black; border-bottom: 2px solid black; background-color: LightCoral">4</div></td> + </tr> + <tr> + <td id="tableCell5" style="overflow: hidden"><div style="display: inline-block; border-right: 9px solid black; width: 100px; height: 100px; background-color: LightSeaGreen">5</div></td> + </tr> + <tr> + <td id="tableCell6" style="overflow: hidden"><div style="box-sizing: border-box; width: 100px; height: 100px; padding-top: 3px; padding-right: 13px; background-color: Orange">6</div></td> + </tr> + <tr> + <td id="tableCell7" style="overflow: hidden"><div style="display: inline-block; margin-right: 11px; margin-left: 4px; box-sizing: border-box; width: 100px; height: 100px; border-right: 6px solid black; border-bottom: 8px solid black; padding-top: 5px; padding-right: 9px; padding-bottom: 8px; padding-left: 7px; background-color: Silver">7</div></td> + </tr> + <tr> + <td id="tableCell8" style="overflow: hidden"><div style="display: inline-block; margin-top: 7px; margin-bottom: 1px; border-right: 6px solid black; border-bottom: 8px solid black; padding-top: 5px; padding-right: 9px; padding-bottom: 8px; padding-left: 7px; width: 100px; height: 100px; background-color: Turquoise">8</div></td> + </tr> + </tbody> + </table> + <div id="status" style="display: none"></div> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +/** Test for Bug 861217 **/ +function runTest() { + var tableCell1 = document.getElementById("tableCell1"), + bcr1 = tableCell1.getBoundingClientRect(), + tableCell2 = document.getElementById("tableCell2"), + bcr2 = tableCell2.getBoundingClientRect(), + tableCell3 = document.getElementById("tableCell3"), + bcr3 = tableCell3.getBoundingClientRect(), + tableCell4 = document.getElementById("tableCell4"), + bcr4 = tableCell4.getBoundingClientRect(), + tableCell5 = document.getElementById("tableCell5"), + bcr5 = tableCell5.getBoundingClientRect(), + tableCell6 = document.getElementById("tableCell6"), + bcr6 = tableCell6.getBoundingClientRect(), + tableCell7 = document.getElementById("tableCell7"), + bcr7 = tableCell7.getBoundingClientRect(), + tableCell8 = document.getElementById("tableCell8"), + bcr8 = tableCell8.getBoundingClientRect(); + + is(bcr1.width, 50, "Width of bounding client rect of #tableCell1"); + is(tableCell1.scrollWidth, 100, "scrollWidth of #tableCell1"); + is(bcr1.height, 100, "Height of bounding client rect of #tableCell1"); + is(tableCell1.scrollHeight, 100, "scrollHeight of #tableCell1"); + + is(bcr2.width, 50, "Width of bounding client rect of #tableCell2"); + is(tableCell2.scrollWidth, 107, "scrollWidth of #tableCell2"); + is(bcr2.height, 105, "Height of bounding client rect of #tableCell2"); + is(tableCell2.scrollHeight, 105, "scrollHeight of #tableCell2"); + + is(bcr3.width, 50, "Width of bounding client rect of #tableCell3"); + is(tableCell3.scrollWidth, 108, "scrollWidth of #tableCell3"); + is(bcr3.height, 110, "Height of bounding client rect of #tableCell3"); + is(tableCell3.scrollHeight, 110, "scrollHeight of #tableCell3"); + + is(bcr4.width, 50, "Width of bounding client rect of #tableCell4"); + is(tableCell4.scrollWidth, 104, "scrollWidth of #tableCell4"); + is(bcr4.height, 100, "Height of bounding client rect of #tableCell4"); + is(tableCell4.scrollHeight, 100, "scrollHeight of #tableCell4"); + + is(bcr5.width, 50, "Width of bounding client rect of #tableCell5"); + is(tableCell5.scrollWidth, 109, "scrollWidth of #tableCell5"); + is(bcr5.height, 100, "Height of bounding client rect of #tableCell5"); + is(tableCell5.scrollHeight, 100, "scrollHeight of #tableCell5"); + + is(bcr6.width, 50, "Width of bounding client rect of #tableCell6"); + is(tableCell6.scrollWidth, 100, "scrollWidth of #tableCell6"); + is(bcr6.height, 100, "Height of bounding client rect of #tableCell6"); + is(tableCell6.scrollHeight, 100, "scrollHeight of #tableCell6"); + + is(bcr7.width, 50, "Width of bounding client rect of #tableCell7"); + is(tableCell7.scrollWidth, 115, "scrollWidth of #tableCell7"); + is(bcr7.height, 100, "Height of bounding client rect of #tableCell7"); + is(tableCell7.scrollHeight, 100, "scrollHeight of #tableCell7"); + + is(bcr8.width, 50, "Width of bounding client rect of #tableCell8"); + is(tableCell8.scrollWidth, 122, "scrollWidth of #tableCell8"); + is(bcr8.height, 129, "Height of bounding client rect of #tableCell8"); + is(tableCell8.scrollHeight, 129, "scrollHeight of #tableCell8"); + + SimpleTest.finish(); +} + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_clientRects.html b/dom/tests/mochitest/general/test_clientRects.html new file mode 100644 index 000000000..058db69f9 --- /dev/null +++ b/dom/tests/mochitest/general/test_clientRects.html @@ -0,0 +1,130 @@ +<!DOCTYPE HTML> +<html id="d9" style="width:800px; height:1000px"> +<head> + <title>Tests for getClientRects/getBoundingClientRect</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> +<body style="margin:0" onload="doTest()"> + +<script> +function isWithinEps(v1, v2, eps, msg) { + if (eps) { + ok(Math.abs(v1 - v2) < eps, msg + " (within " + eps + "); got " + v1 + ", expected " + v2); + } else { + is(v1, v2, msg); + } +} +function checkRect(clientRect, r, eps, exprMsg, restMsg) { + isWithinEps(clientRect.left, r[0], eps, exprMsg + ".left" + restMsg); + isWithinEps(clientRect.top, r[1], eps, exprMsg + ".top" + restMsg); + isWithinEps(clientRect.right, r[2], eps, exprMsg + ".right" + restMsg); + isWithinEps(clientRect.bottom, r[3], eps, exprMsg + ".bottom" + restMsg); + isWithinEps(clientRect.width, r[2] - r[0], eps, exprMsg + ".width" + restMsg); + isWithinEps(clientRect.height, r[3] - r[1], eps, exprMsg + ".height" + restMsg); +} +function doc(id) { + return document.getElementById(id).contentDocument; +} +function checkElement(id, list, eps, doc) { + var e = (doc || document).getElementById(id); + var clientRects = e.getClientRects(); + ok(clientRects instanceof e.ownerDocument.defaultView.Array, + "getClientRects retval should have Array.prototype on its proto chain"); + clientRects.map(function(rect) { + ok(rect instanceof DOMRect, "Should have a DOMRect here"); + }); + is(clientRects.length, list.length, "getClientRects().length for element '" + id + "'"); + var bounds = list.length > 0 ? list[0] : [0,0,0,0]; + for (var i = 0; i < clientRects.length && i < list.length; ++i) { + var r = list[i]; + r[2] += r[0]; + r[3] += r[1]; + checkRect(clientRects[i], r, eps, "getClientRects()[" + i + "]", " for element '" + id + "'"); + if (r[2] != r[0] && r[3] != r[1]) { + bounds[0] = Math.min(bounds[0], r[0]); + bounds[1] = Math.min(bounds[1], r[1]); + bounds[2] = Math.max(bounds[2], r[2]); + bounds[3] = Math.max(bounds[3], r[3]); + } + } + checkRect(e.getBoundingClientRect(), bounds, eps, "getBoundingClientRect()", " for element '" + id + "'"); +} +</script> + +<!-- Simple case --> +<div id="d1" style="position:absolute; left:50px; top:50px; width:20px; height:30px; background:pink;"></div> +<!-- Multiple boxes --> +<div style="position:absolute; left:50px; top:100px; width:400px; height:100px; -moz-column-count:2; -moz-column-gap:0; column-count:2; column-gap:0"> + <div id="d2"> + <div style="width:200px; height:100px; background:yellow"></div> + <div style="width:200px; height:100px; background:lime"></div> + </div> +</div> +<!-- No boxes --> +<div id="d3" style="display:none"></div> +<!-- Element in transform --> +<div style="-moz-transform:translate(50px, 50px); transform:translate(50px,50px); position:absolute; left:0; top:200px"> + <div id="d4" style="width:50px; height:50px; background:blue;"></div> +</div> +<svg style="position:absolute; left:50px; top:300px; width:100px; height:100px;"> + <!-- Element in SVG foreignobject --> + <foreignObject x="20" y="30" width="40" height="40"> + <div id="d5" style="width:40px; height:40px; background:pink;"></div> + </foreignObject> + <!-- SVG Element --> + <circle id="s1" cx="60" cy="60" r="10" fill="yellow"/> +</svg> +<!-- Element in transform with bounding-box --> +<div style="-moz-transform:rotate(45deg); transform:rotate(45deg); position:absolute; left:50px; top:450px; width:100px; height:100px;"> + <div id="d6" style="width:100px; height:100px; background:orange;"></div> +</div> +<!-- Element in two transforms; we should combine transforms instead of taking bounding-box twice --> +<div style="-moz-transform:rotate(45deg); transform:rotate(45deg); position:absolute; left:50px; top:550px; width:100px; height:100px;"> + <div style="-moz-transform:rotate(-45deg); transform:rotate(-45deg); width:100px; height:100px;"> + <div id="d7" style="width:100px; height:100px; background:lime;"></div> + </div> +</div> +<!-- Fixed-pos element --> +<div id="d8" style="position:fixed; left:50px; top:700px; width:100px; height:100px; background:gray;"></div> +<!-- Root element; see d9 --> +<!-- Element in iframe --> +<iframe id="f1" style="position:absolute; left:300px; top:0; width:100px; height:200px; border:none" + src="data:text/html,<div id='d10' style='position:absolute; left:0; top:25px; width:100px; height:100px; background:cyan'>"> +</iframe> +<!-- Root element in iframe --> +<iframe id="f2" style="position:absolute; left:300px; top:250px; width:100px; height:200px; border:none" + src="data:text/html,<html id='d11' style='width:100px; height:100px; background:magenta'>"> +</iframe> +<!-- Fixed-pos element in iframe --> +<iframe id="f3" style="position:absolute; left:300px; top:400px; border:none" + src="data:text/html,<div id='d12' style='position:fixed; left:0; top:0; width:100px; height:100px;'>"></iframe> + +<script> +function doTest() { + checkElement("d1", [[50,50,20,30]]); + checkElement("d2", [[50,100,200,100],[250,100,200,100]]); + checkElement("d3", []); + checkElement("d4", [[50,250,50,50]]); + checkElement("d5", [[70,330,40,40]]); + checkElement("s1", [[100,350,20,20]], 0.1); + var sqrt2 = Math.sqrt(2); + checkElement("d6", [[100 - 50*sqrt2,500 - 50*sqrt2,100*sqrt2,100*sqrt2]], 0.1); + checkElement("d7", [[50,550,100,100]]); + checkElement("d8", [[50,700,100,100]]); + checkElement("d9", [[0,0,800,1000]]); + checkElement("d10", [[0,25,100,100]], 0, doc("f1")); + checkElement("d11", [[0,0,100,100]], 0, doc("f2")); + checkElement("d12", [[0,0,100,100]], 0, doc("f3")); + SimpleTest.finish(); +} +SimpleTest.waitForExplicitFinish(); +</script> + +<p id="display"></p> +<div id="content" style="display: none"> + +</div> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_clipboard_disallowed.html b/dom/tests/mochitest/general/test_clipboard_disallowed.html new file mode 100644 index 000000000..71ba09d62 --- /dev/null +++ b/dom/tests/mochitest/general/test_clipboard_disallowed.html @@ -0,0 +1,61 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for Clipboard Events</title> + <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> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<input id="input" value="INPUT TEXT" oncopy="checkAllowed(event)"> + +<script> +function doTest() +{ + document.getElementById("input").focus(); + synthesizeKey("c", {accelKey: 1}); +} + +function checkAllowed(event) +{ + let clipboardData = event.clipboardData; + + let exception; + try { + clipboardData.mozSetDataAt("text/customdata", document.getElementById("input"), 0); + } catch(ex) { + exception = ex; + } + is(String(exception).indexOf("SecurityError"), 0, "Cannot set non-string"); + + exception = null; + try { + clipboardData.mozSetDataAt("application/x-moz-file", "Test", 0); + } catch(ex) { + exception = ex; + } + is(String(exception).indexOf("SecurityError"), 0, "Cannot set file"); + + exception = null; + try { + clipboardData.mozSetDataAt("application/x-moz-file-promise", "Test", 0); + } catch(ex) { + exception = ex; + } + is(String(exception).indexOf("SecurityError"), 0, "Cannot set file promise"); + + exception = null; + try { + clipboardData.mozSetDataAt("application/something", "This is data", 0); + } catch(ex) { + exception = ex; + } + is(exception, null, "Can set custom data to a string"); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(doTest); +</script> diff --git a/dom/tests/mochitest/general/test_clipboard_events.html b/dom/tests/mochitest/general/test_clipboard_events.html new file mode 100644 index 000000000..eb8e0258f --- /dev/null +++ b/dom/tests/mochitest/general/test_clipboard_events.html @@ -0,0 +1,725 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for Clipboard Events</title> + <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> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="border: 3px solid black; padding: 3em;">CONTENT TEXT<input id="content-input" value="INPUT TEXT"></div> +<img id="image" src="image_50.png"> +<button id="button">Button</button> + +<div id="syntheticSpot" oncut="compareSynthetic(event, 'cut')" + oncopy="compareSynthetic(event, 'copy')" + onpaste="compareSynthetic(event, 'paste')">Spot</div> + +<pre id="test"> +<script class="testbody" type="text/javascript;version=1.7"> + +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_image_dataTransfer, + ]; + +function doTests() +{ + // Init clipboard + setClipboardText(clipboardInitialValue); + + // 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"); +} + + +function setClipboardText(text) { + var helper = SpecialPowers.Cc["@mozilla.org/widget/clipboardhelper;1"] + .getService(SpecialPowers.Ci.nsIClipboardHelper); + helper.copyString(text); +} + +function selectContentDiv() { + // Set selection + var selection = window.getSelection(); + selection.removeAllRanges(); + selection.selectAllChildren(content); +} + +function selectContentInput() { + contentInput.select(); + contentInput.focus(); +} + +function test_dom_oncopy() { + // 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}); + ok(oncopy_fired, "copy event firing on DOM element"); + is(getClipboardText(), "CONTENT TEXT", + "copy on DOM element set clipboard correctly"); + } finally { + content.oncopy = null; + } +} + +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". + selectContentDiv(); + var oncut_fired = false; + content.oncut = function() { oncut_fired = true; }; + try { + synthesizeKey("x", {accelKey: 1}); + ok(oncut_fired, "cut event firing on DOM element") + is(getClipboardText(), clipboardInitialValue, + "cut on DOM element did not modify clipboard"); + } finally { + content.oncut = null; + } +} + + +function test_dom_onpaste() { + // Setup an onpaste event handler, fire paste. Ensure that the event + // handler was called. + selectContentDiv(); + var onpaste_fired = false; + content.onpaste = function() { onpaste_fired = true; }; + try { + synthesizeKey("v", {accelKey: 1}); + ok(onpaste_fired, "paste event firing on DOM element"); + } finally { + content.onpaste = null; + } +} + + +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. + selectContentDiv(); + var oncopy_fired = false; + content.oncopy = function() { oncopy_fired = true; return false; }; + try { + synthesizeKey("c", {accelKey: 1}); + 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; + } +} + + +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. + selectContentInput(); + contentInput.focus(); + contentInput.setSelectionRange(2, 8); + + var oncopy_fired = false; + contentInput.oncopy = function() { oncopy_fired = true; }; + try { + synthesizeKey("c", {accelKey: 1}); + ok(oncopy_fired, "copy event firing on plaintext editor"); + is(getClipboardText(), "PUT TE", + "copy on plaintext editor set clipboard correctly"); + } finally { + contentInput.oncopy = null; + } +} + + +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. + selectContentInput(); + var oncut_fired = false; + contentInput.oncut = function() { oncut_fired = true; }; + try { + synthesizeKey("x", {accelKey: 1}); + 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; + } +} + + +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). + selectContentInput(); + var onpaste_fired = false; + contentInput.onpaste = function() { onpaste_fired = true; }; + try { + synthesizeKey("v", {accelKey: 1}); + ok(onpaste_fired, "paste event firing on plaintext editor"); + is(getClipboardText(), clipboardInitialValue, + "paste on plaintext editor did not modify clipboard contents"); + is(contentInput.value, clipboardInitialValue, + "paste on plaintext editor did modify editor value"); + } finally { + contentInput.onpaste = null; + } +} + + +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}); + 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; + } +} + + +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. + selectContentInput(); + var oncut_fired = false; + contentInput.oncut = function() { oncut_fired = true; return false; }; + try { + synthesizeKey("x", {accelKey: 1}); + 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; + } +} + + +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). + selectContentInput(); + var onpaste_fired = false; + contentInput.onpaste = function() { onpaste_fired = true; return false; }; + try { + synthesizeKey("v", {accelKey: 1}); + ok(onpaste_fired, + "paste event (to-be-cancelled) firing on plaintext editor"); + is(getClipboardText(), clipboardInitialValue, + "aborted paste on plaintext editor did not modify clipboard"); + is(contentInput.value, "INPUT TEXT", + "aborted paste on plaintext editor did not modify modified editor value"); + } finally { + contentInput.onpaste = null; + } +} + + +function test_input_cut_dataTransfer() { + // Cut using event.dataTransfer. The event is not cancelled so the default + // cut should occur + selectContentInput(); + contentInput.oncut = function(event) { + ok(event instanceof ClipboardEvent, "cut event is a ClipboardEvent"); + ok(event.clipboardData instanceof DataTransfer, "cut event dataTransfer is a DataTransfer"); + is(event.target, contentInput, "cut event target"); + is(event.clipboardData.mozItemCount, 0, "cut event mozItemCount"); + is(event.clipboardData.getData("text/plain"), "", "cut event getData"); + event.clipboardData.setData("text/plain", "This is some dataTransfer text"); + cachedCutData = event.clipboardData; + }; + try { + synthesizeKey("x", {accelKey: 1}); + is(getClipboardText(), "INPUT TEXT", + "cut using dataTransfer on plaintext editor set clipboard correctly"); + is(contentInput.value, "", + "cut using dataTransfer on plaintext editor cleared input"); + } finally { + contentInput.oncut = null; + } +} + + +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. + selectContentInput(); + contentInput.oncut = function(event) { + event.clipboardData.setData("text/plain", "Cut dataTransfer text"); + return false; + }; + try { + synthesizeKey("x", {accelKey: 1}); + is(getClipboardText(), "Cut dataTransfer text", + "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; + } +} + + +function test_input_copy_dataTransfer() { + // Copy using event.dataTransfer + selectContentInput(); + contentInput.oncopy = function(event) { + ok(event instanceof ClipboardEvent, "copy event is a ClipboardEvent"); + ok(event.clipboardData instanceof DataTransfer, "copy event dataTransfer is a DataTransfer"); + is(event.target, contentInput, "copy event target"); + is(event.clipboardData.mozItemCount, 0, "copy event mozItemCount"); + is(event.clipboardData.getData("text/plain"), "", "copy event getData"); + event.clipboardData.setData("text/plain", "Copied dataTransfer text"); + cachedCopyData = event.clipboardData; + }; + try { + synthesizeKey("c", {accelKey: 1}); + is(getClipboardText(), "INPUT TEXT", + "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; + } +} + + +function test_input_copy_abort_dataTransfer() { + // Copy using event.dataTransfer but cancel the event. + selectContentInput(); + contentInput.oncopy = function(event) { + event.clipboardData.setData("text/plain", "Copy dataTransfer text"); + return false; + }; + try { + synthesizeKey("x", {accelKey: 1}); + is(getClipboardText(), "Copy dataTransfer text", + "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; + } +} + + +function test_input_paste_dataTransfer() { + // Paste using event.dataTransfer + selectContentInput(); + contentInput.onpaste = function(event) { + ok(event instanceof ClipboardEvent, "paste event is an ClipboardEvent"); + ok(event.clipboardData instanceof DataTransfer, "paste event dataTransfer is a DataTransfer"); + is(event.target, contentInput, "paste event target"); + is(event.clipboardData.mozItemCount, 1, "paste event mozItemCount"); + is(event.clipboardData.getData("text/plain"), clipboardInitialValue, "paste event getData"); + cachedPasteData = event.clipboardData; + }; + try { + synthesizeKey("v", {accelKey: 1}); + is(getClipboardText(), clipboardInitialValue, + "paste using dataTransfer on plaintext editor did not modify clipboard contents"); + is(contentInput.value, clipboardInitialValue, + "paste using dataTransfer on plaintext editor modified input"); + } finally { + contentInput.onpaste = null; + } +} + + +function test_input_paste_abort_dataTransfer() { + // Paste using event.dataTransfer but cancel the event + selectContentInput(); + contentInput.onpaste = function(event) { + is(event.clipboardData.getData("text/plain"), clipboardInitialValue, "get data on aborted paste"); + contentInput.value = "Alternate Paste"; + return false; + }; + try { + synthesizeKey("v", {accelKey: 1}); + is(getClipboardText(), clipboardInitialValue, + "aborted paste using dataTransfer on plaintext editor did not modify clipboard contents"); + is(contentInput.value, "Alternate Paste", + "aborted paste using dataTransfer on plaintext editor modified input"); + } finally { + contentInput.onpaste = null; + } +} + +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) { + var cd = event.clipboardData; + cd.setData("text/plain", "would be a phrase"); + + var exh = false; + try { cd.mozSetDataAt("text/plain", "Text", 1); } catch (ex) { exh = true; } + ok(exh, "exception occured mozSetDataAt 1"); + exh = false; + try { cd.mozTypesAt(1); } catch (ex) { exh = true; } + ok(exh, "exception occured mozTypesAt 1"); + exh = false; + try { cd.mozGetDataAt("text/plain", 1); } catch (ex) { exh = true; } + ok(exh, "exception occured mozGetDataAt 1"); + exh = false; + try { cd.mozClearDataAt("text/plain", 1); } catch (ex) { exh = true; } + ok(exh, "exception occured mozClearDataAt 1"); + + cd.setData("text/x-moz-url", "http://www.mozilla.org"); + cd.mozSetDataAt("text/x-custom", "Custom Text with \u0000 null", 0); + is(cd.mozItemCount, 1, "mozItemCount after set multiple types"); + return false; + }; + + try { + selectContentInput(); + synthesizeKey("c", {accelKey: 1}); + } + finally { + contentInput.oncopy = null; + } + + is(getClipboardText(), "would be a phrase", "copy multiple types text"); + + contentInput.setSelectionRange(5, 14); + + contentInput.onpaste = function(event) { + var cd = event.clipboardData; + is(cd.mozItemCount, 1, "paste after copy multiple types mozItemCount"); + is(cd.getData("text/plain"), "would be a phrase", "paste text/plain multiple types"); + + // Firefox for Android's clipboard code doesn't handle x-moz-url. Therefore + // disabling the following test. Enable this once bug #840101 is fixed. + if (navigator.appVersion.indexOf("Android") == -1) { + is(cd.getData("text/x-moz-url"), "http://www.mozilla.org", "paste text/x-moz-url multiple types"); + is(cd.getData("text/x-custom"), "Custom Text with \u0000 null", "paste text/custom multiple types"); + } else { + is(cd.getData("text/x-custom"), "", "paste text/custom multiple types"); + } + + is(cd.getData("application/x-moz-custom-clipdata"), "", "application/x-moz-custom-clipdata is not present"); + + exh = false; + try { cd.setData("application/x-moz-custom-clipdata", "Some Data"); } catch (ex) { exh = true; } + ok(exh, "exception occured setData with application/x-moz-custom-clipdata"); + + exh = false; + try { cd.setData("text/plain", "Text on Paste"); } catch (ex) { exh = true; } + ok(exh, "exception occured setData on paste"); + + is(cd.getData("text/plain"), "would be a phrase", "text/plain data unchanged"); + }; + try { + synthesizeKey("v", {accelKey: 1}); + is(contentInput.value, "This would be a phrase of text", + "default paste after copy multiple types"); + } finally { + contentInput.onpaste = null; + } +} + +function test_input_copy_button_dataTransfer() { + // Copy using event.dataTransfer when a button is focused. + var button = document.getElementById("button"); + button.focus(); + button.oncopy = function(event) { + ok(false, "should not be firing copy event on button"); + return false; + }; + 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"); + + selectContentDiv(); + synthesizeKey("c", {accelKey: 1}); + is(getClipboardText(), "CONTENT TEXT", + "copy using dataTransfer with selection on plaintext editor set clipboard correctly for button"); + + } finally { + document.documentElement.oncopy = null; + } +} + +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); + } + }); +} + +let expectedData = []; + +// Check to make that synthetic events do not change the clipboard +function checkSyntheticEvents() +{ + let syntheticSpot = document.getElementById("syntheticSpot"); + setClipboardText(clipboardInitialValue); + + // No dataType specified + let event = new ClipboardEvent("cut", { data: "something" }); + expectedData = { type: "cut", data: null } + compareSynthetic(event, "before"); + syntheticSpot.dispatchEvent(event); + ok(expectedData.eventFired, "cut event fired"); + compareSynthetic(event, "after"); + + event = new ClipboardEvent("cut", { dataType: "text/plain", data: "something" }); + expectedData = { type: "cut", dataType: "text/plain", data: "something" } + compareSynthetic(event, "before"); + syntheticSpot.dispatchEvent(event); + ok(expectedData.eventFired, "cut event fired"); + compareSynthetic(event, "after"); + + event = new ClipboardEvent("copy", { dataType: "text/plain", data: "something" }); + expectedData = { type: "copy", dataType: "text/plain", data: "something" } + compareSynthetic(event, "before"); + syntheticSpot.dispatchEvent(event); + ok(expectedData.eventFired, "copy event fired"); + compareSynthetic(event, "after"); + + event = new ClipboardEvent("copy", { dataType: "text/plain" }); + expectedData = { type: "copy", dataType: "text/plain", data: "" } + compareSynthetic(event, "before"); + syntheticSpot.dispatchEvent(event); + ok(expectedData.eventFired, "copy event fired"); + compareSynthetic(event, "after"); + + event = new ClipboardEvent("paste", { dataType: "text/plain", data: "something" }); + expectedData = { type: "paste", dataType: "text/plain", data: "something" } + compareSynthetic(event, "before"); + syntheticSpot.dispatchEvent(event); + ok(expectedData.eventFired, "paste event fired"); + compareSynthetic(event, "after"); + + event = new ClipboardEvent("paste", { dataType: "application/unknown", data: "unknown" }); + expectedData = { type: "paste", dataType: "application/unknown", data: "unknown" } + compareSynthetic(event, "before"); + syntheticSpot.dispatchEvent(event); + ok(expectedData.eventFired, "paste event fired"); + compareSynthetic(event, "after"); +} + +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"); + } + + ok(event.clipboardData instanceof DataTransfer, "clipboardData is assigned"); + + is(event.type, expectedData.type, "synthetic " + eventtype + " event type"); + if (expectedData.data === null) { + is(event.clipboardData.mozItemCount, 0, "synthetic " + eventtype + " empty data"); + } + else { + is(event.clipboardData.mozItemCount, 1, "synthetic " + eventtype + " item count"); + is(event.clipboardData.types.length, 1, "synthetic " + eventtype + " types length"); + is(event.clipboardData.getData(expectedData.dataType), expectedData.data, + "synthetic " + eventtype + " data"); + } + + is(getClipboardText(), "empty", "event does not change the clipboard " + step + " dispatch"); + + if (step == "during") { + expectedData.eventFired = true; + } +} + +function checkCachedDataTransfer(cd, eventtype) +{ + var testprefix = "cached " + eventtype + " dataTransfer"; + + setClipboardText("Some Clipboard Text"); + + var oldtext = cd.getData("text/plain"); + ok(oldtext != "Some Clipboard Text", "clipboard get using " + testprefix); + + var exh = false; + try { cd.mozSetDataAt("text/plain", "Test Cache Data", 0); } catch (ex) { exh = true; } + ok(eventtype == "paste" ? exh : !exh, "exception occured setting " + testprefix); + + var newtext = (eventtype == "paste") ? cd.getData("text/plain") : + cd.mozGetDataAt("text/plain", 0); + is(newtext, (eventtype == "paste") ? oldtext : "Test Cache Data", + " clipboardData not changed using " + testprefix); + + is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix); + + var exh = false; + try { cd.mozClearDataAt("text/plain", 0); } catch (ex) { exh = true; } + ok(eventtype == "paste" ? exh : !exh, "exception occured clearing " + testprefix); + + is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix); +} + +// Try copying an image to the clipboard and make sure that it looks correct when pasting it. +function test_image_dataTransfer() { + // 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"); + + let onpasteCalled = false; + document.onpaste = function(event) { + ok(event instanceof ClipboardEvent, "paste event is an ClipboardEvent"); + ok(event.clipboardData instanceof DataTransfer, "paste event dataTransfer is a DataTransfer"); + let items = event.clipboardData.items; + let foundData = false; + for (let i = 0; i < items.length; ++i) { + if (items[i].kind == "file") { + foundData = true; + is(items[i].type, "image/png", "The type of the data must be image/png"); + is(items[i].getAsFile().type, "image/png", "The attached file must be image/png"); + } + } + ok(foundData, "Should have found a file entry in the DataTransferItemList"); + let files = event.clipboardData.files; + is(files.length, 1, "There should only be one file on the DataTransfer"); + is(files[0].type, "image/png", "The only file should be an image/png"); + onpasteCalled = true; + } + + try { + synthesizeKey("v", {accelKey: 1}); + ok(onpasteCalled, "The paste event listener must have been called"); + } finally { + document.onpaste = null; + } +} + +SimpleTest.waitForFocus(doTests); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_consoleAPI.html b/dom/tests/mochitest/general/test_consoleAPI.html new file mode 100644 index 000000000..8d710a51a --- /dev/null +++ b/dom/tests/mochitest/general/test_consoleAPI.html @@ -0,0 +1,67 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>window.console test</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> + +<body id="body"> + +<script type="application/javascript;version=1.8"> + +function doTest() { + ok(window.console, "console exists"); + + try { + ok(!console.foo, "random property doesn't throw"); + } catch (ex) { + ok(false, "random property threw: " + ex); + } + + var expectedProps = { + "log": "function", + "info": "function", + "warn": "function", + "error": "function", + "exception": "function", + "debug": "function", + "trace": "function", + "dir": "function", + "group": "function", + "groupCollapsed": "function", + "groupEnd": "function", + "time": "function", + "timeEnd": "function", + "profile": "function", + "profileEnd": "function", + "assert": "function", + "count": "function", + "table": "function", + "clear": "function", + "dirxml": "function", + "markTimeline": "function", + "timeline": "function", + "timelineEnd": "function", + "timeStamp": "function", + }; + + var foundProps = 0; + for (var prop in console) { + foundProps++; + is(typeof(console[prop]), expectedProps[prop], "expect console prop " + prop + " exists"); + } + is(foundProps, Object.keys(expectedProps).length, "found correct number of properties"); + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +addLoadEvent(doTest); + +</script> + +<p id="display"></p> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html b/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html new file mode 100644 index 000000000..f7e2024f6 --- /dev/null +++ b/dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html @@ -0,0 +1,358 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>nsIContentViewer::overrideDPPX test</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> + +<body> + +<iframe></iframe> + +<script type="application/javascript;version=1.8"> + +SimpleTest.waitForExplicitFinish(); + +const frameWindow = document.querySelector("iframe").contentWindow; + +const originalDPR = window.devicePixelRatio; +const originalZoom = SpecialPowers.getFullZoom(window); +const dppx = originalDPR * 1.5; +const zoom = originalZoom * 0.5; + +const setOverrideDPPX = (value) => { + if (value > 0) { + info(`override window's dppx to ${value}`); + } else { + info(`restore window's dppx to default value`); + } + + SpecialPowers.setOverrideDPPX(window, value); +} + +const setFullZoom = (value) => { + info(`set window's fullZoom to ${value}`); + SpecialPowers.setFullZoom(window, value); +} + +const createFontStyleForDPPX = (doc, value, size) => { + info(`adding a stylesheet that set font size to ${size}px for ${value}dppx`); + + let style = doc.createElement("style"); + + style.setAttribute("media", `(resolution: ${value}dppx)`); + + doc.head.appendChild(style); + + style.sheet.insertRule(`body {font-size: ${size}px}`, 0); + + return style; +} + +const getBodyFontSize = (w) => w.getComputedStyle(w.document.body).fontSize; + +const assertValuesAreInitial = () => { + is(window.devicePixelRatio, originalDPR, + "devicePixelRatio has the original value."); + is(SpecialPowers.getFullZoom(window), originalZoom, + "fullZoom has the original value."); + + is(frameWindow.devicePixelRatio, originalDPR, + "devicePixelRatio has the original value."); + is(SpecialPowers.getFullZoom(frameWindow), originalZoom, + "fullZoom has the original value."); +} + +const gTests = { + "test overrideDPPX with devicePixelRatio": (done) => { + assertValuesAreInitial(); + + setOverrideDPPX(dppx); + + is(window.devicePixelRatio, dppx, + "devicePixelRatio overridden."); + is(frameWindow.devicePixelRatio, dppx, + "frame's devicePixelRatio overridden."); + + setOverrideDPPX(0); + + is(window.devicePixelRatio, originalDPR, + "devicePixelRatio back to default."); + is(frameWindow.devicePixelRatio, originalDPR, + "frame's devicePixelRatio back to default."); + + done(); + }, + "test overrideDPPX with devicePixelRatio and fullZoom": (done) => { + assertValuesAreInitial(); + + setFullZoom(zoom); + setOverrideDPPX(dppx); + + is(window.devicePixelRatio, dppx, + "devicePixelRatio overridden; fullZoom ignored"); + is(frameWindow.devicePixelRatio, dppx, + "frame's devicePixelRatio overridden; fullZoom ignored"); + + setOverrideDPPX(0); + + is(window.devicePixelRatio, originalDPR * zoom, + "devicePixelRatio now is affected by fullZoom"); + is(frameWindow.devicePixelRatio, originalDPR * zoom, + "frame's devicePixelRatio now is affected by fullZoom"); + isnot(dppx, originalDPR * zoom, + "test is no longer testing what it should be"); + + setFullZoom(originalZoom); + + is(window.devicePixelRatio, originalDPR, + "devicePixelRatio back to default."); + is(frameWindow.devicePixelRatio, originalDPR, + "frame's devicePixelRatio back to default."); + + done(); + + }, + "test overrideDPPX with media queries": (done) => { + assertValuesAreInitial(); + + let frameDoc = frameWindow.document; + + let originalFontSize = getBodyFontSize(window); + let frameOriginalFontSize = getBodyFontSize(frameWindow); + + let style = createFontStyleForDPPX(document, dppx, "32"); + let frameStyle = createFontStyleForDPPX(frameDoc, dppx, "32"); + + let currentFontSize = getBodyFontSize(window); + let frameCurrentFontSize = getBodyFontSize(frameWindow); + + is(currentFontSize, originalFontSize, + "media queries are not applied yet"); + is(frameCurrentFontSize, frameOriginalFontSize, + "frame's media queries are not applied yet"); + + setOverrideDPPX(dppx); + + currentFontSize = getBodyFontSize(window); + frameCurrentFontSize = getBodyFontSize(frameWindow); + + isnot(currentFontSize, originalFontSize, + "media queries are applied."); + isnot(frameCurrentFontSize, frameOriginalFontSize, + "frame's media queries are applied."); + + is(currentFontSize, "32px", + "font size has the expected value."); + is(frameCurrentFontSize, "32px", + "frame's font size has the expected value."); + + setOverrideDPPX(0); + + currentFontSize = getBodyFontSize(window); + frameCurrentFontSize = getBodyFontSize(frameWindow); + + is(currentFontSize, originalFontSize, + "media queries are not applied anymore."); + is(frameCurrentFontSize, frameOriginalFontSize, + "media queries are not applied anymore."); + + style.remove(); + frameStyle.remove(); + + done(); + }, + "test overrideDPPX with media queries and fullZoom": (done) => { + assertValuesAreInitial(); + + let frameDoc = frameWindow.document; + + let styles = [ + createFontStyleForDPPX(document, originalDPR, "23"), + createFontStyleForDPPX(document, dppx, "32"), + createFontStyleForDPPX(document, originalDPR * zoom, "48"), + createFontStyleForDPPX(frameDoc, originalDPR, "23"), + createFontStyleForDPPX(frameDoc, dppx, "32"), + createFontStyleForDPPX(frameDoc, originalDPR * zoom, "48") + ]; + + let currentFontSize = getBodyFontSize(window); + let frameCurrentFontSize = getBodyFontSize(frameWindow); + is(currentFontSize, "23px", + "media queries are not applied yet"); + is(frameCurrentFontSize, "23px", + "frame's media queries are not applied yet"); + + setFullZoom(zoom); + setOverrideDPPX(dppx); + + currentFontSize = getBodyFontSize(window); + frameCurrentFontSize = getBodyFontSize(frameWindow); + is(currentFontSize, "32px", + "media queries are applied for overridden DDPX; fullZoom ignored."); + is(frameCurrentFontSize, "32px", + "frame's media queries are applied for overridden DDPX; fullZoom ignored."); + + setOverrideDPPX(0); + + currentFontSize = getBodyFontSize(window); + frameCurrentFontSize = getBodyFontSize(frameWindow); + is(currentFontSize, "48px", + "media queries are applied for fullZoom."); + is(frameCurrentFontSize, "48px", + "frame's media queries are applied for fullZoom."); + + setFullZoom(originalZoom); + + currentFontSize = getBodyFontSize(window); + frameCurrentFontSize = getBodyFontSize(frameWindow); + is(currentFontSize, "23px", + "media queries are not applied anymore."); + is(frameCurrentFontSize, "23px", + "frame's media queries are not applied anymore."); + + styles.forEach(style => style.remove()); + + done(); + }, + "test OverrideDPPX with MediaQueryList": (done) => { + assertValuesAreInitial(); + + let promises = [ + new Promise(resolve => { + let mql = window.matchMedia(`(resolution: ${dppx}dppx)`); + + mql.addListener(function listener() { + ok("MediaQueryList's listener invoked.") + mql.removeListener(listener); + resolve(); + }); + }), + new Promise(resolve => { + let mql = frameWindow.matchMedia(`(resolution: ${dppx}dppx)`); + + mql.addListener(function listener() { + ok("frame's MediaQueryList's listener invoked.") + mql.removeListener(listener); + resolve(); + }); + }) + ]; + + Promise.all(promises) + .then(() => setOverrideDPPX(0)) + .then(done, e => {throw e}); + + setOverrideDPPX(dppx); + }, + "test OverrideDPPX with MediaQueryList and fullZoom": (done) => { + assertValuesAreInitial(); + + let overridden = false; + + let promises = [ + new Promise(resolve => { + let mql = window.matchMedia(`(resolution: ${dppx}dppx)`); + + mql.addListener(function listener() { + ok("MediaQueryList's listener for dppx invoked."); + mql.removeListener(listener); + overridden = true; + resolve(); + }); + }), + new Promise(resolve => { + let mql = window.matchMedia(`(resolution: ${originalDPR * zoom}dppx)`); + + mql.addListener(function listener() { + ok(overridden, + "MediaQueryList's listener for zoom invoked in the right order"); + + mql.removeListener(listener); + resolve(); + }); + }) + ]; + + promises[0] + .then(() => setOverrideDPPX(0)) + .then(promises[1]) + .then(() => setFullZoom(originalZoom)) + .then(done, e => {throw e}); + + setOverrideDPPX(dppx); + setFullZoom(zoom); + }, + "test OverrideDPPX is kept on document navigation": (done) => { + assertValuesAreInitial(); + + let frameOriginalFontSize = getBodyFontSize(frameWindow); + let frameStyle = createFontStyleForDPPX(frameWindow.document, dppx, "32"); + let frameCurrentFontSize = getBodyFontSize(frameWindow); + + is(frameCurrentFontSize, frameOriginalFontSize, + "frame's media queries are not applied yet"); + + setOverrideDPPX(dppx); + + frameCurrentFontSize = getBodyFontSize(frameWindow); + + is(frameWindow.devicePixelRatio, dppx, + "frame's devicePixelRatio overridden."); + isnot(frameCurrentFontSize, frameOriginalFontSize, + "frame's media queries are applied."); + is(frameCurrentFontSize, "32px", + "frame's font size has the expected value."); + + frameWindow.frameElement.addEventListener("load", function listener() { + this.removeEventListener("load", listener); + + frameStyle = createFontStyleForDPPX(frameWindow.document, dppx, "32"); + + frameCurrentFontSize = getBodyFontSize(frameWindow); + + is(frameWindow.devicePixelRatio, dppx, + "frame's devicePixelRatio is still overridden."); + isnot(frameCurrentFontSize, frameOriginalFontSize, + "frame's media queries are still applied."); + is(frameCurrentFontSize, "32px", + "frame's font size has still the expected value."); + + frameStyle.remove(); + + setOverrideDPPX(0); + + done(); + }); + + frameWindow.location.reload(true); + } +}; + +function* runner(tests) { + for (let name of Object.keys(tests)) { + info(name); + tests[name](next); + yield undefined; + } +}; + +const gTestRunner = runner(gTests); + +function next() { + SimpleTest.executeSoon(function() { + if (gTestRunner.next().done) { + SimpleTest.finish(); + } + }); +} + +// Run the tests +addLoadEvent(next); + +</script> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_domWindowUtils.html b/dom/tests/mochitest/general/test_domWindowUtils.html new file mode 100644 index 000000000..377900c80 --- /dev/null +++ b/dom/tests/mochitest/general/test_domWindowUtils.html @@ -0,0 +1,197 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test nsIDOMWindowUtils</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + <style> + html, body, div { + padding: 0; + margin: 0; + } + + div.test { + position: absolute; + height: 10px; + width: 10px; + } + </style> +</head> + +<body id="body"> + +<div class="test" id="onscreen" style="top: 100px; background-color: red;"></div> +<div class="test" id="offscreen" style="top: 100000px; background-color: green;"></div> + +<script type="application/javascript;version=1.8"> + +SimpleTest.waitForExplicitFinish(); + +var domWindowUtils = SpecialPowers.getDOMWindowUtils(window); + +var gTests = [ +/* + nsIDOMElement elementFromPoint(in long aX, + in long aY, + in boolean aIgnoreRootScrollFrame, + in boolean aFlushLayout); +*/ +function testElementFromPoint() { + let onscreen = document.getElementById("onscreen"); + let offscreen = document.getElementById("offscreen"); + let htmldoc = document.documentElement; + ok(onscreen, "on screen element exists"); + ok(offscreen, "off screen element exists"); + ok(htmldoc, "htmldoc element exists"); + + let testData = [ + // default behavior is to return null for items outside the viewport + [[0, 100], null, onscreen], + [[9, 109], null, onscreen], + [[0, 100000], null, null], + [[9, 100009], null, null], + + // ignore scroll frame + [[0, 100, true, false], null, onscreen], + [[9, 109, true, false], null, onscreen], + [[0, 100000, true, false], null, offscreen], + [[9, 100009, true, false], null, offscreen], + + // layout flush tests + // test moving element 10px to the left and down, and flushing layout + [[10, 110, false, true], [[10, 110], onscreen], onscreen], + // test moving element back, not flushing layout + // (will get the html document instead) + [[0, 100, false, false], [[0, 100], onscreen], htmldoc], + // test element at same position, flushing layout + [[0, 100, false, true], [[0, 100], onscreen], onscreen], + + // same tests repeated for offscreen element + [[10, 100010, true, true], [[10, 100010], offscreen], offscreen], + [[0, 100000, true, false], [[0, 100000], offscreen], htmldoc], + [[0, 100000, true, true], [[0, 100000], offscreen], offscreen], + ]; + + for (let i = 0; i < testData.length; ++i) { + let [x, y, ignoreScroll, flushLayout] = testData[i][0]; + let moveData = testData[i][1]; + let expected = testData[i][2]; + + if (moveData) { + let moveEl = moveData[1]; + let [moveX, moveY] = moveData[0]; + + moveEl.style.left = moveX + "px"; + moveEl.style.top = moveY + "px"; + } + let found = SpecialPowers.unwrap(domWindowUtils.elementFromPoint( + x, y, ignoreScroll, flushLayout)); + is(found, expected, "at index " + i + " for data " + testData[i][0].toSource()); + } + + SimpleTest.executeSoon(function() { + next(); + }); +}, + +/** + * Test .isHandlingUserInput attribute. + */ +function testHandlingUserInput() { + ok('isHandlingUserInput' in domWindowUtils, + "isHandlingUserInput should be present"); + + is(domWindowUtils.isHandlingUserInput, false, + "isHandlingUserInput should return false if nothing is happening"); + + function testEvents() { + var data = [ + { + eventName: "click", + result: true, + }, + { + eventName: "mousemove", + result: false, + }, + { + eventName: "mouseup", + result: true, + }, + { + eventName: "mousedown", + result: true, + }, + { + eventName: "keydown", + result: true, + }, + { + eventName: "keyup", + result: true, + }, + ]; + + for (let i=0; i<data.length; ++i) { + document.addEventListener(data[i].eventName, function() { + document.removeEventListener(data[i].eventName, arguments.callee); + + is(domWindowUtils.isHandlingUserInput, data[i].result, + "isHandlingUserInput should be " + data[i].result); + + SimpleTest.executeSoon(function() { + try { testEventsRunner.next(); } catch (e) { } + }); + }); + + SimpleTest.executeSoon(function() { + if (data[i].eventName == "click") { + synthesizeMouseAtCenter(document.body, {}); + } else if (data[i].eventName.indexOf("key") == 0) { + synthesizeKey("VK_A", { type: data[i].eventName }); + } else { + synthesizeMouseAtCenter(document.body, { type: data[i].eventName }); + } + }); + + yield undefined; + } + + SimpleTest.executeSoon(function() { + next(); + }); + } + + var testEventsRunner = testEvents(); + testEventsRunner.next(); +}, +]; + +function runner() { + for (let i=0; i<gTests.length; ++i) { + gTests[i](); + yield undefined; + } + + SimpleTest.finish(); +}; + +var gTestRunner = runner(); + +function next() { + try { gTestRunner.next(); } catch (e) { if (e != StopIteration) { throw e; } } +} + +// Run the test from onload, since the onscreen and offscreen divs should be in +// the right places by then. +addLoadEvent(function() { + gTestRunner.next(); +}); + +</script> + +<p id="display"></p> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html b/dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html new file mode 100644 index 000000000..c6ee89ee3 --- /dev/null +++ b/dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html @@ -0,0 +1,90 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>nsIDOMWindowUtils::elementFromPoint test</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + <style> + body { + /* Make room for scrolling */ + } + </style> +</head> + +<body id="body"> + <script type="application/javascript;version=1.8"> + /* + void getScrollXY(in boolean aFlushLayout, out long aScrollX, out long aScrollY); + */ + function doTests() { + testScrollXY(); + testHiddenIframe(); + + SimpleTest.finish(); + } + + function testScrollXY() { + let iframe = document.getElementById("iframe"); + let cwindow = iframe.contentWindow; + let domWindowUtils = SpecialPowers.getDOMWindowUtils(cwindow); + + function checkGetScrollXYState(flush, vals, testName) { + let scrollX = {}, scrollY = {}; + domWindowUtils.getScrollXY(flush, scrollX, scrollY); + is(scrollX.value, vals[0], "getScrollXY x for test: " + testName); + is(scrollY.value, vals[1], "getScrollXY y for test: " + testName); + } + + function checkWindowScrollState(vals, testName) { + is(cwindow.scrollX, vals[0], "scrollX for test: " + testName); + is(cwindow.scrollY, vals[1], "scrollY for test: " + testName); + } + + // Check initial state (0, 0) + checkGetScrollXYState(false, [0, 0], "initial getScrollXY state"); + checkGetScrollXYState(true, [0, 0], "initial getScrollXY state+flush"); + checkWindowScrollState([0, 0], "initial window.scroll* state"); + + // scroll + cwindow.scrollTo(900, 1000); + checkGetScrollXYState(false, [900, 1000], "after scroll getScrollXY state"); + checkGetScrollXYState(true, [900, 1000], "after scroll getScrollXY state+flush"); + checkWindowScrollState([900, 1000], "after scroll window.scroll* state"); + + // ensure flush=false works + cwindow.document.body.style.width = 'auto'; + cwindow.document.body.style.height = 'auto'; + checkGetScrollXYState(false, [900, 1000], "didn't flush layout for getScrollXY"); + checkGetScrollXYState(true, [0, 0], "flushed layout for getScrollXY"); + } + + function testHiddenIframe() { + let iframe = document.getElementById("hidden-iframe"); + let cwindow = iframe.contentWindow; + let domWindowUtils = SpecialPowers.getDOMWindowUtils(cwindow); + + // make sure getScrollXY doesn't throw + let scrollX = {}, scrollY = {}; + domWindowUtils.getScrollXY(false, scrollX, scrollY); + + is(scrollX.value, 0, "scrollX is zero for display:none iframe"); + is(scrollY.value, 0, "scrollY is zero for display:none iframe"); + } + + SimpleTest.waitForExplicitFinish(); + </script> + + <!-- can't run this in the test document, since it potentially runs in a + scrolling="no" test harness iframe, and that causes a failure for some + reason --> + <iframe src="data:text/html,<body style='width: 100000px; height: 100000px;'><p>top</p></body>" + id="iframe" + onload="doTests();"> + </iframe> + + <iframe id="hidden-iframe" style="display: none;"></iframe> + + <p id="display"></p> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_domWindowUtils_scrollbarSize.html b/dom/tests/mochitest/general/test_domWindowUtils_scrollbarSize.html new file mode 100644 index 000000000..b440870fd --- /dev/null +++ b/dom/tests/mochitest/general/test_domWindowUtils_scrollbarSize.html @@ -0,0 +1,65 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>nsIDOMWindowUtils::getScrollbarSize test</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> + +<body id="body"> + <script type="application/javascript;version=1.8"> + function doTests() { + let iframe = document.getElementById("iframe"); + let cwindow = iframe.contentWindow; + let utils = SpecialPowers.getDOMWindowUtils(cwindow); + let doc = cwindow.document; + + function haveNonFloatingScrollbars() { + return doc.getElementById("float").offsetWidth > 200; + } + + checkScrollbarSizeFlush(utils, (w, h) => w == 0 && h == 0, + "[overflow=hidden] corrrect scrollbar size after flushing"); + + // Some platforms (esp. mobile) may have floating scrollbars that don't + // affect layout. Thus getScrollbarSize() would always return zeros. + if (haveNonFloatingScrollbars()) { + let body = doc.querySelector("body"); + body.style.overflowY = "scroll"; + + checkScrollbarSize(utils, (w, h) => w == 0 && h == 0, + "[overflowY=scroll] correct scrollbar size w/o flushing"); + + checkScrollbarSizeFlush(utils, (w, h) => w > 0 && h == 0, + "[overflowY=scroll] correct scrollbar size after flushing"); + + body.style.overflowX = "scroll"; + checkScrollbarSize(utils, (w, h) => w > 0 && h == 0, + "[overflowXY=scroll] correct scrollbar size w/o flushing"); + + checkScrollbarSizeFlush(utils, (w, h) => w > 0 && h > 0, + "[overflowXY=scroll] correct scrollbar size after flushing"); + } + + SimpleTest.finish(); + } + + function checkScrollbarSize(utils, check, msg, flush = false) { + let width = {}, height = {}; + utils.getScrollbarSize(flush, width, height); + ok(check(width.value, height.value), msg); + } + + function checkScrollbarSizeFlush(utils, check, msg) { + checkScrollbarSize(utils, check, msg, true); + } + + SimpleTest.waitForExplicitFinish(); + </script> + + <iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/general/file_domWindowUtils_scrollbarSize.html" + id="iframe" onload="doTests();"> + </iframe> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_donottrack.html b/dom/tests/mochitest/general/test_donottrack.html new file mode 100644 index 000000000..837f984f8 --- /dev/null +++ b/dom/tests/mochitest/general/test_donottrack.html @@ -0,0 +1,72 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=629535 +--> +<head> + <title>Test for Bug 629535</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629535">Mozilla Bug 629535</a> + +<script type="application/javascript"> + +const dntPref = 'privacy.donottrackheader.enabled'; + +SimpleTest.waitForExplicitFinish(); + +var currentTestIdx = -1; +var tests = []; +function nextTest() { + currentTestIdx++; + if (currentTestIdx >= tests.length) { + SimpleTest.finish(); + return; + } + + tests[currentTestIdx](); +} + +tests.push(function testDefaultValues() { + // The default pref values depend on the OS it seems. + var isAndroid = !!navigator.userAgent.includes("Android"); + var isB2G = !isAndroid && /Mobile|Tablet/.test(navigator.userAgent); + + is(SpecialPowers.getBoolPref(dntPref), false, + 'DNT should be disabled by default'); + is(navigator.doNotTrack, 'unspecified', + 'navigator.doNotTrack should initially be "unspecified".'); + + nextTest(); +}); + +tests.push(function clearedEnabled() { + SpecialPowers.pushPrefEnv({"clear": [[dntPref]]}, function() { + is(navigator.doNotTrack, "unspecified", 'after clearing pref'); + nextTest(); + }); +}); + +tests.push(function setEnabled() { + SpecialPowers.pushPrefEnv({"set": [[dntPref, true]]}, function() { + is(navigator.doNotTrack, "1", 'after setting pref to true'); + nextTest(); + }); +}); + +tests.push(function setDisabled() { + SpecialPowers.pushPrefEnv({"set": [[dntPref, false]]}, function() { + is(navigator.doNotTrack, "unspecified", 'after setting pref to false'); + nextTest(); + }); +}); + +nextTest(); + +</script> + +</body> +</html> + diff --git a/dom/tests/mochitest/general/test_focus_legend_noparent.html b/dom/tests/mochitest/general/test_focus_legend_noparent.html new file mode 100644 index 000000000..015fead38 --- /dev/null +++ b/dom/tests/mochitest/general/test_focus_legend_noparent.html @@ -0,0 +1,36 @@ +<html> +<head> +<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +<script type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); +SimpleTest.requestFlakyTimeout("untriaged"); + +function runTest() +{ + window.focus(); + var g = document.createElementNS("http://www.w3.org/1999/xhtml", "legend"); + document.body.appendChild(g); + setTimeout(g.focus.bind(g), 0); + setTimeout(done, 10); +} + +function done() +{ + ok(document.hasFocus(), "document is still focused"); + is(document.activeElement, document.body, "document has no focused element") + SimpleTest.finish(); +} + +SimpleTest.waitForFocus(runTest); +</script> +</head> + +<p id="display"></p> +<div id="content" style="display: none"> +</div> + +<body onblur="dump('blurred window!\n')"></body> +</html> diff --git a/dom/tests/mochitest/general/test_focusrings.xul b/dom/tests/mochitest/general/test_focusrings.xul new file mode 100644 index 000000000..dd91c23e9 --- /dev/null +++ b/dom/tests/mochitest/general/test_focusrings.xul @@ -0,0 +1,175 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<script type="application/javascript" + src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" + src="/tests/SimpleTest/EventUtils.js"></script> +<script type="application/javascript" + src="/tests/SimpleTest/WindowSnapshot.js"></script> + +<html:style xmlns:html="http://www.w3.org/1999/xhtml" type="text/css"> +* { outline: none; } +#l1:-moz-focusring, #l3:-moz-focusring, #b1:-moz-focusring { outline: 2px solid red; } +#l2:focus, #b2:focus { outline: 2px solid red; } +</html:style> + +<script> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +function snapShot(element) { + var rect = element.getBoundingClientRect(); + adjustedRect = { left: rect.left - 6, top: rect.top - 6, + width: rect.width + 12, height: rect.height + 12 } + return SpecialPowers.snapshotRect(window, adjustedRect, "transparent"); +} + +function initTest() +{ + SpecialPowers.pushPrefEnv({"set": [['accessibility.tabfocus', 7]]}, runTest); +} + +function runTest() +{ + var isMac = (navigator.platform.indexOf("Mac") >= 0); + var isWin = (navigator.platform.indexOf("Win") >= 0); + + function checkFocus(element, visible, testid) + { + var outline = getComputedStyle(element, "").outlineWidth; + is(outline, visible ? "2px" : "0px", testid); + } + + // make sure that a focus ring appears on the focused button + if (navigator.platform.indexOf("Mac") >= 0) { + var focusedButton = $("b3"); + ok(compareSnapshots(snapShot(focusedButton), snapShot($("b2")), true)[0], "unfocused shows no ring"); + focusedButton.focus(); + ok(compareSnapshots(snapShot(focusedButton), snapShot($("b2")), false)[0], "focus shows ring"); + } + + checkFocus($("l1"), false, "initial appearance"); + + // we can't really test the situation on Windows where a dialog doesn't show + // focus rings until a key is pressed, as the default state depends on what + // kind of real user input, mouse or key, was last entered. But we can handle + // the test regardless of which user input last occurred. + $("l1").focus(); + var expectedVisible = (!isWin || getComputedStyle($("l1"), "").outlineWidth == "2px"); + testHTMLElements(htmlElements, isMac, isWin && !expectedVisible); + + $("l1").focus(); + checkFocus($("l1"), expectedVisible, "appearance on list after focus() with :moz-focusring"); + $("l2").focus(); + + checkFocus($("l2"), true, "appearance on list after focus() with :focus"); + + is(getComputedStyle($("l1"), "").outlineWidth, "0px", "appearance on previous list after focus() with :focus"); + + synthesizeMouse($("l1"), 4, 4, { }); + checkFocus($("l1"), expectedVisible, "appearance on list after mouse focus with :moz-focusring"); + synthesizeMouse($("l2"), 4, 4, { }); + checkFocus($("l2"), true, "appearance on list after mouse focus with :focus"); + + synthesizeMouse($("b1"), 4, 4, { }); + checkFocus($("b1"), !isMac && expectedVisible, "appearance on button after mouse focus with :moz-focusring"); + if (navigator.platform.indexOf("Mac") >= 0) { + ok(compareSnapshots(snapShot($("b1")), snapShot($("b2")), false)[0], "focus after mouse shows no ring"); + } + + synthesizeMouse($("b2"), 4, 4, { }); + checkFocus($("b2"), !isMac, "appearance on button after mouse focus with :focus"); + + // after a key is pressed, the focus ring will always be visible + $("l2").focus(); + synthesizeKey("VK_TAB", { }); + checkFocus($("l3"), true, "appearance on list after tab focus"); + + if (isMac) { + SpecialPowers.pushPrefEnv({"set": [['accessibility.mouse_focuses_formcontrol', true]]}, testMacFocusesFormControl); + } + else { + SimpleTest.finish(); + } +} + +function testMacFocusesFormControl() +{ + testHTMLElements(htmlElementsMacPrefSet, true, false); + SimpleTest.finish(); +} + +var htmlElements = [ + "<button id='elem'>Button</button>", + "<input id='elem' class='canfocus'>", + "<input id='elem' type='password' class='canfocus'>", + "<input id='elem' type='button'>", + "<input id='elem' type='checkbox'>", + "<textarea id='elem' class='canfocus'></textarea>", + "<select id='elem' class='canfocus'><option>One</select>", + "<select id='elem' rows='5' class='canfocus'><option>One</select>", + "<div id='elem' tabindex='0' class='canfocus' style='width: 10px; height: 10px;'></div>", + "<a href='about:blank' class='canfocus' onclick='return false;'>about:blank</a>", +]; + +var htmlElementsMacPrefSet = [ + "<button id='elem' class='canfocus'>Button</button>", + "<input id='elem' class='canfocus'>", + "<input id='elem' type='button' class='canfocus'>", + "<input id='elem' type='checkbox' class='canfocus'>", +]; + +function testHTMLElements(list, isMac, expectedNoRingsOnWin) +{ + var childwin = frames[0]; + var childdoc = childwin.document; + var container = childdoc.getElementById("container"); + for (var e = 0; e < list.length; e++) { + container.innerHTML = list[e]; + + var elem = container.firstChild; + + var shouldFocus = !isMac || (elem.className == "canfocus"); + var ringSize = (shouldFocus ? (expectedNoRingsOnWin ? 2 : 1) : 0) + "px"; + if (elem.localName == "a") + ringSize = "0px"; + + synthesizeMouse(elem, 8, 8, { }, childwin); + is(childdoc.activeElement, shouldFocus ? elem : childdoc.body, "mouse click on " + list[e]); + is(childwin.getComputedStyle(elem, "").outlineWidth, ringSize, "mouse click on " + list[e] + " ring"); + + if (childdoc.activeElement) + childdoc.activeElement.blur(); + + ringSize = (elem.localName == "a" ? "0" : (expectedNoRingsOnWin ? 2 : 1)) + "px"; + + elem.focus(); + is(childdoc.activeElement, elem, "focus() on " + list[e]); + is(childwin.getComputedStyle(elem, "").outlineWidth, ringSize, + "focus() on " + list[e] + " ring"); + + childdoc.activeElement.blur(); + } +} + +SimpleTest.waitForFocus(runTest); + +]]> +</script> + +<listbox id="l1" class="plain" height="20"/> +<listbox id="l2" class="plain" height="20"/> +<listbox id="l3" class="plain" height="20"/> +<button id="b1" label="Button"/> +<button id="b2" label="Button"/> +<button id="b3" label="Button"/> + +<iframe id="child" src="data:text/html,<html><style>* { outline: none; -moz-appearance: none; } %23elem:focus { outline: 2px solid red; } %23elem:-moz-focusring { outline: 1px solid blue; }</style><div id='container'></html>"/> + +<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + +</window> diff --git a/dom/tests/mochitest/general/test_for_of.html b/dom/tests/mochitest/general/test_for_of.html new file mode 100644 index 000000000..0f8ddc603 --- /dev/null +++ b/dom/tests/mochitest/general/test_for_of.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Tests for for-of loops</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> +<body onload="doTest()"> +<p id="display"></p> +<div id="content" style="display: none"></div> +<script> +function doTest() { + // DOM NodeLists are iterable. + var a = []; + for (var e of document.body.childNodes) + if (e.nodeType === 1) + a.push(e.tagName); + is("P DIV SCRIPT", a.join(" "), "for-of should see each element in the body"); + + SimpleTest.finish(); +} +SimpleTest.waitForExplicitFinish(); +</script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_frameElementWrapping.html b/dom/tests/mochitest/general/test_frameElementWrapping.html new file mode 100644 index 000000000..33bc7fdde --- /dev/null +++ b/dom/tests/mochitest/general/test_frameElementWrapping.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for same-origin and cross-origin wrapping of frameElement</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<iframe id="ifr" src="file_frameElementWrapping.html"></iframe> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +// +// This test has sort of morphed over time to become less and less useful. +// In the past, we had special security policy for frameElement, but that's +// more or less gone away with compartment/proxy wrapping. So we just go +// through the motions to make sure that, indeed, frameElement is subject +// to the same-origin policy. +// + +SimpleTest.waitForExplicitFinish(); + +var count = 0; + +function runTest(result, message) { + ok(result === 'PASS', message); + + if (++count === 2) + SimpleTest.finish(); + else + $('ifr').contentWindow.location = 'http://example.org/tests/dom/tests/mochitest/general/file_frameElementWrapping.html'; +} + +window.addEventListener("message", + function(event) { runTest.apply(null, event.data.split(',')) }, + false); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_framedhistoryframes.html b/dom/tests/mochitest/general/test_framedhistoryframes.html new file mode 100644 index 000000000..b69817808 --- /dev/null +++ b/dom/tests/mochitest/general/test_framedhistoryframes.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602256 +--> +<head> + <title>Test for Bug 602256</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a> +<p id="display"></p> +<div id="content"> + <iframe id="iframe" src="historyframes.html"></iframe> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 602256 **/ + +SimpleTest.waitForExplicitFinish(); + +function done() { + SimpleTest.finish(); +} + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_idleapi_permissions.html b/dom/tests/mochitest/general/test_idleapi_permissions.html new file mode 100644 index 000000000..4f690f462 --- /dev/null +++ b/dom/tests/mochitest/general/test_idleapi_permissions.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for idle api permissions</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +<script type="application/javascript"> + +var idleObserver = { + onidle: null, + onactive: null +}; + +function doAddIdleObserver(obs) { + var i = document.createElement("iframe"); + document.body.appendChild(i); + var added = false; + try { + i.contentWindow.navigator.addIdleObserver(obs); + added = true; + } catch (e) { } + i.remove(); + return added; +} + +function run_test() { + // addIdleObserver checks whether time is > 0. + this.idleObserver.time = 100; + + SpecialPowers.pushPermissions([{type: "idle", allow: true, context: document}], () => { + added = doAddIdleObserver(this.idleObserver, true); + ok(added, "Should be able to add idle observer with permission."); + SimpleTest.finish(); + }); +} + +SimpleTest.waitForExplicitFinish(); +addLoadEvent(run_test); +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_img_mutations.html b/dom/tests/mochitest/general/test_img_mutations.html new file mode 100644 index 000000000..a6fca3f81 --- /dev/null +++ b/dom/tests/mochitest/general/test_img_mutations.html @@ -0,0 +1,236 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Image srcset mutations</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script type="application/javascript"> + "use strict"; + + // Tests the relevant mutations part of the spec for img src and srcset + // and that img.src still behaves by the older spec. (Bug 1076583) + // https://html.spec.whatwg.org/#relevant-mutations + SimpleTest.waitForExplicitFinish(); + + // 50x50 png + var testPNG50 = new URL("image_50.png", location).href; + // 100x100 png + var testPNG100 = new URL("image_100.png", location).href; + // 200x200 png + var testPNG200 = new URL("image_200.png", location).href; + + var tests = []; + var img; + var expectingErrors = 0; + var expectingLoads = 0; + var afterExpectCallback; + + function onImgLoad() { + ok(expectingLoads > 0, "expected load"); + if (expectingLoads > 0) { + expectingLoads--; + } + if (!expectingLoads && !expectingErrors && afterExpectCallback) { + setTimeout(afterExpectCallback, 0); + afterExpectCallback = null; + } + } + function onImgError() { + ok(expectingErrors > 0, "expected error"); + if (expectingErrors > 0) { + expectingErrors--; + } + if (!expectingLoads && !expectingErrors && afterExpectCallback) { + setTimeout(afterExpectCallback, 0); + afterExpectCallback = null; + } + } + function expectEvents(loads, errors, callback) { + if (!loads && !errors) { + setTimeout(callback, 0); + } else { + expectingLoads += loads; + expectingErrors += errors; + info("Waiting for " + expectingLoads + " load and " + expectingErrors + " error events"); + afterExpectCallback = callback; + } + } + + // + // Test that img.src still does some work synchronously per the older spec (bug 1076583) + // + tests.push(function test1() { + info("test 1"); + img.src = testPNG50; + is(img.currentSrc, testPNG50, "Should have synchronously selected source"); + + // Assigning a wrong URL should not trigger error event (bug 1321300). + img.src = '//:0'; // Wrong URL + + img.src = "non_existent_image.404"; + ok(img.currentSrc.endsWith("non_existent_image.404"), "Should have synchronously selected source"); + + img.removeAttribute("src"); + is(img.currentSrc, '', "Should have dropped currentSrc"); + + // Load another image while previous load is still pending + img.src = testPNG200; + is(img.currentSrc, testPNG200, "Should have synchronously selected source"); + + // No events should have fired synchronously, now we should get just one load (and no 404 error) + expectEvents(1, 0, nextTest); + }); + + + // Setting srcset should be async + tests.push(function () { + info("test 2"); + img.srcset = testPNG100; + is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); + nextTest(); + }); + }); + + // Setting srcset, even to no ultimate effect, should trigger a reload + tests.push(function () { + info("test 3"); + img.srcset = testPNG100 + " 1x, " + testPNG200 + " 2x"; + is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); + nextTest(); + }); + }); + + // Should switch to src as 1x source + tests.push(function () { + info("test 4"); + img.srcset = testPNG50 + " 2x"; + is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request"); + nextTest(); + }); + }); + + // Changing src while we have responsive attributes should not be sync + tests.push(function () { + info("test 5"); + img.src = testPNG100; + is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); + + // Switch to using srcset again for next test + img.srcset = testPNG100; + expectEvents(1, 0, nextTest); + }); + }); + + // img.src = img.src should trigger an async event even in responsive mode + tests.push(function () { + info("test 6"); + is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); + img.src = img.src; + is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); + + expectEvents(1, 0, nextTest); + }); + + // img.srcset = img.srcset should be a no-op + tests.push(function () { + info("test 7"); + img.srcset = img.srcset; + is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); + + expectEvents(0, 0, nextTest); + }); + + // re-binding the image to the document should be a no-op + tests.push(function () { + info("test 8"); + document.body.appendChild(img); + is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); + + expectEvents(0, 0, nextTest); + }); + + // We should re-run our selection algorithm when any load event occurs + tests.push(function () { + info("test 9"); + img.srcset = testPNG50 + " 1x, " + testPNG200 + " 2x"; + is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request"); + + // The preference change will trigger a load, as the image will change + SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] }); + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request"); + img.src = img.src; + is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); + // img.src = img.src is special-cased by the spec. It should always + // trigger an load event + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); + expectEvents(0, 0, nextTest); + }); + }) + }); + }); + + // Removing srcset attr should async switch back to src + tests.push(function () { + info("test 10"); + is(img.currentSrc, testPNG200, "Should have testPNG200 as current request"); + + img.removeAttribute("srcset"); + is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); + + expectEvents(0, 0, nextTest); + }); + }); + + function nextTest() { + if (tests.length) { + // Spin event loop to make sure no unexpected image events are + // pending (unexpected events will assert in the handlers) + setTimeout(function() { + (tests.shift())(); + }, 0); + } else { + // Remove the event listeners to prevent the prefenv being popped from + // causing test failures. + img.removeEventListener("load", onImgLoad); + img.removeEventListener("error", onImgError); + SimpleTest.finish(); + } + } + + addEventListener("load", function() { + SpecialPowers.pushPrefEnv({'set': [["layout.css.devPixelsPerPx", "1.0"]] }, + function() { + // Create this after the pref is set, as it is guarding webIDL attributes + img = document.createElement("img"); + img.addEventListener("load", onImgLoad); + img.addEventListener("error", onImgError); + document.body.appendChild(img); + setTimeout(nextTest, 0); + }); + }); + </script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_innerScreen.xul b/dom/tests/mochitest/general/test_innerScreen.xul new file mode 100644 index 000000000..55666c28a --- /dev/null +++ b/dom/tests/mochitest/general/test_innerScreen.xul @@ -0,0 +1,89 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- + Tests for mozInnerScreenX/Y properties + --> +<window title="Test mozInnerScreenX/Y Properties" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test resuls are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml" + style="height: 400px; position:relative; overflow: auto;"> + <iframe id="f" + style="position:absolute; left:100px; + top:200px; width:200px; height:200px; border:none;"></iframe> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + +function isRounded(a, b, msg) { + ok(Math.round(a) == Math.round(b), + msg + " (rounded), got " + a + ", expected " + b); +} + +function doTests() +{ + var readable = false; + try + { + mozScreenPixelsPerCSSPixel; + readable = true; + } + catch(ex) { } + ok(!readable, "window pixels per css pixel shouldn't be readable to content"); + + var domWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils); + var devPxPerCSSPx = domWindowUtils.screenPixelsPerCSSPixel; + + is(window.devicePixelRatio, devPxPerCSSPx, "window.devicePixelRatio"); + + var windowBO = document.documentElement.boxObject; + isRounded(window.mozInnerScreenX*devPxPerCSSPx, windowBO.screenX, + "window screen X"); + isRounded(window.mozInnerScreenY*devPxPerCSSPx, windowBO.screenY, + "window screen Y"); + + var f = document.getElementById("f"); + var fBounds = f.getBoundingClientRect(); + + const CI = Components.interfaces; + var fshell = f.contentWindow.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIWebNavigation).QueryInterface(CI.nsIDocShell); + var fmudv = fshell.contentViewer; + + isRounded(f.contentWindow.mozInnerScreenX, + window.mozInnerScreenX + fBounds.left, + "frame screen X"); + isRounded(f.contentWindow.mozInnerScreenY, + window.mozInnerScreenY + fBounds.top, + "frame screen Y"); + + fmudv.fullZoom *= 2; + var frameDomWindowUtils = f.contentWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils); + is(frameDomWindowUtils.screenPixelsPerCSSPixel, 2*devPxPerCSSPx, + "frame screen pixels per CSS pixel"); + + is(f.contentWindow.devicePixelRatio, 2*devPxPerCSSPx, "frame devicePixelRatio"); + + isRounded(f.contentWindow.mozInnerScreenX*2, + window.mozInnerScreenX + fBounds.left, + "zoomed frame screen X"); + isRounded(f.contentWindow.mozInnerScreenY*2, + window.mozInnerScreenY + fBounds.top, + "zoomed frame screen Y"); + fmudv.fullZoom = 1.0; + + SimpleTest.finish(); +} + +addLoadEvent(doTests); +SimpleTest.waitForExplicitFinish(); + +]]> +</script> + +</window> diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html new file mode 100644 index 000000000..acbc12e07 --- /dev/null +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -0,0 +1,1383 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=766694 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 766694</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=766694">Mozilla Bug 766694</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 766694 **/ + +// This is a list of all interfaces that are exposed to every webpage. +// Please only add things to this list with great care and proper review +// from the associated module peers. + +// This file lists global interfaces we want exposed and verifies they +// are what we intend. Each entry in the arrays below can either be a +// simple string with the interface name, or an object with a 'name' +// property giving the interface name as a string, and additional +// properties which qualify the exposure of that interface. For example: +// +// [ +// "AGlobalInterface", +// {name: "ExperimentalThing", release: false}, +// {name: "ReallyExperimentalThing", nightly: true}, +// {name: "DesktopOnlyThing", desktop: true}, +// {name: "FancyControl", xbl: true}, +// {name: "DisabledEverywhere", disabled: true}, +// ]; +// +// See createInterfaceMap() below for a complete list of properties. + +// IMPORTANT: Do not change this list without review from +// a JavaScript Engine peer! +var ecmaGlobals = + [ + "Array", + "ArrayBuffer", + "Boolean", + "DataView", + "Date", + "Error", + "EvalError", + "Float32Array", + "Float64Array", + "Function", + // NB: We haven't bothered to resolve constants like Infinity and NaN on + // Xrayed windows (which are seen from the XBL scope). We could support + // this if needed with some refactoring. + {name: "Infinity", xbl: false}, + "Int16Array", + "Int32Array", + "Int8Array", + "InternalError", + {name: "Intl", android: false}, + "Iterator", + "JSON", + "Map", + "Math", + {name: "NaN", xbl: false}, + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + {name: "SharedArrayBuffer", release: false}, + {name: "SIMD", nightly: true}, + {name: "Atomics", release: false}, + "StopIteration", + "String", + "Symbol", + "SyntaxError", + {name: "TypedObject", nightly: true}, + "TypeError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "URIError", + "WeakMap", + "WeakSet", + ]; +// IMPORTANT: Do not change the list above without review from +// a JavaScript Engine peer! + +// IMPORTANT: Do not change the list below without review from a DOM peer, +// except to remove items from it! +// +// This is a list of interfaces that were prefixed with 'moz' instead of 'Moz'. +// We should never to that again, interfaces in the DOM start with an uppercase +// letter. If you think you need to add an interface here, DON'T. Rename your +// interface. +var legacyMozPrefixedInterfaces = + [ + "mozContact", + "mozRTCIceCandidate", + "mozRTCPeerConnection", + "mozRTCSessionDescription", + ]; +// IMPORTANT: Do not change the list above without review from a DOM peer, +// except to remove items from it! + +// IMPORTANT: Do not change the list below without review from a DOM peer! +var interfaceNamesInGlobalScope = + [ +// IMPORTANT: Do not change this list without review from a DOM peer! + "AnalyserNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "Animation"}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "AnimationEffectReadOnly", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "AnimationEffectTiming", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "AnimationEffectTimingReadOnly", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "AnimationEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "AnimationPlaybackEvent", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "AnimationTimeline", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "Attr", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Audio", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioBuffer", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioContext", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioBufferSourceNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioDestinationNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioListener", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioParam", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioProcessingEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "AudioStreamTrack", +// IMPORTANT: Do not change this list without review from a DOM peer! + "BarProp", +// IMPORTANT: Do not change this list without review from a DOM peer! + "BatteryManager", +// IMPORTANT: Do not change this list without review from a DOM peer! + "BeforeUnloadEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "BiquadFilterNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Blob", +// IMPORTANT: Do not change this list without review from a DOM peer! + "BlobEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "BoxObject", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "BroadcastChannel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Cache", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CacheStorage", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CanvasCaptureMediaStream", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CanvasGradient", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CanvasPattern", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CanvasRenderingContext2D", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CaretPosition", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CDATASection", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ChannelMergerNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ChannelSplitterNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CharacterData", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "ChromeNodeList", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "ChromeWindow", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "ClipboardEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CloseEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CommandEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Comment", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CompositionEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ConstantSourceNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Controllers", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ConvolverNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Crypto", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CryptoKey", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSS", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSS2Properties", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "CSSAnimation", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSConditionRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSCounterStyleRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSFontFaceRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSFontFeatureValuesRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSGroupingRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSImportRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSKeyframeRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSKeyframesRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSMediaRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSMozDocumentRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSNameSpaceRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSPageRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSPrimitiveValue", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "CSSPseudoElement", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSRuleList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSStyleDeclaration", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSStyleRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSStyleSheet", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSSupportsRule", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "CSSTransition", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSValue", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CSSValueList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CustomElementRegistry", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CustomEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DataChannel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DataTransfer", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DataTransferItem", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DataTransferItemList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DelayNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DesktopNotification", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DesktopNotificationCenter", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DeviceLightEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DeviceMotionEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DeviceOrientationEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DeviceProximityEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DeviceStorageAreaChangedEvent", desktop: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DeviceStorageAreaListener", desktop: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DeviceStorage", desktop: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DeviceStorageChangeEvent", desktop: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "Directory", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Document", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DocumentFragment", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "DocumentTimeline", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "DocumentType", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "DOMConstructor", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMCursor", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMError", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMException", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMImplementation", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMMatrix", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMMatrixReadOnly", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMParser", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMPoint", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMPointReadOnly", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMQuad", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMRect", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMRectList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMRectReadOnly", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMStringList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMStringMap", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DOMTokenList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DragEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "DynamicsCompressorNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Element", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ErrorEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Event", +// IMPORTANT: Do not change this list without review from a DOM peer! + "EventSource", +// IMPORTANT: Do not change this list without review from a DOM peer! + "EventTarget", +// IMPORTANT: Do not change this list without review from a DOM peer! + "External", +// IMPORTANT: Do not change this list without review from a DOM peer! + "File", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileReader", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileSystem", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileSystemDirectoryEntry", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileSystemDirectoryReader", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileSystemEntry", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FileSystemFileEntry", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FocusEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FormData", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FontFace", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FontFaceSet", +// IMPORTANT: Do not change this list without review from a DOM peer! + "FontFaceSetLoadEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "GainNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Gamepad", +// IMPORTANT: Do not change this list without review from a DOM peer! + "GamepadAxisMoveEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "GamepadButtonEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "GamepadButton", +// IMPORTANT: Do not change this list without review from a DOM peer! + "GamepadEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "GamepadPose", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "HashChangeEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Headers", +// IMPORTANT: Do not change this list without review from a DOM peer! + "History", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLAllCollection", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLAnchorElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLAppletElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLAreaElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLAudioElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLBaseElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLBodyElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLBRElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLButtonElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLCanvasElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLCollection", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLContentElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLDataElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLDataListElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLDetailsElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLDirectoryElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLDivElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLDListElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLDocument", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLEmbedElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLFieldSetElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLFontElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLFormControlsCollection", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLFormElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLFrameElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLFrameSetElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLHeadElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLHeadingElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLHRElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLHtmlElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLIFrameElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLImageElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLInputElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLLabelElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLLegendElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLLIElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLLinkElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLMapElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLMediaElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLMenuElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLMenuItemElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLMetaElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLMeterElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLModElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLObjectElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLOListElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLOptGroupElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLOptionElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLOptionsCollection", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLOutputElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLParagraphElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLParamElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLPreElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLPictureElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLProgressElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLQuoteElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLScriptElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLSelectElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLShadowElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLSourceElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLSpanElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLStyleElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTableCaptionElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTableCellElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTableColElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTableElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTableRowElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTableSectionElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTemplateElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTextAreaElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTimeElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTitleElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLTrackElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLUListElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLUnknownElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "HTMLVideoElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "IdleDeadline", nightly: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBCursor", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBCursorWithValue", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBDatabase", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBFactory", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBFileHandle", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBFileRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBIndex", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBKeyRange", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBMutableFile", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBObjectStore", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBOpenDBRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBTransaction", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IDBVersionChangeEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "IIRFilterNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Image", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ImageBitmap", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ImageBitmapRenderingContext", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "ImageCapture", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "ImageCaptureErrorEvent", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "ImageData", +// IMPORTANT: Do not change this list without review from a DOM peer! + "InputEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "InstallTrigger", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "IntersectionObserver", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "IntersectionObserverEntry", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "KeyEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "KeyboardEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "KeyframeEffectReadOnly", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "KeyframeEffect", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "LocalMediaStream", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Location", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaDeviceInfo", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaDevices", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaElementAudioSourceNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaError", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "MediaKeyError", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "MediaEncryptedEvent", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "MediaKeys", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "MediaKeySession", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "MediaKeySystemAccess", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "MediaKeyMessageEvent", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "MediaKeyStatusMap", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaQueryList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaRecorder", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaSource", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaStream", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaStreamAudioDestinationNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaStreamAudioSourceNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaStreamEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaStreamTrackEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MediaStreamTrack", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "MenuBoxObject", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "MessageChannel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MessageEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MessagePort", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MimeType", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MimeTypeArray", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MouseEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MouseScrollEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "mozRTCIceCandidate", +// IMPORTANT: Do not change this list without review from a DOM peer! + "mozRTCPeerConnection", +// IMPORTANT: Do not change this list without review from a DOM peer! + "mozRTCSessionDescription", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MutationEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MutationObserver", +// IMPORTANT: Do not change this list without review from a DOM peer! + "MutationRecord", +// IMPORTANT: Do not change this list without review from a DOM peer! + "NamedNodeMap", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Navigator", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "NetworkInformation", desktop: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "Node", +// IMPORTANT: Do not change this list without review from a DOM peer! + "NodeFilter", +// IMPORTANT: Do not change this list without review from a DOM peer! + "NodeIterator", +// IMPORTANT: Do not change this list without review from a DOM peer! + "NodeList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Notification", +// IMPORTANT: Do not change this list without review from a DOM peer! + "NotifyPaintEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "OffscreenCanvas", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "OfflineAudioCompletionEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "OfflineAudioContext", +// IMPORTANT: Do not change this list without review from a DOM peer! + "OfflineResourceList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Option", +// IMPORTANT: Do not change this list without review from a DOM peer! + "OscillatorNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PageTransitionEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PaintRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PaintRequestList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PannerNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Path2D", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Performance", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceEntry", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceMark", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceMeasure", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceNavigation", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PerformanceObserver", nightly: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PerformanceObserverEntryList", nightly: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceResourceTiming", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PerformanceTiming", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PeriodicWave", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Permissions", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PermissionStatus", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Plugin", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PluginArray", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PointerEvent", nightly: true, desktop: true, disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "PopStateEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "PopupBlockedEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PopupBoxObject", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PresentationDeviceInfoManager", + disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "Presentation", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PresentationAvailability", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PresentationConnection", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PresentationConnectionAvailableEvent", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PresentationConnectionClosedEvent", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PresentationConnectionList", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PresentationReceiver", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "PresentationRequest", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "ProcessingInstruction", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ProgressEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RadioNodeList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Range", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RecordErrorEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Rect", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Request", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Response", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RGBColor", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCCertificate", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCDataChannelEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCDTMFSender", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCDTMFToneChangeEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCIceCandidate", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCPeerConnection", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCPeerConnectionIceEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCRtpReceiver", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCRtpSender", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCSessionDescription", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCStatsReport", +// IMPORTANT: Do not change this list without review from a DOM peer! + "RTCTrackEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Screen", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ScreenOrientation", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ScriptProcessorNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ScrollAreaEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Selection", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SettingsLock", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ShadowRoot", // Bogus, but the test harness forces it on. See bug 1159768. +// IMPORTANT: Do not change this list without review from a DOM peer! + "SharedWorker", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SimpleGestureEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "SimpleTest", xbl: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "SourceBuffer", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SourceBufferList", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "SpeechSynthesisErrorEvent", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "SpeechSynthesisEvent", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "SpeechSynthesis", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "SpeechSynthesisUtterance", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "SpeechSynthesisVoice", android: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "SpecialPowers", xbl: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "StereoPannerNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Storage", +// IMPORTANT: Do not change this list without review from a DOM peer! + "StorageEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "StorageManager", nightly: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "StyleSheet", +// IMPORTANT: Do not change this list without review from a DOM peer! + "StyleSheetList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SubtleCrypto", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAngle", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedAngle", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedBoolean", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedEnumeration", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedInteger", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedLength", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedLengthList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedNumber", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedNumberList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedPreserveAspectRatio", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedRect", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedString", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimatedTransformList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimateElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimateMotionElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimateTransformElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGAnimationElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGCircleElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGClipPathElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGComponentTransferFunctionElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGDefsElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGDescElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGEllipseElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEBlendElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEColorMatrixElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEComponentTransferElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFECompositeElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEConvolveMatrixElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEDiffuseLightingElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEDisplacementMapElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEDistantLightElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEDropShadowElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEFloodElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEFuncAElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEFuncBElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEFuncGElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEFuncRElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEGaussianBlurElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEImageElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEMergeElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEMergeNodeElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEMorphologyElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEOffsetElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFEPointLightElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFESpecularLightingElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFESpotLightElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFETileElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFETurbulenceElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGFilterElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGForeignObjectElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGGElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGGradientElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGGraphicsElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGImageElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGLength", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGLengthList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGLinearGradientElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGLineElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGMarkerElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGMaskElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGMatrix", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGMetadataElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGMPathElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGNumber", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGNumberList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSeg", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegArcAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegArcRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegClosePath", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegCurvetoCubicAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegCurvetoCubicRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegCurvetoCubicSmoothAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegCurvetoCubicSmoothRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegCurvetoQuadraticAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegCurvetoQuadraticRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegCurvetoQuadraticSmoothAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegCurvetoQuadraticSmoothRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegLinetoAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegLinetoHorizontalAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegLinetoHorizontalRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegLinetoRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegLinetoVerticalAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegLinetoVerticalRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegMovetoAbs", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPathSegMovetoRel", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPatternElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPoint", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPointList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPolygonElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPolylineElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGPreserveAspectRatio", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGRadialGradientElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGRect", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGRectElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGScriptElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGSetElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGStopElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGStringList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGStyleElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGSVGElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGSwitchElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGSymbolElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGTextContentElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGTextElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGTextPathElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGTextPositioningElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGTitleElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGTransform", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGTransformList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGTSpanElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGUnitTypes", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGUseElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGViewElement", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGZoomAndPan", +// IMPORTANT: Do not change this list without review from a DOM peer! + "SVGZoomEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Text", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextDecoder", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextEncoder", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextMetrics", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextTrack", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextTrackCue", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextTrackCueList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TextTrackList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TimeEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TimeRanges", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Touch", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TouchEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TouchList", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TrackEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "TransitionEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "TreeColumn", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "TreeColumns", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "TreeContentView", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "TreeSelection", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "TreeWalker", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "U2F", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "UIEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "URL", +// IMPORTANT: Do not change this list without review from a DOM peer! + "URLSearchParams", +// IMPORTANT: Do not change this list without review from a DOM peer! + "UserProximityEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "ValidityState", +// IMPORTANT: Do not change this list without review from a DOM peer! + "VideoPlaybackQuality", +// IMPORTANT: Do not change this list without review from a DOM peer! + "VideoStreamTrack", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VRDisplay", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VRDisplayCapabilities", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VREyeParameters", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VRFieldOfView", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VRFrameData", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VRPose", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VRStageParameters", release: false}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "VTTCue", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "VTTRegion", disabled: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + "WaveShaperNode", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLActiveInfo", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLBuffer", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLContextEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLFramebuffer", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLProgram", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLQuery", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLRenderbuffer", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLRenderingContext", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGL2RenderingContext", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLSampler", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLShader", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLShaderPrecisionFormat", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLSync", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLTexture", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLTransformFeedback", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLUniformLocation", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebGLVertexArrayObject", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebKitCSSMatrix", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WebSocket", +// IMPORTANT: Do not change this list without review from a DOM peer! + "WheelEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Window", +// IMPORTANT: Do not change this list without review from a DOM peer! + "Worker", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XMLDocument", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XMLHttpRequest", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XMLHttpRequestEventTarget", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XMLHttpRequestUpload", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XMLSerializer", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XMLStylesheetProcessingInstruction", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XPathEvaluator", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XPathExpression", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XPathResult", +// IMPORTANT: Do not change this list without review from a DOM peer! + "XSLTProcessor", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULButtonElement", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULCheckboxElement", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULCommandDispatcher", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULCommandEvent", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULControlElement", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULControllers", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULDocument", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULElement", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULLabeledControlElement", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULPopupElement", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULTemplateBuilder", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "XULTreeBuilder", xbl: true}, +// IMPORTANT: Do not change this list without review from a DOM peer! + ]; +// IMPORTANT: Do not change the list above without review from a DOM peer! + +function createInterfaceMap(isXBLScope) { + var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version; + var isNightly = version.endsWith("a1"); + var isRelease = !version.includes("a"); + var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent); + var isMac = /Mac OS/.test(navigator.oscpu); + var isWindows = /Windows/.test(navigator.oscpu); + var isAndroid = navigator.userAgent.includes("Android"); + var isLinux = /Linux/.test(navigator.oscpu) && !isAndroid; + + var interfaceMap = {}; + + function addInterfaces(interfaces) + { + for (var entry of interfaces) { + if (typeof(entry) === "string") { + interfaceMap[entry] = true; + } else { + ok(!("pref" in entry), "Bogus pref annotation for " + entry.name); + if ((entry.nightly === !isNightly) || + (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) || + (entry.xbl === !isXBLScope) || + (entry.desktop === !isDesktop) || + (entry.windows === !isWindows) || + (entry.mac === !isMac) || + (entry.linux === !isLinux) || + (entry.android === !isAndroid && !entry.nightlyAndroid) || + (entry.release === !isRelease) || + entry.disabled) { + interfaceMap[entry.name] = false; + } else { + interfaceMap[entry.name] = true; + } + } + } + } + + addInterfaces(ecmaGlobals); + addInterfaces(interfaceNamesInGlobalScope); + if (isXBLScope) { + // We expose QueryInterface to XBL scopes. It's not an interface but we + // need to handle it because it's an own property of the global and the + // property name starts with an uppercase letter. + interfaceMap["QueryInterface"] = true; + } + + return interfaceMap; +} + +function runTest(isXBLScope) { + var interfaceMap = createInterfaceMap(isXBLScope); + for (var name of Object.getOwnPropertyNames(window)) { + // An interface name should start with an upper case character. + // However, we have a couple of legacy interfaces that start with 'moz', so + // we want to allow those until we can remove them. + if (!/^[A-Z]/.test(name) && legacyMozPrefixedInterfaces.indexOf(name) < 0) { + continue; + } + ok(interfaceMap[name], + "If this is failing: DANGER, are you sure you want to expose the new interface " + name + + " to all webpages as a property on the window (XBL: " + isXBLScope + ")? Do not make a change to this file without a " + + " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)"); + delete interfaceMap[name]; + } + for (var name of Object.keys(interfaceMap)) { + ok(name in window === interfaceMap[name], + name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the " + (isXBLScope ? "XBL" : "global") +" scope"); + if (!interfaceMap[name]) { + delete interfaceMap[name]; + } + } + if (isXBLScope) { + todo_is(Object.keys(interfaceMap).length, 0, + "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", ")); + } else { + is(Object.keys(interfaceMap).length, 0, + "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", ")); + } +} + +runTest(false); +SimpleTest.waitForExplicitFinish(); + +</script> +<span id="span" style="-moz-binding: url(file_interfaces.xml)"></span> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_navigation_timing.html b/dom/tests/mochitest/general/test_navigation_timing.html new file mode 100644 index 000000000..c131daca3 --- /dev/null +++ b/dom/tests/mochitest/general/test_navigation_timing.html @@ -0,0 +1,36 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822480 +--> +<head> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<pre id="test"> +<script type="application/javascript"> + +var subwindow = null; + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + subwindow = window.open("navigation_timing.html?x=0#_blank", "_blank"); +} + +function finishTests() { + subwindow.close(); + SimpleTest.finish(); +} + +</script> +</pre> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_network_events.html b/dom/tests/mochitest/general/test_network_events.html new file mode 100644 index 000000000..ef8853f64 --- /dev/null +++ b/dom/tests/mochitest/general/test_network_events.html @@ -0,0 +1,72 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=795136 +--> +<head> + <meta charset="utf-8"> + <title>Test for moznetworkupload and moznetworkdownload</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=795136">Mozilla Bug 795136</a> +<p id="display"></p> +<div id="content"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 795136 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.addPermission("network-events", true, document); + +var gNetworkUpload = false; +var gNetworkDownload = false; + +function isFinished() { + return gNetworkUpload && gNetworkDownload; +} + +function finish() { + removeEventListener('moznetworkupload', uploadHandler); + removeEventListener('moznetworkdownload', downloadHandler); + + SpecialPowers.removePermission("network-events", document); + + SimpleTest.finish(); +} + +function uploadHandler() { + ok(true, 'got a network upload event'); + gNetworkUpload = true; + + if (isFinished()) { + finish(); + } +} + +function downloadHandler() { + ok(true, 'got a network download event'); + gNetworkDownload = true; + + if (isFinished()) { + finish(); + } +} + +addEventListener('moznetworkupload', uploadHandler); +addEventListener('moznetworkdownload', downloadHandler); + +var iframe = document.createElement('iframe'); +iframe.src = 'http://mozilla.org'; + +document.getElementById('content').appendChild(iframe); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_offsets.css b/dom/tests/mochitest/general/test_offsets.css new file mode 100644 index 000000000..18231c4d9 --- /dev/null +++ b/dom/tests/mochitest/general/test_offsets.css @@ -0,0 +1,3 @@ + button, vbox, menu, menuitem, menupopup { + box-sizing: content-box; + } diff --git a/dom/tests/mochitest/general/test_offsets.html b/dom/tests/mochitest/general/test_offsets.html new file mode 100644 index 000000000..2926ac447 --- /dev/null +++ b/dom/tests/mochitest/general/test_offsets.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML> +<html style="margin: 5px; border: 0; padding: 1px;"> +<head> + <title>HTML Tests for offset/client/scroll properties</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="test_offsets.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> + +<style> + input { + box-sizing: content-box; + } +</style> +</head> + +<!-- We set a transform on the body element so that it creates a reference frame. + This makes sure that snapping of scrolled areas for the contained elements + is not influenced by offsets outside of this document. --> +<body id="body" onload="setTimeout(testElements, 0, 'testelements', SimpleTest.finish);" + style="margin: 1px; border: 2px solid black; padding: 4px; transform: translateY(1px);"> + +<div id="testelements" style="margin: 0; border: 0; padding: 0;"> + <div id="div1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px" + _offsetLeft="13" _offsetTop="9" _offsetWidth="64" _offsetHeight="34" + _scrollWidth="62" _scrollHeight="32" + _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"></div> + <div id="noscroll" style="margin: 2px; border: 1px solid blue; padding: 3px;" + _offsetLeft="10" _offsetTop="12" _offsetWidth="64" _offsetHeight="34" + _scrollWidth="62" _scrollHeight="32" + _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"> + <div id="inner">Inner Text</div> + </div> + + <div id="absolute" style="position: absolute; margin: 5px; border: 2px solid blue; padding: 0;"> + <div id="absolute-block" _offsetParent="absolute"> + <div id="absolute-replaced" _offsetParent="absolute" style="margin: 1px; border: 0; padding: 3px;"></div> + </div> + </div> + + <div id="absolutelr" style="position: absolute; margin: 5px; border: 2px solid blue; padding: 0; left: 90px; top: 130px;"> + This is some absolute positioned text. + <div id="absolutelr-block" _offsetParent="absolutelr"> + <div id="absolutelr-replaced" _offsetParent="absolutelr" style="margin: 1px; border: 0; padding: 3px;"></div> + </div> + </div> + + <div id="relative" style="position: relative; margin: 2px; border: 1px solid orange; padding: 7px; left: 10px; top: 5px;"> + This is some relative positioned text. + <div id="relative-block" _offsetParent="relative"> + <div id="relative-replaced" _offsetParent="relative" style="margin: 1px; border: 0; padding: 3px;"></div> + </div> + </div> + + <div id="fixed" style="position: fixed; margin: 2px; border: 1px solid orange; padding: 7px; left: 87px; top: 12px;"> + This is some fixed positioned text. + <div id="fixed-block" _offsetParent="fixed"> + <div id="fixed-replaced" _offsetParent="fixed" style="margin: 1px; border: 0; padding: 3px;"></div> + </div> + </div> + + <div id="scrollbox" + style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px" + _scrollWidth="62" _scrollHeight="32" + _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"><p id="p1" style="margin: 0; padding: 0;">One</p> + <p id="p2">Two</p> + <p id="scrollchild">Three</p> + <p id="lastlinebox" style="margin: 0; padding: 0;"><input id="lastline" type="button" + style="margin: 0px; border: 2px solid red;" + value="This button is much longer than the others"> + </p></div> + + <div id="overflow-visible" style="width:100px; height:100px;"> + <div id="overflow-visible-1" style="width:200px; height:1px; background:yellow;"></div> + <div id="overflow-visible-2" style="height:200px; background:lime;"></div> + </div> + + <div id="div-displaynone" style="display: none; border: 0; padding: 0;" + _offsetParent="null"></div> + <p id="p3" style="margin: 2px; border: 0; padding: 1px;" + _offsetLeft="9" _offsetTop="9" _offsetWidth="64" _offsetHeight="34" + _scrollWidth="62" _scrollHeight="32" + _clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"> + <div id="div-nosize" style="width: 0; height: 0; margin: 0; border: 0; padding: 0;"></div> + </p> + +</div> + +<div id="scrollbox-test" style="float: left; overflow: scroll; margin: 0; border: 0; padding: 0"></div> + +<script type="application/javascript"> +SimpleTest.waitForExplicitFinish(); +</script> + +<p id="display"></p> +<div id="content" style="display: none"> + +</div> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_offsets.js b/dom/tests/mochitest/general/test_offsets.js new file mode 100644 index 000000000..509daff8c --- /dev/null +++ b/dom/tests/mochitest/general/test_offsets.js @@ -0,0 +1,222 @@ +var scrollbarWidth = 17, scrollbarHeight = 17; + +function testElements(baseid, callback) +{ + scrollbarWidth = scrollbarHeight = gcs($("scrollbox-test"), "width"); + + var elements = $(baseid).getElementsByTagName("*"); + for (var t = 0; t < elements.length; t++) { + var element = elements[t]; + testElement(element); + } + + var nonappended = document.createElement("div"); + nonappended.id = "nonappended"; + nonappended.setAttribute("_offsetParent", "null"); + testElement(nonappended); + + checkScrolledElement($("scrollbox"), $("scrollchild")); + + var div = $("noscroll"); + div.scrollLeft = 10; + div.scrollTop = 10; + is(element.scrollLeft, 0, element.id + " scrollLeft after nonscroll"); + is(element.scrollTop, 0, element.id + " scrollTop after nonscroll"); + + callback(); +} + +function toNearestAppunit(v) +{ + // 60 appunits per CSS pixel; round result to the nearest appunit + return Math.round(v*60)/60; +} + +function isEqualAppunits(a, b, msg) +{ + is(toNearestAppunit(a), toNearestAppunit(b), msg); +} + +function testElement(element) +{ + var offsetParent = element.getAttribute("_offsetParent"); + offsetParent = $(offsetParent == "null" ? null: (offsetParent ? offsetParent : "body")); + + var borderLeft = gcs(element, "borderLeftWidth"); + var borderTop = gcs(element, "borderTopWidth"); + var borderRight = gcs(element, "borderRightWidth"); + var borderBottom = gcs(element, "borderBottomWidth"); + var paddingLeft = gcs(element, "paddingLeft"); + var paddingTop = gcs(element, "paddingTop"); + var paddingRight = gcs(element, "paddingRight"); + var paddingBottom = gcs(element, "paddingBottom"); + var width = gcs(element, "width"); + var height = gcs(element, "height"); + + if (element instanceof HTMLElement) + checkOffsetState(element, -10000, -10000, + borderLeft + paddingLeft + width + paddingRight + borderRight, + borderTop + paddingTop + height + paddingBottom + borderBottom, + offsetParent, element.id); + + var scrollWidth, scrollHeight, clientWidth, clientHeight; + var doScrollCheck = true; + if (element.id == "scrollbox") { + var lastchild = $("lastline"); + scrollWidth = lastchild.getBoundingClientRect().width + paddingLeft + paddingRight; + var top = element.firstChild.getBoundingClientRect().top; + var bottom = element.lastChild.getBoundingClientRect().bottom; + var contentsHeight = bottom - top; + scrollHeight = contentsHeight + paddingTop + paddingBottom; + clientWidth = paddingLeft + width + paddingRight - scrollbarWidth; + clientHeight = paddingTop + height + paddingBottom - scrollbarHeight; + } else { + clientWidth = paddingLeft + width + paddingRight; + clientHeight = paddingTop + height + paddingBottom; + if (element.id == "overflow-visible") { + scrollWidth = 200; + scrollHeight = 201; + } else if (element.scrollWidth > clientWidth || + element.scrollHeight > clientHeight) { + // The element overflows. Don't check scrollWidth/scrollHeight since the + // above calculation is not correct. + doScrollCheck = false; + } else { + scrollWidth = clientWidth; + scrollHeight = clientHeight; + } + } + + if (doScrollCheck) { + if (element instanceof SVGElement) + checkScrollState(element, 0, 0, 0, 0, element.id); + else + checkScrollState(element, 0, 0, scrollWidth, scrollHeight, element.id); + } + + if (element instanceof SVGElement) + checkClientState(element, 0, 0, 0, 0, element.id); + else + checkClientState(element, borderLeft, borderTop, clientWidth, clientHeight, element.id); + + var boundingrect = element.getBoundingClientRect(); + isEqualAppunits(boundingrect.width, borderLeft + paddingLeft + width + paddingRight + borderRight, + element.id + " bounding rect width"); + isEqualAppunits(boundingrect.height, borderTop + paddingTop + height + paddingBottom + borderBottom, + element.id + " bounding rect height"); + isEqualAppunits(boundingrect.right - boundingrect.left, boundingrect.width, + element.id + " bounding rect right"); + isEqualAppunits(boundingrect.bottom - boundingrect.top, boundingrect.height, + element.id + " bounding rect bottom"); + + var rects = element.getClientRects(); + if (element.id == "div-displaynone" || element.id == "nonappended") { + is(rects.length, 0, element.id + " getClientRects empty"); + } + else { + is(rects[0].left, boundingrect.left, element.id + " getClientRects left"); + is(rects[0].top, boundingrect.top, element.id + " getClientRects top"); + is(rects[0].right, boundingrect.right, element.id + " getClientRects right"); + is(rects[0].bottom, boundingrect.bottom, element.id + " getClientRects bottom"); + } +} + +function checkScrolledElement(element, child) +{ + var elemrect = element.getBoundingClientRect(); + var childrect = child.getBoundingClientRect(); + + var topdiff = childrect.top - elemrect.top; + + element.scrollTop = 20; + is(element.scrollLeft, 0, element.id + " scrollLeft after vertical scroll"); + is(element.scrollTop, 20, element.id + " scrollTop after vertical scroll"); + // If the viewport has been transformed, then we might have scrolled to a subpixel value + // that's slightly different from what we requested. After rounding, however, it should + // be the same. + is(Math.round(childrect.top - child.getBoundingClientRect().top), 20, "child position after vertical scroll"); + + element.scrollTop = 0; + is(element.scrollLeft, 0, element.id + " scrollLeft after vertical scroll reset"); + is(element.scrollTop, 0, element.id + " scrollTop after vertical scroll reset"); + // Scrolling back to the top should work precisely. + is(child.getBoundingClientRect().top, childrect.top, "child position after vertical scroll reset"); + + element.scrollTop = 10; + element.scrollTop = -30; + is(element.scrollLeft, 0, element.id + " scrollLeft after vertical scroll negative"); + is(element.scrollTop, 0, element.id + " scrollTop after vertical scroll negative"); + is(child.getBoundingClientRect().top, childrect.top, "child position after vertical scroll negative"); + + element.scrollLeft = 18; + is(element.scrollLeft, 18, element.id + " scrollLeft after horizontal scroll"); + is(element.scrollTop, 0, element.id + " scrollTop after horizontal scroll"); + is(Math.round(childrect.left - child.getBoundingClientRect().left), 18, "child position after horizontal scroll"); + + element.scrollLeft = -30; + is(element.scrollLeft, 0, element.id + " scrollLeft after horizontal scroll reset"); + is(element.scrollTop, 0, element.id + " scrollTop after horizontal scroll reset"); + is(child.getBoundingClientRect().left, childrect.left, "child position after horizontal scroll reset"); +} + +function checkOffsetState(element, left, top, width, height, parent, testname) +{ + checkCoords(element, "offset", left, top, width, height, testname); + is(element.offsetParent, parent, testname + " offsetParent"); +} + +function checkScrollState(element, left, top, width, height, testname) +{ + checkCoords(element, "scroll", left, top, width, height, testname); +} + +function checkClientState(element, left, top, width, height, testname) +{ + checkCoords(element, "client", left, top, width, height, testname); +} + +function checkCoord(element, type, val, testname) +{ + if (val != -10000) + is(element[type], Math.round(val), testname + " " + type); +} + +function checkCoordFuzzy(element, type, val, fuzz, testname) +{ + if (val != -10000) + ok(Math.abs(element[type] - Math.round(val)) <= fuzz, testname + " " + type); +} + +function checkCoords(element, type, left, top, width, height, testname) +{ + checkCoord(element, type + "Left", left, testname); + checkCoord(element, type + "Top", top, testname); + + if (type == "scroll") { + // scrollWidth and scrollHeight can deviate by 1 pixel due to snapping. + checkCoordFuzzy(element, type + "Width", width, 1, testname); + checkCoordFuzzy(element, type + "Height", height, 1, testname); + } else { + checkCoord(element, type + "Width", width, testname); + checkCoord(element, type + "Height", height, testname); + } + + if (element instanceof SVGElement) + return; + + if (element.id == "outerpopup" && !element.parentNode.open) // closed popup + return; + + if (element.id == "div-displaynone" || element.id == "nonappended") // hidden elements + ok(element[type + "Width"] == 0 && element[type + "Height"] == 0, + element.id + " has zero " + type + " width and height"); +} + +function gcs(element, prop) +{ + var propVal = (element instanceof SVGElement && (prop == "width" || prop == "height")) ? + element.getAttribute(prop) : getComputedStyle(element, "")[prop]; + if (propVal == "auto") + return 0; + return parseFloat(propVal); +} diff --git a/dom/tests/mochitest/general/test_offsets.xul b/dom/tests/mochitest/general/test_offsets.xul new file mode 100644 index 000000000..ff054dbca --- /dev/null +++ b/dom/tests/mochitest/general/test_offsets.xul @@ -0,0 +1,99 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<?xml-stylesheet href="test_offsets.css" type="text/css"?> +<!-- + XUL Tests for client/scroll properties + --> +<window title="Test Offset/Client/Scroll Properties" width="500" height="600" + style="margin: 1px !important" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="text/javascript" src="test_offsets.js"/> + +<vbox id="testelements" style="margin: 0; padding: 0; border: 0;"> +<vbox id="vbox" style="margin: 5px 0 0 2px;"> + <vbox id="noscroll" align="start"> + <button id="button1" label="Button One" style="margin: 0px; padding: 0; border: 0;"/> + <button id="button2" label="Button Two" width="140" height="120"/> + </vbox> + <hbox align="start"> + <vbox id="scrollbox" style="overflow: scroll; padding: 2px; margin: 3px; border: 4px solid green;" + maxwidth="66" maxheight="56"> + <label value="One" style="margin: 0"/> + <label id="scrollchild" value="Two"/> + <label value="Three"/> + <label id="lastline" value="This fourth label is much longer than the others" + style="margin: 0; padding: 0; border: 0;"/> + </vbox> + <vbox id="scrollbox-test"> + <scrollbar orient="vertical" style="border: 0; padding: 0;"/> + </vbox> + </hbox> +</vbox> + +<!-- wrap svg in a div so that it can take its intrinsic width --> +<div> +<svg:svg id="svgbase" width="45" height="20" xmlns:svg="http://www.w3.org/2000/svg"> + <svg:rect id="svgrect" x="3" y="5" width="45" height="20" fill="red"/> +</svg:svg> +</div> + +</vbox> + +<button id="outermenu" type="menu" label="Menu"> + <menupopup id="outerpopup" + style="margin-left: 5px; padding-left: 3px; padding: 0;" + onpopupshown="this.firstChild.open = true" + onpopuphidden="if (event.target == this) SimpleTest.finish();"> + <menu id="innermenu" label="Open" + style="margin: 0; padding: 0; border: 2px black solid; -moz-appearance: none;"> + <menupopup style="margin: 0; padding: 0; border: 1px black solid; -moz-appearance: none;" + onpopupshown="testElements('outermenu', doneTests)"> + <menuitem label="Document"/> + <menuitem id="innermenuitem" style="margin: 2px; padding: 3px;" label="Page"/> + </menupopup> + </menu> + <menuitem id="outermenuitem" label="Close"/> + </menupopup> +</button> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + +var gTestSet = "box"; + +var whichpopup = "outer"; + +SimpleTest.waitForExplicitFinish(); + +function startTests() +{ + testElements('testelements', doneTests); +} + +function doneTests() +{ + if (gTestSet == "box") { + gTestSet = "popup"; + // only test this on Mac for now + if (navigator.platform.indexOf("Mac") >= 0) { + checkScrollState($("outerpopup"), 0, 0, 0, 0, "popup before open"); + checkClientState($("outerpopup"), 0, 0, 0, 0, "popup before open"); + } + $("outermenu").open = true; + } + else { + $("outermenu").open = false; + } +} + +SimpleTest.waitForFocus(startTests); + +]]> +</script> + +</window> diff --git a/dom/tests/mochitest/general/test_outerHTML.html b/dom/tests/mochitest/general/test_outerHTML.html new file mode 100644 index 000000000..e1636cd2a --- /dev/null +++ b/dom/tests/mochitest/general/test_outerHTML.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=92264 +--> +<head> + <title>Test for Bug 92264</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body onload="runTest();"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=92264">Mozilla Bug 92264</a> +<p id="display"></p> +<div id="content" style="display: none"> +<div id="wrap"><dl></dl><p id="thep">foo<span>bar</span></p><ol></ol></div> +<table id="thetable"><tbody><tr><td>1</td></tr><tr id="thetr"><td>2</td></tr><tr><td>3</td></tr></tbody></table> +<iframe></iframe> +<div id="fragmentwrap"></div> +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 92264 **/ + +SimpleTest.waitForExplicitFinish(); + +function runTest() { + + var thep = document.getElementById("thep"); + var wrap = document.getElementById("wrap"); + is(thep.outerHTML, '<p id="thep">foo<span>bar</span></p>', "Unexpected thep outerHTML"); + thep.outerHTML = "<ul></ul><tr></tr><p></p>"; + is(wrap.innerHTML, "<dl></dl><ul></ul><p></p><ol></ol>", "Bad outerHTML parsing inside wrap"); + + var thetr = document.getElementById("thetr"); + thetr.outerHTML = "<tr><td>a</td></tr><div></div><tr><td>b</td></tr>"; + var thetable = document.getElementById("thetable"); + is(thetable.innerHTML, "<tbody><tr><td>1</td></tr><tr><td>a</td></tr><div></div><tr><td>b</td></tr><tr><td>3</td></tr></tbody>", "Wrong outerHTML parsing inside table"); + + var iframe = document.getElementsByTagName("iframe")[0]; + var oldbody = iframe.contentDocument.body; + iframe.contentDocument.body.outerHTML = "<body></body>"; + isnot(oldbody, iframe.contentDocument.body, "Failed to replace body"); + is(iframe.contentDocument.getElementsByTagName("body").length, 1, "Should have gotten one body"); + // Yes, two heads per spec. Also Ragnarök and Chrome produce two heads. + is(iframe.contentDocument.getElementsByTagName("head").length, 2, "Should have gotten two heads"); + + try { + document.documentElement.outerHTML = "<html></html>"; + ok(false, "Should have thrown an exception"); + } catch(e) { + is(e.name, "NoModificationAllowedError", "outerHTML should throw NoModificationAllowedError"); + is(e.code, 7, "outerHTML should throw NO_MODIFICATION_ALLOWED_ERR"); + } + + var f = document.createDocumentFragment(); + var dl = document.createElement("dl"); + var p = document.createElement("p"); + var ol = document.createElement("ol"); + f.appendChild(dl); + f.appendChild(p); + f.appendChild(ol); + p.outerHTML = "<ul></ul><tr></tr><body></body><p></p>"; + var fragmentwrap = document.getElementById("fragmentwrap"); + fragmentwrap.appendChild(f); + is(fragmentwrap.innerHTML, "<dl></dl><ul></ul><p></p><ol></ol>", "Bad outerHTML parsing in fragment"); + + SimpleTest.finish(); +} + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_outerHTML.xhtml b/dom/tests/mochitest/general/test_outerHTML.xhtml new file mode 100644 index 000000000..e1274a259 --- /dev/null +++ b/dom/tests/mochitest/general/test_outerHTML.xhtml @@ -0,0 +1,75 @@ +<!DOCTYPE HTML> +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=92264 +--> +<head> + <title>Test for Bug 92264</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body onload="runTest();"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=92264">Mozilla Bug 92264</a> +<p id="display"></p> +<div id="content" style="display: none"> +<div id="wrap"><dl></dl><p id="thep">foo<span>bar</span></p><ol></ol></div> +<table id="thetable"><tbody><tr><td>1</td></tr><tr id="thetr"><td>2</td></tr><tr><td>3</td></tr></tbody></table> +<iframe></iframe> +<div id="fragmentwrap"></div> +</div> +<pre id="test"> +<script type="application/javascript"> +<![CDATA[ + +/** Test for Bug 92264 **/ + +SimpleTest.waitForExplicitFinish(); + +function runTest() { + + var thep = document.getElementById("thep"); + var wrap = document.getElementById("wrap"); + is(thep.outerHTML, '<p xmlns="http://www.w3.org/1999/xhtml" id="thep">foo<span>bar</span></p>', "Unexpected thep outerHTML"); + thep.outerHTML = "<ul></ul><tr></tr><p></p>"; + is(wrap.innerHTML, '<dl xmlns="http://www.w3.org/1999/xhtml"></dl><ul xmlns="http://www.w3.org/1999/xhtml"></ul><tr xmlns="http://www.w3.org/1999/xhtml"></tr><p xmlns="http://www.w3.org/1999/xhtml"></p><ol xmlns="http://www.w3.org/1999/xhtml"></ol>', "Bad outerHTML parsing inside wrap"); + + var thetr = document.getElementById("thetr"); + thetr.outerHTML = "<tr><td>a</td></tr><div></div><tr><td>b</td></tr>"; + var thetable = document.getElementById("thetable"); + is(thetable.innerHTML, '<tbody xmlns="http://www.w3.org/1999/xhtml"><tr><td>1</td></tr><tr><td>a</td></tr><div></div><tr><td>b</td></tr><tr><td>3</td></tr></tbody>', "Wrong outerHTML parsing inside table"); + + var iframe = document.getElementsByTagName("iframe")[0]; + var oldbody = iframe.contentDocument.body; + iframe.contentDocument.body.outerHTML = "<body></body>"; + isnot(oldbody, iframe.contentDocument.body, "Failed to replace body"); + is(iframe.contentDocument.getElementsByTagName("body").length, 1, "Should have gotten one body"); + // Yes, two heads per spec. Also Ragnarök and Chrome produce two heads. + is(iframe.contentDocument.getElementsByTagName("head").length, 2, "Should have gotten two heads"); + + try { + document.documentElement.outerHTML = "<html></html>"; + ok(false, "Should have thrown an exception"); + } catch(e) { + is(e.name, "NoModificationAllowedError", "outerHTML should throw NoModificationAllowedError"); + is(e.code, 7, "outerHTML should throw NO_MODIFICATION_ALLOWED_ERR"); + } + + var f = document.createDocumentFragment(); + var dl = document.createElement("dl"); + var p = document.createElement("p"); + var ol = document.createElement("ol"); + f.appendChild(dl); + f.appendChild(p); + f.appendChild(ol); + p.outerHTML = "<ul></ul><tr></tr><body></body><p></p>"; + var fragmentwrap = document.getElementById("fragmentwrap"); + fragmentwrap.appendChild(f); + is(fragmentwrap.innerHTML, '<dl xmlns="http://www.w3.org/1999/xhtml"></dl><ul xmlns="http://www.w3.org/1999/xhtml"></ul><tr xmlns="http://www.w3.org/1999/xhtml"></tr><body xmlns="http://www.w3.org/1999/xhtml"></body><p xmlns="http://www.w3.org/1999/xhtml"></p><ol xmlns="http://www.w3.org/1999/xhtml"></ol>', "Bad outerHTML parsing in fragment"); + + SimpleTest.finish(); +} +]]> +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_paste_selection.html b/dom/tests/mochitest/general/test_paste_selection.html new file mode 100644 index 000000000..0072e40b7 --- /dev/null +++ b/dom/tests/mochitest/general/test_paste_selection.html @@ -0,0 +1,122 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for middle-click to paste selection with paste events</title> + <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> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<p id="display"></p> +<input id="copy-area" value="CLIPBOARD"> +<input id="paste-selection-area" value="" onpaste="pastedSelection(event)"> +<input id="paste-global-area" value="" onpaste="pastedGlobal(event)"> + +<script> + +var supportsSelectionClipboard = SpecialPowers.supportsSelectionClipboard(); + +function checkSelectionClipboardText(count) +{ + if ((!supportsSelectionClipboard || + SpecialPowers.getClipboardData("text/unicode", SpecialPowers.Ci.nsIClipboard.kSelectionClipboard) == "COPY TEXT") && + SpecialPowers.getClipboardData("text/unicode", SpecialPowers.Ci.nsIClipboard.kGlobalClipboard) == "CLIPBOARD") { + pasteArea(); + return; + } + + if (count > 10) { + ok(false, "could not set clipboards"); + pasteArea(); + SimpleTest.finish(); + return; + } + + setTimeout(checkSelectionClipboardText, 5, count + 1); +} + +function selectArea() +{ + var copyArea = document.getElementById("copy-area"); + copyArea.focus(); + copyArea.select(); + synthesizeKey("x", { accelKey: true }); + + if (supportsSelectionClipboard) { + var clipboardHelper = SpecialPowers.Cc["@mozilla.org/widget/clipboardhelper;1"] + .getService(SpecialPowers.Ci.nsIClipboardHelper); + clipboardHelper.copyStringToClipboard("COPY TEXT", + SpecialPowers.Ci.nsIClipboard.kSelectionClipboard); + } + + setTimeout(checkSelectionClipboardText, 50, 0); +} + +var selectionPasted = false; +var globalPasted = false; + +function pasteArea() +{ + var pasteArea = document.getElementById("paste-selection-area"); + var pasteAreaRect = pasteArea.getBoundingClientRect(); + var pasteAreaCenterX = pasteAreaRect.left + pasteAreaRect.width/2; + var pasteAreaCenterY = pasteAreaRect.top + pasteAreaRect.height/2; + var util = SpecialPowers.getDOMWindowUtils(window); + + pasteArea.focus(); + util.sendMouseEventToWindow("mousedown", pasteAreaCenterX, pasteAreaCenterY, 1, 1, 0, true); + util.sendMouseEventToWindow("mouseup", pasteAreaCenterX, pasteAreaCenterY, 1, 1, 0, true); + + var usesMouseButtonPaste = SpecialPowers.getBoolPref("middlemouse.paste"); + if (usesMouseButtonPaste) { + // The data transfer should contain the selection data when the selection clipboard is supported, + // not the global clipboard data. + var expectedText = supportsSelectionClipboard ? "COPY TEXT" : "CLIPBOARD"; + is(document.getElementById("paste-selection-area").value, expectedText, "In pasteArea(): data pasted properly from selection"); + ok(selectionPasted, "selection event fired"); + } + else { + is(pasteArea.value, "", "data not pasted when middle click not supported"); + } + + var pasteArea = document.getElementById("paste-global-area"); + pasteArea.focus(); + synthesizeKey("v", { accelKey: true }); + + ok(globalPasted, "global event fired"); + is(document.getElementById("paste-global-area").value, "CLIPBOARD", "data pasted properly from global clipboard"); + SimpleTest.finish(); +} + +function pastedSelection(event) +{ + ok(SpecialPowers.getBoolPref("middlemouse.paste"), "paste on middle click is valid"); + + // Mac and Windows shouldn't get here as the middle mouse preference is false by default + ok(navigator.platform.indexOf("Mac") == -1 && navigator.platform.indexOf("Win") == -1, "middle click enabled on right platforms"); + + var expectedText = supportsSelectionClipboard ? "COPY TEXT" : "CLIPBOARD"; + is(event.clipboardData.getData("text/plain"), expectedText, "In pastedSelection(): data pasted properly from selection"); + + selectionPasted = true; +} + +function pastedGlobal(event) +{ + // The data transfer should contain the global data. + is(event.clipboardData.getData("text/plain"), "CLIPBOARD", "data correct in global clipboard data transfer"); + globalPasted = true; +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); +SimpleTest.waitForFocus(function() { + SpecialPowers.pushPrefEnv({"set": [["general.autoScroll", false]]}, selectArea); +}); +</script> + +</pre> +</body> +</html> + diff --git a/dom/tests/mochitest/general/test_performance_now.html b/dom/tests/mochitest/general/test_performance_now.html new file mode 100644 index 000000000..f062b6ee2 --- /dev/null +++ b/dom/tests/mochitest/general/test_performance_now.html @@ -0,0 +1,66 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for High Resolution Timer</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + <script> + ok(window.performance, "Performance object should exist."); + ok(typeof window.performance.now == 'function', "Performance object should have a 'now' method."); + var n = window.performance.now(), d = Date.now(); + ok(n >= 0, "The value of now() should be equal to or greater than 0."); + ok(window.performance.now() >= n, "The value of now() should monotonically increase."); + SimpleTest.waitForExplicitFinish(); + SimpleTest.requestFlakyTimeout("untriaged"); + + // The spec says performance.now() should have micro-second resolution, but allows 1ms if the platform doesn't support it. + // Our implementation does provide micro-second resolution, except for windows XP combined with some HW properties + // where we can't use QueryPerformanceCounters (see comments at mozilla-central/xpcom/ds/TimeStamp_windows.cpp). + // This XP-low-res case results in about 15ms resolutions, and can be identified when perf.now() returns only integers. + // + // Since setTimeout might return too early/late, our goal is that perf.now() changed within 2ms + // (or 25ms for XP-low-res), rather than specific number of setTimeout(N) invocations. + // See bug 749894 (intermittent failures of this test) + var platformPossiblyLowRes = navigator.oscpu.indexOf("Windows NT 5.1") == 0; // XP only + var allInts = (n % 1) == 0; // Indicator of limited HW resolution. + var checks = 0; + + function checkAfterTimeout() { + checks++; + var d2 = Date.now(); + var n2 = window.performance.now(); + + allInts = allInts && (n2 % 1) == 0; + var lowResCounter = platformPossiblyLowRes && allInts; + + if ( n2 == n && checks < 50 && // 50 is just a failsafe. Our real goals are 2ms or 25ms. + ( (d2 - d) < 2 // The spec allows 1ms resolution. We allow up to measured 2ms to ellapse. + || + lowResCounter && + (d2 - d) < 25 + ) + ) { + setTimeout(checkAfterTimeout, 1); + return; + } + + // Loose spec: 1ms resolution, or 15ms resolution for the XP-low-res case. + // We shouldn't test that dt is actually within 2/25ms since the iterations break if it isn't, and timeout could be late. + ok(n2 > n, "Loose - the value of now() should increase within 2ms (or 25ms if low-res counter) (delta now(): " + (n2 - n) + " ms)."); + + // Strict spec: if it's not the XP-low-res case, while the spec allows 1ms resolution, it prefers microseconds, which we provide. + // Since the fastest setTimeout return which I observed was ~500 microseconds, a microseconds counter should change in 1 iteretion. + ok(n2 > n && (lowResCounter || checks == 1), + "Strict - [if high-res counter] the value of now() should increase after one setTimeout (hi-res: " + (!lowResCounter) + + ", iters: " + checks + + ", dt: " + (d2 - d) + + ", now(): " + n2 + ")."); + SimpleTest.finish(); + }; + setTimeout(checkAfterTimeout, 1); + </script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_performance_timeline.html b/dom/tests/mochitest/general/test_performance_timeline.html new file mode 100644 index 000000000..0db8a19b4 --- /dev/null +++ b/dom/tests/mochitest/general/test_performance_timeline.html @@ -0,0 +1,36 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<pre id="test"> +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +// Resource timing is prefed off by default, so we had to use this workaround +SpecialPowers.pushPrefEnv({"set": [["dom.enable_resource_timing", true], + ["dom.enable_user_timing", true]]}, start); +var subwindow = null; + +function start() { + subwindow = window.open("performance_timeline_main_test.html"); +} + +function finishTests() { + subwindow.close(); + SimpleTest.finish(); +} +</script> +</pre> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_picture_apng.html b/dom/tests/mochitest/general/test_picture_apng.html new file mode 100644 index 000000000..f8cf818d8 --- /dev/null +++ b/dom/tests/mochitest/general/test_picture_apng.html @@ -0,0 +1,77 @@ +<!DOCTYPE HTML> +<html> + +<head> + <meta charset="utf-8"> + <title>Image srcset mutations</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<script type="application/javascript"> +"use strict"; +window.onload = function() { + // Smoke test, check picture working as expected + const t0 = document.querySelector("#test0 img"); + ok(t0.currentSrc.endsWith("apng"), `t0: expected pass.apng, got '${t0.currentSrc}'`); + + // Test that the apng is selected over bogus types. + const t1 = document.querySelector("#test1 img"); + ok(t1.currentSrc.endsWith("apng"), `t1: expected pass.apng, got '${t1.currentSrc}'`); + + // Test that tree order precedence applies + const t2 = document.querySelector("#test2 img"); + ok(t2.currentSrc.endsWith("apng"), `t2: expected pass.apng, got '${t2.currentSrc}'`); + + // Test that apng doesn't alway win + const t3 = document.querySelector("#test3 img"); + ok(t3.currentSrc.endsWith("apng"), `t3: expected pass.apng, got '${t3.currentSrc}'`); + + // Test dynamically constructed picture, where apng is selected over a bogus + // source or the img src attribute + const pic = document.createElement("picture"); + pic.id = "test4"; + const t4 = document.createElement("img"); + const bogusSource = document.createElement("source"); + bogusSource.type = "bogus/bogus"; + bogusSource.srcset = "fail.png"; + const legitSource = document.createElement("source"); + legitSource.type = "image/apng"; + legitSource.srcset = "pass.apng"; + pic.appendChild(bogusSource); + pic.appendChild(legitSource); + pic.appendChild(t4); + t4.src = "fail.png"; + document.body.appendChild(pic); + t4.onload = ()=>{ + ok(t4.currentSrc.endsWith("apng"), `t4: Expected pass.apng, got '${t4.currentSrc}'`); + SimpleTest.finish(); + } +}; +SimpleTest.waitForExplicitFinish(); +</script> + +<body> + <picture id="test0"> + <source> + <img src="pass.apng"> + </picture> + <picture id="test1"> + <source type="bogus/type" srcset="fail.png"> + <source type="image/apng" srcset="pass.apng"> + <source type="image/jpeg" srcset="fail.png"> + <img src="fail-fallback"> + </picture> + <picture id="test2"> + <source type="image/png" srcset="pass.apng"> + <source srcset="fail.png"> + <source type="bogus/type" srcset="fail.png"> + <img src="fail-fallback"> + </picture> + <picture id="test3"> + <source type="image/jpeg" srcset="pass.apng"> + <source type="image/apng" srcset="fail.png"> + <img src="fail-fallback"> + </picture> +</body> + +</html> diff --git a/dom/tests/mochitest/general/test_picture_mutations.html b/dom/tests/mochitest/general/test_picture_mutations.html new file mode 100644 index 000000000..491504aba --- /dev/null +++ b/dom/tests/mochitest/general/test_picture_mutations.html @@ -0,0 +1,311 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Image srcset mutations</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + <script type="application/javascript"> + "use strict"; + + // Tests the relevant mutations part of the spec for <img> inside <picture> tags + // https://html.spec.whatwg.org/#relevant-mutations + SimpleTest.waitForExplicitFinish(); + + // 50x50 png + var testPNG50 = new URL("image_50.png", location).href; + // 100x100 png + var testPNG100 = new URL("image_100.png", location).href; + // 200x200 png + var testPNG200 = new URL("image_200.png", location).href; + + var tests = []; + var img; + var picture; + var source1; + var source2; + var source3; + var expectingErrors = 0; + var expectingLoads = 0; + var afterExpectCallback; + + function onImgLoad() { + ok(expectingLoads > 0, "expected load"); + if (expectingLoads > 0) { + expectingLoads--; + } + if (!expectingLoads && !expectingErrors) { + setTimeout(afterExpectCallback, 0); + } + } + function onImgError() { + ok(expectingErrors > 0, "expected error"); + if (expectingErrors > 0) { + expectingErrors--; + } + if (!expectingLoads && !expectingErrors) { + setTimeout(afterExpectCallback, 0); + } + } + function expectEvents(loads, errors, callback) { + if (!loads && !errors) { + setTimeout(callback, 0); + } else { + expectingLoads += loads; + expectingErrors += errors; + info("Waiting for " + expectingLoads + " load and " + expectingErrors + " error events"); + afterExpectCallback = callback; + } + } + + // Setup image outside the tree dom, make sure it loads + tests.push(function() { + info("test 1"); + img.srcset = testPNG100; + img.src = testPNG50; + is(img.currentSrc, '', "Should not have synchronously selected source"); + + // No events should have fired synchronously, now we should get just one load (and no 404 error) + expectEvents(1, 0, nextTest); + }); + + // Binding to an empty picture should trigger an event, even if source doesn't change + tests.push(function() { + info("test 2"); + is(img.currentSrc, testPNG100, "Should have loaded testPNG100"); + document.body.appendChild(picture); + picture.appendChild(img); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + expectEvents(1, 0, nextTest); + }); + + // inserting and removing an empty source before the image should both trigger a no-op reload + tests.push(function() { + info("test 3"); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + picture.insertBefore(source1, img); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // should fire one event, not change source + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + picture.removeChild(source1); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // Should also no-op fire + expectEvents(1, 0, nextTest); + }); + }); + + // insert and remove valid source before + tests.push(function() { + info("test 4"); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // Insert source1 before img with valid candidate + source1.srcset = testPNG50; + picture.insertBefore(source1, img); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // should fire one event, change to the source + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG50, "Should have switched to testPNG50"); + picture.removeChild(source1); + is(img.currentSrc, testPNG50, "Should still have testPNG50"); + + // Should also no-op fire + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG100, "Should have returned to testPNG100"); + nextTest(); + }); + }); + }); + + // insert and remove valid source after + tests.push(function() { + info("test 5"); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // Insert source1 before img with valid candidate + source1.srcset = testPNG50; + picture.appendChild(source1); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // should fire nothing, no action + expectEvents(0, 0, function() { + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // Same with removing + picture.removeChild(source1); + expectEvents(0, 0, function() { + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + nextTest(); + }); + }); + }); + + // Should re-consider earlier sources when a load event occurs. + tests.push(function() { + info("test 6"); + + // Insert two valid sources, with MQ causing us to select the second + source1.srcset = testPNG50 + " 1x"; + source1.media = "(min-resolution: 2dppx)"; // Wont match, test starts at 1x + source2.srcset = testPNG200; + picture.insertBefore(source1, img); + picture.insertBefore(source2, img); + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // should get one load, selecting source2 + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG200, "Should have selected testPNG200"); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG50, "Should have switched to testPNG50"); + + // Now add a source *also* wanting that DPI *just before* the + // selected source. Properly re-running the algorithm should + // re-consider all sources and thus go back to the first + // source, not just the valid source just inserted before us. + source3.media = source1.media; + source3.srcset = testPNG100; + picture.insertBefore(source3, source2); + // This should trigger a reload, but we should re-consider + // source1 and remain with that, not just the newly added source2 + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG50, "Should have remained on testPNG50"); + expectEvents(0, 0, nextTest); + }); + }); + + // Switch DPI to match the first source. + SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] }); + }); + }); + + // insert and remove valid source after our current source should + // trigger a reload, but not switch source + tests.push(function() { + info("test 7"); + // Should be using source1 from last test + is(img.currentSrc, testPNG50, "Should still have testPNG50"); + + // Remove source2, should trigger an event even though we would + // not switch + + picture.removeChild(source2); + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG50, "Should still have testPNG50"); + + // Same with re-adding + picture.insertBefore(source2, img); + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG50, "Should still have testPNG50"); + expectEvents(0, 0, nextTest); + }); + }); + }); + + // Changing source attributes should trigger updates + tests.push(function() { + info("test 8"); + // Should be using source1 from last test + is(img.currentSrc, testPNG50, "Should still have testPNG50"); + + // Reconfigure source1 to have empty srcset. Should switch to + // next source due to becoming invalid. + source1.srcset = ""; + is(img.currentSrc, testPNG50, "Should still have testPNG50"); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG100, "Should have switched to testPNG100"); + + // Give source1 valid sizes. Should trigger an event but not switch anywhere. + source1.sizes = "100vw"; + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG100, "Should still have testPNG100"); + + // And a valid MQ + source1.media = "(min-resolution: 1dppx)"; + expectEvents(1, 0, function() { + // And a valid type... + source1.type = "image/png"; + expectEvents(1, 0, function() { + // Finally restore srcset, should trigger load and re-consider source1 which is valid again + source1.srcset = testPNG50; + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG50, "Should have selected testPNG50"); + expectEvents(0, 0, nextTest); + }); + }); + }); + }); + }); + }); + + // Inserting a source after <img> and touching its attributes should all be no-ops + tests.push(function() { + info("test 9"); + // Setup: source2 + picture.removeChild(source2); + + expectEvents(1, 0, function() { + is(img.currentSrc, testPNG50, "Should still have testPNG50"); + + source2.srcset = testPNG200; + source2.media = ""; + source2.sizes = "100vw"; + source2.type = "image/png"; + // Append valid source + picture.appendChild(source2); + // Touch all the things (but keep it valid) + source2.srcset = testPNG100; + source2.media = "(min-resolution: 2dppx)"; + source2.sizes = "50vw"; + source2.type = "image/png"; + + // No event should fire. Source should not change. + expectEvents(0, 0, function() { + is(img.currentSrc, testPNG50, "Should still have testPNG50"); + expectEvents(0, 0, nextTest); + }); + }); + }); + + function nextTest() { + if (tests.length) { + // Spin event loop to make sure no unexpected image events are + // pending (unexpected events will assert in the handlers) + setTimeout(function() { + (tests.shift())(); + }, 0); + } else { + // We'll get a flood of load events due to prefs being popped while cleaning up. + // Ignore it all. + img.removeEventListener("load", onImgLoad); + img.removeEventListener("error", onImgError); + SimpleTest.finish(); + } + } + + addEventListener("load", function() { + SpecialPowers.pushPrefEnv({'set': [["layout.css.devPixelsPerPx", "1.0" ]] }, + function() { + // Create these after the pref is set, as it is guarding webIDL attributes + img = document.createElement("img"); + img.addEventListener("load", onImgLoad); + img.addEventListener("error", onImgError); + picture = document.createElement("picture"); + source1 = document.createElement("source"); + source2 = document.createElement("source"); + source3 = document.createElement("source"); + setTimeout(nextTest, 0); + }); + }); + </script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_pointerPreserves3D.html b/dom/tests/mochitest/general/test_pointerPreserves3D.html new file mode 100644 index 000000000..b9826a185 --- /dev/null +++ b/dom/tests/mochitest/general/test_pointerPreserves3D.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for pointer events with preserve-3d</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<div> + <div> + <div style="transform-style: preserve-3d; transform: rotateX(90deg);"> + <div id="color" style="transform: rotateX(-90deg); display: block; background-color: blue; width: 200px; height: 200px;"></div> + </div> + </div> +</div> +<script class="testbody" type="text/javascript"> +function runTest() { + var target = document.elementFromPoint(100, 110); + ok(target == document.getElementById("color"), "Find the right target."); +} + +runTest(); +</script> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_pointerPreserves3DClip.html b/dom/tests/mochitest/general/test_pointerPreserves3DClip.html new file mode 100644 index 000000000..29c8f3e7e --- /dev/null +++ b/dom/tests/mochitest/general/test_pointerPreserves3DClip.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for pointer events with preserve-3d and clips</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <style type="text/css"> + .outer { + transform-style: preserve-3d; + } + .container { + overflow-y: scroll; + overflow-x: hidden; + width: 200px; + height: 300px; + } + .content { + width: 200px; + height: 1000px; + transform-style: preserve-3d; + } + #container1 { + background-color: green; + transform: translateZ(2px); + } + #container2 { + height: 100px; + transform: translateY(-200px) translateZ(10px); + background-color: red; + } + </style> + </head> + <body onload="runTest();"> + <div class="outer" id="outer"> + <div class="container" id="container1"> + <div class="content"></div> + </div> + <div class="container" id="container2"> + <div class="content"></div> + </div> + </div> +<script class="testbody" type="text/javascript"> +function runTest() { + var outer = document.getElementById("outer"); + var x = outer.offsetLeft; + var y = outer.offsetTop; + var target = document.elementFromPoint(x + 100, y + 250); + ok(target.parentNode == document.getElementById("container1"), "Find the right target."); + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +</script> + </body> +</html> diff --git a/dom/tests/mochitest/general/test_resource_timing.html b/dom/tests/mochitest/general/test_resource_timing.html new file mode 100644 index 000000000..febd9f5ca --- /dev/null +++ b/dom/tests/mochitest/general/test_resource_timing.html @@ -0,0 +1,38 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822480 +--> +<head> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<pre id="test"> +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +// Resource timing is prefed off by default, so we had to use this workaround +SpecialPowers.pushPrefEnv({"set": [["dom.enable_resource_timing", true]]}, start); +var subwindow = null; + +function start() { + subwindow = window.open("resource_timing_main_test.html"); +} + +function finishTests() { + subwindow.close(); + SimpleTest.finish(); +} +</script> +</pre> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_resource_timing_cross_origin.html b/dom/tests/mochitest/general/test_resource_timing_cross_origin.html new file mode 100644 index 000000000..9085d3121 --- /dev/null +++ b/dom/tests/mochitest/general/test_resource_timing_cross_origin.html @@ -0,0 +1,45 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=822480 +--> +<head> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<pre id="test"> +<script type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +// Resource timing is prefed off by default, so we had to use this workaround +var subwindow = null; +SpecialPowers.pushPrefEnv({"set": [["dom.enable_resource_timing", true]]}, start); + +function start() { + subwindow = window.open("resource_timing_cross_origin.html"); +} + +function finishTests() { + subwindow.close(); + SpecialPowers.pushPrefEnv({"clear":[["dom.enable_resource_timing"]]}, SimpleTest.finish); +} + +</script> +</pre> + +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=936814" + title="Cross origin resource timing"> + Bug #936814 - Implement the "timing allow check algorithm" for cross-origin Resource Timing +</a> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_resource_timing_frameset.html b/dom/tests/mochitest/general/test_resource_timing_frameset.html new file mode 100644 index 000000000..64f5ce71b --- /dev/null +++ b/dom/tests/mochitest/general/test_resource_timing_frameset.html @@ -0,0 +1,29 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN"> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>browser_frametree_sample_frameset.html</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + + <script type="text/javascript"> + SimpleTest.waitForExplicitFinish(); + window.addEventListener("load", function() { + var frameEntries = performance.getEntriesByName("http://mochi.test:8888/tests/dom/base/test/file_empty.html"); + + is(frameEntries.length, 2, "correct number"); + is(frameEntries[0].initiatorType, "frame", "correct type"); + SimpleTest.finish(); + }); + </script> + </head> + <frameset id="frames" rows="50%, 50%"> + <frame src="http://mochi.test:8888/tests/dom/base/test/file_empty.html"> + <frame src="http://mochi.test:8888/tests/dom/base/test/file_empty.html"> + </frameset> +</html> diff --git a/dom/tests/mochitest/general/test_selectevents.html b/dom/tests/mochitest/general/test_selectevents.html new file mode 100644 index 000000000..d510d19d0 --- /dev/null +++ b/dom/tests/mochitest/general/test_selectevents.html @@ -0,0 +1,32 @@ +<!doctype html> +<html> + <head> + <title>Testing Selection Events</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> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + </head> + + <body> + <iframe width="500"></iframe> + <script> + add_task(function* () { + // Push the correct preferences for the test + yield new Promise((done) => { + SpecialPowers.pushPrefEnv({'set': [['dom.select_events.enabled', true]]}, done); + }); + + // Start the actual test + yield new Promise((done) => { + var iframe = document.querySelector('iframe'); + iframe.addEventListener('load', done); + iframe.setAttribute('src', 'frameSelectEvents.html'); + }); + + // The child iframe will call add_task before we reach this point, + // and will handle the rest of the test. + }); + </script> + </body> +</html> diff --git a/dom/tests/mochitest/general/test_showModalDialog.html b/dom/tests/mochitest/general/test_showModalDialog.html new file mode 100644 index 000000000..511c79e63 --- /dev/null +++ b/dom/tests/mochitest/general/test_showModalDialog.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=862918 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 862918</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for window.showModalDialog. **/ + + // The modal window needs to touch Cu, which means that it needs + // SpecialPowers. But given the semantics of modal windows, we have to + // do some funny stuff with postMessage to set this up. + window.onmessage = function(evt) { + is(evt.data, 'dosetup', "message from modal window is correct"); + var win = SpecialPowers.wrap(evt.source); + win.wrappedJSObject.SpecialPowers = SpecialPowers; + SpecialPowers.setWrapped(win.wrappedJSObject, 'is', SpecialPowers.wrap(is)); + SpecialPowers.setWrapped(win.wrappedJSObject, 'ok', SpecialPowers.wrap(ok)); + win.wrappedJSObject.go(); + }; + + var someObj = { foo: 42, bar: "hi"}; + var xurl = location.toString() + .replace('mochi.test:8888', 'example.org') + .replace('test_showModal', 'file_showModal'); + if (xurl.indexOf('?') != -1) + xurl = xurl.substring(0, xurl.indexOf('?')); + is(showModalDialog('file_showModalDialog.html'), "rv: undefined"); + is(showModalDialog(xurl), undefined); + is(showModalDialog('file_showModalDialog.html', 3), "rv: 3"); + is(showModalDialog(xurl, 3), undefined); + is(showModalDialog('file_showModalDialog.html', someObj), "rv: " + someObj); + is(showModalDialog(xurl, someObj), undefined); + + // Test sequential navigations. + is(showModalDialog('file_showModalDialog.html?http://mochi.test:8888', 4), + 'rv: 4'); + is(showModalDialog('file_showModalDialog.html?http://example.com', 4), undefined); + is(showModalDialog('file_showModalDialog.html?http://example.com,http://mochi.test:8888', 4), 'rv: 4'); + + // This test used to assert after gc. Make sure it doesn't. + SpecialPowers.gc(); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=862918">Mozilla Bug 862918</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_showModalDialog_e10s.html b/dom/tests/mochitest/general/test_showModalDialog_e10s.html new file mode 100644 index 000000000..a3d9880c5 --- /dev/null +++ b/dom/tests/mochitest/general/test_showModalDialog_e10s.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1077002 +--> +<head> + <title>Test for showModalDialog unavailability in e10s</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1077002">Mozilla Bug 1077002</a> +<p id="display"></p> +<div id="content"> + <iframe id="frame" style="height:100px; width:100px; border:0"></iframe> + <div id="status" style="display: none"></div> +</div> +<pre id="test"> +<script type="application/javascript;version=1.7"> + +/** Test for showModalDialog unavailability in e10s **/ + +// NB: This test runs in e10s only. In e10s showModalDialog should not +// exist. +ok(!window.showModalDialog, "showModalDialog should not exist"); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_spacetopagedown.html b/dom/tests/mochitest/general/test_spacetopagedown.html new file mode 100644 index 000000000..03f840f3d --- /dev/null +++ b/dom/tests/mochitest/general/test_spacetopagedown.html @@ -0,0 +1,76 @@ +<html> +<head> + <meta charset="utf-8"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <script type="application/javascript;version=1.7"> + +SimpleTest.waitForExplicitFinish(); + +Components.utils.import("resource://gre/modules/Task.jsm"); + +var windowUtils = SpecialPowers.getDOMWindowUtils(window); + +function pressKey(isShift) +{ + return new Promise(resolve => { + synthesizeKey(" ", { shiftKey: isShift }); + windowUtils.advanceTimeAndRefresh(100); + SimpleTest.executeSoon(resolve); + }); +} + +function initTest() +{ + SpecialPowers.pushPrefEnv({"set":[["general.smoothScroll", false]]}, runTest); +} + +function runTest() +{ + Task.async(function () { + yield pressKey(false); + + ok(window.scrollY > 0, "Space with no focus" + window.scrollY); + yield pressKey(true); + is(window.scrollY, 0, "Shift+Space with no focus"); + + let checkbox = document.getElementById("checkbox"); + checkbox.focus(); + yield pressKey(false); + + is(window.scrollY, 0, "Space with checkbox focused"); + ok(checkbox.checked, "Space with checkbox focused, checked"); + yield pressKey(true); + is(window.scrollY, 0, "Shift+Space with checkbox focused"); + ok(!checkbox.checked, "Space with checkbox focused, unchecked"); + + let input = document.getElementById("input"); + input.focus(); + yield pressKey(false); + is(window.scrollY, 0, "Space with input focused"); + is(input.value, " ", "Space with input focused, value"); + yield pressKey(true); + is(window.scrollY, 0, "Shift+Space with input focused"); + is(input.value, " ", "Space with input focused, value"); + + windowUtils.restoreNormalRefresh(); + SimpleTest.finish(); + })(); +} + + </script> +</head> +<body onload="SimpleTest.waitForFocus(initTest)"> + +<input id="checkbox" type="checkbox">Checkbox +<input id="input"> +<p style="height: 4000px">Text</p> + +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_storagePermissionsAccept.html b/dom/tests/mochitest/general/test_storagePermissionsAccept.html new file mode 100644 index 000000000..10bd43594 --- /dev/null +++ b/dom/tests/mochitest/general/test_storagePermissionsAccept.html @@ -0,0 +1,42 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>Storage Permission Restrictions</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="storagePermissionsUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <iframe></iframe> + + <script type="text/javascript"> + +task(function* () { + yield setCookieBehavior(BEHAVIOR_ACCEPT); + + // We should be able to access storage + yield storageAllowed(); + + // Same origin iframes should be allowed, unless they redirect to a URI with the null principal + yield runIFrame("frameStorageAllowed.html"); + yield runIFrame("frameStorageNullprincipal.sjs"); + yield runIFrame("frameStorageChrome.html?allowed=yes"); + + // Sandboxed iframes should have the null principal, and thus can't access storage + document.querySelector('iframe').setAttribute('sandbox', 'allow-scripts'); + yield runIFrame("frameStoragePrevented.html#nullprincipal"); + yield runIFrame("frameStorageNullprincipal.sjs"); + document.querySelector('iframe').removeAttribute('sandbox'); + + // Thirdparty iframes should be allowed, unless they redirect to a URI with the null principal + yield runIFrame(thirdparty + "frameStorageAllowed.html"); + yield runIFrame(thirdparty + "frameStorageNullprincipal.sjs"); + yield runIFrame(thirdparty + "frameStorageChrome.html?allowed=yes"); + + // Workers should be able to access storage + yield runWorker("workerStorageAllowed.js"); +}); + + </script> + </body> +</html> diff --git a/dom/tests/mochitest/general/test_storagePermissionsLimitForeign.html b/dom/tests/mochitest/general/test_storagePermissionsLimitForeign.html new file mode 100644 index 000000000..36a752316 --- /dev/null +++ b/dom/tests/mochitest/general/test_storagePermissionsLimitForeign.html @@ -0,0 +1,44 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>Storage Permission Restrictions</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="storagePermissionsUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <iframe></iframe> + + <script type="text/javascript"> + +task(function* () { + yield setCookieBehavior(BEHAVIOR_LIMIT_FOREIGN); + + // We should be able to access storage + yield storageAllowed(); + + // Same origin iframes should be allowed. + yield runIFrame("frameStorageAllowed.html"); + yield runIFrame("frameStorageChrome.html?allowed=yes"); + + // Null principal iframes should not. + yield runIFrame("frameStorageNullprincipal.sjs"); + + // Sandboxed iframes should have the null principal, and thus can't access storage + document.querySelector('iframe').setAttribute('sandbox', 'allow-scripts'); + yield runIFrame("frameStoragePrevented.html#nullprincipal"); + yield runIFrame("frameStorageNullprincipal.sjs"); + document.querySelector('iframe').removeAttribute('sandbox'); + + // Thirdparty iframes should be blocked, even when accessed from chrome over Xrays. + yield runIFrame(thirdparty + "frameStoragePrevented.html#thirdparty"); + yield runIFrame(thirdparty + "frameStorageNullprincipal.sjs"); + yield runIFrame(thirdparty + "frameStorageChrome.html?allowed=no"); + + // Workers should be unable to access storage + yield runWorker("workerStorageAllowed.js"); +}); + + </script> + </body> +</html> diff --git a/dom/tests/mochitest/general/test_storagePermissionsReject.html b/dom/tests/mochitest/general/test_storagePermissionsReject.html new file mode 100644 index 000000000..2a93f4d07 --- /dev/null +++ b/dom/tests/mochitest/general/test_storagePermissionsReject.html @@ -0,0 +1,42 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>Storage Permission Restrictions</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="storagePermissionsUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <iframe></iframe> + + <script type="text/javascript"> + +task(function* () { + yield setCookieBehavior(BEHAVIOR_REJECT); + + // We should be unable to access storage + yield storagePrevented(); + + // Same origin iframes should be blocked. + yield runIFrame("frameStoragePrevented.html"); + yield runIFrame("frameStorageNullprincipal.sjs"); + yield runIFrame("frameStorageChrome.html?allowed=no&blockSessionStorage=yes"); + + // Sandboxed iframes should have the null principal, and thus can't access storage + document.querySelector('iframe').setAttribute('sandbox', 'allow-scripts'); + yield runIFrame("frameStoragePrevented.html#nullprincipal"); + yield runIFrame("frameStorageNullprincipal.sjs"); + document.querySelector('iframe').removeAttribute('sandbox'); + + // thirdparty iframes should be blocked. + yield runIFrame(thirdparty + "frameStoragePrevented.html"); + yield runIFrame(thirdparty + "frameStorageNullprincipal.sjs"); + yield runIFrame(thirdparty + "frameStorageChrome.html?allowed=no&blockSessionStorage=yes"); + + // Workers should be unable to access storage + yield runWorker("workerStoragePrevented.js"); +}); + + </script> + </body> +</html> diff --git a/dom/tests/mochitest/general/test_storagePermissionsRejectForeign.html b/dom/tests/mochitest/general/test_storagePermissionsRejectForeign.html new file mode 100644 index 000000000..5ff3fc63f --- /dev/null +++ b/dom/tests/mochitest/general/test_storagePermissionsRejectForeign.html @@ -0,0 +1,42 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>Storage Permission Restrictions</title> + + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="storagePermissionsUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <iframe></iframe> + + <script type="text/javascript"> + +task(function* () { + yield setCookieBehavior(BEHAVIOR_REJECT_FOREIGN); + + // We should be able to access storage + yield storageAllowed(); + + // Same origin iframes should be allowed, unless they redirect to a URI with the null principal + yield runIFrame("frameStorageAllowed.html"); + yield runIFrame("frameStorageNullprincipal.sjs"); + yield runIFrame("frameStorageChrome.html?allowed=yes"); + + // Sandboxed iframes should have the null principal, and thus can't access storage + document.querySelector('iframe').setAttribute('sandbox', 'allow-scripts'); + yield runIFrame("frameStoragePrevented.html#nullprincipal"); + yield runIFrame("frameStorageNullprincipal.sjs"); + document.querySelector('iframe').removeAttribute('sandbox'); + + // thirdparty iframes should be blocked. + yield runIFrame(thirdparty + "frameStoragePrevented.html#thirdparty"); + yield runIFrame(thirdparty + "frameStorageNullprincipal.sjs"); + yield runIFrame(thirdparty + "frameStorageChrome.html?allowed=no"); + + // Workers should be able to access storage + yield runWorker("workerStorageAllowed.js"); +}); + + </script> + </body> +</html> diff --git a/dom/tests/mochitest/general/test_stylesheetPI.html b/dom/tests/mochitest/general/test_stylesheetPI.html new file mode 100644 index 000000000..efafac250 --- /dev/null +++ b/dom/tests/mochitest/general/test_stylesheetPI.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=836809 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 836809</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript"> + + /** Test for Bug 836809 **/ + + var pi = document.createProcessingInstruction('xml-stylesheet', 'href="/tests/SimpleTest/test.css" type="text/css"'); + + function checkSheet(e) + { + ok("sheet" in pi, "XMLStyleSheetProcessingInstruction should have a sheet property"); + ok(pi.sheet, "XMLStyleSheetProcessingInstruction should have a sheet property"); + } + + pi.addEventListener("load", checkSheet); + document.insertBefore(pi, document.documentElement); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=836809">Mozilla Bug 836809</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/test_vibrator.html b/dom/tests/mochitest/general/test_vibrator.html new file mode 100644 index 000000000..2874c783b --- /dev/null +++ b/dom/tests/mochitest/general/test_vibrator.html @@ -0,0 +1,93 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for Vibrator</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<!-- Although we can't test that the vibrator works properly, we can test that + navigator.vibrate throws an exception where appropriate. --> + +<script class="testbody" type="text/javascript;version=1.7"> +SimpleTest.waitForExplicitFinish(); +var result; +function expectFailure(param) { + result = navigator.vibrate(param); + is(result, false, 'vibrate(' + param + ') should have failed.'); +} + +function expectSuccess(param) { + result = navigator.vibrate(param); + is(result, true, 'vibrate(' + param + ') must succeed.'); +} + +function tests(aEnabled) { + // Some edge cases that the bindings should handle for us. + expectSuccess(null); + expectSuccess(undefined); + // -1 will be converted to the highest unsigned long then clamped. + expectSuccess(-1); + expectSuccess('a'); + // -1 will be converted to the highest unsigned long then clamped. + expectSuccess([100, -1]); + expectSuccess([100, 'a']); + + var maxVibrateMs = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_ms'); + var maxVibrateListLen = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_list_len'); + + // If we pass a vibration pattern with a value higher than max_vibrate_ms or a + // pattern longer than max_vibrate_list_len, the call should succeed but the + // pattern should be modified to match the restrictions. + + // Values will be clamped to dom.vibrator.max_vibrate_ms. + expectSuccess(maxVibrateMs + 1); + expectSuccess([maxVibrateMs + 1]); + + var arr = []; + for (var i = 0; i < maxVibrateListLen + 1; i++) { + arr[i] = 0; + } + // The array will be truncated to have a length equal to dom.vibrator.max_vibrate_list_len. + expectSuccess(arr); + + + expectSuccess(0); + expectSuccess([]); + expectSuccess('1000'); + expectSuccess(1000); + expectSuccess(1000.1); + expectSuccess([0, 0, 0]); + expectSuccess(['1000', 1000]); + expectSuccess([1000, 1000]); + expectSuccess([1000, 1000.1]); + + // The following loop shouldn't cause us to crash. See bug 701716. + for (var i = 0; i < 10000; i++) { + navigator.vibrate([100, 100]); + } + ok(true, "Didn't crash after issuing a lot of vibrate() calls."); + if(!aEnabled) + SimpleTest.finish(); +} + +SpecialPowers.pushPermissions([ + {type: 'vibration', allow: true, context: document} + ], function() { + // Test with the vibrator pref enabled. + SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', true]]}, function() { + tests(true); + SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', false]]}, tests(false)); + }); + // Everything should be the same when the vibrator is disabled -- in + // particular, a disabled vibrator shouldn't eat failures we'd otherwise + // observe. + } +); + +</script> +</body> + +</html> diff --git a/dom/tests/mochitest/general/test_windowProperties.html b/dom/tests/mochitest/general/test_windowProperties.html new file mode 100644 index 000000000..3f8028393 --- /dev/null +++ b/dom/tests/mochitest/general/test_windowProperties.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test that all window properties are accessible to nonprivileged code</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> +</head> + +<body id="body"> + +<script type="application/javascript"> +var lastProp; +try { + var propVals = []; + for (var prop in window) { + lastProp = prop; + propVals.push(window[prop]); + } + ok(true, "Read all " + propVals.length + " window properties"); +} catch (ex) { + ok(false, "Exception occurred reading window." + lastProp); +} +</script> + +<p id="display"></p> + +</body> +</html> diff --git a/dom/tests/mochitest/general/test_windowedhistoryframes.html b/dom/tests/mochitest/general/test_windowedhistoryframes.html new file mode 100644 index 000000000..d27987beb --- /dev/null +++ b/dom/tests/mochitest/general/test_windowedhistoryframes.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602256 +--> +<head> + <title>Test for Bug 602256</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 602256 **/ + +SimpleTest.waitForExplicitFinish(); + +function done() { + subWin.close(); + SimpleTest.finish(); +} + +var subWin = window.open("historyframes.html", "_blank"); + +</script> +</pre> +</body> +</html> diff --git a/dom/tests/mochitest/general/workerStorageAllowed.js b/dom/tests/mochitest/general/workerStorageAllowed.js new file mode 100644 index 000000000..da76a7a0e --- /dev/null +++ b/dom/tests/mochitest/general/workerStorageAllowed.js @@ -0,0 +1,61 @@ +// Unfortunately, workers can't share the code from storagePermissionsUtils. +// These are basic mechanisms for communicating to the test runner. + +function ok(condition, text) { + if (!condition) { + self.postMessage("FAILURE: " + text); + } else { + self.postMessage(text); + } +} + +function finishTest() { + self.postMessage("done"); + self.close(); +} + +// Workers don't have access to localstorage or sessionstorage +ok(typeof self.localStorage == "undefined", "localStorage should be undefined"); +ok(typeof self.sessionStorage == "undefined", "sessionStorage should be undefined"); + +// Make sure that we can access indexedDB +try { + indexedDB; + ok(true, "WORKER getting indexedDB didn't throw"); +} catch (e) { + ok(false, "WORKER getting indexedDB should not throw"); +} + +// Make sure that we can access caches +try { + var promise = caches.keys(); + ok(true, "WORKER getting caches didn't throw"); + + promise.then(function() { + ok(location.protocol == "https:", "WORKER The promise was not rejected"); + workerTest(); + }, function() { + ok(location.protocol != "https:", "WORKER The promise should not have been rejected"); + workerTest(); + }); +} catch (e) { + ok(false, "WORKER getting caches should not have thrown"); +} + +// Try to spawn an inner worker, and make sure that it can also access storage +function workerTest() { + if (location.hash == "#inner") { // Don't recurse infinitely, if we are the inner worker, don't spawn another + finishTest(); + return; + } + // Create the inner worker, and listen for test messages from it + var worker = new Worker("workerStorageAllowed.js#inner"); + worker.addEventListener('message', function(e) { + if (e.data == "done") { + finishTest(); + return; + } + + ok(!e.data.match(/^FAILURE/), e.data + " (WORKER = workerStorageAllowed.js#inner)"); + }); +} diff --git a/dom/tests/mochitest/general/workerStoragePrevented.js b/dom/tests/mochitest/general/workerStoragePrevented.js new file mode 100644 index 000000000..93a81eb75 --- /dev/null +++ b/dom/tests/mochitest/general/workerStoragePrevented.js @@ -0,0 +1,61 @@ +// Unfortunately, workers can't share the code from storagePermissionsUtils. +// These are basic mechanisms for communicating to the test runner. + +function ok(condition, text) { + if (!condition) { + self.postMessage("FAILURE: " + text); + } else { + self.postMessage(text); + } +} + +function finishTest() { + self.postMessage("done"); + self.close(); +} + +// Workers don't have access to localstorage or sessionstorage +ok(typeof self.localStorage == "undefined", "localStorage should be undefined"); +ok(typeof self.sessionStorage == "undefined", "sessionStorage should be undefined"); + +// Make sure that we can't access indexedDB +try { + indexedDB; + ok(false, "WORKER getting indexedDB should have thrown"); +} catch (e) { + ok(true, "WORKER getting indexedDB threw"); +} + +// Make sure that we can't access caches +try { + var promise = caches.keys(); + ok(true, "WORKER getting caches didn't throw"); + + promise.then(function() { + ok(false, "WORKER The promise should have rejected"); + workerTest(); + }, function() { + ok(true, "WORKER The promise was rejected"); + workerTest(); + }); +} catch (e) { + ok(false, "WORKER getting caches should not have thrown"); +} + +// Try to spawn an inner worker, and make sure that it also can't access storage +function workerTest() { + if (location.hash == "#inner") { // Don't recurse infinitely, if we are the inner worker, don't spawn another + finishTest(); + return; + } + // Create the inner worker, and listen for test messages from it + var worker = new Worker("workerStoragePrevented.js#inner"); + worker.addEventListener('message', function(e) { + if (e.data == "done") { + finishTest(); + return; + } + + ok(!e.data.match(/^FAILURE/), e.data + " (WORKER = workerStoragePrevented.js#inner)"); + }); +} |