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>