This commit is contained in:
Ryan VanderMeulen 2015-07-15 13:25:33 -04:00
Родитель 56f7352392 a6e30d7d68
Коммит 2806c1d5bc
64 изменённых файлов: 631 добавлений и 4292 удалений

Просмотреть файл

@ -185,7 +185,6 @@ let DebuggerController = {
if (this._target.isTabActor) {
this.Workers.disconnect();
}
this.Tracer.disconnect();
this.disconnect();
this._shutdown = true;
@ -204,7 +203,7 @@ let DebuggerController = {
}
let target = this._target;
let { client, form: { chromeDebugger, traceActor, actor } } = target;
let { client, form: { chromeDebugger, actor } } = target;
target.on("close", this._onTabDetached);
target.on("navigate", this._onTabNavigated);
target.on("will-navigate", this._onTabNavigated);
@ -218,10 +217,6 @@ let DebuggerController = {
yield this._startChromeDebugging(chromeDebugger);
} else {
yield this._startDebuggingTab();
if (Prefs.tracerEnabled && traceActor) {
yield this._startTracingTab(traceActor);
}
}
this._hideUnsupportedFeatures();
@ -390,31 +385,6 @@ let DebuggerController = {
return deferred.promise;
},
/**
* Sets up an execution tracing session.
*
* @param object aTraceActor
* The remote protocol grip of the trace actor.
* @return object
* A promise resolved once the client attaches to the tracer.
*/
_startTracingTab: function(aTraceActor) {
let deferred = promise.defer();
this.client.attachTracer(aTraceActor, (response, traceClient) => {
if (!traceClient) {
deferred.reject(new Error("Failed to attach to tracing actor."));
return;
}
this.traceClient = traceClient;
this.Tracer.connect();
deferred.resolve();
});
return deferred.promise;
},
/**
* Detach and reattach to the thread actor with useSourceMaps true, blow
* away old sources and get them again.
@ -1301,7 +1271,6 @@ SourceScripts.prototype = {
// both in the editor and the breakpoints pane.
DebuggerController.Breakpoints.updatePaneBreakpoints();
DebuggerController.Breakpoints.updateEditorBreakpoints();
DebuggerController.HitCounts.updateEditorHitCounts();
// Make sure the events listeners are up to date.
if (DebuggerView.instrumentsPaneTab == "events-tab") {
@ -1354,7 +1323,6 @@ SourceScripts.prototype = {
// both in the editor and the breakpoints pane.
DebuggerController.Breakpoints.updatePaneBreakpoints();
DebuggerController.Breakpoints.updateEditorBreakpoints();
DebuggerController.HitCounts.updateEditorHitCounts();
// Signal that sources have been added.
window.emit(EVENTS.SOURCES_ADDED);
@ -1566,237 +1534,6 @@ SourceScripts.prototype = {
}
};
/**
* Tracer update the UI according to the messages exchanged with the tracer
* actor.
*/
function Tracer() {
this._trace = null;
this._idCounter = 0;
this.onTraces = this.onTraces.bind(this);
}
Tracer.prototype = {
get client() {
return DebuggerController.client;
},
get traceClient() {
return DebuggerController.traceClient;
},
get tracing() {
return !!this._trace;
},
/**
* Hooks up the debugger controller with the tracer client.
*/
connect: function() {
this._stack = [];
this.client.addListener("traces", this.onTraces);
},
/**
* Disconnects the debugger controller from the tracer client. Any further
* communcation with the tracer actor will not have any effect on the UI.
*/
disconnect: function() {
this._stack = null;
this.client.removeListener("traces", this.onTraces);
},
/**
* Instructs the tracer actor to start tracing.
*/
startTracing: function(aCallback = () => {}) {
if (this.tracing) {
return;
}
DebuggerView.Tracer.selectTab();
let id = this._trace = "dbg.trace" + Math.random();
let fields = [
"name",
"location",
"hitCount",
"parameterNames",
"depth",
"arguments",
"return",
"throw",
"yield"
];
this.traceClient.startTrace(fields, id, aResponse => {
const { error } = aResponse;
if (error) {
DevToolsUtils.reportException("Tracer.prototype.startTracing", error);
this._trace = null;
}
aCallback(aResponse);
});
},
/**
* Instructs the tracer actor to stop tracing.
*/
stopTracing: function(aCallback = () => {}) {
if (!this.tracing) {
return;
}
this.traceClient.stopTrace(this._trace, aResponse => {
const { error } = aResponse;
if (error) {
DevToolsUtils.reportException("Tracer.prototype.stopTracing", error);
}
this._trace = null;
DebuggerController.HitCounts.clear();
aCallback(aResponse);
});
},
onTraces: function (aEvent, { traces }) {
const tracesLength = traces.length;
let tracesToShow;
// Update hit counts.
for (let t of traces) {
if (t.type == "enteredFrame") {
DebuggerController.HitCounts.set(t.location, t.hitCount);
}
}
DebuggerController.HitCounts.updateEditorHitCounts();
// Limit number of traces to be shown in the log.
if (tracesLength > TracerView.MAX_TRACES) {
tracesToShow = traces.slice(tracesLength - TracerView.MAX_TRACES, tracesLength);
this._stack.splice(0, this._stack.length);
DebuggerView.Tracer.empty();
} else {
tracesToShow = traces;
}
// Show traces in the log.
for (let t of tracesToShow) {
if (t.type == "enteredFrame") {
this._onCall(t);
} else {
this._onReturn(t);
}
}
DebuggerView.Tracer.commit();
},
/**
* Callback for handling a new call frame.
*/
_onCall: function({ name, location, blackBoxed, parameterNames, depth, arguments: args }) {
const item = {
name: name,
location: location,
id: this._idCounter++,
blackBoxed
};
this._stack.push(item);
DebuggerView.Tracer.addTrace({
type: "call",
name: name,
location: location,
depth: depth,
parameterNames: parameterNames,
arguments: args,
frameId: item.id,
blackBoxed
});
},
/**
* Callback for handling an exited frame.
*/
_onReturn: function(aPacket) {
if (!this._stack.length) {
return;
}
const { name, id, location, blackBoxed } = this._stack.pop();
DebuggerView.Tracer.addTrace({
type: aPacket.why,
name: name,
location: location,
depth: aPacket.depth,
frameId: id,
returnVal: aPacket.return || aPacket.throw || aPacket.yield,
blackBoxed
});
},
/**
* Create an object which has the same interface as a normal object client,
* but since we already have all the information for an object that we will
* ever get (the server doesn't create actors when tracing, just firehoses
* data and forgets about it) just return the data immdiately.
*
* @param Object aObject
* The tracer object "grip" (more like a limited snapshot).
* @returns Object
* The synchronous client object.
*/
syncGripClient: function(aObject) {
return {
get isFrozen() { return aObject.frozen; },
get isSealed() { return aObject.sealed; },
get isExtensible() { return aObject.extensible; },
get ownProperties() { return aObject.ownProperties; },
get prototype() { return null; },
getParameterNames: callback => callback(aObject),
getPrototypeAndProperties: callback => callback(aObject),
getPrototype: callback => callback(aObject),
getOwnPropertyNames: (callback) => {
callback({
ownPropertyNames: aObject.ownProperties
? Object.keys(aObject.ownProperties)
: []
});
},
getProperty: (property, callback) => {
callback({
descriptor: aObject.ownProperties
? aObject.ownProperties[property]
: null
});
},
getDisplayString: callback => callback("[object " + aObject.class + "]"),
getScope: callback => callback({
error: "scopeNotAvailable",
message: "Cannot get scopes for traced objects"
})
};
},
/**
* Wraps object snapshots received from the tracer server so that we can
* differentiate them from long living object grips from the debugger server
* in the variables view.
*
* @param Object aObject
* The object snapshot from the tracer actor.
*/
WrappedObject: function(aObject) {
this.object = aObject;
}
};
/**
* Handles breaking on event listeners in the currently debugged target.
*/
@ -2167,10 +1904,10 @@ Breakpoints.prototype = {
}
}
// Preserve information about the breakpoint's line text, to display it
// in the sources pane without requiring fetching the source (for example,
// after the target navigated). Note that this will get out of sync
// if the source text contents change.
// Preserve information about the breakpoint's line text, to display it in
// the sources pane without requiring fetching the source (for example,
// after the target navigated). Note that this will get out of sync if the
// source text contents change.
let line = aBreakpointClient.location.line - 1;
aBreakpointClient.text = DebuggerView.editor.getText(line).trim();
@ -2427,87 +2164,6 @@ Object.defineProperty(Breakpoints.prototype, "_addedOrDisabled", {
}
});
/**
* Handles Tracer's hit counts.
*/
function HitCounts() {
/**
* Storage of hit counts for every location
* hitCount = _locations[url][line][column]
*/
this._hitCounts = Object.create(null);
}
HitCounts.prototype = {
set: function({url, line, column}, aHitCount) {
if (url) {
if (!this._hitCounts[url]) {
this._hitCounts[url] = Object.create(null);
}
if (!this._hitCounts[url][line]) {
this._hitCounts[url][line] = Object.create(null);
}
this._hitCounts[url][line][column] = aHitCount;
}
},
/**
* Update all the hit counts in the editor view. This is invoked when the
* selected script is changed, or when new sources are received via the
* _onNewSource and _onSourcesAdded event listeners.
*/
updateEditorHitCounts: function() {
// First, remove all hit counters.
DebuggerView.editor.removeAllMarkers("hit-counts");
// Then, add new hit counts, just for the current source.
for (let url in this._hitCounts) {
for (let line in this._hitCounts[url]) {
for (let column in this._hitCounts[url][line]) {
this._updateEditorHitCount({url, line, column});
}
}
}
},
/**
* Update a hit counter on a certain line.
*/
_updateEditorHitCount: function({url, line, column}) {
// Editor must be initialized.
if (!DebuggerView.editor) {
return;
}
// No need to do anything if the counter's source is not being shown in the
// editor.
if (url &&
DebuggerView.Sources.selectedItem.attachment.source.url != url) {
return;
}
// There might be more counters on the same line. We need to combine them
// into one.
let content = Object.keys(this._hitCounts[url][line])
.sort() // Sort by key (column).
.map(a => this._hitCounts[url][line][a]) // Extract values.
.map(a => a + "\u00D7") // Format hit count (e.g. 146×).
.join("|");
// CodeMirror's lines are indexed from 0, while traces start from 1
DebuggerView.editor.addContentMarker(line - 1, "hit-counts", "hit-count",
content);
},
/**
* Remove all hit couters and clear the storage
*/
clear: function() {
DebuggerView.editor.removeAllMarkers("hit-counts");
this._hitCounts = Object.create(null);
}
}
/**
* Localization convenience methods.
*/
@ -2528,7 +2184,6 @@ let Prefs = new ViewHelpers.Prefs("devtools", {
sourceMapsEnabled: ["Bool", "debugger.source-maps-enabled"],
prettyPrintEnabled: ["Bool", "debugger.pretty-print-enabled"],
autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
tracerEnabled: ["Bool", "debugger.tracer"],
workersEnabled: ["Bool", "debugger.workers"],
editorTabSize: ["Int", "editor.tabsize"],
autoBlackBox: ["Bool", "debugger.auto-black-box"]
@ -2550,8 +2205,6 @@ DebuggerController.StackFrames = new StackFrames();
DebuggerController.SourceScripts = new SourceScripts();
DebuggerController.Breakpoints = new Breakpoints();
DebuggerController.Breakpoints.DOM = new EventListeners();
DebuggerController.Tracer = new Tracer();
DebuggerController.HitCounts = new HitCounts();
/**
* Export some properties to the global scope for easier access.

Просмотреть файл

@ -58,7 +58,6 @@ let DebuggerView = {
this.Workers.initialize();
this.Sources.initialize();
this.VariableBubble.initialize();
this.Tracer.initialize();
this.WatchExpressions.initialize();
this.EventListeners.initialize();
this.GlobalSearch.initialize();
@ -91,7 +90,6 @@ let DebuggerView = {
this.StackFramesClassicList.destroy();
this.Sources.destroy();
this.VariableBubble.destroy();
this.Tracer.destroy();
this.WatchExpressions.destroy();
this.EventListeners.destroy();
this.GlobalSearch.destroy();
@ -174,9 +172,7 @@ let DebuggerView = {
VariablesViewController.attach(this.Variables, {
getEnvironmentClient: aObject => gThreadClient.environment(aObject),
getObjectClient: aObject => {
return aObject instanceof DebuggerController.Tracer.WrappedObject
? DebuggerController.Tracer.syncGripClient(aObject.object)
: gThreadClient.pauseGrip(aObject)
return gThreadClient.pauseGrip(aObject)
}
});
@ -219,9 +215,6 @@ let DebuggerView = {
}
let gutters = ["breakpoints"];
if (Services.prefs.getBoolPref("devtools.debugger.tracer")) {
gutters.unshift("hit-counts");
}
this.editor = new Editor({
mode: Editor.modes.text,
@ -414,7 +407,6 @@ let DebuggerView = {
// source.
DebuggerView.Sources.selectedValue = aSource.actor;
DebuggerController.Breakpoints.updateEditorBreakpoints();
DebuggerController.HitCounts.updateEditorHitCounts();
histogram.add(Date.now() - startTime);
@ -673,7 +665,6 @@ let DebuggerView = {
GlobalSearch: null,
StackFrames: null,
Sources: null,
Tracer: null,
Variables: null,
VariableBubble: null,
WatchExpressions: null,

Просмотреть файл

@ -31,7 +31,6 @@
<script type="text/javascript" src="debugger/workers-view.js"/>
<script type="text/javascript" src="debugger/sources-view.js"/>
<script type="text/javascript" src="debugger/variable-bubble-view.js"/>
<script type="text/javascript" src="debugger/tracer-view.js"/>
<script type="text/javascript" src="debugger/watch-expressions-view.js"/>
<script type="text/javascript" src="debugger/event-listeners-view.js"/>
<script type="text/javascript" src="debugger/global-search-view.js"/>
@ -280,13 +279,6 @@
class="devtools-toolbarbutton"
tabindex="0"/>
</hbox>
<hbox>
<toolbarbutton id="trace"
class="devtools-toolbarbutton"
command="toggleTracing"
tabindex="0"
hidden="true"/>
</hbox>
<vbox id="stackframes" flex="1"/>
<textbox id="searchbox"
class="devtools-searchinput" type="search"/>
@ -324,7 +316,6 @@
<tabs>
<tab id="sources-tab" label="&debuggerUI.tabs.sources;"/>
<tab id="callstack-tab" label="&debuggerUI.tabs.callstack;"/>
<tab id="tracer-tab" label="&debuggerUI.tabs.traces;" hidden="true"/>
</tabs>
<tabpanels flex="1">
<tabpanel id="sources-tabpanel">
@ -355,26 +346,6 @@
<tabpanel id="callstack-tabpanel">
<vbox id="callstack-list" flex="1"/>
</tabpanel>
<tabpanel id="tracer-tabpanel">
<vbox id="tracer-traces" flex="1"/>
<hbox class="trace-item-template" hidden="true">
<hbox class="trace-item" align="center" flex="1" crop="end">
<label class="trace-type plain"/>
<label class="trace-name plain" crop="end"/>
</hbox>
</hbox>
<toolbar id="tracer-toolbar" class="devtools-toolbar">
<toolbarbutton id="clear-tracer"
label="&debuggerUI.clearButton;"
tooltiptext="&debuggerUI.clearButton.tooltip;"
command="clearTraces"
class="devtools-toolbarbutton"/>
<textbox id="tracer-search"
class="devtools-searchinput"
flex="1"
type="search"/>
</toolbar>
</tabpanel>
</tabpanels>
</tabbox>
</vbox>

Просмотреть файл

@ -33,7 +33,6 @@ support-files =
code_script-switching-01.js
code_script-switching-02.js
code_test-editor-mode
code_tracing-01.js
code_ugly.js
code_ugly-2.js
code_ugly-3.js
@ -91,7 +90,6 @@ support-files =
doc_promise.html
doc_random-javascript.html
doc_recursion-stack.html
doc_same-line-functions.html
doc_scope-variable.html
doc_scope-variable-2.html
doc_scope-variable-3.html
@ -103,7 +101,6 @@ support-files =
doc_split-console-paused-reload.html
doc_step-out.html
doc_terminate-on-tab-close.html
doc_tracing-01.html
doc_watch-expressions.html
doc_watch-expression-button.html
doc_with-frame.html
@ -247,10 +244,6 @@ skip-if = e10s && debug
skip-if = e10s # TODO
[browser_dbg_hide-toolbar-buttons.js]
skip-if = e10s
[browser_dbg_hit-counts-01.js]
skip-if = e10s && debug
[browser_dbg_hit-counts-02.js]
skip-if = e10s && debug
[browser_dbg_host-layout.js]
skip-if = e10s && debug
[browser_dbg_iframes.js]
@ -353,7 +346,7 @@ skip-if = e10s && debug
[browser_dbg_promises-allocation-stack.js]
skip-if = e10s && debug
[browser_dbg_promises-chrome-allocation-stack.js]
skip-if = (e10s && debug) || os == "linux" # Bug 1177730
skip-if = (e10s && debug) || os == "linux" || os == "win" # Bug 1177730
[browser_dbg_reload-preferred-script-01.js]
skip-if = e10s && debug
[browser_dbg_reload-preferred-script-02.js]
@ -453,22 +446,6 @@ skip-if = e10s # TODO
skip-if = e10s # TODO
[browser_dbg_terminate-on-tab-close.js]
skip-if = e10s && debug
[browser_dbg_tracing-01.js]
skip-if = e10s && debug
[browser_dbg_tracing-02.js]
skip-if = e10s && debug
[browser_dbg_tracing-03.js]
skip-if = e10s && debug
[browser_dbg_tracing-04.js]
skip-if = e10s && debug
[browser_dbg_tracing-05.js]
skip-if = e10s && debug
[browser_dbg_tracing-06.js]
skip-if = e10s && debug
[browser_dbg_tracing-07.js]
skip-if = e10s && debug
[browser_dbg_tracing-08.js]
skip-if = e10s && debug
[browser_dbg_variables-view-01.js]
skip-if = e10s && debug
[browser_dbg_variables-view-02.js]

Просмотреть файл

@ -1,61 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Evaluating two functions on the same line and checking for correct hit count
* for both of them in CodeMirror's gutter.
*/
const TAB_URL = EXAMPLE_URL + "doc_same-line-functions.html";
const CODE_URL = "code_same-line-functions.js";
let gTab, gPanel, gDebugger;
let gEditor;
function test() {
Task.async(function* () {
yield pushPrefs(["devtools.debugger.tracer", true]);
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
Task.async(function* () {
yield waitForSourceShown(gPanel, CODE_URL);
yield startTracing(gPanel);
clickButton();
yield waitForClientEvents(aPanel, "traces");
testHitCounts();
yield stopTracing(gPanel);
yield popPrefs();
yield closeDebuggerAndFinish(gPanel);
})();
});
})().catch(e => {
ok(false, "Got an error: " + e.message + "\n" + e.stack);
});
}
function clickButton() {
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
}
function testHitCounts() {
let marker = gEditor.getMarker(0, 'hit-counts');
is(marker.innerHTML, "1\u00D7|1\u00D7",
"Both functions should be hit only once.");
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
gDebugger = null;
gEditor = null;
});

Просмотреть файл

@ -1,66 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* When tracing is stopped all hit counters should be cleared.
*/
const TAB_URL = EXAMPLE_URL + "doc_same-line-functions.html";
const CODE_URL = "code_same-line-functions.js";
let gTab, gPanel, gDebugger;
let gEditor;
function test() {
Task.async(function* () {
yield pushPrefs(["devtools.debugger.tracer", true]);
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gEditor = gDebugger.DebuggerView.editor;
Task.async(function* () {
yield waitForSourceShown(gPanel, CODE_URL);
yield startTracing(gPanel);
clickButton();
yield waitForClientEvents(aPanel, "traces");
testHitCountsBeforeStopping();
yield stopTracing(gPanel);
testHitCountsAfterStopping();
yield popPrefs();
yield closeDebuggerAndFinish(gPanel);
})();
});
})().catch(e => {
ok(false, "Got an error: " + e.message + "\n" + e.stack);
});
}
function clickButton() {
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
}
function testHitCountsBeforeStopping() {
let marker = gEditor.getMarker(0, 'hit-counts');
ok(marker, "A counter should exists.");
}
function testHitCountsAfterStopping() {
let marker = gEditor.getMarker(0, 'hit-counts');
is(marker, undefined, "A counter should be cleared.");
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
gDebugger = null;
gEditor = null;
});

Просмотреть файл

@ -1,105 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we get the expected frame enter/exit logs in the tracer view.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
let gTab, gPanel, gDebugger;
function test() {
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(() => startTracing(gPanel))
.then(clickButton)
.then(() => waitForClientEvents(aPanel, "traces"))
.then(testTraceLogs)
.then(() => stopTracing(gPanel))
.then(() => {
const deferred = promise.defer();
SpecialPowers.popPrefEnv(deferred.resolve);
return deferred.promise;
})
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
});
}
function clickButton() {
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
}
function testTraceLogs() {
const onclickLogs = filterTraces(gPanel,
t => t.querySelector(".trace-name[value=onclick]"));
is(onclickLogs.length, 2, "Should have two logs from 'onclick'");
ok(onclickLogs[0].querySelector(".trace-call"),
"The first 'onclick' log should be a call.");
ok(onclickLogs[1].querySelector(".trace-return"),
"The second 'onclick' log should be a return.");
for (let t of onclickLogs) {
ok(t.querySelector(".trace-item").getAttribute("tooltiptext")
.includes("doc_tracing-01.html"));
}
const nonOnclickLogs = filterTraces(gPanel,
t => !t.querySelector(".trace-name[value=onclick]"));
for (let t of nonOnclickLogs) {
ok(t.querySelector(".trace-item").getAttribute("tooltiptext")
.includes("code_tracing-01.js"));
}
const mainLogs = filterTraces(gPanel,
t => t.querySelector(".trace-name[value=main]"));
is(mainLogs.length, 2, "Should have an enter and an exit for 'main'");
ok(mainLogs[0].querySelector(".trace-call"),
"The first 'main' log should be a call.");
ok(mainLogs[1].querySelector(".trace-return"),
"The second 'main' log should be a return.");
const factorialLogs = filterTraces(gPanel,
t => t.querySelector(".trace-name[value=factorial]"));
is(factorialLogs.length, 10, "Should have 5 enter, and 5 exit frames for 'factorial'");
ok(factorialLogs.slice(0, 5).every(t => t.querySelector(".trace-call")),
"The first five 'factorial' logs should be calls.");
ok(factorialLogs.slice(5).every(t => t.querySelector(".trace-return")),
"The second five 'factorial' logs should be returns.")
// Test that the depth affects padding so that calls are indented properly.
let lastDepth = -Infinity;
for (let t of factorialLogs.slice(0, 5)) {
let depth = parseInt(t.querySelector(".trace-item").style.MozPaddingStart, 10);
ok(depth > lastDepth, "The depth should be increasing");
lastDepth = depth;
}
lastDepth = Infinity;
for (let t of factorialLogs.slice(5)) {
let depth = parseInt(t.querySelector(".trace-item").style.MozPaddingStart, 10);
ok(depth < lastDepth, "The depth should be decreasing");
lastDepth = depth;
}
const throwerLogs = filterTraces(gPanel,
t => t.querySelector(".trace-name[value=thrower]"));
is(throwerLogs.length, 2, "Should have an enter and an exit for 'thrower'");
ok(throwerLogs[0].querySelector(".trace-call"),
"The first 'thrower' log should be a call.");
ok(throwerLogs[1].querySelector(".trace-throw",
"The second 'thrower' log should be a throw."));
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
gDebugger = null;
});

Просмотреть файл

@ -1,74 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that we highlight matching calls and returns on hover.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
let gTab, gPanel, gDebugger;
function test() {
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(() => startTracing(gPanel))
.then(clickButton)
.then(() => waitForClientEvents(aPanel, "traces"))
.then(highlightCall)
.then(testReturnHighlighted)
.then(unhighlightCall)
.then(testNoneHighlighted)
.then(() => stopTracing(gPanel))
.then(() => {
const deferred = promise.defer();
SpecialPowers.popPrefEnv(deferred.resolve);
return deferred.promise;
})
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
});
}
function clickButton() {
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
}
function highlightCall() {
const callTrace = filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[0];
EventUtils.sendMouseEvent({ type: "mouseover" },
callTrace,
gDebugger);
}
function testReturnHighlighted() {
const returnTrace = filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[1];
ok(Array.indexOf(returnTrace.querySelector(".trace-item").classList, "selected-matching") >= 0,
"The corresponding return log should be highlighted.");
}
function unhighlightCall() {
const callTrace = filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[0];
EventUtils.sendMouseEvent({ type: "mouseout" },
callTrace,
gDebugger);
}
function testNoneHighlighted() {
const highlightedTraces = filterTraces(gPanel, t => t.querySelector(".selected-matching"));
is(highlightedTraces.length, 0, "Shouldn't have any highlighted traces");
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
gDebugger = null;
});

Просмотреть файл

@ -1,70 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
SimpleTest.requestCompleteLog();
/**
* Test that we can jump to function definitions by clicking on logs.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
let gTab, gPanel, gDebugger, gSources;
function test() {
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gSources = gDebugger.DebuggerView.Sources;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(() => startTracing(gPanel))
.then(() => clickButton())
.then(() => waitForClientEvents(aPanel, "traces"))
.then(() => {
// Switch away from the JS file so we can make sure that clicking on a
// log will switch us back to the correct JS file.
gSources.selectedValue = getSourceActor(gSources, TAB_URL);
return ensureSourceIs(aPanel, getSourceActor(gSources, TAB_URL), true);
})
.then(() => {
const finished = waitForSourceShown(gPanel, "code_tracing-01.js");
clickTraceLog();
return finished;
})
.then(testCorrectLine)
.then(() => stopTracing(gPanel))
.then(() => {
const deferred = promise.defer();
SpecialPowers.popPrefEnv(deferred.resolve);
return deferred.promise;
})
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
});
}
function clickButton() {
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
}
function clickTraceLog() {
filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[0].click();
}
function testCorrectLine() {
is(gDebugger.DebuggerView.editor.getCursor().line, 18,
"The editor should have the function definition site's line selected.");
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
gDebugger = null;
gSources = null;
});

Просмотреть файл

@ -1,80 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that when we click on logs, we get the parameters/return value in the variables view.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
let gTab, gPanel, gDebugger;
function test() {
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(() => startTracing(gPanel))
.then(clickButton)
.then(() => waitForClientEvents(aPanel, "traces"))
.then(clickTraceCall)
.then(testParams)
.then(clickTraceReturn)
.then(testReturn)
.then(() => stopTracing(gPanel))
.then(() => {
const deferred = promise.defer();
SpecialPowers.popPrefEnv(deferred.resolve);
return deferred.promise;
})
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
DevToolsUtils.reportException("browser_dbg_tracing-04.js", aError);
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
});
}
function clickButton() {
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
}
function clickTraceCall() {
filterTraces(gPanel, t => t.querySelector(".trace-name[value=factorial]"))[0]
.click();
}
function testParams() {
const name = gDebugger.document.querySelector(".variables-view-variable .name");
ok(name, "Should have a variable name");
is(name.getAttribute("value"), "n", "The variable name should be n");
const value = gDebugger.document.querySelector(".variables-view-variable .value.token-number");
ok(value, "Should have a variable value");
is(value.getAttribute("value"), "5", "The variable value should be 5");
}
function clickTraceReturn() {
filterTraces(gPanel, t => t.querySelector(".trace-name[value=factorial]"))
.pop().click();
}
function testReturn() {
const name = gDebugger.document.querySelector(".variables-view-variable .name");
ok(name, "Should have a variable name");
is(name.getAttribute("value"), "<return>", "The variable name should be <return>");
const value = gDebugger.document.querySelector(".variables-view-variable .value.token-number");
ok(value, "Should have a variable value");
is(value.getAttribute("value"), "120", "The variable value should be 120");
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
gDebugger = null;
});

Просмотреть файл

@ -1,81 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that text describing the tracing state is correctly displayed.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
let gTab, gPanel, gDebugger;
let gTracer, gL10N;
function test() {
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gTracer = gDebugger.DebuggerView.Tracer;
gL10N = gDebugger.L10N;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(testTracingNotStartedText)
.then(() => gTracer._onStartTracing())
.then(testFunctionCallsUnavailableText)
.then(clickButton)
.then(() => waitForClientEvents(aPanel, "traces"))
.then(testNoEmptyText)
.then(() => gTracer._onClear())
.then(testFunctionCallsUnavailableText)
.then(() => gTracer._onStopTracing())
.then(testTracingNotStartedText)
.then(() => gTracer._onClear())
.then(testTracingNotStartedText)
.then(() => {
const deferred = promise.defer();
SpecialPowers.popPrefEnv(deferred.resolve);
return deferred.promise;
})
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
DevToolsUtils.reportException("browser_dbg_tracing-05.js", aError);
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
});
}
function testTracingNotStartedText() {
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
ok(label,
"A label is displayed in the tracer tabpanel.");
is(label.getAttribute("value"), gL10N.getStr("tracingNotStartedText"),
"The correct {{tracingNotStartedText}} is displayed in the tracer tabpanel.");
}
function testFunctionCallsUnavailableText() {
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
ok(label,
"A label is displayed in the tracer tabpanel.");
is(label.getAttribute("value"), gL10N.getStr("noFunctionCallsText"),
"The correct {{noFunctionCallsText}} is displayed in the tracer tabpanel.");
}
function testNoEmptyText() {
let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
ok(!label,
"No label should be displayed in the tracer tabpanel.");
}
function clickButton() {
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
gDebugger = null;
gTracer = null;
gL10N = null;
});

Просмотреть файл

@ -1,37 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that the tracer doesn't connect to the backend when tracing is disabled.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
const TRACER_PREF = "devtools.debugger.tracer";
let gTab, gPanel, gDebugger;
let gOriginalPref = Services.prefs.getBoolPref(TRACER_PREF);
Services.prefs.setBoolPref(TRACER_PREF, false);
function test() {
initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
waitForSourceShown(gPanel, "code_tracing-01.js")
.then(() => {
ok(!gDebugger.DebuggerController.traceClient, "Should not have a trace client");
closeDebuggerAndFinish(gPanel);
})
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
gDebugger = null;
Services.prefs.setBoolPref(TRACER_PREF, gOriginalPref);
});

Просмотреть файл

@ -1,83 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Execute code both before and after blackboxing and test that we get
* appropriately styled traces.
*/
const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
let gTab, gPanel;
function test() {
Task.async(function*() {
yield pushPref();
[gTab,, gPanel] = yield initDebugger(TAB_URL);
yield startTracing(gPanel);
yield clickButton();
yield waitForClientEvents(gPanel, "traces");
/**
* Test that there are some traces which are not blackboxed.
*/
const firstBbButton = getBlackBoxButton(gPanel);
ok(!firstBbButton.checked, "Should not be black boxed by default");
const blackBoxedTraces =
gPanel.panelWin.document.querySelectorAll(".trace-item.black-boxed");
ok(blackBoxedTraces.length === 0, "There should no blackboxed traces.");
const notBlackBoxedTraces =
gPanel.panelWin.document.querySelectorAll(".trace-item:not(.black-boxed)");
ok(notBlackBoxedTraces.length > 0,
"There should be some traces which are not blackboxed.");
yield toggleBlackBoxing(gPanel);
yield clickButton();
yield waitForClientEvents(gPanel, "traces");
/**
* Test that there are some traces which are blackboxed.
*/
const secondBbButton = getBlackBoxButton(gPanel);
ok(secondBbButton.checked, "The checkbox should no longer be checked.");
const traces =
gPanel.panelWin.document.querySelectorAll(".trace-item.black-boxed");
ok(traces.length > 0, "There should be some blackboxed traces.");
yield stopTracing(gPanel);
yield popPref();
yield closeDebuggerAndFinish(gPanel);
finish();
})().catch(e => {
ok(false, "Got an error: " + e.message + "\n" + e.stack);
finish();
});
}
function clickButton() {
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
}
function pushPref() {
let deferred = promise.defer();
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]},
deferred.resolve);
return deferred.promise;
}
function popPref() {
let deferred = promise.defer();
SpecialPowers.popPrefEnv(deferred.resolve);
return deferred.promise;
}
registerCleanupFunction(function() {
gTab = null;
gPanel = null;
});

