import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,6 @@
"use strict";
module.exports = {
// Extend from the common devtools mochitest eslintrc config.
"extends": "../../../.eslintrc.xpcshell.js"
};

View file

@ -0,0 +1,170 @@
<!DOCTYPE html>
<style>
.not-animated {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: #eee;
}
.simple-animation {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: red;
animation: move 200s infinite;
}
.multiple-animations {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: #eee;
animation: move 200s infinite , glow 100s 5;
animation-timing-function: ease-out;
animation-direction: reverse;
animation-fill-mode: both;
}
.transition {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: #f06;
transition: width 500s ease-out;
}
.transition.get-round {
width: 200px;
}
.long-animation {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: gold;
animation: move 100s;
}
.short-animation {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: purple;
animation: move 1s;
}
.delayed-animation {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: rebeccapurple;
animation: move 200s 5s infinite;
}
.delayed-transition {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: black;
transition: width 500s 3s;
}
.delayed-transition.get-round {
width: 200px;
}
.delayed-multiple-animations {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: green;
animation: move .5s 1s 10, glow 1s .75s 30;
}
.multiple-animations-2 {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: blue;
animation: move .5s, glow 100s 2s infinite, grow 300s 1s 100;
}
.all-transitions {
position: absolute;
top: 0;
right: 0;
width: 50px;
height: 50px;
background: blue;
transition: all .2s;
}
.all-transitions.expand {
width: 200px;
height: 100px;
}
@keyframes move {
100% {
transform: translateY(100px);
}
}
@keyframes glow {
100% {
background: yellow;
}
}
@keyframes grow {
100% {
width: 100px;
}
}
</style>
<div class="not-animated"></div>
<div class="simple-animation"></div>
<div class="multiple-animations"></div>
<div class="transition"></div>
<div class="long-animation"></div>
<div class="short-animation"></div>
<div class="delayed-animation"></div>
<div class="delayed-transition"></div>
<div class="delayed-multiple-animations"></div>
<div class="multiple-animations-2"></div>
<div class="all-transitions"></div>
<script type="text/javascript">
// Get the transitions started when the page loads
var players;
addEventListener("load", function() {
document.querySelector(".transition").classList.add("get-round");
document.querySelector(".delayed-transition").classList.add("get-round");
});
</script>

View file

@ -0,0 +1,97 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
animation.html
doc_allocations.html
doc_force_cc.html
doc_force_gc.html
doc_innerHTML.html
doc_perf.html
navigate-first.html
navigate-second.html
storage-dynamic-windows.html
storage-listings.html
storage-unsecured-iframe.html
storage-updates.html
storage-secured-iframe.html
stylesheets-nested-iframes.html
timeline-iframe-child.html
timeline-iframe-parent.html
director-script-target.html
storage-helpers.js
!/devtools/server/tests/mochitest/hello-actor.js
[browser_animation_emitMutations.js]
[browser_animation_getFrames.js]
[browser_animation_getProperties.js]
[browser_animation_getMultipleStates.js]
[browser_animation_getPlayers.js]
[browser_animation_getStateAfterFinished.js]
[browser_animation_getSubTreeAnimations.js]
[browser_animation_keepFinished.js]
[browser_animation_playerState.js]
[browser_animation_playPauseIframe.js]
[browser_animation_playPauseSeveral.js]
[browser_animation_reconstructState.js]
[browser_animation_refreshTransitions.js]
[browser_animation_setCurrentTime.js]
[browser_animation_setPlaybackRate.js]
[browser_animation_simple.js]
[browser_animation_updatedState.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_canvasframe_helper_01.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_canvasframe_helper_02.js]
[browser_canvasframe_helper_03.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_canvasframe_helper_04.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_canvasframe_helper_05.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_canvasframe_helper_06.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_markers-cycle-collection.js]
[browser_markers-docloading-01.js]
[browser_markers-docloading-02.js]
[browser_markers-docloading-03.js]
[browser_markers-gc.js]
[browser_markers-minor-gc.js]
[browser_markers-parse-html.js]
[browser_markers-styles.js]
[browser_markers-timestamp.js]
[browser_navigateEvents.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_perf-allocation-data.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_perf-profiler-01.js]
[browser_perf-profiler-02.js]
skip-if = true # Needs to be updated for async actor destruction
[browser_perf-profiler-03.js]
skip-if = true # Needs to be updated for async actor destruction
[browser_perf-realtime-markers.js]
[browser_perf-recording-actor-01.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_perf-recording-actor-02.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_perf-samples-01.js]
[browser_perf-samples-02.js]
#[browser_perf-front-profiler-01.js] bug 1077464
#[browser_perf-front-profiler-05.js] bug 1077464
#[browser_perf-front-profiler-06.js]
[browser_storage_dynamic_windows.js]
[browser_storage_listings.js]
[browser_storage_updates.js]
[browser_stylesheets_getTextEmpty.js]
[browser_stylesheets_nested-iframes.js]
[browser_timeline.js]
[browser_timeline_actors.js]
[browser_timeline_iframes.js]
[browser_directorscript_actors_exports.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_directorscript_actors_error_events.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_directorscript_actors.js]
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
[browser_register_actor.js]

View file

@ -0,0 +1,62 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the AnimationsActor emits events about changed animations on a
// node after getAnimationPlayersForNode was called on that node.
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
info("Retrieve a non-animated node");
let node = yield walker.querySelector(walker.rootNode, ".not-animated");
info("Retrieve the animation player for the node");
let players = yield animations.getAnimationPlayersForNode(node);
is(players.length, 0, "The node has no animation players");
info("Listen for new animations");
let onMutations = once(animations, "mutations");
info("Add a couple of animation on the node");
yield node.modifyAttributes([
{attributeName: "class", newValue: "multiple-animations"}
]);
let changes = yield onMutations;
ok(true, "The mutations event was emitted");
is(changes.length, 2, "There are 2 changes in the mutation event");
ok(changes.every(({type}) => type === "added"), "Both changes are additions");
let names = changes.map(c => c.player.initialState.name).sort();
is(names[0], "glow", "The animation 'glow' was added");
is(names[1], "move", "The animation 'move' was added");
info("Store the 2 new players for comparing later");
let p1 = changes[0].player;
let p2 = changes[1].player;
info("Listen for removed animations");
onMutations = once(animations, "mutations");
info("Remove the animation css class on the node");
yield node.modifyAttributes([
{attributeName: "class", newValue: "not-animated"}
]);
changes = yield onMutations;
ok(true, "The mutations event was emitted");
is(changes.length, 2, "There are 2 changes in the mutation event");
ok(changes.every(({type}) => type === "removed"), "Both are removals");
ok(changes[0].player === p1 || changes[0].player === p2,
"The first removed player was one of the previously added players");
ok(changes[1].player === p1 || changes[1].player === p2,
"The second removed player was one of the previously added players");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,32 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the AnimationPlayerActor exposes a getFrames method that returns
// the list of keyframes in the animation.
const URL = MAIN_DOMAIN + "animation.html";
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
info("Get the test node and its animation front");
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
let [player] = yield animations.getAnimationPlayersForNode(node);
ok(player.getFrames, "The front has the getFrames method");
let frames = yield player.getFrames();
is(frames.length, 2, "The correct number of keyframes was retrieved");
ok(frames[0].transform, "Frame 0 has the transform property");
ok(frames[1].transform, "Frame 1 has the transform property");
// Note that we don't really test the content of the frame object here on
// purpose. This object comes straight out of the web animations API
// unmodified.
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,55 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({
set: [["dom.ipc.processCount", 1]]
});
});
// Check that the duration, iterationCount and delay are retrieved correctly for
// multiple animations.
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
yield playerHasAnInitialState(walker, animations);
yield client.close();
gBrowser.removeCurrentTab();
});
function* playerHasAnInitialState(walker, animations) {
let state = yield getAnimationStateForNode(walker, animations,
".delayed-multiple-animations", 0);
ok(state.duration, 50000,
"The duration of the first animation is correct");
ok(state.iterationCount, 10,
"The iterationCount of the first animation is correct");
ok(state.delay, 1000,
"The delay of the first animation is correct");
state = yield getAnimationStateForNode(walker, animations,
".delayed-multiple-animations", 1);
ok(state.duration, 100000,
"The duration of the secon animation is correct");
ok(state.iterationCount, 30,
"The iterationCount of the secon animation is correct");
ok(state.delay, 750,
"The delay of the secon animation is correct");
}
function* getAnimationStateForNode(walker, animations, selector, playerIndex) {
let node = yield walker.querySelector(walker.rootNode, selector);
let players = yield animations.getAnimationPlayersForNode(node);
let player = players[playerIndex];
yield player.ready();
let state = yield player.getCurrentState();
return state;
}

View file

@ -0,0 +1,63 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check the output of getAnimationPlayersForNode
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
yield theRightNumberOfPlayersIsReturned(walker, animations);
yield playersCanBePausedAndResumed(walker, animations);
yield client.close();
gBrowser.removeCurrentTab();
});
function* theRightNumberOfPlayersIsReturned(walker, animations) {
let node = yield walker.querySelector(walker.rootNode, ".not-animated");
let players = yield animations.getAnimationPlayersForNode(node);
is(players.length, 0,
"0 players were returned for the unanimated node");
node = yield walker.querySelector(walker.rootNode, ".simple-animation");
players = yield animations.getAnimationPlayersForNode(node);
is(players.length, 1,
"One animation player was returned");
node = yield walker.querySelector(walker.rootNode, ".multiple-animations");
players = yield animations.getAnimationPlayersForNode(node);
is(players.length, 2,
"Two animation players were returned");
node = yield walker.querySelector(walker.rootNode, ".transition");
players = yield animations.getAnimationPlayersForNode(node);
is(players.length, 1,
"One animation player was returned for the transitioned node");
}
function* playersCanBePausedAndResumed(walker, animations) {
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
let [player] = yield animations.getAnimationPlayersForNode(node);
yield player.ready();
ok(player.initialState,
"The player has an initialState");
ok(player.getCurrentState,
"The player has the getCurrentState method");
is(player.initialState.playState, "running",
"The animation is currently running");
yield player.pause();
let state = yield player.getCurrentState();
is(state.playState, "paused",
"The animation is now paused");
yield player.play();
state = yield player.getCurrentState();
is(state.playState, "running",
"The animation is now running again");
}

View file

@ -0,0 +1,36 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the AnimationPlayerActor exposes a getProperties method that
// returns the list of animated properties in the animation.
const URL = MAIN_DOMAIN + "animation.html";
add_task(function* () {
let {client, walker, animations} = yield initAnimationsFrontForUrl(URL);
info("Get the test node and its animation front");
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
let [player] = yield animations.getAnimationPlayersForNode(node);
ok(player.getProperties, "The front has the getProperties method");
let properties = yield player.getProperties();
is(properties.length, 1, "The correct number of properties was retrieved");
let propertyObject = properties[0];
is(propertyObject.name, "transform", "Property 0 is transform");
is(propertyObject.values.length, 2,
"The correct number of property values was retrieved");
// Note that we don't really test the content of the frame object here on
// purpose. This object comes straight out of the web animations API
// unmodified.
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,55 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the right duration/iterationCount/delay are retrieved even when
// the node has multiple animations and one of them already ended before getting
// the player objects.
// See devtools/server/actors/animation.js |getPlayerIndex| for more
// information.
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
info("Retrieve a non animated node");
let node = yield walker.querySelector(walker.rootNode, ".not-animated");
info("Apply the multiple-animations-2 class to start the animations");
yield node.modifyAttributes([
{attributeName: "class", newValue: "multiple-animations-2"}
]);
info("Get the list of players, by the time this executes, the first, " +
"short, animation should have ended.");
let players = yield animations.getAnimationPlayersForNode(node);
if (players.length === 3) {
info("The short animation hasn't ended yet, wait for a bit.");
// The animation lasts for 500ms, so 1000ms should do it.
yield new Promise(resolve => setTimeout(resolve, 1000));
info("And get the list again");
players = yield animations.getAnimationPlayersForNode(node);
}
is(players.length, 2, "2 animations remain on the node");
is(players[0].state.duration, 100000,
"The duration of the first animation is correct");
is(players[0].state.delay, 2000,
"The delay of the first animation is correct");
is(players[0].state.iterationCount, null,
"The iterationCount of the first animation is correct");
is(players[1].state.duration, 300000,
"The duration of the second animation is correct");
is(players[1].state.delay, 1000,
"The delay of the second animation is correct");
is(players[1].state.iterationCount, 100,
"The iterationCount of the second animation is correct");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,38 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the AnimationsActor can retrieve all animations inside a node's
// subtree (but not going into iframes).
const URL = MAIN_DOMAIN + "animation.html";
add_task(function* () {
info("Creating a test document with 2 iframes containing animated nodes");
let {client, walker, animations} = yield initAnimationsFrontForUrl(
"data:text/html;charset=utf-8," +
"<iframe id='iframe' src='" + URL + "'></iframe>");
info("Try retrieving all animations from the root doc's <body> node");
let rootBody = yield walker.querySelector(walker.rootNode, "body");
let players = yield animations.getAnimationPlayersForNode(rootBody);
is(players.length, 0, "The node has no animation players");
info("Retrieve all animations from the iframe's <body> node");
let iframe = yield walker.querySelector(walker.rootNode, "#iframe");
let {nodes} = yield walker.children(iframe);
let frameBody = yield walker.querySelector(nodes[0], "body");
players = yield animations.getAnimationPlayersForNode(frameBody);
// Testing for a hard-coded number of animations here would intermittently
// fail depending on how fast or slow the test is (indeed, the test page
// contains short transitions, and delayed animations). So just make sure we
// at least have the infinitely running animations.
ok(players.length >= 4, "All subtree animations were retrieved");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,54 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the AnimationsActor doesn't report finished animations as removed.
// Indeed, animations that only have the "finished" playState can be modified
// still, so we want the AnimationsActor to preserve the corresponding
// AnimationPlayerActor.
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
info("Retrieve a non-animated node");
let node = yield walker.querySelector(walker.rootNode, ".not-animated");
info("Retrieve the animation player for the node");
let players = yield animations.getAnimationPlayersForNode(node);
is(players.length, 0, "The node has no animation players");
info("Listen for new animations");
let reportedMutations = [];
function onMutations(mutations) {
reportedMutations = [...reportedMutations, ...mutations];
}
animations.on("mutations", onMutations);
info("Add a short animation on the node");
yield node.modifyAttributes([
{attributeName: "class", newValue: "short-animation"}
]);
info("Wait for longer than the animation's duration");
yield wait(2000);
players = yield animations.getAnimationPlayersForNode(node);
is(players.length, 0, "The added animation is surely finished");
is(reportedMutations.length, 1, "Only one mutation was reported");
is(reportedMutations[0].type, "added", "The mutation was an addition");
animations.off("mutations", onMutations);
yield client.close();
gBrowser.removeCurrentTab();
});
function wait(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}

View file

@ -0,0 +1,51 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the AnimationsActor can pause/play all animations even those
// within iframes.
const URL = MAIN_DOMAIN + "animation.html";
add_task(function* () {
info("Creating a test document with 2 iframes containing animated nodes");
let {client, walker, animations} = yield initAnimationsFrontForUrl(
"data:text/html;charset=utf-8," +
"<iframe id='i1' src='" + URL + "'></iframe>" +
"<iframe id='i2' src='" + URL + "'></iframe>");
info("Getting the 2 iframe container nodes and animated nodes in them");
let nodeInFrame1 = yield getNodeInFrame(walker, "#i1", ".simple-animation");
let nodeInFrame2 = yield getNodeInFrame(walker, "#i2", ".simple-animation");
info("Pause all animations in the test document");
yield animations.pauseAll();
yield checkState(animations, nodeInFrame1, "paused");
yield checkState(animations, nodeInFrame2, "paused");
info("Play all animations in the test document");
yield animations.playAll();
yield checkState(animations, nodeInFrame1, "running");
yield checkState(animations, nodeInFrame2, "running");
yield client.close();
gBrowser.removeCurrentTab();
});
function* checkState(animations, nodeFront, playState) {
info("Getting the AnimationPlayerFront for the test node");
let [player] = yield animations.getAnimationPlayersForNode(nodeFront);
yield player.ready;
let state = yield player.getCurrentState();
is(state.playState, playState,
"The playState of the test node is " + playState);
}
function* getNodeInFrame(walker, frameSelector, nodeSelector) {
let iframe = yield walker.querySelector(walker.rootNode, frameSelector);
let {nodes} = yield walker.children(iframe);
return walker.querySelector(nodes[0], nodeSelector);
}

View file

@ -0,0 +1,92 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the AnimationsActor can pause/play all animations at once, and
// check that it can also pause/play a given list of animations at once.
// List of selectors that match "all" animated nodes in the test page.
// This list misses a bunch of animated nodes on purpose. Only the ones that
// have infinite animations are listed. This is done to avoid intermittents
// caused when finite animations are already done playing by the time the test
// runs.
const ALL_ANIMATED_NODES = [".simple-animation", ".multiple-animations",
".delayed-animation"];
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
info("Pause all animations in the test document");
yield animations.pauseAll();
yield checkStates(walker, animations, ALL_ANIMATED_NODES, "paused");
info("Play all animations in the test document");
yield animations.playAll();
yield checkStates(walker, animations, ALL_ANIMATED_NODES, "running");
info("Pause all animations in the test document using toggleAll");
yield animations.toggleAll();
yield checkStates(walker, animations, ALL_ANIMATED_NODES, "paused");
info("Play all animations in the test document using toggleAll");
yield animations.toggleAll();
yield checkStates(walker, animations, ALL_ANIMATED_NODES, "running");
info("Play all animations from multiple animated node using toggleSeveral");
let players = yield getPlayersFor(walker, animations,
[".multiple-animations"]);
is(players.length, 2, "Node has 2 animation players");
yield animations.toggleSeveral(players, false);
let state1 = yield players[0].getCurrentState();
is(state1.playState, "running",
"The playState of the first player is running");
let state2 = yield players[1].getCurrentState();
is(state2.playState, "running",
"The playState of the second player is running");
info("Pause one animation from a multiple animated node using toggleSeveral");
yield animations.toggleSeveral([players[0]], true);
state1 = yield players[0].getCurrentState();
is(state1.playState, "paused", "The playState of the first player is paused");
state2 = yield players[1].getCurrentState();
is(state2.playState, "running",
"The playState of the second player is running");
info("Play the same animation");
yield animations.toggleSeveral([players[0]], false);
state1 = yield players[0].getCurrentState();
is(state1.playState, "running",
"The playState of the first player is running");
state2 = yield players[1].getCurrentState();
is(state2.playState, "running",
"The playState of the second player is running");
yield client.close();
gBrowser.removeCurrentTab();
});
function* checkStates(walker, animations, selectors, playState) {
info("Checking the playState of all the nodes that have infinite running " +
"animations");
for (let selector of selectors) {
info("Getting the AnimationPlayerFront for node " + selector);
let [player] = yield getPlayersFor(walker, animations, selector);
yield player.ready();
yield checkPlayState(player, selector, playState);
}
}
function* getPlayersFor(walker, animations, selector) {
let node = yield walker.querySelector(walker.rootNode, selector);
return animations.getAnimationPlayersForNode(node);
}
function* checkPlayState(player, selector, expectedState) {
let state = yield player.getCurrentState();
is(state.playState, expectedState,
"The playState of node " + selector + " is " + expectedState);
}

