diff options
Diffstat (limited to 'devtools/server/tests/mochitest')
99 files changed, 9931 insertions, 0 deletions
diff --git a/devtools/server/tests/mochitest/.eslintrc.js b/devtools/server/tests/mochitest/.eslintrc.js new file mode 100644 index 000000000..c5b919ce3 --- /dev/null +++ b/devtools/server/tests/mochitest/.eslintrc.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = { + // Extend from the common devtools mochitest eslintrc config. + "extends": "../../../.eslintrc.xpcshell.js" +}; diff --git a/devtools/server/tests/mochitest/Debugger.Source.prototype.element-2.js b/devtools/server/tests/mochitest/Debugger.Source.prototype.element-2.js new file mode 100644 index 000000000..eab746921 --- /dev/null +++ b/devtools/server/tests/mochitest/Debugger.Source.prototype.element-2.js @@ -0,0 +1 @@ +debugger; diff --git a/devtools/server/tests/mochitest/Debugger.Source.prototype.element.html b/devtools/server/tests/mochitest/Debugger.Source.prototype.element.html new file mode 100644 index 000000000..fcf4c6c85 --- /dev/null +++ b/devtools/server/tests/mochitest/Debugger.Source.prototype.element.html @@ -0,0 +1,17 @@ +<head> + <!-- Static (not dynamically inserted) inline script. --> + <script id='franz'>function franz() { debugger; }</script> + + <!-- Static out-of-line script element. --> + <script id='heinrich' src='Debugger.Source.prototype.element.js'></script> +</head> + +<!-- HTML requires some body element onfoo attributes to add handlers to the + *window*, not the element --- but Debugger.Source.prototype.element should + return the element. Here, that rule should apply to the body's 'onresize' + handler. (For the reason for the 'cancelable' check, see the code that + sends the event.) --> +<body onresize='if (event.cancelable) debugger;'> + <!-- Ordinary content element with event handler. --> + <div id='heidi' onclick='heinrichFun();'>Heidi</div> +</body> diff --git a/devtools/server/tests/mochitest/Debugger.Source.prototype.element.js b/devtools/server/tests/mochitest/Debugger.Source.prototype.element.js new file mode 100644 index 000000000..44115b373 --- /dev/null +++ b/devtools/server/tests/mochitest/Debugger.Source.prototype.element.js @@ -0,0 +1 @@ +function heinrichFun() { franz(); } diff --git a/devtools/server/tests/mochitest/animation-data.html b/devtools/server/tests/mochitest/animation-data.html new file mode 100644 index 000000000..01be59548 --- /dev/null +++ b/devtools/server/tests/mochitest/animation-data.html @@ -0,0 +1,120 @@ +<html> +<head> + <meta charset="UTF-8"> + <title>Animation Test Data</title> + <style> + .ball { + width: 80px; + height: 80px; + border-radius: 50%; + background: #f06; + + position: absolute; + } + + .still { + top: 0; + left: 10px; + } + + .animated { + top: 100px; + left: 10px; + + animation: simple-animation 2s infinite alternate; + } + + .multi { + top: 200px; + left: 10px; + + animation: simple-animation 2s infinite alternate, + other-animation 5s infinite alternate; + } + + .delayed { + top: 300px; + left: 10px; + background: rebeccapurple; + + animation: simple-animation 3s 60s 10; + } + + .multi-finite { + top: 400px; + left: 10px; + background: yellow; + + animation: simple-animation 3s, + other-animation 4s; + } + + .short { + top: 500px; + left: 10px; + background: red; + + animation: simple-animation 2s; + } + + .long { + top: 600px; + left: 10px; + background: blue; + + animation: simple-animation 120s; + } + + .negative-delay { + top: 700px; + left: 10px; + background: gray; + + animation: simple-animation 15s -10s; + animation-fill-mode: forwards; + } + + .no-compositor { + top: 0; + right: 10px; + background: gold; + + animation: no-compositor 10s cubic-bezier(.57,-0.02,1,.31) forwards; + } + + @keyframes simple-animation { + 100% { + transform: translateX(300px); + } + } + + @keyframes other-animation { + 100% { + background: blue; + } + } + + @keyframes no-compositor { + 100% { + margin-right: 600px; + } + } + </style> + <script type="text/javascript"> + window.onload = function() { + window.opener.postMessage('ready', '*'); + }; + </script> +</head> +</body> + <div class="ball still"></div> + <div class="ball animated"></div> + <div class="ball multi"></div> + <div class="ball delayed"></div> + <div class="ball multi-finite"></div> + <div class="ball short"></div> + <div class="ball long"></div> + <div class="ball negative-delay"></div> + <div class="ball no-compositor"></div> +</body> +</html> diff --git a/devtools/server/tests/mochitest/chrome.ini b/devtools/server/tests/mochitest/chrome.ini new file mode 100644 index 000000000..ae69d163e --- /dev/null +++ b/devtools/server/tests/mochitest/chrome.ini @@ -0,0 +1,103 @@ +[DEFAULT] +tags = devtools +skip-if = os == 'android' +support-files = + animation-data.html + Debugger.Source.prototype.element.js + Debugger.Source.prototype.element-2.js + Debugger.Source.prototype.element.html + director-helpers.js + hello-actor.js + inspector_css-properties.html + inspector_getImageData.html + inspector-delay-image-response.sjs + inspector-eyedropper.html + inspector-helpers.js + inspector-search-data.html + inspector-styles-data.css + inspector-styles-data.html + inspector-traversal-data.html + large-image.jpg + memory-helpers.js + nonchrome_unsafeDereference.html + small-image.gif + setup-in-child.js + setup-in-parent.js + +[test_animation_actor-lifetime.html] +[test_connection-manager.html] +[test_connectToChild.html] +[test_css-logic.html] +[test_css-logic-media-queries.html] +[test_css-logic-specificity.html] +[test_css-properties_01.html] +[test_css-properties_02.html] +[test_Debugger.Source.prototype.introductionScript.html] +[test_Debugger.Source.prototype.introductionType.html] +[test_Debugger.Source.prototype.element.html] +[test_Debugger.Script.prototype.global.html] +[test_device.html] +[test_director.html] +[test_director_connectToChild.html] +[test_executeInGlobal-outerized_this.html] +[test_framerate_01.html] +[test_framerate_02.html] +[test_framerate_03.html] +[test_framerate_04.html] +[test_framerate_05.html] +[test_framerate_06.html] +[test_getProcess.html] +[test_inspector-anonymous.html] +[test_inspector-changeattrs.html] +[test_inspector-changevalue.html] +[test_inspector-dead-nodes.html] +[test_inspector-duplicate-node.html] +[test_inspector_getImageData.html] +[test_inspector_getImageDataFromURL.html] +[test_inspector_getImageData-wait-for-load.html] +[test_inspector_getNodeFromActor.html] +[test_inspector-hide.html] +[test_inspector-insert.html] +[test_inspector-mutations-attr.html] +[test_inspector-mutations-events.html] +[test_inspector-mutations-childlist.html] +[test_inspector-mutations-frameload.html] +[test_inspector-mutations-value.html] +[test_inspector-pick-color.html] +[test_inspector-pseudoclass-lock.html] +[test_inspector-release.html] +[test_inspector-reload.html] +[test_inspector-remove.html] +[test_inspector-resize.html] +[test_inspector-resolve-url.html] +[test_inspector-retain.html] +[test_inspector-search.html] +[test_inspector-search-front.html] +[test_inspector-scroll-into-view.html] +[test_inspector-traversal.html] +[test_makeGlobalObjectReference.html] +[test_memory.html] +[test_memory_allocations_01.html] +[test_memory_allocations_02.html] +[test_memory_allocations_03.html] +[test_memory_allocations_04.html] +[test_memory_allocations_05.html] +[test_memory_allocations_06.html] +[test_memory_allocations_07.html] +[test_memory_attach_01.html] +[test_memory_attach_02.html] +[test_memory_census.html] +[test_memory_gc_01.html] +[test_memory_gc_events.html] +[test_preference.html] +[test_settings.html] +[test_setupInParentChild.html] +[test_styles-applied.html] +[test_styles-computed.html] +[test_styles-layout.html] +[test_styles-matched.html] +[test_styles-modify.html] +[test_styles-svg.html] +[test_unsafeDereference.html] +[test_websocket-server.html] +skip-if = false diff --git a/devtools/server/tests/mochitest/director-helpers.js b/devtools/server/tests/mochitest/director-helpers.js new file mode 100644 index 000000000..fe1f7d394 --- /dev/null +++ b/devtools/server/tests/mochitest/director-helpers.js @@ -0,0 +1,44 @@ +var Cu = Components.utils; +const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); +const {DebuggerClient} = require("devtools/shared/client/main"); +const {DebuggerServer} = require("devtools/server/main"); +const Services = require("Services"); + +// Always log packets when running tests. +Services.prefs.setBoolPref("devtools.debugger.log", true); +Services.prefs.setBoolPref("dom.mozBrowserFramesEnabled", true); + +SimpleTest.registerCleanupFunction(function () { + Services.prefs.clearUserPref("devtools.debugger.log"); + Services.prefs.clearUserPref("dom.mozBrowserFramesEnabled"); +}); + +const { DirectorRegistry } = require("devtools/server/actors/director-registry"); +const { DirectorRegistryFront } = require("devtools/shared/fronts/director-registry"); + +const { DirectorManagerFront } = require("devtools/shared/fronts/director-manager"); + +const { Task } = require("devtools/shared/task"); + +/** ********************************* + * director helpers functions + **********************************/ + +function* newConnectedDebuggerClient(opts) { + var transport = DebuggerServer.connectPipe(); + var client = new DebuggerClient(transport); + + yield client.connect(); + + var root = yield client.listTabs(); + + return { + client: client, + root: root, + transport: transport + }; +} + +function purgeInstalledDirectorScripts() { + DirectorRegistry.clear(); +} diff --git a/devtools/server/tests/mochitest/hello-actor.js b/devtools/server/tests/mochitest/hello-actor.js new file mode 100644 index 000000000..603192938 --- /dev/null +++ b/devtools/server/tests/mochitest/hello-actor.js @@ -0,0 +1,26 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const protocol = require("devtools/shared/protocol"); + +const helloSpec = protocol.generateActorSpec({ + typeName: "helloActor", + + methods: { + count: { + request: {}, + response: {count: protocol.RetVal("number")} + } + } +}); + +var HelloActor = protocol.ActorClassWithSpec(helloSpec, { + initialize: function () { + protocol.Actor.prototype.initialize.apply(this, arguments); + this.counter = 0; + }, + + count: function () { + return ++this.counter; + } +}); diff --git a/devtools/server/tests/mochitest/inspector-delay-image-response.sjs b/devtools/server/tests/mochitest/inspector-delay-image-response.sjs new file mode 100644 index 000000000..215d1e4d8 --- /dev/null +++ b/devtools/server/tests/mochitest/inspector-delay-image-response.sjs @@ -0,0 +1,42 @@ +/** + * Adapted from https://dxr.mozilla.org/mozilla-central/rev/ + * 4e883591bb5dff021c108d3e30198a99547eed1e/layout/reftests/backgrounds/ + * delay-image-response.sjs + */ +"use strict"; + +// A 1x1 PNG image. +// Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain) +const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); + +// To avoid GC. +let timer = null; + +function handleRequest(request, response) { + let query = {}; + request.queryString.split("&").forEach(function(val) { + let [name, value] = val.split("="); + query[name] = unescape(value); + }); + + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "image/png", false); + + // If there is no delay, we write the image and leave. + if (!("delay" in query)) { + response.write(IMAGE); + return; + } + + // If there is a delay, we create a timer which, when it fires, will write + // image and leave. + response.processAsync(); + const nsITimer = Components.interfaces.nsITimer; + + timer = Components.classes["@mozilla.org/timer;1"].createInstance(nsITimer); + timer.initWithCallback(function() { + response.write(IMAGE); + response.finish(); + }, query.delay, nsITimer.TYPE_ONE_SHOT); +} diff --git a/devtools/server/tests/mochitest/inspector-eyedropper.html b/devtools/server/tests/mochitest/inspector-eyedropper.html new file mode 100644 index 000000000..ded9f392d --- /dev/null +++ b/devtools/server/tests/mochitest/inspector-eyedropper.html @@ -0,0 +1,18 @@ +<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Inspector Eyedropper tests</title>
+ <style>
+ html {
+ background: black;
+ }
+ </style>
+ <script type="text/javascript">
+ window.onload = function() {
+ window.opener.postMessage('ready', '*');
+ };
+ </script>
+</head>
+</body>
+</body>
+</html>
\ No newline at end of file diff --git a/devtools/server/tests/mochitest/inspector-helpers.js b/devtools/server/tests/mochitest/inspector-helpers.js new file mode 100644 index 000000000..47c643868 --- /dev/null +++ b/devtools/server/tests/mochitest/inspector-helpers.js @@ -0,0 +1,310 @@ +var Cu = Components.utils; + +const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); +const {DebuggerClient} = require("devtools/shared/client/main"); +const {DebuggerServer} = require("devtools/server/main"); +const { Task } = require("devtools/shared/task"); + +const Services = require("Services"); +const promise = require("promise"); +const {_documentWalker} = require("devtools/server/actors/inspector"); + +// Always log packets when running tests. +Services.prefs.setBoolPref("devtools.debugger.log", true); +SimpleTest.registerCleanupFunction(function () { + Services.prefs.clearUserPref("devtools.debugger.log"); +}); + + +if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + SimpleTest.registerCleanupFunction(function () { + DebuggerServer.destroy(); + }); +} + +var gAttachCleanups = []; + +SimpleTest.registerCleanupFunction(function () { + for (let cleanup of gAttachCleanups) { + cleanup(); + } +}); + +/** + * Open a tab, load the url, wait for it to signal its readiness, + * find the tab with the debugger server, and call the callback. + * + * Returns a function which can be called to close the opened ta + * and disconnect its debugger client. + */ +function attachURL(url, callback) { + var win = window.open(url, "_blank"); + var client = null; + + let cleanup = () => { + if (client) { + client.close(); + client = null; + } + if (win) { + win.close(); + win = null; + } + }; + gAttachCleanups.push(cleanup); + + window.addEventListener("message", function loadListener(event) { + if (event.data === "ready") { + client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect().then(([applicationType, traits]) => { + client.listTabs(response => { + for (let tab of response.tabs) { + if (tab.url === url) { + window.removeEventListener("message", loadListener, false); + client.attachTab(tab.actor, function (aResponse, aTabClient) { + try { + callback(null, client, tab, win.document); + } catch (ex) { + Cu.reportError(ex); + dump(ex); + } + }); + break; + } + } + }); + }); + } + }, false); + + return cleanup; +} + +function promiseOnce(target, event) { + let deferred = promise.defer(); + target.on(event, (...args) => { + if (args.length === 1) { + deferred.resolve(args[0]); + } else { + deferred.resolve(args); + } + }); + return deferred.promise; +} + +function sortOwnershipChildren(children) { + return children.sort((a, b) => a.name.localeCompare(b.name)); +} + +function serverOwnershipSubtree(walker, node) { + let actor = walker._refMap.get(node); + if (!actor) { + return undefined; + } + + let children = []; + let docwalker = new _documentWalker(node, window); + let child = docwalker.firstChild(); + while (child) { + let item = serverOwnershipSubtree(walker, child); + if (item) { + children.push(item); + } + child = docwalker.nextSibling(); + } + return { + name: actor.actorID, + children: sortOwnershipChildren(children) + }; +} + +function serverOwnershipTree(walker) { + let serverWalker = DebuggerServer._searchAllConnectionsForActor(walker.actorID); + + return { + root: serverOwnershipSubtree(serverWalker, serverWalker.rootDoc), + orphaned: [...serverWalker._orphaned].map(o => serverOwnershipSubtree(serverWalker, o.rawNode)), + retained: [...serverWalker._retainedOrphans].map(o => serverOwnershipSubtree(serverWalker, o.rawNode)) + }; +} + +function clientOwnershipSubtree(node) { + return { + name: node.actorID, + children: sortOwnershipChildren(node.treeChildren().map(child => clientOwnershipSubtree(child))) + }; +} + +function clientOwnershipTree(walker) { + return { + root: clientOwnershipSubtree(walker.rootNode), + orphaned: [...walker._orphaned].map(o => clientOwnershipSubtree(o)), + retained: [...walker._retainedOrphans].map(o => clientOwnershipSubtree(o)) + }; +} + +function ownershipTreeSize(tree) { + let size = 1; + for (let child of tree.children) { + size += ownershipTreeSize(child); + } + return size; +} + +function assertOwnershipTrees(walker) { + let serverTree = serverOwnershipTree(walker); + let clientTree = clientOwnershipTree(walker); + is(JSON.stringify(clientTree, null, " "), JSON.stringify(serverTree, null, " "), "Server and client ownership trees should match."); + + return ownershipTreeSize(clientTree.root); +} + +// Verify that an actorID is inaccessible both from the client library and the server. +function checkMissing(client, actorID) { + let deferred = promise.defer(); + let front = client.getActor(actorID); + ok(!front, "Front shouldn't be accessible from the client for actorID: " + actorID); + + deferred = promise.defer(); + client.request({ + to: actorID, + type: "request", + }, response => { + is(response.error, "noSuchActor", "node list actor should no longer be contactable."); + deferred.resolve(undefined); + }); + return deferred.promise; +} + +// Verify that an actorID is accessible both from the client library and the server. +function checkAvailable(client, actorID) { + let deferred = promise.defer(); + let front = client.getActor(actorID); + ok(front, "Front should be accessible from the client for actorID: " + actorID); + + deferred = promise.defer(); + client.request({ + to: actorID, + type: "garbageAvailableTest", + }, response => { + is(response.error, "unrecognizedPacketType", "node list actor should be contactable."); + deferred.resolve(undefined); + }); + return deferred.promise; +} + +function promiseDone(promise) { + promise.then(null, err => { + ok(false, "Promise failed: " + err); + if (err.stack) { + dump(err.stack); + } + SimpleTest.finish(); + }); +} + +// Mutation list testing + +function isSrcChange(change) { + return (change.type === "attributes" && change.attributeName === "src"); +} + +function assertAndStrip(mutations, message, test) { + let size = mutations.length; + mutations = mutations.filter(test); + ok((mutations.size != size), message); + return mutations; +} + +function isSrcChange(change) { + return change.type === "attributes" && change.attributeName === "src"; +} + +function isUnload(change) { + return change.type === "documentUnload"; +} + +function isFrameLoad(change) { + return change.type === "frameLoad"; +} + +function isUnretained(change) { + return change.type === "unretained"; +} + +function isChildList(change) { + return change.type === "childList"; +} + +function isNewRoot(change) { + return change.type === "newRoot"; +} + +// Make sure an iframe's src attribute changed and then +// strip that mutation out of the list. +function assertSrcChange(mutations) { + return assertAndStrip(mutations, "Should have had an iframe source change.", isSrcChange); +} + +// Make sure there's an unload in the mutation list and strip +// that mutation out of the list +function assertUnload(mutations) { + return assertAndStrip(mutations, "Should have had a document unload change.", isUnload); +} + +// Make sure there's a frame load in the mutation list and strip +// that mutation out of the list +function assertFrameLoad(mutations) { + return assertAndStrip(mutations, "Should have had a frame load change.", isFrameLoad); +} + +// Make sure there's a childList change in the mutation list and strip +// that mutation out of the list +function assertChildList(mutations) { + return assertAndStrip(mutations, "Should have had a frame load change.", isChildList); +} + +// Load mutations aren't predictable, so keep accumulating mutations until +// the one we're looking for shows up. +function waitForMutation(walker, test, mutations = []) { + let deferred = promise.defer(); + for (let change of mutations) { + if (test(change)) { + deferred.resolve(mutations); + } + } + + walker.once("mutations", newMutations => { + waitForMutation(walker, test, mutations.concat(newMutations)).then(finalMutations => { + deferred.resolve(finalMutations); + }); + }); + + return deferred.promise; +} + + +var _tests = []; +function addTest(test) { + _tests.push(test); +} + +function addAsyncTest(generator) { + _tests.push(() => Task.spawn(generator).then(null, ok.bind(null, false))); +} + +function runNextTest() { + if (_tests.length == 0) { + SimpleTest.finish(); + return; + } + var fn = _tests.shift(); + try { + fn(); + } catch (ex) { + info("Test function " + (fn.name ? "'" + fn.name + "' " : "") + + "threw an exception: " + ex); + } +} diff --git a/devtools/server/tests/mochitest/inspector-search-data.html b/devtools/server/tests/mochitest/inspector-search-data.html new file mode 100644 index 000000000..d0d5f9c68 --- /dev/null +++ b/devtools/server/tests/mochitest/inspector-search-data.html @@ -0,0 +1,52 @@ +<html> +<head> + <meta charset="UTF-8"> + <title>Inspector Search Test Data</title> + <style> + #pseudo { + display: block; + margin: 0; + } + #pseudo:before { + content: "before element"; + } + #pseudo:after { + content: "after element"; + } + </style> + <script type="text/javascript"> + window.onload = function() { + window.opener.postMessage('ready', '*'); + }; + </script> +</head> +</body> + <!-- A comment + spread across multiple lines --> + + <img width="100" height="100" src="large-image.jpg" /> + + <h1 id="pseudo">Heading 1</h1> + <p>A p tag with the text 'h1' inside of it. + <strong>A strong h1 result</strong> + </p> + + <div id="arrows" northwest="↖" northeast="↗" southeast="↘" southwest="↙"> + Unicode arrows + </div> + + <h2>Heading 2</h2> + <h2>Heading 2</h2> + <h2>Heading 2</h2> + + <h3>Heading 3</h3> + <h3>Heading 3</h3> + <h3>Heading 3</h3> + + <h4>Heading 4</h4> + <h4>Heading 4</h4> + <h4>Heading 4</h4> + + <div class="💩" id="💩" 💩="💩"></div> +</body> +</html>
\ No newline at end of file diff --git a/devtools/server/tests/mochitest/inspector-styles-data.css b/devtools/server/tests/mochitest/inspector-styles-data.css new file mode 100644 index 000000000..5c3652f52 --- /dev/null +++ b/devtools/server/tests/mochitest/inspector-styles-data.css @@ -0,0 +1,3 @@ +.external-rule { + cursor: crosshair; +} diff --git a/devtools/server/tests/mochitest/inspector-styles-data.html b/devtools/server/tests/mochitest/inspector-styles-data.html new file mode 100644 index 000000000..a2a126f0e --- /dev/null +++ b/devtools/server/tests/mochitest/inspector-styles-data.html @@ -0,0 +1,81 @@ +<html> +<script> + window.onload = () => { + window.opener.postMessage('ready', '*') + } +</script> +<style> + .inheritable-rule { + font-size: 15px; + } + .uninheritable-rule { + background-color: #f06; + } + @media screen { + #mediaqueried { + background-color: #f06; + } + } + #svgcontent rect { + fill: rgb(1,2,3); + } + + #layout-element, + #layout-auto-margin-element { + width: 50px; + height: 50px; + padding: 3px 5px 7px 5px; + border: 5px solid red; + margin: 10px 20px 30px 0; + box-sizing: border-box; + position: absolute; + z-index: 2; + } + + #layout-auto-margin-element { + margin: 10px auto; + } +</style> +<link type="text/css" rel="stylesheet" href="inspector-styles-data.css"></link> +<body> + <h1>Style Actor Tests</h1> + <!-- Inheritance checks --> + <div id="inheritable-rule-uninheritable-style" class="inheritable-rule" style="background-color: purple"> + <div id="inheritable-rule-inheritable-style" class="inheritable-rule" style="color: blue"> + <div id="uninheritable-rule-uninheritable-style" class="uninheritable-rule" style="background-color: green"> + <div id="uninheritable-rule-inheritable-style" class="uninheritable-rule" style="color: red"> + <div id="test-node"> + Here is the test node. + </div> + </div> + </div> + </div> + </div> + + <!-- Computed checks --> + <div id="computed-parent" class="external-rule inheritable-rule uninheritable-rule" style="color: red;"> + <div id="computed-test-node" class="external-rule"> + Here is the test node. + </div> + </div> + + <!-- Matched checks --> + <div id="matched-parent" class="external-rule inheritable-rule uninheritable-rule" style="color: red;"> + <div id="matched-test-node" style="font-size: 10px" class="external-rule"> + Here is the test node. + </div> + </div> + + <div id="mediaqueried"> + Screen mediaqueried. + </div> + + <div id="svgcontent"> + <svg><rect></rect></svg> + </div> + + <div id="layout-element">I can has layout</div> + <div id="layout-auto-margin-element">I can has layout too</div> + +</body> +</html> diff --git a/devtools/server/tests/mochitest/inspector-traversal-data.html b/devtools/server/tests/mochitest/inspector-traversal-data.html new file mode 100644 index 000000000..45b8c2ede --- /dev/null +++ b/devtools/server/tests/mochitest/inspector-traversal-data.html @@ -0,0 +1,90 @@ +<html> +<head> + <meta charset="UTF-8"> + <title>Inspector Traversal Test Data</title> + <style type="text/css"> + #pseudo::before { + content: "before"; + } + #pseudo::after { + content: "after"; + } + #pseudo-empty::before { + content: "before an empty element"; + } + #shadow::before { + content: "Testing ::before on a shadow host"; + } + </style> + <script type="text/javascript"> + window.onload = function() { + + // Set up a basic shadow DOM + var host = document.querySelector('#shadow'); + if (host.createShadowRoot) { + var root = host.createShadowRoot(); + root.innerHTML = '<h3>Shadow <em>DOM</em></h3><select multiple></select>'; + } + + // Put a copy of the body in an iframe to test frame traversal. + var body = document.querySelector("body"); + var data = "data:text/html,<html>" + body.outerHTML + "<html>"; + var iframe = document.createElement("iframe"); + iframe.setAttribute("id", "childFrame"); + iframe.onload = function() { + window.opener.postMessage('ready', '*') + }; + iframe.src = data; + body.appendChild(iframe); + } + </script> +</head> +<body style="background-color:white"> + <h1>Inspector Actor Tests</h1> + <span id="longstring">longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong</span> + <span id="shortstring">short</span> + <span id="empty"></span> + <div id="longlist" data-test="exists"> + <div id="a">a</div> + <div id="b">b</div> + <div id="c">c</div> + <div id="d">d</div> + <div id="e">e</div> + <div id="f">f</div> + <div id="g">g</div> + <div id="h">h</div> + <div id="i">i</div> + <div id="j">j</div> + <div id="k">k</div> + <div id="l">l</div> + <div id="m">m</div> + <div id="n">n</div> + <div id="o">o</div> + <div id="p">p</div> + <div id="q">q</div> + <div id="r">r</div> + <div id="s">s</div> + <div id="t">t</div> + <div id="u">u</div> + <div id="v">v</div> + <div id="w">w</div> + <div id="x">x</div> + <div id="y">y</div> + <div id="z">z</div> + </div> + <div id="longlist-sibling"> + <div id="longlist-sibling-firstchild"></div> + </div> + <p id="edit-html"></p> + + <select multiple><option>one</option><option>two</option></select> + <div id="pseudo"><span>middle</span></div> + <div id="pseudo-empty"></div> + <div id="shadow">light dom</div> + <object> + <div id="1"></div> + </object> + <div class="node-to-duplicate"></div> + <div id="scroll-into-view" style="margin-top: 1000px;">scroll</div> +</body> +</html> diff --git a/devtools/server/tests/mochitest/inspector_css-properties.html b/devtools/server/tests/mochitest/inspector_css-properties.html new file mode 100644 index 000000000..2c160c928 --- /dev/null +++ b/devtools/server/tests/mochitest/inspector_css-properties.html @@ -0,0 +1,10 @@ +<html> +<head> +<body> + <script type="text/javascript"> + window.onload = function() { + window.opener.postMessage('ready', '*'); + }; + </script> +</body> +</html> diff --git a/devtools/server/tests/mochitest/inspector_getImageData.html b/devtools/server/tests/mochitest/inspector_getImageData.html new file mode 100644 index 000000000..eebecea90 --- /dev/null +++ b/devtools/server/tests/mochitest/inspector_getImageData.html @@ -0,0 +1,21 @@ +<html> +<head> +<body> + <img class="custom"> + <img class="big-horizontal" src="large-image.jpg" style="width:500px;"> + <canvas class="big-vertical" style="width:500px;"></canvas> + <img class="small" src="small-image.gif"> + <img class="data" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAJklEQVRIie3NMREAAAgAoe9fWls4eAzMVM0xoVAoFAqFQqFQ+C9chp4NHvu+4Q4AAAAASUVORK5CYII="> + <script> + window.onload = () => { + var canvas = document.querySelector("canvas"), ctx = canvas.getContext("2d"); + canvas.width = 1000; + canvas.height = 2000; + ctx.fillStyle = "red"; + ctx.fillRect(0, 0, 1000, 2000); + + window.opener.postMessage('ready', '*') + } + </script> +</body> +</html> diff --git a/devtools/server/tests/mochitest/large-image.jpg b/devtools/server/tests/mochitest/large-image.jpg Binary files differnew file mode 100644 index 000000000..bda383e59 --- /dev/null +++ b/devtools/server/tests/mochitest/large-image.jpg diff --git a/devtools/server/tests/mochitest/memory-helpers.js b/devtools/server/tests/mochitest/memory-helpers.js new file mode 100644 index 000000000..aea8c4732 --- /dev/null +++ b/devtools/server/tests/mochitest/memory-helpers.js @@ -0,0 +1,52 @@ +var Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; + +var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); +const { Task } = require("devtools/shared/task"); +var Services = require("Services"); +var { DebuggerClient } = require("devtools/shared/client/main"); +var { DebuggerServer } = require("devtools/server/main"); + +var { MemoryFront } = require("devtools/shared/fronts/memory"); + +// Always log packets when running tests. +Services.prefs.setBoolPref("devtools.debugger.log", true); +SimpleTest.registerCleanupFunction(function () { + Services.prefs.clearUserPref("devtools.debugger.log"); +}); + +function startServerAndGetSelectedTabMemory() { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + var client = new DebuggerClient(DebuggerServer.connectPipe()); + + return client.connect() + .then(() => client.listTabs()) + .then(response => { + var form = response.tabs[response.selected]; + var memory = MemoryFront(client, form, response); + + return { memory, client }; + }); +} + +function destroyServerAndFinish(client) { + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish(); + }); +} + +function waitForTime(ms) { + return new Promise((resolve, reject) => { + setTimeout(resolve, ms); + }); +} + +function waitUntil(predicate) { + if (predicate()) { + return Promise.resolve(true); + } + return new Promise(resolve => setTimeout(() => waitUntil(predicate).then(() => resolve(true)), 10)); +} diff --git a/devtools/server/tests/mochitest/nonchrome_unsafeDereference.html b/devtools/server/tests/mochitest/nonchrome_unsafeDereference.html new file mode 100644 index 000000000..6c19d3104 --- /dev/null +++ b/devtools/server/tests/mochitest/nonchrome_unsafeDereference.html @@ -0,0 +1,8 @@ +<!DOCTYPE HTML> +<html> +<script> +var xhr = new XMLHttpRequest; +xhr.timeout = 1742; +xhr.expando = 'Expando!'; +</script> +</html> diff --git a/devtools/server/tests/mochitest/setup-in-child.js b/devtools/server/tests/mochitest/setup-in-child.js new file mode 100644 index 000000000..a575faa20 --- /dev/null +++ b/devtools/server/tests/mochitest/setup-in-child.js @@ -0,0 +1,20 @@ +const {Cc, Ci} = require("chrome"); +const cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]. + getService(Ci.nsIMessageListenerManager); +const { DebuggerServer } = require("devtools/server/main"); + +exports.setupChild = function (a, b, c) { + cpmm.sendAsyncMessage("test:setupChild", [a, b, c]); +}; + +exports.callParent = function () { + // Hack! Fetch DebuggerServerConnection objects directly within DebuggerServer guts. + for (let id in DebuggerServer._connections) { + let conn = DebuggerServer._connections[id]; + conn.setupInParent({ + module: "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/setup-in-parent.js", + setupParent: "setupParent", + args: [{one: true}, 2, "three"] + }); + } +}; diff --git a/devtools/server/tests/mochitest/setup-in-parent.js b/devtools/server/tests/mochitest/setup-in-parent.js new file mode 100644 index 000000000..9845ee647 --- /dev/null +++ b/devtools/server/tests/mochitest/setup-in-parent.js @@ -0,0 +1,10 @@ +var {Ci} = require("chrome"); +var Services = require("Services"); + +exports.setupParent = function ({mm, prefix}) { + let args = [ + !!mm.QueryInterface(Ci.nsIMessageSender), + prefix + ]; + Services.obs.notifyObservers(null, "test:setupParent", JSON.stringify(args)); +}; diff --git a/devtools/server/tests/mochitest/small-image.gif b/devtools/server/tests/mochitest/small-image.gif Binary files differnew file mode 100644 index 000000000..e702427a5 --- /dev/null +++ b/devtools/server/tests/mochitest/small-image.gif diff --git a/devtools/server/tests/mochitest/test_Debugger.Script.prototype.global.html b/devtools/server/tests/mochitest/test_Debugger.Script.prototype.global.html new file mode 100644 index 000000000..77357e608 --- /dev/null +++ b/devtools/server/tests/mochitest/test_Debugger.Script.prototype.global.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=958646 + +Debugger.Script.prototype.global should return innerize globals, not WindowProxies. +--> +<head> + <meta charset="utf-8"> + <title>Debugger.Script.prototype.global should return inner windows</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> +<pre id="test"> +<script> + +Components.utils.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +window.onload = function () { + SimpleTest.waitForExplicitFinish(); + + var iframe = document.createElement("iframe"); + iframe.src = "data:text/html,<script>function glorp() { }<\/script>"; + iframe.onload = firstOnLoadHandler; + document.body.appendChild(iframe); + + function firstOnLoadHandler() { + var dbg = new Debugger; + var iframeDO = dbg.addDebuggee(iframe.contentWindow); + + // For sanity: check that the debuggee global is the inner window, + // and that the outer window gets a distinct D.O. + var iframeWindowProxyDO = iframeDO.makeDebuggeeValue(iframe.contentWindow); + ok(iframeDO !== iframeWindowProxyDO); + + // The real test: Debugger.Script.prototype.global returns inner windows. + ok(iframeDO.getOwnPropertyDescriptor('glorp').value.script.global === iframeDO); + + SimpleTest.finish(); + } +} + +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_Debugger.Source.prototype.element.html b/devtools/server/tests/mochitest/test_Debugger.Source.prototype.element.html new file mode 100644 index 000000000..3cbc22353 --- /dev/null +++ b/devtools/server/tests/mochitest/test_Debugger.Source.prototype.element.html @@ -0,0 +1,182 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=941876 + +Debugger.Source.prototype.element and .elementAttributeName should report the DOM +element to which code is attached (if any), and how. +--> +<head> + <meta charset="utf-8"> + <title>Debugger.Source.prototype.element should return owning element</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> +<pre id="test"> +<script> + +Components.utils.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +window.onload = function () { + SimpleTest.waitForExplicitFinish(); + + var log = ''; + var doc, dieter, ulrich, isolde, albrecht; + var dbg, iframeDO, DOFor; + + // Create an iframe to debug. + // We can't use a data: URL here, because we want to test script elements + // that refer to the JavaScript via 'src' attributes, and data: documents + // can't refer to those. So we use a separate HTML document. + var iframe = document.createElement("iframe"); + iframe.src = "Debugger.Source.prototype.element.html"; + iframe.onload = onLoadHandler; + document.body.appendChild(iframe); + + function onLoadHandler() { + log += 'l'; + + // Now that the iframe's window has been created, we can add + // it as a debuggee. + dbg = new Debugger; + dbg.onDebuggerStatement = franzDebuggerHandler; + iframeDO = dbg.addDebuggee(iframe.contentWindow); + DOFor = iframeDO.makeDebuggeeValue.bind(iframeDO); + + // Send a click event to heidi. + doc = iframe.contentWindow.document; + doc.getElementById('heidi').dispatchEvent(new Event('click')); + } + + function franzDebuggerHandler(frame) { + log += 'f'; + + // The top stack frame should be franz, belonging to the script element. + ok(frame.callee.displayName === 'franz', 'top frame is franz'); + ok(frame.script.source.element === DOFor(doc.getElementById('franz')), + 'top frame source belongs to element franz'); + ok(frame.script.source.elementAttributeName === undefined, + "top frame source doesn't belong to an attribute"); + + // The second stack frame should belong to heinrich. + ok(frame.older.script.source.element === DOFor(doc.getElementById('heinrich')), + "second frame source belongs to element heinrich"); + ok(frame.older.script.source.elementAttributeName === undefined, + "second frame source doesn't belong to an attribute"); + + // The next stack frame should belong to heidi's onclick handler. + ok(frame.older.older.script.source.element === DOFor(doc.getElementById('heidi')), + 'third frame source belongs to element heidi'); + ok(frame.older.older.script.source.elementAttributeName === 'onclick', + "third frame source belongs to 'onclick' attribute"); + + // Try a dynamically inserted inline script element. + ulrich = doc.createElement('script'); + ulrich.text = 'debugger;' + dbg.onDebuggerStatement = ulrichDebuggerHandler; + doc.body.appendChild(ulrich); + } + + function ulrichDebuggerHandler(frame) { + log += 'u'; + + // The top frame should be ulrich's text. + ok(frame.script.source.element === DOFor(ulrich), + "top frame belongs to ulrich"); + ok(frame.script.source.elementAttributeName === undefined, + "top frame is not on an attribute of ulrich"); + + // Try a dynamically inserted out-of-line script element. + isolde = doc.createElement('script'); + isolde.setAttribute('src', 'Debugger.Source.prototype.element-2.js'); + isolde.setAttribute('id', 'idolde, my dear'); + dbg.onDebuggerStatement = isoldeDebuggerHandler; + doc.body.appendChild(isolde); + } + + function isoldeDebuggerHandler(frame) { + log += 'i'; + + // The top frame should belong to isolde. + ok(frame.script.source.element === DOFor(isolde), + "top frame belongs to isolde"); + info("frame.script.source.element is: " + uneval(frame.script.source.element)); + if (typeof frame.script.source.element.unsafeDereference() == 'object') { + info(" toString: " + frame.script.source.element.unsafeDereference()); + info(" id: " + frame.script.source.element.unsafeDereference().id); + } + + ok(frame.script.source.elementAttributeName === undefined, + "top frame source is not an attribute of isolde"); + info("frame.script.source.elementAttributeName is: " + + uneval(frame.script.source.elementAttributeName)); + + // Try a dynamically created div element with a handler. + dieter = doc.createElement('div'); + dieter.setAttribute('id', 'dieter'); + dieter.setAttribute('ondrag', 'debugger;'); + dbg.onDebuggerStatement = dieterDebuggerHandler; + dieter.dispatchEvent(new Event('drag')); + } + + function dieterDebuggerHandler(frame) { + log += 'd'; + + // The top frame should belong to dieter's ondrag handler. + ok(frame.script.source.element === DOFor(dieter), + "second event's handler belongs to dieter"); + ok(frame.script.source.elementAttributeName === 'ondrag', + "second event's handler is on dieter's 'ondrag' element"); + + // Try sending an 'onresize' event to the window. + // + // Note that we only want Debugger to see the events we send, not any + // genuine resize events accidentally generated by the test harness (see bug + // 1162067). So we mark our events as cancelable; that seems to be the only + // bit chrome can fiddle on an Event that content code will see and that + // won't affect propagation. Then, the content event only runs its + // 'debugger' statement when the event is cancelable. It's a kludge. + dbg.onDebuggerStatement = resizeDebuggerHandler; + iframe.contentWindow.dispatchEvent(new Event('resize', { cancelable: true })); + } + + function resizeDebuggerHandler(frame) { + log += 'e'; + + // The top frame should belong to the body's 'onresize' handler, even + // though we sent the message to the window and it was handled. + ok(frame.script.source.element === DOFor(doc.body), + "onresize event handler belongs to body element"); + ok(frame.script.source.elementAttributeName === 'onresize', + "onresize event handler is on body element's 'onresize' attribute"); + + // In SVG, the event and the attribute that holds that event's handler + // have different names. Debugger.Source.prototype.elementAttributeName + // should report (as one might infer) the attribute name, not the event + // name. + albrecht = doc.createElementNS('http://www.w3.org/2000/svg', 'svg'); + albrecht.setAttribute('onload', 'debugger;'); + dbg.onDebuggerStatement = SVGLoadHandler; + albrecht.dispatchEvent(new Event("SVGLoad")); + } + + function SVGLoadHandler(frame) { + log += 's'; + + // The top frame's source should be on albrecht's 'onload' attribute. + ok(frame.script.source.element === DOFor(albrecht), + "SVGLoad event handler belongs to albrecht"); + ok(frame.script.source.elementAttributeName === 'onload', + "SVGLoad event handler is on albrecht's 'onload' attribute"); + + ok(log === 'lfuides', "all tests actually ran"); + SimpleTest.finish(); + } +} + +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionScript.html b/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionScript.html new file mode 100644 index 000000000..cbc2ae615 --- /dev/null +++ b/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionScript.html @@ -0,0 +1,97 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=969786 + +Debugger.Source.prototype.introductionScript and .introductionOffset should +behave when 'eval' is called with no scripted frames active at all. +--> +<head> + <meta charset="utf-8"> + <title>Debugger.Source.prototype.introductionScript with no caller</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> +<pre id="test"> +<script> + +Components.utils.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +window.onload = function () { + SimpleTest.waitForExplicitFinish(); + + var dbg, iframeDO, doc, script2DO; + + // Create an iframe to debug. + var iframe = document.createElement("iframe"); + iframe.src = "data:text/html,<div>Hi!</div>"; + iframe.onload = onLoadHandler; + document.body.appendChild(iframe); + + function onLoadHandler() { + // Now that the iframe's window has been created, we can add + // it as a debuggee. + dbg = new Debugger; + iframeDO = dbg.addDebuggee(iframe.contentWindow); + + doc = iframe.contentWindow.document; + var script = doc.createElement('script'); + script.text = "setTimeout(eval.bind(null, 'debugger;'), 0);"; + dbg.onDebuggerStatement = timerHandler; + doc.body.appendChild(script); + } + + function timerHandler(frame) { + // The top stack frame's source should have an undefined + // introduction script and introduction offset. + var source = frame.script.source; + ok(source.introductionScript === undefined, + "setTimeout eval introductionScript is undefined"); + ok(source.introductionOffset === undefined, + "setTimeout eval introductionOffset is undefined"); + + // Check that the above isn't just some quirk of iframes, or the + // browser milieu destroying information: an eval script should indeed + // have proper introduction information. + var script2 = doc.createElement('script'); + script2.text = "eval('debugger;');"; + script2DO = iframeDO.makeDebuggeeValue(script2); + dbg.onDebuggerStatement = evalHandler; + doc.body.appendChild(script2); + } + + function evalHandler(frame) { + // The top stack frame's source should be introduced by the script that + // called eval. + var source = frame.script.source; + var frame2 = frame.older; + + ok(source.introductionType === 'eval', + "top frame's source was introduced by 'eval'"); + ok(source.introductionScript === frame2.script, + "eval frame's introduction script is the older frame's script"); + ok(source.introductionOffset === frame2.offset, + "eval frame's introduction offset is current offset in older frame"); + ok(source.introductionScript.source.element === script2DO, + "eval frame's introducer belongs to script2 element"); + + // The frame that called eval, in turn, should have no introduction + // information. (In the future, we certainly could point at the call + // that inserted the script element into the document; if that happens, + // we can update this test.) + ok(frame2.script.source.introductionType === 'scriptElement', + "older frame has no introduction type"); + ok(frame2.script.source.introductionScript === undefined, + "older frame has no introduction script"); + ok(frame2.script.source.introductionOffset === undefined, + "older frame has no introduction offset"); + + SimpleTest.finish(); + } +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionType.html b/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionType.html new file mode 100644 index 000000000..c0066659c --- /dev/null +++ b/devtools/server/tests/mochitest/test_Debugger.Source.prototype.introductionType.html @@ -0,0 +1,181 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=935203 + +Debugger.Source.prototype.introductionType should return 'eventHandler' for +JavaScrip appearing in an inline event handler attribute. +--> +<head> + <meta charset="utf-8"> + <title>Debugger.Source.prototype.introductionType should identify event handlers</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<pre id="test"> +<script> + +Components.utils.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +var dbg; +var iframeDO, doc; +var Tootles, TootlesDO; + +window.onload = function () { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +}; + +addTest(function setup() { + // Create an iframe to debug. + var iframe = document.createElement("iframe"); + iframe.src = "data:text/html," + + "<div id='Tootles' onclick='debugger;'>I'm a DIV!</div>" + + "<script id='Auddie'>function auddie() { debugger; }<\/script>"; + iframe.onload = onLoadHandler; + document.body.appendChild(iframe); + + function onLoadHandler() { + // Now that the iframe's window has been created, we can add + // it as a debuggee. + dbg = new Debugger; + iframeDO = dbg.addDebuggee(iframe.contentWindow); + doc = iframe.contentWindow.document; + Tootles = doc.getElementById('Tootles'); + TootlesDO = iframeDO.makeDebuggeeValue(Tootles); + + runNextTest(); + } +}); + + +// Check the introduction type of in-markup event handler code. +// Send a click event to Tootles, whose handler has a 'debugger' statement, +// and check that script's introduction type. +addTest(function ClickOnTootles() { + dbg.onDebuggerStatement = TootlesClickDebugger; + Tootles.dispatchEvent(new Event('click')); + + function TootlesClickDebugger(frame) { + // some sanity checks + ok(frame.script.source.element === TootlesDO, + "top frame source belongs to element 'Tootles'"); + is(frame.script.source.elementAttributeName, 'onclick', + "top frame source belongs to 'onclick' attribute"); + + // And, the actual point of this test: + is(frame.script.source.introductionType, 'eventHandler', + "top frame source's introductionType is 'eventHandler'"); + + runNextTest(); + } +}); + + +// Check the introduction type of dynamically added event handler code. +// Add a drag event handler to Tootles as a string, and then send +// Tootles a drag event. +addTest(function DragTootles() { + dbg.onDebuggerStatement = TootlesDragDebugger; + Tootles.setAttribute('ondrag', 'debugger;'); + Tootles.dispatchEvent(new Event('drag')); + + function TootlesDragDebugger(frame) { + // sanity checks + ok(frame.script.source.element === TootlesDO, + "top frame source belongs to element 'Tootles'"); + is(frame.script.source.elementAttributeName, 'ondrag', + "top frame source belongs to 'ondrag' attribute"); + + // And, the actual point of this test: + is(frame.script.source.introductionType, 'eventHandler', + "top frame source's introductionType is 'eventHandler'"); + + runNextTest(); + } +}); + + +// Check the introduction type of an in-markup script element. +addTest(function checkAuddie() { + var fnDO = iframeDO.getOwnPropertyDescriptor('auddie').value; + var AuddieDO = iframeDO.makeDebuggeeValue(doc.getElementById('Auddie')); + + is(fnDO.class, 'Function', + "Script element 'Auddie' defined function 'auddie'."); + ok(fnDO.script.source.element === AuddieDO, + "Function auddie's script belongs to script element 'Auddie'"); + is(fnDO.script.source.elementAttributeName, undefined, + "Function auddie's script doesn't belong to any attribute of 'Auddie'"); + is(fnDO.script.source.introductionType, 'scriptElement', + "Function auddie's script's source was introduced by a script element"); + + runNextTest(); +}); + + +// Check the introduction type of a dynamically inserted script element. +addTest(function InsertRover() { + dbg.onDebuggerStatement = RoverDebugger; + var rover = doc.createElement('script'); + var roverDO = iframeDO.makeDebuggeeValue(rover); + rover.text = 'debugger;'; + doc.body.appendChild(rover); + + function RoverDebugger(frame) { + // sanity checks + ok(frame.script.source.element === roverDO, + "Rover script belongs to Rover"); + ok(frame.script.source.elementAttributeName === undefined, + "Rover script doesn't belong to an attribute of Rover"); + + // Check the introduction type. + ok(frame.script.source.introductionType === 'scriptElement', + "Rover script's introduction type is 'scriptElement'"); + + runNextTest(); + } +}); + + +// Create a XUL document with a script element, and check its introduction type. +addTest(function XULDocumentScript() { + var xulFrame = document.createElement('iframe'); + xulFrame.src = "data:application/vnd.mozilla.xul+xml;charset=utf-8," + + "<?xml version=\"1.0\"?>" + + "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'>" + + "<script id='xulie'>function xulScriptFunc() { debugger; }<\/script>" + + "</window>"; + xulFrame.onload = xulLoaded; + info("Appending iframe containing XUL document"); + document.body.appendChild(xulFrame); + + function xulLoaded() { + info("Loaded XUL document"); + var xulFrameDO = dbg.addDebuggee(xulFrame.contentWindow); + var xulDoc = xulFrame.contentWindow.document; + var xulieDO = xulFrameDO.makeDebuggeeValue(xulDoc.getElementById('xulie')); + var xulFnDO = xulFrameDO.getOwnPropertyDescriptor('xulScriptFunc').value; + is(typeof xulFnDO, 'object', "XUL script element defined 'xulScriptFunc'"); + is(xulFnDO.class, 'Function', + "XUL global 'xulScriptFunc' is indeed a function"); + + // A XUL document's script elements' code gets shared amongst all + // instantiations of the document, so there's no specific DOM element + // we can attribute the code to. + is(xulFnDO.script.source.element, undefined, + "XUL script code should not be attributed to any individual element"); + + is(xulFnDO.script.source.introductionType, 'scriptElement', + "xulScriptFunc's introduction type is 'scriptElement'"); + runNextTest(); + } +}); + +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_animation_actor-lifetime.html b/devtools/server/tests/mochitest/test_animation_actor-lifetime.html new file mode 100644 index 000000000..a5265d918 --- /dev/null +++ b/devtools/server/tests/mochitest/test_animation_actor-lifetime.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1247243 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1247243</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +window.onload = function() { + const Ci = Components.interfaces; + const {AnimationsFront} = require("devtools/shared/fronts/animation"); + const {InspectorFront} = require("devtools/shared/fronts/inspector"); + + SimpleTest.waitForExplicitFinish(); + + let gWalker = null; + let gClient = null; + let animationsFront = null; + + addTest(function setup() { + info ("Setting up inspector and animation actors."); + + let url = document.getElementById("animationContent").href; + attachURL(url, function(err, client, tab, doc) { + let inspector = InspectorFront(client, tab); + + animationsFront = new AnimationsFront(client, tab); + + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + + }); + }); + + addAsyncTest(function* testActorLifetime() { + + info ("Testing animated node actor"); + let animatedNodeActor = yield gWalker.querySelector(gWalker.rootNode, + ".animated"); + yield animationsFront.getAnimationPlayersForNode(animatedNodeActor); + + let animationsActor = DebuggerServer._searchAllConnectionsForActor(animationsFront.actorID); + + is(animationsActor.actors.length, 1, + "AnimationActor have 1 AnimationPlayerActors"); + + info ("Testing AnimationPlayerActors release"); + let stillNodeActor = yield gWalker.querySelector(gWalker.rootNode, + ".still"); + yield animationsFront.getAnimationPlayersForNode(stillNodeActor); + is(animationsActor.actors.length, 0, + "AnimationActor does not have any AnimationPlayerActors anymore"); + + info ("Testing multi animated node actor"); + let multiNodeActor = yield gWalker.querySelector(gWalker.rootNode, + ".multi"); + yield animationsFront.getAnimationPlayersForNode(multiNodeActor); + is(animationsActor.actors.length, 2, + "AnimationActor has now 2 AnimationPlayerActors"); + + info ("Testing single animated node actor"); + yield animationsFront.getAnimationPlayersForNode(animatedNodeActor); + is(animationsActor.actors.length, 1, + "AnimationActor has only one AnimationPlayerActors"); + + info ("Testing AnimationPlayerActors release again"); + yield animationsFront.getAnimationPlayersForNode(stillNodeActor); + is(animationsActor.actors.length, 0, + "AnimationActor does not have any AnimationPlayerActors anymore"); + + runNextTest(); + }); + + + runNextTest(); +}; + </script> +</head> +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247243">Mozilla Bug 1247243</a> + <a id="animationContent" target="_blank" href="animation-data.html">Test Document</a> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_connectToChild.html b/devtools/server/tests/mochitest/test_connectToChild.html new file mode 100644 index 000000000..3bda7b566 --- /dev/null +++ b/devtools/server/tests/mochitest/test_connectToChild.html @@ -0,0 +1,134 @@ +<SDOCTYPv HTM.> +<html> +<!-- +Bug 966991 - Test DebuggerServer.connectToChild +--> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug</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> +<pre id="test"> +<script type="application/javascript;version=1.8"> + +let Cu = Components.utils; +let Cc = Components.classes; +let Ci = Components.interfaces; + +let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); +let { DebuggerClient } = require("devtools/shared/client/main"); +let { DebuggerServer } = require("devtools/server/main"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({ + "set": [ + // Always log packets when running tests. + ["devtools.debugger.log", true], + ["dom.mozBrowserFramesEnabled", true] + ] + }, runTests); +} + +function runTests() { + // Create a minimal iframe with a message manager + let iframe = document.createElement("iframe"); + iframe.mozbrowser = true; + document.body.appendChild(iframe); + + let mm = iframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager; + + // Register a test actor in the child process so that we can know if and when + // this fake actor is disconnected. + mm.loadFrameScript("data:text/javascript,new " + function FrameScriptScope() { + const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); + const { DebuggerServer } = require("devtools/server/main"); + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + } + + function TestActor() {dump("instanciate test actor\n");} + TestActor.prototype = { + actorPrefix: "test", + + disconnect: function () { + sendAsyncMessage("test-actor-disconnected", null); + }, + hello: function () { + return {msg:"world"}; + } + }; + TestActor.prototype.requestTypes = { + "hello": TestActor.prototype.hello + }; + DebuggerServer.addTabActor(TestActor, "testActor"); + }, false); + + // Instantiate a minimal server + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + } + if (!DebuggerServer.createRootActor) { + DebuggerServer.addBrowserActors(); + } + + function firstClient() { + // Fake a first connection to an iframe + let transport = DebuggerServer.connectPipe(); + let conn = transport._serverConnection; + let client = new DebuggerClient(transport); + DebuggerServer.connectToChild(conn, iframe).then(actor => { + ok(actor.testActor, "Got the test actor"); + + // Ensure sending at least one request to our actor, + // otherwise it won't be instanciated, nor be disconnected... + client.request({ + to: actor.testActor, + type: "hello", + }, function (response) { + + // Then close the client. That should end up cleaning our test actor + client.close(); + + // Ensure that our test actor got cleaned up; + // its disconnect method should be called + mm.addMessageListener("test-actor-disconnected", function listener() { + mm.removeMessageListener("test-actor-disconnected", listener); + ok(true, "Actor is cleaned up"); + + secondClient(actor.testActor); + }); + }); + }); + } + + function secondClient(firstActor) { + // Then fake a second one, that should spawn a new set of tab actors + let transport = DebuggerServer.connectPipe(); + let conn = transport._serverConnection; + let client = new DebuggerClient(transport); + DebuggerServer.connectToChild(conn, iframe).then(actor => { + ok(actor.testActor, "Got a test actor for the second connection"); + isnot(actor.testActor, firstActor, "We get different actor instances between two connections"); + + client.close(cleanup); + }); + } + + function cleanup() { + DebuggerServer.destroy(); + iframe.remove(); + SimpleTest.finish() + } + + firstClient(); +} + +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_connection-manager.html b/devtools/server/tests/mochitest/test_connection-manager.html new file mode 100644 index 000000000..bc802f933 --- /dev/null +++ b/devtools/server/tests/mochitest/test_connection-manager.html @@ -0,0 +1,119 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 898485 - [app manager] Implement an abstract connection manager +--> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug</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> +<pre id="test"> +<script> + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + var Cu = Components.utils; + + var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var {DebuggerServer} = require("devtools/server/main"); + var Services = require("Services"); + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + var {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager"); + + var orgCount = ConnectionManager.connections.length; + + ConnectionManager.once("new", (event, c) => { + is(ConnectionManager.connections[orgCount], c, "new event fired, with correct connection"); + }); + + var c1 = ConnectionManager.createConnection(); + var c2 = ConnectionManager.createConnection(); + + is(ConnectionManager.connections[orgCount], c1, "Connection 1 registered"); + is(ConnectionManager.connections[orgCount + 1], c2, "Connection 2 registered"); + + c1.once(Connection.Events.DESTROYED, function() { + is(ConnectionManager.connections.length, orgCount + 1, "Connection 1 destroyed"); + + var c = c2; + + var eventsRef = "connecting connected disconnecting disconnected host-changed disconnected timeout destroyed"; + var events = []; + + var s = Connection.Status; + + is(c.status, s.DISCONNECTED, "disconnected"); + + c.once(Connection.Events.CONNECTING, function(e) { events.push(e); is(c.status, s.CONNECTING, "connecting"); }); + c.once(Connection.Events.CONNECTED, function(e) { events.push(e); is(c.status, s.CONNECTED, "connected"); c.disconnect()}); + c.once(Connection.Events.DISCONNECTING, function(e) { events.push(e); is(c.status, s.DISCONNECTING, "disconnecting"); }); + c.once(Connection.Events.DISCONNECTED, function(e) { events.push(e); is(c.status, s.DISCONNECTED, "disconnected"); testError()}); + c.once(Connection.Events.DESTROYED, function(e) { events.push(e); is(c.status, s.DESTROYED, "destroyed"); finish()}); + + c.connect(); + + function testStore() { + c.store.on("set", function(e,path) { + if (path.join(".") == "device.width") { + is(c.store.object.device.width, window.screen.width, "Store is fed with valid data"); + c.disconnect(); + } + }); + } + + function testError() { + c.once(Connection.Events.DISCONNECTED, function(e) { + events.push(e); + testKeepConnecting(); + }); + c.once(Connection.Events.HOST_CHANGED, function(e) { + events.push(e); + c.connect(); + }); + c.port = 1; + c.host = "localhost"; + } + + function testKeepConnecting() { + // ensure that keepConnecting keep trying connecting + // until the connection attempts timeout + var originalTimeout = Services.prefs.getIntPref("devtools.debugger.remote-timeout"); + Services.prefs.setIntPref("devtools.debugger.remote-timeout", 1000); + c.once("timeout", function (e) { + events.push(e); + Services.prefs.setIntPref("devtools.debugger.remote-timeout", originalTimeout); + ConnectionManager.destroyConnection(c); + }); + c.keepConnecting = true; + var port = ConnectionManager.getFreeTCPPort(); + ok(parseInt(port), "Free TCP port looks like a port number"); + c.port = port; + c.host = "locahost"; + c.connect(); + } + + function finish() { + is(events.join(" "), eventsRef, "Events received in the right order"); + DebuggerServer.destroy(); + SimpleTest.finish(); + } + + }); + + ConnectionManager.destroyConnection(c1); + + +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_css-logic-media-queries.html b/devtools/server/tests/mochitest/test_css-logic-media-queries.html new file mode 100644 index 000000000..bc465df55 --- /dev/null +++ b/devtools/server/tests/mochitest/test_css-logic-media-queries.html @@ -0,0 +1,62 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that css-logic handles media-queries correctly +--> +<head> + <meta charset="utf-8"> + <title>Test css-logic media-queries</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"> + <style> + div { + width: 1000px; + height: 100px; + background-color: #f00; + } + + @media screen and (min-width: 1px) { + div { + width: 200px; + } + } + </style> +</head> +<body> + <div></div> + <script type="application/javascript;version=1.8"> + + window.onload = function() { + var { classes: Cc, utils: Cu, interfaces: Ci } = Components; + const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"] + .getService(Ci.inIDOMUtils); + + var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var Services = require("Services"); + const {CssLogic} = require("devtools/server/css-logic"); + + SimpleTest.waitForExplicitFinish(); + + let div = document.querySelector("div"); + let cssLogic = new CssLogic(DOMUtils.isInheritedProperty); + cssLogic.highlight(div); + cssLogic.processMatchedSelectors(); + + let _strings = Services.strings + .createBundle("chrome://devtools-shared/locale/styleinspector.properties"); + + let inline = _strings.GetStringFromName("rule.sourceInline"); + + let source1 = inline + ":12"; + let source2 = inline + ":19 @media screen and (min-width: 1px)"; + is(cssLogic._matchedRules[0][0].source, source1, + "rule.source gives correct output for rule 1"); + is(cssLogic._matchedRules[1][0].source, source2, + "rule.source gives correct output for rule 2"); + + SimpleTest.finish(); + } + + </script> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_css-logic-specificity.html b/devtools/server/tests/mochitest/test_css-logic-specificity.html new file mode 100644 index 000000000..45169c1fd --- /dev/null +++ b/devtools/server/tests/mochitest/test_css-logic-specificity.html @@ -0,0 +1,84 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that css-logic calculates CSS specificity properly +--> +<head> + <meta charset="utf-8"> + <title>Test css-logic specificity</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +</head> +<body style="background:blue;"> + <script type="application/javascript;version=1.8"> + + window.onload = function() { + var {utils: Cu, classes: Cc, interfaces: Ci} = Components; + + const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + const {CssLogic, CssSelector} = require("devtools/server/css-logic"); + const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"] + .getService(Ci.inIDOMUtils); + + const TEST_DATA = [ + {text: "*", expected: 0}, + {text: "LI", expected: 1}, + {text: "UL LI", expected: 2}, + {text: "UL OL + LI", expected: 3}, + {text: "H1 + [REL=\"up\"]", expected: 257}, + {text: "UL OL LI.red", expected: 259}, + {text: "LI.red.level", expected: 513}, + {text: ".red .level", expected: 512}, + {text: "#x34y", expected: 65536}, + {text: "#s12:not(FOO)", expected: 65537}, + {text: "body#home div#warning p.message", expected: 131331}, + {text: "* body#home div#warning p.message", expected: 131331}, + {text: "#footer :not(nav) li", expected: 65538}, + {text: "bar:nth-child(n)", expected: 257}, + {text: "li::-moz-list-number", expected: 1}, + {text: "a:hover", expected: 257} + ]; + + function createDocument() { + let text = TEST_DATA.map(i=>i.text).join(","); + text = '<style type="text/css">' + text + " {color:red;}</style>"; + document.body.innerHTML = text; + } + + function getExpectedSpecificity(selectorText) { + return TEST_DATA.filter(i => i.text === selectorText)[0].expected; + } + + SimpleTest.waitForExplicitFinish(); + + createDocument(); + let cssLogic = new CssLogic(DOMUtils.isInheritedProperty); + + cssLogic.highlight(document.body); + let cssSheet = cssLogic.sheets[0]; + let cssRule = cssSheet.domSheet.cssRules[0]; + let selectors = CssLogic.getSelectors(cssRule); + + info("Iterating over the test selectors") + for (let i = 0; i < selectors.length; i++) { + let selectorText = selectors[i]; + info("Testing selector " + selectorText); + + let selector = new CssSelector(cssRule, selectorText, i); + let expected = getExpectedSpecificity(selectorText); + let specificity = DOMUtils.getSpecificity(selector.cssRule, + selector.selectorIndex) + is(specificity, expected, + 'Selector "' + selectorText + '" has a specificity of ' + expected); + } + + info("Testing specificity of element.style"); + let colorProp = cssLogic.getPropertyInfo("background"); + is(colorProp.matchedSelectors[0].specificity, 0x01000000, + "Element styles have specificity of 0x01000000 (16777216)."); + + SimpleTest.finish(); + } + + </script> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_css-logic.html b/devtools/server/tests/mochitest/test_css-logic.html new file mode 100644 index 000000000..6c21e72c8 --- /dev/null +++ b/devtools/server/tests/mochitest/test_css-logic.html @@ -0,0 +1,167 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const {CssLogic} = require("devtools/server/css-logic"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +addTest(function findAllCssSelectors() { + var nodes = document.querySelectorAll('*'); + for (var i = 0; i < nodes.length; i++) { + var selector = CssLogic.findCssSelector(nodes[i]); + var matches = document.querySelectorAll(selector); + + is(matches.length, 1, 'There is a single match: ' + selector); + is(matches[0], nodes[i], 'The selector matches the correct node: ' + selector); + } + + runNextTest(); +}); + +addTest(function findCssSelectorNotContainedInDocument() { + + var unattached = document.createElement("div"); + unattached.id = "unattached"; + try { + CssLogic.findCssSelector(unattached); + ok (false, "Unattached node did not throw") + } catch(e) { + ok(e, "Unattached node throws an exception"); + } + + var unattachedChild = document.createElement("div"); + unattached.appendChild(unattachedChild); + try { + CssLogic.findCssSelector(unattachedChild); + ok (false, "Unattached child node did not throw") + } catch(e) { + ok(e, "Unattached child node throws an exception"); + } + + var unattachedBody = document.createElement("body"); + try { + CssLogic.findCssSelector(unattachedBody); + ok (false, "Unattached body node did not throw") + } catch(e) { + ok(e, "Unattached body node throws an exception"); + } + + runNextTest(); +}); + +addTest(function findCssSelector() { + + let data = [ + "#one", + "#" + CSS.escape("2"), + ".three", + "." + CSS.escape("4"), + "#find-css-selector > div:nth-child(5)", + "#find-css-selector > p:nth-child(6)", + ".seven", + ".eight", + ".nine", + ".ten", + "div.sameclass:nth-child(11)", + "div.sameclass:nth-child(12)", + "div.sameclass:nth-child(13)", + "#" + CSS.escape("!, \", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \\, ], ^, `, {, |, }, ~"), + ]; + + let container = document.querySelector("#find-css-selector"); + is (container.children.length, data.length, "Container has correct number of children."); + + for (let i = 0; i < data.length; i++) { + let node = container.children[i]; + is (CssLogic.findCssSelector(node), data[i], "matched id for index " + (i-1)); + } + + runNextTest(); +}); + +addTest(function getComputedStyle() { + let node = document.querySelector("#computed-style"); + is (CssLogic.getComputedStyle(node).getPropertyValue("width"), + "50px", "Computed style on a normal node works (width)"); + is (CssLogic.getComputedStyle(node).getPropertyValue("height"), + "10px", "Computed style on a normal node works (height)"); + + let firstChild = new _documentWalker(node, window).firstChild(); + is (CssLogic.getComputedStyle(firstChild).getPropertyValue("content"), + "\"before\"", "Computed style on a ::before node works (content)"); + let lastChild = new _documentWalker(node, window).lastChild(); + is (CssLogic.getComputedStyle(lastChild).getPropertyValue("content"), + "\"after\"", "Computed style on a ::after node works (content)"); + + runNextTest(); +}); + +addTest(function getBindingElementAndPseudo() { + let node = document.querySelector("#computed-style"); + var {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(node); + + is (bindingElement, node, + "Binding element is the node itself for a normal node"); + ok (!pseudo, "Pseudo is null for a normal node"); + + let firstChild = new _documentWalker(node, window).firstChild(); + var {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(firstChild); + is (bindingElement, node, + "Binding element is the parent for a pseudo node"); + is (pseudo, ":before", "Pseudo is correct for a ::before node"); + + let lastChild = new _documentWalker(node, window).lastChild(); + var {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(lastChild); + is (bindingElement, node, + "Binding element is the parent for a pseudo node"); + is (pseudo, ":after", "Pseudo is correct for a ::after node"); + + runNextTest(); +}); + + </script> +</head> +<body> + <div id="find-css-selector"> + <div id="one"></div> <!-- Basic ID --> + <div id="2"></div> <!-- Escaped ID --> + <div class="three"></div> <!-- Basic Class --> + <div class="4"></div> <!-- Escaped Class --> + <div attr="5"></div> <!-- Only an attribute --> + <p></p> <!-- Nothing unique --> + <div class="seven seven"></div> <!-- Two classes with same name --> + <div class="eight eight2"></div> <!-- Two classes with different names --> + + <!-- Two elements with the same id - should not use ID --> + <div class="nine" id="nine-and-ten"></div> + <div class="ten" id="nine-and-ten"></div> + + <!-- Three elements with the same id - should use class and nth-child instead --> + <div class="sameclass" id="11-12-13"></div> + <div class="sameclass" id="11-12-13"></div> + <div class="sameclass" id="11-12-13"></div> + + <!-- Special characters --> + <div id="!, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, `, {, |, }, ~"></div> + </div> + <style type="text/css"> + #computed-style { width: 50px; height: 10px; } + #computed-style::before { content: "before"; } + #computed-style::after { content: "after"; } + </style> + <div id="computed-style"></div> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_css-properties_01.html b/devtools/server/tests/mochitest/test_css-properties_01.html new file mode 100644 index 000000000..45386b830 --- /dev/null +++ b/devtools/server/tests/mochitest/test_css-properties_01.html @@ -0,0 +1,121 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1265798 - Replace inIDOMUtils.cssPropertyIsShorthand +--> +<head> + <meta charset="utf-8"> + <title>Test CSS Properties Actor</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +window.onload = function() { + const { initCssProperties, getCssProperties } = + require("devtools/shared/fronts/css-properties"); + + function promiseAttachUrl (url) { + return new Promise((resolve, reject) => { + attachURL(url, function(err, client, tab, doc) { + if (err) { + return reject(err); + } + resolve({client, tab, doc}); + }); + }) + } + + function toSortedString(array) { + return JSON.stringify(array.sort()); + } + + const runCssPropertiesTests = Task.async(function* (url, useActor) { + info(`Opening two tabs ${useActor ? "with" : "without"} CssPropertiesActor support.`); + + let attachmentA = yield promiseAttachUrl(url); + let attachmentB = yield promiseAttachUrl(url); + + const toolboxMockA = { + target: { + hasActor: () => useActor, + client: attachmentA.client, + form: attachmentA.tab + }, + // Fake the window for css-properties.js's getClientBrowserVersion to work + win: window + }; + const toolboxMockB = { + target: { + hasActor: () => useActor, + client: attachmentB.client, + form: attachmentB.tab + }, + win: window + }; + + yield initCssProperties(toolboxMockA); + yield initCssProperties(toolboxMockB); + + const cssProperties = getCssProperties(toolboxMockA); + const cssPropertiesA = getCssProperties(toolboxMockA); + const cssPropertiesB = getCssProperties(toolboxMockB); + + is(cssProperties, cssPropertiesA, + "Multiple calls with the same toolbox returns the same object."); + isnot(cssProperties, cssPropertiesB, + "Multiple calls with the different toolboxes return different "+ + " objects."); + + ok(cssProperties.isKnown("border"), + "The `border` shorthand property is known."); + ok(cssProperties.isKnown("display"), + "The `display` property is known."); + ok(!cssProperties.isKnown("foobar"), + "A fake property is not known."); + ok(cssProperties.isKnown("--foobar"), + "A CSS variable properly evaluates."); + ok(cssProperties.isKnown("--foob\\{ar"), + "A CSS variable with escaped character properly evaluates."); + ok(cssProperties.isKnown("--fübar"), + "A CSS variable unicode properly evaluates."); + ok(!cssProperties.isKnown("--foo bar"), + "A CSS variable with spaces fails"); + + is(toSortedString(cssProperties.getValues('margin')), + toSortedString(["-moz-calc","auto","calc","inherit","initial","unset"]), + "Can get values for the CSS margin."); + is(cssProperties.getValues('foobar').length, 0, + "Unknown values return an empty array."); + + const bgColorValues = cssProperties.getValues('background-color'); + ok(bgColorValues.includes("blanchedalmond"), + "A property with color values includes blanchedalmond."); + ok(bgColorValues.includes("papayawhip"), + "A property with color values includes papayawhip."); + ok(bgColorValues.includes("rgb"), + "A property with color values includes non-colors."); + + ok(cssProperties.isValidOnClient("margin", "0px", window.document), + "Margin and 0px are valid CSS values"); + ok(!cssProperties.isValidOnClient("margin", "foo", window.document), + "Margin and foo are not valid CSS values"); + }); + + addAsyncTest(function* setup() { + let url = document.getElementById("cssProperties").href; + yield runCssPropertiesTests(url, true); + yield runCssPropertiesTests(url, false); + + runNextTest(); + }); + + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + </script> +</head> +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265798">Mozilla Bug 1265798</a> + <a id="cssProperties" target="_blank" href="inspector_css-properties.html">Test Document</a> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_css-properties_02.html b/devtools/server/tests/mochitest/test_css-properties_02.html new file mode 100644 index 000000000..1a5d99d72 --- /dev/null +++ b/devtools/server/tests/mochitest/test_css-properties_02.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1265798 - Replace inIDOMUtils.cssPropertyIsShorthand +--> +<head> + <meta charset="utf-8"> + <title>Test CSS Properties Actor</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +window.onload = function() { + const { initCssProperties, getCssProperties } = + require("devtools/shared/fronts/css-properties"); + + const { CSS_PROPERTIES_DB } = require("devtools/shared/css/properties-db"); + + function promiseAttachUrl (url) { + return new Promise((resolve, reject) => { + attachURL(url, function(err, client, tab, doc) { + if (err) { + return reject(err); + } + resolve({client, tab, doc}); + }); + }) + } + + addAsyncTest(function* setup() { + let url = document.getElementById("cssProperties").href; + + let attachmentA = yield promiseAttachUrl(url); + let attachmentB = yield promiseAttachUrl(url); + let attachmentC = yield promiseAttachUrl(url); + + const toolboxMatchingVersions = { + target: { + hasActor: () => true, + client: attachmentA.client, + form: attachmentA.tab, + }, + win: window + }; + const toolboxDifferentVersions = { + target: { + hasActor: () => true, + client: attachmentB.client, + form: attachmentB.tab + }, + win: { navigator: { userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 " + + "Firefox/30.0" }} + }; + + // Modify a property on the static database, to differentiate between a generated + // and static CSS properties database. + CSS_PROPERTIES_DB.properties.color.isStatic = true; + + yield initCssProperties(toolboxMatchingVersions); + yield initCssProperties(toolboxDifferentVersions); + + const cssPropertiesMatching = getCssProperties(toolboxMatchingVersions); + const cssPropertiesDifferent = getCssProperties(toolboxDifferentVersions); + + is(cssPropertiesMatching.properties.color.isStatic, true, + "The static CSS database is used when the client and platform versions match."); + isnot(cssPropertiesDifferent.properties.color.isStatic, undefined, + "The generated CSS database is used when the client and platform versions do " + + "not match, but the client is a Firefox."); + + delete CSS_PROPERTIES_DB.properties.color.isStatic; + + runNextTest(); + }); + + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + </script> +</head> +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265798">Mozilla Bug 1265798</a> + <a id="cssProperties" target="_blank" href="inspector_css-properties.html">Test Document</a> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_device.html b/devtools/server/tests/mochitest/test_device.html new file mode 100644 index 000000000..d678f185f --- /dev/null +++ b/devtools/server/tests/mochitest/test_device.html @@ -0,0 +1,99 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 895360 - [app manager] Device meta data actor +--> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug</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> +<pre id="test"> +<script> + +window.onload = function() { + var Cu = Components.utils; + var Cc = Components.classes; + var Ci = Components.interfaces; + + Cu.import("resource://gre/modules/PermissionsTable.jsm"); + var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var {DebuggerClient} = require("devtools/shared/client/main"); + var {DebuggerServer} = require("devtools/server/main"); + var Services = require("Services"); + + SimpleTest.waitForExplicitFinish(); + + var {getDeviceFront} = require("devtools/shared/fronts/device"); + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + var client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect().then(function onConnect() { + client.listTabs(function onListTabs(aResponse) { + var d = getDeviceFront(client, aResponse); + + var desc, permissions; + var appInfo = Services.appinfo; + var utils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + + + var localDesc = { + appid: appInfo.ID, + vendor: appInfo.vendor, + name: appInfo.name, + version: appInfo.version, + appbuildid: appInfo.appBuildID, + platformbuildid: appInfo.platformBuildID, + platformversion: appInfo.platformVersion, + geckobuildid: appInfo.platformBuildID, + geckoversion: appInfo.platformVersion, + useragent: window.navigator.userAgent, + locale: Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).getSelectedLocale("global"), + os: appInfo.OS, + processor: appInfo.XPCOMABI.split("-")[0], + compiler: appInfo.XPCOMABI.split("-")[1], + dpi: utils.displayDPI, + width: window.screen.width, + height: window.screen.height + } + + function checkValues() { + for (var key in localDesc) { + is(desc[key], localDesc[key], "valid field (" + key + ")"); + } + + var currProfD = Services.dirsvc.get("ProfD", Ci.nsIFile); + var profileDir = currProfD.path; + ok(profileDir.indexOf(desc.profile.length > 0 && desc.profile) != -1, "valid profile name"); + + var a = JSON.stringify(PermissionsTable); + var b = JSON.stringify(permissions.rawPermissionsTable); + + is(a, b, "Permissions Tables is valid"); + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish() + }); + } + + + d.getDescription().then((v) => desc = v) + .then(() => d.getRawPermissionsTable()) + .then((json) => permissions = json) + .then(checkValues); + + }); + }); + +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_director.html b/devtools/server/tests/mochitest/test_director.html new file mode 100644 index 000000000..ad2648bfa --- /dev/null +++ b/devtools/server/tests/mochitest/test_director.html @@ -0,0 +1,114 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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> +<pre id="test"> + <script type="application/javascript;version=1.8" src="./director-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const WAIT_EVENT_TIMEOUT = 3000; + +window.onload = function() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + var tests = [ + runDirectorRegistryActorTest + ].map((testCase) => { + return function* () { + setup(); + yield testCase().then(null, (e) => { + console.error("Exception during testCase run", e); + ok(false, "Exception during testCase run: " + [e, e.fileName, e.lineNumber].join("\n\t")); + }); + + teardown(); + }; + }); + + for (var test of tests) { + yield test(); + } + }).then( + function success() { + SimpleTest.finish() + }, + function error(e) { + console.error("Exception during testCase run", e); + ok(false, "Exception during testCase run: " + [e, e.fileName, e.lineNumber].join("\n\t")); + + SimpleTest.finish(); + } + ); +}; + +var targetWin = null; + +function setup() { + if (!DebuggerServer.initialized) { + DebuggerServer.init(() => true); + DebuggerServer.addBrowserActors(); + + SimpleTest.registerCleanupFunction(teardown); + } +} + +function teardown() { + purgeInstalledDirectorScripts(); + + DebuggerServer.destroy(); + if (targetWin) { + targetWin.close(); + } +} + +/*********************************** + * test cases + **********************************/ + + +function runDirectorRegistryActorTest() { + let testDirectorScriptOptions = { + scriptCode: "(" + (function() { + module.exports = function({port}) { + port.onmessage = function(evt) { + // echo messages + evt.source.postMessage(evt.data); + }; + }; + }).toString() + ")();", + scriptOptions: {} + } + + return Task.spawn(function* () { + let { client, root } = yield newConnectedDebuggerClient(); + + var directorRegistryClient = new DirectorRegistryFront(client, root); + + let installed = yield directorRegistryClient.install("testDirectorScript", testDirectorScriptOptions); + is(installed, true, "DirectorManager.install returns true"); + + let list = yield directorRegistryClient.list(); + is(JSON.stringify(list), JSON.stringify(["testDirectorScript"]), + "DirectorManager.list contains the installed director script"); + + let uninstalled = yield directorRegistryClient.uninstall("testDirectorScript"); + is(uninstalled, true, "DirectorManager.uninstall return true"); + + yield client.close(); + }); +} + + + </script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_director_connectToChild.html b/devtools/server/tests/mochitest/test_director_connectToChild.html new file mode 100644 index 000000000..cb348efe6 --- /dev/null +++ b/devtools/server/tests/mochitest/test_director_connectToChild.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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> +<pre id="test"> + <script type="application/javascript;version=1.8" src="./director-helpers.js"></script> + <script type="application/javascript;version=1.8"> +window.onload = function() { + Task.spawn(function* () { + SimpleTest.waitForExplicitFinish(); + + var tests = [ + runPropagateDirectorScriptsToChildTest, + ].map((testCase) => { + return function* () { + setup(); + yield testCase().then(null, (e) => { + ok(false, "Exception during testCase run: " + [e, e.fileName, e.lineNumber].join("\n\t")); + }); + + teardown(); + }; + }); + + for (var test of tests) { + yield test(); + } + + SimpleTest.finish(); + }); +}; + +function setup() { + if (!DebuggerServer.initialized) { + DebuggerServer.init(() => true); + DebuggerServer.addBrowserActors(); + SimpleTest.registerCleanupFunction(function() { + DebuggerServer.destroy(); + }); + } +} + +function teardown() { + purgeInstalledDirectorScripts(); + DebuggerServer.destroy(); +} + +/*********************************** + * test cases + **********************************/ + +function runPropagateDirectorScriptsToChildTest() { + let iframe = document.createElement("iframe"); + iframe.mozbrowser = true; + + document.body.appendChild(iframe); + + return Task.spawn(function* () { + var { client, root, transport } = yield newConnectedDebuggerClient(); + + var directorRegistryClient = new DirectorRegistryFront(client, root); + + // install a director script + yield directorRegistryClient.install("testPropagatedDirectorScript", { + scriptCode: "console.log('director script test');", + scriptOptions: {} + }); + + var conn = transport._serverConnection; + var childActor = yield DebuggerServer.connectToChild(conn, iframe); + + ok(typeof childActor.directorManagerActor !== "undefined", + "childActor.directorActor should be defined"); + + var childDirectorManagerClient = new DirectorManagerFront(client, childActor); + + var directorScriptList = yield childDirectorManagerClient.list(); + + ok(directorScriptList.installed.length === 1 && + directorScriptList.installed[0] === "testPropagatedDirectorScript", + "director scripts propagated correctly") + + yield client.close(); + }); +} + </script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_executeInGlobal-outerized_this.html b/devtools/server/tests/mochitest/test_executeInGlobal-outerized_this.html new file mode 100644 index 000000000..8bedde618 --- /dev/null +++ b/devtools/server/tests/mochitest/test_executeInGlobal-outerized_this.html @@ -0,0 +1,69 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=837060 + +When we use Debugger.Object.prototype.executeInGlobal, the 'this' value seen +by the evaluated code should be the WindowProxy, not the inner window +object. +--> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug 837060</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> +<pre id="test"> +<script> + +Components.utils.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +window.onload = function () { + SimpleTest.waitForExplicitFinish(); + + var iframe = document.createElement("iframe"); + iframe.src = "data:text/html,<script>var me = 'page 1';<\/script>"; + iframe.onload = firstOnLoadHandler; + document.body.appendChild(iframe); + + function firstOnLoadHandler() { + var dbg = new Debugger; + var page1DO = dbg.addDebuggee(iframe.contentWindow); + iframe.src = "data:text/html,<script>var me = 'page 2';<\/script>"; + iframe.onload = function () { + var page2DO = dbg.addDebuggee(iframe.contentWindow); + ok(page1DO !== page2DO, "the two pages' globals get distinct D.O's"); + ok(page1DO.unsafeDereference() === page2DO.unsafeDereference(), + "unwrapping page1DO and page2DO outerizes both, yielding the same outer window"); + + is(page1DO.executeInGlobal('me').return, 'page 1', "page1DO continues to refer to original page"); + is(page2DO.executeInGlobal('me').return, 'page 2', "page2DO refers to current page"); + + is(page1DO.executeInGlobal('this === window').return, true, + "page 1: Debugger.Object.prototype.executeInGlobal should outerize 'this'"); + is(page1DO.executeInGlobalWithBindings('this === window', {x:2}).return, true, + "page 1: Debugger.Object.prototype.executeInGlobal should outerize 'this'"); + + is(page2DO.executeInGlobal('this === window').return, true, + "page 2: Debugger.Object.prototype.executeInGlobal should outerize 'this'"); + is(page2DO.executeInGlobalWithBindings('this === window', {x:2}).return, true, + "page 2: Debugger.Object.prototype.executeInGlobal should outerize 'this'"); + + // Debugger doesn't let one use outer windows as globals. You have to innerize. + var outerDO = page1DO.makeDebuggeeValue(page1DO.unsafeDereference()); + ok(outerDO !== page1DO, "outer window gets its own D.O, distinct from page 1's global"); + ok(outerDO !== page2DO, "outer window gets its own D.O, distinct from page 2's global"); + SimpleTest.doesThrow(function () { outerDO.executeInGlobal('me'); }, + "outer window D.Os can't be used as globals"); + + SimpleTest.finish(); + } + } +} + +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_framerate_01.html b/devtools/server/tests/mochitest/test_framerate_01.html new file mode 100644 index 000000000..0282d50a2 --- /dev/null +++ b/devtools/server/tests/mochitest/test_framerate_01.html @@ -0,0 +1,141 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1007200 - Create a framerate actor +--> +<head> + <meta charset="utf-8"> + <title>Framerate actor test</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> +<pre id="test"> +<script> + +window.onload = function() { + var Cu = Components.utils; + var Cc = Components.classes; + var Ci = Components.interfaces; + + var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var Services = require("Services"); + var { DebuggerClient } = require("devtools/shared/client/main"); + var { DebuggerServer } = require("devtools/server/main"); + + // Always log packets when running tests. + Services.prefs.setBoolPref("devtools.debugger.log", true); + SimpleTest.registerCleanupFunction(function() { + Services.prefs.clearUserPref("devtools.debugger.log"); + }); + + SimpleTest.waitForExplicitFinish(); + + var {FramerateFront} = require("devtools/shared/fronts/framerate"); + + function plotFPS(ticks, interval = 100, clamp = 60) { + var timeline = []; + var totalTicks = ticks.length; + + // If the refresh driver didn't get a chance to tick before the + // recording was stopped, assume framerate was 0. + if (totalTicks == 0) { + timeline.push({ delta: 0, value: 0 }); + timeline.push({ delta: interval, value: 0 }); + return timeline; + } + + var frameCount = 0; + var prevTime = ticks[0]; + + for (var i = 1; i < totalTicks; i++) { + var currTime = ticks[i]; + frameCount++; + + var elapsedTime = currTime - prevTime; + if (elapsedTime < interval) { + continue; + } + + var framerate = Math.min(1000 / (elapsedTime / frameCount), clamp); + timeline.push({ delta: prevTime, value: framerate }); + timeline.push({ delta: currTime, value: framerate }); + + frameCount = 0; + prevTime = currTime; + } + + return timeline; + }; + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + var client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect().then(function onConnect() { + client.listTabs(function onListTabs(aResponse) { + var form = aResponse.tabs[aResponse.selected]; + var front = FramerateFront(client, form); + + window.setTimeout(() => { + front.startRecording().then(() => { + window.setTimeout(() => { + front.stopRecording().then(rawData => { + onRecordingStopped(front, rawData); + }); + }, 1000); + }); + }, 1000); + }); + }); + + function onRecordingStopped(front, rawData) { + ok(rawData, "There should be a recording available."); + + var timeline = plotFPS(rawData); + ok(timeline.length >= 2, + "There should be at least one measurement available, with two entries."); + + var prevTimeStart = timeline[0].delta; + + for (var i = 0; i < timeline.length; i += 2) { + var currTimeStart = timeline[i].delta; + var currTimeEnd = timeline[i + 1].delta; + info("Testing delta: " + currTimeStart + " vs. " + currTimeEnd); + + ok(currTimeStart < currTimeEnd, + "The start and end time deltas should be consecutive."); + is(currTimeStart, prevTimeStart, + "There should be two time deltas for each framerate value."); + + prevTimeStart = currTimeEnd; + } + + var prevFramerateValue = -1; + + for (var i = 0; i < timeline.length; i += 2) { + var currFramerateStart = timeline[i].value; + var currFramerateEnd = timeline[i + 1].value; + info("Testing framerate: " + currFramerateStart); + + is(currFramerateStart, currFramerateEnd, + "The start and end framerate values should be equal."); + + is(typeof currFramerateStart, "number", "All values should be numbers."); + ok(currFramerateStart <= 60, "All values were correctly clamped.") + + prevFramerateValue = currFramerateStart; + } + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish() + }); + } +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_framerate_02.html b/devtools/server/tests/mochitest/test_framerate_02.html new file mode 100644 index 000000000..9d4626b12 --- /dev/null +++ b/devtools/server/tests/mochitest/test_framerate_02.html @@ -0,0 +1,113 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1007200 - Create a framerate actor +--> +<head> + <meta charset="utf-8"> + <title>Framerate actor test</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> +<pre id="test"> +<script> + +window.onload = function() { + var Cu = Components.utils; + var Cc = Components.classes; + var Ci = Components.interfaces; + + var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var {DebuggerClient} = require("devtools/shared/client/main"); + var {DebuggerServer} = require("devtools/server/main"); + var Services = require("Services"); + + // Always log packets when running tests. + Services.prefs.setBoolPref("devtools.debugger.log", true); + SimpleTest.registerCleanupFunction(function() { + Services.prefs.clearUserPref("devtools.debugger.log"); + }); + + SimpleTest.waitForExplicitFinish(); + + var {FramerateFront} = require("devtools/shared/fronts/framerate"); + + function plotFPS(ticks, interval = 100, clamp = 60) { + var timeline = []; + var totalTicks = ticks.length; + + // If the refresh driver didn't get a chance to tick before the + // recording was stopped, assume framerate was 0. + if (totalTicks == 0) { + timeline.push({ delta: 0, value: 0 }); + timeline.push({ delta: interval, value: 0 }); + return timeline; + } + + var frameCount = 0; + var prevTime = ticks[0]; + + for (var i = 1; i < totalTicks; i++) { + var currTime = ticks[i]; + frameCount++; + + var elapsedTime = currTime - prevTime; + if (elapsedTime < interval) { + continue; + } + + var framerate = Math.min(1000 / (elapsedTime / frameCount), clamp); + timeline.push({ delta: prevTime, value: framerate }); + timeline.push({ delta: currTime, value: framerate }); + + frameCount = 0; + prevTime = currTime; + } + + return timeline; + }; + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + var client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect().then(function onConnect() { + client.listTabs(function onListTabs(aResponse) { + var form = aResponse.tabs[aResponse.selected]; + var front = FramerateFront(client, form); + + front.stopRecording().then(rawData => { + ok(rawData, "There should be a recording available."); + is(rawData.length, 0, "...but it should be empty."); + + var timeline = plotFPS(rawData); + is(timeline.length, 2, + "There should be one measurement plotted, with two entries."); + + info("The framerate should be assumed to be 0 if the recording is empty."); + + is(timeline[0].delta, 0, + "The first time delta should be 0."); + is(timeline[0].value, 0, + "The first framerate value should be 0."); + + is(timeline[1].delta, 100, + "The last time delta should be 100 (the default interval value)."); + is(timeline[1].value, 0, + "The last framerate value should be 0."); + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish() + }); + }); + }); + }); +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_framerate_03.html b/devtools/server/tests/mochitest/test_framerate_03.html new file mode 100644 index 000000000..da76ebc20 --- /dev/null +++ b/devtools/server/tests/mochitest/test_framerate_03.html @@ -0,0 +1,82 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1023018 - Tests whether or not the framerate actor can handle time ranges. +--> +<head> + <meta charset="utf-8"> + <title>Framerate actor test</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> +<pre id="test"> +<script> + +window.onload = function() { + var Cu = Components.utils; + var Cc = Components.classes; + var Ci = Components.interfaces; + + var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var {DebuggerClient} = require("devtools/shared/client/main"); + var {DebuggerServer} = require("devtools/server/main"); + var Services = require("Services"); + + // Always log packets when running tests. + Services.prefs.setBoolPref("devtools.debugger.log", true); + SimpleTest.registerCleanupFunction(function() { + Services.prefs.clearUserPref("devtools.debugger.log"); + }); + + SimpleTest.waitForExplicitFinish(); + + var {FramerateFront} = require("devtools/shared/fronts/framerate"); + var START_TICK = 2000; + var STOP_TICK = 3000; + var TOTAL_TIME = 5000; + + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + var client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect().then(function onConnect() { + client.listTabs(function onListTabs(aResponse) { + var form = aResponse.tabs[aResponse.selected]; + var front = FramerateFront(client, form); + + front.startRecording().then(() => { + window.setTimeout(() => { + front.stopRecording(START_TICK, STOP_TICK).then(rawData => { + onRecordingStopped(front, rawData); + }); + }, TOTAL_TIME); + }); + }); + }); + + function onRecordingStopped(front, rawData) { + ok(rawData, "There should be a recording available."); + + ok(!rawData.find(e => e < START_TICK), + "There should be no tick before 2000ms."); + ok(!rawData.find(e => e > STOP_TICK), + "There should be no tick after 3000ms."); + + for (var tick of rawData) { + info("Testing tick: " + tick); + is(typeof tick, "number", "All values should be numbers."); + } + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish() + }); + } +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_framerate_04.html b/devtools/server/tests/mochitest/test_framerate_04.html new file mode 100644 index 000000000..af6747291 --- /dev/null +++ b/devtools/server/tests/mochitest/test_framerate_04.html @@ -0,0 +1,72 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1023018 - Tests if the framerate actor keeps recording after navigations. +--> +<head> + <meta charset="utf-8"> + <title>Framerate actor test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<pre id="test"> +<script> + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + var {FramerateFront} = require("devtools/shared/fronts/framerate"); + var {TargetFactory} = require("devtools/client/framework/target"); + + var url = document.getElementById("testContent").href; + attachURL(url, onTab); + + function onTab(_, client, form, contentDoc) { + var contentWin = contentDoc.defaultView; + var chromeWin = Services.wm.getMostRecentWindow("navigator:browser"); + var selectedTab = chromeWin.gBrowser.selectedTab; + + var target = TargetFactory.forTab(selectedTab); + var front = FramerateFront(client, form); + + front.startRecording().then(() => { + window.setTimeout(() => { + front.getPendingTicks().then(firstBatch => { + target.once("will-navigate", () => { + window.setTimeout(() => { + front.stopRecording().then(secondBatch => { + onRecordingStopped(client, firstBatch, secondBatch); + }); + }, 1000); + }); + contentWin.location.reload(); + }); + }, 1000); + }); + } + + function onRecordingStopped(client, firstBatch, secondBatch) { + ok(firstBatch, "There should be a first batch recording available."); + ok(secondBatch, "There should be a second batch recording available."); + + var diff = secondBatch.length - firstBatch.length; + info("Difference in ticks: " + diff); + ok(diff > 0, "More ticks should be recorded in the second batch."); + + ok(firstBatch.every((e) => secondBatch.indexOf(e) != -1), + "All the ticks in the first batch should be in the second batch as well."); + ok(secondBatch.every((e, i, array) => i < array.length - 1 ? e < array[i + 1] : true), + "All the ticks in the final batch should be ascending in value."); + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish() + }); + } +} +</script> +</pre> +<a id="testContent" target="_blank" href="inspector_getImageData.html">Test Document</a> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_framerate_05.html b/devtools/server/tests/mochitest/test_framerate_05.html new file mode 100644 index 000000000..96f56a18f --- /dev/null +++ b/devtools/server/tests/mochitest/test_framerate_05.html @@ -0,0 +1,77 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1034648 - Tests whether a framerate recording can be cancelled. +--> +<head> + <meta charset="utf-8"> + <title>Framerate actor test</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> +<pre id="test"> +<script> + +window.onload = function() { + var Cu = Components.utils; + var Cc = Components.classes; + var Ci = Components.interfaces; + + var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var {DebuggerClient} = require("devtools/shared/client/main"); + var {DebuggerServer} = require("devtools/server/main"); + var Services = require("Services"); + + // Always log packets when running tests. + Services.prefs.setBoolPref("devtools.debugger.log", true); + SimpleTest.registerCleanupFunction(function() { + Services.prefs.clearUserPref("devtools.debugger.log"); + }); + + SimpleTest.waitForExplicitFinish(); + + var {FramerateFront} = require("devtools/shared/fronts/framerate"); + + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + + var client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect().then(function onConnect() { + client.listTabs(function onListTabs(aResponse) { + var form = aResponse.tabs[aResponse.selected]; + var front = FramerateFront(client, form); + + front.startRecording().then(() => { + window.setTimeout(() => { + front.cancelRecording().then(() => { + window.setTimeout(() => { + front.getPendingTicks().then(rawTicks => { + ok(rawTicks, + "The returned pending ticks should be empty (1)."); + is(rawTicks.length, 0, + "The returned pending ticks should be empty (2)."); + + front.stopRecording().then(rawData => { + ok(rawData, + "The returned raw data should be an empty array (1)."); + is(rawData.length, 0, + "The returned raw data should be an empty array (2)."); + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish() + }); + }); + }); + }, 1000); + }); + }, 1000); + }); + }); + }); +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_framerate_06.html b/devtools/server/tests/mochitest/test_framerate_06.html new file mode 100644 index 000000000..ecb0a71e0 --- /dev/null +++ b/devtools/server/tests/mochitest/test_framerate_06.html @@ -0,0 +1,82 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1171489 - Tests if the framerate actor does not record timestamps from multiple frames. +--> +<head> + <meta charset="utf-8"> + <title>Framerate actor test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<pre id="test"> +<script> + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + var {FramerateFront} = require("devtools/shared/fronts/framerate"); + var {TargetFactory} = require("devtools/client/framework/target"); + + var url = document.getElementById("testContent").href; + attachURL(url, onTab); + + function onTab(_, client, form, contentDoc) { + var contentWin = contentDoc.defaultView; + var chromeWin = Services.wm.getMostRecentWindow("navigator:browser"); + var selectedTab = chromeWin.gBrowser.selectedTab; + + var target = TargetFactory.forTab(selectedTab); + var front = FramerateFront(client, form); + + front.startRecording().then(() => { + window.setTimeout(() => { + // Wait for the iframe to be loaded again + window.addEventListener("message", function loaded (event) { + if (event.data === "ready") { + window.removeEventListener("message", loaded); + window.setTimeout(() => { + front.stopRecording().then(ticks => { + onRecordingStopped(client, ticks); + }); + }, 1000); + } + }); + contentWin.location.reload(); + }, 1000); + }); + } + + function onRecordingStopped(client, ticks) { + var diffs = []; + + info(`Got ${ticks.length} ticks.`); + + for (var i = 1; i < ticks.length; i++) { + var prev = ticks[i - 1]; + var curr = ticks[i]; + diffs.push(curr - prev); + info(curr + " - " + (curr - prev)); + } + + // 1000 / 60 => 16.666... so we shouldn't get more than diffs of 16.66.. but + // when we get ticks from other frames they're usually at diffs of < 1. Sometimes + // ticks can still be less than 16ms even on one frame (usually following a very slow + // frame), so use a low number (2) to be our threshold + var THRESHOLD = 2; + ok(ticks.length >= 20, "we should have atleast 20 ticks over the course of two seconds."); + var belowThreshold = diffs.filter(v => v <= THRESHOLD); + ok(belowThreshold.length <= 10, "we should have very few frames less than the threshold"); + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish() + }); + } +} +</script> +</pre> +<a id="testContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_getProcess.html b/devtools/server/tests/mochitest/test_getProcess.html new file mode 100644 index 000000000..3c8ca5727 --- /dev/null +++ b/devtools/server/tests/mochitest/test_getProcess.html @@ -0,0 +1,120 @@ +<SDOCTYPv HTM.> +<html> +<!-- +Bug 1060093 - Test DebuggerServer.getProcess +--> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug</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> +<pre id="test"> +<script type="application/javascript;version=1.8"> + +let Cu = Components.utils; +let Cc = Components.classes; +let Ci = Components.interfaces; + +let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); +let {DebuggerClient} = require("devtools/shared/client/main"); +let {DebuggerServer} = require("devtools/server/main"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({ + "set": [ + // Always log packets when running tests. + ["devtools.debugger.log", true], + // Enabled mozbrowser frame to support remote=true + ["dom.mozBrowserFramesEnabled", true], + // Allows creating a branch new process when creation the iframe + ["dom.ipc.processCount", 10], + ] + }, runTests); +} + +function runTests() { + // Instantiate a minimal server + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + } + DebuggerServer.allowChromeProcess = true; + if (!DebuggerServer.createRootActor) { + DebuggerServer.addBrowserActors(); + } + + let client, iframe, processCount; + + function connect() { + // Fake a first connection to the content process + let transport = DebuggerServer.connectPipe(); + client = new DebuggerClient(transport); + client.connect().then(listProcess); + } + + function listProcess() { + // Call listProcesses in order to start receiving new process notifications + client.addListener("processListChanged", function listener() { + client.removeListener("processListChanged", listener); + ok(true, "Received processListChanged event"); + getProcess(); + }); + client.mainRoot.listProcesses(response => { + processCount = response.processes.length; + // Create a remote iframe to spawn a new process + createRemoteIframe(); + }); + } + + function createRemoteIframe() { + iframe = document.createElement("iframe"); + iframe.mozbrowser = true; + iframe.setAttribute("remote", "true"); + iframe.setAttribute("src", "data:text/html,foo"); + document.body.appendChild(iframe); + } + + function getProcess() { + client.mainRoot.listProcesses(response => { + ok(response.processes.length >= 2, "Got at least the parent process and one child"); + is(response.processes.length, processCount+1 , "Got one additional process on the second call to listProcesses"); + + // Connect to the first content processe available + let content = response.processes.filter(p => (!p.parent))[0]; + + client.getProcess(content.id).then(response => { + let actor = response.form; + ok(actor.consoleActor, "Got the console actor"); + ok(actor.chromeDebugger, "Got the thread actor"); + + // Ensure sending at least one request to an actor... + client.request({ + to: actor.consoleActor, + type: "evaluateJS", + text: "var a = 42; a" + }, function (response) { + ok(response.result, 42, "console.eval worked"); + cleanup(); + }); + }); + }); + } + + function cleanup() { + client.close().then(function () { + DebuggerServer.destroy(); + iframe.remove(); + SimpleTest.finish() + }); + } + + connect(); +} + +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-anonymous.html b/devtools/server/tests/mochitest/test_inspector-anonymous.html new file mode 100644 index 000000000..56a911c89 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-anonymous.html @@ -0,0 +1,201 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=777674 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 777674</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +window.onload = function() { + const {InspectorFront} = + require("devtools/shared/fronts/inspector"); + const {_documentWalker} = + require("devtools/server/actors/inspector"); + const nodeFilterConstants = + require("devtools/shared/dom-node-filter-constants"); + const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + + SpecialPowers.pushPrefEnv({"set": [ + ["dom.webcomponents.enabled", true] + ]}); + SimpleTest.waitForExplicitFinish(); + + let gWalker = null; + let gClient = null; + + addTest(function setup() { + info ("Setting up inspector and walker actors."); + + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); + }); + + addAsyncTest(function* testXBLAnonymousInHTMLDocument() { + info ("Testing XBL anonymous in an HTML document."); + let rawToolbarbutton = gInspectee.createElementNS(XUL_NS, "toolbarbutton"); + gInspectee.documentElement.appendChild(rawToolbarbutton); + + let toolbarbutton = yield gWalker.querySelector(gWalker.rootNode, "toolbarbutton"); + let children = yield gWalker.children(toolbarbutton); + + is (toolbarbutton.numChildren, 0, "XBL content is not visible in HTML doc"); + is (children.nodes.length, 0, "XBL content is not returned in HTML doc"); + + runNextTest(); + }); + + addAsyncTest(function* testNativeAnonymous() { + info ("Testing native anonymous content with walker."); + + let select = yield gWalker.querySelector(gWalker.rootNode, "select"); + let children = yield gWalker.children(select); + + is (select.numChildren, 2, "No native anon content for form control"); + is (children.nodes.length, 2, "No native anon content for form control"); + + runNextTest(); + }); + + addAsyncTest(function* testNativeAnonymousStartingNode() { + info ("Tests attaching an element that a walker can't see."); + + let serverWalker = DebuggerServer._searchAllConnectionsForActor(gWalker.actorID); + let docwalker = new _documentWalker( + gInspectee.querySelector("select"), + gInspectee.defaultView, + nodeFilterConstants.SHOW_ALL, + () => { + return nodeFilterConstants.FILTER_ACCEPT + } + ); + let scrollbar = docwalker.lastChild(); + is (scrollbar.tagName, "scrollbar", "An anonymous child has been fetched"); + + let node = yield serverWalker.attachElement(scrollbar); + + ok (node, "A response has arrived"); + ok (node.node, "A node is in the response"); + is (node.node.rawNode.tagName, "SELECT", + "The node has changed to a parent that the walker recognizes"); + + runNextTest(); + }); + + addAsyncTest(function* testPseudoElements() { + info ("Testing pseudo elements with walker."); + + // Markup looks like: <div><::before /><span /><::after /></div> + let pseudo = yield gWalker.querySelector(gWalker.rootNode, "#pseudo"); + let children = yield gWalker.children(pseudo); + + is (pseudo.numChildren, 1, "::before/::after are not counted if there is a child"); + is (children.nodes.length, 3, "Correct number of children"); + + let before = children.nodes[0]; + ok (before.isAnonymous, "Child is anonymous"); + ok (!before._form.isXBLAnonymous, "Child is not XBL anonymous"); + ok (!before._form.isShadowAnonymous, "Child is not shadow anonymous"); + ok (before._form.isNativeAnonymous, "Child is native anonymous"); + + let span = children.nodes[1]; + ok (!span.isAnonymous, "Child is not anonymous"); + + let after = children.nodes[2]; + ok (after.isAnonymous, "Child is anonymous"); + ok (!after._form.isXBLAnonymous, "Child is not XBL anonymous"); + ok (!after._form.isShadowAnonymous, "Child is not shadow anonymous"); + ok (after._form.isNativeAnonymous, "Child is native anonymous"); + + runNextTest(); + }); + + addAsyncTest(function* testEmptyWithPseudo() { + info ("Testing elements with no childrent, except for pseudos."); + + info ("Checking an element whose only child is a pseudo element"); + let pseudo = yield gWalker.querySelector(gWalker.rootNode, "#pseudo-empty"); + let children = yield gWalker.children(pseudo); + + is (pseudo.numChildren, 1, "::before/::after are is counted if there are no other children"); + is (children.nodes.length, 1, "Correct number of children"); + + let before = children.nodes[0]; + ok (before.isAnonymous, "Child is anonymous"); + ok (!before._form.isXBLAnonymous, "Child is not XBL anonymous"); + ok (!before._form.isShadowAnonymous, "Child is not shadow anonymous"); + ok (before._form.isNativeAnonymous, "Child is native anonymous"); + + runNextTest(); + }); + + addAsyncTest(function* testShadowAnonymous() { + info ("Testing shadow DOM content."); + + let shadow = yield gWalker.querySelector(gWalker.rootNode, "#shadow"); + let children = yield gWalker.children(shadow); + + is (shadow.numChildren, 3, "Children of the shadow root are counted"); + is (children.nodes.length, 3, "Children returned from walker"); + + let before = children.nodes[0]; + ok (before.isAnonymous, "Child is anonymous"); + ok (!before._form.isXBLAnonymous, "Child is not XBL anonymous"); + ok (!before._form.isShadowAnonymous, "Child is not shadow anonymous"); + ok (before._form.isNativeAnonymous, "Child is native anonymous"); + + // <h3>Shadow <em>DOM</em></h3> + let shadowChild1 = children.nodes[1]; + ok (shadowChild1.isAnonymous, "Child is anonymous"); + ok (!shadowChild1._form.isXBLAnonymous, "Child is not XBL anonymous"); + ok (shadowChild1._form.isShadowAnonymous, "Child is shadow anonymous"); + ok (!shadowChild1._form.isNativeAnonymous, "Child is not native anonymous"); + + let shadowSubChildren = yield gWalker.children(children.nodes[1]); + is (shadowChild1.numChildren, 2, "Subchildren of the shadow root are counted"); + is (shadowSubChildren.nodes.length, 2, "Subchildren are returned from walker"); + + // <em>DOM</em> + let shadowSubChild = children.nodes[1]; + ok (shadowSubChild.isAnonymous, "Child is anonymous"); + ok (!shadowSubChild._form.isXBLAnonymous, "Child is not XBL anonymous"); + ok (shadowSubChild._form.isShadowAnonymous, "Child is shadow anonymous"); + ok (!shadowSubChild._form.isNativeAnonymous, "Child is not native anonymous"); + + // <select multiple></select> + let shadowChild2 = children.nodes[2]; + ok (shadowChild2.isAnonymous, "Child is anonymous"); + ok (!shadowChild2._form.isXBLAnonymous, "Child is not XBL anonymous"); + ok (shadowChild2._form.isShadowAnonymous, "Child is shadow anonymous"); + ok (!shadowChild2._form.isNativeAnonymous, "Child is not native anonymous"); + + runNextTest(); + }); + + runNextTest(); +}; + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-changeattrs.html b/devtools/server/tests/mochitest/test_inspector-changeattrs.html new file mode 100644 index 000000000..23b7660d2 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-changeattrs.html @@ -0,0 +1,99 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gClient = null; +var gWalker = null; +var checkActorIDs = []; + +function assertOwnership() { + assertOwnershipTrees(gWalker); +} + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(function testChangeAttrs() { + let attrNode = gInspectee.querySelector("#a"); + let attrFront; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(front => { + attrFront = front; + dump("attrFront is: " + attrFront + "\n"); + // Add a few attributes. + let list = attrFront.startModifyingAttributes(); + list.setAttribute("data-newattr", "newvalue"); + list.setAttribute("data-newattr2", "newvalue"); + return list.apply(); + }).then(() => { + // We're only going to test that the change hit the document. + // There are other tests that make sure changes are propagated + // to the client. + is(attrNode.getAttribute("data-newattr"), "newvalue", "Node should have the first new attribute"); + is(attrNode.getAttribute("data-newattr2"), "newvalue", "Node should have the second new attribute."); + }).then(() => { + // Change an attribute. + let list = attrFront.startModifyingAttributes(); + list.setAttribute("data-newattr", "changedvalue"); + return list.apply(); + }).then(() => { + is(attrNode.getAttribute("data-newattr"), "changedvalue", "Node should have the changed first value."); + is(attrNode.getAttribute("data-newattr2"), "newvalue", "Second value should remain unchanged."); + }).then(() => { + let list = attrFront.startModifyingAttributes(); + list.removeAttribute("data-newattr2"); + return list.apply(); + }).then(() => { + is(attrNode.getAttribute("data-newattr"), "changedvalue", "Node should have the changed first value."); + ok(!attrNode.hasAttribute("data-newattr2"), "Second value should be removed."); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gWalker; + delete gInspectee; + delete gClient; + runNextTest(); +}); + + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-changevalue.html b/devtools/server/tests/mochitest/test_inspector-changevalue.html new file mode 100644 index 000000000..a5b613157 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-changevalue.html @@ -0,0 +1,82 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const Ci = Components.interfaces; +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gClient = null; +var gWalker = null; + +function assertOwnership() { + assertOwnershipTrees(gWalker); +} + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(function testChangeValue() { + let contentNode = gInspectee.querySelector("#a").firstChild; + let nodeFront; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(front => { + // Get the text child + return gWalker.children(front, { maxNodes: 1 }); + }).then(children => { + nodeFront = children.nodes[0]; + is(nodeFront.nodeType, Ci.nsIDOMNode.TEXT_NODE); + return nodeFront.setNodeValue("newvalue"); + }).then(() => { + // We're only going to test that the change hit the document. + // There are other tests that make sure changes are propagated + // to the client. + is(contentNode.nodeValue, "newvalue", "Node should have a new value."); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gWalker; + delete gInspectee; + delete gClient; + runNextTest(); +}); + + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-dead-nodes.html b/devtools/server/tests/mochitest/test_inspector-dead-nodes.html new file mode 100644 index 000000000..274636cd6 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-dead-nodes.html @@ -0,0 +1,386 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1121528 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1121528</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker, gDoc; + +addAsyncTest(function() { + let url = document.getElementById("inspectorContent").href; + + let def = promise.defer(); + attachURL(url, function(err, client, tab, doc) { + def.resolve({client, tab, doc}); + }); + let {client, tab, doc} = yield def.promise; + gDoc = doc; + + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + gWalker = yield inspector.getWalker(); + + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.parents(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.parents(nodeFront); + yield newRoot; + + ok(true, "The call to walker.parents() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.children(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "body"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.children(nodeFront); + yield newRoot; + + ok(true, "The call to walker.children() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.siblings(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.siblings(nodeFront); + yield newRoot; + + ok(true, "The call to walker.siblings() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.nextSibling(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.nextSibling(nodeFront); + yield newRoot; + + ok(true, "The call to walker.nextSibling() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.previousSibling(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.previousSibling(nodeFront); + yield newRoot; + + ok(true, "The call to walker.previousSibling() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.addPseudoClassLock(nodeFront) before the load completes " + + "shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.addPseudoClassLock(nodeFront, ":hover"); + yield newRoot; + + ok(true, "The call to walker.addPseudoClassLock() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.removePseudoClassLock(nodeFront) before the load completes " + + "shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.removePseudoClassLock(nodeFront, ":hover"); + yield newRoot; + + ok(true, "The call to walker.removePseudoClassLock() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.clearPseudoClassLocks(nodeFront) before the load completes " + + "shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.clearPseudoClassLocks(nodeFront); + yield newRoot; + + ok(true, "The call to walker.clearPseudoClassLocks() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.innerHTML(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.innerHTML(nodeFront); + yield newRoot; + + ok(true, "The call to walker.innerHTML() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.setInnerHTML(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.setInnerHTML(nodeFront, "<span>innerHTML changed</span>"); + yield newRoot; + + ok(true, "The call to walker.setInnerHTML() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.outerHTML(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.outerHTML(nodeFront); + yield newRoot; + + ok(true, "The call to walker.outerHTML() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.setOuterHTML(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.setOuterHTML(nodeFront, "<h1><span>innerHTML changed</span></h1>"); + yield newRoot; + + ok(true, "The call to walker.setOuterHTML() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.insertAdjacentHTML(nodeFront) before the load completes shouldn't " + + "fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.insertAdjacentHTML(nodeFront, "afterEnd", + "<span>new adjacent HTML</span>"); + yield newRoot; + + ok(true, "The call to walker.insertAdjacentHTML() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.removeNode(nodeFront) before the load completes should throw"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + let hasThrown = false; + try { + yield gWalker.removeNode(nodeFront); + } catch (e) { + hasThrown = true; + } + yield newRoot; + + ok(hasThrown, "The call to walker.removeNode() threw"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.removeNodes([nodeFront]) before the load completes should throw"); + + let nodeFront1 = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let nodeFront2 = yield gWalker.querySelector(gWalker.rootNode, "#longstring"); + let nodeFront3 = yield gWalker.querySelector(gWalker.rootNode, "#shortstring"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + let hasThrown = false; + try { + yield gWalker.removeNodes([nodeFront1, nodeFront2, nodeFront3]); + } catch (e) { + hasThrown = true; + } + yield newRoot; + + ok(hasThrown, "The call to walker.removeNodes() threw"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.insertBefore(nodeFront, parent, null) before the load completes " + + "shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newParentFront = yield gWalker.querySelector(gWalker.rootNode, "#longlist"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.insertBefore(nodeFront, newParentFront); + yield newRoot; + + ok(true, "The call to walker.insertBefore() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.insertBefore(nodeFront, parent, sibling) before the load completes " + + "shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newParentFront = yield gWalker.querySelector(gWalker.rootNode, "#longlist"); + let siblingFront = yield gWalker.querySelector(gWalker.rootNode, "#b"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.insertBefore(nodeFront, newParentFront, siblingFront); + yield newRoot; + + ok(true, "The call to walker.insertBefore() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.editTagName(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.editTagName(nodeFront, "h2"); + yield newRoot; + + ok(true, "The call to walker.editTagName() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.hideNode(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.hideNode(nodeFront); + yield newRoot; + + ok(true, "The call to walker.hideNode() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.unhideNode(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.unhideNode(nodeFront); + yield newRoot; + + ok(true, "The call to walker.unhideNode() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.releaseNode(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "h1"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.releaseNode(nodeFront); + yield newRoot; + + ok(true, "The call to walker.releaseNode() didn't fail"); + runNextTest(); +}); + +addAsyncTest(function() { + info("Getting a nodeFront, reloading the page, and calling " + + "walker.querySelector(nodeFront) before the load completes shouldn't fail"); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, "body"); + let newRoot = waitForMutation(gWalker, isNewRoot); + gDoc.defaultView.location.reload(); + yield gWalker.querySelector(nodeFront, "h1"); + yield newRoot; + + ok(true, "The call to walker.querySelector() didn't fail"); + runNextTest(); +}); + +addTest(function cleanup() { + gWalker = gDoc = null; + runNextTest(); +}); + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=932937">Mozilla Bug 1121528</a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-duplicate-node.html b/devtools/server/tests/mochitest/test_inspector-duplicate-node.html new file mode 100644 index 000000000..35722f226 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-duplicate-node.html @@ -0,0 +1,75 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1208864 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1208864</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gClient = null; +var gWalker = null; + +function assertOwnership() { + assertOwnershipTrees(gWalker); +} + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(Task.async(function* testDuplicateNode() { + let className = ".node-to-duplicate"; + let matches = yield gWalker.querySelectorAll(gWalker.rootNode, className); + is(matches.length, 1, "There should initially be one node to duplicate."); + + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, className); + yield gWalker.duplicateNode(nodeFront); + + matches = yield gWalker.querySelectorAll(gWalker.rootNode, className); + is(matches.length, 2, "The node should now be duplicated."); + + runNextTest(); +})); + +addTest(function cleanup() { + delete gWalker; + delete gInspectee; + delete gClient; + runNextTest(); +}); + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1208864">Mozilla Bug 1208864</a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-hide.html b/devtools/server/tests/mochitest/test_inspector-hide.html new file mode 100644 index 000000000..d9b134c22 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-hide.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gClient = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(function testRearrange() { + let listFront = null; + let listNode = gInspectee.querySelector("#longlist"); + + promiseDone(gWalker.querySelector(gWalker.rootNode, "#longlist").then(front => { + listFront = front; + }).then(() => { + let computed = gInspectee.defaultView.getComputedStyle(listNode); + ok(computed.visibility, "visible", "Node should be visible to start with"); + return gWalker.hideNode(listFront); + }).then(response => { + let computed = gInspectee.defaultView.getComputedStyle(listNode); + ok(computed.visibility, "hidden", "Node should be hidden"); + return gWalker.unhideNode(listFront); + }).then(() => { + let computed = gInspectee.defaultView.getComputedStyle(listNode); + ok(computed.visibility, "visible", "Node should be visible again."); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-insert.html b/devtools/server/tests/mochitest/test_inspector-insert.html new file mode 100644 index 000000000..82b4fef3e --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-insert.html @@ -0,0 +1,119 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/server/actors/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gClient = null; + +function assertOwnership() { + return assertOwnershipTrees(gWalker); +} + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addAsyncTest(function* testRearrange() { + let longlist = yield gWalker.querySelector(gWalker.rootNode, "#longlist"); + let children = yield gWalker.children(longlist); + let nodeA = children.nodes[0]; + is(nodeA.id, "a", "Got the expected node."); + + // Move nodeA to the end of the list. + yield gWalker.insertBefore(nodeA, longlist, null); + ok(!gInspectee.querySelector("#a").nextSibling, "a should now be at the end of the list."); + children = yield gWalker.children(longlist); + is(nodeA, children.nodes[children.nodes.length - 1], "a should now be the last returned child."); + + // Now move it to the middle of the list. + let nextNode = children.nodes[13]; + yield gWalker.insertBefore(nodeA, longlist, nextNode); + let sibling = + new inspector._documentWalker(gInspectee.querySelector("#a"), window).nextSibling(); + is(sibling, nextNode.rawNode(), "Node should match the expected next node."); + children = yield gWalker.children(longlist); + is(nodeA, children.nodes[13], "a should be where we expect it."); + is(nextNode, children.nodes[14], "next node should be where we expect it."); + + runNextTest(); +}); + +addAsyncTest(function* testInsertInvalidInput() { + let longlist = yield gWalker.querySelector(gWalker.rootNode, "#longlist"); + let children = yield gWalker.children(longlist); + let nodeA = children.nodes[0]; + let nextSibling = children.nodes[1]; + + // Now move it to the original location and make sure no mutation happens. + let hasMutated = false; + let observer = new gInspectee.defaultView.MutationObserver(() => { + hasMutated = true; + }); + observer.observe(longlist.rawNode(), { + childList: true, + }); + + yield gWalker.insertBefore(nodeA, longlist, nodeA); + ok(!hasMutated, "hasn't mutated"); + hasMutated = false; + + yield gWalker.insertBefore(nodeA, longlist, nextSibling); + ok(!hasMutated, "still hasn't mutated after inserting before nextSibling"); + hasMutated = false; + + yield gWalker.insertBefore(nodeA, longlist); + ok(hasMutated, "has mutated after inserting with null sibling"); + hasMutated = false; + + yield gWalker.insertBefore(nodeA, longlist); + ok(!hasMutated, "hasn't mutated after inserting with null sibling again"); + + observer.disconnect(); + runNextTest(); +}); + +addTest(function cleanup() { + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-mutations-attr.html b/devtools/server/tests/mochitest/test_inspector-mutations-attr.html new file mode 100644 index 000000000..15c14608b --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-mutations-attr.html @@ -0,0 +1,167 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gWalker = null; +var gClient = null; +var attrNode; +var attrFront; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(setupAttrTest); +addTest(testAddAttribute); +addTest(testChangeAttribute); +addTest(testRemoveAttribute); +addTest(testQueuedMutations); +addTest(setupFrameAttrTest); +addTest(testAddAttribute); +addTest(testChangeAttribute); +addTest(testRemoveAttribute); +addTest(testQueuedMutations); + +function setupAttrTest() { + attrNode = gInspectee.querySelector("#a") + promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(node => { + attrFront = node; + }).then(runNextTest)); +} + +function setupFrameAttrTest() { + let frame = gInspectee.querySelector('#childFrame'); + attrNode = frame.contentDocument.querySelector("#a"); + + promiseDone(gWalker.querySelector(gWalker.rootNode, "#childFrame").then(childFrame => { + return gWalker.children(childFrame); + }).then(children => { + let nodes = children.nodes; + ok(nodes.length, 1, "There should be only one child of the iframe"); + is(nodes[0].nodeType, Node.DOCUMENT_NODE, "iframe child should be a document node"); + return gWalker.querySelector(nodes[0], "#a"); + }).then(node => { + attrFront = node; + }).then(runNextTest)); +} + +function testAddAttribute() { + attrNode.setAttribute("data-newattr", "newvalue"); + attrNode.setAttribute("data-newattr2", "newvalue"); + gWalker.once("mutations", () => { + is(attrFront.attributes.length, 3, "Should have id and two new attributes."); + is(attrFront.getAttribute("data-newattr"), "newvalue", "Node front should have the first new attribute"); + is(attrFront.getAttribute("data-newattr2"), "newvalue", "Node front should have the second new attribute."); + runNextTest(); + }); +} + +function testChangeAttribute() { + attrNode.setAttribute("data-newattr", "changedvalue1"); + attrNode.setAttribute("data-newattr", "changedvalue2"); + attrNode.setAttribute("data-newattr", "changedvalue3"); + gWalker.once("mutations", mutations => { + is(mutations.length, 1, "Only one mutation is sent for multiple queued attribute changes"); + is(attrFront.attributes.length, 3, "Should have id and two new attributes."); + is(attrFront.getAttribute("data-newattr"), "changedvalue3", "Node front should have the changed first value"); + is(attrFront.getAttribute("data-newattr2"), "newvalue", "Second value should remain unchanged."); + runNextTest(); + }); +} + +function testRemoveAttribute() { + attrNode.removeAttribute("data-newattr2"); + gWalker.once("mutations", () => { + is(attrFront.attributes.length, 2, "Should have id and one remaining attribute."); + is(attrFront.getAttribute("data-newattr"), "changedvalue3", "Node front should still have the first value"); + ok(!attrFront.hasAttribute("data-newattr2"), "Second value should be removed."); + runNextTest(); + }) +} + +function testQueuedMutations() { + // All modifications to each attribute should be queued in one mutation event. + + attrNode.removeAttribute("data-newattr"); + attrNode.setAttribute("data-newattr", "1"); + attrNode.removeAttribute("data-newattr"); + attrNode.setAttribute("data-newattr", "2"); + attrNode.removeAttribute("data-newattr"); + + for (var i = 0; i <= 1000; i++) { + attrNode.setAttribute("data-newattr2", i); + } + + attrNode.removeAttribute("data-newattr3"); + attrNode.setAttribute("data-newattr3", "1"); + attrNode.removeAttribute("data-newattr3"); + attrNode.setAttribute("data-newattr3", "2"); + attrNode.removeAttribute("data-newattr3"); + attrNode.setAttribute("data-newattr3", "3"); + + // This shouldn't be added in the attribute set, since it's a new + // attribute that's been added and removed. + attrNode.setAttribute("data-newattr4", "4"); + attrNode.removeAttribute("data-newattr4"); + + gWalker.once("mutations", mutations => { + is(mutations.length, 4, "Only one mutation each is sent for multiple queued attribute changes"); + is(attrFront.attributes.length, 3, "Should have id, data-newattr2, and data-newattr3."); + + is(attrFront.getAttribute("data-newattr2"), "1000", "Node front should still have the correct value"); + is(attrFront.getAttribute("data-newattr3"), "3", "Node front should still have the correct value"); + ok(!attrFront.hasAttribute("data-newattr"), "Attribute value should be removed."); + ok(!attrFront.hasAttribute("data-newattr4"), "Attribute value should be removed."); + + runNextTest(); + }) +} + +addTest(function cleanup() { + delete gInspectee; + delete gWalker; + delete gClient; + runNextTest(); +}); + + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-mutations-childlist.html b/devtools/server/tests/mochitest/test_inspector-mutations-childlist.html new file mode 100644 index 000000000..d845b987e --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-mutations-childlist.html @@ -0,0 +1,310 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gWalker = null; +var gClient = null; +var gCleanupConnection = null; + +function setup(callback) { + let url = document.getElementById("inspectorContent").href; + gCleanupConnection = attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + gClient = client; + gWalker = walker; + }).then(callback)); + }); +} + +function teardown() { + gWalker = null; + gClient = null; + gInspectee = null; + if (gCleanupConnection) { + gCleanupConnection(); + gCleanupConnection = null; + } +} + +function assertOwnership() { + let num = assertOwnershipTrees(gWalker); +} + +function setParent(nodeSelector, newParentSelector) { + let node = gInspectee.querySelector(nodeSelector); + if (newParentSelector) { + let newParent = gInspectee.querySelector(newParentSelector); + newParent.appendChild(node); + } else { + node.parentNode.removeChild(node); + } +} + +function loadSelector(selector) { + return gWalker.querySelectorAll(gWalker.rootNode, selector).then(nodeList => { + return nodeList.items(); + }); +} + +function loadSelectors(selectors) { + return promise.all(Array.from(selectors, (sel) => loadSelector(sel))); +} + +function doMoves(moves) { + for (let move of moves) { + setParent(move[0], move[1]); + } +} + +/** + * Test a set of tree rearrangements and make sure they cause the expected changes. + */ + +var gDummySerial = 0; + +function mutationTest(testSpec) { + return function() { + setup(() => { + promiseDone(loadSelectors(testSpec.load || ["html"]).then(() => { + gWalker.autoCleanup = !!testSpec.autoCleanup; + if (testSpec.preCheck) { + testSpec.preCheck(); + } + doMoves(testSpec.moves || []); + + // Some of these moves will trigger no mutation events, + // so do a dummy change to the root node to trigger + // a mutation event anyway. + gInspectee.documentElement.setAttribute("data-dummy", gDummySerial++); + + gWalker.once("mutations", (mutations) => { + // Filter out our dummy mutation. + mutations = mutations.filter(change => { + if (change.type == "attributes" && + change.attributeName == "data-dummy") { + return false; + } + return true; + }); + assertOwnership(); + if (testSpec.postCheck) { + testSpec.postCheck(mutations); + } + teardown(); + runNextTest(); + }); + })); + }) + } +} + +// Verify that our dummy mutation works. +addTest(mutationTest({ + autoCleanup: false, + postCheck: function(mutations) { + is(mutations.length, 0, "Dummy mutation is filtered out."); + } +})); + +// Test a simple move to a different location in the sibling list for the same +// parent. +addTest(mutationTest({ + autoCleanup: false, + load: ["#longlist div"], + moves: [ + ["#a", "#longlist"] + ], + postCheck: function(mutations) { + let remove = mutations[0]; + is(remove.type, "childList", "First mutation should be a childList.") + ok(remove.removed.length > 0, "First mutation should be a removal.") + let add = mutations[1]; + is(add.type, "childList", "Second mutation should be a childList removal.") + ok(add.added.length > 0, "Second mutation should be an addition.") + let a = add.added[0]; + is(a.id, "a", "Added node should be #a"); + is(a.parentNode(), remove.target, "Should still be a child of longlist."); + is(remove.target, add.target, "First and second mutations should be against the same node."); + } +})); + +// Test a move to another location that is within our ownership tree. +addTest(mutationTest({ + autoCleanup: false, + load: ["#longlist div", "#longlist-sibling"], + moves: [ + ["#a", "#longlist-sibling"] + ], + postCheck: function(mutations) { + let remove = mutations[0]; + is(remove.type, "childList", "First mutation should be a childList.") + ok(remove.removed.length > 0, "First mutation should be a removal.") + let add = mutations[1]; + is(add.type, "childList", "Second mutation should be a childList removal.") + ok(add.added.length > 0, "Second mutation should be an addition.") + let a = add.added[0]; + is(a.id, "a", "Added node should be #a"); + is(a.parentNode(), add.target, "Should still be a child of longlist."); + is(add.target.id, "longlist-sibling", "long-sibling should be the target."); + } +})); + +// Move an unseen node with a seen parent into our ownership tree - should generate a +// childList pair with no adds or removes. +addTest(mutationTest({ + autoCleanup: false, + load: ["#longlist"], + moves: [ + ["#longlist-sibling", "#longlist"] + ], + postCheck: function(mutations) { + is(mutations.length, 2, "Should generate two mutations"); + is(mutations[0].type, "childList", "Should be childList mutations."); + is(mutations[0].added.length, 0, "Should have no adds."); + is(mutations[0].removed.length, 0, "Should have no removes."); + is(mutations[1].type, "childList", "Should be childList mutations."); + is(mutations[1].added.length, 0, "Should have no adds."); + is(mutations[1].removed.length, 0, "Should have no removes."); + } +})); + +// Move an unseen node with an unseen parent into our ownership tree. Should only +// generate one childList mutation with no adds or removes. +addTest(mutationTest({ + autoCleanup: false, + load: ["#longlist div"], + moves: [ + ["#longlist-sibling-firstchild", "#longlist"] + ], + postCheck: function(mutations) { + is(mutations.length, 1, "Should generate two mutations"); + is(mutations[0].type, "childList", "Should be childList mutations."); + is(mutations[0].added.length, 0, "Should have no adds."); + is(mutations[0].removed.length, 0, "Should have no removes."); + } +})); + +// Move a node between unseen nodes, should generate no mutations. +addTest(mutationTest({ + autoCleanup: false, + load: ["html"], + moves: [ + ["#longlist-sibling", "#longlist"] + ], + postCheck: function(mutations) { + is(mutations.length, 0, "Should generate no mutations."); + } +})); + +// Orphan a node and don't clean it up +addTest(mutationTest({ + autoCleanup: false, + load: ["#longlist div"], + moves: [ + ["#longlist", null] + ], + postCheck: function(mutations) { + is(mutations.length, 1, "Should generate one mutation."); + let change = mutations[0]; + is(change.type, "childList", "Should be a childList."); + is(change.removed.length, 1, "Should have removed a child."); + let ownership = clientOwnershipTree(gWalker); + is(ownership.orphaned.length, 1, "Should have one orphaned subtree."); + is(ownershipTreeSize(ownership.orphaned[0]), 1 + 26 + 26, "Should have orphaned longlist, and 26 children, and 26 singleTextChilds"); + } +})); + +// Orphan a node, and do clean it up. +addTest(mutationTest({ + autoCleanup: true, + load: ["#longlist div"], + moves: [ + ["#longlist", null] + ], + postCheck: function(mutations) { + is(mutations.length, 1, "Should generate one mutation."); + let change = mutations[0]; + is(change.type, "childList", "Should be a childList."); + is(change.removed.length, 1, "Should have removed a child."); + let ownership = clientOwnershipTree(gWalker); + is(ownership.orphaned.length, 0, "Should have no orphaned subtrees."); + } +})); + +// Orphan a node by moving it into the tree but out of our visible subtree. +addTest(mutationTest({ + autoCleanup: false, + load: ["#longlist div"], + moves: [ + ["#longlist", "#longlist-sibling"] + ], + postCheck: function(mutations) { + is(mutations.length, 1, "Should generate one mutation."); + let change = mutations[0]; + is(change.type, "childList", "Should be a childList."); + is(change.removed.length, 1, "Should have removed a child."); + let ownership = clientOwnershipTree(gWalker); + is(ownership.orphaned.length, 1, "Should have one orphaned subtree."); + is(ownershipTreeSize(ownership.orphaned[0]), 1 + 26 + 26, "Should have orphaned longlist, 26 children, and 26 singleTextChilds."); + } +})); + +// Orphan a node by moving it into the tree but out of our visible subtree, and clean it up. +addTest(mutationTest({ + autoCleanup: true, + load: ["#longlist div"], + moves: [ + ["#longlist", "#longlist-sibling"] + ], + postCheck: function(mutations) { + is(mutations.length, 1, "Should generate one mutation."); + let change = mutations[0]; + is(change.type, "childList", "Should be a childList."); + is(change.removed.length, 1, "Should have removed a child."); + let ownership = clientOwnershipTree(gWalker); + is(ownership.orphaned.length, 0, "Should have no orphaned subtrees."); + } +})); + + +addTest(function cleanup() { + delete gInspectee; + delete gWalker; + delete gClient; + runNextTest(); +}); + + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-mutations-events.html b/devtools/server/tests/mochitest/test_inspector-mutations-events.html new file mode 100644 index 000000000..992bc7f8d --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-mutations-events.html @@ -0,0 +1,183 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1157469 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1157469</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> + +window.onload = function() { + + const Cu = Components.utils; + const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + const {InspectorFront} = require("devtools/shared/fronts/inspector"); + + SimpleTest.waitForExplicitFinish(); + + let inspectee = null; + let inspector = null; + let walker = null; + let eventListener1 = function () {}; + let eventListener2 = function () {}; + let eventNode1; + let eventNode2; + let eventFront1; + let eventFront2; + + addAsyncTest(function* setup() { + info ("Setting up inspector and walker actors."); + let url = document.getElementById("inspectorContent").href; + + yield new Promise(resolve => { + attachURL(url, function(err, client, tab, doc) { + inspectee = doc; + inspector = InspectorFront(client, tab); + resolve(); + }); + }); + + walker = yield inspector.getWalker(); + ok(walker, "getWalker() should return an actor."); + + runNextTest(); + }); + + addAsyncTest(function* setupEventTest() { + eventNode1 = inspectee.querySelector("#a") + eventNode2 = inspectee.querySelector("#b") + + eventFront1 = yield walker.querySelector(walker.rootNode, "#a"); + eventFront2 = yield walker.querySelector(walker.rootNode, "#b"); + + runNextTest(); + }); + + addAsyncTest(function* testChangeEventListenerOnSingleNode() { + checkNodesHaveNoEventListener(); + + info("add event listener on a single node"); + eventNode1.addEventListener("click", eventListener1); + + let mutations = yield waitForMutations(); + is(mutations.length, 1, "one mutation expected"); + is(mutations[0].target, eventFront1, "mutation targets eventFront1"); + is(mutations[0].type, "events", "mutation type is events"); + is(mutations[0].hasEventListeners, true, "mutation target should have event listeners"); + is(eventFront1.hasEventListeners, true, "eventFront1 should have event listeners"); + + info("remove event listener on a single node"); + eventNode1.removeEventListener("click", eventListener1); + + mutations = yield waitForMutations(); + is(mutations.length, 1, "one mutation expected"); + is(mutations[0].target, eventFront1, "mutation targets eventFront1"); + is(mutations[0].type, "events", "mutation type is events"); + is(mutations[0].hasEventListeners, false, "mutation target should have no event listeners"); + is(eventFront1.hasEventListeners, false, "eventFront1 should have no event listeners"); + + info("perform several event listener changes on a single node") + eventNode1.addEventListener("click", eventListener1); + eventNode1.addEventListener("click", eventListener2); + eventNode1.removeEventListener("click", eventListener1); + eventNode1.removeEventListener("click", eventListener2); + + mutations = yield waitForMutations(); + is(mutations.length, 1, "one mutation expected"); + is(mutations[0].target, eventFront1, "mutation targets eventFront1"); + is(mutations[0].type, "events", "mutation type is events"); + is(mutations[0].hasEventListeners, false, "no event listener expected on mutation target"); + is(eventFront1.hasEventListeners, false, "no event listener expected on node"); + + runNextTest(); + }); + + addAsyncTest(function* testChangeEventsOnSeveralNodes() { + checkNodesHaveNoEventListener(); + + info("add event listeners on both nodes"); + eventNode1.addEventListener("click", eventListener1); + eventNode2.addEventListener("click", eventListener2); + + let mutations = yield waitForMutations(); + is(mutations.length, 2, "two mutations expected, one for each modified node"); + // first mutation + is(mutations[0].target, eventFront1, "first mutation targets eventFront1"); + is(mutations[0].type, "events", "mutation type is events"); + is(mutations[0].hasEventListeners, true, "mutation target should have event listeners"); + is(eventFront1.hasEventListeners, true, "eventFront1 should have event listeners"); + // second mutation + is(mutations[1].target, eventFront2, "second mutation targets eventFront2"); + is(mutations[1].type, "events", "mutation type is events"); + is(mutations[1].hasEventListeners, true, "mutation target should have event listeners"); + is(eventFront2.hasEventListeners, true, "eventFront1 should have event listeners"); + + info("remove event listeners on both nodes"); + eventNode1.removeEventListener("click", eventListener1); + eventNode2.removeEventListener("click", eventListener2); + + mutations = yield waitForMutations(); + is(mutations.length, 2, "one mutation registered for event listener change"); + // first mutation + is(mutations[0].target, eventFront1, "first mutation targets eventFront1"); + is(mutations[0].type, "events", "mutation type is events"); + is(mutations[0].hasEventListeners, false, "mutation target should have no event listeners"); + is(eventFront1.hasEventListeners, false, "eventFront2 should have no event listeners"); + // second mutation + is(mutations[1].target, eventFront2, "second mutation targets eventFront2"); + is(mutations[1].type, "events", "mutation type is events"); + is(mutations[1].hasEventListeners, false, "mutation target should have no event listeners"); + is(eventFront2.hasEventListeners, false, "eventFront2 should have no event listeners"); + + runNextTest(); + }); + + addAsyncTest(function* testRemoveMissingEvent() { + checkNodesHaveNoEventListener(); + + info("try to remove an event listener not previously added"); + eventNode1.removeEventListener("click", eventListener1); + + info("set any attribute on the node to trigger a mutation") + eventNode1.setAttribute("data-attr", "somevalue"); + + let mutations = yield waitForMutations(); + is(mutations.length, 1, "expect only one mutation"); + isnot(mutations.type, "events", "mutation type should not be events"); + + runNextTest(); + }); + + function checkNodesHaveNoEventListener() { + is(eventFront1.hasEventListeners, false, "eventFront1 hasEventListeners should be false"); + is(eventFront2.hasEventListeners, false, "eventFront2 hasEventListeners should be false"); + }; + + function waitForMutations() { + return new Promise(resolve => { + walker.once("mutations", mutations => { + resolve(mutations); + }); + }); + } + + runNextTest(); +} + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1157469">Mozilla Bug 1157469</a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-mutations-frameload.html b/devtools/server/tests/mochitest/test_inspector-mutations-frameload.html new file mode 100644 index 000000000..54966cea7 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-mutations-frameload.html @@ -0,0 +1,214 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gWalker = null; +var gClient = null; +var gChildFrame = null; +var gChildDocument = null; +var gCleanupConnection = null; + +function setup(callback) { + let url = document.getElementById("inspectorContent").href; + gCleanupConnection = attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + gClient = client; + gWalker = walker; + }).then(callback)); + }); +} + +function teardown() { + gWalker = null; + gClient = null; + gInspectee = null; + gChildFrame = null; + if (gCleanupConnection) { + gCleanupConnection(); + gCleanupConnection = null; + } +} + +function assertOwnership() { + return assertOwnershipTrees(gWalker); +} + +function loadChildSelector(selector) { + return gWalker.querySelector(gWalker.rootNode, "#childFrame").then(frame => { + ok(frame.numChildren > 0, "Child frame should consider its loaded document as a child."); + gChildFrame = frame; + return gWalker.children(frame); + }).then(children => { + return gWalker.querySelectorAll(children.nodes[0], selector); + }).then(nodeList => { + return nodeList.items(); + }); +} + +function getUnloadedDoc(mutations) { + for (let change of mutations) { + if (isUnload(change)) { + return change.target; + } + } + return null; +} + +addTest(function loadNewChild() { + setup(() => { + let beforeUnloadSize = 0; + // Load a bunch of fronts for actors inside the child frame. + promiseDone(loadChildSelector("#longlist div").then(() => { + let childFrame = gInspectee.querySelector("#childFrame"); + childFrame.src = "data:text/html,<html>new child</html>"; + return waitForMutation(gWalker, isChildList); + }).then(mutations => { + let unloaded = getUnloadedDoc(mutations); + mutations = assertSrcChange(mutations); + mutations = assertUnload(mutations); + mutations = assertFrameLoad(mutations); + mutations = assertChildList(mutations); + + is(mutations.length, 0, "Got the expected mutations."); + + assertOwnership(); + + return checkMissing(gClient, unloaded); + }).then(() => { + teardown(); + }).then(runNextTest)); + }); +}); + +addTest(function loadNewChildTwice() { + setup(() => { + let beforeUnloadSize = 0; + // Load a bunch of fronts for actors inside the child frame. + promiseDone(loadChildSelector("#longlist div").then(() => { + let childFrame = gInspectee.querySelector("#childFrame"); + childFrame.src = "data:text/html,<html>new child</html>"; + return waitForMutation(gWalker, isChildList); + }).then(mutations => { + // The first load went through as expected (as tested in loadNewChild) + // Now change the source again, but this time we *don't* expect + // an unload, because we haven't seen the new child document yet. + let childFrame = gInspectee.querySelector("#childFrame"); + childFrame.src = "data:text/html,<html>second new child</html>"; + return waitForMutation(gWalker, isChildList); + }).then(mutations => { + mutations = assertSrcChange(mutations); + mutations = assertFrameLoad(mutations); + mutations = assertChildList(mutations); + ok(!getUnloadedDoc(mutations), "Should not have gotten an unload."); + + is(mutations.length, 0, "Got the expected mutations."); + + assertOwnership(); + }).then(() => { + teardown(); + }).then(runNextTest)); + }); +}); + + +addTest(function loadNewChildTwiceAndCareAboutIt() { + setup(() => { + let beforeUnloadSize = 0; + // Load a bunch of fronts for actors inside the child frame. + promiseDone(loadChildSelector("#longlist div").then(() => { + let childFrame = gInspectee.querySelector("#childFrame"); + childFrame.src = "data:text/html,<html>new child</html>"; + return waitForMutation(gWalker, isChildList); + }).then(mutations => { + // Read the new child + return loadChildSelector("#longlist div"); + }).then(() => { + // Now change the source again, and expect the same results as loadNewChild. + let childFrame = gInspectee.querySelector("#childFrame"); + childFrame.src = "data:text/html,<html>second new child</html>"; + return waitForMutation(gWalker, isChildList); + }).then(mutations => { + let unloaded = getUnloadedDoc(mutations); + + mutations = assertSrcChange(mutations); + mutations = assertUnload(mutations); + mutations = assertFrameLoad(mutations); + mutations = assertChildList(mutations); + + is(mutations.length, 0, "Got the expected mutations."); + + assertOwnership(); + + return checkMissing(gClient, unloaded); + }).then(() => { + teardown(); + }).then(runNextTest)); + }); +}); + +addTest(function testBack() { + setup(() => { + let beforeUnloadSize = 0; + // Load a bunch of fronts for actors inside the child frame. + promiseDone(loadChildSelector("#longlist div").then(() => { + let childFrame = gInspectee.querySelector("#childFrame"); + childFrame.src = "data:text/html,<html>new child</html>"; + return waitForMutation(gWalker, isChildList); + }).then(mutations => { + // Read the new child + return loadChildSelector("#longlist div"); + }).then(() => { + // Now use history.back to change the source, and expect the same results as loadNewChild. + let childFrame = gInspectee.querySelector("#childFrame"); + childFrame.contentWindow.history.back(); + return waitForMutation(gWalker, isChildList); + }).then(mutations => { + let unloaded = getUnloadedDoc(mutations); + mutations = assertSrcChange(mutations); + mutations = assertUnload(mutations); + mutations = assertFrameLoad(mutations); + mutations = assertChildList(mutations); + is(mutations.length, 0, "Got the expected mutations."); + + assertOwnership(); + + return checkMissing(gClient, unloaded); + }).then(() => { + teardown(); + }).then(runNextTest)); + }); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-mutations-value.html b/devtools/server/tests/mochitest/test_inspector-mutations-value.html new file mode 100644 index 000000000..352526b13 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-mutations-value.html @@ -0,0 +1,168 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/server/actors/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +const testSummaryLength = 10; +inspector.setValueSummaryLength(testSummaryLength); +SimpleTest.registerCleanupFunction(function() { + inspector.setValueSummaryLength(inspector.DEFAULT_VALUE_SUMMARY_LENGTH); +}); + +var gInspectee = null; +var gWalker = null; +var gClient = null; +var valueNode; +var valueFront; +var longStringFront; +var longString = "stringstringstringstringstringstringstringstringstringstringstring"; +var truncatedLongString = longString.substring(0, testSummaryLength); +var shortString = "str"; +var shortString2 = "str2"; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(setupValueTest); +addTest(testKeepLongValue); +addTest(testSetShortValue); +addTest(testKeepShortValue); +addTest(testSetLongValue); +addTest(setupFrameValueTest); +addTest(testKeepLongValue); +addTest(testSetShortValue); +addTest(testKeepShortValue); +addTest(testSetLongValue); + +function setupValueTest() { + valueNode = gInspectee.querySelector("#longstring").firstChild; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#longstring").then(node => { + longStringFront = node; + return gWalker.children(node); + }).then(children => { + valueFront = children.nodes[0]; + }).then(runNextTest)); +} + +function setupFrameValueTest() { + let frame = gInspectee.querySelector('#childFrame'); + valueNode = frame.contentDocument.querySelector("#longstring").firstChild; + + promiseDone(gWalker.querySelector(gWalker.rootNode, "#childFrame").then(childFrame => { + return gWalker.children(childFrame); + }).then(children => { + let nodes = children.nodes; + is(nodes.length, 1, "There should be only one child of the iframe"); + is(nodes[0].nodeType, Node.DOCUMENT_NODE, "iframe child should be a document node"); + return gWalker.querySelector(nodes[0], "#longstring"); + }).then(node => { + longStringFront = node; + return gWalker.children(node); + }).then(children => { + valueFront = children.nodes[0]; + }).then(runNextTest)); +} + +function checkNodeFrontValue(front, expectedValue) { + return front.getNodeValue().then(longstring => { + return longstring.string(); + }).then(str => { + is(str, expectedValue, "Node value is as expected"); + }) +} + +function testKeepLongValue() { + // After first setup we should have a long string in the node + ok(!longStringFront.inlineTextChild, "Text node is too long to be inlined."); + + valueNode.nodeValue = longString; + gWalker.once("mutations", (changes) => { + ok(!longStringFront.inlineTextChild, "Text node is too long to be inlined."); + ok(!changes.some(change => change.type === "inlineTextChild"), + "No inline text child mutation was fired."); + checkNodeFrontValue(valueFront, longString).then(runNextTest); + }); +} + +function testSetShortValue() { + ok(!longStringFront.inlineTextChild, "Text node is too long to be inlined."); + + valueNode.nodeValue = shortString; + gWalker.once("mutations", (changes) => { + ok(!!longStringFront.inlineTextChild, "Text node is short enough to be inlined."); + ok(changes.some(change => change.type === "inlineTextChild"), + "An inlineTextChild mutation was fired."); + checkNodeFrontValue(valueFront, shortString).then(runNextTest); + }); +} + +function testKeepShortValue() { + ok(!!longStringFront.inlineTextChild, "Text node is short enough to be inlined."); + + valueNode.nodeValue = shortString2; + gWalker.once("mutations", (changes) => { + ok(!!longStringFront.inlineTextChild, "Text node is short enough to be inlined."); + ok(!changes.some(change => change.type === "inlineTextChild"), + "No inline text child mutation was fired."); + checkNodeFrontValue(valueFront, shortString2).then(runNextTest); + }); +} + +function testSetLongValue() { + ok(!!longStringFront.inlineTextChild, "Text node is short enough to be inlined."); + + valueNode.nodeValue = longString; + gWalker.once("mutations", (changes) => { + ok(!longStringFront.inlineTextChild, "Text node is too long to be inlined."); + ok(changes.some(change => change.type === "inlineTextChild"), + "An inlineTextChild mutation was fired."); + checkNodeFrontValue(valueFront, longString).then(runNextTest); + }); +} + +addTest(function cleanup() { + delete gInspectee; + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-pick-color.html b/devtools/server/tests/mochitest/test_inspector-pick-color.html new file mode 100644 index 000000000..48ad08468 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-pick-color.html @@ -0,0 +1,101 @@ +<!DOCTYPE HTML>
+<html>
+<!--
+Test that the inspector actor has the pickColorFromPage and cancelPickColorFromPage
+methods and that when a color is picked the color-picked event is emitted and that when
+the eyedropper is dimissed, the color-pick-canceled event is emitted.
+https://bugzilla.mozilla.org/show_bug.cgi?id=1262439
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1262439</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">
+ <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
+ <script type="application/javascript;version=1.8">
+window.onload = function() {
+ const Cu = Components.utils;
+ Cu.import("resource://devtools/shared/Loader.jsm");
+ const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+ const {InspectorFront} = devtools.require("devtools/shared/fronts/inspector");
+ const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
+
+ SimpleTest.waitForExplicitFinish();
+
+ let win = null;
+ let inspector = null;
+
+ addAsyncTest(function*() {
+ info("Setting up inspector actor");
+
+ let url = document.getElementById("inspectorContent").href;
+
+ yield new Promise(resolve => {
+ attachURL(url, function(err, client, tab, doc) {
+ win = doc.defaultView;
+ inspector = InspectorFront(client, tab);
+ resolve();
+ });
+ });
+
+ runNextTest();
+ });
+
+ addAsyncTest(function*() {
+ info("Start picking a color from the page");
+ yield inspector.pickColorFromPage();
+
+ info("Click in the page and make sure a color-picked event is received");
+ let onColorPicked = waitForEvent("color-picked");
+ win.document.body.click();
+ let color = yield onColorPicked;
+
+ is(color, "#000000", "The color-picked event was received with the right color");
+
+ runNextTest();
+ });
+
+ addAsyncTest(function*() {
+ info("Start picking a color from the page");
+ yield inspector.pickColorFromPage();
+
+ info("Use the escape key to dismiss the eyedropper");
+ let onPickCanceled = waitForEvent("color-pick-canceled");
+
+ let keyboardEvent = win.document.createEvent("KeyboardEvent");
+ keyboardEvent.initKeyEvent("keydown", true, true, win, false, false,
+ false, false, 27, 0);
+ win.document.dispatchEvent(keyboardEvent);
+
+ yield onPickCanceled;
+ ok(true, "The color-pick-canceled event was received");
+
+ runNextTest();
+ });
+
+ addAsyncTest(function*() {
+ info("Start picking a color from the page");
+ yield inspector.pickColorFromPage();
+
+ info("And cancel the color picking");
+ yield inspector.cancelPickColorFromPage();
+
+ runNextTest();
+ });
+
+ function waitForEvent(name) {
+ return new Promise(resolve => inspector.once(name, resolve));
+ }
+
+ runNextTest();
+};
+ </script>
+</head>
+<body>
+<a id="inspectorContent" target="_blank" href="inspector-eyedropper.html">Test Document</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html b/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html new file mode 100644 index 000000000..64bb03f80 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html @@ -0,0 +1,174 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); +const DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"]. + getService(Components.interfaces.inIDOMUtils); + +const KNOWN_PSEUDOCLASSES = [':hover', ':active', ':focus'] + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gWalker = null; +var gClient = null; +var gCleanupConnection = null; + +function setup(callback) { + let url = document.getElementById("inspectorContent").href; + gCleanupConnection = attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + gClient = client; + gWalker = walker; + }).then(callback)); + }); +} + +function teardown() { + gWalker = null; + gClient = null; + gInspectee = null; + if (gCleanupConnection) { + gCleanupConnection(); + gCleanupConnection = null; + } +} + +function checkChange(change, expectation) { + is(change.type, "pseudoClassLock", "Expect a pseudoclass lock change."); + let target = change.target; + if (expectation.id) + is(target.id, expectation.id, "Expect a change on node id " + expectation.id); + if (expectation.nodeName) + is(target.nodeName, expectation.nodeName, "Expect a change on node name " + expectation.nodeName); + + is(target.pseudoClassLocks.length, expectation.pseudos.length, + "Expect " + expectation.pseudos.length + " pseudoclass locks."); + for (let pseudo of expectation.pseudos) { + ok(target.hasPseudoClassLock(pseudo), "Expect lock: " + pseudo); + ok(DOMUtils.hasPseudoClassLock(target.rawNode(), pseudo), "Expect lock in dom: " + pseudo); + } + + for (let pseudo of KNOWN_PSEUDOCLASSES) { + if (!expectation.pseudos.some(expected => pseudo === expected)) { + ok(!target.hasPseudoClassLock(pseudo), "Don't expect lock: " + pseudo); + ok(!DOMUtils.hasPseudoClassLock(target.rawNode(), pseudo), "Don't expect lock in dom: " + pseudo); + + } + } +} + +function checkMutations(mutations, expectations) { + is(mutations.length, expectations.length, "Should get the right number of mutations."); + for (let i = 0; i < mutations.length; i++) { + checkChange(mutations[i] , expectations[i]); + } +} + +addTest(function testPseudoClassLock() { + let contentNode; + let nodeFront; + setup(() => { + contentNode = gInspectee.querySelector("#b"); + return promiseDone(gWalker.querySelector(gWalker.rootNode, "#b").then(front => { + nodeFront = front; + // Lock the pseudoclass alone, no parents. + gWalker.addPseudoClassLock(nodeFront, ':active'); + // Expect a single pseudoClassLock mutation. + return promiseOnce(gWalker, "mutations"); + }).then(mutations => { + is(mutations.length, 1, "Should get one mutations"); + is(mutations[0].target, nodeFront, "Should be the node we tried to apply to"); + checkChange(mutations[0], { + id: "b", + nodeName: "DIV", + pseudos: [":active"] + }); + }).then(() => { + // Now add :hover, this time with parents. + gWalker.addPseudoClassLock(nodeFront, ':hover', {parents: true}); + return promiseOnce(gWalker, "mutations"); + }).then(mutations => { + let expectedMutations = [{ + id: 'b', + nodeName: 'DIV', + pseudos: [':hover', ':active'], + }, + { + id: 'longlist', + nodeName: 'DIV', + pseudos: [':hover'] + }, + { + nodeName: 'BODY', + pseudos: [':hover'] + }, + { + nodeName: 'HTML', + pseudos: [':hover'] + }]; + checkMutations(mutations, expectedMutations); + }).then(() => { + // Now remove the :hover on all parents + gWalker.removePseudoClassLock(nodeFront, ':hover', {parents: true}); + return promiseOnce(gWalker, "mutations"); + }).then(mutations => { + let expectedMutations = [{ + id: 'b', + nodeName: 'DIV', + // Should still have :active on the original node. + pseudos: [':active'] + }, + { + id: 'longlist', + nodeName: 'DIV', + pseudos: [] + }, + { + nodeName: 'BODY', + pseudos: [] + }, + { + nodeName: 'HTML', + pseudos: [] + }]; + checkMutations(mutations, expectedMutations); + }).then(() => { + // Now shut down the walker and make sure that clears up the remaining lock. + return gWalker.release(); + }).then(() => { + ok(!DOMUtils.hasPseudoClassLock(contentNode, ':active'), "Pseudoclass should have been removed during destruction."); + teardown(); + }).then(runNextTest)); + }); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-release.html b/devtools/server/tests/mochitest/test_inspector-release.html new file mode 100644 index 000000000..45412bef0 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-release.html @@ -0,0 +1,102 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gClient = null; + +function assertOwnership() { + return assertOwnershipTrees(gWalker); +} + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(function testReleaseSubtree() { + let originalOwnershipSize = 0; + let longlist = null; + let firstChild = null; + promiseDone(gWalker.querySelectorAll(gWalker.rootNode, "#longlist div").then(list => { + // Make sure we have the 26 children of longlist in our ownership tree. + is(list.length, 26, "Expect 26 div children."); + // Make sure we've read in all those children and incorporated them in our ownership tree. + return list.items(); + }).then((items)=> { + originalOwnershipSize = assertOwnership(); + + // Here is how the ownership tree is summed up: + // #document 1 + // <html> 1 + // <body> 1 + // <div id=longlist> 1 + // <div id=a>a</div> 26*2 (each child plus it's singleTextChild) + // ... + // <div id=z>z</div> + // ----- + // 56 + is(originalOwnershipSize, 56, "Correct number of items in ownership tree"); + firstChild = items[0].actorID; + }).then(() => { + // Now get the longlist and release it from the ownership tree. + return gWalker.querySelector(gWalker.rootNode, "#longlist"); + }).then(node => { + longlist = node.actorID; + return gWalker.releaseNode(node); + }).then(() => { + // Our ownership size should now be 53 fewer (we forgot about #longlist + 26 children + 26 singleTextChild nodes) + let newOwnershipSize = assertOwnership(); + is(newOwnershipSize, originalOwnershipSize - 53, + "Ownership tree should be lower"); + // Now verify that some nodes have gone away + return checkMissing(gClient, longlist); + }).then(() => { + return checkMissing(gClient, firstChild); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gWalker; + delete gClient; + runNextTest(); +}); + + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-reload.html b/devtools/server/tests/mochitest/test_inspector-reload.html new file mode 100644 index 000000000..91252aa8f --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-reload.html @@ -0,0 +1,80 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gClient = null; +var gWalker = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + return inspector.getWalker(); + }).then(walker => { + dump(walker.actorID + "\n"); + ok(walker === gWalker, "getWalker() twice should return the same walker."); + }).then(runNextTest)); + }); +}); + +addTest(function testReload() { + let nodeFront; + let oldRootID = gWalker.rootNode.actorID; + // Load a node to populate the tree a bit. + promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(front => { + gInspectee.defaultView.location.reload(); + return waitForMutation(gWalker, isNewRoot); + }).then(() => { + ok(gWalker.rootNode.actorID != oldRootID, "Root node should have changed."); + }).then(() => { + // Make sure we can still access the document + return gWalker.querySelector(gWalker.rootNode, "#a"); + }).then(front => { + ok(front.actorID, "Got a new actor ID"); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gWalker; + delete gInspectee; + delete gClient; + runNextTest(); +}); + + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-remove.html b/devtools/server/tests/mochitest/test_inspector-remove.html new file mode 100644 index 000000000..2331c3e30 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-remove.html @@ -0,0 +1,117 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gClient = null; + +function assertOwnership() { + return assertOwnershipTrees(gWalker); +} + +function ignoreNode(node) { + // Duplicate the walker logic to skip blank nodes... + return node.nodeType === Components.interfaces.nsIDOMNode.TEXT_NODE && + !/[^\s]/.test(node.nodeValue); +} + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(function testRemoveSubtree() { + let originalOwnershipSize = 0; + let longlist = null; + let longlistID = null; + + let nextSibling = gInspectee.querySelector("#longlist").nextSibling; + while (nextSibling && ignoreNode(nextSibling)) { + nextSibling = nextSibling.nextSibling; + } + + let previousSibling = gInspectee.querySelector("#longlist").previousSibling; + while (previousSibling && ignoreNode(previousSibling)) { + previousSibling = previousSibling.previousSibling; + } + + promiseDone(gWalker.querySelector(gWalker.rootNode, "#longlist").then(listFront => { + longlist = listFront; + longlistID = longlist.actorID; + }).then(() => { + return gWalker.children(longlist); + }).then((items)=> { + originalOwnershipSize = assertOwnership(); + // Here is how the ownership tree is summed up: + // #document 1 + // <html> 1 + // <body> 1 + // <div id=longlist> 1 + // <div id=a>a</div> 26*2 (each child plus it's singleTextChild) + // ... + // <div id=z>z</div> + // ----- + // 56 + is(originalOwnershipSize, 56, "Correct number of items in ownership tree"); + return gWalker.removeNode(longlist); + }).then(siblings => { + is(siblings.previousSibling.rawNode(), previousSibling, "Should have returned the previous sibling."); + is(siblings.nextSibling.rawNode(), nextSibling, "Should have returned the next sibling."); + return waitForMutation(gWalker, isChildList); + }).then(() => { + // Our ownership size should now be 51 fewer (we forgot about #longlist + 26 + // children + 26 singleTextChild nodes, but learned about #longlist's + // prev/next sibling) + let newOwnershipSize = assertOwnership(); + is(newOwnershipSize, originalOwnershipSize - 51, + "Ownership tree should be lower"); + // Now verify that some nodes have gone away + return checkMissing(gClient, longlistID); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gWalker; + delete gClient; + runNextTest(); +}); + + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-resize.html b/devtools/server/tests/mochitest/test_inspector-resize.html new file mode 100644 index 000000000..eafa6436c --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-resize.html @@ -0,0 +1,77 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test that the inspector actor emits "resize" events when the page is resized. +https://bugzilla.mozilla.org/show_bug.cgi?id=1222409 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1222409</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +window.onload = function() { + const Cu = Components.utils; + const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + const promise = require("promise"); + const {InspectorFront} = require("devtools/shared/fronts/inspector"); + const {console} = Cu.import("resource://gre/modules/Console.jsm", {}); + + SimpleTest.waitForExplicitFinish(); + + let win = null; + let inspector = null; + + addAsyncTest(function* setup() { + info ("Setting up inspector and walker actors."); + + let url = document.getElementById("inspectorContent").href; + + yield new promise(resolve => { + attachURL(url, function(err, client, tab, doc) { + win = doc.defaultView; + inspector = InspectorFront(client, tab); + resolve(); + }); + }); + + runNextTest(); + }); + + addAsyncTest(function*() { + let walker = yield inspector.getWalker(); + + // We can't receive events from the walker if we haven't first executed a + // method on the actor to initialize it. + yield walker.querySelector(walker.rootNode, "img"); + + let {outerWidth, outerHeight} = win; + let onResize = new promise(resolve => { + walker.once("resize", () => { + resolve(); + }); + }); + win.resizeTo(800, 600); + yield onResize; + + ok(true, "The resize event was emitted"); + win.resizeTo(outerWidth, outerHeight); + + runNextTest(); + }); + + runNextTest(); +}; + </script> +</head> +<body> +<a id="inspectorContent" target="_blank" href="inspector-search-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-resolve-url.html b/devtools/server/tests/mochitest/test_inspector-resolve-url.html new file mode 100644 index 000000000..1494739ed --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-resolve-url.html @@ -0,0 +1,88 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=921102 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 921102</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspector; +var gDoc; + +addTest(function() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gDoc = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + gInspector = InspectorFront(client, tab); + runNextTest(); + }); +}); + +addTest(function() { + info("Resolve a relative URL without providing a context node"); + gInspector.resolveRelativeURL("test.png?id=4#wow").then(url => { + is(url, "chrome://mochitests/content/chrome/devtools/server/tests/" + + "mochitest/test.png?id=4#wow"); + runNextTest(); + }); +}); + +addTest(function() { + info("Resolve an absolute URL without providing a context node"); + gInspector.resolveRelativeURL("chrome://mochitests/content/chrome/" + + "devtools/server/").then(url => { + is(url, "chrome://mochitests/content/chrome/devtools/server/"); + runNextTest(); + }); +}); + +addTest(function() { + info("Resolve a relative URL providing a context node"); + let node = gDoc.querySelector(".big-horizontal"); + gInspector.resolveRelativeURL("test.png?id=4#wow", node).then(url => { + is(url, "chrome://mochitests/content/chrome/devtools/server/tests/" + + "mochitest/test.png?id=4#wow"); + runNextTest(); + }); +}); + +addTest(function() { + info("Resolve an absolute URL providing a context node"); + let node = gDoc.querySelector(".big-horizontal"); + gInspector.resolveRelativeURL("chrome://mochitests/content/chrome/" + + "devtools/server/", node).then(url => { + is(url, "chrome://mochitests/content/chrome/devtools/server/"); + runNextTest(); + }); +}); + +addTest(function() { + gInspector = gDoc = null; + runNextTest(); +}); + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=921102">Mozilla Bug 921102</a> +<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-retain.html b/devtools/server/tests/mochitest/test_inspector-retain.html new file mode 100644 index 000000000..e8342cf67 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-retain.html @@ -0,0 +1,180 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gClient = null; +var gInspectee = null; + +function assertOwnership() { + return assertOwnershipTrees(gWalker); +} + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +// Retain a node, and a second-order child (in another document, for kicks) +// Release the parent of the top item, which should cause one retained orphan. + +// Then unretain the top node, which should retain the orphan. + +// Then change the source of the iframe, which should kill that orphan. + +addTest(function testRetain() { + let originalOwnershipSize = 0; + let bodyFront = null; + let frameFront = null; + let childListFront = null; + // Get the toplevel body element and retain it. + promiseDone(gWalker.querySelector(gWalker.rootNode, "body").then(front => { + bodyFront = front; + return gWalker.retainNode(bodyFront); + }).then(() => { + // Get an element in the child frame and retain it. + return gWalker.querySelector(gWalker.rootNode, "#childFrame"); + }).then(frame => { + frameFront = frame; + return gWalker.children(frame, { maxNodes: 1 }).then(children => { + return children.nodes[0]; + }); + }).then(childDoc => { + return gWalker.querySelector(childDoc, "#longlist"); + }).then(list => { + childListFront = list; + originalOwnershipSize = assertOwnership(); + // and rtain it. + return gWalker.retainNode(childListFront); + }).then(() => { + // OK, try releasing the parent of the first retained. + return gWalker.releaseNode(bodyFront.parentNode()); + }).then(() => { + let size = assertOwnership(); + let clientTree = clientOwnershipTree(gWalker); + + // That request should have freed the parent of the first retained + // but moved the rest into the retained orphaned tree. + is(ownershipTreeSize(clientTree.root) + ownershipTreeSize(clientTree.retained[0]) + 1, + originalOwnershipSize, + "Should have only lost one item overall."); + is(gWalker._retainedOrphans.size, 1, "Should have retained one orphan"); + ok(gWalker._retainedOrphans.has(bodyFront), "Should have retained the expected node."); + }).then(() => { + // Unretain the body, which should promote the childListFront to a retained orphan. + return gWalker.unretainNode(bodyFront); + }).then(() => { + assertOwnership(); + let clientTree = clientOwnershipTree(gWalker); + + is(gWalker._retainedOrphans.size, 1, "Should still only have one retained orphan."); + ok(!gWalker._retainedOrphans.has(bodyFront), "Should have dropped the body node.") + ok(gWalker._retainedOrphans.has(childListFront), "Should have retained the child node.") + }).then(() => { + // Change the source of the iframe, which should kill the retained orphan. + gInspectee.querySelector("#childFrame").src = "data:text/html,<html>new child</html>"; + return waitForMutation(gWalker, isUnretained); + }).then(mutations => { + assertOwnership(); + let clientTree = clientOwnershipTree(gWalker); + is(gWalker._retainedOrphans.size, 0, "Should have no more retained orphans."); + + }).then(runNextTest)); +}); + +// Get a hold of a node, remove it from the doc and retain it at the same time. +// We should always win that race (even though the mutation happens before the +// retain request), because we haven't issued `getMutations` yet. +addTest(function testWinRace() { + let front = null; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(node => { + front = node; + let contentNode = gInspectee.querySelector("#a"); + contentNode.parentNode.removeChild(contentNode); + // Now wait for that mutation and retain response to come in. + return promise.all([ + gWalker.retainNode(front), + waitForMutation(gWalker, isChildList) + ]); + }).then(() => { + assertOwnership(); + let clientTree = clientOwnershipTree(gWalker); + is(gWalker._retainedOrphans.size, 1, "Should have a retained orphan."); + ok(gWalker._retainedOrphans.has(front), "Should have retained our expected node."); + return gWalker.unretainNode(front); + }).then(() => { + // Make sure we're clear for the next test. + assertOwnership(); + let clientTree = clientOwnershipTree(gWalker); + is(gWalker._retainedOrphans.size, 0, "Should have no more retained orphans."); + }).then(runNextTest)); +}); + +// Same as above, but issue the request right after the 'new-mutations' event, so that +// we *lose* the race. +addTest(function testLoseRace() { + let front = null; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#z").then(node => { + front = node; + gInspectee.querySelector("#z").parentNode = null; + let contentNode = gInspectee.querySelector("#a"); + contentNode.parentNode.removeChild(contentNode); + return promiseOnce(gWalker, "new-mutations"); + }).then(() => { + // Verify that we have an outstanding request (no good way to tell that it's a + // getMutations request, but there's nothing else it would be). + is(gWalker._requests.length, 1, "Should have an outstanding request."); + return gWalker.retainNode(front) + }).then(() => { ok(false, "Request should not have succeeded!"); }, + (err) => { + ok(err, "noSuchActor", "Should have lost the race."); + let clientTree = clientOwnershipTree(gWalker); + is(gWalker._retainedOrphans.size, 0, "Should have no more retained orphans."); + // Don't re-throw the error. + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-scroll-into-view.html b/devtools/server/tests/mochitest/test_inspector-scroll-into-view.html new file mode 100644 index 000000000..1e164e83d --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-scroll-into-view.html @@ -0,0 +1,87 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=901250 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 901250</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gClient = null; +var gWalker = null; + +function assertOwnership() { + assertOwnershipTrees(gWalker); +} + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(Task.async(function* testScrollIntoView() { + let id = "#scroll-into-view"; + let rect = gInspectee.querySelector(id).getBoundingClientRect(); + let nodeFront = yield gWalker.querySelector(gWalker.rootNode, id); + let inViewport = rect.x >= 0 && + rect.y >= 0 && + rect.y <= gInspectee.defaultView.innerHeight && + rect.x <= gInspectee.defaultView.innerWidth; + + ok(!inViewport, "Element is not in viewport."); + + yield nodeFront.scrollIntoView(); + + SimpleTest.executeSoon(() => { + rect = gInspectee.querySelector(id).getBoundingClientRect(); + inViewport = rect.x >= 0 && + rect.y >= 0 && + rect.y <= gInspectee.defaultView.innerHeight && + rect.x <= gInspectee.defaultView.innerWidth; + + ok(inViewport, "Element is in viewport."); + + runNextTest(); + }); +})); + +addTest(function cleanup() { + delete gWalker; + delete gInspectee; + delete gClient; + runNextTest(); +}); + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=901250">Mozilla Bug 901250</a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-search-front.html b/devtools/server/tests/mochitest/test_inspector-search-front.html new file mode 100644 index 000000000..e0f8f77e8 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-search-front.html @@ -0,0 +1,217 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=835896 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 835896</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +window.onload = function() { + const Cu = Components.utils; + const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + const promise = require("promise"); + const {InspectorFront} = require("devtools/shared/fronts/inspector"); + const {console} = Cu.import("resource://gre/modules/Console.jsm", {}); + + SimpleTest.waitForExplicitFinish(); + + let walkerFront = null; + let inspectee = null; + let inspector = null; + + // WalkerFront specific tests. These aren't to excercise search + // edge cases so much as to test the state the Front maintains between + // searches. + // See also test_inspector-search.html + + addAsyncTest(function* setup() { + info ("Setting up inspector and walker actors."); + + let url = document.getElementById("inspectorContent").href; + + yield new promise(resolve => { + attachURL(url, function(err, client, tab, doc) { + inspectee = doc; + inspector = InspectorFront(client, tab); + resolve(); + }); + }); + + walkerFront = yield inspector.getWalker(); + ok(walkerFront, "getWalker() should return an actor."); + + runNextTest(); + }); + + addAsyncTest(function* testWalkerFrontDefaults() { + info ("Testing search API using WalkerFront."); + let nodes = yield walkerFront.querySelectorAll(walkerFront.rootNode, "h2"); + let fronts = yield nodes.items(); + + let frontResult = yield walkerFront.search(""); + ok(!frontResult, "Null result on front when searching for ''"); + + let results = yield walkerFront.search("h2"); + isDeeply(results, { + node: fronts[0], + type: "search", + resultsIndex: 0, + resultsLength: 3 + }, "Default options work"); + + results = yield walkerFront.search("h2", { }); + isDeeply(results, { + node: fronts[1], + type: "search", + resultsIndex: 1, + resultsLength: 3 + }, "Search works with empty options"); + + // Clear search data to remove result state on the front + yield walkerFront.search(""); + runNextTest(); + }); + + addAsyncTest(function* testMultipleSearches() { + info ("Testing search API using WalkerFront (reverse=false)"); + let nodes = yield walkerFront.querySelectorAll(walkerFront.rootNode, "h2"); + let fronts = yield nodes.items(); + + let results = yield walkerFront.search("h2"); + isDeeply(results, { + node: fronts[0], + type: "search", + resultsIndex: 0, + resultsLength: 3 + }, "Search works with multiple results (reverse=false)"); + + results = yield walkerFront.search("h2"); + isDeeply(results, { + node: fronts[1], + type: "search", + resultsIndex: 1, + resultsLength: 3 + }, "Search works with multiple results (reverse=false)"); + + results = yield walkerFront.search("h2"); + isDeeply(results, { + node: fronts[2], + type: "search", + resultsIndex: 2, + resultsLength: 3 + }, "Search works with multiple results (reverse=false)"); + + results = yield walkerFront.search("h2"); + isDeeply(results, { + node: fronts[0], + type: "search", + resultsIndex: 0, + resultsLength: 3 + }, "Search works with multiple results (reverse=false)"); + + // Clear search data to remove result state on the front + yield walkerFront.search(""); + runNextTest(); + }); + + addAsyncTest(function* testMultipleSearchesReverse() { + info ("Testing search API using WalkerFront (reverse=true)"); + let nodes = yield walkerFront.querySelectorAll(walkerFront.rootNode, "h2"); + let fronts = yield nodes.items(); + + let results = yield walkerFront.search("h2", {reverse: true}); + isDeeply(results, { + node: fronts[2], + type: "search", + resultsIndex: 2, + resultsLength: 3 + }, "Search works with multiple results (reverse=true)"); + + results = yield walkerFront.search("h2", {reverse: true}); + isDeeply(results, { + node: fronts[1], + type: "search", + resultsIndex: 1, + resultsLength: 3 + }, "Search works with multiple results (reverse=true)"); + + results = yield walkerFront.search("h2", {reverse: true}); + isDeeply(results, { + node: fronts[0], + type: "search", + resultsIndex: 0, + resultsLength: 3 + }, "Search works with multiple results (reverse=true)"); + + results = yield walkerFront.search("h2", {reverse: true}); + isDeeply(results, { + node: fronts[2], + type: "search", + resultsIndex: 2, + resultsLength: 3 + }, "Search works with multiple results (reverse=true)"); + + results = yield walkerFront.search("h2", {reverse: false}); + isDeeply(results, { + node: fronts[0], + type: "search", + resultsIndex: 0, + resultsLength: 3 + }, "Search works with multiple results (reverse=false)"); + + // Clear search data to remove result state on the front + yield walkerFront.search(""); + runNextTest(); + }); + + + addAsyncTest(function* testBackwardsCompat() { + info ("Simulating a server that doesn't have the new search functionality."); + walkerFront.traits.textSearch = false; + let front = yield walkerFront.querySelector(walkerFront.rootNode, "h1"); + + let results = yield walkerFront.search("h1"); + isDeeply(results, { + node: front, + type: "selector", + resultsIndex: 0, + resultsLength: 1 + }, "Only querySelectorAll results being returned"); + + // Clear search data to remove result state on the front + yield walkerFront.search(""); + + // Reset the normal textSearch behavior + walkerFront.traits.textSearch = true; + + results = yield walkerFront.search("h1"); + isDeeply(results, { + node: front, + type: "search", + resultsIndex: 0, + resultsLength: 3 + }, "Other results being included"); + + // Clear search data to remove result state on the front + yield walkerFront.search(""); + runNextTest(); + }); + + runNextTest(); +}; + </script> +</head> +<body> +<a id="inspectorContent" target="_blank" href="inspector-search-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-search.html b/devtools/server/tests/mochitest/test_inspector-search.html new file mode 100644 index 000000000..623d3018d --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-search.html @@ -0,0 +1,296 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=835896 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 835896</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +window.onload = function() { + const Cu = Components.utils; + const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + const promise = require("promise"); + const {InspectorFront} = require("devtools/shared/fronts/inspector"); + const {WalkerSearch, WalkerIndex} = + require("devtools/server/actors/utils/walker-search"); + const {console} = Cu.import("resource://gre/modules/Console.jsm", {}); + + SimpleTest.waitForExplicitFinish(); + + let walkerActor = null; + let walkerSearch = null; + let inspectee = null; + let inspector = null; + + // WalkerSearch specific tests. This is to make sure search results are + // coming back as expected. + // See also test_inspector-search-front.html. + + addAsyncTest(function* setup() { + info ("Setting up inspector and walker actors."); + + let url = document.getElementById("inspectorContent").href; + + yield new promise(resolve => { + attachURL(url, function(err, client, tab, doc) { + inspectee = doc; + inspector = InspectorFront(client, tab); + resolve(); + }); + }); + + let walkerFront = yield inspector.getWalker(); + ok(walkerFront, "getWalker() should return an actor."); + + walkerActor = DebuggerServer._searchAllConnectionsForActor(walkerFront.actorID); + ok(walkerActor, + "Got a reference to the walker actor (" + walkerFront.actorID + ")"); + + walkerSearch = walkerActor.walkerSearch; + + runNextTest(); + }); + + addAsyncTest(function* testIndexExists() { + info ("Testing basic index APIs exist."); + + let index = new WalkerIndex(walkerActor); + ok(index.data.size > 0, "public index is filled after getting"); + + index.clearIndex(); + ok(!index._data, "private index is empty after clearing"); + ok(index.data.size > 0, "public index is filled after getting"); + + index.destroy(); + runNextTest(); + }); + + addAsyncTest(function* testSearchExists() { + info ("Testing basic search APIs exist."); + + ok(walkerSearch, "walker search exists on the WalkerActor"); + ok(walkerSearch.search, "walker search has `search` method"); + ok(walkerSearch.index, "walker search has `index` property"); + is(walkerSearch.walker, walkerActor, "referencing the correct WalkerActor"); + + let search = new WalkerSearch(walkerActor); + ok(search, "a new search instance can be created"); + ok(search.search, "new search instance has `search` method"); + ok(search.index, "new search instance has `index` property"); + isnot(search, walkerSearch, "new search instance differs from the WalkerActor's"); + + search.destroy(); + runNextTest(); + }); + + addAsyncTest(function* testEmptySearch() { + info ("Testing search with an empty query."); + results = walkerSearch.search(""); + is(results.length, 0, "No results when searching for ''"); + + results = walkerSearch.search(null); + is(results.length, 0, "No results when searching for null"); + + results = walkerSearch.search(undefined); + is(results.length, 0, "No results when searching for undefined"); + + results = walkerSearch.search(10); + is(results.length, 0, "No results when searching for 10"); + + runNextTest(); + }); + + addAsyncTest(function* testBasicSearchData() { + let testData = [ + { + desc: "Search for tag with one result.", + search: "body", + expected: [ + {node: inspectee.body, type: "tag"} + ] + }, + { + desc: "Search for tag with multiple results", + search: "h2", + expected: [ + {node: inspectee.querySelectorAll("h2")[0], type: "tag"}, + {node: inspectee.querySelectorAll("h2")[1], type: "tag"}, + {node: inspectee.querySelectorAll("h2")[2], type: "tag"}, + ] + }, + { + desc: "Search for selector with multiple results", + search: "body > h2", + expected: [ + {node: inspectee.querySelectorAll("h2")[0], type: "selector"}, + {node: inspectee.querySelectorAll("h2")[1], type: "selector"}, + {node: inspectee.querySelectorAll("h2")[2], type: "selector"}, + ] + }, + { + desc: "Search for selector with multiple results", + search: ":root h2", + expected: [ + {node: inspectee.querySelectorAll("h2")[0], type: "selector"}, + {node: inspectee.querySelectorAll("h2")[1], type: "selector"}, + {node: inspectee.querySelectorAll("h2")[2], type: "selector"}, + ] + }, + { + desc: "Search for selector with multiple results", + search: "* h2", + expected: [ + {node: inspectee.querySelectorAll("h2")[0], type: "selector"}, + {node: inspectee.querySelectorAll("h2")[1], type: "selector"}, + {node: inspectee.querySelectorAll("h2")[2], type: "selector"}, + ] + }, + { + desc: "Search with multiple matches in a single tag expecting a single result", + search: "💩", + expected: [ + {node: inspectee.getElementById("💩"), type: "attributeValue"} + ] + }, + { + desc: "Search that has tag and text results", + search: "h1", + expected: [ + {node: inspectee.querySelector("h1"), type: "tag"}, + {node: inspectee.querySelector("h1 + p").childNodes[0], type: "text"}, + {node: inspectee.querySelector("h1 + p > strong").childNodes[0], type: "text"}, + ] + }, + ] + + for (let {desc, search, expected} of testData) { + info("Running test: " + desc); + let results = walkerSearch.search(search); + isDeeply(results, expected, + "Search returns correct results with '" + search + "'"); + } + + runNextTest(); + }); + + addAsyncTest(function* testPseudoElements() { + info ("Testing ::before and ::after element matching"); + + let beforeElt = new _documentWalker(inspectee.querySelector("#pseudo"), + inspectee.defaultView).firstChild(); + let afterElt = new _documentWalker(inspectee.querySelector("#pseudo"), + inspectee.defaultView).lastChild(); + let styleText = inspectee.querySelector("style").childNodes[0]; + + // ::before + let results = walkerSearch.search("::before"); + isDeeply(results, [ {node: beforeElt, type: "tag"} ], + "Tag search works for pseudo element"); + + results = walkerSearch.search("_moz_generated_content_before"); + is(results.length, 0, "No results for anon tag name"); + + results = walkerSearch.search("before element"); + isDeeply(results, [ + {node: styleText, type: "text"}, + {node: beforeElt, type: "text"} + ], "Text search works for pseudo element"); + + // ::after + results = walkerSearch.search("::after"); + isDeeply(results, [ {node: afterElt, type: "tag"} ], + "Tag search works for pseudo element"); + + results = walkerSearch.search("_moz_generated_content_after"); + is(results.length, 0, "No results for anon tag name"); + + results = walkerSearch.search("after element"); + isDeeply(results, [ + {node: styleText, type: "text"}, + {node: afterElt, type: "text"} + ], "Text search works for pseudo element"); + + runNextTest(); + }); + + addAsyncTest(function* testSearchMutationChangeResults() { + info ("Testing search before and after a mutation."); + let expected = [ + {node: inspectee.querySelectorAll("h3")[0], type: "tag"}, + {node: inspectee.querySelectorAll("h3")[1], type: "tag"}, + {node: inspectee.querySelectorAll("h3")[2], type: "tag"}, + ]; + + let results = walkerSearch.search("h3"); + isDeeply(results, expected, "Search works with tag results"); + + yield mutateDocumentAndWaitForMutation(() => { + expected[0].node.remove(); + }); + + results = walkerSearch.search("h3"); + isDeeply(results, [ + expected[1], + expected[2] + ], "Results are updated after removal"); + + yield new promise(resolve => { + info("Waiting for a mutation to happen"); + let observer = new inspectee.defaultView.MutationObserver(() => { + resolve(); + }); + observer.observe(inspectee, {attributes: true, subtree: true}); + inspectee.body.setAttribute("h3", "true"); + }); + + results = walkerSearch.search("h3"); + isDeeply(results, [ + {node: inspectee.body, type: "attributeName"}, + expected[1], + expected[2] + ], "Results are updated after addition"); + + yield new promise(resolve => { + info("Waiting for a mutation to happen"); + let observer = new inspectee.defaultView.MutationObserver(() => { + resolve(); + }); + observer.observe(inspectee, {attributes: true, childList: true, subtree: true}); + inspectee.body.removeAttribute("h3"); + expected[1].node.remove(); + expected[2].node.remove(); + }); + + results = walkerSearch.search("h3"); + is(results.length, 0, "Results are updated after removal"); + + runNextTest(); + }); + + runNextTest(); + + function mutateDocumentAndWaitForMutation(mutationFn) { + return new promise(resolve => { + info("Listening to markup mutation on the inspectee"); + let observer = new inspectee.defaultView.MutationObserver(resolve); + observer.observe(inspectee, {childList: true, subtree: true}); + mutationFn(); + }); + } +}; + </script> +</head> +<body> +<a id="inspectorContent" target="_blank" href="inspector-search-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector-traversal.html b/devtools/server/tests/mochitest/test_inspector-traversal.html new file mode 100644 index 000000000..ffac8e915 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector-traversal.html @@ -0,0 +1,354 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/server/actors/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspectee = null; +var gClient = null; +var gWalker = null; +var checkActorIDs = []; + +function assertOwnership() { + assertOwnershipTrees(gWalker); +} +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(function testWalkerRoot() { + // Make sure that refetching the root document of the walker returns the same + // actor as the getWalker returned. + promiseDone(gWalker.document().then(root => { + ok(root === gWalker.rootNode, "Re-fetching the document node should match the root document node."); + checkActorIDs.push(root.actorID); + assertOwnership(); + }).then(runNextTest)); +}); + +addTest(function testInnerHTML() { + promiseDone(gWalker.documentElement().then(docElement => { + return gWalker.innerHTML(docElement); + }).then(longstring => { + return longstring.string(); + }).then(innerHTML => { + ok(innerHTML === gInspectee.documentElement.innerHTML, "innerHTML should match"); + }).then(runNextTest)); +}); + +addTest(function testOuterHTML() { + promiseDone(gWalker.documentElement().then(docElement => { + return gWalker.outerHTML(docElement); + }).then(longstring => { + return longstring.string(); + }).then(outerHTML => { + ok(outerHTML === gInspectee.documentElement.outerHTML, "outerHTML should match"); + }).then(runNextTest)); +}); + +addTest(function testSetOuterHTMLNode() { + let newHTML = "<p id=\"edit-html-done\">after edit</p>"; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#edit-html").then(node => { + return gWalker.setOuterHTML(node, newHTML); + }).then(() => { + return gWalker.querySelector(gWalker.rootNode, "#edit-html-done"); + }).then(node => { + return gWalker.outerHTML(node); + }).then(longstring => { + return longstring.string(); + }).then(outerHTML => { + is(outerHTML, newHTML, "outerHTML has been updated"); + }).then(() => { + return gWalker.querySelector(gWalker.rootNode, "#edit-html"); + }).then(node => { + ok(!node, "The node with the old ID cannot be selected anymore"); + }).then(runNextTest)); +}); + +addTest(function testQuerySelector() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#longlist").then(node => { + is(node.getAttribute("data-test"), "exists", "should have found the right node"); + assertOwnership(); + }).then(() => { + return gWalker.querySelector(gWalker.rootNode, "unknownqueryselector").then(node => { + ok(!node, "Should not find a node here."); + assertOwnership(); + }); + }).then(runNextTest)); +}); + +addTest(function testQuerySelectors() { + let nodeList = null; + let firstNode = null; + let nodeListID = null; + promiseDone(gWalker.querySelectorAll(gWalker.rootNode, "#longlist div").then(list => { + nodeList = list; + is(nodeList.length, 26, "Expect 26 div children."); + assertOwnership(); + return nodeList.item(0); + }).then(node => { + firstNode = node; + checkActorIDs.push(node.actorID); + is(node.id, "a", "First child should be a"); + assertOwnership(); + return nodeList.items(); + }).then(nodes => { + is(nodes.length, 26, "Expect 26 nodes"); + is(nodes[0], firstNode, "First node should be reused."); + ok(nodes[0]._parent, "Parent node should be set."); + ok(nodes[0]._next || nodes[0]._prev, "Siblings should be set."); + ok(nodes[25]._next || nodes[25]._prev, "Siblings of " + nodes[25] + " should be set."); + assertOwnership(); + return nodeList.items(-1); + }).then(nodes => { + is(nodes.length, 1, "Expect 1 node") + is(nodes[0].id, "z", "Expect it to be the last node."); + checkActorIDs.push(nodes[0].actorID); + // Save the node list ID so we can ensure it was destroyed. + nodeListID = nodeList.actorID; + assertOwnership(); + return nodeList.release(); + }).then(() => { + ok(!nodeList.actorID, "Actor should have been destroyed."); + assertOwnership(); + return checkMissing(gClient, nodeListID); + }).then(runNextTest)); +}); + +// Helper to check the response of requests that return hasFirst/hasLast/nodes +// node lists (like `children` and `siblings`) +function nodeArrayChecker(first, last, ids) { + return function(response) { + is(response.hasFirst, first, "Should " + (first ? "" : "not ") + " have the first node."); + is(response.hasLast, last, "Should " + (last ? "" : "not ") + " have the last node."); + is(response.nodes.length, ids.length, "Should have " + ids.length + " children listed."); + let responseIds = ''; + for (node of response.nodes) { + responseIds += node.id; + } + is(responseIds, ids, "Correct nodes were returned."); + assertOwnership(); + } +} + +addTest(function testNoChildren() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#empty").then(empty => { + assertOwnership(); + return gWalker.children(empty).then(nodeArrayChecker(true, true, "")); + }).then(runNextTest)); +}); + +addTest(function testLongListTraversal() { + var longList; + var allChildren; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#longlist").then(node => { + longList = node; + // First call with no options, expect all children. + assertOwnership(); + return gWalker.children(longList).then(response => { + nodeArrayChecker(true, true, "abcdefghijklmnopqrstuvwxyz")(response); + allChildren = response.nodes; + assertOwnership(); + }); + }).then(() => { + // maxNodes should limit us to the first 5 nodes. + assertOwnership(); + return gWalker.children(longList, { maxNodes: 5 }).then(nodeArrayChecker(true, false, 'abcde')); + }).then(() => { + assertOwnership(); + // maxNodes with the second item centered should still give us the first 5 nodes. + return gWalker.children(longList, { maxNodes: 5, center: allChildren[1] }).then( + nodeArrayChecker(true, false, 'abcde') + ); + }).then(() => { + // maxNodes with a center in the middle of the list should put that item in the middle + let center = allChildren[13]; + is(center.id, 'n', "Make sure I know how to count letters."); + return gWalker.children(longList, { maxNodes: 5, center: center }).then( + nodeArrayChecker(false, false, 'lmnop') + ); + }).then(() => { + // maxNodes with the second-to-last item centered should give us the last 5 nodes. + return gWalker.children(longList, { maxNodes: 5, center: allChildren[24] }).then( + nodeArrayChecker(false, true, 'vwxyz') + ); + }).then(() => { + // maxNodes with a start in the middle should start at that node and fetch 5 + let start = allChildren[13]; + is(start.id, 'n', "Make sure I know how to count letters.") + return gWalker.children(longList, { maxNodes: 5, start: start }).then( + nodeArrayChecker(false, false, 'nopqr') + ); + }).then(() => { + // maxNodes near the end should only return what's left + return gWalker.children(longList, { maxNodes: 5, start: allChildren[24] }).then( + nodeArrayChecker(false, true, 'yz') + ); + }).then(runNextTest)); +}); + +addTest(function testObjectNodeChildren() { + promiseDone( + gWalker.querySelector(gWalker.rootNode, "object") + .then(object => gWalker.children(object)) + .then(nodeArrayChecker(true, true, "1")) + .then(runNextTest)); +}); + +addTest(function testSiblings() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(a => { + return gWalker.siblings(a, { maxNodes: 5, center: a }).then(nodeArrayChecker(true, false, "abcde")); + }).then(() => { + return gWalker.siblings(gWalker.rootNode).then(response => { + ok(response.hasFirst && response.hasLast, "Has first and last."); + is(response.nodes.length, 1, "Has only the document element."); + ok(response.nodes[0] === gWalker.rootNode, "Document element is its own sibling."); + }); + }).then(runNextTest)); +}); + +addTest(function testNextSibling() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#y").then(y => { + is(y.id, "y", "Got the right node."); + return gWalker.nextSibling(y); + }).then(z => { + is(z.id, "z", "nextSibling got the next node."); + return gWalker.nextSibling(z); + }).then(nothing => { + is(nothing, null, "nextSibling on the last node returned null."); + }).then(runNextTest)); +}); + +addTest(function testPreviousSibling() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#b").then(b => { + is(b.id, "b", "Got the right node."); + return gWalker.previousSibling(b); + }).then(a => { + is(a.id, "a", "nextSibling got the next node."); + return gWalker.previousSibling(a); + }).then(nothing => { + is(nothing, null, "previousSibling on the first node returned null."); + }).then(runNextTest)); +}); + + +addTest(function testFrameTraversal() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#childFrame").then(childFrame => { + return gWalker.children(childFrame); + }).then(children => { + let nodes = children.nodes; + is(nodes.length, 1, "There should be only one child of the iframe"); + is(nodes[0].nodeType, Node.DOCUMENT_NODE, "iframe child should be a document node"); + return gWalker.querySelector(nodes[0], "#z"); + }).then(childDocumentZ => { + return gWalker.parents(childDocumentZ); + }).then(parents => { + // Expected set of parent tag names for this item: + let expectedParents = ['DIV', 'BODY', 'HTML', '#document', 'IFRAME', 'BODY', 'HTML', '#document']; + for (let parent of parents) { + let expected = expectedParents.shift(); + is(parent.nodeName, expected, "Got expected parent"); + } + }).then(runNextTest)); +}); + +addTest(function testLongValue() { + const testSummaryLength = 10; + inspector.setValueSummaryLength(testSummaryLength); + SimpleTest.registerCleanupFunction(function() { + inspector.setValueSummaryLength(inspector.DEFAULT_VALUE_SUMMARY_LENGTH); + }); + + let longstringText = gInspectee.getElementById("longstring").firstChild.nodeValue; + + promiseDone(gWalker.querySelector(gWalker.rootNode, "#longstring").then(node => { + ok(!node.inlineTextChild, "Text is too long to be inlined"); + // Now we need to get the text node child... + return gWalker.children(node, { maxNodes: 1 }); + }).then(children => { + let textNode = children.nodes[0]; + is(textNode.nodeType, Node.TEXT_NODE, "Value should be a text node"); + return textNode; + }).then(textNode => { + return textNode.getNodeValue(); + }).then(value => { + return value.string(); + }).then(valueStr => { + is(valueStr, longstringText, "Full node value should match the string from the document."); + }).then(runNextTest)); +}); + +addTest(function testShortValue() { + let shortstringText = gInspectee.getElementById("shortstring").firstChild.nodeValue; + + promiseDone(gWalker.querySelector(gWalker.rootNode, "#shortstring").then(node => { + ok(!!node.inlineTextChild, "Text is short enough to be inlined"); + // Now we need to get the text node child... + return gWalker.children(node, { maxNodes: 1 }); + }).then(children => { + let textNode = children.nodes[0]; + is(textNode.nodeType, Node.TEXT_NODE, "Value should be a text node"); + return textNode; + }).then(textNode => { + return textNode.getNodeValue(); + }).then(value => { + return value.string(); + }).then(valueStr => { + is(valueStr, shortstringText, "Full node value should match the string from the document."); + }).then(runNextTest)); +}); + +addTest(function testReleaseWalker() { + checkActorIDs.push(gWalker.actorID); + + promiseDone(gWalker.release().then(() => { + let promises = Array.from(checkActorIDs, (id) => checkMissing(gClient, id)); + return promise.all(promises) + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gWalker; + delete gInspectee; + delete gClient; + runNextTest(); +}); + + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html b/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html new file mode 100644 index 000000000..63eb0bd3c --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html @@ -0,0 +1,136 @@ +<!DOCTYPE HTML> +<html> +<!-- +Tests for InspectorActor.getImageData() in following cases: + * Image takes too long to load (the method rejects after a timeout). + * Image is loading when the method is called and the load finishes before + timeout. + * Image fails to load. + +https://bugzilla.mozilla.org/show_bug.cgi?id=1192536 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1192536</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> + +const flags = require("devtools/shared/flags"); +const wasTesting = flags.testing; +SimpleTest.registerCleanupFunction(() => flags.testing = wasTesting); + +const PATH = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/"; +const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs"; +const DELAYED_IMAGE = BASE_IMAGE + "?delay=300"; +const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000"; +const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png"; + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gImg = null; +var gNodeFront = null; +var gWalker = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + + promiseDone(inspector.getWalker().then(walker => { + gWalker = walker; + return walker.querySelector(gWalker.rootNode, "img.custom").then(img => { + gNodeFront = img; + gImg = doc.querySelector("img.custom"); + + ok(gNodeFront, "Got the image NodeFront."); + ok(gImg, "Got the image Node."); + }); + }).then(runNextTest)); + }); +}); + +addTest(function testTimeout() { + info("Testing that the method aborts if the image takes too long to load."); + + // imageToImageData() only times out when flags.testing is not set. + flags.testing = false; + + gImg.src = TIMEOUT_IMAGE; + + info("Calling getImageData()."); + ensureRejects(gNodeFront.getImageData(), "Timeout image").then(runNextTest); +}); + +addTest(function testNonExistentImage() { + info("Testing that non-existent image causes a rejection."); + + // This test shouldn't hit the timeout. + flags.testing = true; + + gImg.src = NONEXISTENT_IMAGE; + + info("Calling getImageData()."); + ensureRejects(gNodeFront.getImageData(), "Non-existent image").then(runNextTest); +}); + +addTest(function testDelayedImage() { + info("Testing that the method waits for an image to load."); + + // This test shouldn't hit the timeout. + flags.testing = true; + + gImg.src = DELAYED_IMAGE; + + info("Calling getImageData()."); + checkImageData(gNodeFront.getImageData()).then(runNextTest); +}); + +addTest(function cleanup() { + delete gImg; + delete gNodeFront + delete gWalker; + runNextTest(); +}); + +/** + * Asserts that the given promise rejects. + */ +function ensureRejects(promise, desc) { + return promise.then(() => { + ok(false, desc + ": promise resolved unexpectedly."); + }, () => { + ok(true, desc + ": promise rejected as expected."); + }); +} + +/** + * Waits for the call to getImageData() the resolve and checks that the image + * size is reported correctly. + */ +function checkImageData(promise, { width, height } = { width: 1, height: 1 }) { + return promise.then(({ size }) => { + is(size.naturalWidth, width, "The width is correct."); + is(size.naturalHeight, height, "The height is correct."); + }); +} + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1192536">Mozilla Bug 1192536</a> +<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector_getImageData.html b/devtools/server/tests/mochitest/test_inspector_getImageData.html new file mode 100644 index 000000000..be3c24194 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector_getImageData.html @@ -0,0 +1,166 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=932937 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 932937</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + + promiseDone(inspector.getWalker().then(walker => { + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(function testLargeImage() { + // Select the image node from the test page + gWalker.querySelector(gWalker.rootNode, ".big-horizontal").then(img => { + ok(img, "Image node found in the test page"); + ok(img.getImageData, "Image node has the getImageData function"); + + img.getImageData(100).then(imageData => { + ok(imageData.data, "Image data actor was sent back"); + ok(imageData.size, "Image size info was sent back too"); + is(imageData.size.naturalWidth, 5333, "Natural width of the image correct"); + is(imageData.size.naturalHeight, 3000, "Natural width of the image correct"); + ok(imageData.size.resized, "Image was resized"); + + imageData.data.string().then(str => { + ok(str, "We have an image data string!"); + testResizing(imageData, str); + }); + }); + }); +}); + +addTest(function testLargeCanvas() { + // Select the canvas node from the test page + gWalker.querySelector(gWalker.rootNode, ".big-vertical").then(canvas => { + ok(canvas, "Image node found in the test page"); + ok(canvas.getImageData, "Image node has the getImageData function"); + + canvas.getImageData(350).then(imageData => { + ok(imageData.data, "Image data actor was sent back"); + ok(imageData.size, "Image size info was sent back too"); + is(imageData.size.naturalWidth, 1000, "Natural width of the image correct"); + is(imageData.size.naturalHeight, 2000, "Natural width of the image correct"); + ok(imageData.size.resized, "Image was resized"); + + imageData.data.string().then(str => { + ok(str, "We have an image data string!"); + testResizing(imageData, str); + }); + }); + }); +}); + +addTest(function testSmallImage() { + // Select the small image node from the test page + gWalker.querySelector(gWalker.rootNode, ".small").then(img => { + ok(img, "Image node found in the test page"); + ok(img.getImageData, "Image node has the getImageData function"); + + img.getImageData().then(imageData => { + ok(imageData.data, "Image data actor was sent back"); + ok(imageData.size, "Image size info was sent back too"); + is(imageData.size.naturalWidth, 245, "Natural width of the image correct"); + is(imageData.size.naturalHeight, 240, "Natural width of the image correct"); + ok(!imageData.size.resized, "Image was NOT resized"); + + imageData.data.string().then(str => { + ok(str, "We have an image data string!"); + testResizing(imageData, str); + }); + }); + }); +}); + +addTest(function testDataImage() { + // Select the data image node from the test page + gWalker.querySelector(gWalker.rootNode, ".data").then(img => { + ok(img, "Image node found in the test page"); + ok(img.getImageData, "Image node has the getImageData function"); + + img.getImageData(14).then(imageData => { + ok(imageData.data, "Image data actor was sent back"); + ok(imageData.size, "Image size info was sent back too"); + is(imageData.size.naturalWidth, 28, "Natural width of the image correct"); + is(imageData.size.naturalHeight, 28, "Natural width of the image correct"); + ok(imageData.size.resized, "Image was resized"); + + imageData.data.string().then(str => { + ok(str, "We have an image data string!"); + testResizing(imageData, str); + }); + }); + }); +}); + +addTest(function testNonImgOrCanvasElements() { + gWalker.querySelector(gWalker.rootNode, "body").then(body => { + ensureRejects(body.getImageData(), "Invalid element").then(runNextTest); + }); +}); + +addTest(function cleanup() { + delete gWalker; + runNextTest(); +}); + +/** + * Checks if the server told the truth about resizing the image + */ +function testResizing(imageData, str) { + let img = document.createElement("img"); + img.addEventListener("load", () => { + let resized = !(img.naturalWidth == imageData.size.naturalWidth && + img.naturalHeight == imageData.size.naturalHeight); + is(imageData.size.resized, resized, "Server told the truth about resizing"); + runNextTest(); + }, false); + img.src = str; +} + +/** + * Asserts that the given promise rejects. + */ +function ensureRejects(promise, desc) { + return promise.then(() => { + ok(false, desc + ": promise resolved unexpectedly."); + }, () => { + ok(true, desc + ": promise rejected as expected."); + }); +} + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=932937">Mozilla Bug 932937</a> +<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html b/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html new file mode 100644 index 000000000..473a62275 --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html @@ -0,0 +1,114 @@ +<!DOCTYPE HTML> +<html> +<!-- +Tests for InspectorActor.getImageDataFromURL() in following cases: + * Normal case, image loads after a small delay. + * Image takes too long to load (the method rejects after a timeout). + * Image fails to load. + +https://bugzilla.mozilla.org/show_bug.cgi?id=1192536 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1192536</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> + +const flags = require("devtools/shared/flags"); +const wasTesting = flags.testing; +SimpleTest.registerCleanupFunction(() => flags.testing = wasTesting); + +const PATH = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/"; +const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs"; +const DELAYED_IMAGE = BASE_IMAGE + "?delay=300"; +const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000"; +const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png"; + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gInspector = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + gInspector = InspectorFront(client, tab); + runNextTest(); + }); +}); + +addTest(function testTimeout() { + info("Testing that the method aborts if the image takes too long to load."); + + // imageToImageData() only times out when flags.testing is not set. + flags.testing = false; + + ensureRejects(gInspector.getImageDataFromURL(TIMEOUT_IMAGE), + "Image that loads for too long").then(runNextTest); +}); + +addTest(function testNonExistentImage() { + info("Testing that non-existent image causes a rejection."); + + // This test shouldn't hit the timeout. + flags.testing = true; + + ensureRejects(gInspector.getImageDataFromURL(NONEXISTENT_IMAGE), + "Non-existent image").then(runNextTest); +}); + +addTest(function testNormalImage() { + info("Testing that the method waits for an image to load."); + + // This test shouldn't hit the timeout. + flags.testing = true; + + checkImageData(gInspector.getImageDataFromURL(DELAYED_IMAGE)).then(runNextTest); +}); + +addTest(function cleanup() { + delete gInspector; + runNextTest(); +}); + +/** + * Asserts that the given promise rejects. + */ +function ensureRejects(promise, desc) { + return promise.then(() => { + ok(false, desc + ": promise resolved unexpectedly."); + }, () => { + ok(true, desc + ": promise rejected as expected."); + }); +} + +/** + * Waits for the call to getImageData() the resolve and checks that the image + * size is reported correctly. + */ +function checkImageData(promise, { width, height } = { width: 1, height: 1 }) { + return promise.then(({ size }) => { + is(size.naturalWidth, width, "The width is correct."); + is(size.naturalHeight, height, "The height is correct."); + }); +} + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1192536">Mozilla Bug 1192536</a> +<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_inspector_getNodeFromActor.html b/devtools/server/tests/mochitest/test_inspector_getNodeFromActor.html new file mode 100644 index 000000000..6c06d8a7b --- /dev/null +++ b/devtools/server/tests/mochitest/test_inspector_getNodeFromActor.html @@ -0,0 +1,88 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1155653 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1155653</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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/shared/fronts/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker; + +addTest(function() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + + promiseDone(inspector.getWalker().then(walker => { + gWalker = walker; + }).then(runNextTest)); + }); +}); + +addTest(function() { + info("Try to get a NodeFront from an invalid actorID"); + gWalker.getNodeFromActor("invalid", ["node"]).then(node => { + ok(!node, "The node returned is null"); + runNextTest(); + }); +}); + +addTest(function() { + info("Try to get a NodeFront from a valid actorID but invalid path"); + gWalker.getNodeFromActor(gWalker.actorID, ["invalid", "path"]).then(node => { + ok(!node, "The node returned is null"); + runNextTest(); + }); +}); + +addTest(function() { + info("Try to get a NodeFront from a valid actorID and valid path"); + gWalker.getNodeFromActor(gWalker.actorID, ["rootDoc"]).then(rootDocNode => { + ok(rootDocNode, "A node was returned"); + is(rootDocNode, gWalker.rootNode, "The right node was returned"); + runNextTest(); + }); +}); + +addTest(function() { + info("Try to get a NodeFront from a valid actorID and valid complex path"); + gWalker.getNodeFromActor(gWalker.actorID, + ["tabActor", "window", "document", "body"]).then(bodyNode => { + ok(bodyNode, "A node was returned"); + gWalker.querySelector(gWalker.rootNode, "body").then(node => { + is(bodyNode, node, "The body node was returned"); + runNextTest(); + }); + }); +}); + +addTest(function() { + gWalker = null; + runNextTest(); +}); + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1155653">Mozilla Bug 1155653</a> +<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_makeGlobalObjectReference.html b/devtools/server/tests/mochitest/test_makeGlobalObjectReference.html new file mode 100644 index 000000000..8bd7e0476 --- /dev/null +++ b/devtools/server/tests/mochitest/test_makeGlobalObjectReference.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=914405 + +Debugger.prototype.makeGlobalObjectReference should dereference WindowProxy +(outer window) objects. +--> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug 914405</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> +<pre id="test"> +<script> + +Components.utils.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +window.onload = function () { + SimpleTest.waitForExplicitFinish(); + + var iframe = document.createElement("iframe"); + iframe.src = "data:text/html,<html>The word 'smorgasbord', spoken by an adorably plump child, symbolizing prosperity</html>"; + iframe.onload = iframeOnLoad; + document.body.appendChild(iframe); + + function iframeOnLoad() { + var dbg = new Debugger; + + var g1o = iframe.contentWindow; // 'o' for 'outer window' + ok(!dbg.hasDebuggee(g1o), "iframe is not initially a debuggee"); + + // Like addDebuggee, makeGlobalObjectReference innerizes. + // 'i' stands for 'inner window'. + // 'DO' stands for 'Debugger.Object'. + var g1iDO = dbg.makeGlobalObjectReference(g1o); + ok(!dbg.hasDebuggee(g1o), "makeGlobalObjectReference does not add g1 as debuggee, designated via outer"); + ok(!dbg.hasDebuggee(g1iDO), "makeGlobalObjectReference does not add g1 as debuggee, designated via D.O "); + + // Wrapping an object automatically outerizes it, so dereferencing an + // inner object D.O gets you an outer object. + // ('===' does distinguish inner and outer objects.) + // (That's a capital '=', if you must know.) + ok(g1iDO.unsafeDereference() === g1o, "g1iDO has the right referent"); + + // However, Debugger.Objects do distinguish inner and outer windows. + var g1oDO = g1iDO.makeDebuggeeValue(g1o); + ok(g1iDO !== g1oDO, "makeDebuggeeValue doesn't innerize"); + ok(g1iDO.unsafeDereference() === g1oDO.unsafeDereference(), + "unsafeDereference() outerizes, so inner and outer window D.Os both dereference to outer"); + + ok(dbg.addDebuggee(g1o) === g1iDO, "addDebuggee returns the inner window's D.O"); + ok(dbg.hasDebuggee(g1o), "addDebuggee adds the correct global"); + ok(dbg.hasDebuggee(g1iDO), "hasDebuggee can take a D.O referring to the inner window"); + ok(dbg.hasDebuggee(g1oDO), "hasDebuggee can take a D.O referring to the outer window"); + + var iframe2 = document.createElement("iframe"); + iframe2.src = "data:text/html,<html>Her retrospection, in hindsight, was prescient.</html>"; + iframe2.onload = iframe2OnLoad; + document.body.appendChild(iframe2); + + function iframe2OnLoad() { + // makeGlobalObjectReference dereferences CCWs. + var g2o = iframe2.contentWindow; + g2o.g1o = g1o; + + var g2iDO = dbg.addDebuggee(g2o); + var g2g1oDO = g2iDO.getOwnPropertyDescriptor('g1o').value; + ok(g2g1oDO !== g1oDO, "g2's cross-compartment wrapper for g1o gets its own D.O"); + ok(g2g1oDO.unwrap() === g1oDO, + "unwrapping g2's cross-compartment wrapper for g1o gets the right D.O"); + ok(dbg.makeGlobalObjectReference(g2g1oDO) === g1iDO, + "makeGlobalObjectReference unwraps cross-compartment wrappers, and innerizes"); + + SimpleTest.finish(); + } + } +} + +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory.html b/devtools/server/tests/mochitest/test_memory.html new file mode 100644 index 000000000..9f191da76 --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 923275 - Add a memory monitor widget to the developer toolbar +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + var measurement = yield memory.measure(); + ok(measurement.total > 0, "total memory is valid"); + ok(measurement.domSize > 0, "domSize is valid"); + ok(measurement.styleSize > 0, "styleSize is valid"); + ok(measurement.jsObjectsSize > 0, "jsObjectsSize is valid"); + ok(measurement.jsStringsSize > 0, "jsStringsSize is valid"); + ok(measurement.jsOtherSize > 0, "jsOtherSize is valid"); + ok(measurement.otherSize > 0, "otherSize is valid"); + ok(measurement.jsMilliseconds, "jsMilliseconds is valid"); + ok(measurement.nonJSMilliseconds, "nonJSMilliseconds is valid"); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_allocations_01.html b/devtools/server/tests/mochitest/test_memory_allocations_01.html new file mode 100644 index 000000000..2ed9b74bc --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_allocations_01.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1067491 - Test recording allocations. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + yield memory.startRecordingAllocations(); + ok(true, "Can start recording allocations"); + + // Allocate some objects. + + var alloc1, alloc2, alloc3; + (function outer() { + (function middle() { + (function inner() { + alloc1 = {}; alloc1.line = Error().lineNumber; + alloc2 = []; alloc2.line = Error().lineNumber; + alloc3 = new function() {}; alloc3.line = Error().lineNumber; + }()); + }()); + }()); + + var response = yield memory.getAllocations(); + + yield memory.stopRecordingAllocations(); + ok(true, "Can stop recording allocations"); + + // Filter out allocations by library and test code, and get only the + // allocations that occurred in our test case above. + + function isTestAllocation(alloc) { + var frame = response.frames[alloc]; + return frame + && frame.functionDisplayName === "inner" + && (frame.line === alloc1.line + || frame.line === alloc2.line + || frame.line === alloc3.line); + } + + var testAllocations = response.allocations.filter(isTestAllocation); + ok(testAllocations.length >= 3, + "Should find our 3 test allocations (plus some allocations for the error " + + "objects used to get line numbers)"); + + // For each of the test case's allocations, ensure that the parent frame + // indices are correct. Also test that we did get an allocation at each + // line we expected (rather than a bunch on the first line and none on the + // others, etc). + + var expectedLines = new Set([alloc1.line, alloc2.line, alloc3.line]); + + for (var alloc of testAllocations) { + var innerFrame = response.frames[alloc]; + ok(innerFrame, "Should get the inner frame"); + is(innerFrame.functionDisplayName, "inner"); + expectedLines.delete(innerFrame.line); + + var middleFrame = response.frames[innerFrame.parent]; + ok(middleFrame, "Should get the middle frame"); + is(middleFrame.functionDisplayName, "middle"); + + var outerFrame = response.frames[middleFrame.parent]; + ok(outerFrame, "Should get the outer frame"); + is(outerFrame.functionDisplayName, "outer"); + + // Not going to test the rest of the frames because they are Task.jsm + // and promise frames and it gets gross. Plus, I wouldn't want this test + // to start failing if they changed their implementations in a way that + // added or removed stack frames here. + } + + is(expectedLines.size, 0, + "Should have found all the expected lines"); + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_allocations_02.html b/devtools/server/tests/mochitest/test_memory_allocations_02.html new file mode 100644 index 000000000..0133a27b0 --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_allocations_02.html @@ -0,0 +1,76 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1132764 - Test controlling the maximum allocations log length over the RDP. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + var allocs = []; + var eventsFired = 0; + var intervalId = null; + function onAlloc () { + eventsFired++; + } + function startAllocating () { + intervalId = setInterval(() => { + for (var i = 100000; --i;) { + allocs.push(new Object()); + } + }, 1); + } + function stopAllocating () { + clearInterval(intervalId); + } + + memory.on("allocations", onAlloc); + + yield memory.startRecordingAllocations({ + drainAllocationsTimeout: 10 + }); + + yield waitUntil(() => eventsFired > 5); + ok(eventsFired > 5, "Some allocation events fired without allocating much via auto drain"); + yield memory.stopRecordingAllocations(); + + // Set a really high auto drain timer so we can test if + // it fires on GC + eventsFired = 0; + var startTime = performance.now(); + var drainTimer = 1000000; + yield memory.startRecordingAllocations({ + drainAllocationsTimeout: drainTimer + }); + + startAllocating(); + yield waitUntil(() => { + Cu.forceGC(); + return eventsFired > 1; + }); + stopAllocating(); + ok(performance.now() - drainTimer < startTime, "Allocation events fired on GC before timer"); + yield memory.stopRecordingAllocations(); + + memory.off("allocations", onAlloc); + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_allocations_03.html b/devtools/server/tests/mochitest/test_memory_allocations_03.html new file mode 100644 index 000000000..b7d18d7ed --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_allocations_03.html @@ -0,0 +1,78 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1067491 - Test that frames keep the same index while we are recording. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + yield memory.startRecordingAllocations(); + + // Allocate twice with the exact same stack (hence setTimeout rather than + // allocating directly in the generator), but with getAllocations() calls in + // between. + + var allocs = []; + function allocator() { + allocs.push({}); + } + + setTimeout(allocator, 1); + yield waitForTime(2); + var first = yield memory.getAllocations(); + + setTimeout(allocator, 1); + yield waitForTime(2); + var second = yield memory.getAllocations(); + + yield memory.stopRecordingAllocations(); + + // Assert that each frame in the first response has the same index in the + // second response. This isn't commutative, so we don't check that all + // of the second response's frames are the same in the first response, + // because there might be new allocations that happen after the first query + // but before the second. + + function assertSameFrame(a, b) { + info("Checking frames at index " + i + ":"); + info(" First frame = " + JSON.stringify(a, null, 4)); + info(" Second frame = " + JSON.stringify(b, null, 4)); + + is(!!a, !!b); + if (!a || !b) { + return; + } + + is(a.source, b.source); + is(a.line, b.line); + is(a.column, b.column); + is(a.functionDisplayName, b.functionDisplayName); + is(a.parent, b.parent); + } + + for (var i = 0; i < first.frames.length; i++) { + assertSameFrame(first.frames[i], second.frames[i]); + } + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_allocations_04.html b/devtools/server/tests/mochitest/test_memory_allocations_04.html new file mode 100644 index 000000000..5568736d3 --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_allocations_04.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1068171 - Test controlling the memory actor's allocation sampling probability. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + var allocs = []; + function allocator() { + for (var i = 0; i < 100; i++) { + allocs.push({}); + } + } + + var testProbability = Task.async(function* (p, expected) { + info("probability = " + p); + yield memory.startRecordingAllocations({ + probability: p + }); + allocator(); + var response = yield memory.getAllocations(); + yield memory.stopRecordingAllocations(); + return response.allocations.length; + }); + + is((yield testProbability(0.0)), 0, + "With probability = 0.0, we shouldn't get any allocations."); + + ok((yield testProbability(1.0)) >= 100, + "With probability = 1.0, we should get all 100 allocations (plus " + + "whatever allocations the actor and SpiderMonkey make)."); + + // We don't test any other probabilities because the test would be + // non-deterministic. We don't have a way to control the PRNG like we do in + // jit-tests + // (js/src/jit-test/tests/debug/Memory-allocationsSamplingProbability-*.js). + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_allocations_05.html b/devtools/server/tests/mochitest/test_memory_allocations_05.html new file mode 100644 index 000000000..0eeb7bd16 --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_allocations_05.html @@ -0,0 +1,87 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1068144 - Test getting the timestamps for allocations. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + var allocs = []; + function allocator() { + allocs.push(new Object); + } + + // Using setTimeout results in wildly varying delays that make it hard to + // test our timestamps and results in intermittent failures. Instead, we + // actually spin an empty loop for a whole millisecond. + function actuallyWaitOneWholeMillisecond() { + var start = window.performance.now(); + while (window.performance.now() - start < 1.000) ; + } + + yield memory.startRecordingAllocations(); + + allocator(); + actuallyWaitOneWholeMillisecond(); + allocator(); + actuallyWaitOneWholeMillisecond(); + allocator(); + + var response = yield memory.getAllocations(); + yield memory.stopRecordingAllocations(); + + ok(response.allocationsTimestamps, "The response should have timestamps."); + is(response.allocationsTimestamps.length, response.allocations.length, + "There should be a timestamp for every allocation."); + + var allocatorIndices = response.allocations + .map(function (a, idx) { + var frame = response.frames[a]; + if (frame && frame.functionDisplayName === "allocator") { + return idx; + } + }) + .filter(function (idx) { + return idx !== undefined; + }); + + is(allocatorIndices.length, 3, "Should have our 3 allocations from the `allocator` timeouts."); + + var lastTimestamp; + for (var i = 0; i < 3; i++) { + var timestamp = response.allocationsTimestamps[allocatorIndices[i]]; + info("timestamp", timestamp); + ok(timestamp, "We should have a timestamp for the `allocator` allocation."); + + if (lastTimestamp) { + var delta = timestamp - lastTimestamp; + info("delta since last timestamp", delta); + ok(delta >= 1 /* ms */, + "The timestamp should be about 1 ms after the last timestamp."); + } + + lastTimestamp = timestamp; + } + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_allocations_06.html b/devtools/server/tests/mochitest/test_memory_allocations_06.html new file mode 100644 index 000000000..56a9f8041 --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_allocations_06.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1132764 - Test controlling the maximum allocations log length over the RDP. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + var allocs = []; + function allocator() { + allocs.push(new Object); + } + + yield memory.startRecordingAllocations({ + maxLogLength: 1 + }); + + allocator(); + allocator(); + allocator(); + + var response = yield memory.getAllocations(); + yield memory.stopRecordingAllocations(); + + is(response.allocations.length, 1, + "There should only be one entry in the allocations log."); + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_allocations_07.html b/devtools/server/tests/mochitest/test_memory_allocations_07.html new file mode 100644 index 000000000..c26c2d8ec --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_allocations_07.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1192335 - Test getting the byte sizes for allocations. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + var allocs = []; + function allocator() { + allocs.push(new Object); + } + + yield memory.startRecordingAllocations(); + + allocator(); + allocator(); + allocator(); + + var response = yield memory.getAllocations(); + yield memory.stopRecordingAllocations(); + + ok(response.allocationSizes, "The response should have bytesizes."); + is(response.allocationSizes.length, response.allocations.length, + "There should be a bytesize for every allocation."); + ok(response.allocationSizes.length >= 3, + "There are atleast 3 allocations."); + ok(response.allocationSizes.every(isPositiveNumber), "every bytesize is a positive number"); + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; + +function isPositiveNumber (n) { + return typeof n === "number" && n > 0; +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_attach_01.html b/devtools/server/tests/mochitest/test_memory_attach_01.html new file mode 100644 index 000000000..5b0b3f75e --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_attach_01.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 960671 - Test attaching and detaching from a memory actor. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + ok(true, "Shouldn't have gotten an error attaching."); + yield memory.detach(); + ok(true, "Shouldn't have gotten an error detaching."); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_attach_02.html b/devtools/server/tests/mochitest/test_memory_attach_02.html new file mode 100644 index 000000000..76269a6df --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_attach_02.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 960671 - Test attaching and detaching while in the wrong state. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + + var e = null; + try { + yield memory.detach(); + } + catch (ee) { + e = ee; + } + ok(e, "Should have hit the wrongState error"); + + yield memory.attach(); + + e = null; + try { + yield memory.attach(); + } + catch (ee) { + e = ee; + } + ok(e, "Should have hit the wrongState error"); + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_census.html b/devtools/server/tests/mochitest/test_memory_census.html new file mode 100644 index 000000000..f24050337 --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_census.html @@ -0,0 +1,33 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1067491 - Test taking a census over the RDP. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + var census = yield memory.takeCensus(); + is(typeof census, "object"); + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_gc_01.html b/devtools/server/tests/mochitest/test_memory_gc_01.html new file mode 100644 index 000000000..97cb754f0 --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_gc_01.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1067491 - Test forcing a gc. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + + do { + var objects = []; + for (var i = 0; i < 1000; i++) { + var o = {}; + o[Math.random()] = 1; + objects.push(o); + } + + objects = null; + + var { total: beforeGC } = yield memory.measure(); + + yield memory.forceGarbageCollection(); + var { total: afterGC } = yield memory.measure(); + } while(beforeGC < afterGC); + + ok(true, "The amount of memory after GC should eventually decrease"); + + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_memory_gc_events.html b/devtools/server/tests/mochitest/test_memory_gc_events.html new file mode 100644 index 000000000..2297481d4 --- /dev/null +++ b/devtools/server/tests/mochitest/test_memory_gc_events.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1137527 - Test receiving GC events from the memory actor. +--> +<head> + <meta charset="utf-8"> + <title>Memory monitoring actor test</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> +<pre id="test"> +<script src="memory-helpers.js" type="application/javascript;version=1.8"></script> +<script> +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + var event = require("sdk/event/core"); + + Task.spawn(function* () { + var { memory, client } = yield startServerAndGetSelectedTabMemory(); + yield memory.attach(); + + var gotGcEvent = new Promise(resolve => { + event.on(memory, "garbage-collection", gcData => { + ok(gcData, "Got GC data"); + resolve(); + }); + }); + + memory.forceGarbageCollection(); + yield gotGcEvent; + + yield memory.detach(); + destroyServerAndFinish(client); + }); +}; +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_preference.html b/devtools/server/tests/mochitest/test_preference.html new file mode 100644 index 000000000..54903f455 --- /dev/null +++ b/devtools/server/tests/mochitest/test_preference.html @@ -0,0 +1,115 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 943251 - Allow accessing about:config from WebIDE +--> +<head> + <meta charset="utf-8"> + <title>Test Preference Actor</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> +<pre id="test"> +<script> + +function runTests() { + var Cu = Components.utils; + var Cc = Components.classes; + var Ci = Components.interfaces; + + var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var {DebuggerClient} = require("devtools/shared/client/main"); + var {DebuggerServer} = require("devtools/server/main"); + var Services = require("Services"); + + SimpleTest.waitForExplicitFinish(); + + var {getPreferenceFront} = require("devtools/shared/fronts/preference"); + + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + + var client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect().then(function onConnect() { + client.listTabs(function onListTabs(aResponse) { + var p = getPreferenceFront(client, aResponse); + + var prefs = {}; + + var localPref = { + boolPref: true, + intPref: 0x1234, + charPref: "Hello World", + }; + + + function checkValues() { + is(prefs.boolPref, localPref.boolPref, "read/write bool pref"); + is(prefs.intPref, localPref.intPref, "read/write int pref"); + is(prefs.charPref, localPref.charPref, "read/write string pref"); + + ["test.all.bool", "test.all.int", "test.all.string"].forEach(function(key) { + var expectedValue; + switch(Services.prefs.getPrefType(key)) { + case Ci.nsIPrefBranch.PREF_STRING: + expectedValue = Services.prefs.getCharPref(key); + break; + case Ci.nsIPrefBranch.PREF_INT: + expectedValue = Services.prefs.getIntPref(key); + break; + case Ci.nsIPrefBranch.PREF_BOOL: + expectedValue = Services.prefs.getBoolPref(key); + break; + default: + ok(false, "unexpected pref type (" + key + ")"); + break; + } + + is(prefs.allPrefs[key].value, expectedValue, "valid preference value (" + key + ")"); + is(prefs.allPrefs[key].hasUserValue, Services.prefs.prefHasUserValue(key), "valid hasUserValue (" + key + ")"); + }); + + ["test.bool", "test.int", "test.string"].forEach(function(key) { + ok(!prefs.allPrefs.hasOwnProperty(key), "expect no pref (" + key + ")"); + is(Services.prefs.getPrefType(key), Ci.nsIPrefBranch.PREF_INVALID, "pref (" + key + ") is clear"); + }); + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish() + }); + } + + + p.getAllPrefs().then((json) => prefs["allPrefs"] = json) + .then(() => p.setBoolPref("test.bool", localPref.boolPref)) + .then(() => p.setIntPref("test.int", localPref.intPref)) + .then(() => p.setCharPref("test.string", localPref.charPref)) + .then(() => p.getBoolPref("test.bool")).then((value) => prefs["boolPref"] = value) + .then(() => p.getIntPref("test.int")).then((value) => prefs["intPref"] = value) + .then(() => p.getCharPref("test.string")).then((value) => prefs["charPref"] = value) + .then(() => p.clearUserPref("test.bool")) + .then(() => p.clearUserPref("test.int")) + .then(() => p.clearUserPref("test.string")) + .then(checkValues); + + }); + }); + +} + +window.onload = function () { + SpecialPowers.pushPrefEnv({ + "set": [ + ["devtools.debugger.forbid-certified-apps", false], + ["test.all.bool", true], + ["test.all.int", 0x4321], + ["test.all.string", "allizom"], + ] + }, runTests); +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_settings.html b/devtools/server/tests/mochitest/test_settings.html new file mode 100644 index 000000000..5665b46b3 --- /dev/null +++ b/devtools/server/tests/mochitest/test_settings.html @@ -0,0 +1,130 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1022797 - Settings support from WebIDE +--> +<head> + <meta charset="utf-8"> + <title>Test Settings Actor</title> + <script type="text/javascript" src="chrome://mochikit/content/MochiKit/MochiKit.js"></script> + <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> +<pre id="test"> +<script> + +function runTests() { + var Cu = Components.utils; + var Cc = Components.classes; + var Ci = Components.interfaces; + + var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + var {DebuggerClient} = require("devtools/shared/client/main"); + var {DebuggerServer} = require("devtools/server/main"); + + if (SpecialPowers.isMainProcess()) { + Cu.import("resource://gre/modules/SettingsRequestManager.jsm"); + } + + SimpleTest.waitForExplicitFinish(); + + var {getSettingsFront} = require("devtools/shared/fronts/settings"); + var {_setDefaultSettings} = require("devtools/server/actors/settings"); + + DebuggerServer.init(function () { return true; }); + DebuggerServer.addBrowserActors(); + + var client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect().then(function onConnect() { + client.listTabs(function onListTabs(aResponse) { + var s = getSettingsFront(client, aResponse); + + var settings = {}; + var resetSettings = {}; + var fakeSettings = { + "wifi.enabled": true, + "audio.volume.alarm": 15, + "app.reportCrashes": "ask", + "app.someObject": { active: true } + }; + var localSetting = { + "wifi.enabled": false, + "audio.volume.alarm": 0, + "app.reportCrashes": "none", + "app.someObject": {} + }; + + function checkValues() { + is(settings.allSettings["wifi.enabled"].hasUserValue, false, "original unchanged bool setting"); + is(settings.allSettings["audio.volume.alarm"].hasUserValue, false, "original unchanged int setting"); + is(settings.allSettings["app.reportCrashes"].hasUserValue, false, "original unchanged string setting"); + is(settings.allSettings["app.someObject"].hasUserValue, false, "original unchanged object setting"); + + is(settings.allSettings["wifi.enabled"].value, fakeSettings["wifi.enabled"], "original read/write bool setting"); + is(settings.allSettings["audio.volume.alarm"].value, fakeSettings["audio.volume.alarm"], "original read/write int setting"); + is(settings.allSettings["app.reportCrashes"].value, fakeSettings["app.reportCrashes"], "original read/write string setting"); + is(JSON.stringify(settings.allSettings["app.someObject"].value), JSON.stringify(fakeSettings["app.someObject"]), "original read/write object setting"); + + is(settings.allUpdatedSettings["wifi.enabled"].hasUserValue, true, "updated user-changed bool setting"); + is(settings.allUpdatedSettings["audio.volume.alarm"].hasUserValue, true, "updated user-changed int setting"); + is(settings.allUpdatedSettings["app.reportCrashes"].hasUserValue, true, "updated user-changed string setting"); + is(settings.allUpdatedSettings["app.someObject"].hasUserValue, true, "updated user-changed object setting"); + + is(settings["wifi.enabled"], localSetting["wifi.enabled"], "updated bool setting"); + is(settings["audio.volume.alarm"], localSetting["audio.volume.alarm"], "updated int setting"); + is(settings["app.reportCrashes"], localSetting["app.reportCrashes"], "updated string setting"); + is(JSON.stringify(settings["app.someObject"]), JSON.stringify(localSetting["app.someObject"]), "updated object as string setting"); + + is(resetSettings["wifi.enabled"], fakeSettings["wifi.enabled"], "reset to original bool setting"); + is(resetSettings["audio.volume.alarm"], fakeSettings["audio.volume.alarm"], "reset to original int setting"); + is(resetSettings["app.reportCrashes"], fakeSettings["app.reportCrashes"], "reset to original string setting"); + is(JSON.stringify(resetSettings["app.someObject"]), JSON.stringify(fakeSettings["app.someObject"]), "reset to original object setting"); + + client.close().then(() => { + DebuggerServer.destroy(); + SimpleTest.finish(); + }); + } + + // settings.json doesn't exist outside of b2g so we will fake it. + _setDefaultSettings(fakeSettings); + s.setSetting("wifi.enabled", fakeSettings["wifi.enabled"]) + .then(() => s.setSetting("audio.volume.alarm", fakeSettings["audio.volume.alarm"])) + .then(() => s.setSetting("app.reportCrashes", fakeSettings["app.reportCrashes"])) + .then(() => s.setSetting("app.someObject", fakeSettings["app.someObject"])) + .then(() => s.getAllSettings().then(json => settings.allSettings = json)) + .then(() => s.setSetting("wifi.enabled", localSetting["wifi.enabled"])) + .then(() => s.setSetting("audio.volume.alarm", localSetting["audio.volume.alarm"])) + .then(() => s.setSetting("app.reportCrashes", localSetting["app.reportCrashes"])) + .then(() => s.setSetting("app.someObject", localSetting["app.someObject"])) + .then(() => s.getAllSettings().then(json => settings.allUpdatedSettings = json)) + .then(() => s.getSetting("wifi.enabled")).then(value => settings["wifi.enabled"] = value) + .then(() => s.getSetting("audio.volume.alarm")).then(value => settings["audio.volume.alarm"] = value) + .then(() => s.getSetting("app.reportCrashes")).then(value => settings["app.reportCrashes"] = value) + .then(() => s.getSetting("app.someObject")).then(value => settings["app.someObject"] = value) + .then(() => s.clearUserSetting("wifi.enabled")).then(() => { + s.getSetting("wifi.enabled").then(value => resetSettings["wifi.enabled"] = value); + }) + .then(() => s.clearUserSetting("audio.volume.alarm")).then(() => { + s.getSetting("audio.volume.alarm").then(value => resetSettings["audio.volume.alarm"] = value); + }) + .then(() => s.clearUserSetting("app.reportCrashes")).then(() => { + s.getSetting("app.reportCrashes").then(value => resetSettings["app.reportCrashes"] = value); + }) + .then(() => s.clearUserSetting("app.someObject")).then(() => { + s.getSetting("app.someObject").then(value => { + resetSettings["app.someObject"] = value + }).then(checkValues); + }); + }); + }); +} + +window.onload = function () { + runTests(); +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_setupInParentChild.html b/devtools/server/tests/mochitest/test_setupInParentChild.html new file mode 100644 index 000000000..fc94ca96a --- /dev/null +++ b/devtools/server/tests/mochitest/test_setupInParentChild.html @@ -0,0 +1,110 @@ +<!DOCTYPE HTML> +<html> +<!-- +Bug 1181100 - Test DebuggerServerConnection.setupInParent and DebuggerServer.setupInChild +--> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug</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> +<pre id="test"> +<script type="application/javascript;version=1.8"> + +let Cu = Components.utils; +let Cc = Components.classes; +let Ci = Components.interfaces; + +let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); +let {DebuggerClient} = require("devtools/shared/client/main"); +let {DebuggerServer} = require("devtools/server/main"); +let Services = require("Services"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({ + "set": [ + // Always log packets when running tests. + ["devtools.debugger.log", true], + ["dom.mozBrowserFramesEnabled", true] + ] + }, runTests); +} + +function runTests() { + // Create a minimal iframe with a message manager + let iframe = document.createElement("iframe"); + iframe.mozbrowser = true; + document.body.appendChild(iframe); + + let mm = iframe.frameLoader.messageManager; + + // Instantiate a minimal server + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + } + if (!DebuggerServer.createRootActor) { + DebuggerServer.addBrowserActors(); + } + + // Fake a connection to an iframe + let transport = DebuggerServer.connectPipe(); + let conn = transport._serverConnection; + let client = new DebuggerClient(transport); + + // Wait for a response from setupInChild + const ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"] + .getService(Ci.nsIMessageListenerManager); + let onChild = msg => { + ppmm.removeMessageListener("test:setupChild", onChild); + let args = msg.json; + + is(args[0], 1, "Got first numeric argument"); + is(args[1], "two", "Got second string argument"); + is(args[2].three, true, "Got last JSON argument"); + + // Ask the child to call setupInParent + DebuggerServer.setupInChild({ + module: "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/setup-in-child.js", + setupChild: "callParent" + }); + }; + ppmm.addMessageListener("test:setupChild", onChild); + + // Wait also for a reponse from setupInParent called from setup-in-child.js + let onParent = (_, topic, args) => { + Services.obs.removeObserver(onParent, "test:setupParent", false); + args = JSON.parse(args); + + is(args[0], true, "Got `mm` argument, a message manager"); + ok(args[1].match(/server\d+.conn\d+.child\d+/), "Got `prefix` argument"); + + cleanup(); + }; + Services.obs.addObserver(onParent, "test:setupParent", false); + + // Instanciate e10s machinery and call setupInChild + DebuggerServer.connectToChild(conn, iframe).then(actor => { + DebuggerServer.setupInChild({ + module: "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/setup-in-child.js", + setupChild: "setupChild", + args: [1, "two", {three: true}] + }); + }); + + function cleanup() { + client.close().then(function () { + DebuggerServer.destroy(); + iframe.remove(); + SimpleTest.finish() + }); + } + +} +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_styles-applied.html b/devtools/server/tests/mochitest/test_styles-applied.html new file mode 100644 index 000000000..d9fb6ec7f --- /dev/null +++ b/devtools/server/tests/mochitest/test_styles-applied.html @@ -0,0 +1,145 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/server/actors/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gStyles = null; +var gClient = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + return inspector.getPageStyle(); + }).then(styles => { + gStyles = styles; + }).then(runNextTest)); + }); +}); + +addTest(function inheritedUserStyles() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#test-node").then(node => { + return gStyles.getApplied(node, { inherited: true, filter: "user" }); + }).then(applied => { + ok(!applied[0].inherited, "Entry 0 should be uninherited"); + is(applied[0].rule.type, 100, "Entry 0 should be an element style"); + ok(!!applied[0].rule.href, "Element styles should have a URL"); + is(applied[0].rule.cssText, "", "Entry 0 should be an empty style"); + + is(applied[1].inherited.id, "uninheritable-rule-inheritable-style", + "Entry 1 should be inherited from the parent"); + is(applied[1].rule.type, 100, "Entry 1 should be an element style"); + is(applied[1].rule.cssText, "color: red;", "Entry 1 should have the expected cssText"); + + is(applied[2].inherited.id, "inheritable-rule-inheritable-style", + "Entry 2 should be inherited from the parent's parent"); + is(applied[2].rule.type, 100, "Entry 2 should be an element style"); + is(applied[2].rule.cssText, "color: blue;", "Entry 2 should have the expected cssText"); + + is(applied[3].inherited.id, "inheritable-rule-inheritable-style", + "Entry 3 should be inherited from the parent's parent"); + is(applied[3].rule.type, 1, "Entry 3 should be a rule style"); + is(applied[3].rule.cssText, "font-size: 15px;", "Entry 3 should have the expected cssText"); + ok(!applied[3].matchedSelectors, "Shouldn't get matchedSelectors with this request."); + + is(applied[4].inherited.id, "inheritable-rule-uninheritable-style", + "Entry 4 should be inherited from the parent's parent"); + is(applied[4].rule.type, 1, "Entry 4 should be an rule style"); + is(applied[4].rule.cssText, "font-size: 15px;", "Entry 4 should have the expected cssText"); + ok(!applied[4].matchedSelectors, "Shouldn't get matchedSelectors with this request."); + + is(applied.length, 5, "Should have 5 rules."); + }).then(runNextTest)); +}); + +addTest(function inheritedSystemStyles() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#test-node").then(node => { + return gStyles.getApplied(node, { inherited: true, filter: "ua" }); + }).then(applied => { + // If our system stylesheets are prone to churn, this might be a fragile + // test. If you're here because of that I apologize, file a bug + // and we can find a different way to test. + + ok(!applied[1].inherited, "Entry 1 should not be inherited"); + ok(!applied[1].rule.parentStyleSheet.system, "Entry 1 should be a system style"); + is(applied[1].rule.type, 1, "Entry 1 should be a rule style"); + + is(applied.length, 12, "Should have 12 rules."); + }).then(runNextTest)); +}); + +addTest(function noInheritedStyles() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#test-node").then(node => { + return gStyles.getApplied(node, { inherited: false, filter: "user" }); + }).then(applied => { + ok(!applied[0].inherited, "Entry 0 should be uninherited"); + is(applied[0].rule.type, 100, "Entry 0 should be an element style"); + is(applied[0].rule.cssText, "", "Entry 0 should be an empty style"); + is(applied.length, 1, "Should have 1 rule."); + }).then(runNextTest)); +}); + +addTest(function matchedSelectors() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#test-node").then(node => { + return gStyles.getApplied(node, { + inherited: true, filter: "user", matchedSelectors: true + }); + }).then(applied => { + is(applied[3].matchedSelectors[0], ".inheritable-rule", "Entry 3 should have a matched selector"); + is(applied[4].matchedSelectors[0], ".inheritable-rule", "Entry 4 should have a matched selector"); + }).then(runNextTest)); +}); + +addTest(function testMediaQuery() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#mediaqueried").then(node => { + return gStyles.getApplied(node, { + inherited: false, filter: "user", matchedSelectors: true + }); + }).then(applied => { + is(applied[1].rule.type, 1, "Entry 1 is a rule style"); + is(applied[1].rule.parentRule.type, 4, "Entry 1's parent rule is a media rule"); + is(applied[1].rule.media[0], "screen", "Entry 1's rule has the expected medium"); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gStyles; + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-styles-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_styles-computed.html b/devtools/server/tests/mochitest/test_styles-computed.html new file mode 100644 index 000000000..c70adc8eb --- /dev/null +++ b/devtools/server/tests/mochitest/test_styles-computed.html @@ -0,0 +1,139 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/server/actors/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gStyles = null; +var gClient = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + return inspector.getPageStyle(); + }).then(styles => { + gStyles = styles; + }).then(runNextTest)); + }); +}); + +addTest(function testComputed() { + let localNode = gInspectee.querySelector("#computed-test-node"); + let elementStyle = null; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#computed-test-node").then(node => { + return gStyles.getComputed(node, {}); + }).then(computed => { + // Test a smattering of properties that include some system-defined + // props, some props that were defined in this node's stylesheet, + // and some default props. + is(computed["white-space"].value, "normal", "Default value should appear"); + is(computed["display"].value, "block", "System stylesheet item should appear"); + is(computed["cursor"].value, "crosshair", "Included stylesheet rule should appear"); + is(computed["color"].value, "rgb(255, 0, 0)", "Inherited style attribute should appear"); + is(computed["font-size"].value, "15px", "Inherited inline rule should appear"); + + // We didn't request markMatched, so these shouldn't be set + ok(!computed["cursor"].matched, "Didn't ask for matched, shouldn't get it"); + ok(!computed["color"].matched, "Didn't ask for matched, shouldn't get it"); + ok(!computed["font-size"].matched, "Didn't ask for matched, shouldn't get it"); + }).then(runNextTest)); +}); + +addTest(function testComputedUserMatched() { + let localNode = gInspectee.querySelector("#computed-test-node"); + let elementStyle = null; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#computed-test-node").then(node => { + return gStyles.getComputed(node, { filter: "user", markMatched: true }); + }).then(computed => { + ok(!computed["white-space"].matched, "Default style shouldn't match"); + ok(!computed["display"].matched, "Only user styles should match"); + ok(computed["cursor"].matched, "Asked for matched, should get it"); + ok(computed["color"].matched, "Asked for matched, should get it"); + ok(computed["font-size"].matched, "Asked for matched, should get it"); + }).then(runNextTest)); +}); + +addTest(function testComputedSystemMatched() { + let localNode = gInspectee.querySelector("#computed-test-node"); + let elementStyle = null; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#computed-test-node").then(node => { + return gStyles.getComputed(node, { filter: "ua", markMatched: true }); + }).then(computed => { + ok(!computed["white-space"].matched, "Default style shouldn't match"); + ok(computed["display"].matched, "System stylesheets should match"); + ok(computed["cursor"].matched, "Asked for matched, should get it"); + ok(computed["color"].matched, "Asked for matched, should get it"); + ok(computed["font-size"].matched, "Asked for matched, should get it"); + }).then(runNextTest)); +}); + +addTest(function testComputedUserOnlyMatched() { + let localNode = gInspectee.querySelector("#computed-test-node"); + let elementStyle = null; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#computed-test-node").then(node => { + return gStyles.getComputed(node, { filter: "user", onlyMatched: true }); + }).then(computed => { + ok(!("white-space" in computed), "Default style shouldn't exist"); + ok(!("display" in computed), "System stylesheets shouldn't exist"); + ok(("cursor" in computed), "User items should exist."); + ok(("color" in computed), "User items should exist."); + ok(("font-size" in computed), "User items should exist."); + }).then(runNextTest)); +}); + +addTest(function testComputedSystemOnlyMatched() { + let localNode = gInspectee.querySelector("#computed-test-node"); + let elementStyle = null; + promiseDone(gWalker.querySelector(gWalker.rootNode, "#computed-test-node").then(node => { + return gStyles.getComputed(node, { filter: "ua", onlyMatched: true }); + }).then(computed => { + ok(!("white-space" in computed), "Default style shouldn't exist"); + ok(("display" in computed), "System stylesheets should exist"); + ok(("cursor" in computed), "User items should exist."); + ok(("color" in computed), "User items should exist."); + ok(("font-size" in computed), "User items should exist."); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gStyles; + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-styles-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_styles-layout.html b/devtools/server/tests/mochitest/test_styles-layout.html new file mode 100644 index 000000000..b2134b0c9 --- /dev/null +++ b/devtools/server/tests/mochitest/test_styles-layout.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML> +<html> +<head> +<meta charset="utf-8"> +<title>Test for Bug 1175040 - PageStyleActor.getLayout</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"> +<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> +<script type="application/javascript;version=1.8"> +"use strict"; + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +}; + +let gWalker = null; +let gStyles = null; + +addTest(function() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gWalker = walker; + return inspector.getPageStyle(); + }).then(styles => { + gStyles = styles; + }).then(runNextTest)); + }); +}); + +addTest(function() { + ok(gStyles.getLayout, "The PageStyleActor has a getLayout method"); + runNextTest(); +}); + +addAsyncTest(function*() { + let node = yield gWalker.querySelector(gWalker.rootNode, "#layout-element"); + let layout = yield gStyles.getLayout(node, {}); + + let properties = ["width", "height", + "margin-top", "margin-right", "margin-bottom", + "margin-left", "padding-top", "padding-right", + "padding-bottom", "padding-left", "border-top-width", + "border-right-width", "border-bottom-width", + "border-left-width", "z-index", "box-sizing", "display", + "position"]; + for (let prop of properties) { + ok((prop in layout), "The layout object returned has " + prop); + } + + runNextTest(); +}); + +addAsyncTest(function*() { + let node = yield gWalker.querySelector(gWalker.rootNode, "#layout-element"); + let layout = yield gStyles.getLayout(node, {}); + + let expected = { + "box-sizing": "border-box", + "position": "absolute", + "z-index": "2", + "display": "block", + "width": 50, + "height": 50, + "margin-top": "10px", + "margin-right": "20px", + "margin-bottom": "30px", + "margin-left": "0px" + }; + + for (let name in expected) { + is(layout[name], expected[name], "The " + name + " property is correct"); + } + + runNextTest(); +}); + +addAsyncTest(function*() { + let node = yield gWalker.querySelector(gWalker.rootNode, + "#layout-auto-margin-element"); + + let layout = yield gStyles.getLayout(node, {}); + ok(!("autoMargins" in layout), + "By default, getLayout doesn't return auto margins"); + + layout = yield gStyles.getLayout(node, {autoMargins: true}); + ok(("autoMargins" in layout), + "getLayout does return auto margins when asked to"); + is(layout.autoMargins.left, "auto", "The left margin is auto"); + is(layout.autoMargins.right, "auto", "The right margin is auto"); + ok(!layout.autoMargins.bottom, "The bottom margin is not auto"); + ok(!layout.autoMargins.top, "The top margin is not auto"); + + runNextTest(); +}); + +addTest(function() { + gStyles = gWalker = null; + runNextTest(); +}); + +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175040">Mozilla Bug 1175040</a> +<a id="inspectorContent" target="_blank" href="inspector-styles-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_styles-matched.html b/devtools/server/tests/mochitest/test_styles-matched.html new file mode 100644 index 000000000..4d24fbe70 --- /dev/null +++ b/devtools/server/tests/mochitest/test_styles-matched.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/server/actors/inspector"); +const CssLogic = require("devtools/shared/inspector/css-logic"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gStyles = null; +var gClient = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + return inspector.getPageStyle(); + }).then(styles => { + gStyles = styles; + }).then(runNextTest)); + }); +}); + +addTest(function testMatchedStyles() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#matched-test-node").then(node => { + return gStyles.getMatchedSelectors(node, "font-size", {}); + }).then(matched => { + is(matched[0].sourceText, "this.style", "First match comes from the element style"); + is(matched[0].selector, "@element.style", "Element style has a special selector"); + is(matched[0].value, "10px", "First match has the expected value"); + is(matched[0].status, CssLogic.STATUS.BEST, "First match is the best match") + is(matched[0].rule.type, 100, "First match is an element style"); + is(matched[0].rule.href, gInspectee.defaultView.location.href, "Node style comes from this document") + + is(matched[1].sourceText, ".inheritable-rule", "Second match comes from a rule"); + is(matched[1].selector, ".inheritable-rule", "Second style has a selector"); + is(matched[1].value, "15px", "Second match has the expected value"); + is(matched[1].status, CssLogic.STATUS.PARENT_MATCH, "Second match is from the parent") + is(matched[1].rule.parentStyleSheet.href, null, "Inline stylesheet shouldn't have an href"); + is(matched[1].rule.parentStyleSheet.nodeHref, gInspectee.defaultView.location.href, "Inline stylesheet's nodeHref should match the current document"); + ok(!matched[1].rule.parentStyleSheet.system, "Inline stylesheet shouldn't be a system stylesheet."); + }).then(runNextTest)); +}); + +addTest(function testSystemStyles() { + let testNode = null; + + promiseDone(gWalker.querySelector(gWalker.rootNode, "#matched-test-node").then(node => { + testNode = node; + return gStyles.getMatchedSelectors(testNode, "display", { filter: "user" }); + }).then(matched => { + is(matched.length, 0, "No user selectors apply to this rule."); + return gStyles.getMatchedSelectors(testNode, "display", { filter: "ua" }); + }).then(matched => { + is(matched[0].selector, "div", "Should match system div selector"); + is(matched[0].value, "block"); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gStyles; + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-styles-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_styles-modify.html b/devtools/server/tests/mochitest/test_styles-modify.html new file mode 100644 index 000000000..5a8e20bc3 --- /dev/null +++ b/devtools/server/tests/mochitest/test_styles-modify.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id= +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/server/actors/inspector"); +const {isCssPropertyKnown} = require("devtools/server/actors/css-properties"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gStyles = null; +var gClient = null; + +addAsyncTest(function* setup() { + let url = document.getElementById("inspectorContent").href; + let inspector; + + yield new Promise(resolve => { + attachURL(url, function(err, client, tab, doc) { + gInspectee = doc; + gClient = client; + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + inspector = InspectorFront(client, tab); + resolve(); + }); + }); + + gWalker = yield inspector.getWalker(); + gStyles = yield inspector.getPageStyle(); + + runNextTest(); +}); + +addAsyncTest(function* modifyProperties() { + let localNode = gInspectee.querySelector("#inheritable-rule-inheritable-style"); + + let node = yield gWalker.querySelector(gWalker.rootNode, + "#inheritable-rule-inheritable-style"); + + let applied = yield gStyles.getApplied(node, + { inherited: false, filter: "user" }); + + let elementStyle = applied[0].rule; + is(elementStyle.cssText, localNode.style.cssText, "Got expected css text"); + + // Change an existing property... + yield setProperty(elementStyle, 0, "color", "black"); + // Create a new property + yield setProperty(elementStyle, 1, "background-color", "green"); + + // Create a new property and then change it immediately. + yield setProperty(elementStyle, 2, "border", "1px solid black"); + yield setProperty(elementStyle, 2, "border", "2px solid black"); + + is(elementStyle.cssText, + "color: black; background-color: green; border: 2px solid black;", + "Should have expected cssText"); + is(elementStyle.cssText, localNode.style.cssText, + "Local node and style front match."); + + // Remove all the properties + yield removeProperty(elementStyle, 0, "color"); + yield removeProperty(elementStyle, 0, "background-color"); + yield removeProperty(elementStyle, 0, "border"); + + is(elementStyle.cssText, "", "Should have expected cssText"); + is(elementStyle.cssText, localNode.style.cssText, + "Local node and style front match."); + + runNextTest(); +}); + +function* setProperty(rule, index, name, value) { + let changes = rule.startModifyingProperties(isCssPropertyKnown); + changes.setProperty(index, name, value); + yield changes.apply(); +} + +function* removeProperty(rule, index, name) { + let changes = rule.startModifyingProperties(isCssPropertyKnown); + changes.removeProperty(index, name); + yield changes.apply(); +} + +addTest(function cleanup() { + delete gStyles; + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a> +<a id="inspectorContent" target="_blank" href="inspector-styles-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_styles-svg.html b/devtools/server/tests/mochitest/test_styles-svg.html new file mode 100644 index 000000000..51a84420c --- /dev/null +++ b/devtools/server/tests/mochitest/test_styles-svg.html @@ -0,0 +1,70 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=921191 +Bug 921191 - allow inspection/editing of SVG elements' CSS properties +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug </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"> + <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script> + <script type="application/javascript;version=1.8"> +const inspector = require("devtools/server/actors/inspector"); + +window.onload = function() { + SimpleTest.waitForExplicitFinish(); + runNextTest(); +} + +var gWalker = null; +var gStyles = null; +var gClient = null; + +addTest(function setup() { + let url = document.getElementById("inspectorContent").href; + attachURL(url, function(err, client, tab, doc) { + let {InspectorFront} = require("devtools/shared/fronts/inspector"); + let inspector = InspectorFront(client, tab); + promiseDone(inspector.getWalker().then(walker => { + ok(walker, "getWalker() should return an actor."); + gClient = client; + gWalker = walker; + return inspector.getPageStyle(); + }).then(styles => { + gStyles = styles; + }).then(runNextTest)); + }); +}); + +addTest(function inheritedUserStyles() { + promiseDone(gWalker.querySelector(gWalker.rootNode, "#svgcontent rect").then(node => { + return gStyles.getApplied(node, { inherited: true, filter: "user" }); + }).then(applied => { + is(applied.length, 2, "Should have 2 rules"); + is(applied[1].rule.cssText, "fill: rgb(1, 2, 3);", "cssText is right"); + }).then(runNextTest)); +}); + +addTest(function cleanup() { + delete gStyles; + delete gWalker; + delete gClient; + runNextTest(); +}); + + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=921191">Mozilla Bug 921191</a> +<a id="inspectorContent" target="_blank" href="inspector-styles-data.html">Test Document</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_unsafeDereference.html b/devtools/server/tests/mochitest/test_unsafeDereference.html new file mode 100644 index 000000000..df44fac51 --- /dev/null +++ b/devtools/server/tests/mochitest/test_unsafeDereference.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=837723 + +When we use Debugger.Object.prototype.unsafeDereference to get a non-D.O +reference to a content object in chrome, that reference should be via an +xray wrapper. +--> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug 837723</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> +<pre id="test"> +<script> + +Components.utils.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +window.onload = function () { + SimpleTest.waitForExplicitFinish(); + + var iframe = document.createElement("iframe"); + iframe.src = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/nonchrome_unsafeDereference.html"; + + iframe.onload = function () { + var dbg = new Debugger; + var contentDO = dbg.addDebuggee(iframe.contentWindow); + var xhrDesc = contentDO.getOwnPropertyDescriptor('xhr'); + + isnot(xhrDesc, undefined, "xhr should be visible as property of content global"); + isnot(xhrDesc.value, undefined, "xhr should have a value"); + + var xhr = xhrDesc.value.unsafeDereference(); + + is(typeof xhr, "object", "we should be able to deference xhr's value's D.O"); + is(xhr.timeout, 1742, "chrome should see the xhr's 'timeout' property"); + is(xhr.expando, undefined, "chrome should not see the xhr's 'expando' property"); + + SimpleTest.finish(); + } + + document.body.appendChild(iframe); +} + +</script> +</pre> +</body> +</html> diff --git a/devtools/server/tests/mochitest/test_websocket-server.html b/devtools/server/tests/mochitest/test_websocket-server.html new file mode 100644 index 000000000..583d96dd9 --- /dev/null +++ b/devtools/server/tests/mochitest/test_websocket-server.html @@ -0,0 +1,82 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Mozilla Bug</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<script> +window.onload = function() { + const { Constructor: CC, utils: Cu } = Components; + const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); + const { Task } = require("devtools/shared/task"); + const WebSocketServer = require("devtools/server/websocket-server"); + + const ServerSocket = CC("@mozilla.org/network/server-socket;1", + "nsIServerSocket", "init"); + + add_task(function* () { + // Create a TCP server on auto-assigned port + let server = new ServerSocket(-1, true, -1); + ok(server, `Launched WebSocket server on port ${server.port}`); + server.asyncListen({ + onSocketAccepted: Task.async(function* (socket, transport) { + info("Accepted incoming connection"); + let input = transport.openInputStream(0, 0, 0); + let output = transport.openOutputStream(0, 0, 0); + + // Perform the WebSocket handshake + let webSocket = yield WebSocketServer.accept(transport, input, output); + + // Echo the received message back to the sender + webSocket.onmessage = ({ data }) => { + info("Server received message, echoing back"); + webSocket.send(data); + }; + }), + + onStopListening(socket, status) { + info(`Server stopped listening with status: ${status}`); + } + }); + + SimpleTest.registerCleanupFunction(() => { + server.close(); + }); + + // Create client connection + let client = yield new Promise((resolve, reject) => { + let socket = new WebSocket(`ws://localhost:${server.port}`); + socket.onopen = () => resolve(socket); + socket.onerror = reject; + }); + ok(client, `Created WebSocket connection to port ${server.port}`); + + // Create a promise that resolves when the WebSocket closes + let closed = new Promise(resolve => { + client.onclose = resolve; + }); + + // Send a message + let message = "hello there"; + client.send(message); + info("Sent a message to server"); + // Check that it was echoed + let echoedMessage = yield new Promise((resolve, reject) => { + client.onmessage = ({ data }) => resolve(data); + client.onerror = reject; + }); + + is(echoedMessage, message, "Echoed message matches"); + + // Close the connection + client.close(); + yield closed; + }); +} +</script> +</body> +</html> |