Просмотреть файл

@ -1,59 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that tracing about:config doesn't produce errors.
*/
const TAB_URL = "about:config";
let gPanel, gDoneChecks;
function test() {
gDoneChecks = promise.defer();
const tracerPref = promise.defer();
const configPref = promise.defer();
SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, tracerPref.resolve);
SpecialPowers.pushPrefEnv({'set': [["general.warnOnAboutConfig", false]]}, configPref.resolve);
promise.all([tracerPref.promise, configPref.promise]).then(() => {
initDebugger(TAB_URL).then(([,, aPanel]) => {
gPanel = aPanel;
gPanel.panelWin.gClient.addOneTimeListener("traces", testTraceLogs);
}).then(() => startTracing(gPanel))
.then(generateTrace)
.then(() => waitForClientEvents(gPanel, "traces"))
.then(() => gDoneChecks.promise)
.then(() => stopTracing(gPanel))
.then(resetPreferences)
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
}
function testTraceLogs(name, packet) {
info("Traces: " + packet.traces.length);
ok(packet.traces.length > 0, "Got some traces.");
ok(packet.traces.every(t => t.type != "enteredFrame" || !!t.location),
"All enteredFrame traces contain location.");
gDoneChecks.resolve();
}
function generateTrace(name, packet) {
// Interact with the page to cause JS execution.
let search = content.document.getElementById("textbox");
info("Interacting with the page.");
search.value = "devtools";
}
function resetPreferences() {
const deferred = promise.defer();
SpecialPowers.popPrefEnv(() => SpecialPowers.popPrefEnv(deferred.resolve));
return deferred.promise;
}
registerCleanupFunction(function() {
gPanel = null;
gDoneChecks = null;
});

Просмотреть файл

@ -1,29 +0,0 @@
function factorial(n) {
if (n <= 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
function* yielder(n) {
while (n-- >= 0) {
yield { value: n, squared: n * n };
}
}
function thrower() {
throw new Error("Curse your sudden but inevitable betrayal!");
}
function main() {
factorial(5);
// XXX bug 923729: Can't test yielding yet.
// for (let x of yielder(5)) {}
try {
thrower();
} catch (e) {
}
}

Просмотреть файл

@ -1,15 +0,0 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Debugger Tracer test page</title>
</head>
<body>
<script src="code_same-line-functions.js"></script>
<button onclick="first()">Click me!</button>
</body>
</html>

Просмотреть файл

@ -1,20 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Debugger Tracer test page</title>
</head>
<body>
<script src="code_tracing-01.js"></script>
<button onclick="main()">Click me!</button>
<script type="text/javascript">
// Have an inline script to make sure the HTML file is listed
// in the sources. We want to switch between them.
function foo() {
return 5;
}
</script>
</body>
</html>

Просмотреть файл

@ -888,39 +888,6 @@ function reopenVarPopup(...aArgs) {
return hideVarPopup.apply(this, aArgs).then(() => openVarPopup.apply(this, aArgs));
}
// Tracing helpers
function startTracing(aPanel) {
const deferred = promise.defer();
aPanel.panelWin.DebuggerController.Tracer.startTracing(aResponse => {
if (aResponse.error) {
deferred.reject(aResponse);
} else {
deferred.resolve(aResponse);
}
});
return deferred.promise;
}
function stopTracing(aPanel) {
const deferred = promise.defer();
aPanel.panelWin.DebuggerController.Tracer.stopTracing(aResponse => {
if (aResponse.error) {
deferred.reject(aResponse);
} else {
deferred.resolve(aResponse);
}
});
return deferred.promise;
}
function filterTraces(aPanel, f) {
const traces = aPanel.panelWin.document
.getElementById("tracer-traces")
.querySelector("scrollbox")
.children;
return Array.filter(traces, f);
}
function attachAddonActorForUrl(aClient, aUrl) {
let deferred = promise.defer();

Просмотреть файл

@ -1,416 +0,0 @@
/* 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/. */
"use strict";
/**
* Functions handling the traces UI.
*/
function TracerView(DebuggerController, DebuggerView) {
this._selectedItem = null;
this._matchingItems = null;
this.widget = null;
this.Tracer = DebuggerController.Tracer;
this.DebuggerView = DebuggerView;
this._highlightItem = this._highlightItem.bind(this);
this._isNotSelectedItem = this._isNotSelectedItem.bind(this);
this._unhighlightMatchingItems =
DevToolsUtils.makeInfallible(this._unhighlightMatchingItems.bind(this));
this._onToggleTracing =
DevToolsUtils.makeInfallible(this._onToggleTracing.bind(this));
this._onStartTracing =
DevToolsUtils.makeInfallible(this._onStartTracing.bind(this));
this._onClear =
DevToolsUtils.makeInfallible(this._onClear.bind(this));
this._onSelect =
DevToolsUtils.makeInfallible(this._onSelect.bind(this));
this._onMouseOver =
DevToolsUtils.makeInfallible(this._onMouseOver.bind(this));
this._onSearch =
DevToolsUtils.makeInfallible(this._onSearch.bind(this));
}
TracerView.MAX_TRACES = 200;
TracerView.prototype = Heritage.extend(WidgetMethods, {
/**
* Initialization function, called when the debugger is started.
*/
initialize: function() {
dumpn("Initializing the TracerView");
this._traceButton = document.getElementById("trace");
this._tracerTab = document.getElementById("tracer-tab");
// Remove tracer related elements from the dom and tear everything down if
// the tracer isn't enabled.
if (!Prefs.tracerEnabled) {
this._traceButton.remove();
this._traceButton = null;
this._tracerTab.remove();
this._tracerTab = null;
return;
}
this.widget = new FastListWidget(document.getElementById("tracer-traces"));
this._traceButton.removeAttribute("hidden");
this._tracerTab.removeAttribute("hidden");
this._search = document.getElementById("tracer-search");
this._template = document.getElementsByClassName("trace-item-template")[0];
this._templateItem = this._template.getElementsByClassName("trace-item")[0];
this._templateTypeIcon = this._template.getElementsByClassName("trace-type")[0];
this._templateNameNode = this._template.getElementsByClassName("trace-name")[0];
this.widget.addEventListener("select", this._onSelect, false);
this.widget.addEventListener("mouseover", this._onMouseOver, false);
this.widget.addEventListener("mouseout", this._unhighlightMatchingItems, false);
this._search.addEventListener("input", this._onSearch, false);
this._startTooltip = L10N.getStr("startTracingTooltip");
this._stopTooltip = L10N.getStr("stopTracingTooltip");
this._tracingNotStartedString = L10N.getStr("tracingNotStartedText");
this._noFunctionCallsString = L10N.getStr("noFunctionCallsText");
this._traceButton.setAttribute("tooltiptext", this._startTooltip);
this.emptyText = this._tracingNotStartedString;
this._addCommands();
},
/**
* Destruction function, called when the debugger is closed.
*/
destroy: function() {
dumpn("Destroying the TracerView");
if (!this.widget) {
return;
}
this.widget.removeEventListener("select", this._onSelect, false);
this.widget.removeEventListener("mouseover", this._onMouseOver, false);
this.widget.removeEventListener("mouseout", this._unhighlightMatchingItems, false);
this._search.removeEventListener("input", this._onSearch, false);
},
/**
* Add commands that XUL can fire.
*/
_addCommands: function() {
XULUtils.addCommands(document.getElementById('debuggerCommands'), {
toggleTracing: () => this._onToggleTracing(),
startTracing: () => this._onStartTracing(),
clearTraces: () => this._onClear()
});
},
/**
* Function invoked by the "toggleTracing" command to switch the tracer state.
*/
_onToggleTracing: function() {
if (this.Tracer.tracing) {
this._onStopTracing();
} else {
this._onStartTracing();
}
},
/**
* Function invoked either by the "startTracing" command or by
* _onToggleTracing to start execution tracing in the backend.
*
* @return object
* A promise resolved once the tracing has successfully started.
*/
_onStartTracing: function() {
this._traceButton.setAttribute("checked", true);
this._traceButton.setAttribute("tooltiptext", this._stopTooltip);
this.empty();
this.emptyText = this._noFunctionCallsString;
let deferred = promise.defer();
this.Tracer.startTracing(deferred.resolve);
return deferred.promise;
},
/**
* Function invoked by _onToggleTracing to stop execution tracing in the
* backend.
*
* @return object
* A promise resolved once the tracing has successfully stopped.
*/
_onStopTracing: function() {
this._traceButton.removeAttribute("checked");
this._traceButton.setAttribute("tooltiptext", this._startTooltip);
this.emptyText = this._tracingNotStartedString;
let deferred = promise.defer();
this.Tracer.stopTracing(deferred.resolve);
return deferred.promise;
},
/**
* Function invoked by the "clearTraces" command to empty the traces pane.
*/
_onClear: function() {
this.empty();
},
/**
* Populate the given parent scope with the variable with the provided name
* and value.
*
* @param String aName
* The name of the variable.
* @param Object aParent
* The parent scope.
* @param Object aValue
* The value of the variable.
*/
_populateVariable: function(aName, aParent, aValue) {
let item = aParent.addItem(aName, { value: aValue });
if (aValue) {
let wrappedValue = new this.Tracer.WrappedObject(aValue);
this.DebuggerView.Variables.controller.populate(item, wrappedValue);
item.expand();
item.twisty = false;
}
},
/**
* Handler for the widget's "select" event. Displays parameters, exception, or
* return value depending on whether the selected trace is a call, throw, or
* return respectively.
*
* @param Object traceItem
* The selected trace item.
*/
_onSelect: function _onSelect({ detail: traceItem }) {
if (!traceItem) {
return;
}
const data = traceItem.attachment.trace;
const { location: { url, line } } = data;
this.DebuggerView.setEditorLocation(
this.DebuggerView.Sources.getActorForLocation({ url }),
line,
{ noDebug: true }
);
this.DebuggerView.Variables.empty();
const scope = this.DebuggerView.Variables.addScope();
if (data.type == "call") {
const params = DevToolsUtils.zip(data.parameterNames, data.arguments);
for (let [name, val] of params) {
if (val === undefined) {
scope.addItem(name, { value: "<value not available>" });
} else {
this._populateVariable(name, scope, val);
}
}
} else {
const varName = "<" + (data.type == "throw" ? "exception" : data.type) + ">";
this._populateVariable(varName, scope, data.returnVal);
}
scope.expand();
this.DebuggerView.showInstrumentsPane();
},
/**
* Add the hover frame enter/exit highlighting to a given item.
*/
_highlightItem: function(aItem) {
if (!aItem || !aItem.target) {
return;
}
const trace = aItem.target.querySelector(".trace-item");
trace.classList.add("selected-matching");
},
/**
* Remove the hover frame enter/exit highlighting to a given item.
*/
_unhighlightItem: function(aItem) {
if (!aItem || !aItem.target) {
return;
}
const match = aItem.target.querySelector(".selected-matching");
if (match) {
match.classList.remove("selected-matching");
}
},
/**
* Remove the frame enter/exit pair highlighting we do when hovering.
*/
_unhighlightMatchingItems: function() {
if (this._matchingItems) {
this._matchingItems.forEach(this._unhighlightItem);
this._matchingItems = null;
}
},
/**
* Returns true if the given item is not the selected item.
*/
_isNotSelectedItem: function(aItem) {
return aItem !== this.selectedItem;
},
/**
* Highlight the frame enter/exit pair of items for the given item.
*/
_highlightMatchingItems: function(aItem) {
const frameId = aItem.attachment.trace.frameId;
const predicate = e => e.attachment.trace.frameId == frameId;
this._unhighlightMatchingItems();
this._matchingItems = this.items.filter(predicate);
this._matchingItems
.filter(this._isNotSelectedItem)
.forEach(this._highlightItem);
},
/**
* Listener for the mouseover event.
*/
_onMouseOver: function({ target }) {
const traceItem = this.getItemForElement(target);
if (traceItem) {
this._highlightMatchingItems(traceItem);
}
},
/**
* Listener for typing in the search box.
*/
_onSearch: function() {
const query = this._search.value.trim().toLowerCase();
const predicate = name => name.toLowerCase().includes(query);
this.filterContents(item => predicate(item.attachment.trace.name));
},
/**
* Select the traces tab in the sidebar.
*/
selectTab: function() {
const tabs = this._tracerTab.parentElement;
tabs.selectedIndex = Array.indexOf(tabs.children, this._tracerTab);
},
/**
* Commit all staged items to the widget. Overridden so that we can call
* |FastListWidget.prototype.flush|.
*/
commit: function() {
WidgetMethods.commit.call(this);
// TODO: Accessing non-standard widget properties. Figure out what's the
// best way to expose such things. Bug 895514.
this.widget.flush();
},
/**
* Adds the trace record provided as an argument to the view.
*
* @param object aTrace
* The trace record coming from the tracer actor.
*/
addTrace: function(aTrace) {
// Create the element node for the trace item.
let view = this._createView(aTrace);
// Append a source item to this container.
this.push([view], {
staged: true,
attachment: {
trace: aTrace
}
});
},
/**
* Customization function for creating an item's UI.
*
* @return nsIDOMNode
* The network request view.
*/
_createView: function(aTrace) {
let { type, name, location, blackBoxed, depth, frameId } = aTrace;
let { parameterNames, returnVal, arguments: args } = aTrace;
let fragment = document.createDocumentFragment();
this._templateItem.classList.toggle("black-boxed", blackBoxed);
this._templateItem.setAttribute("tooltiptext", SourceUtils.trimUrl(location.url));
this._templateItem.style.MozPaddingStart = depth + "em";
const TYPES = ["call", "yield", "return", "throw"];
for (let t of TYPES) {
this._templateTypeIcon.classList.toggle("trace-" + t, t == type);
}
this._templateTypeIcon.setAttribute("value", {
call: "\u2192",
yield: "Y",
return: "\u2190",
throw: "E",
terminated: "TERMINATED"
}[type]);
this._templateNameNode.setAttribute("value", name);
// All extra syntax and parameter nodes added.
const addedNodes = [];
if (parameterNames) {
const syntax = (p) => {
const el = document.createElement("label");
el.setAttribute("value", p);
el.classList.add("trace-syntax");
el.classList.add("plain");
addedNodes.push(el);
return el;
};
this._templateItem.appendChild(syntax("("));
for (let i = 0, n = parameterNames.length; i < n; i++) {
let param = document.createElement("label");
param.setAttribute("value", parameterNames[i]);
param.classList.add("trace-param");
param.classList.add("plain");
addedNodes.push(param);
this._templateItem.appendChild(param);
if (i + 1 !== n) {
this._templateItem.appendChild(syntax(", "));
}
}
this._templateItem.appendChild(syntax(")"));
}
// Flatten the DOM by removing one redundant box (the template container).
for (let node of this._template.childNodes) {
fragment.appendChild(node.cloneNode(true));
}
// Remove any added nodes from the template.
for (let node of addedNodes) {
this._templateItem.removeChild(node);
}
return fragment;
}
});
DebuggerView.Tracer = new TracerView(DebuggerController, DebuggerView);

Просмотреть файл

@ -71,7 +71,6 @@ browser.jar:
content/browser/devtools/debugger/workers-view.js (debugger/views/workers-view.js)
content/browser/devtools/debugger/sources-view.js (debugger/views/sources-view.js)
content/browser/devtools/debugger/variable-bubble-view.js (debugger/views/variable-bubble-view.js)
content/browser/devtools/debugger/tracer-view.js (debugger/views/tracer-view.js)
content/browser/devtools/debugger/watch-expressions-view.js (debugger/views/watch-expressions-view.js)
content/browser/devtools/debugger/event-listeners-view.js (debugger/views/event-listeners-view.js)
content/browser/devtools/debugger/global-search-view.js (debugger/views/global-search-view.js)

Просмотреть файл

@ -38,6 +38,9 @@ const gNSURLStore = new Map();
// The cache used to store inflated frames.
const gInflatedFrameStore = new WeakMap();
// The cache used to store frame data from `getInfo`.
const gFrameData = new WeakMap();
/**
* Parses the raw location of this function call to retrieve the actual
* function name, source url, host name, line and column.
@ -449,6 +452,69 @@ function isNumeric(c) {
return c >= CHAR_CODE_0 && c <= CHAR_CODE_9;
}
/**
* Calculates the relative costs of this frame compared to a root,
* and generates allocations information if specified. Uses caching
* if possible.
*
* @param {ThreadNode|FrameNode} node
* The node we are calculating.
* @param {ThreadNode} options.root
* The root thread node to calculate relative costs.
* Generates [self|total] [duration|percentage] values.
* @param {boolean} options.allocations
* Generates `totalAllocations` and `selfAllocations`.
*
* @return {object}
*/
function getFrameInfo (node, options) {
let data = gFrameData.get(node);
if (!data) {
if (node.nodeType === "Thread") {
data = Object.create(null);
data.functionName = global.L10N.getStr("table.root");
} else {
data = parseLocation(node.location, node.line, node.column);
data.hasOptimizations = node.hasOptimizations();
data.isContent = node.isContent;
data.isMetaCategory = node.isMetaCategory;
}
data.samples = node.youngestFrameSamples;
data.categoryData = global.CATEGORY_MAPPINGS[node.category] || {};
data.nodeType = node.nodeType;
// Frame name (function location or some meta information)
data.name = data.isMetaCategory ? data.categoryData.label : data.functionName || "";
data.tooltiptext = data.isMetaCategory ? data.categoryData.label : node.location || "";
gFrameData.set(node, data);
}
// If a root specified, calculate the relative costs in the context of
// this call tree. The cached store may already have this, but generate
// if it does not.
if (options && options.root && !data.COSTS_CALCULATED) {
let totalSamples = options.root.samples;
let totalDuration = options.root.duration;
data.selfDuration = node.youngestFrameSamples / totalSamples * totalDuration;
data.selfPercentage = node.youngestFrameSamples / totalSamples * 100;
data.totalDuration = node.samples / totalSamples * totalDuration;
data.totalPercentage = node.samples / totalSamples * 100;
data.COSTS_CALCULATED = true;
}
if (options && options.allocations && !data.ALLOCATIONS_CALCULATED) {
data.totalAllocations = node.allocations + node.calls.reduce((acc, node) => acc + node.allocations, 0);
data.selfAllocations = node.allocations;
data.ALLOCATIONS_CALCULATED = true;
}
return data;
}
exports.getFrameInfo = getFrameInfo;
exports.computeIsContentAndCategory = computeIsContentAndCategory;
exports.parseLocation = parseLocation;
exports.getInflatedFrameCache = getInflatedFrameCache;

Просмотреть файл

@ -5,10 +5,6 @@
const { Cc, Ci, Cu, Cr } = require("chrome");
loader.lazyRequireGetter(this, "L10N",
"devtools/performance/global", true);
loader.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
"devtools/performance/global", true);
loader.lazyRequireGetter(this, "JITOptimizations",
"devtools/performance/jit", true);
loader.lazyRequireGetter(this, "FrameUtils",
@ -39,6 +35,7 @@ function ThreadNode(thread, options = {}) {
this.youngestFrameSamples = 0;
this.calls = [];
this.duration = options.endTime - options.startTime;
this.nodeType = "Thread";
let { samples, stackTable, frameTable, stringTable, allocationsTable } = thread;
@ -307,14 +304,12 @@ ThreadNode.prototype = {
/**
* Gets additional details about this node.
* @see FrameNode.prototype.getInfo for more information.
*
* @return object
*/
getInfo: function() {
return {
nodeType: "Thread",
functionName: L10N.getStr("table.root"),
categoryData: {}
};
getInfo: function(options) {
return FrameUtils.getFrameInfo(this, options);
},
/**
@ -378,6 +373,7 @@ function FrameNode(frameKey, { location, line, category, allocations, isContent
this._stringTable = null;
this.isMetaCategory = !!isMetaCategory;
this.category = category;
this.nodeType = "Frame";
}
FrameNode.prototype = {
@ -443,30 +439,21 @@ FrameNode.prototype = {
/**
* Returns the parsed location and additional data describing
* this frame. Uses cached data if possible.
* this frame. Uses cached data if possible. Takes the following
* options:
*
* @param {ThreadNode} options.root
* The root thread node to calculate relative costs.
* Generates [self|total] [duration|percentage] values.
* @param {boolean} options.allocations
* Generates `totalAllocations` and `selfAllocations`.
*
* @return object
* The computed { name, file, url, line } properties for this
* function call.
* function call, as well as additional params if options specified.
*/
getInfo: function() {
return this._data || this._computeInfo();
},
/**
* Parses the raw location of this function call to retrieve the actual
* function name and source url.
*/
_computeInfo: function() {
let categoryData = CATEGORY_MAPPINGS[this.category] || {};
let parsedData = FrameUtils.parseLocation(this.location, this.line, this.column);
parsedData.nodeType = "Frame";
parsedData.categoryData = categoryData;
parsedData.isContent = this.isContent;
parsedData.isMetaCategory = this.isMetaCategory;
parsedData.hasOptimizations = this.hasOptimizations();
return this._data = parsedData;
getInfo: function(options) {
return FrameUtils.getFrameInfo(this, options);
},
/**

Просмотреть файл

@ -133,39 +133,38 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
* @return nsIDOMNode
*/
_displaySelf: function(document, arrowNode) {
let displayedData = this.getDisplayedData();
let frameInfo = this.frame.getInfo();
let frameInfo = this.getDisplayedData();
if (this.visibleCells.duration) {
var durationCell = this._createTimeCell(document, displayedData.totalDuration);
var durationCell = this._createTimeCell(document, frameInfo.totalDuration);
}
if (this.visibleCells.selfDuration) {
var selfDurationCell = this._createTimeCell(document, displayedData.selfDuration, true);
var selfDurationCell = this._createTimeCell(document, frameInfo.selfDuration, true);
}
if (this.visibleCells.percentage) {
var percentageCell = this._createExecutionCell(document, displayedData.totalPercentage);
var percentageCell = this._createExecutionCell(document, frameInfo.totalPercentage);
}
if (this.visibleCells.selfPercentage) {
var selfPercentageCell = this._createExecutionCell(document, displayedData.selfPercentage, true);
var selfPercentageCell = this._createExecutionCell(document, frameInfo.selfPercentage, true);
}
if (this.visibleCells.allocations) {
var allocationsCell = this._createAllocationsCell(document, displayedData.totalAllocations);
var allocationsCell = this._createAllocationsCell(document, frameInfo.totalAllocations);
}
if (this.visibleCells.selfAllocations) {
var selfAllocationsCell = this._createAllocationsCell(document, displayedData.selfAllocations, true);
var selfAllocationsCell = this._createAllocationsCell(document, frameInfo.selfAllocations, true);
}
if (this.visibleCells.samples) {
var samplesCell = this._createSamplesCell(document, displayedData.samples);
var samplesCell = this._createSamplesCell(document, frameInfo.samples);
}
if (this.visibleCells.function) {
var functionCell = this._createFunctionCell(document, arrowNode, displayedData.name, frameInfo, this.level);
var functionCell = this._createFunctionCell(document, arrowNode, frameInfo.name, frameInfo, this.level);
}
let targetNode = document.createElement("hbox");
targetNode.className = "call-tree-item";
targetNode.setAttribute("origin", frameInfo.isContent ? "content" : "chrome");
targetNode.setAttribute("category", frameInfo.categoryData.abbrev || "");
targetNode.setAttribute("tooltiptext", displayedData.tooltiptext);
targetNode.setAttribute("tooltiptext", frameInfo.tooltiptext);
if (this.hidden) {
targetNode.style.display = "none";
@ -355,8 +354,10 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
return this._cachedDisplayedData;
}
let data = this._cachedDisplayedData = Object.create(null);
let frameInfo = this.frame.getInfo();
return this._cachedDisplayedData = this.frame.getInfo({
root: this.root.frame,
allocations: (this.visibleCells.allocations || this.visibleCells.selfAllocations)
});
/**
* When inverting call tree, the costs and times are dependent on position
@ -373,52 +374,6 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
* Every instance of a `CallView` represents a row in the call tree. The same
* container node is used for all rows.
*/
// Leaf nodes in an inverted tree don't have to do anything special.
let isLeaf = this._level === 0;
let totalSamples = this.root.frame.samples;
let totalDuration = this.root.frame.duration;
// Self duration, cost
if (this.visibleCells.selfDuration) {
data.selfDuration = this.frame.youngestFrameSamples / totalSamples * totalDuration;
}
if (this.visibleCells.selfPercentage) {
data.selfPercentage = this.frame.youngestFrameSamples / totalSamples * 100;
}
// Total duration, cost
if (this.visibleCells.duration) {
data.totalDuration = this.frame.samples / totalSamples * totalDuration;
}
if (this.visibleCells.percentage) {
data.totalPercentage = this.frame.samples / totalSamples * 100;
}
// Raw samples.
if (this.visibleCells.samples) {
data.samples = this.frame.youngestFrameSamples;
}
// Self/total allocations count.
if (this.visibleCells.allocations) {
let childrenAllocations = this.frame.calls.reduce((acc, node) => acc + node.allocations, 0);
data.totalAllocations = this.frame.allocations + childrenAllocations;
}
if (this.visibleCells.selfAllocations) {
data.selfAllocations = this.frame.allocations;
}
// Frame name (function location or some meta information).
data.name = frameInfo.isMetaCategory
? frameInfo.categoryData.label
: frameInfo.functionName || "";
data.tooltiptext = frameInfo.isMetaCategory
? frameInfo.categoryData.label
: this.frame.location || "";
return this._cachedDisplayedData;
},
/**

Просмотреть файл

@ -0,0 +1,149 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the tree model calculates correct costs/percentages for
* frame nodes. The model-only version of browser_profiler-tree-view-10.js
*/
function run_test() {
run_next_test();
}
add_task(function () {
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
let thread = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 50 });
/**
* Samples
*
* A->C
* A->B
* A->B->C x4
* A->B->D x4
*
* Expected Tree
* +--total--+--self--+--tree-------------+
* | 50% | 50% | C
* | 40% | 0 | -> B
* | 30% | 0 | -> A
* | 10% | 0 | -> A
*
* | 40% | 40% | D
* | 40% | 0 | -> B
* | 40% | 0 | -> A
*
* | 10% | 10% | B
* | 10% | 0 | -> A
*/
[ // total, self, name
[ 50, 50, "C", [
[ 40, 0, "B", [
[ 30, 0, "A"]
]],
[ 10, 0, "A"]
]],
[ 40, 40, "D", [
[ 40, 0, "B", [
[ 40, 0, "A"],
]]
]],
[ 10, 10, "B", [
[ 10, 0, "A"],
]]
].forEach(compareFrameInfo(thread));
});
function compareFrameInfo (root, parent) {
parent = parent || root;
return function (def) {
let [total, self, name, children] = def;
let node = getFrameNodePath(parent, name);
let data = node.getInfo({ root });
equal(total, data.totalPercentage, `${name} has correct total percentage: ${data.totalPercentage}`);
equal(self, data.selfPercentage, `${name} has correct self percentage: ${data.selfPercentage}`);
if (children) {
children.forEach(compareFrameInfo(root, node));
}
}
}
let gThread = synthesizeProfileForTest([{
time: 5,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "C" }
]
}, {
time: 10,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "D" }
]
}, {
time: 15,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "C" },
]
}, {
time: 20,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
]
}, {
time: 25,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "C" }
]
}, {
time: 30,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "C" }
]
}, {
time: 35,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "D" }
]
}, {
time: 40,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "D" }
]
}, {
time: 45,
frames: [
{ location: "(root)" },
{ location: "B" },
{ location: "C" }
]
}, {
time: 50,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "D" }
]
}]);

Просмотреть файл

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the costs for recursive frames does not overcount the collapsed
* samples.
*/
function run_test() {
run_next_test();
}
add_task(function () {
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
let thread = new ThreadNode(gThread, { startTime: 0, endTime: 50, flattenRecursion: true });
/**
* Samples
*
* A->B->C
* A->B->B->B->C
* A->B
* A->B->B->B
*/
[ // total, self, name
[ 100, 0, "(root)", [
[ 100, 0, "A", [
[ 100, 50, "B", [
[ 50, 50, "C"]
]]
]],
]],
].forEach(compareFrameInfo(thread));
});
function compareFrameInfo (root, parent) {
parent = parent || root;
return function (def) {
let [total, self, name, children] = def;
let node = getFrameNodePath(parent, name);
let data = node.getInfo({ root });
equal(total, data.totalPercentage, `${name} has correct total percentage: ${data.totalPercentage}`);
equal(self, data.selfPercentage, `${name} has correct self percentage: ${data.selfPercentage}`);
if (children) {
children.forEach(compareFrameInfo(root, node));
}
}
}
let gThread = synthesizeProfileForTest([{
time: 5,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "B" },
{ location: "B" },
{ location: "C" }
]
}, {
time: 10,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "C" }
]
}, {
time: 15,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
{ location: "B" },
{ location: "B" },
]
}, {
time: 20,
frames: [
{ location: "(root)" },
{ location: "A" },
{ location: "B" },
]
}]);

Просмотреть файл

@ -22,6 +22,8 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_tree-model-07.js]
[test_tree-model-08.js]
[test_tree-model-09.js]
[test_tree-model-10.js]
[test_tree-model-11.js]
[test_waterfall-utils-collapse-01.js]
[test_waterfall-utils-collapse-02.js]
[test_waterfall-utils-collapse-03.js]

Просмотреть файл

@ -241,6 +241,10 @@
</intent-filter>
</activity-alias>
<activity android:name="org.mozilla.gecko.trackingprotection.TrackingProtectionPrompt"
android:launchMode="singleTop"
android:theme="@style/OverlayActivity" />
#ifdef MOZ_ANDROID_TAB_QUEUE
<!-- The main reason for the Tab Queue build flag is to not mess with the VIEW intent filter
before the rest of the plumbing is in place -->

Просмотреть файл

@ -5,8 +5,6 @@
package org.mozilla.gecko;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.DynamicToolbar.PinReason;
import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
@ -71,6 +69,7 @@ import org.mozilla.gecko.toolbar.AutocompleteHandler;
import org.mozilla.gecko.toolbar.BrowserToolbar;
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
import org.mozilla.gecko.toolbar.ToolbarProgressView;
import org.mozilla.gecko.trackingprotection.TrackingProtectionPrompt;
import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.EventCallback;
@ -139,6 +138,8 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.ViewFlipper;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
import org.json.JSONException;
import org.json.JSONObject;
@ -2056,6 +2057,22 @@ public class BrowserApp extends GeckoApp
@Override
public void addPrivateTab() {
Tabs.getInstance().addPrivateTab();
showTrackingProtectionPromptIfApplicable();
}
private void showTrackingProtectionPromptIfApplicable() {
final SharedPreferences prefs = getSharedPreferences();
final boolean hasTrackingProtectionPromptBeShownBefore = prefs.getBoolean(GeckoPreferences.PREFS_TRACKING_PROTECTION_PROMPT_SHOWN, false);
if (hasTrackingProtectionPromptBeShownBefore) {
return;
}
prefs.edit().putBoolean(GeckoPreferences.PREFS_TRACKING_PROTECTION_PROMPT_SHOWN, true).apply();
startActivity(new Intent(BrowserApp.this, TrackingProtectionPrompt.class));
}
@Override

