mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 07:48:38 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
6
devtools/server/tests/browser/.eslintrc.js
Normal file
6
devtools/server/tests/browser/.eslintrc.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
// Extend from the common devtools mochitest eslintrc config.
|
||||
"extends": "../../../.eslintrc.xpcshell.js"
|
||||
};
|
||||
170
devtools/server/tests/browser/animation.html
Normal file
170
devtools/server/tests/browser/animation.html
Normal 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>
|
||||
97
devtools/server/tests/browser/browser.ini
Normal file
97
devtools/server/tests/browser/browser.ini
Normal 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]
|
||||
|
|
@ -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();
|
||||
});
|
||||
32
devtools/server/tests/browser/browser_animation_getFrames.js
Normal file
32
devtools/server/tests/browser/browser_animation_getFrames.js
Normal 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();
|
||||
});
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
123
devtools/server/tests/browser/browser_animation_playerState.js
Normal file
123
devtools/server/tests/browser/browser_animation_playerState.js
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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`);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
35
devtools/server/tests/browser/browser_animation_simple.js
Normal file
35
devtools/server/tests/browser/browser_animation_simple.js
Normal 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();
|
||||
});
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
102
devtools/server/tests/browser/browser_canvasframe_helper_03.js
Normal file
102
devtools/server/tests/browser/browser_canvasframe_helper_03.js
Normal 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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
112
devtools/server/tests/browser/browser_canvasframe_helper_05.js
Normal file
112
devtools/server/tests/browser/browser_canvasframe_helper_05.js
Normal 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);
|
||||
}
|
||||
100
devtools/server/tests/browser/browser_canvasframe_helper_06.js
Normal file
100
devtools/server/tests/browser/browser_canvasframe_helper_06.js
Normal 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);
|
||||
}
|
||||
159
devtools/server/tests/browser/browser_directorscript_actors.js
Normal file
159
devtools/server/tests/browser/browser_directorscript_actors.js
Normal 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;
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
50
devtools/server/tests/browser/browser_markers-gc.js
Normal file
50
devtools/server/tests/browser/browser_markers-gc.js
Normal 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();
|
||||
});
|
||||
32
devtools/server/tests/browser/browser_markers-minor-gc.js
Normal file
32
devtools/server/tests/browser/browser_markers-minor-gc.js
Normal 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();
|
||||
});
|
||||
29
devtools/server/tests/browser/browser_markers-parse-html.js
Normal file
29
devtools/server/tests/browser/browser_markers-parse-html.js
Normal 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();
|
||||
});
|
||||
34
devtools/server/tests/browser/browser_markers-styles.js
Normal file
34
devtools/server/tests/browser/browser_markers-styles.js
Normal 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();
|
||||
});
|
||||
43
devtools/server/tests/browser/browser_markers-timestamp.js
Normal file
43
devtools/server/tests/browser/browser_markers-timestamp.js
Normal 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();
|
||||
});
|
||||
160
devtools/server/tests/browser/browser_navigateEvents.js
Normal file
160
devtools/server/tests/browser/browser_navigateEvents.js
Normal 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();
|
||||
});
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
45
devtools/server/tests/browser/browser_perf-profiler-01.js
Normal file
45
devtools/server/tests/browser/browser_perf-profiler-01.js
Normal 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();
|
||||
});
|
||||
46
devtools/server/tests/browser/browser_perf-profiler-02.js
Normal file
46
devtools/server/tests/browser/browser_perf-profiler-02.js
Normal 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();
|
||||
});
|
||||
54
devtools/server/tests/browser/browser_perf-profiler-03.js
Normal file
54
devtools/server/tests/browser/browser_perf-profiler-03.js
Normal 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();
|
||||
});
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -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`);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
63
devtools/server/tests/browser/browser_perf-samples-01.js
Normal file
63
devtools/server/tests/browser/browser_perf-samples-01.js
Normal 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();
|
||||
});
|
||||
77
devtools/server/tests/browser/browser_perf-samples-02.js
Normal file
77
devtools/server/tests/browser/browser_perf-samples-02.js
Normal 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();
|
||||
}
|
||||
76
devtools/server/tests/browser/browser_register_actor.js
Normal file
76
devtools/server/tests/browser/browser_register_actor.js
Normal 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);
|
||||
}
|
||||
294
devtools/server/tests/browser/browser_storage_dynamic_windows.js
Normal file
294
devtools/server/tests/browser/browser_storage_dynamic_windows.js
Normal 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();
|
||||
});
|
||||
610
devtools/server/tests/browser/browser_storage_listings.js
Normal file
610
devtools/server/tests/browser/browser_storage_listings.js
Normal 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();
|
||||
});
|
||||
304
devtools/server/tests/browser/browser_storage_updates.js
Normal file
304
devtools/server/tests/browser/browser_storage_updates.js
Normal 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);
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
63
devtools/server/tests/browser/browser_timeline.js
Normal file
63
devtools/server/tests/browser/browser_timeline.js
Normal 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();
|
||||
});
|
||||
69
devtools/server/tests/browser/browser_timeline_actors.js
Normal file
69
devtools/server/tests/browser/browser_timeline_actors.js
Normal 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));
|
||||
}
|
||||
41
devtools/server/tests/browser/browser_timeline_iframes.js
Normal file
41
devtools/server/tests/browser/browser_timeline_iframes.js
Normal 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));
|
||||
}
|
||||
15
devtools/server/tests/browser/director-script-target.html
Normal file
15
devtools/server/tests/browser/director-script-target.html
Normal 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>
|
||||
21
devtools/server/tests/browser/doc_allocations.html
Normal file
21
devtools/server/tests/browser/doc_allocations.html
Normal 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>
|
||||
29
devtools/server/tests/browser/doc_force_cc.html
Normal file
29
devtools/server/tests/browser/doc_force_cc.html
Normal 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>
|
||||
27
devtools/server/tests/browser/doc_force_gc.html
Normal file
27
devtools/server/tests/browser/doc_force_gc.html
Normal 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>
|
||||
21
devtools/server/tests/browser/doc_innerHTML.html
Normal file
21
devtools/server/tests/browser/doc_innerHTML.html
Normal 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>
|
||||
25
devtools/server/tests/browser/doc_perf.html
Normal file
25
devtools/server/tests/browser/doc_perf.html
Normal 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>
|
||||
203
devtools/server/tests/browser/head.js
Normal file
203
devtools/server/tests/browser/head.js
Normal 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;
|
||||
}
|
||||
15
devtools/server/tests/browser/navigate-first.html
Normal file
15
devtools/server/tests/browser/navigate-first.html
Normal 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>
|
||||
9
devtools/server/tests/browser/navigate-second.html
Normal file
9
devtools/server/tests/browser/navigate-second.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
Second
|
||||
</body>
|
||||
</html>
|
||||
117
devtools/server/tests/browser/storage-dynamic-windows.html
Normal file
117
devtools/server/tests/browser/storage-dynamic-windows.html
Normal 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>
|
||||
85
devtools/server/tests/browser/storage-helpers.js
Normal file
85
devtools/server/tests/browser/storage-helpers.js
Normal 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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
123
devtools/server/tests/browser/storage-listings.html
Normal file
123
devtools/server/tests/browser/storage-listings.html
Normal 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>
|
||||
94
devtools/server/tests/browser/storage-secured-iframe.html
Normal file
94
devtools/server/tests/browser/storage-secured-iframe.html
Normal 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>
|
||||
26
devtools/server/tests/browser/storage-unsecured-iframe.html
Normal file
26
devtools/server/tests/browser/storage-unsecured-iframe.html
Normal 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>
|
||||
47
devtools/server/tests/browser/storage-updates.html
Normal file
47
devtools/server/tests/browser/storage-updates.html
Normal 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>
|
||||
|
|
@ -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>
|
||||
19
devtools/server/tests/browser/timeline-iframe-child.html
Normal file
19
devtools/server/tests/browser/timeline-iframe-child.html
Normal 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>
|
||||
11
devtools/server/tests/browser/timeline-iframe-parent.html
Normal file
11
devtools/server/tests/browser/timeline-iframe-parent.html
Normal 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>
|
||||
6
devtools/server/tests/mochitest/.eslintrc.js
Normal file
6
devtools/server/tests/mochitest/.eslintrc.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
// Extend from the common devtools mochitest eslintrc config.
|
||||
"extends": "../../../.eslintrc.xpcshell.js"
|
||||
};
|
||||
|
|
@ -0,0 +1 @@
|
|||
debugger;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<head>
|
||||
<!-- Static (not dynamically inserted) inline script. -->
|
||||
<script id='franz'>function franz() { debugger; }</script>
|
||||
|
||||
<!-- Static out-of-line script element. -->
|
||||
<script id='heinrich' src='Debugger.Source.prototype.element.js'></script>
|
||||
</head>
|
||||
|
||||
<!-- HTML requires some body element onfoo attributes to add handlers to the
|
||||
*window*, not the element --- but Debugger.Source.prototype.element should
|
||||
return the element. Here, that rule should apply to the body's 'onresize'
|
||||
handler. (For the reason for the 'cancelable' check, see the code that
|
||||
sends the event.) -->
|
||||
<body onresize='if (event.cancelable) debugger;'>
|
||||
<!-- Ordinary content element with event handler. -->
|
||||
<div id='heidi' onclick='heinrichFun();'>Heidi</div>
|
||||
</body>
|
||||
|
|
@ -0,0 +1 @@
|
|||
function heinrichFun() { franz(); }
|
||||
120
devtools/server/tests/mochitest/animation-data.html
Normal file
120
devtools/server/tests/mochitest/animation-data.html
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Animation Test Data</title>
|
||||
<style>
|
||||
.ball {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: #f06;
|
||||
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.still {
|
||||
top: 0;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.animated {
|
||||
top: 100px;
|
||||
left: 10px;
|
||||
|
||||
animation: simple-animation 2s infinite alternate;
|
||||
}
|
||||
|
||||
.multi {
|
||||
top: 200px;
|
||||
left: 10px;
|
||||
|
||||
animation: simple-animation 2s infinite alternate,
|
||||
other-animation 5s infinite alternate;
|
||||
}
|
||||
|
||||
.delayed {
|
||||
top: 300px;
|
||||
left: 10px;
|
||||
background: rebeccapurple;
|
||||
|
||||
animation: simple-animation 3s 60s 10;
|
||||
}
|
||||
|
||||
.multi-finite {
|
||||
top: 400px;
|
||||
left: 10px;
|
||||
background: yellow;
|
||||
|
||||
animation: simple-animation 3s,
|
||||
other-animation 4s;
|
||||
}
|
||||
|
||||
.short {
|
||||
top: 500px;
|
||||
left: 10px;
|
||||
background: red;
|
||||
|
||||
animation: simple-animation 2s;
|
||||
}
|
||||
|
||||
.long {
|
||||
top: 600px;
|
||||
left: 10px;
|
||||
background: blue;
|
||||
|
||||
animation: simple-animation 120s;
|
||||
}
|
||||
|
||||
.negative-delay {
|
||||
top: 700px;
|
||||
left: 10px;
|
||||
background: gray;
|
||||
|
||||
animation: simple-animation 15s -10s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.no-compositor {
|
||||
top: 0;
|
||||
right: 10px;
|
||||
background: gold;
|
||||
|
||||
animation: no-compositor 10s cubic-bezier(.57,-0.02,1,.31) forwards;
|
||||
}
|
||||
|
||||
@keyframes simple-animation {
|
||||
100% {
|
||||
transform: translateX(300px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes other-animation {
|
||||
100% {
|
||||
background: blue;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes no-compositor {
|
||||
100% {
|
||||
margin-right: 600px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
window.opener.postMessage('ready', '*');
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
</body>
|
||||
<div class="ball still"></div>
|
||||
<div class="ball animated"></div>
|
||||
<div class="ball multi"></div>
|
||||
<div class="ball delayed"></div>
|
||||
<div class="ball multi-finite"></div>
|
||||
<div class="ball short"></div>
|
||||
<div class="ball long"></div>
|
||||
<div class="ball negative-delay"></div>
|
||||
<div class="ball no-compositor"></div>
|
||||
</body>
|
||||
</html>
|
||||
103
devtools/server/tests/mochitest/chrome.ini
Normal file
103
devtools/server/tests/mochitest/chrome.ini
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
skip-if = os == 'android'
|
||||
support-files =
|
||||
animation-data.html
|
||||
Debugger.Source.prototype.element.js
|
||||
Debugger.Source.prototype.element-2.js
|
||||
Debugger.Source.prototype.element.html
|
||||
director-helpers.js
|
||||
hello-actor.js
|
||||
inspector_css-properties.html
|
||||
inspector_getImageData.html
|
||||
inspector-delay-image-response.sjs
|
||||
inspector-eyedropper.html
|
||||
inspector-helpers.js
|
||||
inspector-search-data.html
|
||||
inspector-styles-data.css
|
||||
inspector-styles-data.html
|
||||
inspector-traversal-data.html
|
||||
large-image.jpg
|
||||
memory-helpers.js
|
||||
nonchrome_unsafeDereference.html
|
||||
small-image.gif
|
||||
setup-in-child.js
|
||||
setup-in-parent.js
|
||||
|
||||
[test_animation_actor-lifetime.html]
|
||||
[test_connection-manager.html]
|
||||
[test_connectToChild.html]
|
||||
[test_css-logic.html]
|
||||
[test_css-logic-media-queries.html]
|
||||
[test_css-logic-specificity.html]
|
||||
[test_css-properties_01.html]
|
||||
[test_css-properties_02.html]
|
||||
[test_Debugger.Source.prototype.introductionScript.html]
|
||||
[test_Debugger.Source.prototype.introductionType.html]
|
||||
[test_Debugger.Source.prototype.element.html]
|
||||
[test_Debugger.Script.prototype.global.html]
|
||||
[test_device.html]
|
||||
[test_director.html]
|
||||
[test_director_connectToChild.html]
|
||||
[test_executeInGlobal-outerized_this.html]
|
||||
[test_framerate_01.html]
|
||||
[test_framerate_02.html]
|
||||
[test_framerate_03.html]
|
||||
[test_framerate_04.html]
|
||||
[test_framerate_05.html]
|
||||
[test_framerate_06.html]
|
||||
[test_getProcess.html]
|
||||
[test_inspector-anonymous.html]
|
||||
[test_inspector-changeattrs.html]
|
||||
[test_inspector-changevalue.html]
|
||||
[test_inspector-dead-nodes.html]
|
||||
[test_inspector-duplicate-node.html]
|
||||
[test_inspector_getImageData.html]
|
||||
[test_inspector_getImageDataFromURL.html]
|
||||
[test_inspector_getImageData-wait-for-load.html]
|
||||
[test_inspector_getNodeFromActor.html]
|
||||
[test_inspector-hide.html]
|
||||
[test_inspector-insert.html]
|
||||
[test_inspector-mutations-attr.html]
|
||||
[test_inspector-mutations-events.html]
|
||||
[test_inspector-mutations-childlist.html]
|
||||
[test_inspector-mutations-frameload.html]
|
||||
[test_inspector-mutations-value.html]
|
||||
[test_inspector-pick-color.html]
|
||||
[test_inspector-pseudoclass-lock.html]
|
||||
[test_inspector-release.html]
|
||||
[test_inspector-reload.html]
|
||||
[test_inspector-remove.html]
|
||||
[test_inspector-resize.html]
|
||||
[test_inspector-resolve-url.html]
|
||||
[test_inspector-retain.html]
|
||||
[test_inspector-search.html]
|
||||
[test_inspector-search-front.html]
|
||||
[test_inspector-scroll-into-view.html]
|
||||
[test_inspector-traversal.html]
|
||||
[test_makeGlobalObjectReference.html]
|
||||
[test_memory.html]
|
||||
[test_memory_allocations_01.html]
|
||||
[test_memory_allocations_02.html]
|
||||
[test_memory_allocations_03.html]
|
||||
[test_memory_allocations_04.html]
|
||||
[test_memory_allocations_05.html]
|
||||
[test_memory_allocations_06.html]
|
||||
[test_memory_allocations_07.html]
|
||||
[test_memory_attach_01.html]
|
||||
[test_memory_attach_02.html]
|
||||
[test_memory_census.html]
|
||||
[test_memory_gc_01.html]
|
||||
[test_memory_gc_events.html]
|
||||
[test_preference.html]
|
||||
[test_settings.html]
|
||||
[test_setupInParentChild.html]
|
||||
[test_styles-applied.html]
|
||||
[test_styles-computed.html]
|
||||
[test_styles-layout.html]
|
||||
[test_styles-matched.html]
|
||||
[test_styles-modify.html]
|
||||
[test_styles-svg.html]
|
||||
[test_unsafeDereference.html]
|
||||
[test_websocket-server.html]
|
||||
skip-if = false
|
||||
44
devtools/server/tests/mochitest/director-helpers.js
Normal file
44
devtools/server/tests/mochitest/director-helpers.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
var Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {DebuggerClient} = require("devtools/shared/client/main");
|
||||
const {DebuggerServer} = require("devtools/server/main");
|
||||
const Services = require("Services");
|
||||
|
||||
// Always log packets when running tests.
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
Services.prefs.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref("devtools.debugger.log");
|
||||
Services.prefs.clearUserPref("dom.mozBrowserFramesEnabled");
|
||||
});
|
||||
|
||||
const { DirectorRegistry } = require("devtools/server/actors/director-registry");
|
||||
const { DirectorRegistryFront } = require("devtools/shared/fronts/director-registry");
|
||||
|
||||
const { DirectorManagerFront } = require("devtools/shared/fronts/director-manager");
|
||||
|
||||
const { Task } = require("devtools/shared/task");
|
||||
|
||||
/** *********************************
|
||||
* director helpers functions
|
||||
**********************************/
|
||||
|
||||
function* newConnectedDebuggerClient(opts) {
|
||||
var transport = DebuggerServer.connectPipe();
|
||||
var client = new DebuggerClient(transport);
|
||||
|
||||
yield client.connect();
|
||||
|
||||
var root = yield client.listTabs();
|
||||
|
||||
return {
|
||||
client: client,
|
||||
root: root,
|
||||
transport: transport
|
||||
};
|
||||
}
|
||||
|
||||
function purgeInstalledDirectorScripts() {
|
||||
DirectorRegistry.clear();
|
||||
}
|
||||
26
devtools/server/tests/mochitest/hello-actor.js
Normal file
26
devtools/server/tests/mochitest/hello-actor.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
|
||||
const helloSpec = protocol.generateActorSpec({
|
||||
typeName: "helloActor",
|
||||
|
||||
methods: {
|
||||
count: {
|
||||
request: {},
|
||||
response: {count: protocol.RetVal("number")}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var HelloActor = protocol.ActorClassWithSpec(helloSpec, {
|
||||
initialize: function () {
|
||||
protocol.Actor.prototype.initialize.apply(this, arguments);
|
||||
this.counter = 0;
|
||||
},
|
||||
|
||||
count: function () {
|
||||
return ++this.counter;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Adapted from https://dxr.mozilla.org/mozilla-central/rev/
|
||||
* 4e883591bb5dff021c108d3e30198a99547eed1e/layout/reftests/backgrounds/
|
||||
* delay-image-response.sjs
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
// A 1x1 PNG image.
|
||||
// Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
|
||||
const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
|
||||
"ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
|
||||
|
||||
// To avoid GC.
|
||||
let timer = null;
|
||||
|
||||
function handleRequest(request, response) {
|
||||
let query = {};
|
||||
request.queryString.split("&").forEach(function(val) {
|
||||
let [name, value] = val.split("=");
|
||||
query[name] = unescape(value);
|
||||
});
|
||||
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "image/png", false);
|
||||
|
||||
// If there is no delay, we write the image and leave.
|
||||
if (!("delay" in query)) {
|
||||
response.write(IMAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is a delay, we create a timer which, when it fires, will write
|
||||
// image and leave.
|
||||
response.processAsync();
|
||||
const nsITimer = Components.interfaces.nsITimer;
|
||||
|
||||
timer = Components.classes["@mozilla.org/timer;1"].createInstance(nsITimer);
|
||||
timer.initWithCallback(function() {
|
||||
response.write(IMAGE);
|
||||
response.finish();
|
||||
}, query.delay, nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
18
devtools/server/tests/mochitest/inspector-eyedropper.html
Normal file
18
devtools/server/tests/mochitest/inspector-eyedropper.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Inspector Eyedropper tests</title>
|
||||
<style>
|
||||
html {
|
||||
background: black;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
window.opener.postMessage('ready', '*');
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
310
devtools/server/tests/mochitest/inspector-helpers.js
Normal file
310
devtools/server/tests/mochitest/inspector-helpers.js
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
var Cu = Components.utils;
|
||||
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {DebuggerClient} = require("devtools/shared/client/main");
|
||||
const {DebuggerServer} = require("devtools/server/main");
|
||||
const { Task } = require("devtools/shared/task");
|
||||
|
||||
const Services = require("Services");
|
||||
const promise = require("promise");
|
||||
const {_documentWalker} = require("devtools/server/actors/inspector");
|
||||
|
||||
// Always log packets when running tests.
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref("devtools.debugger.log");
|
||||
});
|
||||
|
||||
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
DebuggerServer.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
var gAttachCleanups = [];
|
||||
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
for (let cleanup of gAttachCleanups) {
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Open a tab, load the url, wait for it to signal its readiness,
|
||||
* find the tab with the debugger server, and call the callback.
|
||||
*
|
||||
* Returns a function which can be called to close the opened ta
|
||||
* and disconnect its debugger client.
|
||||
*/
|
||||
function attachURL(url, callback) {
|
||||
var win = window.open(url, "_blank");
|
||||
var client = null;
|
||||
|
||||
let cleanup = () => {
|
||||
if (client) {
|
||||
client.close();
|
||||
client = null;
|
||||
}
|
||||
if (win) {
|
||||
win.close();
|
||||
win = null;
|
||||
}
|
||||
};
|
||||
gAttachCleanups.push(cleanup);
|
||||
|
||||
window.addEventListener("message", function loadListener(event) {
|
||||
if (event.data === "ready") {
|
||||
client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
client.connect().then(([applicationType, traits]) => {
|
||||
client.listTabs(response => {
|
||||
for (let tab of response.tabs) {
|
||||
if (tab.url === url) {
|
||||
window.removeEventListener("message", loadListener, false);
|
||||
client.attachTab(tab.actor, function (aResponse, aTabClient) {
|
||||
try {
|
||||
callback(null, client, tab, win.document);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
dump(ex);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
|
||||
return cleanup;
|
||||
}
|
||||
|
||||
function promiseOnce(target, event) {
|
||||
let deferred = promise.defer();
|
||||
target.on(event, (...args) => {
|
||||
if (args.length === 1) {
|
||||
deferred.resolve(args[0]);
|
||||
} else {
|
||||
deferred.resolve(args);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function sortOwnershipChildren(children) {
|
||||
return children.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
function serverOwnershipSubtree(walker, node) {
|
||||
let actor = walker._refMap.get(node);
|
||||
if (!actor) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let children = [];
|
||||
let docwalker = new _documentWalker(node, window);
|
||||
let child = docwalker.firstChild();
|
||||
while (child) {
|
||||
let item = serverOwnershipSubtree(walker, child);
|
||||
if (item) {
|
||||
children.push(item);
|
||||
}
|
||||
child = docwalker.nextSibling();
|
||||
}
|
||||
return {
|
||||
name: actor.actorID,
|
||||
children: sortOwnershipChildren(children)
|
||||
};
|
||||
}
|
||||
|
||||
function serverOwnershipTree(walker) {
|
||||
let serverWalker = DebuggerServer._searchAllConnectionsForActor(walker.actorID);
|
||||
|
||||
return {
|
||||
root: serverOwnershipSubtree(serverWalker, serverWalker.rootDoc),
|
||||
orphaned: [...serverWalker._orphaned].map(o => serverOwnershipSubtree(serverWalker, o.rawNode)),
|
||||
retained: [...serverWalker._retainedOrphans].map(o => serverOwnershipSubtree(serverWalker, o.rawNode))
|
||||
};
|
||||
}
|
||||
|
||||
function clientOwnershipSubtree(node) {
|
||||
return {
|
||||
name: node.actorID,
|
||||
children: sortOwnershipChildren(node.treeChildren().map(child => clientOwnershipSubtree(child)))
|
||||
};
|
||||
}
|
||||
|
||||
function clientOwnershipTree(walker) {
|
||||
return {
|
||||
root: clientOwnershipSubtree(walker.rootNode),
|
||||
orphaned: [...walker._orphaned].map(o => clientOwnershipSubtree(o)),
|
||||
retained: [...walker._retainedOrphans].map(o => clientOwnershipSubtree(o))
|
||||
};
|
||||
}
|
||||
|
||||
function ownershipTreeSize(tree) {
|
||||
let size = 1;
|
||||
for (let child of tree.children) {
|
||||
size += ownershipTreeSize(child);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
function assertOwnershipTrees(walker) {
|
||||
let serverTree = serverOwnershipTree(walker);
|
||||
let clientTree = clientOwnershipTree(walker);
|
||||
is(JSON.stringify(clientTree, null, " "), JSON.stringify(serverTree, null, " "), "Server and client ownership trees should match.");
|
||||
|
||||
return ownershipTreeSize(clientTree.root);
|
||||
}
|
||||
|
||||
// Verify that an actorID is inaccessible both from the client library and the server.
|
||||
function checkMissing(client, actorID) {
|
||||
let deferred = promise.defer();
|
||||
let front = client.getActor(actorID);
|
||||
ok(!front, "Front shouldn't be accessible from the client for actorID: " + actorID);
|
||||
|
||||
deferred = promise.defer();
|
||||
client.request({
|
||||
to: actorID,
|
||||
type: "request",
|
||||
}, response => {
|
||||
is(response.error, "noSuchActor", "node list actor should no longer be contactable.");
|
||||
deferred.resolve(undefined);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Verify that an actorID is accessible both from the client library and the server.
|
||||
function checkAvailable(client, actorID) {
|
||||
let deferred = promise.defer();
|
||||
let front = client.getActor(actorID);
|
||||
ok(front, "Front should be accessible from the client for actorID: " + actorID);
|
||||
|
||||
deferred = promise.defer();
|
||||
client.request({
|
||||
to: actorID,
|
||||
type: "garbageAvailableTest",
|
||||
}, response => {
|
||||
is(response.error, "unrecognizedPacketType", "node list actor should be contactable.");
|
||||
deferred.resolve(undefined);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseDone(promise) {
|
||||
promise.then(null, err => {
|
||||
ok(false, "Promise failed: " + err);
|
||||
if (err.stack) {
|
||||
dump(err.stack);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
// Mutation list testing
|
||||
|
||||
function isSrcChange(change) {
|
||||
return (change.type === "attributes" && change.attributeName === "src");
|
||||
}
|
||||
|
||||
function assertAndStrip(mutations, message, test) {
|
||||
let size = mutations.length;
|
||||
mutations = mutations.filter(test);
|
||||
ok((mutations.size != size), message);
|
||||
return mutations;
|
||||
}
|
||||
|
||||
function isSrcChange(change) {
|
||||
return change.type === "attributes" && change.attributeName === "src";
|
||||
}
|
||||
|
||||
function isUnload(change) {
|
||||
return change.type === "documentUnload";
|
||||
}
|
||||
|
||||
function isFrameLoad(change) {
|
||||
return change.type === "frameLoad";
|
||||
}
|
||||
|
||||
function isUnretained(change) {
|
||||
return change.type === "unretained";
|
||||
}
|
||||
|
||||
function isChildList(change) {
|
||||
return change.type === "childList";
|
||||
}
|
||||
|
||||
function isNewRoot(change) {
|
||||
return change.type === "newRoot";
|
||||
}
|
||||
|
||||
// Make sure an iframe's src attribute changed and then
|
||||
// strip that mutation out of the list.
|
||||
function assertSrcChange(mutations) {
|
||||
return assertAndStrip(mutations, "Should have had an iframe source change.", isSrcChange);
|
||||
}
|
||||
|
||||
// Make sure there's an unload in the mutation list and strip
|
||||
// that mutation out of the list
|
||||
function assertUnload(mutations) {
|
||||
return assertAndStrip(mutations, "Should have had a document unload change.", isUnload);
|
||||
}
|
||||
|
||||
// Make sure there's a frame load in the mutation list and strip
|
||||
// that mutation out of the list
|
||||
function assertFrameLoad(mutations) {
|
||||
return assertAndStrip(mutations, "Should have had a frame load change.", isFrameLoad);
|
||||
}
|
||||
|
||||
// Make sure there's a childList change in the mutation list and strip
|
||||
// that mutation out of the list
|
||||
function assertChildList(mutations) {
|
||||
return assertAndStrip(mutations, "Should have had a frame load change.", isChildList);
|
||||
}
|
||||
|
||||
// Load mutations aren't predictable, so keep accumulating mutations until
|
||||
// the one we're looking for shows up.
|
||||
function waitForMutation(walker, test, mutations = []) {
|
||||
let deferred = promise.defer();
|
||||
for (let change of mutations) {
|
||||
if (test(change)) {
|
||||
deferred.resolve(mutations);
|
||||
}
|
||||
}
|
||||
|
||||
walker.once("mutations", newMutations => {
|
||||
waitForMutation(walker, test, mutations.concat(newMutations)).then(finalMutations => {
|
||||
deferred.resolve(finalMutations);
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
||||
var _tests = [];
|
||||
function addTest(test) {
|
||||
_tests.push(test);
|
||||
}
|
||||
|
||||
function addAsyncTest(generator) {
|
||||
_tests.push(() => Task.spawn(generator).then(null, ok.bind(null, false)));
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
if (_tests.length == 0) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
var fn = _tests.shift();
|
||||
try {
|
||||
fn();
|
||||
} catch (ex) {
|
||||
info("Test function " + (fn.name ? "'" + fn.name + "' " : "") +
|
||||
"threw an exception: " + ex);
|
||||
}
|
||||
}
|
||||
52
devtools/server/tests/mochitest/inspector-search-data.html
Normal file
52
devtools/server/tests/mochitest/inspector-search-data.html
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Inspector Search Test Data</title>
|
||||
<style>
|
||||
#pseudo {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
#pseudo:before {
|
||||
content: "before element";
|
||||
}
|
||||
#pseudo:after {
|
||||
content: "after element";
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
window.opener.postMessage('ready', '*');
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
</body>
|
||||
<!-- A comment
|
||||
spread across multiple lines -->
|
||||
|
||||
<img width="100" height="100" src="large-image.jpg" />
|
||||
|
||||
<h1 id="pseudo">Heading 1</h1>
|
||||
<p>A p tag with the text 'h1' inside of it.
|
||||
<strong>A strong h1 result</strong>
|
||||
</p>
|
||||
|
||||
<div id="arrows" northwest="↖" northeast="↗" southeast="↘" southwest="↙">
|
||||
Unicode arrows
|
||||
</div>
|
||||
|
||||
<h2>Heading 2</h2>
|
||||
<h2>Heading 2</h2>
|
||||
<h2>Heading 2</h2>
|
||||
|
||||
<h3>Heading 3</h3>
|
||||
<h3>Heading 3</h3>
|
||||
<h3>Heading 3</h3>
|
||||
|
||||
<h4>Heading 4</h4>
|
||||
<h4>Heading 4</h4>
|
||||
<h4>Heading 4</h4>
|
||||
|
||||
<div class="💩" id="💩" 💩="💩"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.external-rule {
|
||||
cursor: crosshair;
|
||||
}
|
||||
81
devtools/server/tests/mochitest/inspector-styles-data.html
Normal file
81
devtools/server/tests/mochitest/inspector-styles-data.html
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<html>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
window.opener.postMessage('ready', '*')
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.inheritable-rule {
|
||||
font-size: 15px;
|
||||
}
|
||||
.uninheritable-rule {
|
||||
background-color: #f06;
|
||||
}
|
||||
@media screen {
|
||||
#mediaqueried {
|
||||
background-color: #f06;
|
||||
}
|
||||
}
|
||||
#svgcontent rect {
|
||||
fill: rgb(1,2,3);
|
||||
}
|
||||
|
||||
#layout-element,
|
||||
#layout-auto-margin-element {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
padding: 3px 5px 7px 5px;
|
||||
border: 5px solid red;
|
||||
margin: 10px 20px 30px 0;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#layout-auto-margin-element {
|
||||
margin: 10px auto;
|
||||
}
|
||||
</style>
|
||||
<link type="text/css" rel="stylesheet" href="inspector-styles-data.css"></link>
|
||||
<body>
|
||||
<h1>Style Actor Tests</h1>
|
||||
<!-- Inheritance checks -->
|
||||
<div id="inheritable-rule-uninheritable-style" class="inheritable-rule" style="background-color: purple">
|
||||
<div id="inheritable-rule-inheritable-style" class="inheritable-rule" style="color: blue">
|
||||
<div id="uninheritable-rule-uninheritable-style" class="uninheritable-rule" style="background-color: green">
|
||||
<div id="uninheritable-rule-inheritable-style" class="uninheritable-rule" style="color: red">
|
||||
<div id="test-node">
|
||||
Here is the test node.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Computed checks -->
|
||||
<div id="computed-parent" class="external-rule inheritable-rule uninheritable-rule" style="color: red;">
|
||||
<div id="computed-test-node" class="external-rule">
|
||||
Here is the test node.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Matched checks -->
|
||||
<div id="matched-parent" class="external-rule inheritable-rule uninheritable-rule" style="color: red;">
|
||||
<div id="matched-test-node" style="font-size: 10px" class="external-rule">
|
||||
Here is the test node.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mediaqueried">
|
||||
Screen mediaqueried.
|
||||
</div>
|
||||
|
||||
<div id="svgcontent">
|
||||
<svg><rect></rect></svg>
|
||||
</div>
|
||||
|
||||
<div id="layout-element">I can has layout</div>
|
||||
<div id="layout-auto-margin-element">I can has layout too</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Inspector Traversal Test Data</title>
|
||||
<style type="text/css">
|
||||
#pseudo::before {
|
||||
content: "before";
|
||||
}
|
||||
#pseudo::after {
|
||||
content: "after";
|
||||
}
|
||||
#pseudo-empty::before {
|
||||
content: "before an empty element";
|
||||
}
|
||||
#shadow::before {
|
||||
content: "Testing ::before on a shadow host";
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
|
||||
// Set up a basic shadow DOM
|
||||
var host = document.querySelector('#shadow');
|
||||
if (host.createShadowRoot) {
|
||||
var root = host.createShadowRoot();
|
||||
root.innerHTML = '<h3>Shadow <em>DOM</em></h3><select multiple></select>';
|
||||
}
|
||||
|
||||
// Put a copy of the body in an iframe to test frame traversal.
|
||||
var body = document.querySelector("body");
|
||||
var data = "data:text/html,<html>" + body.outerHTML + "<html>";
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("id", "childFrame");
|
||||
iframe.onload = function() {
|
||||
window.opener.postMessage('ready', '*')
|
||||
};
|
||||
iframe.src = data;
|
||||
body.appendChild(iframe);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="background-color:white">
|
||||
<h1>Inspector Actor Tests</h1>
|
||||
<span id="longstring">longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong</span>
|
||||
<span id="shortstring">short</span>
|
||||
<span id="empty"></span>
|
||||
<div id="longlist" data-test="exists">
|
||||
<div id="a">a</div>
|
||||
<div id="b">b</div>
|
||||
<div id="c">c</div>
|
||||
<div id="d">d</div>
|
||||
<div id="e">e</div>
|
||||
<div id="f">f</div>
|
||||
<div id="g">g</div>
|
||||
<div id="h">h</div>
|
||||
<div id="i">i</div>
|
||||
<div id="j">j</div>
|
||||
<div id="k">k</div>
|
||||
<div id="l">l</div>
|
||||
<div id="m">m</div>
|
||||
<div id="n">n</div>
|
||||
<div id="o">o</div>
|
||||
<div id="p">p</div>
|
||||
<div id="q">q</div>
|
||||
<div id="r">r</div>
|
||||
<div id="s">s</div>
|
||||
<div id="t">t</div>
|
||||
<div id="u">u</div>
|
||||
<div id="v">v</div>
|
||||
<div id="w">w</div>
|
||||
<div id="x">x</div>
|
||||
<div id="y">y</div>
|
||||
<div id="z">z</div>
|
||||
</div>
|
||||
<div id="longlist-sibling">
|
||||
<div id="longlist-sibling-firstchild"></div>
|
||||
</div>
|
||||
<p id="edit-html"></p>
|
||||
|
||||
<select multiple><option>one</option><option>two</option></select>
|
||||
<div id="pseudo"><span>middle</span></div>
|
||||
<div id="pseudo-empty"></div>
|
||||
<div id="shadow">light dom</div>
|
||||
<object>
|
||||
<div id="1"></div>
|
||||
</object>
|
||||
<div class="node-to-duplicate"></div>
|
||||
<div id="scroll-into-view" style="margin-top: 1000px;">scroll</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
window.opener.postMessage('ready', '*');
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
21
devtools/server/tests/mochitest/inspector_getImageData.html
Normal file
21
devtools/server/tests/mochitest/inspector_getImageData.html
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<html>
|
||||
<head>
|
||||
<body>
|
||||
<img class="custom">
|
||||
<img class="big-horizontal" src="large-image.jpg" style="width:500px;">
|
||||
<canvas class="big-vertical" style="width:500px;"></canvas>
|
||||
<img class="small" src="small-image.gif">
|
||||
<img class="data" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAJklEQVRIie3NMREAAAgAoe9fWls4eAzMVM0xoVAoFAqFQqFQ+C9chp4NHvu+4Q4AAAAASUVORK5CYII=">
|
||||
<script>
|
||||
window.onload = () => {
|
||||
var canvas = document.querySelector("canvas"), ctx = canvas.getContext("2d");
|
||||
canvas.width = 1000;
|
||||
canvas.height = 2000;
|
||||
ctx.fillStyle = "red";
|
||||
ctx.fillRect(0, 0, 1000, 2000);
|
||||
|
||||
window.opener.postMessage('ready', '*')
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
devtools/server/tests/mochitest/large-image.jpg
Normal file
BIN
devtools/server/tests/mochitest/large-image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 775 KiB |
52
devtools/server/tests/mochitest/memory-helpers.js
Normal file
52
devtools/server/tests/mochitest/memory-helpers.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
var Cu = Components.utils;
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const { Task } = require("devtools/shared/task");
|
||||
var Services = require("Services");
|
||||
var { DebuggerClient } = require("devtools/shared/client/main");
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
|
||||
var { MemoryFront } = require("devtools/shared/fronts/memory");
|
||||
|
||||
// Always log packets when running tests.
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref("devtools.debugger.log");
|
||||
});
|
||||
|
||||
function startServerAndGetSelectedTabMemory() {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
var client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
|
||||
return client.connect()
|
||||
.then(() => client.listTabs())
|
||||
.then(response => {
|
||||
var form = response.tabs[response.selected];
|
||||
var memory = MemoryFront(client, form, response);
|
||||
|
||||
return { memory, client };
|
||||
});
|
||||
}
|
||||
|
||||
function destroyServerAndFinish(client) {
|
||||
client.close().then(() => {
|
||||
DebuggerServer.destroy();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
function waitForTime(ms) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
function waitUntil(predicate) {
|
||||
if (predicate()) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return new Promise(resolve => setTimeout(() => waitUntil(predicate).then(() => resolve(true)), 10));
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<script>
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.timeout = 1742;
|
||||
xhr.expando = 'Expando!';
|
||||
</script>
|
||||
</html>
|
||||
20
devtools/server/tests/mochitest/setup-in-child.js
Normal file
20
devtools/server/tests/mochitest/setup-in-child.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const {Cc, Ci} = require("chrome");
|
||||
const cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"].
|
||||
getService(Ci.nsIMessageListenerManager);
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
|
||||
exports.setupChild = function (a, b, c) {
|
||||
cpmm.sendAsyncMessage("test:setupChild", [a, b, c]);
|
||||
};
|
||||
|
||||
exports.callParent = function () {
|
||||
// Hack! Fetch DebuggerServerConnection objects directly within DebuggerServer guts.
|
||||
for (let id in DebuggerServer._connections) {
|
||||
let conn = DebuggerServer._connections[id];
|
||||
conn.setupInParent({
|
||||
module: "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/setup-in-parent.js",
|
||||
setupParent: "setupParent",
|
||||
args: [{one: true}, 2, "three"]
|
||||
});
|
||||
}
|
||||
};
|
||||
10
devtools/server/tests/mochitest/setup-in-parent.js
Normal file
10
devtools/server/tests/mochitest/setup-in-parent.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
var {Ci} = require("chrome");
|
||||
var Services = require("Services");
|
||||
|
||||
exports.setupParent = function ({mm, prefix}) {
|
||||
let args = [
|
||||
!!mm.QueryInterface(Ci.nsIMessageSender),
|
||||
prefix
|
||||
];
|
||||
Services.obs.notifyObservers(null, "test:setupParent", JSON.stringify(args));
|
||||
};
|
||||
BIN
devtools/server/tests/mochitest/small-image.gif
Normal file
BIN
devtools/server/tests/mochitest/small-image.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 499 KiB |
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=958646
|
||||
|
||||
Debugger.Script.prototype.global should return innerize globals, not WindowProxies.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Debugger.Script.prototype.global should return inner windows</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script>
|
||||
|
||||
Components.utils.import("resource://gre/modules/jsdebugger.jsm");
|
||||
addDebuggerToGlobal(this);
|
||||
|
||||
window.onload = function () {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = "data:text/html,<script>function glorp() { }<\/script>";
|
||||
iframe.onload = firstOnLoadHandler;
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
function firstOnLoadHandler() {
|
||||
var dbg = new Debugger;
|
||||
var iframeDO = dbg.addDebuggee(iframe.contentWindow);
|
||||
|
||||
// For sanity: check that the debuggee global is the inner window,
|
||||
// and that the outer window gets a distinct D.O.
|
||||
var iframeWindowProxyDO = iframeDO.makeDebuggeeValue(iframe.contentWindow);
|
||||
ok(iframeDO !== iframeWindowProxyDO);
|
||||
|
||||
// The real test: Debugger.Script.prototype.global returns inner windows.
|
||||
ok(iframeDO.getOwnPropertyDescriptor('glorp').value.script.global === iframeDO);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=941876
|
||||
|
||||
Debugger.Source.prototype.element and .elementAttributeName should report the DOM
|
||||
element to which code is attached (if any), and how.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Debugger.Source.prototype.element should return owning element</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script>
|
||||
|
||||
Components.utils.import("resource://gre/modules/jsdebugger.jsm");
|
||||
addDebuggerToGlobal(this);
|
||||
|
||||
window.onload = function () {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var log = '';
|
||||
var doc, dieter, ulrich, isolde, albrecht;
|
||||
var dbg, iframeDO, DOFor;
|
||||
|
||||
// Create an iframe to debug.
|
||||
// We can't use a data: URL here, because we want to test script elements
|
||||
// that refer to the JavaScript via 'src' attributes, and data: documents
|
||||
// can't refer to those. So we use a separate HTML document.
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = "Debugger.Source.prototype.element.html";
|
||||
iframe.onload = onLoadHandler;
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
function onLoadHandler() {
|
||||
log += 'l';
|
||||
|
||||
// Now that the iframe's window has been created, we can add
|
||||
// it as a debuggee.
|
||||
dbg = new Debugger;
|
||||
dbg.onDebuggerStatement = franzDebuggerHandler;
|
||||
iframeDO = dbg.addDebuggee(iframe.contentWindow);
|
||||
DOFor = iframeDO.makeDebuggeeValue.bind(iframeDO);
|
||||
|
||||
// Send a click event to heidi.
|
||||
doc = iframe.contentWindow.document;
|
||||
doc.getElementById('heidi').dispatchEvent(new Event('click'));
|
||||
}
|
||||
|
||||
function franzDebuggerHandler(frame) {
|
||||
log += 'f';
|
||||
|
||||
// The top stack frame should be franz, belonging to the script element.
|
||||
ok(frame.callee.displayName === 'franz', 'top frame is franz');
|
||||
ok(frame.script.source.element === DOFor(doc.getElementById('franz')),
|
||||
'top frame source belongs to element franz');
|
||||
ok(frame.script.source.elementAttributeName === undefined,
|
||||
"top frame source doesn't belong to an attribute");
|
||||
|
||||
// The second stack frame should belong to heinrich.
|
||||
ok(frame.older.script.source.element === DOFor(doc.getElementById('heinrich')),
|
||||
"second frame source belongs to element heinrich");
|
||||
ok(frame.older.script.source.elementAttributeName === undefined,
|
||||
"second frame source doesn't belong to an attribute");
|
||||
|
||||
// The next stack frame should belong to heidi's onclick handler.
|
||||
ok(frame.older.older.script.source.element === DOFor(doc.getElementById('heidi')),
|
||||
'third frame source belongs to element heidi');
|
||||
ok(frame.older.older.script.source.elementAttributeName === 'onclick',
|
||||
"third frame source belongs to 'onclick' attribute");
|
||||
|
||||
// Try a dynamically inserted inline script element.
|
||||
ulrich = doc.createElement('script');
|
||||
ulrich.text = 'debugger;'
|
||||
dbg.onDebuggerStatement = ulrichDebuggerHandler;
|
||||
doc.body.appendChild(ulrich);
|
||||
}
|
||||
|
||||
function ulrichDebuggerHandler(frame) {
|
||||
log += 'u';
|
||||
|
||||
// The top frame should be ulrich's text.
|
||||
ok(frame.script.source.element === DOFor(ulrich),
|
||||
"top frame belongs to ulrich");
|
||||
ok(frame.script.source.elementAttributeName === undefined,
|
||||
"top frame is not on an attribute of ulrich");
|
||||
|
||||
// Try a dynamically inserted out-of-line script element.
|
||||
isolde = doc.createElement('script');
|
||||
isolde.setAttribute('src', 'Debugger.Source.prototype.element-2.js');
|
||||
isolde.setAttribute('id', 'idolde, my dear');
|
||||
dbg.onDebuggerStatement = isoldeDebuggerHandler;
|
||||
doc.body.appendChild(isolde);
|
||||
}
|
||||
|
||||
function isoldeDebuggerHandler(frame) {
|
||||
log += 'i';
|
||||
|
||||
// The top frame should belong to isolde.
|
||||
ok(frame.script.source.element === DOFor(isolde),
|
||||
"top frame belongs to isolde");
|
||||
info("frame.script.source.element is: " + uneval(frame.script.source.element));
|
||||
if (typeof frame.script.source.element.unsafeDereference() == 'object') {
|
||||
info(" toString: " + frame.script.source.element.unsafeDereference());
|
||||
info(" id: " + frame.script.source.element.unsafeDereference().id);
|
||||
}
|
||||
|
||||
ok(frame.script.source.elementAttributeName === undefined,
|
||||
"top frame source is not an attribute of isolde");
|
||||
info("frame.script.source.elementAttributeName is: " +
|
||||
uneval(frame.script.source.elementAttributeName));
|
||||
|
||||
// Try a dynamically created div element with a handler.
|
||||
dieter = doc.createElement('div');
|
||||
dieter.setAttribute('id', 'dieter');
|
||||
dieter.setAttribute('ondrag', 'debugger;');
|
||||
dbg.onDebuggerStatement = dieterDebuggerHandler;
|
||||
dieter.dispatchEvent(new Event('drag'));
|
||||
}
|
||||
|
||||
function dieterDebuggerHandler(frame) {
|
||||
log += 'd';
|
||||
|
||||
// The top frame should belong to dieter's ondrag handler.
|
||||
ok(frame.script.source.element === DOFor(dieter),
|
||||
"second event's handler belongs to dieter");
|
||||
ok(frame.script.source.elementAttributeName === 'ondrag',
|
||||
"second event's handler is on dieter's 'ondrag' element");
|
||||
|
||||
// Try sending an 'onresize' event to the window.
|
||||
//
|
||||
// Note that we only want Debugger to see the events we send, not any
|
||||
// genuine resize events accidentally generated by the test harness (see bug
|
||||
// 1162067). So we mark our events as cancelable; that seems to be the only
|
||||
// bit chrome can fiddle on an Event that content code will see and that
|
||||
// won't affect propagation. Then, the content event only runs its
|
||||
// 'debugger' statement when the event is cancelable. It's a kludge.
|
||||
dbg.onDebuggerStatement = resizeDebuggerHandler;
|
||||
iframe.contentWindow.dispatchEvent(new Event('resize', { cancelable: true }));
|
||||
}
|
||||
|
||||
function resizeDebuggerHandler(frame) {
|
||||
log += 'e';
|
||||
|
||||
// The top frame should belong to the body's 'onresize' handler, even
|
||||
// though we sent the message to the window and it was handled.
|
||||
ok(frame.script.source.element === DOFor(doc.body),
|
||||
"onresize event handler belongs to body element");
|
||||
ok(frame.script.source.elementAttributeName === 'onresize',
|
||||
"onresize event handler is on body element's 'onresize' attribute");
|
||||
|
||||
// In SVG, the event and the attribute that holds that event's handler
|
||||
// have different names. Debugger.Source.prototype.elementAttributeName
|
||||
// should report (as one might infer) the attribute name, not the event
|
||||
// name.
|
||||
albrecht = doc.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
albrecht.setAttribute('onload', 'debugger;');
|
||||
dbg.onDebuggerStatement = SVGLoadHandler;
|
||||
albrecht.dispatchEvent(new Event("SVGLoad"));
|
||||
}
|
||||
|
||||
function SVGLoadHandler(frame) {
|
||||
log += 's';
|
||||
|
||||
// The top frame's source should be on albrecht's 'onload' attribute.
|
||||
ok(frame.script.source.element === DOFor(albrecht),
|
||||
"SVGLoad event handler belongs to albrecht");
|
||||
ok(frame.script.source.elementAttributeName === 'onload',
|
||||
"SVGLoad event handler is on albrecht's 'onload' attribute");
|
||||
|
||||
ok(log === 'lfuides', "all tests actually ran");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue