|
Packit |
f0b94e |
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
Packit |
f0b94e |
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
|
Packit |
f0b94e |
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
Packit |
f0b94e |
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
Packit |
f0b94e |
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
Packit |
f0b94e |
"use strict";
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const DBG_STRINGS_URI = "devtools/client/locales/debugger.properties";
|
|
Packit |
f0b94e |
const NEW_SOURCE_IGNORED_URLS = ["debugger eval code", "XStringBundle"];
|
|
Packit |
f0b94e |
const NEW_SOURCE_DISPLAY_DELAY = 200; // ms
|
|
Packit |
f0b94e |
const FETCH_SOURCE_RESPONSE_DELAY = 200; // ms
|
|
Packit |
f0b94e |
const FRAME_STEP_CLEAR_DELAY = 100; // ms
|
|
Packit |
f0b94e |
const CALL_STACK_PAGE_SIZE = 25; // frames
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// The panel's window global is an EventEmitter firing the following events:
|
|
Packit |
f0b94e |
const EVENTS = {
|
|
Packit |
f0b94e |
// When the debugger's source editor instance finishes loading or unloading.
|
|
Packit |
f0b94e |
EDITOR_LOADED: "Debugger:EditorLoaded",
|
|
Packit |
f0b94e |
EDITOR_UNLOADED: "Debugger:EditorUnloaded",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When new sources are received from the debugger server.
|
|
Packit |
f0b94e |
NEW_SOURCE: "Debugger:NewSource",
|
|
Packit |
f0b94e |
SOURCES_ADDED: "Debugger:SourcesAdded",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When a source is shown in the source editor.
|
|
Packit |
f0b94e |
SOURCE_SHOWN: "Debugger:EditorSourceShown",
|
|
Packit |
f0b94e |
SOURCE_ERROR_SHOWN: "Debugger:EditorSourceErrorShown",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When the editor has shown a source and set the line / column position
|
|
Packit |
f0b94e |
EDITOR_LOCATION_SET: "Debugger:EditorLocationSet",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When scopes, variables, properties and watch expressions are fetched and
|
|
Packit |
f0b94e |
// displayed in the variables view.
|
|
Packit |
f0b94e |
FETCHED_SCOPES: "Debugger:FetchedScopes",
|
|
Packit |
f0b94e |
FETCHED_VARIABLES: "Debugger:FetchedVariables",
|
|
Packit |
f0b94e |
FETCHED_PROPERTIES: "Debugger:FetchedProperties",
|
|
Packit |
f0b94e |
FETCHED_BUBBLE_PROPERTIES: "Debugger:FetchedBubbleProperties",
|
|
Packit |
f0b94e |
FETCHED_WATCH_EXPRESSIONS: "Debugger:FetchedWatchExpressions",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When a breakpoint has been added or removed on the debugger server.
|
|
Packit |
f0b94e |
BREAKPOINT_ADDED: "Debugger:BreakpointAdded",
|
|
Packit |
f0b94e |
BREAKPOINT_REMOVED: "Debugger:BreakpointRemoved",
|
|
Packit |
f0b94e |
BREAKPOINT_CLICKED: "Debugger:BreakpointClicked",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When a breakpoint has been shown or hidden in the source editor
|
|
Packit |
f0b94e |
// or the pane.
|
|
Packit |
f0b94e |
BREAKPOINT_SHOWN_IN_EDITOR: "Debugger:BreakpointShownInEditor",
|
|
Packit |
f0b94e |
BREAKPOINT_SHOWN_IN_PANE: "Debugger:BreakpointShownInPane",
|
|
Packit |
f0b94e |
BREAKPOINT_HIDDEN_IN_EDITOR: "Debugger:BreakpointHiddenInEditor",
|
|
Packit |
f0b94e |
BREAKPOINT_HIDDEN_IN_PANE: "Debugger:BreakpointHiddenInPane",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When a conditional breakpoint's popup is shown/hidden.
|
|
Packit |
f0b94e |
CONDITIONAL_BREAKPOINT_POPUP_SHOWN: "Debugger:ConditionalBreakpointPopupShown",
|
|
Packit |
f0b94e |
CONDITIONAL_BREAKPOINT_POPUP_HIDDEN: "Debugger:ConditionalBreakpointPopupHidden",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When event listeners are fetched or event breakpoints are updated.
|
|
Packit |
f0b94e |
EVENT_LISTENERS_FETCHED: "Debugger:EventListenersFetched",
|
|
Packit |
f0b94e |
EVENT_BREAKPOINTS_UPDATED: "Debugger:EventBreakpointsUpdated",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When a file search was performed.
|
|
Packit |
f0b94e |
FILE_SEARCH_MATCH_FOUND: "Debugger:FileSearch:MatchFound",
|
|
Packit |
f0b94e |
FILE_SEARCH_MATCH_NOT_FOUND: "Debugger:FileSearch:MatchNotFound",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When a function search was performed.
|
|
Packit |
f0b94e |
FUNCTION_SEARCH_MATCH_FOUND: "Debugger:FunctionSearch:MatchFound",
|
|
Packit |
f0b94e |
FUNCTION_SEARCH_MATCH_NOT_FOUND: "Debugger:FunctionSearch:MatchNotFound",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When a global text search was performed.
|
|
Packit |
f0b94e |
GLOBAL_SEARCH_MATCH_FOUND: "Debugger:GlobalSearch:MatchFound",
|
|
Packit |
f0b94e |
GLOBAL_SEARCH_MATCH_NOT_FOUND: "Debugger:GlobalSearch:MatchNotFound",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// After the the StackFrames object has been filled with frames
|
|
Packit |
f0b94e |
AFTER_FRAMES_REFILLED: "Debugger:AfterFramesRefilled",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// After the stackframes are cleared and debugger won't pause anymore.
|
|
Packit |
f0b94e |
AFTER_FRAMES_CLEARED: "Debugger:AfterFramesCleared",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When the options popup is showing or hiding.
|
|
Packit |
f0b94e |
OPTIONS_POPUP_SHOWING: "Debugger:OptionsPopupShowing",
|
|
Packit |
f0b94e |
OPTIONS_POPUP_HIDDEN: "Debugger:OptionsPopupHidden",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When the widgets layout has been changed.
|
|
Packit |
f0b94e |
LAYOUT_CHANGED: "Debugger:LayoutChanged",
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// When a worker has been selected.
|
|
Packit |
f0b94e |
WORKER_SELECTED: "Debugger::WorkerSelected"
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Descriptions for what a stack frame represents after the debugger pauses.
|
|
Packit |
f0b94e |
const FRAME_TYPE = {
|
|
Packit |
f0b94e |
NORMAL: 0,
|
|
Packit |
f0b94e |
CONDITIONAL_BREAKPOINT_EVAL: 1,
|
|
Packit |
f0b94e |
WATCH_EXPRESSIONS_EVAL: 2,
|
|
Packit |
f0b94e |
PUBLIC_CLIENT_EVAL: 3
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
|
|
Packit |
f0b94e |
const { require } = BrowserLoader({
|
|
Packit |
f0b94e |
baseURI: "resource://devtools/client/debugger/",
|
|
Packit |
f0b94e |
window,
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
|
|
Packit |
f0b94e |
XPCOMUtils.defineConstant(this, "require", require);
|
|
Packit |
f0b94e |
const { SimpleListWidget } = require("resource://devtools/client/shared/widgets/SimpleListWidget.jsm");
|
|
Packit |
f0b94e |
const { BreadcrumbsWidget } = require("resource://devtools/client/shared/widgets/BreadcrumbsWidget.jsm");
|
|
Packit |
f0b94e |
const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
|
|
Packit |
f0b94e |
const { VariablesView } = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
|
|
Packit |
f0b94e |
const { VariablesViewController, StackFrameUtils } = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
|
|
Packit |
f0b94e |
const EventEmitter = require("devtools/shared/old-event-emitter");
|
|
Packit |
f0b94e |
const { extend } = require("devtools/shared/extend");
|
|
Packit |
f0b94e |
const { gDevTools } = require("devtools/client/framework/devtools");
|
|
Packit |
f0b94e |
const { ViewHelpers, WidgetMethods, setNamedTimeout,
|
|
Packit |
f0b94e |
clearNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Use privileged promise in panel documents to prevent having them to freeze
|
|
Packit |
f0b94e |
// during toolbox destruction. See bug 1402779.
|
|
Packit |
f0b94e |
const Promise = require("Promise");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// React
|
|
Packit |
f0b94e |
const React = require("devtools/client/shared/vendor/react");
|
|
Packit |
f0b94e |
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
|
Packit |
f0b94e |
const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Used to create the Redux store
|
|
Packit |
f0b94e |
const createStore = require("devtools/client/shared/redux/create-store")({
|
|
Packit |
f0b94e |
getTargetClient: () => DebuggerController.client,
|
|
Packit |
f0b94e |
log: false
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
const {
|
|
Packit |
f0b94e |
makeStateBroadcaster,
|
|
Packit |
f0b94e |
enhanceStoreWithBroadcaster,
|
|
Packit |
f0b94e |
combineBroadcastingReducers
|
|
Packit |
f0b94e |
} = require("devtools/client/shared/redux/non-react-subscriber");
|
|
Packit |
f0b94e |
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
|
|
Packit |
f0b94e |
const reducers = require("./content/reducers/index");
|
|
Packit |
f0b94e |
const { onReducerEvents } = require("./content/utils");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const waitUntilService = require("devtools/client/shared/redux/middleware/wait-service");
|
|
Packit |
f0b94e |
var services = {
|
|
Packit |
f0b94e |
WAIT_UNTIL: waitUntilService.NAME
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
var Services = require("Services");
|
|
Packit |
f0b94e |
var {TargetFactory} = require("devtools/client/framework/target");
|
|
Packit |
f0b94e |
var {Toolbox} = require("devtools/client/framework/toolbox");
|
|
Packit |
f0b94e |
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
|
Packit |
f0b94e |
var promise = require("devtools/shared/deprecated-sync-thenables");
|
|
Packit |
f0b94e |
var Editor = require("devtools/client/sourceeditor/editor");
|
|
Packit |
f0b94e |
var DebuggerEditor = require("devtools/client/sourceeditor/debugger");
|
|
Packit |
f0b94e |
var Tooltip = require("devtools/client/shared/widgets/tooltip/Tooltip");
|
|
Packit |
f0b94e |
var FastListWidget = require("devtools/client/shared/widgets/FastListWidget");
|
|
Packit |
f0b94e |
var {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
|
|
Packit |
f0b94e |
var {PrefsHelper} = require("devtools/client/shared/prefs");
|
|
Packit |
f0b94e |
var {Task} = require("devtools/shared/task");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ChromeUtils.defineModuleGetter(this, "Parser",
|
|
Packit |
f0b94e |
"resource://devtools/shared/Parser.jsm");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ChromeUtils.defineModuleGetter(this, "ShortcutUtils",
|
|
Packit |
f0b94e |
"resource://gre/modules/ShortcutUtils.jsm");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
|
|
Packit |
f0b94e |
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Object.defineProperty(this, "NetworkHelper", {
|
|
Packit |
f0b94e |
get: function () {
|
|
Packit |
f0b94e |
return require("devtools/shared/webconsole/network-helper");
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
configurable: true,
|
|
Packit |
f0b94e |
enumerable: true
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Localization convenience methods.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
var L10N = new LocalizationHelper(DBG_STRINGS_URI);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Object defining the debugger controller components.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
var DebuggerController = {
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Initializes the debugger controller.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
initialize: function () {
|
|
Packit |
f0b94e |
dumpn("Initializing the DebuggerController");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this.startupDebugger = this.startupDebugger.bind(this);
|
|
Packit |
f0b94e |
this.shutdownDebugger = this.shutdownDebugger.bind(this);
|
|
Packit |
f0b94e |
this._onNavigate = this._onNavigate.bind(this);
|
|
Packit |
f0b94e |
this._onWillNavigate = this._onWillNavigate.bind(this);
|
|
Packit |
f0b94e |
this._onTabDetached = this._onTabDetached.bind(this);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const broadcaster = makeStateBroadcaster(() => !!this.activeThread);
|
|
Packit |
f0b94e |
const reducer = combineBroadcastingReducers(
|
|
Packit |
f0b94e |
reducers,
|
|
Packit |
f0b94e |
broadcaster.emitChange
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
// TODO: Bug 1228867, clean this up and probably abstract it out
|
|
Packit |
f0b94e |
// better.
|
|
Packit |
f0b94e |
//
|
|
Packit |
f0b94e |
// We only want to process async event that are appropriate for
|
|
Packit |
f0b94e |
// this page. The devtools are open across page reloads, so async
|
|
Packit |
f0b94e |
// requests from the last page might bleed through if reloading
|
|
Packit |
f0b94e |
// fast enough. We check to make sure the async action is part of
|
|
Packit |
f0b94e |
// a current request, and ignore it if not.
|
|
Packit |
f0b94e |
let store = createStore((state, action) => {
|
|
Packit |
f0b94e |
if (action.seqId &&
|
|
Packit |
f0b94e |
(action.status === "done" || action.status === "error") &&
|
|
Packit |
f0b94e |
state && !state.asyncRequests.includes(action.seqId)) {
|
|
Packit |
f0b94e |
return state;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return reducer(state, action);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
store = enhanceStoreWithBroadcaster(store, broadcaster);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// This controller right now acts as the store that's globally
|
|
Packit |
f0b94e |
// available, so just copy the Redux API onto it.
|
|
Packit |
f0b94e |
Object.keys(store).forEach(name => {
|
|
Packit |
f0b94e |
this[name] = store[name];
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Initializes the view.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @return object
|
|
Packit |
f0b94e |
* A promise that is resolved when the debugger finishes startup.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
startupDebugger: Task.async(function* () {
|
|
Packit |
f0b94e |
if (this._startup) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
yield DebuggerView.initialize(this._target.isWorkerTarget);
|
|
Packit |
f0b94e |
this._startup = true;
|
|
Packit |
f0b94e |
}),
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Destroys the view and disconnects the debugger client from the server.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @return object
|
|
Packit |
f0b94e |
* A promise that is resolved when the debugger finishes shutdown.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
shutdownDebugger: Task.async(function* () {
|
|
Packit |
f0b94e |
if (this._shutdown) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
DebuggerView.destroy();
|
|
Packit |
f0b94e |
this.StackFrames.disconnect();
|
|
Packit |
f0b94e |
this.ThreadState.disconnect();
|
|
Packit |
f0b94e |
if (this._target.isTabActor) {
|
|
Packit |
f0b94e |
this.Workers.disconnect();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this.disconnect();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this._shutdown = true;
|
|
Packit |
f0b94e |
}),
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Initiates remote debugging based on the current target, wiring event
|
|
Packit |
f0b94e |
* handlers as necessary.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @return object
|
|
Packit |
f0b94e |
* A promise that is resolved when the debugger finishes connecting.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
connect: Task.async(function* () {
|
|
Packit |
f0b94e |
let target = this._target;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let { client } = target;
|
|
Packit |
f0b94e |
target.on("close", this._onTabDetached);
|
|
Packit |
f0b94e |
target.on("navigate", this._onNavigate);
|
|
Packit |
f0b94e |
target.on("will-navigate", this._onWillNavigate);
|
|
Packit |
f0b94e |
this.client = client;
|
|
Packit |
f0b94e |
this.activeThread = this._toolbox.threadClient;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let wasmBinarySource = !!this.client.mainRoot.traits.wasmBinarySource;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Disable asm.js so that we can set breakpoints and other things
|
|
Packit |
f0b94e |
// on asm.js scripts. For WebAssembly modules allow using of binary
|
|
Packit |
f0b94e |
// source if supported.
|
|
Packit |
f0b94e |
yield this.reconfigureThread({ observeAsmJS: true, wasmBinarySource, });
|
|
Packit |
f0b94e |
yield this.connectThread();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// We need to call this to sync the state of the resume
|
|
Packit |
f0b94e |
// button in the toolbar with the state of the thread.
|
|
Packit |
f0b94e |
this.ThreadState._update();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this._hideUnsupportedFeatures();
|
|
Packit |
f0b94e |
}),
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
connectThread: function () {
|
|
Packit |
f0b94e |
const { newSource, fetchEventListeners } = bindActionCreators(actions, this.dispatch);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// TODO: bug 806775, update the globals list using aPacket.hostAnnotations
|
|
Packit |
f0b94e |
// from bug 801084.
|
|
Packit |
f0b94e |
// this.client.addListener("newGlobal", ...);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this.activeThread.addListener("newSource", (event, packet) => {
|
|
Packit |
f0b94e |
newSource(packet.source);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Make sure the events listeners are up to date.
|
|
Packit |
f0b94e |
if (DebuggerView.instrumentsPaneTab == "events-tab") {
|
|
Packit |
f0b94e |
fetchEventListeners();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (this._target.isTabActor) {
|
|
Packit |
f0b94e |
this.Workers.connect();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
this.ThreadState.connect();
|
|
Packit |
f0b94e |
this.StackFrames.connect();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Load all of the sources. Note that the server will actually
|
|
Packit |
f0b94e |
// emit individual `newSource` notifications, which trigger
|
|
Packit |
f0b94e |
// separate actions, so this won't do anything other than force
|
|
Packit |
f0b94e |
// the server to traverse sources.
|
|
Packit |
f0b94e |
this.dispatch(actions.loadSources()).then(() => {
|
|
Packit |
f0b94e |
// If the engine is already paused, update the UI to represent the
|
|
Packit |
f0b94e |
// paused state
|
|
Packit |
f0b94e |
if (this.activeThread) {
|
|
Packit |
f0b94e |
const pausedPacket = this.activeThread.getLastPausePacket();
|
|
Packit |
f0b94e |
DebuggerView.Toolbar.toggleResumeButtonState(
|
|
Packit |
f0b94e |
this.activeThread.state,
|
|
Packit |
f0b94e |
!!pausedPacket
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
if (pausedPacket) {
|
|
Packit |
f0b94e |
this.StackFrames._onPaused("paused", pausedPacket);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Disconnects the debugger client and removes event handlers as necessary.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
disconnect: function () {
|
|
Packit |
f0b94e |
// Return early if the client didn't even have a chance to instantiate.
|
|
Packit |
f0b94e |
if (!this.client) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this.client.removeListener("newGlobal");
|
|
Packit |
f0b94e |
this.activeThread.removeListener("newSource");
|
|
Packit |
f0b94e |
this.activeThread.removeListener("blackboxchange");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this._connected = false;
|
|
Packit |
f0b94e |
this.client = null;
|
|
Packit |
f0b94e |
this.activeThread = null;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_hideUnsupportedFeatures: function () {
|
|
Packit |
f0b94e |
if (this.client.mainRoot.traits.noPrettyPrinting) {
|
|
Packit |
f0b94e |
DebuggerView.Sources.hidePrettyPrinting();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (this.client.mainRoot.traits.noBlackBoxing) {
|
|
Packit |
f0b94e |
DebuggerView.Sources.hideBlackBoxing();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_onWillNavigate: function (opts = {}) {
|
|
Packit |
f0b94e |
// Reset UI.
|
|
Packit |
f0b94e |
DebuggerView.handleTabNavigation();
|
|
Packit |
f0b94e |
if (!opts.noUnload) {
|
|
Packit |
f0b94e |
this.dispatch(actions.unload());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Discard all the cached parsed sources *before* the target
|
|
Packit |
f0b94e |
// starts navigating. Sources may be fetched during navigation, in
|
|
Packit |
f0b94e |
// which case we don't want to hang on to the old source contents.
|
|
Packit |
f0b94e |
DebuggerController.Parser.clearCache();
|
|
Packit |
f0b94e |
SourceUtils.clearCache();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Prevent performing any actions that were scheduled before
|
|
Packit |
f0b94e |
// navigation.
|
|
Packit |
f0b94e |
clearNamedTimeout("new-source");
|
|
Packit |
f0b94e |
clearNamedTimeout("event-breakpoints-update");
|
|
Packit |
f0b94e |
clearNamedTimeout("event-listeners-fetch");
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_onNavigate: function () {
|
|
Packit |
f0b94e |
this.ThreadState.handleTabNavigation();
|
|
Packit |
f0b94e |
this.StackFrames.handleTabNavigation();
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Called when the debugged tab is closed.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_onTabDetached: function () {
|
|
Packit |
f0b94e |
this.shutdownDebugger();
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Warn if resuming execution produced a wrongOrder error.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_ensureResumptionOrder: function (aResponse) {
|
|
Packit |
f0b94e |
if (aResponse.error == "wrongOrder") {
|
|
Packit |
f0b94e |
DebuggerView.Toolbar.showResumeWarning(aResponse.lastPausedUrl);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Detach and reattach to the thread actor with useSourceMaps true, blow
|
|
Packit |
f0b94e |
* away old sources and get them again.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
reconfigureThread: function (opts) {
|
|
Packit |
f0b94e |
const deferred = promise.defer();
|
|
Packit |
f0b94e |
this.activeThread.reconfigure(
|
|
Packit |
f0b94e |
opts,
|
|
Packit |
f0b94e |
aResponse => {
|
|
Packit |
f0b94e |
if (aResponse.error) {
|
|
Packit |
f0b94e |
deferred.reject(aResponse.error);
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (("useSourceMaps" in opts) || ("autoBlackBox" in opts)) {
|
|
Packit |
f0b94e |
// Reset the view and fetch all the sources again.
|
|
Packit |
f0b94e |
DebuggerView.handleTabNavigation();
|
|
Packit |
f0b94e |
this.dispatch(actions.unload());
|
|
Packit |
f0b94e |
this.dispatch(actions.loadSources());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Update the stack frame list.
|
|
Packit |
f0b94e |
if (this.activeThread.paused) {
|
|
Packit |
f0b94e |
this.activeThread._clearFrames();
|
|
Packit |
f0b94e |
this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
deferred.resolve();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
return deferred.promise;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
waitForSourcesLoaded: function () {
|
|
Packit |
f0b94e |
const deferred = promise.defer();
|
|
Packit |
f0b94e |
this.dispatch({
|
|
Packit |
f0b94e |
type: services.WAIT_UNTIL,
|
|
Packit |
f0b94e |
predicate: action => (action.type === constants.LOAD_SOURCES &&
|
|
Packit |
f0b94e |
action.status === "done"),
|
|
Packit |
f0b94e |
run: deferred.resolve
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
return deferred.promise;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
waitForSourceShown: function (name) {
|
|
Packit |
f0b94e |
const deferred = promise.defer();
|
|
Packit |
f0b94e |
window.on(EVENTS.SOURCE_SHOWN, function onShown(_, source) {
|
|
Packit |
f0b94e |
if (source.url.includes(name)) {
|
|
Packit |
f0b94e |
window.off(EVENTS.SOURCE_SHOWN, onShown);
|
|
Packit |
f0b94e |
deferred.resolve();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
return deferred.promise;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_startup: false,
|
|
Packit |
f0b94e |
_shutdown: false,
|
|
Packit |
f0b94e |
_connected: false,
|
|
Packit |
f0b94e |
client: null,
|
|
Packit |
f0b94e |
activeThread: null
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
function Workers() {
|
|
Packit |
f0b94e |
this._workerForms = Object.create(null);
|
|
Packit |
f0b94e |
this._onWorkerListChanged = this._onWorkerListChanged.bind(this);
|
|
Packit |
f0b94e |
this._onWorkerSelect = this._onWorkerSelect.bind(this);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Workers.prototype = {
|
|
Packit |
f0b94e |
get _tabClient() {
|
|
Packit |
f0b94e |
return DebuggerController._target.activeTab;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
connect: function () {
|
|
Packit |
f0b94e |
if (!Prefs.workersEnabled) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this._updateWorkerList();
|
|
Packit |
f0b94e |
this._tabClient.addListener("workerListChanged", this._onWorkerListChanged);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
disconnect: function () {
|
|
Packit |
f0b94e |
this._tabClient.removeListener("workerListChanged", this._onWorkerListChanged);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_updateWorkerList: function () {
|
|
Packit |
f0b94e |
if (!this._tabClient.listWorkers) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this._tabClient.listWorkers((response) => {
|
|
Packit |
f0b94e |
let workerForms = Object.create(null);
|
|
Packit |
f0b94e |
for (let worker of response.workers) {
|
|
Packit |
f0b94e |
workerForms[worker.actor] = worker;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (let workerActor in this._workerForms) {
|
|
Packit |
f0b94e |
if (!(workerActor in workerForms)) {
|
|
Packit |
f0b94e |
DebuggerView.Workers.removeWorker(this._workerForms[workerActor]);
|
|
Packit |
f0b94e |
delete this._workerForms[workerActor];
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (let workerActor in workerForms) {
|
|
Packit |
f0b94e |
if (!(workerActor in this._workerForms)) {
|
|
Packit |
f0b94e |
let workerForm = workerForms[workerActor];
|
|
Packit |
f0b94e |
this._workerForms[workerActor] = workerForm;
|
|
Packit |
f0b94e |
DebuggerView.Workers.addWorker(workerForm);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_onWorkerListChanged: function () {
|
|
Packit |
f0b94e |
this._updateWorkerList();
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_onWorkerSelect: function (workerForm) {
|
|
Packit |
f0b94e |
DebuggerController.client.attachWorker(workerForm.actor, (response, workerClient) => {
|
|
Packit |
f0b94e |
let toolbox = gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
|
|
Packit |
f0b94e |
"jsdebugger", Toolbox.HostType.WINDOW);
|
|
Packit |
f0b94e |
window.emit(EVENTS.WORKER_SELECTED, toolbox);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* ThreadState keeps the UI up to date with the state of the
|
|
Packit |
f0b94e |
* thread (paused/attached/etc.).
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
function ThreadState() {
|
|
Packit |
f0b94e |
this._update = this._update.bind(this);
|
|
Packit |
f0b94e |
this.interruptedByResumeButton = false;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ThreadState.prototype = {
|
|
Packit |
f0b94e |
get activeThread() {
|
|
Packit |
f0b94e |
return DebuggerController.activeThread;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Connect to the current thread client.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
connect: function () {
|
|
Packit |
f0b94e |
dumpn("ThreadState is connecting...");
|
|
Packit |
f0b94e |
this.activeThread.addListener("paused", this._update);
|
|
Packit |
f0b94e |
this.activeThread.addListener("resumed", this._update);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Disconnect from the client.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
disconnect: function () {
|
|
Packit |
f0b94e |
if (!this.activeThread) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
dumpn("ThreadState is disconnecting...");
|
|
Packit |
f0b94e |
this.activeThread.removeListener("paused", this._update);
|
|
Packit |
f0b94e |
this.activeThread.removeListener("resumed", this._update);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handles any initialization on a tab navigation event issued by the client.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
handleTabNavigation: function () {
|
|
Packit |
f0b94e |
if (!this.activeThread) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
dumpn("Handling tab navigation in the ThreadState");
|
|
Packit |
f0b94e |
this._update();
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Update the UI after a thread state change.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_update: function (aEvent, aPacket) {
|
|
Packit |
f0b94e |
if (aEvent == "paused") {
|
|
Packit |
f0b94e |
if (aPacket.why.type == "interrupted" &&
|
|
Packit |
f0b94e |
this.interruptedByResumeButton) {
|
|
Packit |
f0b94e |
// Interrupt requests suppressed by default, but if this is an
|
|
Packit |
f0b94e |
// explicit interrupt by the pause button we want to emit it.
|
|
Packit |
f0b94e |
gTarget.emit("thread-paused", aPacket);
|
|
Packit |
f0b94e |
} else if (aPacket.why.type == "breakpointConditionThrown" && aPacket.why.message) {
|
|
Packit |
f0b94e |
let where = aPacket.frame.where;
|
|
Packit |
f0b94e |
let aLocation = {
|
|
Packit |
f0b94e |
line: where.line,
|
|
Packit |
f0b94e |
column: where.column,
|
|
Packit |
f0b94e |
actor: where.source ? where.source.actor : null
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
DebuggerView.Sources.showBreakpointConditionThrownMessage(
|
|
Packit |
f0b94e |
aLocation,
|
|
Packit |
f0b94e |
aPacket.why.message
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this.interruptedByResumeButton = false;
|
|
Packit |
f0b94e |
DebuggerView.Toolbar.toggleResumeButtonState(
|
|
Packit |
f0b94e |
this.activeThread.state,
|
|
Packit |
f0b94e |
aPacket ? aPacket.frame : false
|
|
Packit |
f0b94e |
);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Keeps the stack frame list up-to-date, using the thread client's
|
|
Packit |
f0b94e |
* stack frame cache.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
function StackFrames() {
|
|
Packit |
f0b94e |
this._onPaused = this._onPaused.bind(this);
|
|
Packit |
f0b94e |
this._onResumed = this._onResumed.bind(this);
|
|
Packit |
f0b94e |
this._onFrames = this._onFrames.bind(this);
|
|
Packit |
f0b94e |
this._onFramesCleared = this._onFramesCleared.bind(this);
|
|
Packit |
f0b94e |
this._onBlackBoxChange = this._onBlackBoxChange.bind(this);
|
|
Packit |
f0b94e |
this._onPrettyPrintChange = this._onPrettyPrintChange.bind(this);
|
|
Packit |
f0b94e |
this._afterFramesCleared = this._afterFramesCleared.bind(this);
|
|
Packit |
f0b94e |
this.evaluate = this.evaluate.bind(this);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
StackFrames.prototype = {
|
|
Packit |
f0b94e |
get activeThread() {
|
|
Packit |
f0b94e |
return DebuggerController.activeThread;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
currentFrameDepth: -1,
|
|
Packit |
f0b94e |
_currentFrameDescription: FRAME_TYPE.NORMAL,
|
|
Packit |
f0b94e |
_syncedWatchExpressions: null,
|
|
Packit |
f0b94e |
_currentWatchExpressions: null,
|
|
Packit |
f0b94e |
_currentBreakpointLocation: null,
|
|
Packit |
f0b94e |
_currentEvaluation: null,
|
|
Packit |
f0b94e |
_currentException: null,
|
|
Packit |
f0b94e |
_currentReturnedValue: null,
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Connect to the current thread client.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
connect: function () {
|
|
Packit |
f0b94e |
dumpn("StackFrames is connecting...");
|
|
Packit |
f0b94e |
this.activeThread.addListener("paused", this._onPaused);
|
|
Packit |
f0b94e |
this.activeThread.addListener("resumed", this._onResumed);
|
|
Packit |
f0b94e |
this.activeThread.addListener("framesadded", this._onFrames);
|
|
Packit |
f0b94e |
this.activeThread.addListener("framescleared", this._onFramesCleared);
|
|
Packit |
f0b94e |
this.activeThread.addListener("blackboxchange", this._onBlackBoxChange);
|
|
Packit |
f0b94e |
this.activeThread.addListener("prettyprintchange", this._onPrettyPrintChange);
|
|
Packit |
f0b94e |
this.handleTabNavigation();
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Disconnect from the client.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
disconnect: function () {
|
|
Packit |
f0b94e |
if (!this.activeThread) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
dumpn("StackFrames is disconnecting...");
|
|
Packit |
f0b94e |
this.activeThread.removeListener("paused", this._onPaused);
|
|
Packit |
f0b94e |
this.activeThread.removeListener("resumed", this._onResumed);
|
|
Packit |
f0b94e |
this.activeThread.removeListener("framesadded", this._onFrames);
|
|
Packit |
f0b94e |
this.activeThread.removeListener("framescleared", this._onFramesCleared);
|
|
Packit |
f0b94e |
this.activeThread.removeListener("blackboxchange", this._onBlackBoxChange);
|
|
Packit |
f0b94e |
this.activeThread.removeListener("prettyprintchange", this._onPrettyPrintChange);
|
|
Packit |
f0b94e |
clearNamedTimeout("frames-cleared");
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handles any initialization on a tab navigation event issued by the client.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
handleTabNavigation: function () {
|
|
Packit |
f0b94e |
dumpn("Handling tab navigation in the StackFrames");
|
|
Packit |
f0b94e |
// Nothing to do here yet.
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handler for the thread client's paused notification.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @param string aEvent
|
|
Packit |
f0b94e |
* The name of the notification ("paused" in this case).
|
|
Packit |
f0b94e |
* @param object aPacket
|
|
Packit |
f0b94e |
* The response packet.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_onPaused: function (aEvent, aPacket) {
|
|
Packit |
f0b94e |
switch (aPacket.why.type) {
|
|
Packit |
f0b94e |
// If paused by a breakpoint, store the breakpoint location.
|
|
Packit |
f0b94e |
case "breakpoint":
|
|
Packit |
f0b94e |
this._currentBreakpointLocation = aPacket.frame.where;
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
case "breakpointConditionThrown":
|
|
Packit |
f0b94e |
this._currentBreakpointLocation = aPacket.frame.where;
|
|
Packit |
f0b94e |
this._conditionThrowMessage = aPacket.why.message;
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
// If paused by a client evaluation, store the evaluated value.
|
|
Packit |
f0b94e |
case "clientEvaluated":
|
|
Packit |
f0b94e |
this._currentEvaluation = aPacket.why.frameFinished;
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
// If paused by an exception, store the exception value.
|
|
Packit |
f0b94e |
case "exception":
|
|
Packit |
f0b94e |
this._currentException = aPacket.why.exception;
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
// If paused while stepping out of a frame, store the returned value or
|
|
Packit |
f0b94e |
// thrown exception.
|
|
Packit |
f0b94e |
case "resumeLimit":
|
|
Packit |
f0b94e |
if (!aPacket.why.frameFinished) {
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
} else if (aPacket.why.frameFinished.throw) {
|
|
Packit |
f0b94e |
this._currentException = aPacket.why.frameFinished.throw;
|
|
Packit |
f0b94e |
} else if (aPacket.why.frameFinished.return) {
|
|
Packit |
f0b94e |
this._currentReturnedValue = aPacket.why.frameFinished.return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
// If paused by an explicit interrupt, which are generated by the slow
|
|
Packit |
f0b94e |
// script dialog and internal events such as setting breakpoints, ignore
|
|
Packit |
f0b94e |
// the event to avoid UI flicker.
|
|
Packit |
f0b94e |
case "interrupted":
|
|
Packit |
f0b94e |
if (!aPacket.why.onNext) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
|
|
Packit |
f0b94e |
// Focus the editor, but don't steal focus from the split console.
|
|
Packit |
f0b94e |
if (!DebuggerController._toolbox.isSplitConsoleFocused()) {
|
|
Packit |
f0b94e |
DebuggerView.editor.focus();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handler for the thread client's resumed notification.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_onResumed: function () {
|
|
Packit |
f0b94e |
// Prepare the watch expression evaluation string for the next pause.
|
|
Packit |
f0b94e |
if (this._currentFrameDescription != FRAME_TYPE.WATCH_EXPRESSIONS_EVAL) {
|
|
Packit |
f0b94e |
this._currentWatchExpressions = this._syncedWatchExpressions;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handler for the thread client's framesadded notification.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_onFrames: Task.async(function* () {
|
|
Packit |
f0b94e |
// Ignore useless notifications.
|
|
Packit |
f0b94e |
if (!this.activeThread || !this.activeThread.cachedFrames.length) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
if (this._currentFrameDescription != FRAME_TYPE.NORMAL &&
|
|
Packit |
f0b94e |
this._currentFrameDescription != FRAME_TYPE.PUBLIC_CLIENT_EVAL) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// TODO: remove all of this deprecated code: Bug 990137.
|
|
Packit |
f0b94e |
yield this._handleConditionalBreakpoint();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// TODO: handle all of this server-side: Bug 832470, comment 14.
|
|
Packit |
f0b94e |
yield this._handleWatchExpressions();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Make sure the debugger view panes are visible, then refill the frames.
|
|
Packit |
f0b94e |
DebuggerView.showInstrumentsPane();
|
|
Packit |
f0b94e |
this._refillFrames();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// No additional processing is necessary for this stack frame.
|
|
Packit |
f0b94e |
if (this._currentFrameDescription != FRAME_TYPE.NORMAL) {
|
|
Packit |
f0b94e |
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}),
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Fill the StackFrames view with the frames we have in the cache, compressing
|
|
Packit |
f0b94e |
* frames which have black boxed sources into single frames.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_refillFrames: function () {
|
|
Packit |
f0b94e |
// Make sure all the previous stackframes are removed before re-adding them.
|
|
Packit |
f0b94e |
DebuggerView.StackFrames.empty();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (let frame of this.activeThread.cachedFrames) {
|
|
Packit |
f0b94e |
let { depth, source, where: { line, column } } = frame;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let isBlackBoxed = source ? this.activeThread.source(source).isBlackBoxed : false;
|
|
Packit |
f0b94e |
DebuggerView.StackFrames.addFrame(frame, line, column, depth, isBlackBoxed);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
DebuggerView.StackFrames.selectedDepth = Math.max(this.currentFrameDepth, 0);
|
|
Packit |
f0b94e |
DebuggerView.StackFrames.dirty = this.activeThread.moreFrames;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
DebuggerView.StackFrames.addCopyContextMenu();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
window.emit(EVENTS.AFTER_FRAMES_REFILLED);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handler for the thread client's framescleared notification.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_onFramesCleared: function () {
|
|
Packit |
f0b94e |
switch (this._currentFrameDescription) {
|
|
Packit |
f0b94e |
case FRAME_TYPE.NORMAL:
|
|
Packit |
f0b94e |
this._currentEvaluation = null;
|
|
Packit |
f0b94e |
this._currentException = null;
|
|
Packit |
f0b94e |
this._currentReturnedValue = null;
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
case FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL:
|
|
Packit |
f0b94e |
this._currentBreakpointLocation = null;
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
case FRAME_TYPE.WATCH_EXPRESSIONS_EVAL:
|
|
Packit |
f0b94e |
this._currentWatchExpressions = null;
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// After each frame step (in, over, out), framescleared is fired, which
|
|
Packit |
f0b94e |
// forces the UI to be emptied and rebuilt on framesadded. Most of the times
|
|
Packit |
f0b94e |
// this is not necessary, and will result in a brief redraw flicker.
|
|
Packit |
f0b94e |
// To avoid it, invalidate the UI only after a short time if necessary.
|
|
Packit |
f0b94e |
setNamedTimeout("frames-cleared", FRAME_STEP_CLEAR_DELAY, this._afterFramesCleared);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handler for the debugger's blackboxchange notification.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_onBlackBoxChange: function () {
|
|
Packit |
f0b94e |
if (this.activeThread.state == "paused") {
|
|
Packit |
f0b94e |
// Hack to avoid selecting the topmost frame after blackboxing a source.
|
|
Packit |
f0b94e |
this.currentFrameDepth = NaN;
|
|
Packit |
f0b94e |
this._refillFrames();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handler for the debugger's prettyprintchange notification.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_onPrettyPrintChange: function () {
|
|
Packit |
f0b94e |
if (this.activeThread.state != "paused") {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
// Makes sure the selected source remains selected
|
|
Packit |
f0b94e |
// after the fillFrames is called.
|
|
Packit |
f0b94e |
const source = DebuggerView.Sources.selectedValue;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE, () => {
|
|
Packit |
f0b94e |
DebuggerView.Sources.selectedValue = source;
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Called soon after the thread client's framescleared notification.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_afterFramesCleared: function () {
|
|
Packit |
f0b94e |
// Ignore useless notifications.
|
|
Packit |
f0b94e |
if (this.activeThread.cachedFrames.length) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
DebuggerView.editor.clearDebugLocation();
|
|
Packit |
f0b94e |
DebuggerView.StackFrames.empty();
|
|
Packit |
f0b94e |
DebuggerView.Sources.unhighlightBreakpoint();
|
|
Packit |
f0b94e |
DebuggerView.WatchExpressions.toggleContents(true);
|
|
Packit |
f0b94e |
DebuggerView.Variables.empty(0);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
window.emit(EVENTS.AFTER_FRAMES_CLEARED);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Marks the stack frame at the specified depth as selected and updates the
|
|
Packit |
f0b94e |
* properties view with the stack frame's data.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @param number aDepth
|
|
Packit |
f0b94e |
* The depth of the frame in the stack.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
selectFrame: function (aDepth) {
|
|
Packit |
f0b94e |
// Make sure the frame at the specified depth exists first.
|
|
Packit |
f0b94e |
let frame = this.activeThread.cachedFrames[this.currentFrameDepth = aDepth];
|
|
Packit |
f0b94e |
if (!frame) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Check if the frame does not represent the evaluation of debuggee code.
|
|
Packit |
f0b94e |
let { environment, where, source } = frame;
|
|
Packit |
f0b94e |
if (!environment) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Don't change the editor's location if the execution was paused by a
|
|
Packit |
f0b94e |
// public client evaluation. This is useful for adding overlays on
|
|
Packit |
f0b94e |
// top of the editor, like a variable inspection popup.
|
|
Packit |
f0b94e |
let isClientEval = this._currentFrameDescription == FRAME_TYPE.PUBLIC_CLIENT_EVAL;
|
|
Packit |
f0b94e |
let isPopupShown = DebuggerView.VariableBubble.contentsShown();
|
|
Packit |
f0b94e |
if (!isClientEval && !isPopupShown) {
|
|
Packit |
f0b94e |
// Move the editor's caret to the proper url and line.
|
|
Packit |
f0b94e |
DebuggerView.setEditorLocation(source.actor, where.line);
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
// Highlight the line where the execution is paused in the editor.
|
|
Packit |
f0b94e |
DebuggerView.setEditorLocation(source.actor, where.line, { noCaret: true });
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Highlight the breakpoint at the line and column if it exists.
|
|
Packit |
f0b94e |
DebuggerView.Sources.highlightBreakpointAtCursor();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Don't display the watch expressions textbox inputs in the pane.
|
|
Packit |
f0b94e |
DebuggerView.WatchExpressions.toggleContents(false);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Start recording any added variables or properties in any scope and
|
|
Packit |
f0b94e |
// clear existing scopes to create each one dynamically.
|
|
Packit |
f0b94e |
DebuggerView.Variables.empty();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If watch expressions evaluation results are available, create a scope
|
|
Packit |
f0b94e |
// to contain all the values.
|
|
Packit |
f0b94e |
if (this._syncedWatchExpressions && aDepth == 0) {
|
|
Packit |
f0b94e |
let label = L10N.getStr("watchExpressionsScopeLabel");
|
|
Packit |
f0b94e |
let scope = DebuggerView.Variables.addScope(label,
|
|
Packit |
f0b94e |
"variables-view-watch-expressions");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Customize the scope for holding watch expressions evaluations.
|
|
Packit |
f0b94e |
scope.descriptorTooltip = false;
|
|
Packit |
f0b94e |
scope.contextMenuId = "debuggerWatchExpressionsContextMenu";
|
|
Packit |
f0b94e |
scope.separatorStr = L10N.getStr("watchExpressionsSeparatorLabel2");
|
|
Packit |
f0b94e |
scope.switch = DebuggerView.WatchExpressions.switchExpression;
|
|
Packit |
f0b94e |
scope.delete = DebuggerView.WatchExpressions.deleteExpression;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// The evaluation hasn't thrown, so fetch and add the returned results.
|
|
Packit |
f0b94e |
this._fetchWatchExpressions(scope, this._currentEvaluation.return);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// The watch expressions scope is always automatically expanded.
|
|
Packit |
f0b94e |
scope.expand();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
do {
|
|
Packit |
f0b94e |
// Create a scope to contain all the inspected variables in the
|
|
Packit |
f0b94e |
// current environment.
|
|
Packit |
f0b94e |
let label = StackFrameUtils.getScopeLabel(environment);
|
|
Packit |
f0b94e |
let scope = DebuggerView.Variables.addScope(label);
|
|
Packit |
f0b94e |
let innermost = environment == frame.environment;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Handle special additions to the innermost scope.
|
|
Packit |
f0b94e |
if (innermost) {
|
|
Packit |
f0b94e |
this._insertScopeFrameReferences(scope, frame);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Handle the expansion of the scope, lazily populating it with the
|
|
Packit |
f0b94e |
// variables in the current environment.
|
|
Packit |
f0b94e |
DebuggerView.Variables.controller.addExpander(scope, environment);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// The innermost scope is always automatically expanded, because it
|
|
Packit |
f0b94e |
// contains the variables in the current stack frame which are likely to
|
|
Packit |
f0b94e |
// be inspected. The previously expanded scopes are also reexpanded here.
|
|
Packit |
f0b94e |
if (innermost || DebuggerView.Variables.wasExpanded(scope)) {
|
|
Packit |
f0b94e |
scope.expand();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
} while ((environment = environment.parent));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Signal that scope environments have been shown.
|
|
Packit |
f0b94e |
window.emit(EVENTS.FETCHED_SCOPES);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Loads more stack frames from the debugger server cache.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
addMoreFrames: function () {
|
|
Packit |
f0b94e |
this.activeThread.fillFrames(
|
|
Packit |
f0b94e |
this.activeThread.cachedFrames.length + CALL_STACK_PAGE_SIZE);
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Evaluate an expression in the context of the selected frame.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @param string expression
|
|
Packit |
f0b94e |
* The expression to evaluate.
|
|
Packit |
f0b94e |
* @param object options [optional]
|
|
Packit |
f0b94e |
* Additional options for this client evaluation:
|
|
Packit |
f0b94e |
* - depth: the frame depth used for evaluation, 0 being the topmost.
|
|
Packit |
f0b94e |
* - meta: some meta-description for what this evaluation represents.
|
|
Packit |
f0b94e |
* @return object
|
|
Packit |
f0b94e |
* A promise that is resolved when the evaluation finishes,
|
|
Packit |
f0b94e |
* or rejected if there was no stack frame available or some
|
|
Packit |
f0b94e |
* other error occurred.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
evaluate: async function (expression, options = {}) {
|
|
Packit |
f0b94e |
let depth = "depth" in options
|
|
Packit |
f0b94e |
? options.depth
|
|
Packit |
f0b94e |
: this.currentFrameDepth;
|
|
Packit |
f0b94e |
let frame = this.activeThread.cachedFrames[depth];
|
|
Packit |
f0b94e |
if (frame == null) {
|
|
Packit |
f0b94e |
throw new Error("No stack frame available.");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const onThreadPaused = this.activeThread.addOneTimeListener("paused");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let meta = "meta" in options
|
|
Packit |
f0b94e |
? options.meta
|
|
Packit |
f0b94e |
: FRAME_TYPE.PUBLIC_CLIENT_EVAL;
|
|
Packit |
f0b94e |
this._currentFrameDescription = meta;
|
|
Packit |
f0b94e |
this.activeThread.eval(frame.actor, expression);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const packet = await onThreadPaused;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let { type, frameFinished } = packet.why;
|
|
Packit |
f0b94e |
if (type !== "clientEvaluated") {
|
|
Packit |
f0b94e |
throw new Error("Active thread paused unexpectedly.");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return frameFinished;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Add nodes for special frame references in the innermost scope.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @param Scope aScope
|
|
Packit |
f0b94e |
* The scope where the references will be placed into.
|
|
Packit |
f0b94e |
* @param object aFrame
|
|
Packit |
f0b94e |
* The frame to get some references from.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_insertScopeFrameReferences: function (aScope, aFrame) {
|
|
Packit |
f0b94e |
// Add any thrown exception.
|
|
Packit |
f0b94e |
if (this._currentException) {
|
|
Packit |
f0b94e |
let excRef = aScope.addItem("<exception>", { value: this._currentException },
|
|
Packit |
f0b94e |
{ internalItem: true });
|
|
Packit |
f0b94e |
DebuggerView.Variables.controller.addExpander(excRef, this._currentException);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
// Add any returned value.
|
|
Packit |
f0b94e |
if (this._currentReturnedValue) {
|
|
Packit |
f0b94e |
let retRef = aScope.addItem("<return>",
|
|
Packit |
f0b94e |
{ value: this._currentReturnedValue },
|
|
Packit |
f0b94e |
{ internalItem: true });
|
|
Packit |
f0b94e |
DebuggerView.Variables.controller.addExpander(retRef, this._currentReturnedValue);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
// Add "this".
|
|
Packit |
f0b94e |
if (aFrame.this) {
|
|
Packit |
f0b94e |
let thisRef = aScope.addItem("this", { value: aFrame.this });
|
|
Packit |
f0b94e |
DebuggerView.Variables.controller.addExpander(thisRef, aFrame.this);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handles conditional breakpoints when the debugger pauses and the
|
|
Packit |
f0b94e |
* stackframes are received.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* We moved conditional breakpoint handling to the server, but
|
|
Packit |
f0b94e |
* need to support it in the client for a while until most of the
|
|
Packit |
f0b94e |
* server code in production is updated with it.
|
|
Packit |
f0b94e |
* TODO: remove all of this deprecated code: Bug 990137.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @return object
|
|
Packit |
f0b94e |
* A promise that is resolved after a potential breakpoint's
|
|
Packit |
f0b94e |
* conditional expression is evaluated. If there's no breakpoint
|
|
Packit |
f0b94e |
* where the debugger is paused, the promise is resolved immediately.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_handleConditionalBreakpoint: Task.async(function* () {
|
|
Packit |
f0b94e |
if (gClient.mainRoot.traits.conditionalBreakpoints) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
let breakLocation = this._currentBreakpointLocation;
|
|
Packit |
f0b94e |
if (!breakLocation) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let bp = queries.getBreakpoint(DebuggerController.getState(), {
|
|
Packit |
f0b94e |
actor: breakLocation.source.actor,
|
|
Packit |
f0b94e |
line: breakLocation.line
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
let conditionalExpression = bp.condition;
|
|
Packit |
f0b94e |
if (!conditionalExpression) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Evaluating the current breakpoint's conditional expression will
|
|
Packit |
f0b94e |
// cause the stack frames to be cleared and active thread to pause,
|
|
Packit |
f0b94e |
// sending a 'clientEvaluated' packed and adding the frames again.
|
|
Packit |
f0b94e |
let evaluationOptions = { depth: 0, meta: FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL };
|
|
Packit |
f0b94e |
yield this.evaluate(conditionalExpression, evaluationOptions);
|
|
Packit |
f0b94e |
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If the breakpoint's conditional expression evaluation is falsy
|
|
Packit |
f0b94e |
// and there is no exception, automatically resume execution.
|
|
Packit |
f0b94e |
if (!this._currentEvaluation.throw &&
|
|
Packit |
f0b94e |
VariablesView.isFalsy({ value: this._currentEvaluation.return })) {
|
|
Packit |
f0b94e |
this.activeThread.resume(DebuggerController._ensureResumptionOrder);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}),
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Handles watch expressions when the debugger pauses and the stackframes
|
|
Packit |
f0b94e |
* are received.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @return object
|
|
Packit |
f0b94e |
* A promise that is resolved after the potential watch expressions
|
|
Packit |
f0b94e |
* are evaluated. If there are no watch expressions where the debugger
|
|
Packit |
f0b94e |
* is paused, the promise is resolved immediately.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_handleWatchExpressions: Task.async(function* () {
|
|
Packit |
f0b94e |
// Ignore useless notifications.
|
|
Packit |
f0b94e |
if (!this.activeThread || !this.activeThread.cachedFrames.length) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let watchExpressions = this._currentWatchExpressions;
|
|
Packit |
f0b94e |
if (!watchExpressions) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Evaluation causes the stack frames to be cleared and active thread to
|
|
Packit |
f0b94e |
// pause, sending a 'clientEvaluated' packet and adding the frames again.
|
|
Packit |
f0b94e |
let evaluationOptions = { depth: 0, meta: FRAME_TYPE.WATCH_EXPRESSIONS_EVAL };
|
|
Packit |
f0b94e |
yield this.evaluate(watchExpressions, evaluationOptions);
|
|
Packit |
f0b94e |
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If an error was thrown during the evaluation of the watch expressions
|
|
Packit |
f0b94e |
// or the evaluation was terminated from the slow script dialog, then at
|
|
Packit |
f0b94e |
// least one expression evaluation could not be performed. So remove the
|
|
Packit |
f0b94e |
// most recent watch expression and try again.
|
|
Packit |
f0b94e |
if (this._currentEvaluation.throw || this._currentEvaluation.terminated) {
|
|
Packit |
f0b94e |
DebuggerView.WatchExpressions.removeAt(0);
|
|
Packit |
f0b94e |
yield DebuggerController.StackFrames.syncWatchExpressions();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}),
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Adds the watch expressions evaluation results to a scope in the view.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @param Scope aScope
|
|
Packit |
f0b94e |
* The scope where the watch expressions will be placed into.
|
|
Packit |
f0b94e |
* @param object aExp
|
|
Packit |
f0b94e |
* The grip of the evaluation results.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
_fetchWatchExpressions: function (aScope, aExp) {
|
|
Packit |
f0b94e |
// Fetch the expressions only once.
|
|
Packit |
f0b94e |
if (aScope._fetched) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
aScope._fetched = true;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Add nodes for every watch expression in scope.
|
|
Packit |
f0b94e |
this.activeThread.pauseGrip(aExp).getPrototypeAndProperties(aResponse => {
|
|
Packit |
f0b94e |
let ownProperties = aResponse.ownProperties;
|
|
Packit |
f0b94e |
let totalExpressions = DebuggerView.WatchExpressions.itemCount;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (let i = 0; i < totalExpressions; i++) {
|
|
Packit |
f0b94e |
let name = DebuggerView.WatchExpressions.getString(i);
|
|
Packit |
f0b94e |
let expVal = ownProperties[i].value;
|
|
Packit |
f0b94e |
let expRef = aScope.addItem(name, ownProperties[i]);
|
|
Packit |
f0b94e |
DebuggerView.Variables.controller.addExpander(expRef, expVal);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Revert some of the custom watch expressions scope presentation flags,
|
|
Packit |
f0b94e |
// so that they don't propagate to child items.
|
|
Packit |
f0b94e |
expRef.switch = null;
|
|
Packit |
f0b94e |
expRef.delete = null;
|
|
Packit |
f0b94e |
expRef.descriptorTooltip = true;
|
|
Packit |
f0b94e |
expRef.separatorStr = L10N.getStr("variablesSeparatorLabel");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Signal that watch expressions have been fetched.
|
|
Packit |
f0b94e |
window.emit(EVENTS.FETCHED_WATCH_EXPRESSIONS);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Updates a list of watch expressions to evaluate on each pause.
|
|
Packit |
f0b94e |
* TODO: handle all of this server-side: Bug 832470, comment 14.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
syncWatchExpressions: function () {
|
|
Packit |
f0b94e |
let list = DebuggerView.WatchExpressions.getAllStrings();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Sanity check all watch expressions before syncing them. To avoid
|
|
Packit |
f0b94e |
// having the whole watch expressions array throw because of a single
|
|
Packit |
f0b94e |
// faulty expression, simply convert it to a string describing the error.
|
|
Packit |
f0b94e |
// There's no other information necessary to be offered in such cases.
|
|
Packit |
f0b94e |
let sanitizedExpressions = list.map(aString => {
|
|
Packit |
f0b94e |
// Reflect.parse throws when it encounters a syntax error.
|
|
Packit |
f0b94e |
try {
|
|
Packit |
f0b94e |
Parser.reflectionAPI.parse(aString);
|
|
Packit |
f0b94e |
return aString; // Watch expression can be executed safely.
|
|
Packit |
f0b94e |
} catch (e) {
|
|
Packit |
f0b94e |
function safelyEscape(aString) {
|
|
Packit |
f0b94e |
// Convert `str`, a string, to JSON -- that is, to a string beginning
|
|
Packit |
f0b94e |
// and ending with double-quotes, followed by string contents escaped
|
|
Packit |
f0b94e |
// such that the overall string contents are a JSON string literal.
|
|
Packit |
f0b94e |
let str = JSON.stringify(aString);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Remove the leading and trailing double-quotes.
|
|
Packit |
f0b94e |
str = str.substring(1, str.length - 1);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// JSON string literals are not a subset of JS string literals in this
|
|
Packit |
f0b94e |
// one weird case: JSON string literals can directly contain U+2028
|
|
Packit |
f0b94e |
// LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR. Replace these code
|
|
Packit |
f0b94e |
// points with escaped forms.
|
|
Packit |
f0b94e |
str = str.replace(/\u2028/g, "\\u2028");
|
|
Packit |
f0b94e |
str = str.replace(/\u2029/g, "\\u2029");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return str;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return "\"" +
|
|
Packit |
f0b94e |
safelyEscape(e.name) + ": " +
|
|
Packit |
f0b94e |
safelyEscape(e.message) +
|
|
Packit |
f0b94e |
"\""; // Syntax error.
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!sanitizedExpressions.length) {
|
|
Packit |
f0b94e |
this._currentWatchExpressions = null;
|
|
Packit |
f0b94e |
this._syncedWatchExpressions = null;
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
this._syncedWatchExpressions =
|
|
Packit |
f0b94e |
this._currentWatchExpressions = "[" +
|
|
Packit |
f0b94e |
sanitizedExpressions.map(aString =>
|
|
Packit |
f0b94e |
"eval(\"" +
|
|
Packit |
f0b94e |
"try {" +
|
|
Packit |
f0b94e |
// Make sure all quotes are escaped in the expression's syntax,
|
|
Packit |
f0b94e |
// and add a newline after the statement to avoid comments
|
|
Packit |
f0b94e |
// breaking the code integrity inside the eval block.
|
|
Packit |
f0b94e |
aString.replace(/\\/g, "\\\\").replace(/"/g, "\\$&") + "\" + " + "'\\n'" + " + \"" +
|
|
Packit |
f0b94e |
"} catch (e) {" +
|
|
Packit |
f0b94e |
"e.name + ': ' + e.message;" + // TODO: Bug 812765, 812764.
|
|
Packit |
f0b94e |
"}" +
|
|
Packit |
f0b94e |
"\")"
|
|
Packit |
f0b94e |
).join(",") +
|
|
Packit |
f0b94e |
"]";
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
this.currentFrameDepth = -1;
|
|
Packit |
f0b94e |
return this._onFrames();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Shortcuts for accessing various debugger preferences.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
var Prefs = new PrefsHelper("devtools", {
|
|
Packit |
f0b94e |
workersAndSourcesWidth: ["Int", "debugger.ui.panes-workers-and-sources-width"],
|
|
Packit |
f0b94e |
instrumentsWidth: ["Int", "debugger.ui.panes-instruments-width"],
|
|
Packit |
f0b94e |
panesVisibleOnStartup: ["Bool", "debugger.ui.panes-visible-on-startup"],
|
|
Packit |
f0b94e |
variablesSortingEnabled: ["Bool", "debugger.ui.variables-sorting-enabled"],
|
|
Packit |
f0b94e |
variablesOnlyEnumVisible: ["Bool", "debugger.ui.variables-only-enum-visible"],
|
|
Packit |
f0b94e |
variablesSearchboxVisible: ["Bool", "debugger.ui.variables-searchbox-visible"],
|
|
Packit |
f0b94e |
pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
|
|
Packit |
f0b94e |
ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
|
|
Packit |
f0b94e |
sourceMapsEnabled: ["Bool", "debugger.source-maps-enabled"],
|
|
Packit |
f0b94e |
prettyPrintEnabled: ["Bool", "debugger.pretty-print-enabled"],
|
|
Packit |
f0b94e |
autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
|
|
Packit |
f0b94e |
workersEnabled: ["Bool", "debugger.workers"],
|
|
Packit |
f0b94e |
editorTabSize: ["Int", "editor.tabsize"],
|
|
Packit |
f0b94e |
autoBlackBox: ["Bool", "debugger.auto-black-box"],
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Convenient way of emitting events from the panel window.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
EventEmitter.decorate(this);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Preliminary setup for the DebuggerController object.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
DebuggerController.initialize();
|
|
Packit |
f0b94e |
DebuggerController.Parser = new Parser();
|
|
Packit |
f0b94e |
DebuggerController.Workers = new Workers();
|
|
Packit |
f0b94e |
DebuggerController.ThreadState = new ThreadState();
|
|
Packit |
f0b94e |
DebuggerController.StackFrames = new StackFrames();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Export some properties to the global scope for easier access.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
Object.defineProperties(window, {
|
|
Packit |
f0b94e |
"gTarget": {
|
|
Packit |
f0b94e |
get: function () {
|
|
Packit |
f0b94e |
return DebuggerController._target;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
configurable: true
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
"gHostType": {
|
|
Packit |
f0b94e |
get: function () {
|
|
Packit |
f0b94e |
return DebuggerView._hostType;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
configurable: true
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
"gClient": {
|
|
Packit |
f0b94e |
get: function () {
|
|
Packit |
f0b94e |
return DebuggerController.client;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
configurable: true
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
"gThreadClient": {
|
|
Packit |
f0b94e |
get: function () {
|
|
Packit |
f0b94e |
return DebuggerController.activeThread;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
configurable: true
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
"gCallStackPageSize": {
|
|
Packit |
f0b94e |
get: function () {
|
|
Packit |
f0b94e |
return CALL_STACK_PAGE_SIZE;
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
configurable: true
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Helper method for debugging.
|
|
Packit |
f0b94e |
* @param string
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
function dumpn(str) {
|
|
Packit |
f0b94e |
if (wantLogging) {
|
|
Packit |
f0b94e |
dump("DBG-FRONTEND: " + str + "\n");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
var wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|