Просмотреть файл

@ -195,7 +195,15 @@ public class RestrictedProfiles {
return false;
}
return !getRestrictions(context).isEmpty();
Bundle restrictions = getRestrictions(context);
for (String key : restrictions.keySet()) {
if (restrictions.getBoolean(key)) {
// At least one restriction is enabled -> We are a restricted profile
return true;
}
}
return false;
}
public static boolean isAllowed(final Context context, final Restriction action) {

Просмотреть файл

@ -199,6 +199,11 @@
<!ENTITY pref_donottrack_title "Do not track">
<!ENTITY pref_donottrack_summary "&brandShortName; will tell sites that you do not want to be tracked">
<!ENTITY tracking_protection_prompt_title "Now with Tracking Protection">
<!ENTITY tracking_protection_prompt_text "Actively block tracking elements so you don\'t have to worry.">
<!ENTITY tracking_protection_prompt_tip_text "Visit Privacy settings to learn more">
<!ENTITY tracking_protection_prompt_action_button "Got it!">
<!ENTITY tab_queue_toast_message3 "Tab saved in &brandShortName;">
<!ENTITY tab_queue_toast_action "Open now">
<!ENTITY tab_queue_prompt_title "Opening multiple links?">

Просмотреть файл

@ -489,6 +489,7 @@ gbjar.sources += [
'toolbar/ToolbarPrefs.java',
'toolbar/ToolbarProgressView.java',
'TouchEventInterceptor.java',
'trackingprotection/TrackingProtectionPrompt.java',
'updater/UpdateService.java',
'updater/UpdateServiceHelper.java',
'Webapp.java',

Просмотреть файл

@ -100,6 +100,7 @@ OnSharedPreferenceChangeListener
public static final String NON_PREF_PREFIX = "android.not_a_preference.";
public static final String INTENT_EXTRA_RESOURCES = "resource";
public static final String PREFS_TRACKING_PROTECTION_PROMPT_SHOWN = NON_PREF_PREFIX + "trackingProtectionPromptShown";
public static String PREFS_HEALTHREPORT_UPLOAD_ENABLED = NON_PREF_PREFIX + "healthreport.uploadEnabled";
private static boolean sIsCharEncodingEnabled;

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.7 KiB

Просмотреть файл

@ -14,7 +14,7 @@
<LinearLayout
android:id="@+id/tab_queue_container"
android:layout_width="@dimen/tab_queue_container_width"
android:layout_width="@dimen/overlay_prompt_container_width"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:background="@android:color/white"
@ -22,7 +22,7 @@
<TextView
android:id="@+id/title"
android:layout_width="@dimen/tab_queue_content_width"
android:layout_width="@dimen/overlay_prompt_content_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-light"
@ -36,7 +36,7 @@
<TextView
android:id="@+id/text"
android:layout_width="@dimen/tab_queue_content_width"
android:layout_width="@dimen/overlay_prompt_content_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
@ -50,7 +50,7 @@
<TextView
android:id="@+id/tip_text"
android:layout_width="@dimen/tab_queue_content_width"
android:layout_width="@dimen/overlay_prompt_content_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
@ -88,7 +88,7 @@
<TextView
android:id="@+id/cancel_button"
style="@style/Widget.BaseButton"
android:layout_width="@dimen/tab_queue_button_width"
android:layout_width="@dimen/overlay_prompt_button_width"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@color/android:white"
@ -101,7 +101,7 @@
<Button
android:id="@+id/ok_button"
style="@style/Widget.BaseButton"
android:layout_width="@dimen/tab_queue_button_width"
android:layout_width="@dimen/overlay_prompt_button_width"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@drawable/button_background_action_orange_round"

Просмотреть файл

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tracking_protection_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false">
<LinearLayout
android:id="@+id/tracking_protection_inner_container"
android:layout_width="@dimen/overlay_prompt_container_width"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:background="@android:color/white"
android:orientation="vertical">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_tracking_protection"
android:layout_gravity="center"
android:layout_marginTop="40dp"
android:layout_marginBottom="20dp" />
<TextView
android:id="@+id/title"
android:layout_width="@dimen/overlay_prompt_content_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal"
android:text="@string/tracking_protection_prompt_title"
android:textColor="@color/text_and_tabs_tray_grey"
android:textSize="20sp"
tools:text="Now with Tracking Protection"/>
<TextView
android:id="@+id/text"
android:layout_width="@dimen/overlay_prompt_content_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:lineSpacingMultiplier="1.25"
android:paddingTop="20dp"
android:text="@string/tracking_protection_prompt_text"
android:textColor="@color/placeholder_grey"
android:textSize="16sp"
tools:text="Actively block tracking elements so you don't have to worry."/>
<TextView
android:id="@+id/link_text"
android:layout_width="@dimen/overlay_prompt_content_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingBottom="30dp"
android:paddingTop="20dp"
android:text="@string/tracking_protection_prompt_tip_text"
android:textColor="@color/link_blue"
android:textSize="14sp"
tools:text="Visit Privacy settings to learn more"/>
<Button
android:id="@+id/ok_button"
style="@style/Widget.BaseButton"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_gravity="center"
android:layout_marginBottom="40dp"
android:background="@drawable/button_background_action_orange_round"
android:text="@string/tracking_protection_prompt_action_button"
android:textColor="@android:color/white"
android:textSize="16sp"
android:layout_marginLeft="32dp"
android:layout_marginRight="32dp"
tools:text="Got it"/>
</LinearLayout>
</merge>

Просмотреть файл

@ -26,7 +26,7 @@
<dimen name="reading_list_row_height">96dp</dimen>
<dimen name="reading_list_row_padding_right">15dp</dimen>
<dimen name="tab_queue_container_width">360dp</dimen>
<dimen name="overlay_prompt_container_width">360dp</dimen>
<!-- Should be closer to 0.83 (140/168) but various roundings mean that 0.9 works better -->
<item name="thumbnail_aspect_ratio" format="float" type="dimen">0.9</item>

Просмотреть файл

@ -56,9 +56,9 @@
<dimen name="firstrun_content_width">300dp</dimen>
<dimen name="firstrun_min_height">180dp</dimen>
<dimen name="tab_queue_content_width">260dp</dimen>
<dimen name="tab_queue_button_width">148dp</dimen>
<dimen name="tab_queue_container_width">@dimen/match_parent</dimen>
<dimen name="overlay_prompt_content_width">260dp</dimen>
<dimen name="overlay_prompt_button_width">148dp</dimen>
<dimen name="overlay_prompt_container_width">@dimen/match_parent</dimen>
<!-- Site security icon -->
<dimen name="browser_toolbar_site_security_height">@dimen/match_parent</dimen>

Просмотреть файл

@ -250,6 +250,11 @@
<string name="pref_update_autodownload_disabled">&pref_update_autodownload_never;</string>
<string name="pref_update_autodownload_enabled">&pref_update_autodownload_always;</string>
<string name="tracking_protection_prompt_title">&tracking_protection_prompt_title;</string>
<string name="tracking_protection_prompt_text">&tracking_protection_prompt_text;</string>
<string name="tracking_protection_prompt_tip_text">&tracking_protection_prompt_tip_text;</string>
<string name="tracking_protection_prompt_action_button">&tracking_protection_prompt_action_button;</string>
<string name="pref_tab_queue_title">&pref_tab_queue_title2;</string>
<string name="pref_tab_queue_summary">&pref_tab_queue_summary3;</string>
<string name="tab_queue_prompt_title">&tab_queue_prompt_title;</string>

Просмотреть файл

@ -0,0 +1,134 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* 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/. */
package org.mozilla.gecko.trackingprotection;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.R;
import org.mozilla.gecko.animation.TransitionsTracker;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.util.HardwareUtils;
import android.content.Intent;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.AnimatorSet;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper;
public class TrackingProtectionPrompt extends Locales.LocaleAwareActivity {
public static final String LOGTAG = "Gecko" + TrackingProtectionPrompt.class.getSimpleName();
// Flag set during animation to prevent animation multiple-start.
private boolean isAnimating;
private View containerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showPrompt();
}
private void showPrompt() {
setContentView(R.layout.tracking_protection_prompt);
findViewById(R.id.ok_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onConfirmButtonPressed();
}
});
findViewById(R.id.link_text).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
slideOut();
final Intent settingsIntent = new Intent(TrackingProtectionPrompt.this, GeckoPreferences.class);
GeckoPreferences.setResourceToOpen(settingsIntent, "preferences_privacy");
startActivity(settingsIntent);
// Don't use a transition to settings if we're on a device where that
// would look bad.
if (HardwareUtils.IS_KINDLE_DEVICE) {
overridePendingTransition(0, 0);
}
}
});
containerView = findViewById(R.id.tracking_protection_inner_container);
ViewHelper.setTranslationY(containerView, 500);
ViewHelper.setAlpha(containerView, 0);
final Animator translateAnimator = ObjectAnimator.ofFloat(containerView, "translationY", 0);
translateAnimator.setDuration(400);
final Animator alphaAnimator = ObjectAnimator.ofFloat(containerView, "alpha", 1);
alphaAnimator.setStartDelay(200);
alphaAnimator.setDuration(600);
final AnimatorSet set = new AnimatorSet();
set.playTogether(alphaAnimator, translateAnimator);
set.setStartDelay(400);
TransitionsTracker.track(set);
set.start();
}
@Override
public void finish() {
super.finish();
// Don't perform an activity-dismiss animation.
overridePendingTransition(0, 0);
}
private void onConfirmButtonPressed() {
slideOut();
}
/**
* Slide the overlay down off the screen and destroy it.
*/
private void slideOut() {
if (isAnimating) {
return;
}
isAnimating = true;
ObjectAnimator animator = ObjectAnimator.ofFloat(containerView, "translationY", containerView.getHeight());
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finish();
}
});
animator.start();
}
/**
* Close the dialog if back is pressed.
*/
@Override
public void onBackPressed() {
slideOut();
}
/**
* Close the dialog if the anything that isn't a button is tapped.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
slideOut();
return true;
}
}

Просмотреть файл

@ -54,6 +54,7 @@ abstract class PixelTest extends BaseTest {
public void addTab(String url, String title, boolean isPrivate) {
Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
if (isPrivate) {
selectMenuItem(mStringHelper.NEW_PRIVATE_TAB_LABEL);
} else {
@ -61,6 +62,12 @@ abstract class PixelTest extends BaseTest {
}
tabEventExpecter.blockForEvent();
contentEventExpecter.blockForEvent();
if (isPrivate) {
waitForText(mStringHelper.TRACKING_PROTECTION_PROMPT_TITLE);
mSolo.clickOnText(mStringHelper.TRACKING_PROTECTION_PROMPT_BUTTON);
}
waitForText(mStringHelper.TITLE_PLACE_HOLDER);
loadAndPaint(url);
tabEventExpecter.unregisterListener();

Просмотреть файл

@ -144,6 +144,8 @@ public class StringHelper {
// Privacy
public final String TRACKING_PROTECTION_LABEL;
public final String TRACKING_PROTECTION_PROMPT_TITLE;
public final String TRACKING_PROTECTION_PROMPT_BUTTON;
public final String DNT_LABEL;
public final String COOKIES_LABEL;
public final String REMEMBER_LOGINS_LABEL;
@ -327,6 +329,8 @@ public class StringHelper {
// Privacy
TRACKING_PROTECTION_LABEL = res.getString(R.string.pref_tracking_protection_title);
TRACKING_PROTECTION_PROMPT_TITLE = res.getString(R.string.tracking_protection_prompt_title);
TRACKING_PROTECTION_PROMPT_BUTTON = res.getString(R.string.tracking_protection_prompt_action_button);
DNT_LABEL = res.getString(R.string.pref_donottrack_title);
COOKIES_LABEL = res.getString(R.string.pref_cookies_menu);
REMEMBER_LOGINS_LABEL = res.getString(R.string.pref_remember_signons);

Просмотреть файл

@ -1,709 +0,0 @@
/* 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/. */
"use strict";
const { Cu } = require("chrome");
const { DebuggerServer } = require("devtools/server/main");
const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
const Debugger = require("Debugger");
const { GeneratedLocation, getOffsetColumn } = require("devtools/server/actors/common");
const promise = require("promise");
Cu.import("resource://gre/modules/Task.jsm");
// TODO bug 943125: remove this polyfill and use Debugger.Frame.prototype.depth
// once it is implemented.
function getFrameDepth(frame) {
if (typeof(frame.depth) != "number") {
if (!frame.older) {
frame.depth = 0;
} else {
frame.depth = getFrameDepth(frame.older) + 1;
}
}
return frame.depth;
}
const { setTimeout } = require("sdk/timers");
/**
* The number of milliseconds we should buffer frame enter/exit packets before
* sending.
*/
const BUFFER_SEND_DELAY = 50;
/**
* The maximum number of arguments we will send for any single function call.
*/
const MAX_ARGUMENTS = 3;
/**
* The maximum number of an object's properties we will serialize.
*/
const MAX_PROPERTIES = 3;
/**
* The complete set of trace types supported.
*/
const TRACE_TYPES = new Set([
"time",
"return",
"throw",
"yield",
"name",
"location",
"hitCount",
"callsite",
"parameterNames",
"arguments",
"depth"
]);
/**
* Creates a TracerActor. TracerActor provides a stream of function
* call/return packets to a remote client gathering a full trace.
*/
function TracerActor(aConn, aParent)
{
this._dbg = null;
this._parent = aParent;
this._attached = false;
this._activeTraces = new MapStack();
this._totalTraces = 0;
this._startTime = 0;
this._sequence = 0;
this._bufferSendTimer = null;
this._buffer = [];
this._hitCounts = new WeakMap();
this._packetScheduler = new JobScheduler();
// Keep track of how many different trace requests have requested what kind of
// tracing info. This way we can minimize the amount of data we are collecting
// at any given time.
this._requestsForTraceType = Object.create(null);
for (let type of TRACE_TYPES) {
this._requestsForTraceType[type] = 0;
}
this.onEnterFrame = this.onEnterFrame.bind(this);
this.onExitFrame = this.onExitFrame.bind(this);
}
TracerActor.prototype = {
actorPrefix: "trace",
get attached() { return this._attached; },
get idle() { return this._attached && this._activeTraces.size === 0; },
get tracing() { return this._attached && this._activeTraces.size > 0; },
get dbg() {
if (!this._dbg) {
this._dbg = this._parent.makeDebugger();
}
return this._dbg;
},
/**
* Buffer traces and only send them every BUFFER_SEND_DELAY milliseconds.
*/
_send: function(aPacket) {
this._buffer.push(aPacket);
if (this._bufferSendTimer === null) {
this._bufferSendTimer = setTimeout(() => {
this.conn.send({
from: this.actorID,
type: "traces",
traces: this._buffer.splice(0, this._buffer.length)
});
this._bufferSendTimer = null;
}, BUFFER_SEND_DELAY);
}
},
/**
* Handle a protocol request to attach to the trace actor.
*
* @param aRequest object
* The protocol request object.
*/
onAttach: function(aRequest) {
if (this.attached) {
return {
error: "wrongState",
message: "Already attached to a client"
};
}
this.dbg.addDebuggees();
this._attached = true;
return {
type: "attached",
traceTypes: Object.keys(this._requestsForTraceType)
.filter(k => !!this._requestsForTraceType[k])
};
},
/**
* Handle a protocol request to detach from the trace actor.
*
* @param aRequest object
* The protocol request object.
*/
onDetach: function() {
while (this.tracing) {
this.onStopTrace();
}
this._dbg = null;
this._attached = false;
return {
type: "detached"
};
},
/**
* Handle a protocol request to start a new trace.
*
* @param aRequest object
* The protocol request object.
*/
onStartTrace: function(aRequest) {
for (let traceType of aRequest.trace) {
if (!TRACE_TYPES.has(traceType)) {
return {
error: "badParameterType",
message: "No such trace type: " + traceType
};
}
}
if (this.idle) {
this.dbg.onEnterFrame = this.onEnterFrame;
this.dbg.enabled = true;
this._sequence = 0;
this._startTime = Date.now();
}
// Start recording all requested trace types.
for (let traceType of aRequest.trace) {
this._requestsForTraceType[traceType]++;
}
this._totalTraces++;
let name = aRequest.name || "Trace " + this._totalTraces;
this._activeTraces.push(name, aRequest.trace);
return { type: "startedTrace", why: "requested", name: name };
},
/**
* Handle a protocol request to end a trace.
*
* @param aRequest object
* The protocol request object.
*/
onStopTrace: function(aRequest) {
if (!this.tracing) {
return {
error: "wrongState",
message: "No active traces"
};
}
let stoppedTraceTypes, name;
if (aRequest && aRequest.name) {
name = aRequest.name;
if (!this._activeTraces.has(name)) {
return {
error: "noSuchTrace",
message: "No active trace with name: " + name
};
}
stoppedTraceTypes = this._activeTraces.delete(name);
} else {
name = this._activeTraces.peekKey();
stoppedTraceTypes = this._activeTraces.pop();
}
for (let traceType of stoppedTraceTypes) {
this._requestsForTraceType[traceType]--;
}
// Clear hit counts if no trace is requesting them.
if (!this._requestsForTraceType.hitCount) {
this._hitCounts.clear();
}
if (this.idle) {
this._dbg.onEnterFrame = undefined;
this.dbg.enabled = false;
}
return {
type: "stoppedTrace",
why: "requested",
name
};
},
// JS Debugger API hooks.
/**
* Called by the engine when a frame is entered. Sends an unsolicited packet
* to the client carrying requested trace information.
*
* @param aFrame Debugger.Frame
* The stack frame that was entered.
*/
onEnterFrame: function(aFrame) {
Task.spawn(function*() {
// This function might request original (i.e. source-mapped) location,
// which is asynchronous. We need to ensure that packets are sent out
// in the correct order.
let runInOrder = this._packetScheduler.schedule();
let packet = {
type: "enteredFrame",
sequence: this._sequence++
};
if (this._requestsForTraceType.hitCount) {
if (aFrame.script) {
// Increment hit count.
let previousHitCount = this._hitCounts.get(aFrame.script) || 0;
this._hitCounts.set(aFrame.script, previousHitCount + 1);
packet.hitCount = this._hitCounts.get(aFrame.script);
}
}
if (this._parent.threadActor && aFrame.script) {
packet.blackBoxed = this._parent.threadActor.sources.isBlackBoxed(aFrame.script.url);
} else {
packet.blackBoxed = false;
}
if (this._requestsForTraceType.callsite) {
if (aFrame.older && aFrame.older.script) {
let older = aFrame.older;
packet.callsite = {
url: older.script.url,
line: older.script.getOffsetLine(older.offset),
column: getOffsetColumn(older.offset, older.script)
};
}
}
if (this._requestsForTraceType.time) {
packet.time = Date.now() - this._startTime;
}
if (this._requestsForTraceType.parameterNames && aFrame.callee) {
packet.parameterNames = aFrame.callee.parameterNames;
}
if (this._requestsForTraceType.arguments && aFrame.arguments) {
packet.arguments = [];
let i = 0;
for (let arg of aFrame.arguments) {
if (i++ > MAX_ARGUMENTS) {
break;
}
packet.arguments.push(createValueSnapshot(arg, true));
}
}
if (this._requestsForTraceType.depth) {
packet.depth = getFrameDepth(aFrame);
}
const onExitFrame = this.onExitFrame;
aFrame.onPop = function (aCompletion) {
onExitFrame(this, aCompletion);
};
// Async work is done below that doesn't depend on the frame
// being live
let name = aFrame.callee
? aFrame.callee.displayName || "(anonymous function)"
: "(" + aFrame.type + ")";
let sourceMappedLocation;
if (this._requestsForTraceType.name || this._requestsForTraceType.location) {
if (aFrame.script) {
let sources = this._parent.threadActor.sources;
sourceMappedLocation = yield sources.getOriginalLocation(new GeneratedLocation(
sources.createNonSourceMappedActor(aFrame.script.source),
aFrame.script.startLine,
// We should return the location of the start of the script, but
// Debugger.Script does not provide complete start locations (bug
// 901138). Instead, return the current offset (the location of the
// first statement in the function).
getOffsetColumn(aFrame.offset, aFrame.script)
));
}
}
if (this._requestsForTraceType.name) {
if (sourceMappedLocation && sourceMappedLocation.originalName) {
packet.name = sourceMappedLocation.originalName;
} else {
packet.name = name;
}
packet.name = name;
}
if (this._requestsForTraceType.location) {
if (sourceMappedLocation) {
// Don't copy sourceMappedLocation directly because it
// contains a reference to the source actor
packet.location = {
url: sourceMappedLocation.originalUrl,
line: sourceMappedLocation.originalLine,
column: sourceMappedLocation.originalColumn
};
}
}
runInOrder(() => this._send(packet));
}.bind(this));
},
/**
* Called by the engine when a frame is exited. Sends an unsolicited packet to
* the client carrying requested trace information.
*
* @param Debugger.Frame aFrame
* The Debugger.Frame that was just exited.
* @param aCompletion object
* The debugger completion value for the frame.
*/
onExitFrame: function(aFrame, aCompletion) {
let runInOrder = this._packetScheduler.schedule();
let packet = {
type: "exitedFrame",
sequence: this._sequence++,
};
if (!aCompletion) {
packet.why = "terminated";
} else if (aCompletion.hasOwnProperty("return")) {
packet.why = "return";
} else if (aCompletion.hasOwnProperty("yield")) {
packet.why = "yield";
} else {
packet.why = "throw";
}
if (this._requestsForTraceType.time) {
packet.time = Date.now() - this._startTime;
}
if (this._requestsForTraceType.depth) {
packet.depth = getFrameDepth(aFrame);
}
if (aCompletion) {
if (this._requestsForTraceType.return && "return" in aCompletion) {
packet.return = createValueSnapshot(aCompletion.return, true);
}
else if (this._requestsForTraceType.throw && "throw" in aCompletion) {
packet.throw = createValueSnapshot(aCompletion.throw, true);
}
else if (this._requestsForTraceType.yield && "yield" in aCompletion) {
packet.yield = createValueSnapshot(aCompletion.yield, true);
}
}
runInOrder(() => this._send(packet));
}
};
/**
* The request types this actor can handle.
*/
TracerActor.prototype.requestTypes = {
"attach": TracerActor.prototype.onAttach,
"detach": TracerActor.prototype.onDetach,
"startTrace": TracerActor.prototype.onStartTrace,
"stopTrace": TracerActor.prototype.onStopTrace
};
exports.TracerActor = TracerActor;
/**
* MapStack is a collection of key/value pairs with stack ordering,
* where keys are strings and values are any JS value. In addition to
* the push and pop stack operations, supports a "delete" operation,
* which removes the value associated with a given key from any
* location in the stack.
*/
function MapStack()
{
// Essentially a MapStack is just sugar-coating around a standard JS
// object, plus the _stack array to track ordering.
this._stack = [];
this._map = Object.create(null);
}
MapStack.prototype = {
get size() { return this._stack.length; },
/**
* Return the key for the value on the top of the stack, or
* undefined if the stack is empty.
*/
peekKey: function() {
return this._stack[this.size - 1];
},
/**
* Return true iff a value has been associated with the given key.
*
* @param aKey string
* The key whose presence is to be tested.
*/
has: function(aKey) {
return Object.prototype.hasOwnProperty.call(this._map, aKey);
},
/**
* Return the value associated with the given key, or undefined if
* no value is associated with the key.
*
* @param aKey string
* The key whose associated value is to be returned.
*/
get: function(aKey) {
return this._map[aKey] || undefined;
},
/**
* Push a new value onto the stack. If another value with the same
* key is already on the stack, it will be removed before the new
* value is pushed onto the top of the stack.
*
* @param aKey string
* The key of the object to push onto the stack.
*
* @param aValue
* The value to push onto the stack.
*/
push: function(aKey, aValue) {
this.delete(aKey);
this._stack.push(aKey);
this._map[aKey] = aValue;
},
/**
* Remove the value from the top of the stack and return it.
* Returns undefined if the stack is empty.
*/
pop: function() {
let key = this.peekKey();
let value = this.get(key);
this._stack.pop();
delete this._map[key];
return value;
},
/**
* Remove the value associated with the given key from the stack and
* return it. Returns undefined if no value is associated with the
* given key.
*
* @param aKey string
* The key for the value to remove from the stack.
*/
delete: function(aKey) {
let value = this.get(aKey);
if (this.has(aKey)) {
let keyIndex = this._stack.lastIndexOf(aKey);
this._stack.splice(keyIndex, 1);
delete this._map[aKey];
}
return value;
}
};
// Serialization helper functions. Largely copied from script.js and modified
// for use in serialization rather than object actor requests.
/**
* Create a grip for the given debuggee value.
*
* @param aValue Debugger.Object|primitive
* The value to describe with the created grip.
*
* @param aDetailed boolean
* If true, capture slightly more detailed information, like some
* properties on an object.
*
* @return Object
* A primitive value or a snapshot of an object.
*/
function createValueSnapshot(aValue, aDetailed=false) {
switch (typeof aValue) {
case "boolean":
return aValue;
case "string":
if (aValue.length >= DebuggerServer.LONG_STRING_LENGTH) {
return {
type: "longString",
initial: aValue.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH),
length: aValue.length
};
}
return aValue;
case "number":
if (aValue === Infinity) {
return { type: "Infinity" };
} else if (aValue === -Infinity) {
return { type: "-Infinity" };
} else if (Number.isNaN(aValue)) {
return { type: "NaN" };
} else if (!aValue && 1 / aValue === -Infinity) {
return { type: "-0" };
}
return aValue;
case "undefined":
return { type: "undefined" };
case "object":
if (aValue === null) {
return { type: "null" };
}
return aDetailed
? detailedObjectSnapshot(aValue)
: objectSnapshot(aValue);
default:
DevToolsUtils.reportException("TracerActor",
new Error("Failed to provide a grip for: " + aValue));
return null;
}
}
/**
* Create a very minimal snapshot of the given debuggee object.
*
* @param aObject Debugger.Object
* The object to describe with the created grip.
*/
function objectSnapshot(aObject) {
return {
"type": "object",
"class": aObject.class,
};
}
/**
* Create a (slightly more) detailed snapshot of the given debuggee object.
*
* @param aObject Debugger.Object
* The object to describe with the created descriptor.
*/
function detailedObjectSnapshot(aObject) {
let desc = objectSnapshot(aObject);
let ownProperties = desc.ownProperties = Object.create(null);
if (aObject.class == "DeadObject") {
return desc;
}
let i = 0;
for (let name of aObject.getOwnPropertyNames()) {
if (i++ > MAX_PROPERTIES) {
break;
}
let desc = propertySnapshot(name, aObject);
if (desc) {
ownProperties[name] = desc;
}
}
return desc;
}
/**
* A helper method that creates a snapshot of the object's |aName| property.
*
* @param aName string
* The property of which the snapshot is taken.
*
* @param aObject Debugger.Object
* The object whose property the snapshot is taken of.
*
* @return Object
* The snapshot of the property.
*/
function propertySnapshot(aName, aObject) {
let desc;
try {
desc = aObject.getOwnPropertyDescriptor(aName);
} catch (e) {
// Calling getOwnPropertyDescriptor on wrapped native prototypes is not
// allowed (bug 560072). Inform the user with a bogus, but hopefully
// explanatory, descriptor.
return {
configurable: false,
writable: false,
enumerable: false,
value: e.name
};
}
// Only create descriptors for simple values. We skip objects and properties
// that have getters and setters; ain't nobody got time for that!
if (!desc
|| typeof desc.value == "object" && desc.value !== null
|| !("value" in desc)) {
return undefined;
}
return {
configurable: desc.configurable,
enumerable: desc.enumerable,
writable: desc.writable,
value: createValueSnapshot(desc.value)
};
}
/**
* Scheduler for jobs to be run in the same order as in which they were
* scheduled.
*/
function JobScheduler()
{
this._lastScheduledJob = promise.resolve();
}
JobScheduler.prototype = {
/**
* Schedule a new job.
*
* @return A function that can be called anytime with a job as a parameter.
* Job won't be run until all previously scheduled jobs were run.
*/
schedule: function() {
let deferred = promise.defer();
let previousJob = this._lastScheduledJob;
this._lastScheduledJob = deferred.promise;
return function runInOrder(aJob) {
previousJob.then(() => {
aJob();
deferred.resolve();
});
};
}
};
exports.JobScheduler = JobScheduler;