View file

@ -0,0 +1,123 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check the animation player's initial state
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
yield playerHasAnInitialState(walker, animations);
yield playerStateIsCorrect(walker, animations);
yield client.close();
gBrowser.removeCurrentTab();
});
function* playerHasAnInitialState(walker, animations) {
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
let [player] = yield animations.getAnimationPlayersForNode(node);
ok(player.initialState, "The player front has an initial state");
ok("startTime" in player.initialState, "Player's state has startTime");
ok("currentTime" in player.initialState, "Player's state has currentTime");
ok("playState" in player.initialState, "Player's state has playState");
ok("playbackRate" in player.initialState, "Player's state has playbackRate");
ok("name" in player.initialState, "Player's state has name");
ok("duration" in player.initialState, "Player's state has duration");
ok("delay" in player.initialState, "Player's state has delay");
ok("iterationCount" in player.initialState,
"Player's state has iterationCount");
ok("fill" in player.initialState, "Player's state has fill");
ok("easing" in player.initialState, "Player's state has easing");
ok("direction" in player.initialState, "Player's state has direction");
ok("isRunningOnCompositor" in player.initialState,
"Player's state has isRunningOnCompositor");
ok("type" in player.initialState, "Player's state has type");
ok("documentCurrentTime" in player.initialState,
"Player's state has documentCurrentTime");
}
function* playerStateIsCorrect(walker, animations) {
info("Checking the state of the simple animation");
let player = yield getAnimationPlayerForNode(walker, animations,
".simple-animation", 0);
let state = yield player.getCurrentState();
is(state.name, "move", "Name is correct");
is(state.duration, 200000, "Duration is correct");
// null = infinite count
is(state.iterationCount, null, "Iteration count is correct");
is(state.fill, "none", "Fill is correct");
is(state.easing, "linear", "Easing is correct");
is(state.direction, "normal", "Direction is correct");
is(state.playState, "running", "PlayState is correct");
is(state.playbackRate, 1, "PlaybackRate is correct");
is(state.type, "cssanimation", "Type is correct");
info("Checking the state of the transition");
player =
yield getAnimationPlayerForNode(walker, animations, ".transition", 0);
state = yield player.getCurrentState();
is(state.name, "width", "Transition name matches transition property");
is(state.duration, 500000, "Transition duration is correct");
// transitions run only once
is(state.iterationCount, 1, "Transition iteration count is correct");
is(state.fill, "backwards", "Transition fill is correct");
is(state.easing, "linear", "Transition easing is correct");
is(state.direction, "normal", "Transition direction is correct");
is(state.playState, "running", "Transition playState is correct");
is(state.playbackRate, 1, "Transition playbackRate is correct");
is(state.type, "csstransition", "Transition type is correct");
// chech easing in keyframe
let keyframes = yield player.getFrames();
is(keyframes.length, 2, "Transition length of keyframe is correct");
is(keyframes[0].easing,
"ease-out", "Transition kerframes's easing is correct");
info("Checking the state of one of multiple animations on a node");
// Checking the 2nd player
player = yield getAnimationPlayerForNode(walker, animations,
".multiple-animations", 1);
state = yield player.getCurrentState();
is(state.name, "glow", "The 2nd animation's name is correct");
is(state.duration, 100000, "The 2nd animation's duration is correct");
is(state.iterationCount, 5, "The 2nd animation's iteration count is correct");
is(state.fill, "both", "The 2nd animation's fill is correct");
is(state.easing, "linear", "The 2nd animation's easing is correct");
is(state.direction, "reverse", "The 2nd animation's direction is correct");
is(state.playState, "running", "The 2nd animation's playState is correct");
is(state.playbackRate, 1, "The 2nd animation's playbackRate is correct");
// chech easing in keyframe
keyframes = yield player.getFrames();
is(keyframes.length, 2, "The 2nd animation's length of keyframe is correct");
is(keyframes[0].easing,
"ease-out", "The 2nd animation's easing of kerframes is correct");
info("Checking the state of an animation with delay");
player = yield getAnimationPlayerForNode(walker, animations,
".delayed-animation", 0);
state = yield player.getCurrentState();
is(state.delay, 5000, "The animation delay is correct");
info("Checking the state of an transition with delay");
player = yield getAnimationPlayerForNode(walker, animations,
".delayed-transition", 0);
state = yield player.getCurrentState();
is(state.delay, 3000, "The transition delay is correct");
}
function* getAnimationPlayerForNode(walker, animations, nodeSelector, index) {
let node = yield walker.querySelector(walker.rootNode, nodeSelector);
let players = yield animations.getAnimationPlayersForNode(node);
let player = players[index];
yield player.ready();
return player;
}

View file

@ -0,0 +1,38 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that, even though the AnimationPlayerActor only sends the bits of its
// state that change, the front reconstructs the whole state everytime.
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
yield playerHasCompleteStateAtAllTimes(walker, animations);
yield client.close();
gBrowser.removeCurrentTab();
});
function* playerHasCompleteStateAtAllTimes(walker, animations) {
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
let [player] = yield animations.getAnimationPlayersForNode(node);
yield player.ready();
// Get the list of state key names from the initialstate.
let keys = Object.keys(player.initialState);
// Get the state over and over again and check that the object returned
// contains all keys.
// Normally, only the currentTime will have changed in between 2 calls.
for (let i = 0; i < 10; i++) {
yield player.refreshState();
keys.forEach(key => {
ok(typeof player.state[key] !== "undefined",
"The state retrieved has key " + key);
});
}
}

View file

@ -0,0 +1,77 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// When a transition finishes, no "removed" event is sent because it may still
// be used, but when it restarts again (transitions back), then a new
// AnimationPlayerFront should be sent, and the old one should be removed.
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
info("Retrieve the test node");
let node = yield walker.querySelector(walker.rootNode, ".all-transitions");
info("Retrieve the animation players for the node");
let players = yield animations.getAnimationPlayersForNode(node);
is(players.length, 0, "The node has no animation players yet");
info("Play a transition by adding the expand class, wait for mutations");
let onMutations = expectMutationEvents(animations, 2);
let cpow = content.document.querySelector(".all-transitions");
cpow.classList.add("expand");
let reportedMutations = yield onMutations;
is(reportedMutations.length, 2, "2 mutation events were received");
is(reportedMutations[0].type, "added", "The first event was 'added'");
is(reportedMutations[1].type, "added", "The second event was 'added'");
info("Wait for the transitions to be finished");
yield waitForEnd(reportedMutations[0].player);
yield waitForEnd(reportedMutations[1].player);
info("Play the transition back by removing the class, wait for mutations");
onMutations = expectMutationEvents(animations, 4);
cpow.classList.remove("expand");
reportedMutations = yield onMutations;
is(reportedMutations.length, 4, "4 new mutation events were received");
is(reportedMutations.filter(m => m.type === "removed").length, 2,
"2 'removed' events were sent (for the old transitions)");
is(reportedMutations.filter(m => m.type === "added").length, 2,
"2 'added' events were sent (for the new transitions)");
yield client.close();
gBrowser.removeCurrentTab();
});
function expectMutationEvents(animationsFront, nbOfEvents) {
return new Promise(resolve => {
let reportedMutations = [];
function onMutations(mutations) {
reportedMutations = [...reportedMutations, ...mutations];
info("Received " + reportedMutations.length + " mutation events, " +
"expecting " + nbOfEvents);
if (reportedMutations.length === nbOfEvents) {
animationsFront.off("mutations", onMutations);
resolve(reportedMutations);
}
}
info("Start listening for mutation events from the AnimationsFront");
animationsFront.on("mutations", onMutations);
});
}
function* waitForEnd(animationFront) {
let playState;
while (playState !== "finished") {
let state = yield animationFront.getCurrentState();
playState = state.playState;
info("Wait for transition " + animationFront.state.name +
" to finish, playState=" + playState);
}
}

View file

@ -0,0 +1,74 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that a player's currentTime can be changed and that the AnimationsActor
// allows changing many players' currentTimes at once.
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
yield testSetCurrentTime(walker, animations);
yield testSetCurrentTimes(walker, animations);
yield client.close();
gBrowser.removeCurrentTab();
});
function* testSetCurrentTime(walker, animations) {
info("Retrieve an animated node");
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
info("Retrieve the animation player for the node");
let [player] = yield animations.getAnimationPlayersForNode(node);
ok(player.setCurrentTime, "Player has the setCurrentTime method");
info("Check that the setCurrentTime method can be called");
// Note that we don't check that it sets the animation to the right time here,
// this is too prone to intermittent failures, we'll do this later after
// pausing the animation. Here we merely test that the method doesn't fail.
yield player.setCurrentTime(player.initialState.currentTime + 1000);
info("Pause the animation so we can really test if setCurrentTime works");
yield player.pause();
let pausedState = yield player.getCurrentState();
info("Set the current time to currentTime + 5s");
yield player.setCurrentTime(pausedState.currentTime + 5000);
let updatedState1 = yield player.getCurrentState();
is(Math.round(updatedState1.currentTime - pausedState.currentTime), 5000,
"The currentTime was updated to +5s");
info("Set the current time to currentTime - 2s");
yield player.setCurrentTime(updatedState1.currentTime - 2000);
let updatedState2 = yield player.getCurrentState();
is(Math.round(updatedState2.currentTime - updatedState1.currentTime), -2000,
"The currentTime was updated to -2s");
}
function* testSetCurrentTimes(walker, animations) {
ok(animations.setCurrentTimes, "The AnimationsActor has the right method");
info("Retrieve multiple animated node and its animation players");
let nodeMulti = yield walker.querySelector(walker.rootNode,
".multiple-animations");
let players = (yield animations.getAnimationPlayersForNode(nodeMulti));
ok(players.length > 1, "Node has more than 1 animation player");
info("Try to set multiple current times at once");
yield animations.setCurrentTimes(players, 500, true);
info("Get the states of players and verify their correctness");
for (let i = 0; i < players.length; i++) {
let state = yield players[i].getCurrentState();
is(state.playState, "paused", `Player ${i + 1} is paused`);
is(state.currentTime, 500, `Player ${i + 1} has the right currentTime`);
}
}

View file

