toolbar-view.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /* import-globals-from ../debugger-controller.js */
  6. /* import-globals-from ../debugger-view.js */
  7. /* import-globals-from ../utils.js */
  8. /* globals document */
  9. "use strict";
  10. /**
  11. * Functions handling the toolbar view: close button, expand/collapse button,
  12. * pause/resume and stepping buttons etc.
  13. */
  14. function ToolbarView(DebuggerController, DebuggerView) {
  15. dumpn("ToolbarView was instantiated");
  16. this.StackFrames = DebuggerController.StackFrames;
  17. this.ThreadState = DebuggerController.ThreadState;
  18. this.DebuggerController = DebuggerController;
  19. this.DebuggerView = DebuggerView;
  20. this._onTogglePanesActivated = this._onTogglePanesActivated.bind(this);
  21. this._onTogglePanesPressed = this._onTogglePanesPressed.bind(this);
  22. this._onResumePressed = this._onResumePressed.bind(this);
  23. this._onStepOverPressed = this._onStepOverPressed.bind(this);
  24. this._onStepInPressed = this._onStepInPressed.bind(this);
  25. this._onStepOutPressed = this._onStepOutPressed.bind(this);
  26. }
  27. ToolbarView.prototype = {
  28. get activeThread() {
  29. return this.DebuggerController.activeThread;
  30. },
  31. get resumptionWarnFunc() {
  32. return this.DebuggerController._ensureResumptionOrder;
  33. },
  34. /**
  35. * Initialization function, called when the debugger is started.
  36. */
  37. initialize: function () {
  38. dumpn("Initializing the ToolbarView");
  39. this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
  40. this._resumeButton = document.getElementById("resume");
  41. this._stepOverButton = document.getElementById("step-over");
  42. this._stepInButton = document.getElementById("step-in");
  43. this._stepOutButton = document.getElementById("step-out");
  44. this._resumeOrderTooltip = new Tooltip(document);
  45. this._resumeOrderTooltip.defaultPosition = TOOLBAR_ORDER_POPUP_POSITION;
  46. let resumeKey = ShortcutUtils.prettifyShortcut(document.getElementById("resumeKey"));
  47. let stepOverKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOverKey"));
  48. let stepInKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepInKey"));
  49. let stepOutKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOutKey"));
  50. this._resumeTooltip = L10N.getFormatStr("resumeButtonTooltip", resumeKey);
  51. this._pauseTooltip = L10N.getFormatStr("pauseButtonTooltip", resumeKey);
  52. this._pausePendingTooltip = L10N.getStr("pausePendingButtonTooltip");
  53. this._stepOverTooltip = L10N.getFormatStr("stepOverTooltip", stepOverKey);
  54. this._stepInTooltip = L10N.getFormatStr("stepInTooltip", stepInKey);
  55. this._stepOutTooltip = L10N.getFormatStr("stepOutTooltip", stepOutKey);
  56. this._instrumentsPaneToggleButton.addEventListener("mousedown",
  57. this._onTogglePanesActivated, false);
  58. this._instrumentsPaneToggleButton.addEventListener("keydown",
  59. this._onTogglePanesPressed, false);
  60. this._resumeButton.addEventListener("mousedown", this._onResumePressed, false);
  61. this._stepOverButton.addEventListener("mousedown", this._onStepOverPressed, false);
  62. this._stepInButton.addEventListener("mousedown", this._onStepInPressed, false);
  63. this._stepOutButton.addEventListener("mousedown", this._onStepOutPressed, false);
  64. this._stepOverButton.setAttribute("tooltiptext", this._stepOverTooltip);
  65. this._stepInButton.setAttribute("tooltiptext", this._stepInTooltip);
  66. this._stepOutButton.setAttribute("tooltiptext", this._stepOutTooltip);
  67. this._toggleButtonsState({ enabled: false });
  68. this._addCommands();
  69. },
  70. /**
  71. * Destruction function, called when the debugger is closed.
  72. */
  73. destroy: function () {
  74. dumpn("Destroying the ToolbarView");
  75. this._instrumentsPaneToggleButton.removeEventListener("mousedown",
  76. this._onTogglePanesActivated, false);
  77. this._instrumentsPaneToggleButton.removeEventListener("keydown",
  78. this._onTogglePanesPressed, false);
  79. this._resumeButton.removeEventListener("mousedown", this._onResumePressed, false);
  80. this._stepOverButton.removeEventListener("mousedown", this._onStepOverPressed, false);
  81. this._stepInButton.removeEventListener("mousedown", this._onStepInPressed, false);
  82. this._stepOutButton.removeEventListener("mousedown", this._onStepOutPressed, false);
  83. },
  84. /**
  85. * Add commands that XUL can fire.
  86. */
  87. _addCommands: function () {
  88. XULUtils.addCommands(document.getElementById("debuggerCommands"), {
  89. resumeCommand: this.getCommandHandler("resumeCommand"),
  90. stepOverCommand: this.getCommandHandler("stepOverCommand"),
  91. stepInCommand: this.getCommandHandler("stepInCommand"),
  92. stepOutCommand: this.getCommandHandler("stepOutCommand")
  93. });
  94. },
  95. /**
  96. * Retrieve the callback associated with the provided debugger command.
  97. *
  98. * @param {String} command
  99. * The debugger command id.
  100. * @return {Function} the corresponding callback.
  101. */
  102. getCommandHandler: function (command) {
  103. switch (command) {
  104. case "resumeCommand":
  105. return () => this._onResumePressed();
  106. case "stepOverCommand":
  107. return () => this._onStepOverPressed();
  108. case "stepInCommand":
  109. return () => this._onStepInPressed();
  110. case "stepOutCommand":
  111. return () => this._onStepOutPressed();
  112. default:
  113. return () => {};
  114. }
  115. },
  116. /**
  117. * Display a warning when trying to resume a debuggee while another is paused.
  118. * Debuggees must be unpaused in a Last-In-First-Out order.
  119. *
  120. * @param string aPausedUrl
  121. * The URL of the last paused debuggee.
  122. */
  123. showResumeWarning: function (aPausedUrl) {
  124. let label = L10N.getFormatStr("resumptionOrderPanelTitle", aPausedUrl);
  125. let defaultStyle = "default-tooltip-simple-text-colors";
  126. this._resumeOrderTooltip.setTextContent({ messages: [label] });
  127. this._resumeOrderTooltip.show(this._resumeButton);
  128. },
  129. /**
  130. * Sets the resume button state based on the debugger active thread.
  131. *
  132. * @param string aState
  133. * Either "paused", "attached", or "breakOnNext".
  134. * @param boolean hasLocation
  135. * True if we are paused at a specific JS location
  136. */
  137. toggleResumeButtonState: function (aState, hasLocation) {
  138. // Intermidiate state after pressing the pause button and waiting
  139. // for the next script execution to happen.
  140. if (aState == "breakOnNext") {
  141. this._resumeButton.setAttribute("break-on-next", "true");
  142. this._resumeButton.disabled = true;
  143. this._resumeButton.setAttribute("tooltiptext", this._pausePendingTooltip);
  144. return;
  145. }
  146. this._resumeButton.removeAttribute("break-on-next");
  147. this._resumeButton.disabled = false;
  148. // If we're paused, check and show a resume label on the button.
  149. if (aState == "paused") {
  150. this._resumeButton.setAttribute("checked", "true");
  151. this._resumeButton.setAttribute("tooltiptext", this._resumeTooltip);
  152. // Only enable the stepping buttons if we are paused at a
  153. // specific location. After bug 789430, we'll always be paused
  154. // at a location, but currently you can pause the entire engine
  155. // at any point without knowing the location.
  156. if (hasLocation) {
  157. this._toggleButtonsState({ enabled: true });
  158. }
  159. }
  160. // If we're attached, do the opposite.
  161. else if (aState == "attached") {
  162. this._resumeButton.removeAttribute("checked");
  163. this._resumeButton.setAttribute("tooltiptext", this._pauseTooltip);
  164. this._toggleButtonsState({ enabled: false });
  165. }
  166. },
  167. _toggleButtonsState: function ({ enabled }) {
  168. const buttons = [
  169. this._stepOutButton,
  170. this._stepInButton,
  171. this._stepOverButton
  172. ];
  173. for (let button of buttons) {
  174. button.disabled = !enabled;
  175. }
  176. },
  177. /**
  178. * Listener handling the toggle button space and return key event.
  179. */
  180. _onTogglePanesPressed: function (event) {
  181. if (ViewHelpers.isSpaceOrReturn(event)) {
  182. this._onTogglePanesActivated();
  183. }
  184. },
  185. /**
  186. * Listener handling the toggle button click event.
  187. */
  188. _onTogglePanesActivated: function() {
  189. DebuggerView.toggleInstrumentsPane({
  190. visible: DebuggerView.instrumentsPaneHidden,
  191. animated: true,
  192. delayed: true
  193. });
  194. },
  195. /**
  196. * Listener handling the pause/resume button click event.
  197. */
  198. _onResumePressed: function () {
  199. if (this.StackFrames._currentFrameDescription != FRAME_TYPE.NORMAL ||
  200. this._resumeButton.disabled) {
  201. return;
  202. }
  203. if (this.activeThread.paused) {
  204. this.StackFrames.currentFrameDepth = -1;
  205. this.activeThread.resume(this.resumptionWarnFunc);
  206. } else {
  207. this.ThreadState.interruptedByResumeButton = true;
  208. this.toggleResumeButtonState("breakOnNext");
  209. this.activeThread.breakOnNext();
  210. }
  211. },
  212. /**
  213. * Listener handling the step over button click event.
  214. */
  215. _onStepOverPressed: function () {
  216. if (this.activeThread.paused && !this._stepOverButton.disabled) {
  217. this.StackFrames.currentFrameDepth = -1;
  218. this.activeThread.stepOver(this.resumptionWarnFunc);
  219. }
  220. },
  221. /**
  222. * Listener handling the step in button click event.
  223. */
  224. _onStepInPressed: function () {
  225. if (this.StackFrames._currentFrameDescription != FRAME_TYPE.NORMAL ||
  226. this._stepInButton.disabled) {
  227. return;
  228. }
  229. if (this.activeThread.paused) {
  230. this.StackFrames.currentFrameDepth = -1;
  231. this.activeThread.stepIn(this.resumptionWarnFunc);
  232. }
  233. },
  234. /**
  235. * Listener handling the step out button click event.
  236. */
  237. _onStepOutPressed: function () {
  238. if (this.activeThread.paused && !this._stepOutButton.disabled) {
  239. this.StackFrames.currentFrameDepth = -1;
  240. this.activeThread.stepOut(this.resumptionWarnFunc);
  241. }
  242. },
  243. _instrumentsPaneToggleButton: null,
  244. _resumeButton: null,
  245. _stepOverButton: null,
  246. _stepInButton: null,
  247. _stepOutButton: null,
  248. _resumeOrderTooltip: null,
  249. _resumeTooltip: "",
  250. _pauseTooltip: "",
  251. _stepOverTooltip: "",
  252. _stepInTooltip: "",
  253. _stepOutTooltip: ""
  254. };
  255. DebuggerView.Toolbar = new ToolbarView(DebuggerController, DebuggerView);