Просмотреть файл

@ -488,11 +488,6 @@ var DebuggerServer = {
constructor: "GcliActor",
type: { tab: true }
});
this.registerModule("devtools/server/actors/tracer", {
prefix: "trace",
constructor: "TracerActor",
type: { tab: true }
});
this.registerModule("devtools/server/actors/memory", {
prefix: "memory",
constructor: "MemoryActor",

Просмотреть файл

@ -88,7 +88,6 @@ EXTRA_JS_MODULES.devtools.server.actors += [
'actors/styles.js',
'actors/stylesheets.js',
'actors/timeline.js',
'actors/tracer.js',
'actors/webapps.js',
'actors/webaudio.js',
'actors/webbrowser.js',

Просмотреть файл

@ -315,18 +315,6 @@ function startTestDebuggerServer(title, server = DebuggerServer) {
return connect(client).then(() => client);
}
function initTestTracerServer(aServer = DebuggerServer)
{
aServer.registerModule("xpcshell-test/testactors");
aServer.registerModule("devtools/server/actors/tracer", {
prefix: "trace",
constructor: "TracerActor",
type: { global: true, tab: true }
});
// Allow incoming connections.
aServer.init(function () { return true; });
}
function finishClient(aClient)
{
aClient.close(function() {

Просмотреть файл

@ -15,7 +15,7 @@ Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
function run_test()
{
initTestTracerServer();
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-breakpoints");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {

Просмотреть файл

@ -1,128 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Check that tracer links to a correct (source-mapped) location.
*/
var gDebuggee;
var gClient;
var gTraceClient;
var gThreadClient;
Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
function run_test() {
initTestTracerServer();
gDebuggee = addTestGlobal("test-source-map");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestThread(gClient, "test-source-map",
function(aResponse, aTabClient, aThreadClient, aTabResponse) {
gThreadClient = aThreadClient;
gThreadClient.resume(function (aResponse) {
gClient.attachTracer(aTabResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
testSourceMaps();
});
});
});
});
do_test_pending();
}
const testSourceMaps = Task.async(function* () {
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
checkTrace(t);
}
tracesStopped.resolve();
});
yield startTrace();
yield executeOnNextTickAndWaitForPause(evalCode, gClient);
yield resume(gThreadClient);
yield tracesStopped.promise;
yield stopTrace();
finishClient(gClient);
});
function startTrace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name", "location"], null, function() { deferred.resolve(); });
return deferred.promise;
}
function evalCode()
{
let { code, map } = (new SourceNode(null, null, null, [
new SourceNode(10, 0, "a.js", "function fnOuter() {\n"),
new SourceNode(20, 0, "a.js", " fnInner();\n"),
new SourceNode(30, 0, "a.js", "}\n"),
new SourceNode(10, 0, "b.js", "function fnInner() {\n"),
new SourceNode(20, 0, "b.js", " [1].forEach(function noop() {\n"),
new SourceNode(30, 0, "b.js", " debugger;\n"),
new SourceNode(40, 0, "b.js", " });\n"),
new SourceNode(50, 0, "b.js", "}\n"),
new SourceNode(60, 0, "b.js", "fnOuter();\n"),
])).toStringWithSourceMap({
file: "abc.js",
sourceRoot: "http://example.com/"
});
code += "//# sourceMappingURL=data:text/json," + map.toString();
Components.utils.evalInSandbox(code, gDebuggee, "1.8", "http://example.com/abc.js", 1);
}
function checkTrace({ type, sequence, depth, name, location, blackBoxed })
{
switch(sequence) {
// First packet comes from evalInSandbox in evalCode.
case 0:
do_check_eq(name, "(global)");
break;
case 1:
do_check_eq(name, "fnOuter");
do_check_eq(location.url, "http://example.com/a.js");
do_check_eq(location.line, 10);
break;
case 2:
do_check_eq(name, "fnInner");
do_check_eq(location.url, "http://example.com/b.js");
do_check_eq(location.line, 10);
break;
case 3:
do_check_eq(name, "noop");
do_check_eq(location.url, "http://example.com/b.js");
do_check_eq(location.line, 20);
break;
case 4:
case 5:
case 6:
case 7:
do_check_eq(type, "exitedFrame");
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}
function stopTrace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}