@ -0,0 +1,51 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that a player's playbackRate can be changed, and that multiple players
// can have their rates changed at the same time.
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
info("Retrieve an animated node");
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
info("Retrieve the animation player for the node");
let [player] = yield animations.getAnimationPlayersForNode(node);
ok(player.setPlaybackRate, "Player has the setPlaybackRate method");
info("Change the rate to 10");
yield player.setPlaybackRate(10);
info("Query the state again");
let state = yield player.getCurrentState();
is(state.playbackRate, 10, "The playbackRate was updated");
info("Change the rate back to 1");
yield player.setPlaybackRate(1);
info("Query the state again");
state = yield player.getCurrentState();
is(state.playbackRate, 1, "The playbackRate was changed back");
info("Retrieve several animation players and set their rates");
node = yield walker.querySelector(walker.rootNode, "body");
let players = yield animations.getAnimationPlayersForNode(node);
info("Change all animations in <body> to .5 rate");
yield animations.setPlaybackRates(players, .5);
info("Query their states and check they are correct");
for (let player of players) {
let state = yield player.getCurrentState();
is(state.playbackRate, .5, "The playbackRate was updated");
}
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,35 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Simple checks for the AnimationsActor
add_task(function* () {
let {client, walker, animations} = yield initAnimationsFrontForUrl(
"data:text/html;charset=utf-8,<title>test</title><div></div>");
ok(animations, "The AnimationsFront was created");
ok(animations.getAnimationPlayersForNode,
"The getAnimationPlayersForNode method exists");
ok(animations.toggleAll, "The toggleAll method exists");
ok(animations.playAll, "The playAll method exists");
ok(animations.pauseAll, "The pauseAll method exists");
let didThrow = false;
try {
yield animations.getAnimationPlayersForNode(null);
} catch (e) {
didThrow = true;
}
ok(didThrow, "An exception was thrown for a missing NodeActor");
let invalidNode = yield walker.querySelector(walker.rootNode, "title");
let players = yield animations.getAnimationPlayersForNode(invalidNode);
ok(Array.isArray(players), "An array of players was returned");
is(players.length, 0, "0 players have been returned for the invalid node");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,55 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check the animation player's updated state
add_task(function* () {
let {client, walker, animations} =
yield initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
yield playStateIsUpdatedDynamically(walker, animations);
yield client.close();
gBrowser.removeCurrentTab();
});
function* playStateIsUpdatedDynamically(walker, animations) {
info("Getting the test node (which runs a very long animation)");
// The animation lasts for 100s, to avoid intermittents.
let node = yield walker.querySelector(walker.rootNode, ".long-animation");
info("Getting the animation player front for this node");
let [player] = yield animations.getAnimationPlayersForNode(node);
yield player.ready();
let state = yield player.getCurrentState();
is(state.playState, "running",
"The playState is running while the animation is running");
info("Change the animation's currentTime to be near the end and wait for " +
"it to finish");
let onFinished = waitForAnimationPlayState(player, "finished");
// Set the currentTime to 98s, knowing that the animation lasts for 100s.
yield player.setCurrentTime(98 * 1000);
state = yield onFinished;
is(state.playState, "finished",
"The animation has ended and the state has been updated");
ok(state.currentTime > player.initialState.currentTime,
"The currentTime has been updated");
}
function* waitForAnimationPlayState(player, playState) {
let state = {};
while (state.playState !== playState) {
state = yield player.getCurrentState();
yield wait(500);
}
return state;
}
function wait(ms) {
return new Promise(r => setTimeout(r, ms));
}

View file

@ -0,0 +1,90 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Simple CanvasFrameAnonymousContentHelper tests.
// This makes sure the 'domnode' protocol actor type is known when importing
// highlighter.
require("devtools/server/actors/inspector");
const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
const {
CanvasFrameAnonymousContentHelper
} = require("devtools/server/actors/highlighters/utils/markup");
const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test";
add_task(function* () {
let browser = yield addTab(TEST_URL);
let doc = browser.contentDocument;
let nodeBuilder = () => {
let root = doc.createElement("div");
let child = doc.createElement("div");
child.style = "width:200px;height:200px;background:red;";
child.id = "child-element";
child.className = "child-element";
child.textContent = "test element";
root.appendChild(child);
return root;
};
info("Building the helper");
let env = new HighlighterEnvironment();
env.initFromWindow(doc.defaultView);
let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
ok(helper.content instanceof AnonymousContent,
"The helper owns the AnonymousContent object");
ok(helper.getTextContentForElement,
"The helper has the getTextContentForElement method");
ok(helper.setTextContentForElement,
"The helper has the setTextContentForElement method");
ok(helper.setAttributeForElement,
"The helper has the setAttributeForElement method");
ok(helper.getAttributeForElement,
"The helper has the getAttributeForElement method");
ok(helper.removeAttributeForElement,
"The helper has the removeAttributeForElement method");
ok(helper.addEventListenerForElement,
"The helper has the addEventListenerForElement method");
ok(helper.removeEventListenerForElement,
"The helper has the removeEventListenerForElement method");
ok(helper.getElement,
"The helper has the getElement method");
ok(helper.scaleRootElement,
"The helper has the scaleRootElement method");
is(helper.getTextContentForElement("child-element"), "test element",
"The text content was retrieve correctly");
is(helper.getAttributeForElement("child-element", "id"), "child-element",
"The ID attribute was retrieve correctly");
is(helper.getAttributeForElement("child-element", "class"), "child-element",
"The class attribute was retrieve correctly");
let el = helper.getElement("child-element");
ok(el, "The DOMNode-like element was created");
is(el.getTextContent(), "test element",
"The text content was retrieve correctly");
is(el.getAttribute("id"), "child-element",
"The ID attribute was retrieve correctly");
is(el.getAttribute("class"), "child-element",
"The class attribute was retrieve correctly");
info("Destroying the helper");
helper.destroy();
env.destroy();
ok(!helper.getTextContentForElement("child-element"),
"No text content was retrieved after the helper was destroyed");
ok(!helper.getAttributeForElement("child-element", "id"),
"No ID attribute was retrieved after the helper was destroyed");
ok(!helper.getAttributeForElement("child-element", "class"),
"No class attribute was retrieved after the helper was destroyed");
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,48 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the CanvasFrameAnonymousContentHelper does not insert content in
// XUL windows.
// This makes sure the 'domnode' protocol actor type is known when importing
// highlighter.
require("devtools/server/actors/inspector");
const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
const {
CanvasFrameAnonymousContentHelper
} = require("devtools/server/actors/highlighters/utils/markup");
add_task(function* () {
let browser = yield addTab("about:preferences");
let doc = browser.contentDocument;
let nodeBuilder = () => {
let root = doc.createElement("div");
let child = doc.createElement("div");
child.style = "width:200px;height:200px;background:red;";
child.id = "child-element";
child.className = "child-element";
child.textContent = "test element";
root.appendChild(child);
return root;
};
info("Building the helper");
let env = new HighlighterEnvironment();
env.initFromWindow(doc.defaultView);
let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
ok(!helper.content, "The AnonymousContent was not inserted in the window");
ok(!helper.getTextContentForElement("child-element"),
"No text content is returned");
env.destroy();
helper.destroy();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,102 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the CanvasFrameAnonymousContentHelper event handling mechanism.
// This makes sure the 'domnode' protocol actor type is known when importing
// highlighter.
require("devtools/server/actors/inspector");
const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
const {
CanvasFrameAnonymousContentHelper
} = require("devtools/server/actors/highlighters/utils/markup");
const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test";
add_task(function* () {
let browser = yield addTab(TEST_URL);
let doc = browser.contentDocument;
let nodeBuilder = () => {
let root = doc.createElement("div");
let child = doc.createElement("div");
child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
child.id = "child-element";
child.className = "child-element";
root.appendChild(child);
return root;
};
info("Building the helper");
let env = new HighlighterEnvironment();
env.initFromWindow(doc.defaultView);
let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
let el = helper.getElement("child-element");
info("Adding an event listener on the inserted element");
let mouseDownHandled = 0;
function onMouseDown(e, id) {
is(id, "child-element", "The mousedown event was triggered on the element");
ok(!e.originalTarget, "The originalTarget property isn't available");
mouseDownHandled++;
}
el.addEventListener("mousedown", onMouseDown);
info("Synthesizing an event on the inserted element");
let onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(100, 100, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled, 1, "The mousedown event was handled once on the element");
info("Synthesizing an event somewhere else");
onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(400, 400, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled, 1, "The mousedown event was not handled on the element");
info("Removing the event listener");
el.removeEventListener("mousedown", onMouseDown);
info("Synthesizing another event after the listener has been removed");
// Using a document event listener to know when the event has been synthesized.
onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(100, 100, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled, 1,
"The mousedown event hasn't been handled after the listener was removed");
info("Adding again the event listener");
el.addEventListener("mousedown", onMouseDown);
info("Destroying the helper");
env.destroy();
helper.destroy();
info("Synthesizing another event after the helper has been destroyed");
// Using a document event listener to know when the event has been synthesized.
onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(100, 100, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled, 1,
"The mousedown event hasn't been handled after the helper was destroyed");
gBrowser.removeCurrentTab();
});
function synthesizeMouseDown(x, y, win) {
// We need to make sure the inserted anonymous content can be targeted by the
// event right after having been inserted, and so we need to force a sync
// reflow.
let forceReflow = win.document.documentElement.offsetWidth;
EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
}

View file

@ -0,0 +1,98 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the CanvasFrameAnonymousContentHelper re-inserts the content when the
// page reloads.
// This makes sure the 'domnode' protocol actor type is known when importing
// highlighter.
require("devtools/server/actors/inspector");
const events = require("sdk/event/core");
const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
const {
CanvasFrameAnonymousContentHelper
} = require("devtools/server/actors/highlighters/utils/markup");
const TEST_URL_1 = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test 1";
const TEST_URL_2 = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test 2";
add_task(function* () {
let browser = yield addTab(TEST_URL_2);
let doc = browser.contentDocument;
let nodeBuilder = () => {
let root = doc.createElement("div");
let child = doc.createElement("div");
child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
child.id = "child-element";
child.className = "child-element";
child.textContent = "test content";
root.appendChild(child);
return root;
};
info("Building the helper");
let env = new HighlighterEnvironment();
env.initFromWindow(doc.defaultView);
let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
info("Get an element from the helper");
let el = helper.getElement("child-element");
info("Try to access the element");
is(el.getAttribute("class"), "child-element",
"The attribute is correct before navigation");
is(el.getTextContent(), "test content",
"The text content is correct before navigation");
info("Add an event listener on the element");
let mouseDownHandled = 0;
function onMouseDown(e, id) {
is(id, "child-element", "The mousedown event was triggered on the element");
mouseDownHandled++;
}
el.addEventListener("mousedown", onMouseDown);
info("Synthesizing an event on the element");
let onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(100, 100, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled, 1, "The mousedown event was handled once before navigation");
info("Navigating to a new page");
let loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
content.location = TEST_URL_2;
yield loaded;
doc = gBrowser.selectedBrowser.contentWindow.document;
info("Try to access the element again");
is(el.getAttribute("class"), "child-element",
"The attribute is correct after navigation");
is(el.getTextContent(), "test content",
"The text content is correct after navigation");
info("Synthesizing an event on the element again");
onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(100, 100, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled, 1, "The mousedown event was not handled after navigation");
info("Destroying the helper");
env.destroy();
helper.destroy();
gBrowser.removeCurrentTab();
});
function synthesizeMouseDown(x, y, win) {
// We need to make sure the inserted anonymous content can be targeted by the
// event right after having been inserted, and so we need to force a sync
// reflow.
let forceReflow = win.document.documentElement.offsetWidth;
EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
}

View file

@ -0,0 +1,112 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test some edge cases of the CanvasFrameAnonymousContentHelper event handling
// mechanism.
// This makes sure the 'domnode' protocol actor type is known when importing
// highlighter.
require("devtools/server/actors/inspector");
const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
const {
CanvasFrameAnonymousContentHelper
} = require("devtools/server/actors/highlighters/utils/markup");
const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test";
add_task(function* () {
let browser = yield addTab(TEST_URL);
let doc = browser.contentDocument;
let nodeBuilder = () => {
let root = doc.createElement("div");
let parent = doc.createElement("div");
parent.style = "pointer-events:auto;width:300px;height:300px;background:yellow;";
parent.id = "parent-element";
root.appendChild(parent);
let child = doc.createElement("div");
child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
child.id = "child-element";
parent.appendChild(child);
return root;
};
info("Building the helper");
let env = new HighlighterEnvironment();
env.initFromWindow(doc.defaultView);
let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
info("Getting the parent and child elements");
let parentEl = helper.getElement("parent-element");
let childEl = helper.getElement("child-element");
info("Adding an event listener on both elements");
let mouseDownHandled = [];
function onMouseDown(e, id) {
mouseDownHandled.push(id);
}
parentEl.addEventListener("mousedown", onMouseDown);
childEl.addEventListener("mousedown", onMouseDown);
info("Synthesizing an event on the child element");
let onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(100, 100, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled.length, 2, "The mousedown event was handled twice");
is(mouseDownHandled[0], "child-element",
"The mousedown event was handled on the child element");
is(mouseDownHandled[1], "parent-element",
"The mousedown event was handled on the parent element");
info("Synthesizing an event on the parent, outside of the child element");
mouseDownHandled = [];
onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(250, 250, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
is(mouseDownHandled[0], "parent-element",
"The mousedown event was handled on the parent element");
info("Removing the event listener");
parentEl.removeEventListener("mousedown", onMouseDown);
childEl.removeEventListener("mousedown", onMouseDown);
info("Adding an event listener on the parent element only");
mouseDownHandled = [];
parentEl.addEventListener("mousedown", onMouseDown);
info("Synthesizing an event on the child element");
onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(100, 100, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled.length, 1, "The mousedown event was handled once");
is(mouseDownHandled[0], "parent-element",
"The mousedown event did bubble to the parent element");
info("Removing the parent listener");
parentEl.removeEventListener("mousedown", onMouseDown);
env.destroy();
helper.destroy();
gBrowser.removeCurrentTab();
});
function synthesizeMouseDown(x, y, win) {
// We need to make sure the inserted anonymous content can be targeted by the
// event right after having been inserted, and so we need to force a sync
// reflow.
let forceReflow = win.document.documentElement.offsetWidth;
EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
}

View file

@ -0,0 +1,100 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test support for event propagation stop in the
// CanvasFrameAnonymousContentHelper event handling mechanism.
// This makes sure the 'domnode' protocol actor type is known when importing
// highlighter.
require("devtools/server/actors/inspector");
const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
const {
CanvasFrameAnonymousContentHelper
} = require("devtools/server/actors/highlighters/utils/markup");
const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test";
add_task(function* () {
let browser = yield addTab(TEST_URL);
let doc = browser.contentDocument;
let nodeBuilder = () => {
let root = doc.createElement("div");
let parent = doc.createElement("div");
parent.style = "pointer-events:auto;width:300px;height:300px;background:yellow;";
parent.id = "parent-element";
root.appendChild(parent);
let child = doc.createElement("div");
child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
child.id = "child-element";
parent.appendChild(child);
return root;
};
info("Building the helper");
let env = new HighlighterEnvironment();
env.initFromWindow(doc.defaultView);
let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
info("Getting the parent and child elements");
let parentEl = helper.getElement("parent-element");
let childEl = helper.getElement("child-element");
info("Adding an event listener on both elements");
let mouseDownHandled = [];
function onParentMouseDown(e, id) {
mouseDownHandled.push(id);
}
parentEl.addEventListener("mousedown", onParentMouseDown);
function onChildMouseDown(e, id) {
mouseDownHandled.push(id);
e.stopPropagation();
}
childEl.addEventListener("mousedown", onChildMouseDown);
info("Synthesizing an event on the child element");
let onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(100, 100, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
is(mouseDownHandled[0], "child-element",
"The mousedown event was handled on the child element");
info("Synthesizing an event on the parent, outside of the child element");
mouseDownHandled = [];
onDocMouseDown = once(doc, "mousedown");
synthesizeMouseDown(250, 250, doc.defaultView);
yield onDocMouseDown;
is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
is(mouseDownHandled[0], "parent-element",
"The mousedown event was handled on the parent element");
info("Removing the event listener");
parentEl.removeEventListener("mousedown", onParentMouseDown);
childEl.removeEventListener("mousedown", onChildMouseDown);
env.destroy();
helper.destroy();
gBrowser.removeCurrentTab();
});
function synthesizeMouseDown(x, y, win) {
// We need to make sure the inserted anonymous content can be targeted by the
// event right after having been inserted, and so we need to force a sync
// reflow.
let forceReflow = win.document.documentElement.offsetWidth;
EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
}

View file

@ -0,0 +1,159 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {DirectorManagerFront} = require("devtools/shared/fronts/director-manager");
const {DirectorRegistry} = require("devtools/server/actors/director-registry");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "director-script-target.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
DirectorRegistry.clear();
let directorManager = DirectorManagerFront(client, form);
yield testDirectorScriptAttachEventAttributes(directorManager);
yield testDirectorScriptMessagePort(directorManager);
yield testDirectorScriptWindowEval(directorManager);
yield testDirectorScriptUnloadOnDetach(directorManager);
yield client.close();
gBrowser.removeCurrentTab();
DirectorRegistry.clear();
});
function* testDirectorScriptAttachEventAttributes(directorManager) {
let attachEvent = yield installAndEnableDirectorScript(directorManager, {
scriptId: "testDirectorScript_attachEventAttributes",
scriptCode: "(" + (function () {
exports.attach = function () {};
}).toString() + ")();",
scriptOptions: {
attachMethod: "attach"
}
});
let { directorScriptId, url } = attachEvent;
is(directorScriptId, "testDirectorScript_attachEventAttributes",
"attach event should contains directorScriptId");
is(url, MAIN_DOMAIN + "director-script-target.html");
}
function* testDirectorScriptMessagePort(directorManager) {
let { port } = yield installAndEnableDirectorScript(directorManager, {
scriptId: "testDirectorScript_MessagePort",
scriptCode: "(" + (function () {
exports.attach = function ({port}) {
port.onmessage = function (evt) {
// echo messages
evt.source.postMessage(evt.data);
};
};
}).toString() + ")();",
scriptOptions: {
attachMethod: "attach"
}
});
ok(port && port.postMessage, "testDirector_MessagePort port received");
// exchange messages over the MessagePort
let waitForMessagePortEvent = once(port, "message");
// needs to explicit start the port
port.start();
var msg = { k1: "v1", k2: [1, 2, 3] };
port.postMessage(msg);
var reply = yield waitForMessagePortEvent;
is(JSON.stringify(reply.data), JSON.stringify(msg), "echo reply received on the MessagePortClient");
}
function* testDirectorScriptWindowEval(directorManager) {
let { port } = yield installAndEnableDirectorScript(directorManager, {
scriptId: "testDirectorScript_WindowEval",
scriptCode: "(" + (function () {
exports.attach = function ({window, port}) {
var onpageloaded = function () {
var globalVarValue = window.eval("globalAccessibleVar;");
port.postMessage(globalVarValue);
};
if (window.document && window.document.readyState === "complete") {
onpageloaded();
} else {
window.addEventListener("load", onpageloaded, false);
}
};
}).toString() + ")();",
scriptOptions: {
attachMethod: "attach"
}
});
ok(port, "testDirectorScript_WindowEval port received");
// exchange messages over the MessagePort
let waitForMessagePortEvent = once(port, "message");
// needs to explicit start the port
port.start();
var portEvent = yield waitForMessagePortEvent;
ok(portEvent.data !== "unsecure-eval", "window.eval should be wrapped and safe");
is(portEvent.data, "global-value", "globalAccessibleVar should be accessible through window.eval");
}
function* testDirectorScriptUnloadOnDetach(directorManager) {
let { port } = yield installAndEnableDirectorScript(directorManager, {
scriptId: "testDirectorScript_unloadOnDetach",
scriptCode: "(" + (function () {
exports.attach = function ({port, onUnload}) {
onUnload(function () {
port.postMessage("ONUNLOAD");
});
};
}).toString() + ")();",
scriptOptions: {
attachMethod: "attach"
}
});
ok(port, "testDirectorScript_unloadOnDetach port received");
port.start();
let waitForDetach = once(directorManager, "director-script-detach");
let waitForMessage = once(port, "message");
directorManager.disableByScriptIds(["testDirectorScript_unloadOnDetach"], {reload: false});
let { directorScriptId } = yield waitForDetach;
is(directorScriptId, "testDirectorScript_unloadOnDetach",
"detach event should contains directorScriptId");
let portEvent = yield waitForMessage;
is(portEvent.data, "ONUNLOAD", "director-script's exports.onUnload called on detach");
}
function* installAndEnableDirectorScript(directorManager, directorScriptDef) {
let { scriptId } = directorScriptDef;
DirectorRegistry.install(scriptId, directorScriptDef);
let waitForAttach = once(directorManager, "director-script-attach");
let waitForError = once(directorManager, "director-script-error");
directorManager.enableByScriptIds([scriptId], {reload: false});
let attachOrErrorEvent = yield Promise.race([waitForAttach, waitForError]);
return attachOrErrorEvent;
}

View file

@ -0,0 +1,132 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {DirectorManagerFront} = require("devtools/shared/fronts/director-manager");
const {DirectorRegistry} = require("devtools/server/actors/director-registry");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "director-script-target.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
DirectorRegistry.clear();
let directorManager = DirectorManagerFront(client, form);
yield testErrorOnRequire(directorManager);
yield testErrorOnEvaluate(directorManager);
yield testErrorOnAttach(directorManager);
yield testErrorOnDetach(directorManager);
yield client.close();
gBrowser.removeCurrentTab();
DirectorRegistry.clear();
});
function* testErrorOnRequire(directorManager) {
// director script require method should raise a "not implemented" exception
let errorOnRequire = yield installAndEnableDirectorScript(directorManager, {
scriptId: "testDirectorScript_errorOnRequire",
scriptCode: "(" + (function () {
// this director script should generate an error event
// because require raise a "not implemented" exception
require("fake_module");
}).toString() + ")();",
scriptOptions: {}
});
assertIsDirectorScriptError(errorOnRequire);
let { message } = errorOnRequire;
is(message, "Error: NOT IMPLEMENTED", "error.message contains the expected error message");
}
function* testErrorOnEvaluate(directorManager) {
// director scripts should send an error events if the director script raise an exception on
// evaluation
let errorOnEvaluate = yield installAndEnableDirectorScript(directorManager, {
scriptId: "testDirectorScript_errorOnEvaluate",
scriptCode: "(" + (function () {
// this will raise an exception evaluating
// the director script
raise.an_error.during.content_script.load();
}).toString() + ")();",
scriptOptions: {}
});
assertIsDirectorScriptError(errorOnEvaluate);
}
function* testErrorOnAttach(directorManager) {
// director scripts should send an error events if the director script raise an exception on
// evaluation
let errorOnAttach = yield installAndEnableDirectorScript(directorManager, {
scriptId: "testDirectorScript_errorOnAttach",
scriptCode: "(" + (function () {
// this will raise an exception on evaluating
// the director script
module.exports = function () {
raise.an_error.during.content_script.load();
};
}).toString() + ")();",
scriptOptions: {}
});
assertIsDirectorScriptError(errorOnAttach);
}
function* testErrorOnDetach(directorManager) {
// director scripts should send an error events if the director script raise an exception on
// evaluation
let attach = yield installAndEnableDirectorScript(directorManager, {
scriptId: "testDirectorScript_errorOnDetach",
scriptCode: "(" + (function () {
module.exports = function ({onUnload}) {
// this will raise an exception on unload the director script
onUnload(function () {
raise_an_error_onunload();
});
};
}).toString() + ")();",
scriptOptions: {}
});
let waitForDetach = once(directorManager, "director-script-detach");
let waitForError = once(directorManager, "director-script-error");
directorManager.disableByScriptIds(["testDirectorScript_errorOnDetach"], {reload: false});
let detach = yield waitForDetach;
let error = yield waitForError;
ok(detach, "testDirectorScript_errorOnDetach detach event received");
ok(error, "testDirectorScript_errorOnDetach detach error received");
assertIsDirectorScriptError(error);
}
function* installAndEnableDirectorScript(directorManager, directorScriptDef) {
let { scriptId } = directorScriptDef;
DirectorRegistry.install(scriptId, directorScriptDef);
let waitForAttach = once(directorManager, "director-script-attach");
let waitForError = once(directorManager, "director-script-error");
directorManager.enableByScriptIds([scriptId], {reload: false});
let attachOrErrorEvent = yield Promise.race([waitForAttach, waitForError]);
return attachOrErrorEvent;
}
function assertIsDirectorScriptError(error) {
ok(!!error.message, "errors should contain a message");
ok(!!error.stack, "errors should contain a stack trace");
ok(!!error.fileName, "errors should contain a fileName");
ok(typeof error.columnNumber == "number", "errors should contain a columnNumber");
ok(typeof error.lineNumber == "number", "errors should contain a lineNumber");
ok(!!error.directorScriptId, "errors should contain a directorScriptId");
}

View file

@ -0,0 +1,87 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {DirectorManagerFront} = require("devtools/shared/fronts/director-manager");
const {DirectorRegistry} = require("devtools/server/actors/director-registry");
DirectorRegistry.clear();
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "director-script-target.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
DirectorRegistry.clear();
let directorManager = DirectorManagerFront(client, form);
// director scripts attach method defaults to module.exports
let attachModuleExports = yield testDirectorScriptExports(directorManager, {
scriptId: "testDirectorScript_moduleExports",
scriptCode: "(" + (function () {
module.exports = function () {};
}).toString() + ")();",
scriptOptions: {}
});
ok(attachModuleExports.port, "testDirectorScript_moduleExports attach event received");
// director scripts attach method can be configured using the attachMethod scriptOptions
let attachExportsAttach = yield testDirectorScriptExports(directorManager, {
scriptId: "testDirectorScript_exportsAttach",
scriptCode: "(" + (function () {
exports.attach = function () {};
}).toString() + ")();",
scriptOptions: {
attachMethod: "attach"
}
});
ok(attachExportsAttach.port, "testDirectorScript_exportsAttach attach event received");
// director scripts without an attach method generates an error event
let errorUndefinedAttachMethod = yield testDirectorScriptExports(directorManager, {
scriptId: "testDirectorScript_undefinedAttachMethod",
scriptCode: "(" + (function () {
// this director script should raise an error
// because it doesn't export any attach method
}).toString() + ")();",
scriptOptions: {
attachMethod: "attach"
}
});
let { message } = errorUndefinedAttachMethod;
ok(!!message, "testDirectorScript_undefinedAttachMethod error event received");
assertIsDirectorScriptError(errorUndefinedAttachMethod);
yield client.close();
gBrowser.removeCurrentTab();
DirectorRegistry.clear();
});
function assertIsDirectorScriptError(error) {
ok(!!error.message, "errors should contain a message");
ok(!!error.stack, "errors should contain a stack trace");
ok(!!error.fileName, "errors should contain a fileName");
ok(typeof error.columnNumber == "number", "errors should contain a columnNumber");
ok(typeof error.lineNumber == "number", "errors should contain a lineNumber");
ok(!!error.directorScriptId, "errors should contain a directorScriptId");
}
function* testDirectorScriptExports(directorManager, directorScriptDef) {
let { scriptId } = directorScriptDef;
DirectorRegistry.install(scriptId, directorScriptDef);
let waitForAttach = once(directorManager, "director-script-attach");
let waitForError = once(directorManager, "director-script-error");
directorManager.enableByScriptIds([scriptId], {reload: false});
let attachOrErrorEvent = yield Promise.race([waitForAttach, waitForError]);
return attachOrErrorEvent;
}

View file

@ -0,0 +1,33 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get "nsCycleCollector::Collect" and
* "nsCycleCollector::ForgetSkippable" markers when we force cycle collection.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
add_task(function* () {
// This test runs very slowly on linux32 debug EC2 instances.
requestLongerTimeout(2);
let browser = yield addTab(MAIN_DOMAIN + "doc_force_cc.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true });
let markers = yield waitForMarkerType(front, ["nsCycleCollector::Collect", "nsCycleCollector::ForgetSkippable"]);
yield front.stopRecording(rec);
ok(markers.some(m => m.name === "nsCycleCollector::Collect"), "got some nsCycleCollector::Collect markers");
ok(markers.some(m => m.name === "nsCycleCollector::ForgetSkippable"), "got some nsCycleCollector::Collect markers");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,37 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get DOMContentLoaded and Load markers
*/
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = TimelineFront(client, form);
let rec = yield front.start({ withMarkers: true });
front.once("doc-loading", e => {
ok(false, "Should not be emitting doc-loading events.");
});
executeSoon(() => doc.location.reload());
yield waitForMarkerType(front, MARKER_NAMES, () => true, e => e, "markers");
yield front.stop(rec);
ok(true, "Found the required marker names.");
// Wait some more time to make sure the 'doc-loading' events never get fired.
yield DevToolsUtils.waitForTime(1000);
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,35 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get DOMContentLoaded and Load markers
*/
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = TimelineFront(client, form);
let rec = yield front.start({ withMarkers: true, withDocLoadingEvents: true });
yield new Promise(resolve => {
front.once("doc-loading", resolve);
doc.location.reload();
});
ok(true, "At least one doc-loading event got fired.");
yield waitForMarkerType(front, MARKER_NAMES, () => true, e => e, "markers");
yield front.stop(rec);
ok(true, "Found the required marker names.");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get DOMContentLoaded and Load markers
*/
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const MARKER_NAMES = ["document::DOMContentLoaded", "document::Load"];
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = TimelineFront(client, form);
let rec = yield front.start({ withDocLoadingEvents: true });
waitForMarkerType(front, MARKER_NAMES, () => true, e => e, "markers").then(e => {
ok(false, "Should not be emitting doc-loading markers.");
});
yield new Promise(resolve => {
front.once("doc-loading", resolve);
doc.location.reload();
});
ok(true, "At least one doc-loading event got fired.");
yield front.stop(rec);
// Wait some more time to make sure the 'doc-loading' markers never get fired.
yield DevToolsUtils.waitForTime(1000);
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get "GarbageCollection" markers.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
const MARKER_NAME = "GarbageCollection";
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_force_gc.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true });
let markers = yield waitForMarkerType(front, MARKER_NAME);
yield front.stopRecording(rec);
ok(markers.some(m => m.name === MARKER_NAME), `got some ${MARKER_NAME} markers`);
ok(markers.every(({causeName}) => typeof causeName === "string"),
"All markers have a causeName.");
ok(markers.every(({cycle}) => typeof cycle === "number"),
"All markers have a `cycle` ID.");
markers = rec.getMarkers();
// Bug 1197646
let ordered = true;
markers.reduce((previousStart, current, i) => {
if (i === 0) {
return current.start;
}
if (current.start < previousStart) {
ok(false, `markers must be in order. ${current.name} marker has later start time (${current.start}) thanprevious: ${previousStart}`);
ordered = false;
}
return current.start;
});
is(ordered, true, "All GC and non-GC markers are in order by start time.");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,32 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get "MinorGC" markers when we continue to steadily allocate
* objects.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
add_task(function* () {
// This test runs very slowly on linux32 debug EC2 instances.
requestLongerTimeout(2);
let doc = yield addTab(MAIN_DOMAIN + "doc_allocations.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true });
let markers = yield waitForMarkerType(front, ["MinorGC"]);
yield front.stopRecording(rec);
ok(markers.some(m => m.name === "MinorGC" && m.causeName),
"got some MinorGC markers");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get "Parse HTML" markers.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
const MARKER_NAME = "Parse HTML";
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_innerHTML.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true });
let markers = yield waitForMarkerType(front, MARKER_NAME);
yield front.stopRecording(rec);
ok(markers.some(m => m.name === MARKER_NAME), `got some ${MARKER_NAME} markers`);
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get "Styles" markers with correct meta.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
const MARKER_NAME = "Styles";
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true });
let markers = yield waitForMarkerType(front, MARKER_NAME, function (markers) {
return markers.some(({restyleHint}) => restyleHint != void 0);
});
yield front.stopRecording(rec);
ok(markers.some(m => m.name === MARKER_NAME), `got some ${MARKER_NAME} markers`);
ok(markers.some(({restyleHint}) => restyleHint != void 0),
"Some markers have a restyleHint.");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get a "TimeStamp" marker.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
const { pmmConsoleMethod, pmmLoadFrameScripts, pmmClearFrameScripts } = require("devtools/client/performance/test/helpers/profiler-mm-utils");
const MARKER_NAME = "TimeStamp";
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true });
pmmLoadFrameScripts(gBrowser);
pmmConsoleMethod("timeStamp");
pmmConsoleMethod("timeStamp", "myLabel");
let markers = yield waitForMarkerType(front, MARKER_NAME, markers => markers.length >= 2);
yield front.stopRecording(rec);
ok(markers.every(({stack}) => typeof stack === "number"), "All markers have stack references.");
ok(markers.every(({name}) => name === "TimeStamp"), "All markers found are TimeStamp markers");
ok(markers.length === 2, "found 2 TimeStamp markers");
ok(markers.every(({start, end}) => typeof start === "number" && start === end),
"All markers have equal start and end times");
is(markers[0].causeName, void 0, "Unlabeled timestamps have an empty causeName");
is(markers[1].causeName, "myLabel", "Labeled timestamps have correct causeName");
pmmClearFrameScripts();
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,160 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const URL1 = MAIN_DOMAIN + "navigate-first.html";
const URL2 = MAIN_DOMAIN + "navigate-second.html";
var events = require("sdk/event/core");
var client;
SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
// State machine to check events order
var i = 0;
function assertEvent(event, data) {
let x = 0;
switch (i++) {
case x++:
is(event, "request", "Get first page load");
is(data, URL1);
break;
case x++:
is(event, "load-new-document", "Ask to load the second page");
break;
case x++:
is(event, "unload-dialog", "We get the dialog on first page unload");
break;
case x++:
is(event, "will-navigate", "The very first event is will-navigate on server side");
is(data.newURI, URL2, "newURI property is correct");
break;
case x++:
is(event, "request", "RDP is async with messageManager, the request happens after will-navigate");
is(data, URL2);
break;
case x++:
is(event, "tabNavigated", "After the request, the client receive tabNavigated");
is(data.state, "start", "state is start");
is(data.url, URL2, "url property is correct");
is(data.nativeConsoleAPI, true, "nativeConsoleAPI is correct");
break;
case x++:
is(event, "DOMContentLoaded");
is(content.document.readyState, "interactive");
break;
case x++:
is(event, "load");
is(content.document.readyState, "complete");
break;
case x++:
is(event, "navigate", "Then once the second doc is loaded, we get the navigate event");
is(content.document.readyState, "complete", "navigate is emitted only once the document is fully loaded");
break;
case x++:
is(event, "tabNavigated", "Finally, the receive the client event");
is(data.state, "stop", "state is stop");
is(data.url, URL2, "url property is correct");
is(data.nativeConsoleAPI, true, "nativeConsoleAPI is correct");
// End of test!
cleanup();
break;
}
}
function waitForOnBeforeUnloadDialog(browser, callback) {
browser.addEventListener("DOMWillOpenModalDialog", function onModalDialog() {
browser.removeEventListener("DOMWillOpenModalDialog", onModalDialog, true);
executeSoon(() => {
let stack = browser.parentNode;
let dialogs = stack.getElementsByTagName("tabmodalprompt");
let {button0, button1} = dialogs[0].ui;
callback(button0, button1);
});
}, true);
}
var httpObserver = function (subject, topic, state) {
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
let url = channel.URI.spec;
// Only listen for our document request, as many other requests can happen
if (url == URL1 || url == URL2) {
assertEvent("request", url);
}
};
Services.obs.addObserver(httpObserver, "http-on-modify-request", false);
function onDOMContentLoaded() {
assertEvent("DOMContentLoaded");
}
function onLoad() {
assertEvent("load");
}
function getServerTabActor(callback) {
// Ensure having a minimal server
initDebuggerServer();
// Connect to this tab
let transport = DebuggerServer.connectPipe();
client = new DebuggerClient(transport);
connectDebuggerClient(client).then(form => {
let actorID = form.actor;
client.attachTab(actorID, function (aResponse, aTabClient) {
// !Hack! Retrieve a server side object, the BrowserTabActor instance
let tabActor = DebuggerServer._searchAllConnectionsForActor(actorID);
callback(tabActor);
});
});
client.addListener("tabNavigated", function (aEvent, aPacket) {
assertEvent("tabNavigated", aPacket);
});
}
function test() {
// Open a test tab
addTab(URL1).then(function (browser) {
let doc = browser.contentDocument;
getServerTabActor(function (tabActor) {
// In order to listen to internal will-navigate/navigate events
events.on(tabActor, "will-navigate", function (data) {
assertEvent("will-navigate", data);
});
events.on(tabActor, "navigate", function (data) {
assertEvent("navigate", data);
});
// Start listening for page load events
browser.addEventListener("DOMContentLoaded", onDOMContentLoaded, true);
browser.addEventListener("load", onLoad, true);
// Listen for alert() call being made in navigate-first during unload
waitForOnBeforeUnloadDialog(browser, function (btnLeave, btnStay) {
assertEvent("unload-dialog");
// accept to quit this page to another
btnLeave.click();
});
// Load another document in this doc to dispatch these events
assertEvent("load-new-document");
content.location = URL2;
});
});
}
function cleanup() {
let browser = gBrowser.selectedBrowser;
browser.removeEventListener("DOMContentLoaded", onDOMContentLoaded);
browser.removeEventListener("load", onLoad);
client.close().then(function () {
Services.obs.addObserver(httpObserver, "http-on-modify-request", false);
DebuggerServer.destroy();
finish();
});
}

