summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/panel.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/panel.js')
-rw-r--r--devtools/client/debugger/panel.js180
1 files changed, 180 insertions, 0 deletions
diff --git a/devtools/client/debugger/panel.js b/devtools/client/debugger/panel.js
new file mode 100644
index 000000000..352d7b284
--- /dev/null
+++ b/devtools/client/debugger/panel.js
@@ -0,0 +1,180 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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 { Cc, Ci, Cu, Cr } = require("chrome");
+const promise = require("promise");
+const EventEmitter = require("devtools/shared/event-emitter");
+const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+
+function DebuggerPanel(iframeWindow, toolbox) {
+ this.panelWin = iframeWindow;
+ this._toolbox = toolbox;
+ this._destroyer = null;
+
+ this._view = this.panelWin.DebuggerView;
+ this._controller = this.panelWin.DebuggerController;
+ this._view._hostType = this._toolbox.hostType;
+ this._controller._target = this.target;
+ this._controller._toolbox = this._toolbox;
+
+ this.handleHostChanged = this.handleHostChanged.bind(this);
+ EventEmitter.decorate(this);
+}
+
+exports.DebuggerPanel = DebuggerPanel;
+
+DebuggerPanel.prototype = {
+ /**
+ * Open is effectively an asynchronous constructor.
+ *
+ * @return object
+ * A promise that is resolved when the Debugger completes opening.
+ */
+ open: function () {
+ let targetPromise;
+
+ // Local debugging needs to make the target remote.
+ if (!this.target.isRemote) {
+ targetPromise = this.target.makeRemote();
+ // Listen for tab switching events to manage focus when the content window
+ // is paused and events suppressed.
+ this.target.tab.addEventListener("TabSelect", this);
+ } else {
+ targetPromise = promise.resolve(this.target);
+ }
+
+ return targetPromise
+ .then(() => this._controller.startupDebugger())
+ .then(() => this._controller.connect())
+ .then(() => {
+ this._toolbox.on("host-changed", this.handleHostChanged);
+ // Add keys from this document's keyset to the toolbox, so they
+ // can work when the split console is focused.
+ let keysToClone = ["resumeKey", "stepOverKey", "stepInKey", "stepOutKey"];
+ for (let key of keysToClone) {
+ let elm = this.panelWin.document.getElementById(key);
+ let keycode = elm.getAttribute("keycode");
+ let modifiers = elm.getAttribute("modifiers");
+ let command = elm.getAttribute("command");
+ let handler = this._view.Toolbar.getCommandHandler(command);
+
+ let keyShortcut = this.translateToKeyShortcut(keycode, modifiers);
+ this._toolbox.useKeyWithSplitConsole(keyShortcut, handler, "jsdebugger");
+ }
+ this.isReady = true;
+ this.emit("ready");
+ return this;
+ })
+ .then(null, function onError(aReason) {
+ DevToolsUtils.reportException("DebuggerPanel.prototype.open", aReason);
+ });
+ },
+
+ /**
+ * Translate a VK_ keycode, with modifiers, to a key shortcut that can be used with
+ * shared/key-shortcut.
+ *
+ * @param {String} keycode
+ * The VK_* keycode to translate
+ * @param {String} modifiers
+ * The list (blank-space separated) of modifiers applying to this keycode.
+ * @return {String} a key shortcut ready to be used with shared/key-shortcut.js
+ */
+ translateToKeyShortcut: function (keycode, modifiers) {
+ // Remove the VK_ prefix.
+ keycode = keycode.replace("VK_", "");
+
+ // Translate modifiers
+ if (modifiers.includes("shift")) {
+ keycode = "Shift+" + keycode;
+ }
+ if (modifiers.includes("alt")) {
+ keycode = "Alt+" + keycode;
+ }
+ if (modifiers.includes("control")) {
+ keycode = "Ctrl+" + keycode;
+ }
+ if (modifiers.includes("meta")) {
+ keycode = "Cmd+" + keycode;
+ }
+ if (modifiers.includes("accel")) {
+ keycode = "CmdOrCtrl+" + keycode;
+ }
+
+ return keycode;
+ },
+
+ // DevToolPanel API
+
+ get target() {
+ return this._toolbox.target;
+ },
+
+ destroy: function () {
+ // Make sure this panel is not already destroyed.
+ if (this._destroyer) {
+ return this._destroyer;
+ }
+
+ if (!this.target.isRemote) {
+ this.target.tab.removeEventListener("TabSelect", this);
+ }
+
+ return this._destroyer = this._controller.shutdownDebugger().then(() => {
+ this.emit("destroyed");
+ });
+ },
+
+ // DebuggerPanel API
+
+ getFrames() {
+ let framesController = this.panelWin.DebuggerController.StackFrames;
+ let thread = framesController.activeThread;
+ if (thread && thread.paused) {
+ return {
+ frames: thread.cachedFrames,
+ selected: framesController.currentFrameDepth,
+ };
+ }
+
+ return null;
+ },
+
+ addBreakpoint: function (location) {
+ const { actions } = this.panelWin;
+ const { dispatch } = this._controller;
+
+ return dispatch(actions.addBreakpoint(location));
+ },
+
+ removeBreakpoint: function (location) {
+ const { actions } = this.panelWin;
+ const { dispatch } = this._controller;
+
+ return dispatch(actions.removeBreakpoint(location));
+ },
+
+ blackbox: function (source, flag) {
+ const { actions } = this.panelWin;
+ const { dispatch } = this._controller;
+ return dispatch(actions.blackbox(source, flag));
+ },
+
+ handleHostChanged: function () {
+ this._view.handleHostChanged(this._toolbox.hostType);
+ },
+
+ // nsIDOMEventListener API
+
+ handleEvent: function (aEvent) {
+ if (aEvent.target == this.target.tab &&
+ this._controller.activeThread.state == "paused") {
+ // Wait a tick for the content focus event to be delivered.
+ DevToolsUtils.executeSoon(() => this._toolbox.focusTool("jsdebugger"));
+ }
+ }
+};