Просмотреть файл

@ -1,159 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Create 2 sources, A and B, B is source-mapped. When calling functions A->B->A,
* verify that traces are in a proper order.
*/
var gDebuggee;
var gClient;
var gTraceClient;
var gThreadClient;
Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestThread(gClient, "test-tracer-actor",
function(aResponse, aTabClient, aThreadClient, aTabResponse) {
gThreadClient = aThreadClient;
gThreadClient.resume(function (aResponse) {
gClient.attachTracer(aTabResponse.traceActor,
function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
testTraces();
});
});
});
});
do_test_pending();
}
const testTraces = Task.async(function* () {
// Read traces
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
}
tracesStopped.resolve();
});
yield startTrace();
yield executeOnNextTickAndWaitForPause(evalSourceMapSetup, gClient);
yield resume(gThreadClient);
evalSetup();
evalTestCode();
yield tracesStopped.promise;
yield stopTrace();
finishClient(gClient);
});
function startTrace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name", "location"], null,
function() { deferred.resolve(); });
return deferred.promise;
}
function evalSourceMapSetup() {
let { code, map } = (new SourceNode(null, null, null, [
new SourceNode(1, 0, "b_original.js", "" + function fnSourceMapped() {
fnInner();
} + "\n debugger;")
])).toStringWithSourceMap({
file: "b.js",
sourceRoot: "http://example.com/"
});
code += "//# sourceMappingURL=data:text/json," + map.toString();
Components.utils.evalInSandbox(code, gDebuggee, "1.8", "http://example.com/b.js");
}
function evalSetup()
{
Components.utils.evalInSandbox(
"" + function fnOuter() {
fnSourceMapped();
} + "\n" +
"" + function fnInner() {
[1].forEach(function noop() {});
},
gDebuggee,
"1.8",
"http://example.com/a.js",
1
);
}
function evalTestCode()
{
Components.utils.evalInSandbox(
"fnOuter();",
gDebuggee,
"1.8",
"http://example.com/a.js",
1
);
}
function stopTrace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace({ type, sequence, depth, name, location, blackBoxed })
{
switch(sequence) {
// First two packets come from evalInSandbox in evalSetup
// The third packet comes from evalInSandbox in evalTestCode
case 0:
case 2:
case 4:
do_check_eq(name, "(global)");
do_check_eq(type, "enteredFrame");
break;
case 5:
do_check_eq(name, "fnOuter");
break;
case 6:
do_check_eq(name, "fnSourceMapped");
break;
case 7:
do_check_eq(name, "fnInner");
break;
case 8:
do_check_eq(name, "noop");
break;
case 1: // evalInSandbox
case 3: // evalInSandbox
case 9: // noop
case 10: // fnInner
case 11: // fnSourceMapped
case 12: // fnOuter
case 13: // evalInSandbox
do_check_eq(type, "exitedFrame");
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}

Просмотреть файл

@ -1,54 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that TraceActor is available and returns correct responses to
* startTrace and stopTrace requests.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
do_check_true(!!aResponse.traceActor, "TraceActor should be visible in tab");
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_start_stop_response();
});
});
});
do_test_pending();
}
function test_start_stop_response()
{
do_check_true(!gTraceClient.tracing, "TraceClient should start in idle state");
gTraceClient.startTrace([], null, function(aResponse) {
do_check_true(!!gTraceClient.tracing, "TraceClient should be in tracing state");
do_check_true(!aResponse.error,
'startTrace should not respond with error: ' + aResponse.error);
do_check_eq(aResponse.type, "startedTrace",
'startTrace response should have "type":"startedTrace" property');
do_check_eq(aResponse.why, "requested",
'startTrace response should have "why":"requested" property');
gTraceClient.stopTrace(null, function(aResponse) {
do_check_true(!gTraceClient.tracing, "TraceClient should be in idle state");
do_check_true(!aResponse.error,
'stopTrace should not respond with error: ' + aResponse.error);
do_check_eq(aResponse.type, "stoppedTrace",
'stopTrace response should have "type":"stoppedTrace" property');
do_check_eq(aResponse.why, "requested",
'stopTrace response should have "why":"requested" property');
finishClient(gClient);
});
});
}

Просмотреть файл

@ -1,82 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests re-entrant startTrace/stopTrace calls on TraceActor. Tests
* that stopTrace ends the most recently started trace when not
* provided with a name. Tests that starting a trace with the same
* name twice results in only one trace being collected for that name.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_start_stop_reentrant();
});
});
});
do_test_pending();
}
function test_start_stop_reentrant()
{
do_check_true(!gTraceClient.tracing, "TraceClient should start in idle state");
start_named_trace("foo")
.then(start_named_trace.bind(null, "foo"))
.then(start_named_trace.bind(null, "bar"))
.then(start_named_trace.bind(null, "baz"))
.then(stop_trace.bind(null, "bar", "bar"))
.then(stop_trace.bind(null, null, "baz"))
.then(stop_trace.bind(null, null, "foo"))
.then(function() {
do_check_true(!gTraceClient.tracing, "TraceClient should finish in idle state");
finishClient(gClient);
});
}
function start_named_trace(aName)
{
let deferred = promise.defer();
gTraceClient.startTrace([], aName, function(aResponse) {
do_check_true(!!gTraceClient.tracing, "TraceClient should be in tracing state");
do_check_true(!aResponse.error,
'startTrace should not respond with error: ' + aResponse.error);
do_check_eq(aResponse.type, "startedTrace",
'startTrace response should have "type":"startedTrace" property');
do_check_eq(aResponse.why, "requested",
'startTrace response should have "why":"requested" property');
do_check_eq(aResponse.name, aName,
'startTrace response should have the given name');
deferred.resolve();
});
return deferred.promise;
}
function stop_trace(aName, aExpectedName)
{
let deferred = promise.defer();
gTraceClient.stopTrace(aName, function(aResponse) {
do_check_true(!aResponse.error,
'stopTrace should not respond with error: ' + aResponse.error);
do_check_true(aResponse.type === "stoppedTrace",
'stopTrace response should have "type":"stoppedTrace" property');
do_check_true(aResponse.why === "requested",
'stopTrace response should have "why":"requested" property');
do_check_true(aResponse.name === aExpectedName,
'stopTrace response should have name "' + aExpectedName
+ '", but had "' + aResponse.name + '"');
deferred.resolve();
});
return deferred.promise;
}

Просмотреть файл

@ -1,67 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that automatically generated names for traces are unique.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_unique_generated_trace_names();
});
});
});
do_test_pending();
}
function test_unique_generated_trace_names()
{
let deferred = promise.defer();
deferred.resolve([]);
let p = deferred.promise, traces = 50;
for (let i = 0; i < traces; i++)
p = p.then(start_trace);
for (let i = 0; i < traces; i++)
p = p.then(stop_trace);
p = p.then(function() {
finishClient(gClient);
});
}
function start_trace(aTraceNames)
{
let deferred = promise.defer();
gTraceClient.startTrace([], null, function(aResponse) {
let hasDuplicates = aTraceNames.some(name => name === aResponse.name);
do_check_true(!hasDuplicates, "Generated trace names should be unique");
aTraceNames.push(aResponse.name);
deferred.resolve(aTraceNames);
});
return deferred.promise;
}
function stop_trace(aTraceNames)
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function(aResponse) {
do_check_eq(aTraceNames.pop(), aResponse.name,
"Stopped trace should be most recently started trace");
let hasDuplicates = aTraceNames.some(name => name === aResponse.name);
do_check_true(!hasDuplicates, "Generated trace names should be unique");
deferred.resolve(aTraceNames);
});
return deferred.promise;
}

Просмотреть файл

@ -1,111 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that enteredFrame packets are sent on frame entry and
* exitedFrame packets are sent on frame exit. Tests that the "name"
* trace type works for function declarations.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_enter_exit_frame();
});
});
});
do_test_pending();
}
function test_enter_exit_frame()
{
let tracesSeen = 0;
let traceNames = [];
let traceStopped = promise.defer();
gClient.addListener("traces", function onTraces(aEvent, { traces }) {
for (let t of traces) {
tracesSeen++;
if (t.type == "enteredFrame") {
do_check_eq(t.type, "enteredFrame",
'enteredFrame response should have type "enteredFrame"');
do_check_eq(typeof t.sequence, "number",
'enteredFrame response should have sequence number');
do_check_true(!isNaN(t.sequence),
'enteredFrame sequence should be a number');
do_check_eq(typeof t.name, "string",
'enteredFrame response should have function name');
traceNames[t.sequence] = t.name;
} else {
do_check_eq(t.type, "exitedFrame",
'exitedFrame response should have type "exitedFrame"');
do_check_eq(typeof t.sequence, "number",
'exitedFrame response should have sequence number');
do_check_true(!isNaN(t.sequence),
'exitedFrame sequence should be a number');
}
if (tracesSeen == 10) {
gClient.removeListener("traces", onTraces);
traceStopped.resolve();
}
}
});
start_trace()
.then(eval_code)
.then(() => traceStopped.promise)
.then(stop_trace)
.then(function() {
do_check_eq(traceNames[2], "baz",
'Should have entered "baz" frame in third packet');
do_check_eq(traceNames[3], "bar",
'Should have entered "bar" frame in fourth packet');
do_check_eq(traceNames[4], "foo",
'Should have entered "foo" frame in fifth packet');
finishClient(gClient);
})
.then(null, e => DevToolsUtils.reportException("test_trace_actor-04.js",
e));
}
function start_trace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["name"], null, function() { deferred.resolve(); });
return deferred.promise;
}
function eval_code()
{
gDebuggee.eval("(" + function() {
function foo() {
return;
}
function bar() {
return foo();
}
function baz() {
return bar();
}
baz();
} + ")()");
}
function stop_trace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}

Просмотреть файл

@ -1,138 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Simple tests for "location", "callsite", "time", "parameterNames",
* "arguments", and "return" trace types.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_enter_exit_frame();
});
});
});
do_test_pending();
}
function check_number(value)
{
do_check_eq(typeof value, "number");
do_check_true(!isNaN(value));
}
function check_location(actual, expected)
{
do_check_eq(typeof actual, "object");
check_number(actual.line);
check_number(actual.column);
do_check_eq(actual.url, expected.url);
do_check_eq(actual.line, expected.line);
do_check_eq(actual.column, expected.column);
}
function test_enter_exit_frame()
{
let traces = [];
let traceStopped = promise.defer();
gClient.addListener("traces", function(aEvent, aPacket) {
for (let t of aPacket.traces) {
if (t.type == "enteredFrame") {
do_check_eq(typeof t.name, "string");
do_check_eq(typeof t.location, "object");
check_number(t.sequence);
check_number(t.time);
check_number(t.location.line);
check_number(t.location.column);
if (t.callsite) {
check_number(t.callsite.line);
check_number(t.callsite.column);
}
} else {
check_number(t.sequence);
check_number(t.time);
}
traces[t.sequence] = t;
if (traces.length === 4) {
traceStopped.resolve();
}
}
});
start_trace()
.then(eval_code)
.then(() => traceStopped.promise)
.then(stop_trace)
.then(function() {
let url = getFileUrl("tracerlocations.js");
check_location(traces[0].location, { url: url, line: 1, column: 0 });
do_check_eq(traces[1].name, "foo");
// XXX: foo's definition is at tracerlocations.js:3:0, but Debugger.Script
// does not provide complete definition locations (bug 901138). Therefore,
// we use the first statement in the function (tracerlocations.js:4:2) for
// a column approximation.
check_location(traces[1].location, { url: url, line: 3, column: 2 });
check_location(traces[1].callsite, { url: url, line: 8, column: 0 });
do_check_eq(typeof traces[1].parameterNames, "object");
do_check_eq(traces[1].parameterNames.length, 1);
do_check_eq(traces[1].parameterNames[0], "x");
do_check_eq(typeof traces[1].arguments, "object");
do_check_true(Array.isArray(traces[1].arguments));
do_check_eq(traces[1].arguments.length, 1);
do_check_eq(traces[1].arguments[0], 42);
do_check_eq(typeof traces[2].return, "string");
do_check_eq(traces[2].return, "bar");
finishClient(gClient);
}, error => {
DevToolsUtils.reportException("test_trace_actor-05.js", error);
do_check_true(false);
});
}
function start_trace()
{
let deferred = promise.defer();
gTraceClient.startTrace(
["name", "location", "callsite", "time", "parameterNames", "arguments", "return"],
null,
function() { deferred.resolve(); });
return deferred.promise;
}
function eval_code()
{
let code = readFile("tracerlocations.js");
Components.utils.evalInSandbox(code, gDebuggee, "1.8",
getFileUrl("tracerlocations.js"), 1);
}
function stop_trace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}

Просмотреть файл

@ -1,234 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that values are correctly serialized and sent in enteredFrame
* and exitedFrame packets.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_enter_exit_frame();
});
});
});
do_test_pending();
}
function test_enter_exit_frame()
{
const traceStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
if (t.sequence === 27) {
traceStopped.resolve();
}
}
});
start_trace()
.then(eval_code)
.then(() => traceStopped.promise)
.then(stop_trace)
.then(function() {
finishClient(gClient);
});
}
function start_trace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["arguments", "return"], null, function() { deferred.resolve(); });
return deferred.promise;
}
function eval_code()
{
gDebuggee.eval("(" + function() {
function identity(x) {
return x;
}
let circular = {};
circular.self = circular;
// Make sure there is only 3 properties per object because that is the value
// of MAX_PROPERTIES in the server.
let obj = {
num: 0,
str: "foo",
bool: false,
};
let obj2 = {
undef: undefined,
nil: null,
inf: Infinity
};
let obj3 = {
ninf: -Infinity,
nan: NaN,
nzero: -0
};
let obj4 = {
obj: circular,
arr: [1,2,3,4,5]
};
identity();
identity(0);
identity("");
identity(false);
identity(undefined);
identity(null);
identity(Infinity);
identity(-Infinity);
identity(NaN);
identity(-0);
identity(obj);
identity(obj2);
identity(obj3);
identity(obj4);
} + ")()");
}
function stop_trace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace(aTrace)
{
let value = (aTrace.type === "enteredFrame" && aTrace.arguments)
? aTrace.arguments[0]
: aTrace.return;
switch(aTrace.sequence) {
case 2:
do_check_eq(typeof aTrace.arguments, "object");
do_check_eq(aTrace.arguments.length, 0);
break;
case 3:
check_value(value, "object", "undefined");
break;
case 4:
case 5:
check_value(value, "number", 0);
break;
case 6:
case 7:
check_value(value, "string", "");
break;
case 8:
case 9:
check_value(value, "boolean", false);
break;
case 10:
case 11:
check_value(value, "object", "undefined");
break;
case 12:
case 13:
check_value(value, "object", "null");
break;
case 14:
case 15:
check_value(value, "object", "Infinity");
break;
case 16:
case 17:
check_value(value, "object", "-Infinity");
break;
case 18:
case 19:
check_value(value, "object", "NaN");
break;
case 20:
case 21:
check_value(value, "object", "-0");
break;
case 22:
case 23:
check_obj(aTrace.type, value);
break;
case 24:
case 25:
check_obj2(aTrace.type, value);
break;
case 26:
case 27:
check_obj3(aTrace.type, value);
break;
case 28:
case 29:
check_obj4(aTrace.type, value);
break;
}
}
function check_value(aActual, aExpectedType, aExpectedValue)
{
do_check_eq(typeof aActual, aExpectedType);
do_check_eq(aExpectedType === "object" ? aActual.type : aActual, aExpectedValue);
}
function check_obj(aType, aObj) {
do_check_eq(typeof aObj, "object");
do_check_eq(typeof aObj.ownProperties, "object");
do_check_eq(typeof aObj.ownProperties.num, "object");
do_check_eq(aObj.ownProperties.num.value, 0);
do_check_eq(typeof aObj.ownProperties.str, "object");
do_check_eq(aObj.ownProperties.str.value, "foo");
do_check_eq(typeof aObj.ownProperties.bool, "object");
do_check_eq(aObj.ownProperties.bool.value, false);
}
function check_obj2(aType, aObj) {
do_check_eq(typeof aObj.ownProperties.undef, "object");
do_check_eq(typeof aObj.ownProperties.undef.value, "object");
do_check_eq(aObj.ownProperties.undef.value.type, "undefined");
do_check_eq(typeof aObj.ownProperties.nil, "object");
do_check_eq(typeof aObj.ownProperties.nil.value, "object");
do_check_eq(aObj.ownProperties.nil.value.type, "null");
do_check_eq(typeof aObj.ownProperties.inf, "object");
do_check_eq(typeof aObj.ownProperties.inf.value, "object");
do_check_eq(aObj.ownProperties.inf.value.type, "Infinity");
}
function check_obj3(aType, aObj) {
do_check_eq(typeof aObj.ownProperties.ninf, "object");
do_check_eq(typeof aObj.ownProperties.ninf.value, "object");
do_check_eq(aObj.ownProperties.ninf.value.type, "-Infinity");
do_check_eq(typeof aObj.ownProperties.nan, "object");
do_check_eq(typeof aObj.ownProperties.nan.value, "object");
do_check_eq(aObj.ownProperties.nan.value.type, "NaN");
do_check_eq(typeof aObj.ownProperties.nzero, "object");
do_check_eq(typeof aObj.ownProperties.nzero.value, "object");
do_check_eq(aObj.ownProperties.nzero.value.type, "-0");
}
function check_obj4(aType, aObj) {
// Sub-objects aren't added.
do_check_eq(typeof aObj.ownProperties.obj, "undefined");
do_check_eq(typeof aObj.ownProperties.arr, "undefined");
}