View file

@ -0,0 +1,38 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we have allocation data coming from the front.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_allocations.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true, withAllocations: true, withTicks: true });
yield waitUntil(() => rec.getAllocations().frames.length);
yield waitUntil(() => rec.getAllocations().timestamps.length);
yield waitUntil(() => rec.getAllocations().sizes.length);
yield waitUntil(() => rec.getAllocations().sites.length);
yield front.stopRecording(rec);
let { frames, timestamps, sizes, sites } = rec.getAllocations();
is(timestamps.length, sizes.length, "we have the same amount of timestamps and sizes");
ok(timestamps.every(time => time > 0 && typeof time === "number"), "all timestamps have numeric values");
ok(sizes.every(n => n > 0 && typeof n === "number"), "all sizes are positive numbers");
yield front.destroy();
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the profiler connection front does not activate the built-in
* profiler module if not necessary, and doesn't deactivate it when
* a recording is stopped.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
const { pmmIsProfilerActive, pmmStopProfiler, pmmLoadFrameScripts } = require("devtools/client/performance/test/helpers/profiler-mm-utils");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
pmmLoadFrameScripts(gBrowser);
ok(!(yield pmmIsProfilerActive()),
"The built-in profiler module should not have been automatically started.");
let rec = yield front.startRecording();
yield front.stopRecording(rec);
ok((yield pmmIsProfilerActive()),
"The built-in profiler module should still be active (1).");
rec = yield front.startRecording();
yield front.stopRecording(rec);
ok((yield pmmIsProfilerActive()),
"The built-in profiler module should still be active (2).");
yield front.destroy();
yield client.close();
ok(!(yield pmmIsProfilerActive()),
"The built-in profiler module should no longer be active.");
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,46 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the built-in profiler module doesn't deactivate when the toolbox
* is destroyed if there are other consumers using it.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
const { pmmIsProfilerActive, pmmStopProfiler, pmmLoadFrameScripts } = require("devtools/client/performance/test/helpers/profiler-mm-utils");
add_task(function* () {
yield addTab(MAIN_DOMAIN + "doc_perf.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let firstFront = PerformanceFront(client, form);
yield firstFront.connect();
pmmLoadFrameScripts(gBrowser);
yield firstFront.startRecording();
yield addTab(MAIN_DOMAIN + "doc_perf.html");
let client2 = new DebuggerClient(DebuggerServer.connectPipe());
let form2 = yield connectDebuggerClient(client2);
let secondFront = PerformanceFront(client2, form2);
yield secondFront.connect();
pmmLoadFrameScripts(gBrowser);
yield secondFront.startRecording();
// Manually teardown the tabs so we can check profiler status
yield secondFront.destroy();
yield client2.close();
ok((yield pmmIsProfilerActive()),
"The built-in profiler module should still be active.");
yield firstFront.destroy();
yield client.close();
ok(!(yield pmmIsProfilerActive()),
"The built-in profiler module should no longer be active.");
gBrowser.removeCurrentTab();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,54 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the built-in profiler module is not reactivated if no other
* consumer was using it over the remote debugger protocol, and ensures
* that the actor will work properly even in such cases (e.g. the Gecko Profiler
* addon was installed and automatically activated the profiler module).
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
const { pmmIsProfilerActive, pmmStartProfiler, pmmStopProfiler, pmmLoadFrameScripts, pmmClearFrameScripts } = require("devtools/client/performance/test/helpers/profiler-mm-utils");
add_task(function* () {
// Ensure the profiler is already running when the test starts.
pmmLoadFrameScripts(gBrowser);
let entries = 1000000;
let interval = 1;
let features = ["js"];
yield pmmStartProfiler({ entries, interval, features });
ok((yield pmmIsProfilerActive()),
"The built-in profiler module should still be active.");
yield addTab(MAIN_DOMAIN + "doc_perf.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let firstFront = PerformanceFront(client, form);
yield firstFront.connect();
let recording = yield firstFront.startRecording();
yield addTab(MAIN_DOMAIN + "doc_perf.html");
let client2 = new DebuggerClient(DebuggerServer.connectPipe());
let form2 = yield connectDebuggerClient(client2);
let secondFront = PerformanceFront(client2, form2);
yield secondFront.connect();
yield secondFront.destroy();
yield client2.close();
ok((yield pmmIsProfilerActive()),
"The built-in profiler module should still be active.");
yield firstFront.destroy();
yield client.close();
ok(!(yield pmmIsProfilerActive()),
"The built-in profiler module should have been automatically stopped.");
pmmClearFrameScripts();
gBrowser.removeCurrentTab();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,93 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test functionality of real time markers.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let lastMemoryDelta = 0;
let lastTickDelta = 0;
let counters = {
markers: [],
memory: [],
ticks: []
};
let deferreds = {
markers: defer(),
memory: defer(),
ticks: defer()
};
front.on("timeline-data", handler);
let rec = yield front.startRecording({ withMarkers: true, withMemory: true, withTicks: true });
yield Promise.all(Object.keys(deferreds).map(type => deferreds[type].promise));
yield front.stopRecording(rec);
front.off("timeline-data", handler);
is(counters.markers.length, 1, "one marker event fired.");
is(counters.memory.length, 3, "three memory events fired.");
is(counters.ticks.length, 3, "three ticks events fired.");
yield front.destroy();
yield client.close();
gBrowser.removeCurrentTab();
function handler(name, data) {
if (name === "markers") {
if (counters.markers.length >= 1) { return; }
ok(data.markers[0].start, "received atleast one marker with `start`");
ok(data.markers[0].end, "received atleast one marker with `end`");
ok(data.markers[0].name, "received atleast one marker with `name`");
counters.markers.push(data.markers);
}
else if (name === "memory") {
if (counters.memory.length >= 3) { return; }
let { delta, measurement } = data;
is(typeof delta, "number", "received `delta` in memory event");
ok(delta > lastMemoryDelta, "received `delta` in memory event");
ok(measurement.total, "received `total` in memory event");
counters.memory.push({ delta, measurement });
lastMemoryDelta = delta;
}
else if (name === "ticks") {
if (counters.ticks.length >= 3) { return; }
let { delta, timestamps } = data;
ok(delta > lastTickDelta, "received `delta` in ticks event");
// Timestamps aren't guaranteed to always contain tick events, since
// they're dependent on the refresh driver, which may be blocked.
counters.ticks.push({ delta, timestamps });
lastTickDelta = delta;
}
else if (name === "frames") {
// Nothing to do here.
}
else {
ok(false, `Received unknown event: ${name}`);
}
if (name === "markers" && counters[name].length === 1 ||
name === "memory" && counters[name].length === 3 ||
name === "ticks" && counters[name].length === 3) {
deferreds[name].resolve();
}
}
});

View file

@ -0,0 +1,80 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests the state of a recording rec from start to finish for recording,
* completed, and rec data.
*/
const { PerformanceFront } = require("devtools/shared/fronts/performance");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording({ withMarkers: true, withTicks: true, withMemory: true });
ok(rec.isRecording(), "RecordingModel is recording when created");
yield busyWait(100);
yield waitUntil(() => rec.getMemory().length);
ok(true, "RecordingModel populates memory while recording");
yield waitUntil(() => rec.getTicks().length);
ok(true, "RecordingModel populates ticks while recording");
yield waitUntil(() => rec.getMarkers().length);
ok(true, "RecordingModel populates markers while recording");
ok(!rec.isCompleted(), "RecordingModel is not completed when still recording");
let stopping = once(front, "recording-stopping");
let stopped = once(front, "recording-stopped");
front.stopRecording(rec);
yield stopping;
ok(!rec.isRecording(), "on 'recording-stopping', model is no longer recording");
// This handler should be called BEFORE "recording-stopped" is called, as
// there is some delay, but in the event where "recording-stopped" finishes
// before we check here, ensure that we're atleast in the right state
if (rec.getProfile()) {
ok(rec.isCompleted(), "recording is completed once it has profile data");
} else {
ok(!rec.isCompleted(), "recording is not yet completed on 'recording-stopping'");
ok(rec.isFinalizing(), "recording is considered finalizing between 'recording-stopping' and 'recording-stopped'");
}
yield stopped;
ok(!rec.isRecording(), "on 'recording-stopped', model is still no longer recording");
ok(rec.isCompleted(), "on 'recording-stopped', model is considered 'complete'");
checkSystemInfo(rec, "Host");
checkSystemInfo(rec, "Client");
// Export and import a rec, and ensure it has the correct state.
let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
yield rec.exportRecording(file);
let importedModel = yield front.importRecording(file);
ok(importedModel.isCompleted(), "All imported recordings should be completed");
ok(!importedModel.isRecording(), "All imported recordings should not be recording");
ok(importedModel.isImported(), "All imported recordings should be considerd imported");
checkSystemInfo(importedModel, "Host");
checkSystemInfo(importedModel, "Client");
yield front.destroy();
yield client.close();
gBrowser.removeCurrentTab();
});
function checkSystemInfo(recording, type) {
let data = recording[`get${type}SystemInfo`]();
for (let field of ["appid", "apptype", "vendor", "name", "version"]) {
ok(data[field], `get${type}SystemInfo() has ${field} property`);
}
}

View file

@ -0,0 +1,54 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that buffer status is correctly updated in recording models.
*/
var BUFFER_SIZE = 20000;
var config = { bufferSize: BUFFER_SIZE };
const { PerformanceFront } = require("devtools/shared/fronts/performance");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
yield front.setProfilerStatusInterval(10);
let model = yield front.startRecording(config);
let stats = yield once(front, "profiler-status");
is(stats.totalSize, BUFFER_SIZE, `profiler-status event has totalSize: ${stats.totalSize}`);
ok(stats.position < BUFFER_SIZE, `profiler-status event has position: ${stats.position}`);
ok(stats.generation >= 0, `profiler-status event has generation: ${stats.generation}`);
ok(stats.isActive, "profiler-status event is isActive");
is(typeof stats.currentTime, "number", "profiler-status event has currentTime");
// Halt once more for a buffer status to ensure we're beyond 0
yield once(front, "profiler-status");
let lastBufferStatus = 0;
let checkCount = 0;
while (lastBufferStatus < 1) {
let currentBufferStatus = front.getBufferUsageForRecording(model);
ok(currentBufferStatus > lastBufferStatus, `buffer is more filled than before: ${currentBufferStatus} > ${lastBufferStatus}`);
lastBufferStatus = currentBufferStatus;
checkCount++;
yield once(front, "profiler-status");
}
ok(checkCount >= 1, "atleast 1 event were fired until the buffer was filled");
is(lastBufferStatus, 1, "buffer usage cannot surpass 100%");
yield front.stopRecording(model);
is(front.getBufferUsageForRecording(model), null, "buffer usage should be null when no longer recording.");
yield front.destroy();
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,63 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the retrieved profiler data samples are correctly filtered and
* normalized before passed to consumers.
*/
const WAIT_TIME = 1000; // ms
const { PerformanceFront } = require("devtools/shared/fronts/performance");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
// Perform the first recording...
let firstRecording = yield front.startRecording();
let firstRecordingStartTime = firstRecording._startTime;
info("Started profiling at: " + firstRecordingStartTime);
busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
yield front.stopRecording(firstRecording);
ok(firstRecording.getDuration() >= WAIT_TIME,
"The first recording duration is correct.");
// Perform the second recording...
let secondRecording = yield front.startRecording();
let secondRecordingStartTime = secondRecording._startTime;
info("Started profiling at: " + secondRecordingStartTime);
busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity
yield front.stopRecording(secondRecording);
let secondRecordingProfile = secondRecording.getProfile();
let secondRecordingSamples = secondRecordingProfile.threads[0].samples.data;
ok(secondRecording.getDuration() >= WAIT_TIME,
"The second recording duration is correct.");
const TIME_SLOT = secondRecordingProfile.threads[0].samples.schema.time;
ok(secondRecordingSamples[0][TIME_SLOT] < secondRecordingStartTime,
"The second recorded sample times were normalized.");
ok(secondRecordingSamples[0][TIME_SLOT] > 0,
"The second recorded sample times were normalized correctly.");
ok(!secondRecordingSamples.find(e => e[TIME_SLOT] + secondRecordingStartTime <= firstRecording.getDuration()),
"There should be no samples from the first recording in the second one, " +
"even though the total number of frames did not overflow.");
yield front.destroy();
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,77 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests if the retrieved profiler data samples always have a (root) node.
* If this ever changes, the |ThreadNode.prototype.insert| function in
* devtools/client/performance/modules/logic/tree-model.js will have to be changed.
*/
const WAIT_TIME = 1000; // ms
const { PerformanceFront } = require("devtools/shared/fronts/performance");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "doc_perf.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = PerformanceFront(client, form);
yield front.connect();
let rec = yield front.startRecording();
busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
yield front.stopRecording(rec);
let profile = rec.getProfile();
let sampleCount = 0;
for (let thread of profile.threads) {
info("Checking thread: " + thread.name);
for (let sample of thread.samples.data) {
sampleCount++;
let stack = getInflatedStackLocations(thread, sample);
if (stack[0] != "(root)") {
ok(false, "The sample " + stack.toSource() + " doesn't have a root node.");
}
}
}
ok(sampleCount > 0,
"At least some samples have been iterated over, checking for root nodes.");
yield front.destroy();
yield client.close();
gBrowser.removeCurrentTab();
});
/**
* Inflate a particular sample's stack and return an array of strings.
*/
function getInflatedStackLocations(thread, sample) {
let stackTable = thread.stackTable;
let frameTable = thread.frameTable;
let stringTable = thread.stringTable;
let SAMPLE_STACK_SLOT = thread.samples.schema.stack;
let STACK_PREFIX_SLOT = stackTable.schema.prefix;
let STACK_FRAME_SLOT = stackTable.schema.frame;
let FRAME_LOCATION_SLOT = frameTable.schema.location;
// Build the stack from the raw data and accumulate the locations in
// an array.
let stackIndex = sample[SAMPLE_STACK_SLOT];
let locations = [];
while (stackIndex !== null) {
let stackEntry = stackTable.data[stackIndex];
let frame = frameTable.data[stackEntry[STACK_FRAME_SLOT]];
locations.push(stringTable[frame[FRAME_LOCATION_SLOT]]);
stackIndex = stackEntry[STACK_PREFIX_SLOT];
}
// The profiler tree is inverted, so reverse the array.
return locations.reverse();
}

View file

@ -0,0 +1,76 @@
var gClient;
function test() {
waitForExplicitFinish();
var {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
var actorURL = "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/hello-actor.js";
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect()
.then(() => gClient.listTabs())
.then(aResponse => {
var options = {
prefix: "helloActor",
constructor: "HelloActor",
type: { tab: true }
};
var registry = ActorRegistryFront(gClient, aResponse);
registry.registerActor(actorURL, options).then(actorFront => {
gClient.listTabs(response => {
var tab = response.tabs[response.selected];
ok(!!tab.helloActor, "Hello actor must exist");
// Make sure actor's state is maintained across listTabs requests.
checkActorState(tab.helloActor, () => {
// Clean up
actorFront.unregister().then(() => {
gClient.close().then(() => {
DebuggerServer.destroy();
gClient = null;
finish();
});
});
});
});
});
});
}
function checkActorState(helloActor, callback) {
getCount(helloActor, response => {
ok(!response.error, "No error");
is(response.count, 1, "The counter must be valid");
getCount(helloActor, response => {
ok(!response.error, "No error");
is(response.count, 2, "The counter must be valid");
gClient.listTabs(response => {
var tab = response.tabs[response.selected];
is(tab.helloActor, helloActor, "Hello actor must be valid");
getCount(helloActor, response => {
ok(!response.error, "No error");
is(response.count, 3, "The counter must be valid");
callback();
});
});
});
});
}
function getCount(actor, callback) {
gClient.request({
to: actor,
type: "count"
}, callback);
}

View file

@ -0,0 +1,294 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {StorageFront} = require("devtools/shared/fronts/storage");
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
const beforeReload = {
cookies: {
"test1.example.org": ["c1", "cs2", "c3", "uc1"],
"sectest1.example.org": ["uc1", "cs2"]
},
localStorage: {
"http://test1.example.org": ["ls1", "ls2"],
"http://sectest1.example.org": ["iframe-u-ls1"]
},
sessionStorage: {
"http://test1.example.org": ["ss1"],
"http://sectest1.example.org": ["iframe-u-ss1", "iframe-u-ss2"]
},
indexedDB: {
"http://test1.example.org": [
JSON.stringify(["idb1", "obj1"]),
JSON.stringify(["idb1", "obj2"]),
JSON.stringify(["idb2", "obj3"]),
],
"http://sectest1.example.org": []
}
};
function* testStores(data, front) {
testWindowsBeforeReload(data);
// FIXME: Bug 1183581 - browser_storage_dynamic_windows.js IsSafeToRunScript
// errors when testing reload in E10S mode
// yield testReload(front);
yield testAddIframe(front);
yield testRemoveIframe(front);
}
function testWindowsBeforeReload(data) {
for (let storageType in beforeReload) {
ok(data[storageType], storageType + " storage actor is present");
is(Object.keys(data[storageType].hosts).length,
Object.keys(beforeReload[storageType]).length,
"Number of hosts for " + storageType + "match");
for (let host in beforeReload[storageType]) {
ok(data[storageType].hosts[host], "Host " + host + " is present");
}
}
}
function markOutMatched(toBeEmptied, data, deleted) {
if (!Object.keys(toBeEmptied).length) {
info("Object empty");
return;
}
ok(Object.keys(data).length,
"At least one storage type should be present");
for (let storageType in toBeEmptied) {
if (!data[storageType]) {
continue;
}
info("Testing for " + storageType);
for (let host in data[storageType]) {
ok(toBeEmptied[storageType][host], "Host " + host + " found");
if (!deleted) {
for (let item of data[storageType][host]) {
let index = toBeEmptied[storageType][host].indexOf(item);
ok(index > -1, "Item found - " + item);
if (index > -1) {
toBeEmptied[storageType][host].splice(index, 1);
}
}
if (!toBeEmptied[storageType][host].length) {
delete toBeEmptied[storageType][host];
}
} else {
delete toBeEmptied[storageType][host];
}
}
if (!Object.keys(toBeEmptied[storageType]).length) {
delete toBeEmptied[storageType];
}
}
}
// function testReload(front) {
// info("Testing if reload works properly");
// let shouldBeEmptyFirst = Cu.cloneInto(beforeReload, {});
// let shouldBeEmptyLast = Cu.cloneInto(beforeReload, {});
// return new Promise(resolve => {
// let onStoresUpdate = data => {
// info("in stores update of testReload");
// // This might be second time stores update is happening, in which case,
// // data.deleted will be null.
// // OR.. This might be the first time on a super slow machine where both
// // data.deleted and data.added is missing in the first update.
// if (data.deleted) {
// markOutMatched(shouldBeEmptyFirst, data.deleted, true);
// }
// if (!Object.keys(shouldBeEmptyFirst).length) {
// info("shouldBeEmptyFirst is empty now");
// }
// // stores-update call might not have data.added for the first time on
// // slow machines, in which case, data.added will be null
// if (data.added) {
// markOutMatched(shouldBeEmptyLast, data.added);
// }
// if (!Object.keys(shouldBeEmptyLast).length) {
// info("Everything to be received is received.");
// endTestReloaded();
// }
// };
// let endTestReloaded = () => {
// front.off("stores-update", onStoresUpdate);
// resolve();
// };
// front.on("stores-update", onStoresUpdate);
// content.location.reload();
// });
// }
function testAddIframe(front) {
info("Testing if new iframe addition works properly");
return new Promise(resolve => {
let shouldBeEmpty = {
localStorage: {
"https://sectest1.example.org": ["iframe-s-ls1"]
},
sessionStorage: {
"https://sectest1.example.org": ["iframe-s-ss1"]
},
cookies: {
"sectest1.example.org": ["sc1"]
},
indexedDB: {
// empty because indexed db creation happens after the page load, so at
// the time of window-ready, there was no indexed db present.
"https://sectest1.example.org": []
},
Cache: {
"https://sectest1.example.org":[]
}
};
let onStoresUpdate = data => {
info("checking if the hosts list is correct for this iframe addition");
markOutMatched(shouldBeEmpty, data.added);
ok(!data.changed || !data.changed.cookies ||
!data.changed.cookies["https://sectest1.example.org"],
"Nothing got changed for cookies");
ok(!data.changed || !data.changed.localStorage ||
!data.changed.localStorage["https://sectest1.example.org"],
"Nothing got changed for local storage");
ok(!data.changed || !data.changed.sessionStorage ||
!data.changed.sessionStorage["https://sectest1.example.org"],
"Nothing got changed for session storage");
ok(!data.changed || !data.changed.indexedDB ||
!data.changed.indexedDB["https://sectest1.example.org"],
"Nothing got changed for indexed db");
ok(!data.deleted || !data.deleted.cookies ||
!data.deleted.cookies["https://sectest1.example.org"],
"Nothing got deleted for cookies");
ok(!data.deleted || !data.deleted.localStorage ||
!data.deleted.localStorage["https://sectest1.example.org"],
"Nothing got deleted for local storage");
ok(!data.deleted || !data.deleted.sessionStorage ||
!data.deleted.sessionStorage["https://sectest1.example.org"],
"Nothing got deleted for session storage");
ok(!data.deleted || !data.deleted.indexedDB ||
!data.deleted.indexedDB["https://sectest1.example.org"],
"Nothing got deleted for indexed db");
if (!Object.keys(shouldBeEmpty).length) {
info("Everything to be received is received.");
endTestReloaded();
}
};
let endTestReloaded = () => {
front.off("stores-update", onStoresUpdate);
resolve();
};
front.on("stores-update", onStoresUpdate);
let iframe = content.document.createElement("iframe");
iframe.src = ALT_DOMAIN_SECURED + "storage-secured-iframe.html";
content.document.querySelector("body").appendChild(iframe);
});
}
function testRemoveIframe(front) {
info("Testing if iframe removal works properly");
return new Promise(resolve => {
let shouldBeEmpty = {
localStorage: {
"http://sectest1.example.org": []
},
sessionStorage: {
"http://sectest1.example.org": []
},
Cache: {
"http://sectest1.example.org": []
},
indexedDB: {
"http://sectest1.example.org": []
}
};
let onStoresUpdate = data => {
info("checking if the hosts list is correct for this iframe deletion");
markOutMatched(shouldBeEmpty, data.deleted, true);
ok(!data.deleted.cookies || !data.deleted.cookies["sectest1.example.org"],
"Nothing got deleted for Cookies as " +
"the same hostname is still present");
ok(!data.changed || !data.changed.cookies ||
!data.changed.cookies["http://sectest1.example.org"],
"Nothing got changed for cookies");
ok(!data.changed || !data.changed.localStorage ||
!data.changed.localStorage["http://sectest1.example.org"],
"Nothing got changed for local storage");
ok(!data.changed || !data.changed.sessionStorage ||
!data.changed.sessionStorage["http://sectest1.example.org"],
"Nothing got changed for session storage");
ok(!data.added || !data.added.cookies ||
!data.added.cookies["http://sectest1.example.org"],
"Nothing got added for cookies");
ok(!data.added || !data.added.localStorage ||
!data.added.localStorage["http://sectest1.example.org"],
"Nothing got added for local storage");
ok(!data.added || !data.added.sessionStorage ||
!data.added.sessionStorage["http://sectest1.example.org"],
"Nothing got added for session storage");
if (!Object.keys(shouldBeEmpty).length) {
info("Everything to be received is received.");
endTestReloaded();
}
};
let endTestReloaded = () => {
front.off("stores-update", onStoresUpdate);
resolve();
};
front.on("stores-update", onStoresUpdate);
for (let iframe of content.document.querySelectorAll("iframe")) {
if (iframe.src.startsWith("http:")) {
iframe.remove();
break;
}
}
});
}
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-dynamic-windows.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = StorageFront(client, form);
let data = yield front.listStores();
yield testStores(data, front);
yield clearStorage();
// Forcing GC/CC to get rid of docshells and windows created by this test.
forceCollections();
yield client.close();
forceCollections();
DebuggerServer.destroy();
forceCollections();
});

