зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c. a=merge
This commit is contained in:
Коммит
2806c1d5bc
|
@ -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 |
Двоичные данные
mobile/android/base/resources/drawable-xxhdpi/ic_tracking_protection.png
Normal file
Двоичные данные
mobile/android/base/resources/drawable-xxhdpi/ic_tracking_protection.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 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
|
||||
|
|
Загрузка…
Ссылка в новой задаче