Просмотреть файл

@ -1,100 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the exit frame packets get the correct `why` value.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_exit_frame_whys();
});
});
});
do_test_pending();
}
function test_exit_frame_whys()
{
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
if (t.type == "exitedFrame") {
check_trace(t);
}
}
});
start_trace()
.then(eval_code)
.then(stop_trace)
.then(function() {
finishClient(gClient);
}).then(null, error => {
do_check_true(false, "Should not get an error, got: " + error);
});
}
function start_trace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["name"], null, function() { deferred.resolve(); });
return deferred.promise;
}
function eval_code()
{
gDebuggee.eval("(" + function() {
function thrower() {
throw new Error();
}
function* yielder() {
yield 1;
}
function returner() {
return 1;
}
try {
thrower();
} catch(e) {}
// XXX bug 923729: Can't test yielding yet.
// for (let x of yielder()) {
// break;
// }
returner();
} + ")()");
}
function stop_trace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace(aEvent, { sequence, why })
{
switch(sequence) {
case 3:
do_check_eq(why, "throw");
break;
case 5:
do_check_eq(why, "return");
break;
}
}

Просмотреть файл

@ -1,123 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test the "depth" trace type.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_frame_depths();
});
});
});
do_test_pending();
}
function test_frame_depths()
{
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
}
tracesStopped.resolve();
});
start_trace()
.then(eval_code)
.then(() => tracesStopped.promise)
.then(stop_trace)
.then(function() {
finishClient(gClient);
}).then(null, error => {
do_check_true(false, "Should not get an error, got: " + error);
});
}
function start_trace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name"], null, function() { deferred.resolve(); });
return deferred.promise;
}
function eval_code()
{
gDebuggee.eval("(" + function iife() {
function first() {
second();
}
function second() {
third();
}
function third() {
}
first();
} + ")()");
}
function stop_trace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace({ sequence, depth, name })
{
switch(sequence) {
case 0:
do_check_eq(name, "(eval)");
// Fall through
case 9:
do_check_eq(depth, 0);
break;
case 1:
do_check_eq(name, "iife");
// Fall through
case 8:
do_check_eq(depth, 1);
break;
case 2:
do_check_eq(name, "first");
// Fall through
case 7:
do_check_eq(depth, 2);
break;
case 3:
do_check_eq(name, "second");
// Fall through
case 6:
do_check_eq(depth, 3);
break;
case 4:
do_check_eq(name, "third");
// Fall through
case 5:
do_check_eq(depth, 4);
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}

Просмотреть файл

@ -1,100 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that self-hosted functions aren't traced and don't add depth.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_frame_depths();
});
});
});
do_test_pending();
}
function test_frame_depths()
{
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
}
tracesStopped.resolve();
});
start_trace()
.then(eval_code)
.then(() => tracesStopped.promise)
.then(stop_trace)
.then(function() {
finishClient(gClient);
}).then(null, error => {
do_check_true(false, "Should not get an error, got: " + DevToolsUtils.safeErrorString(error));
});
}
function start_trace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name", "location"], null, function() { deferred.resolve(); });
return deferred.promise;
}
function eval_code()
{
gDebuggee.eval("(" + function iife() {
[1].forEach(function noop() {});
for (let x of [1]) {}
} + ")()");
}
function stop_trace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace({ sequence, depth, name, location })
{
if (location) {
do_check_true(location.url !== "self-hosted");
}
switch(sequence) {
case 0:
do_check_eq(name, "(eval)");
case 5:
do_check_eq(depth, 0);
break;
case 1:
do_check_eq(name, "iife");
case 4:
do_check_eq(depth, 1);
break;
case 2:
do_check_eq(name, "noop");
case 3:
do_check_eq(depth, 2);
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}

Просмотреть файл

@ -1,167 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Create 2 sources, A and B, B is black boxed. When calling functions A->B->A,
* verify that only traces from source B are black boxed.
*/
var gDebuggee;
var gClient;
var gTraceClient;
var gThreadClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestThread(gClient, "test-tracer-actor",
function(aResponse, aTabClient, aThreadClient, aTabResponse) {
gThreadClient = aThreadClient;
gThreadClient.resume(function (aResponse) {
gClient.attachTracer(aTabResponse.traceActor,
function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
testTraces();
});
});
});
});
do_test_pending();
}
const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
const SOURCE_URL = "http://example.com/source.js";
const testTraces = Task.async(function* () {
// Read traces
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
}
tracesStopped.resolve();
});
yield startTrace();
evalSetup();
// Blackbox source
const { sources } = yield getSources(gThreadClient);
let sourceClient = gThreadClient.source(
sources.filter(s => s.url == BLACK_BOXED_URL)[0]);
do_check_true(!sourceClient.isBlackBoxed,
"By default the source is not black boxed.");
yield blackBox(sourceClient);
do_check_true(sourceClient.isBlackBoxed);
evalTestCode();
yield tracesStopped.promise;
yield stopTrace();
finishClient(gClient);
});
function startTrace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name", "location"], null,
function() { deferred.resolve(); });
return deferred.promise;
}
function evalSetup()
{
Components.utils.evalInSandbox(
"" + function fnBlackBoxed(k) {
fnInner();
},
gDebuggee,
"1.8",
BLACK_BOXED_URL,
1
);
Components.utils.evalInSandbox(
"" + function fnOuter() {
fnBlackBoxed();
} + "\n" +
"" + function fnInner() {
[1].forEach(function noop() {});
},
gDebuggee,
"1.8",
SOURCE_URL,
1
);
}
function evalTestCode()
{
Components.utils.evalInSandbox(
"fnOuter();",
gDebuggee,
"1.8",
SOURCE_URL,
1
);
}
function stopTrace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace({ type, sequence, depth, name, location, blackBoxed })
{
switch(sequence) {
// First two packets come from evalInSandbox in evalSetup
// The third packet comes from evalInSandbox in evalTestCode
case 0:
case 2:
case 4:
do_check_eq(name, "(global)");
do_check_eq(type, "enteredFrame");
break;
case 5:
do_check_eq(blackBoxed, false);
do_check_eq(name, "fnOuter");
break;
case 6:
do_check_eq(blackBoxed, true);
do_check_eq(name, "fnBlackBoxed");
break;
case 7:
do_check_eq(blackBoxed, false);
do_check_eq(name, "fnInner");
break;
case 8:
do_check_eq(blackBoxed, false);
do_check_eq(name, "noop");
break;
case 1: // evalInSandbox
case 3: // evalInSandbox
case 9: // noop
case 10: // fnInner
case 11: // fnBlackBoxed
case 12: // fnOuter
case 13: // evalInSandbox
do_check_eq(type, "exitedFrame");
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}

Просмотреть файл

@ -1,124 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that hit counts from tracer count function frames correctly, even after
* restarting the trace.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_hit_counts();
});
});
});
do_test_pending();
}
function test_hit_counts()
{
start_trace()
.then(eval_code)
.then(listen_to_traces)
.then(stop_trace)
.then(start_trace) // Restart tracing.
.then(eval_code)
.then(listen_to_traces)
.then(stop_trace)
.then(function() {
finishClient(gClient);
}).then(null, error => {
do_check_true(false, "Should not get an error, got: " + DevToolsUtils.safeErrorString(error));
});
}
function listen_to_traces() {
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
}
tracesStopped.resolve();
});
return tracesStopped.promise;
}
function start_trace()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name", "location", "hitCount"], null, function() { deferred.resolve(); });
return deferred.promise;
}
function eval_code()
{
gDebuggee.eval("(" + function iife() {
[1, 2, 3].forEach(function noop() {
for (let x of [1]) {}
});
} + ")()");
}
function stop_trace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace({ type, sequence, depth, name, location, hitCount })
{
if (location) {
do_check_true(location.url !== "self-hosted");
}
switch(sequence) {
case 0:
do_check_eq(name, "(eval)");
do_check_eq(hitCount, 1);
break;
case 1:
do_check_eq(name, "iife");
do_check_eq(hitCount, 1);
break;
case 2:
do_check_eq(hitCount, 1);
do_check_eq(name, "noop");
break;
case 4:
do_check_eq(hitCount, 2);
do_check_eq(name, "noop");
break;
case 6:
do_check_eq(hitCount, 3);
do_check_eq(name, "noop");
break;
case 3:
case 5:
case 7:
case 8:
case 9:
do_check_eq(type, "exitedFrame");
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}

Просмотреть файл

@ -1,156 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that hit counts are correct even if we start tracing other things first
* and then start tracing hit counts.
*/
var gDebuggee;
var gClient;
var gTraceClient;
function run_test()
{
initTestTracerServer();
gDebuggee = addTestGlobal("test-tracer-actor");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTab(gClient, "test-tracer-actor", function(aResponse, aTabClient) {
gClient.attachTracer(aResponse.traceActor, function(aResponse, aTraceClient) {
gTraceClient = aTraceClient;
test_hit_counts();
});
});
});
do_test_pending();
}
function test_hit_counts()
{
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
}
tracesStopped.resolve();
});
start_trace_without_hit_counts()
.then(eval_code)
.then(start_trace_hit_counts)
.then(eval_code)
.then(() => tracesStopped.promise)
.then(stop_trace)
.then(function() {
finishClient(gClient);
}).then(null, error => {
do_check_true(false,
"Should not get an error, got: " + DevToolsUtils.safeErrorString(error));
});
}
function listen_to_traces() {
const tracesStopped = promise.defer();
gClient.addListener("traces", (aEvent, { traces }) => {
for (let t of traces) {
check_trace(t);
}
tracesStopped.resolve();
});
return tracesStopped;
}
function start_trace_without_hit_counts()
{
let deferred = promise.defer();
gTraceClient.startTrace(["depth", "name", "location"], null,
function() { deferred.resolve(); });
return deferred.promise;
}
function start_trace_hit_counts()
{
let deferred = promise.defer();
gTraceClient.startTrace(["hitCount"], null,
function() { deferred.resolve(); });
return deferred.promise;
}
function eval_code()
{
gDebuggee.eval("(" + function iife() {
[1, 2, 3].forEach(function noop() {
for (let x of [1]) {}
});
} + ")()");
}
function stop_trace()
{
let deferred = promise.defer();
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
return deferred.promise;
}
function check_trace({ type, sequence, depth, name, location, hitCount })
{
if (location) {
do_check_true(location.url !== "self-hosted");
}
switch(sequence) {
// First evaluation (before tracing hit counts).
case 0:
case 1:
case 2:
case 4:
case 6:
do_check_eq(hitCount, undefined);
break;
// Second evaluation (after tracing hit counts).
case 10:
do_check_eq(name, "(eval)");
do_check_eq(hitCount, 1);
break;
case 11:
do_check_eq(name, "iife");
do_check_eq(hitCount, 1);
break;
case 12:
do_check_eq(hitCount, 1);
do_check_eq(name, "noop");
break;
case 14:
do_check_eq(hitCount, 2);
do_check_eq(name, "noop");
break;
case 16:
do_check_eq(hitCount, 3);
do_check_eq(name, "noop");
break;
case 3:
case 5:
case 7:
case 8:
case 9:
case 13:
case 15:
case 17:
case 18:
case 19:
do_check_eq(type, "exitedFrame");
break;
default:
// Should have covered all sequences.
do_check_true(false);
}
}

Просмотреть файл

@ -1,58 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that job scheduler orders the jobs correctly.
*/
let { JobScheduler } = devtools.require("devtools/server/actors/tracer");
function run_test()
{
do_test_pending();
test_in_order();
test_shuffled();
do_timeout(0, do_test_finished);
}
function test_in_order() {
let jobScheduler = new JobScheduler();
let testArray = [0];
let first = jobScheduler.schedule();
let second = jobScheduler.schedule();
let third = jobScheduler.schedule();
first(() => testArray.push(1));
second(() => testArray.push(2));
third(() => testArray.push(3));
do_timeout(0, () => {
do_check_eq(testArray[0], 0);
do_check_eq(testArray[1], 1);
do_check_eq(testArray[2], 2);
do_check_eq(testArray[3], 3);
});
}
function test_shuffled() {
let jobScheduler = new JobScheduler();
let testArray = [0];
let first = jobScheduler.schedule();
let second = jobScheduler.schedule();
let third = jobScheduler.schedule();
third(() => testArray.push(3));
first(() => testArray.push(1));
second(() => testArray.push(2));
do_timeout(0, () => {
do_check_eq(testArray[0], 0);
do_check_eq(testArray[1], 1);
do_check_eq(testArray[2], 2);
do_check_eq(testArray[3], 3);
});
}

Просмотреть файл

@ -17,7 +17,6 @@ support-files =
registertestactors-03.js
sourcemapped.js
testactors.js
tracerlocations.js
heap-snapshot-worker.js
hello-actor.js
setBreakpoint-on-column.js
@ -180,8 +179,6 @@ reason = bug 820380
[test_sourcemaps-11.js]
[test_sourcemaps-12.js]
[test_sourcemaps-13.js]
[test_sourcemaps-14.js]
[test_sourcemaps-15.js]
[test_sourcemaps-16.js]
[test_objectgrips-01.js]
[test_objectgrips-02.js]
@ -236,19 +233,6 @@ reason = bug 820380
[test_profiler_getsharedlibraryinformation.js]
[test_unsafeDereference.js]
[test_add_actors.js]
[test_trace_actor-01.js]
[test_trace_actor-02.js]
[test_trace_actor-03.js]
[test_trace_actor-04.js]
[test_trace_actor-05.js]
[test_trace_actor-06.js]
[test_trace_actor-07.js]
[test_trace_actor-08.js]
[test_trace_actor-09.js]
[test_trace_actor-10.js]
[test_trace_actor-11.js]
[test_trace_actor-12.js]
[test_trace_actor-13.js]
[test_ignore_caught_exceptions.js]
[test_requestTypes.js]
reason = bug 937197