View file

@ -0,0 +1,610 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {StorageFront} = require("devtools/shared/fronts/storage");
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
const storeMap = {
cookies: {
"test1.example.org": [
{
name: "c1",
value: "foobar",
expires: 2000000000000,
path: "/browser",
host: "test1.example.org",
isDomain: false,
isSecure: false,
},
{
name: "cs2",
value: "sessionCookie",
path: "/",
host: ".example.org",
expires: 0,
isDomain: true,
isSecure: false,
},
{
name: "c3",
value: "foobar-2",
expires: 2000000001000,
path: "/",
host: "test1.example.org",
isDomain: false,
isSecure: false,
},
{
name: "uc1",
value: "foobar",
host: ".example.org",
path: "/",
expires: 0,
isDomain: true,
isSecure: true,
}
],
"sectest1.example.org": [
{
name: "uc1",
value: "foobar",
host: ".example.org",
path: "/",
expires: 0,
isDomain: true,
isSecure: true,
},
{
name: "cs2",
value: "sessionCookie",
path: "/",
host: ".example.org",
expires: 0,
isDomain: true,
isSecure: false,
},
{
name: "sc1",
value: "foobar",
path: "/browser/devtools/server/tests/browser/",
host: "sectest1.example.org",
expires: 0,
isDomain: false,
isSecure: false,
}
]
},
localStorage: {
"http://test1.example.org": [
{
name: "ls1",
value: "foobar"
},
{
name: "ls2",
value: "foobar-2"
}
],
"http://sectest1.example.org": [
{
name: "iframe-u-ls1",
value: "foobar"
}
],
"https://sectest1.example.org": [
{
name: "iframe-s-ls1",
value: "foobar"
}
]
},
sessionStorage: {
"http://test1.example.org": [
{
name: "ss1",
value: "foobar-3"
}
],
"http://sectest1.example.org": [
{
name: "iframe-u-ss1",
value: "foobar1"
},
{
name: "iframe-u-ss2",
value: "foobar2"
}
],
"https://sectest1.example.org": [
{
name: "iframe-s-ss1",
value: "foobar-2"
}
]
}
};
const IDBValues = {
listStoresResponse: {
"http://test1.example.org": [
["idb1", "obj1"], ["idb1", "obj2"], ["idb2", "obj3"]
],
"http://sectest1.example.org": [
],
"https://sectest1.example.org": [
["idb-s1", "obj-s1"], ["idb-s2", "obj-s2"]
]
},
dbDetails : {
"http://test1.example.org": [
{
db: "idb1",
origin: "http://test1.example.org",
version: 1,
objectStores: 2
},
{
db: "idb2",
origin: "http://test1.example.org",
version: 1,
objectStores: 1
},
],
"http://sectest1.example.org": [
],
"https://sectest1.example.org": [
{
db: "idb-s1",
origin: "https://sectest1.example.org",
version: 1,
objectStores: 1
},
{
db: "idb-s2",
origin: "https://sectest1.example.org",
version: 1,
objectStores: 1
},
]
},
objectStoreDetails: {
"http://test1.example.org": {
idb1: [
{
objectStore: "obj1",
keyPath: "id",
autoIncrement: false,
indexes: [
{
name: "name",
keyPath: "name",
"unique": false,
multiEntry: false,
},
{
name: "email",
keyPath: "email",
"unique": true,
multiEntry: false,
},
]
},
{
objectStore: "obj2",
keyPath: "id2",
autoIncrement: false,
indexes: []
}
],
idb2: [
{
objectStore: "obj3",
keyPath: "id3",
autoIncrement: false,
indexes: [
{
name: "name2",
keyPath: "name2",
"unique": true,
multiEntry: false,
}
]
},
]
},
"http://sectest1.example.org" : {},
"https://sectest1.example.org": {
"idb-s1": [
{
objectStore: "obj-s1",
keyPath: "id",
autoIncrement: false,
indexes: []
},
],
"idb-s2": [
{
objectStore: "obj-s2",
keyPath: "id3",
autoIncrement: true,
indexes: [
{
name: "name2",
keyPath: "name2",
"unique": true,
multiEntry: false,
}
]
},
]
}
},
entries: {
"http://test1.example.org": {
"idb1#obj1": [
{
name: 1,
value: {
id: 1,
name: "foo",
email: "foo@bar.com",
}
},
{
name: 2,
value: {
id: 2,
name: "foo2",
email: "foo2@bar.com",
}
},
{
name: 3,
value: {
id: 3,
name: "foo2",
email: "foo3@bar.com",
}
}
],
"idb1#obj2": [
{
name: 1,
value: {
id2: 1,
name: "foo",
email: "foo@bar.com",
extra: "baz"
}
}
],
"idb2#obj3": []
},
"http://sectest1.example.org" : {},
"https://sectest1.example.org": {
"idb-s1#obj-s1": [
{
name: 6,
value: {
id: 6,
name: "foo",
email: "foo@bar.com",
}
},
{
name: 7,
value: {
id: 7,
name: "foo2",
email: "foo2@bar.com",
}
}
],
"idb-s2#obj-s2": [
{
name: 13,
value: {
id2: 13,
name2: "foo",
email: "foo@bar.com",
}
}
]
}
}
};
function finishTests(client) {
let closeConnection = () => {
};
}
function* testStores(data) {
ok(data.cookies, "Cookies storage actor is present");
ok(data.localStorage, "Local Storage storage actor is present");
ok(data.sessionStorage, "Session Storage storage actor is present");
ok(data.indexedDB, "Indexed DB storage actor is present");
yield testCookies(data.cookies);
yield testLocalStorage(data.localStorage);
yield testSessionStorage(data.sessionStorage);
yield testIndexedDB(data.indexedDB);
}
function testCookies(cookiesActor) {
is(Object.keys(cookiesActor.hosts).length, 2, "Correct number of host entries for cookies");
return testCookiesObjects(0, cookiesActor.hosts, cookiesActor);
}
var testCookiesObjects = Task.async(function* (index, hosts, cookiesActor) {
let host = Object.keys(hosts)[index];
let matchItems = data => {
let cookiesLength = 0;
for (let secureCookie of storeMap.cookies[host]) {
if (secureCookie.isSecure) {
++cookiesLength;
}
}
// Any secure cookies did not get stored in the database.
is(data.total, storeMap.cookies[host].length - cookiesLength,
"Number of cookies in host " + host + " matches");
for (let item of data.data) {
let found = false;
for (let toMatch of storeMap.cookies[host]) {
if (item.name == toMatch.name) {
found = true;
ok(true, "Found cookie " + item.name + " in response");
is(item.value.str, toMatch.value, "The value matches.");
is(item.expires, toMatch.expires, "The expiry time matches.");
is(item.path, toMatch.path, "The path matches.");
is(item.host, toMatch.host, "The host matches.");
is(item.isSecure, toMatch.isSecure, "The isSecure value matches.");
is(item.isDomain, toMatch.isDomain, "The isDomain value matches.");
break;
}
}
ok(found, "cookie " + item.name + " should exist in response");
}
};
ok(!!storeMap.cookies[host], "Host is present in the list : " + host);
matchItems(yield cookiesActor.getStoreObjects(host));
if (index == Object.keys(hosts).length - 1) {
return;
}
yield testCookiesObjects(++index, hosts, cookiesActor);
});
function testLocalStorage(localStorageActor) {
is(Object.keys(localStorageActor.hosts).length, 3,
"Correct number of host entries for local storage");
return testLocalStorageObjects(0, localStorageActor.hosts, localStorageActor);
}
var testLocalStorageObjects = Task.async(function* (index, hosts, localStorageActor) {
let host = Object.keys(hosts)[index];
let matchItems = data => {
is(data.total, storeMap.localStorage[host].length,
"Number of local storage items in host " + host + " matches");
for (let item of data.data) {
let found = false;
for (let toMatch of storeMap.localStorage[host]) {
if (item.name == toMatch.name) {
found = true;
ok(true, "Found local storage item " + item.name + " in response");
is(item.value.str, toMatch.value, "The value matches.");
break;
}
}
ok(found, "local storage item " + item.name + " should exist in response");
}
};
ok(!!storeMap.localStorage[host], "Host is present in the list : " + host);
matchItems(yield localStorageActor.getStoreObjects(host));
if (index == Object.keys(hosts).length - 1) {
return;
}
yield testLocalStorageObjects(++index, hosts, localStorageActor);
});
function testSessionStorage(sessionStorageActor) {
is(Object.keys(sessionStorageActor.hosts).length, 3,
"Correct number of host entries for session storage");
return testSessionStorageObjects(0, sessionStorageActor.hosts,
sessionStorageActor);
}
var testSessionStorageObjects = Task.async(function* (index, hosts, sessionStorageActor) {
let host = Object.keys(hosts)[index];
let matchItems = data => {
is(data.total, storeMap.sessionStorage[host].length,
"Number of session storage items in host " + host + " matches");
for (let item of data.data) {
let found = false;
for (let toMatch of storeMap.sessionStorage[host]) {
if (item.name == toMatch.name) {
found = true;
ok(true, "Found session storage item " + item.name + " in response");
is(item.value.str, toMatch.value, "The value matches.");
break;
}
}
ok(found, "session storage item " + item.name + " should exist in response");
}
};
ok(!!storeMap.sessionStorage[host], "Host is present in the list : " + host);
matchItems(yield sessionStorageActor.getStoreObjects(host));
if (index == Object.keys(hosts).length - 1) {
return;
}
yield testSessionStorageObjects(++index, hosts, sessionStorageActor);
});
var testIndexedDB = Task.async(function* (indexedDBActor) {
is(Object.keys(indexedDBActor.hosts).length, 3,
"Correct number of host entries for indexed db");
for (let host in indexedDBActor.hosts) {
for (let item of indexedDBActor.hosts[host]) {
let parsedItem = JSON.parse(item);
let found = false;
for (let toMatch of IDBValues.listStoresResponse[host]) {
if (toMatch[0] == parsedItem[0] && toMatch[1] == parsedItem[1]) {
found = true;
break;
}
}
ok(found, item + " should exist in list stores response");
}
}
yield testIndexedDBs(0, indexedDBActor.hosts, indexedDBActor);
yield testObjectStores(0, indexedDBActor.hosts, indexedDBActor);
yield testIDBEntries(0, indexedDBActor.hosts, indexedDBActor);
});
var testIndexedDBs = Task.async(function* (index, hosts, indexedDBActor) {
let host = Object.keys(hosts)[index];
let matchItems = data => {
is(data.total, IDBValues.dbDetails[host].length,
"Number of indexed db in host " + host + " matches");
for (let item of data.data) {
let found = false;
for (let toMatch of IDBValues.dbDetails[host]) {
if (item.db == toMatch.db) {
found = true;
ok(true, "Found indexed db " + item.db + " in response");
is(item.origin, toMatch.origin, "The origin matches.");
is(item.version, toMatch.version, "The version matches.");
is(item.objectStores, toMatch.objectStores,
"The numebr of object stores matches.");
break;
}
}
ok(found, "indexed db " + item.name + " should exist in response");
}
};
ok(!!IDBValues.dbDetails[host], "Host is present in the list : " + host);
matchItems(yield indexedDBActor.getStoreObjects(host));
if (index == Object.keys(hosts).length - 1) {
return;
}
yield testIndexedDBs(++index, hosts, indexedDBActor);
});
var testObjectStores = Task.async(function* (index, hosts, indexedDBActor) {
let host = Object.keys(hosts)[index];
let matchItems = (data, db) => {
is(data.total, IDBValues.objectStoreDetails[host][db].length,
"Number of object stores in host " + host + " matches");
for (let item of data.data) {
let found = false;
for (let toMatch of IDBValues.objectStoreDetails[host][db]) {
if (item.objectStore == toMatch.objectStore) {
found = true;
ok(true, "Found object store " + item.objectStore + " in response");
is(item.keyPath, toMatch.keyPath, "The keyPath matches.");
is(item.autoIncrement, toMatch.autoIncrement, "The autoIncrement matches.");
item.indexes = JSON.parse(item.indexes);
is(item.indexes.length, toMatch.indexes.length, "Number of indexes match");
for (let index of item.indexes) {
let indexFound = false;
for (let toMatchIndex of toMatch.indexes) {
if (toMatchIndex.name == index.name) {
indexFound = true;
ok(true, "Found index " + index.name);
is(index.keyPath, toMatchIndex.keyPath,
"The keyPath of index matches.");
is(index.unique, toMatchIndex.unique, "The unique matches");
is(index.multiEntry, toMatchIndex.multiEntry,
"The multiEntry matches");
break;
}
}
ok(indexFound, "Index " + index + " should exist in response");
}
break;
}
}
ok(found, "indexed db " + item.name + " should exist in response");
}
};
ok(!!IDBValues.objectStoreDetails[host], "Host is present in the list : " + host);
for (let name of hosts[host]) {
let objName = JSON.parse(name).slice(0, 1);
matchItems((
yield indexedDBActor.getStoreObjects(host, [JSON.stringify(objName)])
), objName[0]);
}
if (index == Object.keys(hosts).length - 1) {
return;
}
yield testObjectStores(++index, hosts, indexedDBActor);
});
var testIDBEntries = Task.async(function* (index, hosts, indexedDBActor) {
let host = Object.keys(hosts)[index];
let matchItems = (data, obj) => {
is(data.total, IDBValues.entries[host][obj].length,
"Number of items in object store " + obj + " matches");
for (let item of data.data) {
let found = false;
for (let toMatch of IDBValues.entries[host][obj]) {
if (item.name == toMatch.name) {
found = true;
ok(true, "Found indexed db item " + item.name + " in response");
let value = JSON.parse(item.value.str);
is(Object.keys(value).length, Object.keys(toMatch.value).length,
"Number of entries in the value matches");
for (let key in value) {
is(value[key], toMatch.value[key],
"value of " + key + " value key matches");
}
break;
}
}
ok(found, "indexed db item " + item.name + " should exist in response");
}
};
ok(!!IDBValues.entries[host], "Host is present in the list : " + host);
for (let name of hosts[host]) {
let parsed = JSON.parse(name);
matchItems((
yield indexedDBActor.getStoreObjects(host, [name])
), parsed[0] + "#" + parsed[1]);
}
if (index == Object.keys(hosts).length - 1) {
return;
}
yield testObjectStores(++index, hosts, indexedDBActor);
});
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = StorageFront(client, form);
let data = yield front.listStores();
yield testStores(data);
yield clearStorage();
// Forcing GC/CC to get rid of docshells and windows created by this test.
forceCollections();
yield client.close();
forceCollections();
DebuggerServer.destroy();
forceCollections();
});

View file

@ -0,0 +1,304 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {StorageFront} = require("devtools/shared/fronts/storage");
const beforeReload = {
cookies: ["test1.example.org", "sectest1.example.org"],
localStorage: ["http://test1.example.org", "http://sectest1.example.org"],
sessionStorage: ["http://test1.example.org", "http://sectest1.example.org"],
};
const TESTS = [
// index 0
{
action: function (win) {
info('win.addCookie("c1", "foobar1")');
win.addCookie("c1", "foobar1");
info('win.addCookie("c2", "foobar2")');
win.addCookie("c2", "foobar2");
info('win.localStorage.setItem("l1", "foobar1")');
win.localStorage.setItem("l1", "foobar1");
},
expected: {
added: {
cookies: {
"test1.example.org": ["c1", "c2"]
},
localStorage: {
"http://test1.example.org": ["l1"]
}
}
}
},
// index 1
{
action: function (win) {
info('win.addCookie("c1", "new_foobar1")');
win.addCookie("c1", "new_foobar1");
info('win.localStorage.setItem("l2", "foobar2")');
win.localStorage.setItem("l2", "foobar2");
},
expected: {
changed: {
cookies: {
"test1.example.org": ["c1"]
}
},
added: {
localStorage: {
"http://test1.example.org": ["l2"]
}
}
}
},
// index 2
{
action: function (win) {
info('win.removeCookie("c2")');
win.removeCookie("c2");
info('win.localStorage.removeItem("l1")');
win.localStorage.removeItem("l1");
info('win.localStorage.setItem("l3", "foobar3")');
win.localStorage.setItem("l3", "foobar3");
},
expected: {
deleted: {
cookies: {
"test1.example.org": ["c2"]
},
localStorage: {
"http://test1.example.org": ["l1"]
}
},
added: {
localStorage: {
"http://test1.example.org": ["l3"]
}
}
}
},
// index 3
{
action: function (win) {
info('win.removeCookie("c1")');
win.removeCookie("c1");
info('win.addCookie("c3", "foobar3")');
win.addCookie("c3", "foobar3");
info('win.localStorage.removeItem("l2")');
win.localStorage.removeItem("l2");
info('win.sessionStorage.setItem("s1", "foobar1")');
win.sessionStorage.setItem("s1", "foobar1");
info('win.sessionStorage.setItem("s2", "foobar2")');
win.sessionStorage.setItem("s2", "foobar2");
info('win.localStorage.setItem("l3", "new_foobar3")');
win.localStorage.setItem("l3", "new_foobar3");
},
expected: {
added: {
cookies: {
"test1.example.org": ["c3"]
},
sessionStorage: {
"http://test1.example.org": ["s1", "s2"]
}
},
changed: {
localStorage: {
"http://test1.example.org": ["l3"]
}
},
deleted: {
cookies: {
"test1.example.org": ["c1"]
},
localStorage: {
"http://test1.example.org": ["l2"]
}
}
}
},
// index 4
{
action: function (win) {
info('win.sessionStorage.removeItem("s1")');
win.sessionStorage.removeItem("s1");
},
expected: {
deleted: {
sessionStorage: {
"http://test1.example.org": ["s1"]
}
}
}
},
// index 5
{
action: function (win) {
info("win.clearCookies()");
win.clearCookies();
},
expected: {
deleted: {
cookies: {
"test1.example.org": ["c3"]
}
}
}
}
];
function markOutMatched(toBeEmptied, data) {
if (!Object.keys(toBeEmptied).length) {
info("Object empty");
return;
}
ok(Object.keys(data).length, "At least one storage type should be present");
for (let storageType in toBeEmptied) {
if (!data[storageType]) {
continue;
}
info("Testing for " + storageType);
for (let host in data[storageType]) {
ok(toBeEmptied[storageType][host], "Host " + host + " found");
for (let item of data[storageType][host]) {
let index = toBeEmptied[storageType][host].indexOf(item);
ok(index > -1, "Item found - " + item);
if (index > -1) {
toBeEmptied[storageType][host].splice(index, 1);
}
}
if (!toBeEmptied[storageType][host].length) {
delete toBeEmptied[storageType][host];
}
}
if (!Object.keys(toBeEmptied[storageType]).length) {
delete toBeEmptied[storageType];
}
}
}
function onStoresUpdate(expected, {added, changed, deleted}, index) {
info("inside stores update for index " + index);
// Here, added, changed and deleted might be null even if they are required as
// per expected. This is fine as they might come in the next stores-update
// call or have already come in the previous one.
if (added) {
info("matching added object for index " + index);
markOutMatched(expected.added, added);
}
if (changed) {
info("matching changed object for index " + index);
markOutMatched(expected.changed, changed);
}
if (deleted) {
info("matching deleted object for index " + index);
markOutMatched(expected.deleted, deleted);
}
if ((!expected.added || !Object.keys(expected.added).length) &&
(!expected.changed || !Object.keys(expected.changed).length) &&
(!expected.deleted || !Object.keys(expected.deleted).length)) {
info("Everything expected has been received for index " + index);
} else {
info("Still some updates pending for index " + index);
}
}
function runTest({action, expected}, front, win, index) {
return new Promise(resolve => {
front.once("stores-update", function (addedChangedDeleted) {
onStoresUpdate(expected, addedChangedDeleted, index);
resolve();
});
info("Running test at index " + index);
action(win);
});
}
function* testClearLocalAndSessionStores(front, win) {
return new Promise(resolve => {
// We need to wait until we have received stores-cleared for both local and
// session storage.
let localStorage = false;
let sessionStorage = false;
front.on("stores-cleared", function onStoresCleared(data) {
storesCleared(data);
if (data.localStorage) {
localStorage = true;
}
if (data.sessionStorage) {
sessionStorage = true;
}
if (localStorage && sessionStorage) {
front.off("stores-cleared", onStoresCleared);
resolve();
}
});
win.clearLocalAndSessionStores();
});
}
function storesCleared(data) {
if (data.sessionStorage || data.localStorage) {
let hosts = data.sessionStorage || data.localStorage;
info("Stores cleared required for session storage");
is(hosts.length, 1, "number of hosts is 1");
is(hosts[0], "http://test1.example.org",
"host matches for " + Object.keys(data)[0]);
} else {
ok(false, "Stores cleared should only be for local and session storage");
}
}
function* finishTests(client) {
yield client.close();
DebuggerServer.destroy();
finish();
}
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "storage-updates.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = StorageFront(client, form);
let win = doc.defaultView.wrappedJSObject;
yield front.listStores();
for (let i = 0; i < TESTS.length; i++) {
let test = TESTS[i];
yield runTest(test, front, win, i);
}
yield testClearLocalAndSessionStores(front, win);
yield finishTests(client);
});

View file

@ -0,0 +1,40 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that StyleSheetActor.getText handles empty text correctly.
const {StyleSheetsFront} = require("devtools/shared/fronts/stylesheets");
const CONTENT = "<style>body { background-color: #f0c; }</style>";
const TEST_URI = "data:text/html;charset=utf-8," + encodeURIComponent(CONTENT);
add_task(function* () {
yield addTab(TEST_URI);
info("Initialising the debugger server and client.");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
info("Attaching to the active tab.");
yield client.attachTab(form.actor);
let front = StyleSheetsFront(client, form);
ok(front, "The StyleSheetsFront was created.");
let sheets = yield front.getStyleSheets();
ok(sheets, "getStyleSheets() succeeded");
is(sheets.length, 1,
"getStyleSheets() returned the correct number of sheets");
let sheet = sheets[0];
yield sheet.update("", false);
let longStr = yield sheet.getText();
let source = yield longStr.string();
is(source, "", "text is empty");
yield client.close();
});

View file

@ -0,0 +1,38 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that StyleSheetsActor.getStyleSheets() works if an iframe does not have
// a content document.
const {StyleSheetsFront} = require("devtools/shared/fronts/stylesheets");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "stylesheets-nested-iframes.html");
let doc = browser.contentDocument;
info("Initialising the debugger server and client.");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
info("Attaching to the active tab.");
yield client.attachTab(form.actor);
let front = StyleSheetsFront(client, form);
ok(front, "The StyleSheetsFront was created.");
let sheets = yield front.getStyleSheets();
ok(sheets, "getStyleSheets() succeeded even with documentless iframes.");
// Bug 285395 limits the number of nested iframes to 10. There's one sheet per
// frame so we should get 10 sheets. However, the limit might change in the
// future so it's better not to rely on the limit. Asserting > 2 ensures that
// the test page is actually loading nested iframes and this test is doing
// something sensible (if we got this far, the test has served its purpose).
ok(sheets.length > 2, sheets.length + " sheets found (expected 3 or more).");
yield client.close();
});

View file

@ -0,0 +1,63 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the timeline front's start/stop/isRecording methods work in a
// simple use case, and that markers events are sent when operations occur.
// Note that this test isn't concerned with which markers are actually recorded,
// just that markers are recorded at all.
// Trying to check marker types here may lead to intermittents, see bug 1066474.
const {TimelineFront} = require("devtools/shared/fronts/timeline");
add_task(function* () {
let browser = yield addTab("data:text/html;charset=utf-8,mop");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = TimelineFront(client, form);
ok(front, "The TimelineFront was created");
let isActive = yield front.isRecording();
ok(!isActive, "The TimelineFront is not initially recording");
info("Flush any pending reflows");
let forceSyncReflow = doc.body.innerHeight;
info("Start recording");
yield front.start({ withMarkers: true });
isActive = yield front.isRecording();
ok(isActive, "The TimelineFront is now recording");
info("Change some style on the page to cause style/reflow/paint");
let onMarkers = once(front, "markers");
doc.body.style.padding = "10px";
let markers = yield onMarkers;
ok(true, "The markers event was fired");
ok(markers.length > 0, "Markers were returned");
info("Flush pending reflows again");
forceSyncReflow = doc.body.innerHeight;
info("Change some style on the page to cause style/paint");
onMarkers = once(front, "markers");
doc.body.style.backgroundColor = "red";
markers = yield onMarkers;
ok(markers.length > 0, "markers were returned");
yield front.stop();
isActive = yield front.isRecording();
ok(!isActive, "Not recording after stop()");
yield client.close();
gBrowser.removeCurrentTab();
});

View file

@ -0,0 +1,69 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the timeline can also record data from the memory and framerate
// actors, emitted as events in tadem with the markers.
const {TimelineFront} = require("devtools/shared/fronts/timeline");
add_task(function* () {
let browser = yield addTab("data:text/html;charset=utf-8,mop");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = TimelineFront(client, form);
info("Start timeline marker recording");
yield front.start({ withMemory: true, withTicks: true });
let updatedMemory = 0;
let updatedTicks = 0;
front.on("memory", (delta, measurement) => {
ok(delta > 0, "The delta should be a timestamp.");
ok(measurement, "The measurement should not be null.");
ok(measurement.total > 0, "There should be a 'total' value in the measurement.");
info("Received 'memory' event at " + delta + " with " + measurement.toSource());
updatedMemory++;
});
front.on("ticks", (delta, ticks) => {
ok(delta > 0, "The delta should be a timestamp.");
ok(ticks, "The ticks should not be null.");
info("Received 'ticks' event with " + ticks.toSource());
updatedTicks++;
});
ok((yield waitUntil(() => updatedMemory > 1)),
"Some memory measurements were emitted.");
ok((yield waitUntil(() => updatedTicks > 1)),
"Some refresh driver ticks were emitted.");
info("Stop timeline marker recording");
yield front.stop();
yield client.close();
gBrowser.removeCurrentTab();
});
/**
* Waits until a predicate returns true.
*
* @param function predicate
* Invoked once in a while until it returns true.
* @param number interval [optional]
* How often the predicate is invoked, in milliseconds.
*/
function waitUntil(predicate, interval = 10) {
if (predicate()) {
return Promise.resolve(true);
}
return new Promise(resolve =>
setTimeout(function () {
waitUntil(predicate).then(() => resolve(true));
}, interval));
}

View file

@ -0,0 +1,41 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the timeline front receives markers events for operations that occur in
// iframes.
const {TimelineFront} = require("devtools/shared/fronts/timeline");
add_task(function* () {
let browser = yield addTab(MAIN_DOMAIN + "timeline-iframe-parent.html");
let doc = browser.contentDocument;
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = TimelineFront(client, form);
info("Start timeline marker recording");
yield front.start({ withMarkers: true });
// Check that we get markers for a few iterations of the timer that runs in
// the child frame.
for (let i = 0; i < 3; i++) {
yield wait(300); // That's the time the child frame waits before changing styles.
let markers = yield once(front, "markers");
ok(markers.length, "Markers were received for operations in the child frame");
}
info("Stop timeline marker recording");
yield front.stop();
yield client.close();
gBrowser.removeCurrentTab();
});
function wait(ms) {
return new Promise(resolve =>
setTimeout(resolve, ms));
}

View file

@ -0,0 +1,15 @@
<html>
<head>
<script>
// change the eval function to ensure the window object in the debug-script is correctly wrapped
window.eval = function () {
return "unsecure-eval-called";
};
var globalAccessibleVar = "global-value";
</script>
</head>
<body>
<h1>debug script target</h1>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
window.allocs = [];
window.onload = function() {
function allocator() {
for (var i = 0; i < 1000; i++) {
window.allocs.push(new Object);
}
}
window.setInterval(allocator, 1);
};
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,29 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Performance tool + cycle collection test page</title>
</head>
<body>
<script type="text/javascript">
window.test = function () {
document.body.expando1 = { cycle: document.body };
SpecialPowers.Cu.forceCC();
document.body.expando2 = { cycle: document.body };
SpecialPowers.Cu.forceCC();
document.body.expando3 = { cycle: document.body };
SpecialPowers.Cu.forceCC();
setTimeout(window.test, 100);
};
test();
</script>
</body>
</html>

View file

@ -0,0 +1,27 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Performance tool + garbage collection test page</title>
</head>
<body>
<script type="text/javascript">
var x = 1;
window.test = function () {
SpecialPowers.Cu.forceGC();
document.body.style.borderTop = x + "px solid red";
x = 1^x;
document.body.innerHeight; // flush pending reflows
// Prevent this script from being garbage collected.
setTimeout(window.test, 100);
};
test();
</script>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Performance tool + innerHTML test page</title>
</head>
<body>
<script type="text/javascript">
"use strict";
window.test = function () {
document.body.innerHTML = "<h1>LOL</h1>";
};
setInterval(window.test, 100);
</script>
</body>
</html>

View file

@ -0,0 +1,25 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Performance test page</title>
</head>
<body>
<script type="text/javascript">
var x = 1;
function test() {
document.body.style.borderTop = x + "px solid red";
x = 1^x;
document.body.innerHeight; // flush pending reflows
}
// Prevent this script from being garbage collected.
window.setInterval(test, 1);
</script>
</body>
</html>

View file

