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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue