summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/general
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/tests/mochitest/general
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-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')
-rw-r--r--dom/tests/mochitest/general/497633.html24
-rw-r--r--dom/tests/mochitest/general/chrome.ini9
-rw-r--r--dom/tests/mochitest/general/fail.pngbin0 -> 91 bytes
-rw-r--r--dom/tests/mochitest/general/file_bug628069.html16
-rw-r--r--dom/tests/mochitest/general/file_clonewrapper.html36
-rw-r--r--dom/tests/mochitest/general/file_domWindowUtils_scrollbarSize.html7
-rw-r--r--dom/tests/mochitest/general/file_frameElementWrapping.html32
-rw-r--r--dom/tests/mochitest/general/file_interfaces.xml20
-rw-r--r--dom/tests/mochitest/general/file_moving_nodeList.html31
-rw-r--r--dom/tests/mochitest/general/file_moving_xhr.html28
-rw-r--r--dom/tests/mochitest/general/file_showModalDialog.html35
-rw-r--r--dom/tests/mochitest/general/frameSelectEvents.html467
-rw-r--r--dom/tests/mochitest/general/frameStorageAllowed.html22
-rw-r--r--dom/tests/mochitest/general/frameStorageChrome.html20
-rw-r--r--dom/tests/mochitest/general/frameStorageNullprincipal.sjs33
-rw-r--r--dom/tests/mochitest/general/frameStoragePrevented.html47
-rw-r--r--dom/tests/mochitest/general/historyframes.html156
-rw-r--r--dom/tests/mochitest/general/image_100.pngbin0 -> 91 bytes
-rw-r--r--dom/tests/mochitest/general/image_200.pngbin0 -> 100 bytes
-rw-r--r--dom/tests/mochitest/general/image_50.pngbin0 -> 85 bytes
-rw-r--r--dom/tests/mochitest/general/mochitest.ini128
-rw-r--r--dom/tests/mochitest/general/navigation_timing.html100
-rw-r--r--dom/tests/mochitest/general/pass.apngbin0 -> 188 bytes
-rw-r--r--dom/tests/mochitest/general/performance_timeline_main_test.html101
-rw-r--r--dom/tests/mochitest/general/res0.resource0
-rw-r--r--dom/tests/mochitest/general/res1.resource0
-rw-r--r--dom/tests/mochitest/general/res1.resource^headers^2
-rw-r--r--dom/tests/mochitest/general/res2.resource0
-rw-r--r--dom/tests/mochitest/general/res2.resource^headers^2
-rw-r--r--dom/tests/mochitest/general/res3.resource0
-rw-r--r--dom/tests/mochitest/general/res3.resource^headers^2
-rw-r--r--dom/tests/mochitest/general/res4.resource0
-rw-r--r--dom/tests/mochitest/general/res4.resource^headers^3
-rw-r--r--dom/tests/mochitest/general/res5.resource0
-rw-r--r--dom/tests/mochitest/general/res5.resource^headers^2
-rw-r--r--dom/tests/mochitest/general/res6.resource0
-rw-r--r--dom/tests/mochitest/general/res6.resource^headers^2
-rw-r--r--dom/tests/mochitest/general/res7.resource0
-rw-r--r--dom/tests/mochitest/general/res7.resource^headers^2
-rw-r--r--dom/tests/mochitest/general/res8.resource0
-rw-r--r--dom/tests/mochitest/general/res8.resource^headers^2
-rw-r--r--dom/tests/mochitest/general/resource_timing.js0
-rw-r--r--dom/tests/mochitest/general/resource_timing_cross_origin.html187
-rw-r--r--dom/tests/mochitest/general/resource_timing_iframe.html49
-rw-r--r--dom/tests/mochitest/general/resource_timing_main_test.html288
-rw-r--r--dom/tests/mochitest/general/storagePermissionsUtils.js237
-rw-r--r--dom/tests/mochitest/general/test-data.json1
-rw-r--r--dom/tests/mochitest/general/test-data2.json1
-rw-r--r--dom/tests/mochitest/general/test_497898.html29
-rw-r--r--dom/tests/mochitest/general/test_DOMMatrix.html728
-rw-r--r--dom/tests/mochitest/general/test_WebKitCSSMatrix.html336
-rw-r--r--dom/tests/mochitest/general/test_bug1012662_common.js341
-rw-r--r--dom/tests/mochitest/general/test_bug1012662_editor.html29
-rw-r--r--dom/tests/mochitest/general/test_bug1012662_noeditor.html28
-rw-r--r--dom/tests/mochitest/general/test_bug1161721.html32
-rw-r--r--dom/tests/mochitest/general/test_bug1170911.html90
-rw-r--r--dom/tests/mochitest/general/test_bug1208217.html32
-rw-r--r--dom/tests/mochitest/general/test_bug1313753.html61
-rw-r--r--dom/tests/mochitest/general/test_bug504220.html66
-rw-r--r--dom/tests/mochitest/general/test_bug628069_1.html50
-rw-r--r--dom/tests/mochitest/general/test_bug628069_2.html41
-rw-r--r--dom/tests/mochitest/general/test_bug631440.html39
-rw-r--r--dom/tests/mochitest/general/test_bug653364.html39
-rw-r--r--dom/tests/mochitest/general/test_bug861217.html115
-rw-r--r--dom/tests/mochitest/general/test_clientRects.html130
-rw-r--r--dom/tests/mochitest/general/test_clipboard_disallowed.html61
-rw-r--r--dom/tests/mochitest/general/test_clipboard_events.html725
-rw-r--r--dom/tests/mochitest/general/test_consoleAPI.html67
-rw-r--r--dom/tests/mochitest/general/test_contentViewer_overrideDPPX.html358
-rw-r--r--dom/tests/mochitest/general/test_domWindowUtils.html197
-rw-r--r--dom/tests/mochitest/general/test_domWindowUtils_scrollXY.html90
-rw-r--r--dom/tests/mochitest/general/test_domWindowUtils_scrollbarSize.html65
-rw-r--r--dom/tests/mochitest/general/test_donottrack.html72
-rw-r--r--dom/tests/mochitest/general/test_focus_legend_noparent.html36
-rw-r--r--dom/tests/mochitest/general/test_focusrings.xul175
-rw-r--r--dom/tests/mochitest/general/test_for_of.html25
-rw-r--r--dom/tests/mochitest/general/test_frameElementWrapping.html45
-rw-r--r--dom/tests/mochitest/general/test_framedhistoryframes.html31
-rw-r--r--dom/tests/mochitest/general/test_idleapi_permissions.html48
-rw-r--r--dom/tests/mochitest/general/test_img_mutations.html236
-rw-r--r--dom/tests/mochitest/general/test_innerScreen.xul89
-rw-r--r--dom/tests/mochitest/general/test_interfaces.html1383
-rw-r--r--dom/tests/mochitest/general/test_navigation_timing.html36
-rw-r--r--dom/tests/mochitest/general/test_network_events.html72
-rw-r--r--dom/tests/mochitest/general/test_offsets.css3
-rw-r--r--dom/tests/mochitest/general/test_offsets.html100
-rw-r--r--dom/tests/mochitest/general/test_offsets.js222
-rw-r--r--dom/tests/mochitest/general/test_offsets.xul99
-rw-r--r--dom/tests/mochitest/general/test_outerHTML.html74
-rw-r--r--dom/tests/mochitest/general/test_outerHTML.xhtml75
-rw-r--r--dom/tests/mochitest/general/test_paste_selection.html122
-rw-r--r--dom/tests/mochitest/general/test_performance_now.html66
-rw-r--r--dom/tests/mochitest/general/test_performance_timeline.html36
-rw-r--r--dom/tests/mochitest/general/test_picture_apng.html77
-rw-r--r--dom/tests/mochitest/general/test_picture_mutations.html311
-rw-r--r--dom/tests/mochitest/general/test_pointerPreserves3D.html25
-rw-r--r--dom/tests/mochitest/general/test_pointerPreserves3DClip.html55
-rw-r--r--dom/tests/mochitest/general/test_resource_timing.html38
-rw-r--r--dom/tests/mochitest/general/test_resource_timing_cross_origin.html45
-rw-r--r--dom/tests/mochitest/general/test_resource_timing_frameset.html29
-rw-r--r--dom/tests/mochitest/general/test_selectevents.html32
-rw-r--r--dom/tests/mochitest/general/test_showModalDialog.html60
-rw-r--r--dom/tests/mochitest/general/test_showModalDialog_e10s.html32
-rw-r--r--dom/tests/mochitest/general/test_spacetopagedown.html76
-rw-r--r--dom/tests/mochitest/general/test_storagePermissionsAccept.html42
-rw-r--r--dom/tests/mochitest/general/test_storagePermissionsLimitForeign.html44
-rw-r--r--dom/tests/mochitest/general/test_storagePermissionsReject.html42
-rw-r--r--dom/tests/mochitest/general/test_storagePermissionsRejectForeign.html42
-rw-r--r--dom/tests/mochitest/general/test_stylesheetPI.html37
-rw-r--r--dom/tests/mochitest/general/test_vibrator.html93
-rw-r--r--dom/tests/mochitest/general/test_windowProperties.html28
-rw-r--r--dom/tests/mochitest/general/test_windowedhistoryframes.html32
-rw-r--r--dom/tests/mochitest/general/workerStorageAllowed.js61
-rw-r--r--dom/tests/mochitest/general/workerStoragePrevented.js61
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
new file mode 100644
index 000000000..db812bd7d
--- /dev/null
+++ b/dom/tests/mochitest/general/fail.png
Binary files differ
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
new file mode 100644
index 000000000..df421453c
--- /dev/null
+++ b/dom/tests/mochitest/general/image_100.png
Binary files differ
diff --git a/dom/tests/mochitest/general/image_200.png b/dom/tests/mochitest/general/image_200.png
new file mode 100644
index 000000000..6f76d4438
--- /dev/null
+++ b/dom/tests/mochitest/general/image_200.png
Binary files differ
diff --git a/dom/tests/mochitest/general/image_50.png b/dom/tests/mochitest/general/image_50.png
new file mode 100644
index 000000000..144a2f0b9
--- /dev/null
+++ b/dom/tests/mochitest/general/image_50.png
Binary files differ
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
new file mode 100644
index 000000000..6e78a9eef
--- /dev/null
+++ b/dom/tests/mochitest/general/pass.apng
Binary files differ
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,&lt;html&gt;&lt;style&gt;* { outline: none; -moz-appearance: none; } %23elem:focus { outline: 2px solid red; } %23elem:-moz-focusring { outline: 1px solid blue; }&lt;/style&gt;&lt;div id='container'&gt;&lt;/html&gt;"/>
+
+<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)");
+ });
+}