@ -0,0 +1,203 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {DebuggerClient} = require("devtools/shared/client/main");
const {DebuggerServer} = require("devtools/server/main");
const {defer} = require("promise");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const Services = require("Services");
const PATH = "browser/devtools/server/tests/browser/";
const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
// All tests are asynchronous.
waitForExplicitFinish();
/**
* Add a new test tab in the browser and load the given url.
* @param {String} url The url to be loaded in the new tab
* @return a promise that resolves to the new browser that the document
* is loaded in. Note that we cannot return the document
* directly, since this would be a CPOW in the e10s case,
* and Promises cannot be resolved with CPOWs (see bug 1233497).
*/
var addTab = Task.async(function* (url) {
info(`Adding a new tab with URL: ${url}`);
let tab = gBrowser.selectedTab = gBrowser.addTab(url);
yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
info(`Tab added and URL ${url} loaded`);
return tab.linkedBrowser;
});
function* initAnimationsFrontForUrl(url) {
const {AnimationsFront} = require("devtools/shared/fronts/animation");
const {InspectorFront} = require("devtools/shared/fronts/inspector");
yield addTab(url);
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let inspector = InspectorFront(client, form);
let walker = yield inspector.getWalker();
let animations = AnimationsFront(client, form);
return {inspector, walker, animations, client};
}
function initDebuggerServer() {
try {
// Sometimes debugger server does not get destroyed correctly by previous
// tests.
DebuggerServer.destroy();
} catch (e) {
info(`DebuggerServer destroy error: ${e}\n${e.stack}`);
}
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
/**
* Connect a debugger client.
* @param {DebuggerClient}
* @return {Promise} Resolves to the selected tabActor form when the client is
* connected.
*/
function connectDebuggerClient(client) {
return client.connect()
.then(() => client.listTabs())
.then(tabs => {
return tabs.tabs[tabs.selected];
});
}
/**
* Wait for eventName on target.
* @param {Object} target An observable object that either supports on/off or
* addEventListener/removeEventListener
* @param {String} eventName
* @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
* @return A promise that resolves when the event has been handled
*/
function once(target, eventName, useCapture = false) {
info("Waiting for event: '" + eventName + "' on " + target + ".");
return new Promise(resolve => {
for (let [add, remove] of [
["addEventListener", "removeEventListener"],
["addListener", "removeListener"],
["on", "off"]
]) {
if ((add in target) && (remove in target)) {
target[add](eventName, function onEvent(...aArgs) {
info("Got event: '" + eventName + "' on " + target + ".");
target[remove](eventName, onEvent, useCapture);
resolve(...aArgs);
}, useCapture);
break;
}
}
});
}
/**
* Forces GC, CC and Shrinking GC to get rid of disconnected docshells and
* windows.
*/
function forceCollections() {
Cu.forceGC();
Cu.forceCC();
Cu.forceShrinkingGC();
}
/**
* Get a mock tabActor from a given window.
* This is sometimes useful to test actors or classes that use the tabActor in
* isolation.
* @param {DOMWindow} win
* @return {Object}
*/
function getMockTabActor(win) {
return {
window: win,
isRootActor: true
};
}
registerCleanupFunction(function tearDown() {
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
});
function idleWait(time) {
return DevToolsUtils.waitForTime(time);
}
function busyWait(time) {
let start = Date.now();
let stack;
while (Date.now() - start < time) { stack = Components.stack; }
}
/**
* Waits until a predicate returns true.
*
* @param function predicate
* Invoked once in a while until it returns true.
* @param number interval [optional]
* How often the predicate is invoked, in milliseconds.
*/
function waitUntil(predicate, interval = 10) {
if (predicate()) {
return Promise.resolve(true);
}
return new Promise(resolve => {
setTimeout(function () {
waitUntil(predicate).then(() => resolve(true));
}, interval);
});
}
function waitForMarkerType(front, types, predicate,
unpackFun = (name, data) => data.markers,
eventName = "timeline-data")
{
types = [].concat(types);
predicate = predicate || function () { return true; };
let filteredMarkers = [];
let { promise, resolve } = defer();
info("Waiting for markers of type: " + types);
function handler(name, data) {
if (typeof name === "string" && name !== "markers") {
return;
}
let markers = unpackFun(name, data);
info("Got markers: " + JSON.stringify(markers, null, 2));
filteredMarkers = filteredMarkers.concat(markers.filter(m => types.indexOf(m.name) !== -1));
if (types.every(t => filteredMarkers.some(m => m.name === t)) && predicate(filteredMarkers)) {
front.off(eventName, handler);
resolve(filteredMarkers);
}
}
front.on(eventName, handler);
return promise;
}

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
First
<script>
window.onbeforeunload=function(e){
e.returnValue="?";
};
</script>
</body>
</html>

View file

@ -0,0 +1,9 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
Second
</body>
</html>

View file

@ -0,0 +1,117 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 965872 - Storage inspector actor with cookies, local storage and session storage.
-->
<head>
<meta charset="utf-8">
<title>Storage inspector test for listing hosts and storages</title>
</head>
<body>
<iframe src="http://sectest1.example.org/browser/devtools/server/tests/browser/storage-unsecured-iframe.html"></iframe>
<script type="application/javascript;version=1.7">
"use strict";
const partialHostname = location.hostname.match(/^[^.]+(\..*)$/)[1];
const cookieExpiresTime1 = 2000000000000;
const cookieExpiresTime2 = 2000000001000;
// Setting up some cookies to eat.
document.cookie = "c1=foobar; expires=" +
new Date(cookieExpiresTime1).toGMTString() + "; path=/browser";
document.cookie = "cs2=sessionCookie; path=/; domain=" + partialHostname;
document.cookie = "c3=foobar-2; expires=" +
new Date(cookieExpiresTime2).toGMTString() + "; path=/";
// ... and some local storage items ..
localStorage.setItem("ls1", "foobar");
localStorage.setItem("ls2", "foobar-2");
// ... and finally some session storage items too
sessionStorage.setItem("ss1", "foobar-3");
let idbGenerator = function*() {
let request = indexedDB.open("idb1", 1);
request.onerror = function() {
throw new Error("error opening db connection");
};
let db = yield new Promise(done => {
request.onupgradeneeded = event => {
let db = event.target.result;
let store1 = db.createObjectStore("obj1", { keyPath: "id" });
store1.createIndex("name", "name", { unique: false });
store1.createIndex("email", "email", { unique: true });
let store2 = db.createObjectStore("obj2", { keyPath: "id2" });
store1.transaction.oncomplete = () => {
done(db);
};
};
});
// Prevents AbortError
yield new Promise(done => {
request.onsuccess = done;
});
let transaction = db.transaction(["obj1", "obj2"], "readwrite");
let store1 = transaction.objectStore("obj1");
let store2 = transaction.objectStore("obj2");
store1.add({id: 1, name: "foo", email: "foo@bar.com"});
store1.add({id: 2, name: "foo2", email: "foo2@bar.com"});
store1.add({id: 3, name: "foo2", email: "foo3@bar.com"});
store2.add({
id2: 1,
name: "foo",
email: "foo@bar.com",
extra: "baz"
});
// Prevents AbortError during close()
yield new Promise(success => {
transaction.oncomplete = success;
});
db.close();
request = indexedDB.open("idb2", 1);
let db2 = yield new Promise(done => {
request.onupgradeneeded = event => {
let db2 = event.target.result;
let store3 = db2.createObjectStore("obj3", { keyPath: "id3" });
store3.createIndex("name2", "name2", { unique: true });
store3.transaction.oncomplete = () => {
done(db2);
}
};
});
// Prevents AbortError during close()
yield new Promise(done => {
request.onsuccess = done;
});
db2.close();
console.log("added cookies and stuff from main page");
};
function deleteDB(dbName) {
return new Promise(resolve => {
dump("removing database " + dbName + " from " + document.location + "\n");
indexedDB.deleteDatabase(dbName).onsuccess = resolve;
});
}
window.setup = function*() {
yield idbGenerator();
};
window.clear = function*() {
document.cookie = "c1=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
document.cookie = "c3=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
document.cookie = "cs2=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
localStorage.clear();
yield deleteDB("idb1");
yield deleteDB("idb2");
dump("removed cookies, localStorage and indexedDB data from " +
document.location + "\n");
};
</script>
</body>
</html>

View file

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* This generator function opens the given url in a new tab, then sets up the
* page by waiting for all cookies, indexedDB items etc. to be created.
*
* @param url {String} The url to be opened in the new tab
*
* @return {Promise} A promise that resolves after storage inspector is ready
*/
function* openTabAndSetupStorage(url) {
let content = yield addTab(url);
// Setup the async storages in main window and for all its iframes
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
/**
* Get all windows including frames recursively.
*
* @param {Window} [baseWindow]
* The base window at which to start looking for child windows
* (optional).
* @return {Set}
* A set of windows.
*/
function getAllWindows(baseWindow) {
let windows = new Set();
let _getAllWindows = function (win) {
windows.add(win.wrappedJSObject);
for (let i = 0; i < win.length; i++) {
_getAllWindows(win[i]);
}
};
_getAllWindows(baseWindow);
return windows;
}
let windows = getAllWindows(content);
for (let win of windows) {
if (win.setup) {
yield win.setup();
}
}
});
}
function* clearStorage() {
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
/**
* Get all windows including frames recursively.
*
* @param {Window} [baseWindow]
* The base window at which to start looking for child windows
* (optional).
* @return {Set}
* A set of windows.
*/
function getAllWindows(baseWindow) {
let windows = new Set();
let _getAllWindows = function (win) {
windows.add(win.wrappedJSObject);
for (let i = 0; i < win.length; i++) {
_getAllWindows(win[i]);
}
};
_getAllWindows(baseWindow);
return windows;
}
let windows = getAllWindows(content);
for (let win of windows) {
if (win.clear) {
yield win.clear();
}
}
});
}

View file

@ -0,0 +1,123 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 965872 - Storage inspector actor with cookies, local storage and session storage.
-->
<head>
<meta charset="utf-8">
<title>Storage inspector test for listing hosts and storages</title>
</head>
<body>
<iframe src="http://sectest1.example.org/browser/devtools/server/tests/browser/storage-unsecured-iframe.html"></iframe>
<iframe src="https://sectest1.example.org:443/browser/devtools/server/tests/browser/storage-secured-iframe.html"></iframe>
<script type="application/javascript;version=1.7">
"use strict";
const partialHostname = location.hostname.match(/^[^.]+(\..*)$/)[1];
const cookieExpiresTime1 = 2000000000000;
const cookieExpiresTime2 = 2000000001000;
// Setting up some cookies to eat.
document.cookie = "c1=foobar; expires=" +
new Date(cookieExpiresTime1).toGMTString() + "; path=/browser";
document.cookie = "cs2=sessionCookie; path=/; domain=" + partialHostname;
document.cookie = "c3=foobar-2; secure=true; expires=" +
new Date(cookieExpiresTime2).toGMTString() + "; path=/";
// ... and some local storage items ..
localStorage.setItem("ls1", "foobar");
localStorage.setItem("ls2", "foobar-2");
// ... and finally some session storage items too
sessionStorage.setItem("ss1", "foobar-3");
console.log("added cookies and stuff from main page");
let idbGenerator = function*() {
let request = indexedDB.open("idb1", 1);
request.onerror = function() {
throw new Error("error opening db connection");
};
let db = yield new Promise(done => {
request.onupgradeneeded = event => {
let db = event.target.result;
let store1 = db.createObjectStore("obj1", { keyPath: "id" });
store1.createIndex("name", "name", { unique: false });
store1.createIndex("email", "email", { unique: true });
let store2 = db.createObjectStore("obj2", { keyPath: "id2" });
store1.transaction.oncomplete = () => {
done(db);
};
};
});
// Prevents AbortError
yield new Promise(done => {
request.onsuccess = done;
});
let transaction = db.transaction(["obj1", "obj2"], "readwrite");
let store1 = transaction.objectStore("obj1");
let store2 = transaction.objectStore("obj2");
store1.add({id: 1, name: "foo", email: "foo@bar.com"});
store1.add({id: 2, name: "foo2", email: "foo2@bar.com"});
store1.add({id: 3, name: "foo2", email: "foo3@bar.com"});
store2.add({
id2: 1,
name: "foo",
email: "foo@bar.com",
extra: "baz"
});
// Prevents AbortError during close()
yield new Promise(success => {
transaction.oncomplete = success;
});
db.close();
request = indexedDB.open("idb2", 1);
let db2 = yield new Promise(done => {
request.onupgradeneeded = event => {
let db2 = event.target.result;
let store3 = db2.createObjectStore("obj3", { keyPath: "id3" });
store3.createIndex("name2", "name2", { unique: true });
store3.transaction.oncomplete = () => {
done(db2);
}
};
});
// Prevents AbortError during close()
yield new Promise(done => {
request.onsuccess = done;
});
db2.close();
dump("added cookies and stuff from main page\n");
};
function deleteDB(dbName) {
return new Promise(resolve => {
dump("removing database " + dbName + " from " + document.location + "\n");
indexedDB.deleteDatabase(dbName).onsuccess = resolve;
});
}
window.setup = function*() {
yield idbGenerator();
};
window.clear = function*() {
document.cookie = "c1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/browser";
document.cookie =
"c3=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; secure=true";
document.cookie =
"cs2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=" +
partialHostname;
localStorage.clear();
sessionStorage.clear();
yield deleteDB("idb1");
yield deleteDB("idb2");
dump("removed cookies, localStorage, sessionStorage and indexedDB data " +
"from " + document.location + "\n");
};
</script>
</body>
</html>

View file

@ -0,0 +1,94 @@
<!DOCTYPE HTML>
<html>
<!--
Iframe for testing multiple host detetion in storage actor
-->
<head>
<meta charset="utf-8">
</head>
<body>
<script type="application/javascript;version=1.7">
document.cookie = "sc1=foobar;";
localStorage.setItem("iframe-s-ls1", "foobar");
sessionStorage.setItem("iframe-s-ss1", "foobar-2");
let idbGenerator = function*() {
let request = indexedDB.open("idb-s1", 1);
request.onerror = function() {
throw new Error("error opening db connection");
};
let db = yield new Promise(done => {
request.onupgradeneeded = event => {
let db = event.target.result;
let store1 = db.createObjectStore("obj-s1", { keyPath: "id" });
store1.transaction.oncomplete = () => {
done(db);
};
};
});
yield new Promise(done => {
request.onsuccess = done;
});
let transaction = db.transaction(["obj-s1"], "readwrite");
let store1 = transaction.objectStore("obj-s1");
store1.add({id: 6, name: "foo", email: "foo@bar.com"});
store1.add({id: 7, name: "foo2", email: "foo2@bar.com"});
yield new Promise(success => {
transaction.oncomplete = success;
});
db.close();
request = indexedDB.open("idb-s2", 1);
let db2 = yield new Promise(done => {
request.onupgradeneeded = event => {
let db2 = event.target.result;
let store3 =
db2.createObjectStore("obj-s2", { keyPath: "id3", autoIncrement: true });
store3.createIndex("name2", "name2", { unique: true });
store3.transaction.oncomplete = () => {
done(db2);
};
};
});
yield new Promise(done => {
request.onsuccess = done;
});
transaction = db2.transaction(["obj-s2"], "readwrite");
let store3 = transaction.objectStore("obj-s2");
store3.add({id3: 16, name2: "foo", email: "foo@bar.com"});
yield new Promise(success => {
transaction.oncomplete = success;
});
db2.close();
dump("added cookies and stuff from secured iframe\n");
}
function deleteDB(dbName) {
return new Promise(resolve => {
dump("removing database " + dbName + " from " + document.location + "\n");
indexedDB.deleteDatabase(dbName).onsuccess = resolve;
});
}
window.setup = function*() {
yield idbGenerator();
};
window.clear = function*() {
document.cookie = "sc1=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
localStorage.clear();
yield deleteDB("idb-s1");
yield deleteDB("idb-s2");
console.log("removed cookies and stuff from secured iframe");
}
</script>
</body>
</html>

View file

@ -0,0 +1,26 @@
<!DOCTYPE HTML>
<html>
<!--
Iframe for testing multiple host detetion in storage actor
-->
<head>
<meta charset="utf-8">
</head>
<body>
<script>
document.cookie = "uc1=foobar; domain=.example.org; path=/; secure=true";
localStorage.setItem("iframe-u-ls1", "foobar");
sessionStorage.setItem("iframe-u-ss1", "foobar1");
sessionStorage.setItem("iframe-u-ss2", "foobar2");
console.log("added cookies and stuff from unsecured iframe");
window.clear = function*() {
document.cookie = "uc1=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
localStorage.clear();
sessionStorage.clear();
console.log("removed cookies and stuff from unsecured iframe");
}
</script>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 965872 - Storage inspector actor with cookies, local storage and session storage.
-->
<head>
<meta charset="utf-8">
<title>Storage inspector blank html for tests</title>
</head>
<body>
<script type="application/javascript;version=1.7">
"use strict";
window.addCookie = function(name, value, path, domain, expires, secure) {
let cookieString = name + "=" + value + ";";
if (path) {
cookieString += "path=" + path + ";";
}
if (domain) {
cookieString += "domain=" + domain + ";";
}
if (expires) {
cookieString += "expires=" + expires + ";";
}
if (secure) {
cookieString += "secure=true;";
}
document.cookie = cookieString;
};
window.removeCookie = function(name) {
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
};
window.clearLocalAndSessionStores = function() {
localStorage.clear();
sessionStorage.clear();
};
window.clearCookies = function() {
let cookies = document.cookie;
for (let cookie of cookies.split(";")) {
removeCookie(cookie.split("=")[0]);
}
};
</script>
</body>
</html>

View file

@ -0,0 +1,25 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>StyleSheetsActor iframe test</title>
<style>
p {
padding: 1em;
}
</style>
</head>
<body>
<p>A test page with nested iframes</p>
<iframe></iframe>
<script type="application/javascript;version=1.8">
let iframe = document.querySelector("iframe");
let i = parseInt(location.href.split("?")[1]) || 1;
// The frame can't have the same src URL as any of its ancestors.
// This will not infinitely recurse because a frame won't get a content
// document once it's nested deeply enough.
iframe.src = location.href.split("?")[0] + "?" + (++i);
</script>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Timeline iframe test - child frame</title>
</head>
<body>
<h1>Child frame</h1>
<script>
var h1 = document.querySelector("h1");
setInterval(function() {
h1.style.backgroundColor = "rgb(" + ((Math.random()*255)|0) + "," +
((Math.random()*255)|0) + "," +
((Math.random()*255)|0) +")";
h1.style.width = ((Math.random()*500)|0) + "px";
}, 300);
</script>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Timeline iframe test - parent frame</title>
</head>
<body>
<h1>Parent frame</h1>
<iframe src="timeline-iframe-child.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,6 @@
"use strict";
module.exports = {
// Extend from the common devtools mochitest eslintrc config.
"extends": "../../../.eslintrc.xpcshell.js"
};

View file

@ -0,0 +1 @@
debugger;

View file

@ -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>

View file

@ -0,0 +1 @@
function heinrichFun() { franz(); }

View file

@ -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>

View file

@ -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

View file

@ -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();
}

View file

@ -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;
}
});

View file

@ -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);
}

View file

@ -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>

View file

@ -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);
}
}

View file

@ -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>

View file

@ -0,0 +1,3 @@
.external-rule {
cursor: crosshair;
}

View file

@ -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>

View file

@ -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>

View file

@ -0,0 +1,10 @@
<html>
<head>
<body>
<script type="text/javascript">
window.onload = function() {
window.opener.postMessage('ready', '*');
};
</script>
</body>
</html>

View file

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 KiB

View file

@ -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));
}

View file

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<script>
var xhr = new XMLHttpRequest;
xhr.timeout = 1742;
xhr.expando = 'Expando!';
</script>
</html>

View file

@ -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"]
});
}
};

View file

@ -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));
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 KiB

View file

@ -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>

View file

@ -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>

Some files were not shown because too many files have changed in this diff Show more