browser/devtools/debugger/debugger-toolbar.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 "use strict";
michael@0 7
michael@0 8 // A time interval sufficient for the options popup panel to finish hiding
michael@0 9 // itself.
michael@0 10 const POPUP_HIDDEN_DELAY = 100; // ms
michael@0 11
michael@0 12 /**
michael@0 13 * Functions handling the toolbar view: close button, expand/collapse button,
michael@0 14 * pause/resume and stepping buttons etc.
michael@0 15 */
michael@0 16 function ToolbarView() {
michael@0 17 dumpn("ToolbarView was instantiated");
michael@0 18
michael@0 19 this._onTogglePanesPressed = this._onTogglePanesPressed.bind(this);
michael@0 20 this._onResumePressed = this._onResumePressed.bind(this);
michael@0 21 this._onStepOverPressed = this._onStepOverPressed.bind(this);
michael@0 22 this._onStepInPressed = this._onStepInPressed.bind(this);
michael@0 23 this._onStepOutPressed = this._onStepOutPressed.bind(this);
michael@0 24 }
michael@0 25
michael@0 26 ToolbarView.prototype = {
michael@0 27 /**
michael@0 28 * Initialization function, called when the debugger is started.
michael@0 29 */
michael@0 30 initialize: function() {
michael@0 31 dumpn("Initializing the ToolbarView");
michael@0 32
michael@0 33 this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
michael@0 34 this._resumeButton = document.getElementById("resume");
michael@0 35 this._stepOverButton = document.getElementById("step-over");
michael@0 36 this._stepInButton = document.getElementById("step-in");
michael@0 37 this._stepOutButton = document.getElementById("step-out");
michael@0 38 this._resumeOrderTooltip = new Tooltip(document);
michael@0 39 this._resumeOrderTooltip.defaultPosition = TOOLBAR_ORDER_POPUP_POSITION;
michael@0 40
michael@0 41 let resumeKey = ShortcutUtils.prettifyShortcut(document.getElementById("resumeKey"));
michael@0 42 let stepOverKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOverKey"));
michael@0 43 let stepInKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepInKey"));
michael@0 44 let stepOutKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOutKey"));
michael@0 45 this._resumeTooltip = L10N.getFormatStr("resumeButtonTooltip", resumeKey);
michael@0 46 this._pauseTooltip = L10N.getFormatStr("pauseButtonTooltip", resumeKey);
michael@0 47 this._stepOverTooltip = L10N.getFormatStr("stepOverTooltip", stepOverKey);
michael@0 48 this._stepInTooltip = L10N.getFormatStr("stepInTooltip", stepInKey);
michael@0 49 this._stepOutTooltip = L10N.getFormatStr("stepOutTooltip", stepOutKey);
michael@0 50
michael@0 51 this._instrumentsPaneToggleButton.addEventListener("mousedown", this._onTogglePanesPressed, false);
michael@0 52 this._resumeButton.addEventListener("mousedown", this._onResumePressed, false);
michael@0 53 this._stepOverButton.addEventListener("mousedown", this._onStepOverPressed, false);
michael@0 54 this._stepInButton.addEventListener("mousedown", this._onStepInPressed, false);
michael@0 55 this._stepOutButton.addEventListener("mousedown", this._onStepOutPressed, false);
michael@0 56
michael@0 57 this._stepOverButton.setAttribute("tooltiptext", this._stepOverTooltip);
michael@0 58 this._stepInButton.setAttribute("tooltiptext", this._stepInTooltip);
michael@0 59 this._stepOutButton.setAttribute("tooltiptext", this._stepOutTooltip);
michael@0 60 },
michael@0 61
michael@0 62 /**
michael@0 63 * Destruction function, called when the debugger is closed.
michael@0 64 */
michael@0 65 destroy: function() {
michael@0 66 dumpn("Destroying the ToolbarView");
michael@0 67
michael@0 68 this._instrumentsPaneToggleButton.removeEventListener("mousedown", this._onTogglePanesPressed, false);
michael@0 69 this._resumeButton.removeEventListener("mousedown", this._onResumePressed, false);
michael@0 70 this._stepOverButton.removeEventListener("mousedown", this._onStepOverPressed, false);
michael@0 71 this._stepInButton.removeEventListener("mousedown", this._onStepInPressed, false);
michael@0 72 this._stepOutButton.removeEventListener("mousedown", this._onStepOutPressed, false);
michael@0 73 },
michael@0 74
michael@0 75 /**
michael@0 76 * Display a warning when trying to resume a debuggee while another is paused.
michael@0 77 * Debuggees must be unpaused in a Last-In-First-Out order.
michael@0 78 *
michael@0 79 * @param string aPausedUrl
michael@0 80 * The URL of the last paused debuggee.
michael@0 81 */
michael@0 82 showResumeWarning: function(aPausedUrl) {
michael@0 83 let label = L10N.getFormatStr("resumptionOrderPanelTitle", aPausedUrl);
michael@0 84 let defaultStyle = "default-tooltip-simple-text-colors";
michael@0 85 this._resumeOrderTooltip.setTextContent({ messages: [label], isAlertTooltip: true });
michael@0 86 this._resumeOrderTooltip.show(this._resumeButton);
michael@0 87 },
michael@0 88
michael@0 89 /**
michael@0 90 * Sets the resume button state based on the debugger active thread.
michael@0 91 *
michael@0 92 * @param string aState
michael@0 93 * Either "paused" or "attached".
michael@0 94 */
michael@0 95 toggleResumeButtonState: function(aState) {
michael@0 96 // If we're paused, check and show a resume label on the button.
michael@0 97 if (aState == "paused") {
michael@0 98 this._resumeButton.setAttribute("checked", "true");
michael@0 99 this._resumeButton.setAttribute("tooltiptext", this._resumeTooltip);
michael@0 100 }
michael@0 101 // If we're attached, do the opposite.
michael@0 102 else if (aState == "attached") {
michael@0 103 this._resumeButton.removeAttribute("checked");
michael@0 104 this._resumeButton.setAttribute("tooltiptext", this._pauseTooltip);
michael@0 105 }
michael@0 106 },
michael@0 107
michael@0 108 /**
michael@0 109 * Listener handling the toggle button click event.
michael@0 110 */
michael@0 111 _onTogglePanesPressed: function() {
michael@0 112 DebuggerView.toggleInstrumentsPane({
michael@0 113 visible: DebuggerView.instrumentsPaneHidden,
michael@0 114 animated: true,
michael@0 115 delayed: true
michael@0 116 });
michael@0 117 },
michael@0 118
michael@0 119 /**
michael@0 120 * Listener handling the pause/resume button click event.
michael@0 121 */
michael@0 122 _onResumePressed: function() {
michael@0 123 if (DebuggerController.activeThread.paused) {
michael@0 124 let warn = DebuggerController._ensureResumptionOrder;
michael@0 125 DebuggerController.StackFrames.currentFrameDepth = -1;
michael@0 126 DebuggerController.activeThread.resume(warn);
michael@0 127 } else {
michael@0 128 DebuggerController.activeThread.interrupt();
michael@0 129 }
michael@0 130 },
michael@0 131
michael@0 132 /**
michael@0 133 * Listener handling the step over button click event.
michael@0 134 */
michael@0 135 _onStepOverPressed: function() {
michael@0 136 if (DebuggerController.activeThread.paused) {
michael@0 137 DebuggerController.StackFrames.currentFrameDepth = -1;
michael@0 138 let warn = DebuggerController._ensureResumptionOrder;
michael@0 139 DebuggerController.activeThread.stepOver(warn);
michael@0 140 }
michael@0 141 },
michael@0 142
michael@0 143 /**
michael@0 144 * Listener handling the step in button click event.
michael@0 145 */
michael@0 146 _onStepInPressed: function() {
michael@0 147 if (DebuggerController.activeThread.paused) {
michael@0 148 DebuggerController.StackFrames.currentFrameDepth = -1;
michael@0 149 let warn = DebuggerController._ensureResumptionOrder;
michael@0 150 DebuggerController.activeThread.stepIn(warn);
michael@0 151 }
michael@0 152 },
michael@0 153
michael@0 154 /**
michael@0 155 * Listener handling the step out button click event.
michael@0 156 */
michael@0 157 _onStepOutPressed: function() {
michael@0 158 if (DebuggerController.activeThread.paused) {
michael@0 159 DebuggerController.StackFrames.currentFrameDepth = -1;
michael@0 160 let warn = DebuggerController._ensureResumptionOrder;
michael@0 161 DebuggerController.activeThread.stepOut(warn);
michael@0 162 }
michael@0 163 },
michael@0 164
michael@0 165 _instrumentsPaneToggleButton: null,
michael@0 166 _resumeButton: null,
michael@0 167 _stepOverButton: null,
michael@0 168 _stepInButton: null,
michael@0 169 _stepOutButton: null,
michael@0 170 _resumeOrderTooltip: null,
michael@0 171 _resumeTooltip: "",
michael@0 172 _pauseTooltip: "",
michael@0 173 _stepOverTooltip: "",
michael@0 174 _stepInTooltip: "",
michael@0 175 _stepOutTooltip: ""
michael@0 176 };
michael@0 177
michael@0 178 /**
michael@0 179 * Functions handling the options UI.
michael@0 180 */
michael@0 181 function OptionsView() {
michael@0 182 dumpn("OptionsView was instantiated");
michael@0 183
michael@0 184 this._toggleAutoPrettyPrint = this._toggleAutoPrettyPrint.bind(this);
michael@0 185 this._togglePauseOnExceptions = this._togglePauseOnExceptions.bind(this);
michael@0 186 this._toggleIgnoreCaughtExceptions = this._toggleIgnoreCaughtExceptions.bind(this);
michael@0 187 this._toggleShowPanesOnStartup = this._toggleShowPanesOnStartup.bind(this);
michael@0 188 this._toggleShowVariablesOnlyEnum = this._toggleShowVariablesOnlyEnum.bind(this);
michael@0 189 this._toggleShowVariablesFilterBox = this._toggleShowVariablesFilterBox.bind(this);
michael@0 190 this._toggleShowOriginalSource = this._toggleShowOriginalSource.bind(this);
michael@0 191 }
michael@0 192
michael@0 193 OptionsView.prototype = {
michael@0 194 /**
michael@0 195 * Initialization function, called when the debugger is started.
michael@0 196 */
michael@0 197 initialize: function() {
michael@0 198 dumpn("Initializing the OptionsView");
michael@0 199
michael@0 200 this._button = document.getElementById("debugger-options");
michael@0 201 this._autoPrettyPrint = document.getElementById("auto-pretty-print");
michael@0 202 this._pauseOnExceptionsItem = document.getElementById("pause-on-exceptions");
michael@0 203 this._ignoreCaughtExceptionsItem = document.getElementById("ignore-caught-exceptions");
michael@0 204 this._showPanesOnStartupItem = document.getElementById("show-panes-on-startup");
michael@0 205 this._showVariablesOnlyEnumItem = document.getElementById("show-vars-only-enum");
michael@0 206 this._showVariablesFilterBoxItem = document.getElementById("show-vars-filter-box");
michael@0 207 this._showOriginalSourceItem = document.getElementById("show-original-source");
michael@0 208
michael@0 209 this._autoPrettyPrint.setAttribute("checked", Prefs.autoPrettyPrint);
michael@0 210 this._pauseOnExceptionsItem.setAttribute("checked", Prefs.pauseOnExceptions);
michael@0 211 this._ignoreCaughtExceptionsItem.setAttribute("checked", Prefs.ignoreCaughtExceptions);
michael@0 212 this._showPanesOnStartupItem.setAttribute("checked", Prefs.panesVisibleOnStartup);
michael@0 213 this._showVariablesOnlyEnumItem.setAttribute("checked", Prefs.variablesOnlyEnumVisible);
michael@0 214 this._showVariablesFilterBoxItem.setAttribute("checked", Prefs.variablesSearchboxVisible);
michael@0 215 this._showOriginalSourceItem.setAttribute("checked", Prefs.sourceMapsEnabled);
michael@0 216 },
michael@0 217
michael@0 218 /**
michael@0 219 * Destruction function, called when the debugger is closed.
michael@0 220 */
michael@0 221 destroy: function() {
michael@0 222 dumpn("Destroying the OptionsView");
michael@0 223 // Nothing to do here yet.
michael@0 224 },
michael@0 225
michael@0 226 /**
michael@0 227 * Listener handling the 'gear menu' popup showing event.
michael@0 228 */
michael@0 229 _onPopupShowing: function() {
michael@0 230 this._button.setAttribute("open", "true");
michael@0 231 window.emit(EVENTS.OPTIONS_POPUP_SHOWING);
michael@0 232 },
michael@0 233
michael@0 234 /**
michael@0 235 * Listener handling the 'gear menu' popup hiding event.
michael@0 236 */
michael@0 237 _onPopupHiding: function() {
michael@0 238 this._button.removeAttribute("open");
michael@0 239 },
michael@0 240
michael@0 241 /**
michael@0 242 * Listener handling the 'gear menu' popup hidden event.
michael@0 243 */
michael@0 244 _onPopupHidden: function() {
michael@0 245 window.emit(EVENTS.OPTIONS_POPUP_HIDDEN);
michael@0 246 },
michael@0 247
michael@0 248 /**
michael@0 249 * Listener handling the 'auto pretty print' menuitem command.
michael@0 250 */
michael@0 251 _toggleAutoPrettyPrint: function(){
michael@0 252 Prefs.autoPrettyPrint =
michael@0 253 this._autoPrettyPrint.getAttribute("checked") == "true";
michael@0 254 },
michael@0 255
michael@0 256 /**
michael@0 257 * Listener handling the 'pause on exceptions' menuitem command.
michael@0 258 */
michael@0 259 _togglePauseOnExceptions: function() {
michael@0 260 Prefs.pauseOnExceptions =
michael@0 261 this._pauseOnExceptionsItem.getAttribute("checked") == "true";
michael@0 262
michael@0 263 DebuggerController.activeThread.pauseOnExceptions(
michael@0 264 Prefs.pauseOnExceptions,
michael@0 265 Prefs.ignoreCaughtExceptions);
michael@0 266 },
michael@0 267
michael@0 268 _toggleIgnoreCaughtExceptions: function() {
michael@0 269 Prefs.ignoreCaughtExceptions =
michael@0 270 this._ignoreCaughtExceptionsItem.getAttribute("checked") == "true";
michael@0 271
michael@0 272 DebuggerController.activeThread.pauseOnExceptions(
michael@0 273 Prefs.pauseOnExceptions,
michael@0 274 Prefs.ignoreCaughtExceptions);
michael@0 275 },
michael@0 276
michael@0 277 /**
michael@0 278 * Listener handling the 'show panes on startup' menuitem command.
michael@0 279 */
michael@0 280 _toggleShowPanesOnStartup: function() {
michael@0 281 Prefs.panesVisibleOnStartup =
michael@0 282 this._showPanesOnStartupItem.getAttribute("checked") == "true";
michael@0 283 },
michael@0 284
michael@0 285 /**
michael@0 286 * Listener handling the 'show non-enumerables' menuitem command.
michael@0 287 */
michael@0 288 _toggleShowVariablesOnlyEnum: function() {
michael@0 289 let pref = Prefs.variablesOnlyEnumVisible =
michael@0 290 this._showVariablesOnlyEnumItem.getAttribute("checked") == "true";
michael@0 291
michael@0 292 DebuggerView.Variables.onlyEnumVisible = pref;
michael@0 293 },
michael@0 294
michael@0 295 /**
michael@0 296 * Listener handling the 'show variables searchbox' menuitem command.
michael@0 297 */
michael@0 298 _toggleShowVariablesFilterBox: function() {
michael@0 299 let pref = Prefs.variablesSearchboxVisible =
michael@0 300 this._showVariablesFilterBoxItem.getAttribute("checked") == "true";
michael@0 301
michael@0 302 DebuggerView.Variables.searchEnabled = pref;
michael@0 303 },
michael@0 304
michael@0 305 /**
michael@0 306 * Listener handling the 'show original source' menuitem command.
michael@0 307 */
michael@0 308 _toggleShowOriginalSource: function() {
michael@0 309 let pref = Prefs.sourceMapsEnabled =
michael@0 310 this._showOriginalSourceItem.getAttribute("checked") == "true";
michael@0 311
michael@0 312 // Don't block the UI while reconfiguring the server.
michael@0 313 window.once(EVENTS.OPTIONS_POPUP_HIDDEN, () => {
michael@0 314 // The popup panel needs more time to hide after triggering onpopuphidden.
michael@0 315 window.setTimeout(() => {
michael@0 316 DebuggerController.reconfigureThread(pref);
michael@0 317 }, POPUP_HIDDEN_DELAY);
michael@0 318 }, false);
michael@0 319 },
michael@0 320
michael@0 321 _button: null,
michael@0 322 _pauseOnExceptionsItem: null,
michael@0 323 _showPanesOnStartupItem: null,
michael@0 324 _showVariablesOnlyEnumItem: null,
michael@0 325 _showVariablesFilterBoxItem: null,
michael@0 326 _showOriginalSourceItem: null
michael@0 327 };
michael@0 328
michael@0 329 /**
michael@0 330 * Functions handling the stackframes UI.
michael@0 331 */
michael@0 332 function StackFramesView() {
michael@0 333 dumpn("StackFramesView was instantiated");
michael@0 334
michael@0 335 this._onStackframeRemoved = this._onStackframeRemoved.bind(this);
michael@0 336 this._onSelect = this._onSelect.bind(this);
michael@0 337 this._onScroll = this._onScroll.bind(this);
michael@0 338 this._afterScroll = this._afterScroll.bind(this);
michael@0 339 }
michael@0 340
michael@0 341 StackFramesView.prototype = Heritage.extend(WidgetMethods, {
michael@0 342 /**
michael@0 343 * Initialization function, called when the debugger is started.
michael@0 344 */
michael@0 345 initialize: function() {
michael@0 346 dumpn("Initializing the StackFramesView");
michael@0 347
michael@0 348 this.widget = new BreadcrumbsWidget(document.getElementById("stackframes"));
michael@0 349 this.widget.addEventListener("select", this._onSelect, false);
michael@0 350 this.widget.addEventListener("scroll", this._onScroll, true);
michael@0 351 window.addEventListener("resize", this._onScroll, true);
michael@0 352
michael@0 353 this.autoFocusOnFirstItem = false;
michael@0 354 this.autoFocusOnSelection = false;
michael@0 355
michael@0 356 // This view's contents are also mirrored in a different container.
michael@0 357 this._mirror = DebuggerView.StackFramesClassicList;
michael@0 358 },
michael@0 359
michael@0 360 /**
michael@0 361 * Destruction function, called when the debugger is closed.
michael@0 362 */
michael@0 363 destroy: function() {
michael@0 364 dumpn("Destroying the StackFramesView");
michael@0 365
michael@0 366 this.widget.removeEventListener("select", this._onSelect, false);
michael@0 367 this.widget.removeEventListener("scroll", this._onScroll, true);
michael@0 368 window.removeEventListener("resize", this._onScroll, true);
michael@0 369 },
michael@0 370
michael@0 371 /**
michael@0 372 * Adds a frame in this stackframes container.
michael@0 373 *
michael@0 374 * @param string aTitle
michael@0 375 * The frame title (function name).
michael@0 376 * @param string aUrl
michael@0 377 * The frame source url.
michael@0 378 * @param string aLine
michael@0 379 * The frame line number.
michael@0 380 * @param number aDepth
michael@0 381 * The frame depth in the stack.
michael@0 382 * @param boolean aIsBlackBoxed
michael@0 383 * Whether or not the frame is black boxed.
michael@0 384 */
michael@0 385 addFrame: function(aTitle, aUrl, aLine, aDepth, aIsBlackBoxed) {
michael@0 386 // Blackboxed stack frames are collapsed into a single entry in
michael@0 387 // the view. By convention, only the first frame is displayed.
michael@0 388 if (aIsBlackBoxed) {
michael@0 389 if (this._prevBlackBoxedUrl == aUrl) {
michael@0 390 return;
michael@0 391 }
michael@0 392 this._prevBlackBoxedUrl = aUrl;
michael@0 393 } else {
michael@0 394 this._prevBlackBoxedUrl = null;
michael@0 395 }
michael@0 396
michael@0 397 // Create the element node for the stack frame item.
michael@0 398 let frameView = this._createFrameView.apply(this, arguments);
michael@0 399
michael@0 400 // Append a stack frame item to this container.
michael@0 401 this.push([frameView], {
michael@0 402 index: 0, /* specifies on which position should the item be appended */
michael@0 403 attachment: {
michael@0 404 title: aTitle,
michael@0 405 url: aUrl,
michael@0 406 line: aLine,
michael@0 407 depth: aDepth
michael@0 408 },
michael@0 409 // Make sure that when the stack frame item is removed, the corresponding
michael@0 410 // mirrored item in the classic list is also removed.
michael@0 411 finalize: this._onStackframeRemoved
michael@0 412 });
michael@0 413
michael@0 414 // Mirror this newly inserted item inside the "Call Stack" tab.
michael@0 415 this._mirror.addFrame(aTitle, aUrl, aLine, aDepth);
michael@0 416 },
michael@0 417
michael@0 418 /**
michael@0 419 * Selects the frame at the specified depth in this container.
michael@0 420 * @param number aDepth
michael@0 421 */
michael@0 422 set selectedDepth(aDepth) {
michael@0 423 this.selectedItem = aItem => aItem.attachment.depth == aDepth;
michael@0 424 },
michael@0 425
michael@0 426 /**
michael@0 427 * Gets the currently selected stack frame's depth in this container.
michael@0 428 * This will essentially be the opposite of |selectedIndex|, which deals
michael@0 429 * with the position in the view, where the last item added is actually
michael@0 430 * the bottommost, not topmost.
michael@0 431 * @return number
michael@0 432 */
michael@0 433 get selectedDepth() {
michael@0 434 return this.selectedItem.attachment.depth;
michael@0 435 },
michael@0 436
michael@0 437 /**
michael@0 438 * Specifies if the active thread has more frames that need to be loaded.
michael@0 439 */
michael@0 440 dirty: false,
michael@0 441
michael@0 442 /**
michael@0 443 * Customization function for creating an item's UI.
michael@0 444 *
michael@0 445 * @param string aTitle
michael@0 446 * The frame title to be displayed in the list.
michael@0 447 * @param string aUrl
michael@0 448 * The frame source url.
michael@0 449 * @param string aLine
michael@0 450 * The frame line number.
michael@0 451 * @param number aDepth
michael@0 452 * The frame depth in the stack.
michael@0 453 * @param boolean aIsBlackBoxed
michael@0 454 * Whether or not the frame is black boxed.
michael@0 455 * @return nsIDOMNode
michael@0 456 * The stack frame view.
michael@0 457 */
michael@0 458 _createFrameView: function(aTitle, aUrl, aLine, aDepth, aIsBlackBoxed) {
michael@0 459 let container = document.createElement("hbox");
michael@0 460 container.id = "stackframe-" + aDepth;
michael@0 461 container.className = "dbg-stackframe";
michael@0 462
michael@0 463 let frameDetails = SourceUtils.trimUrlLength(
michael@0 464 SourceUtils.getSourceLabel(aUrl),
michael@0 465 STACK_FRAMES_SOURCE_URL_MAX_LENGTH,
michael@0 466 STACK_FRAMES_SOURCE_URL_TRIM_SECTION);
michael@0 467
michael@0 468 if (aIsBlackBoxed) {
michael@0 469 container.classList.add("dbg-stackframe-black-boxed");
michael@0 470 } else {
michael@0 471 let frameTitleNode = document.createElement("label");
michael@0 472 frameTitleNode.className = "plain dbg-stackframe-title breadcrumbs-widget-item-tag";
michael@0 473 frameTitleNode.setAttribute("value", aTitle);
michael@0 474 container.appendChild(frameTitleNode);
michael@0 475
michael@0 476 frameDetails += SEARCH_LINE_FLAG + aLine;
michael@0 477 }
michael@0 478
michael@0 479 let frameDetailsNode = document.createElement("label");
michael@0 480 frameDetailsNode.className = "plain dbg-stackframe-details breadcrumbs-widget-item-id";
michael@0 481 frameDetailsNode.setAttribute("value", frameDetails);
michael@0 482 container.appendChild(frameDetailsNode);
michael@0 483
michael@0 484 return container;
michael@0 485 },
michael@0 486
michael@0 487 /**
michael@0 488 * Function called each time a stack frame item is removed.
michael@0 489 *
michael@0 490 * @param object aItem
michael@0 491 * The corresponding item.
michael@0 492 */
michael@0 493 _onStackframeRemoved: function(aItem) {
michael@0 494 dumpn("Finalizing stackframe item: " + aItem);
michael@0 495
michael@0 496 // Remove the mirrored item in the classic list.
michael@0 497 let depth = aItem.attachment.depth;
michael@0 498 this._mirror.remove(this._mirror.getItemForAttachment(e => e.depth == depth));
michael@0 499
michael@0 500 // Forget the previously blackboxed stack frame url.
michael@0 501 this._prevBlackBoxedUrl = null;
michael@0 502 },
michael@0 503
michael@0 504 /**
michael@0 505 * The select listener for the stackframes container.
michael@0 506 */
michael@0 507 _onSelect: function(e) {
michael@0 508 let stackframeItem = this.selectedItem;
michael@0 509 if (stackframeItem) {
michael@0 510 // The container is not empty and an actual item was selected.
michael@0 511 let depth = stackframeItem.attachment.depth;
michael@0 512 DebuggerController.StackFrames.selectFrame(depth);
michael@0 513
michael@0 514 // Mirror the selected item in the classic list.
michael@0 515 this.suppressSelectionEvents = true;
michael@0 516 this._mirror.selectedItem = e => e.attachment.depth == depth;
michael@0 517 this.suppressSelectionEvents = false;
michael@0 518 }
michael@0 519 },
michael@0 520
michael@0 521 /**
michael@0 522 * The scroll listener for the stackframes container.
michael@0 523 */
michael@0 524 _onScroll: function() {
michael@0 525 // Update the stackframes container only if we have to.
michael@0 526 if (!this.dirty) {
michael@0 527 return;
michael@0 528 }
michael@0 529 // Allow requests to settle down first.
michael@0 530 setNamedTimeout("stack-scroll", STACK_FRAMES_SCROLL_DELAY, this._afterScroll);
michael@0 531 },
michael@0 532
michael@0 533 /**
michael@0 534 * Requests the addition of more frames from the controller.
michael@0 535 */
michael@0 536 _afterScroll: function() {
michael@0 537 let scrollPosition = this.widget.getAttribute("scrollPosition");
michael@0 538 let scrollWidth = this.widget.getAttribute("scrollWidth");
michael@0 539
michael@0 540 // If the stackframes container scrolled almost to the end, with only
michael@0 541 // 1/10 of a breadcrumb remaining, load more content.
michael@0 542 if (scrollPosition - scrollWidth / 10 < 1) {
michael@0 543 this.ensureIndexIsVisible(CALL_STACK_PAGE_SIZE - 1);
michael@0 544 this.dirty = false;
michael@0 545
michael@0 546 // Loads more stack frames from the debugger server cache.
michael@0 547 DebuggerController.StackFrames.addMoreFrames();
michael@0 548 }
michael@0 549 },
michael@0 550
michael@0 551 _mirror: null,
michael@0 552 _prevBlackBoxedUrl: null
michael@0 553 });
michael@0 554
michael@0 555 /*
michael@0 556 * Functions handling the stackframes classic list UI.
michael@0 557 * Controlled by the DebuggerView.StackFrames isntance.
michael@0 558 */
michael@0 559 function StackFramesClassicListView() {
michael@0 560 dumpn("StackFramesClassicListView was instantiated");
michael@0 561
michael@0 562 this._onSelect = this._onSelect.bind(this);
michael@0 563 }
michael@0 564
michael@0 565 StackFramesClassicListView.prototype = Heritage.extend(WidgetMethods, {
michael@0 566 /**
michael@0 567 * Initialization function, called when the debugger is started.
michael@0 568 */
michael@0 569 initialize: function() {
michael@0 570 dumpn("Initializing the StackFramesClassicListView");
michael@0 571
michael@0 572 this.widget = new SideMenuWidget(document.getElementById("callstack-list"));
michael@0 573 this.widget.addEventListener("select", this._onSelect, false);
michael@0 574
michael@0 575 this.emptyText = L10N.getStr("noStackFramesText");
michael@0 576 this.autoFocusOnFirstItem = false;
michael@0 577 this.autoFocusOnSelection = false;
michael@0 578
michael@0 579 // This view's contents are also mirrored in a different container.
michael@0 580 this._mirror = DebuggerView.StackFrames;
michael@0 581 },
michael@0 582
michael@0 583 /**
michael@0 584 * Destruction function, called when the debugger is closed.
michael@0 585 */
michael@0 586 destroy: function() {
michael@0 587 dumpn("Destroying the StackFramesClassicListView");
michael@0 588
michael@0 589 this.widget.removeEventListener("select", this._onSelect, false);
michael@0 590 },
michael@0 591
michael@0 592 /**
michael@0 593 * Adds a frame in this stackframes container.
michael@0 594 *
michael@0 595 * @param string aTitle
michael@0 596 * The frame title (function name).
michael@0 597 * @param string aUrl
michael@0 598 * The frame source url.
michael@0 599 * @param string aLine
michael@0 600 * The frame line number.
michael@0 601 * @param number aDepth
michael@0 602 * The frame depth in the stack.
michael@0 603 */
michael@0 604 addFrame: function(aTitle, aUrl, aLine, aDepth) {
michael@0 605 // Create the element node for the stack frame item.
michael@0 606 let frameView = this._createFrameView.apply(this, arguments);
michael@0 607
michael@0 608 // Append a stack frame item to this container.
michael@0 609 this.push([frameView], {
michael@0 610 attachment: {
michael@0 611 depth: aDepth
michael@0 612 }
michael@0 613 });
michael@0 614 },
michael@0 615
michael@0 616 /**
michael@0 617 * Customization function for creating an item's UI.
michael@0 618 *
michael@0 619 * @param string aTitle
michael@0 620 * The frame title to be displayed in the list.
michael@0 621 * @param string aUrl
michael@0 622 * The frame source url.
michael@0 623 * @param string aLine
michael@0 624 * The frame line number.
michael@0 625 * @param number aDepth
michael@0 626 * The frame depth in the stack.
michael@0 627 * @return nsIDOMNode
michael@0 628 * The stack frame view.
michael@0 629 */
michael@0 630 _createFrameView: function(aTitle, aUrl, aLine, aDepth) {
michael@0 631 let container = document.createElement("hbox");
michael@0 632 container.id = "classic-stackframe-" + aDepth;
michael@0 633 container.className = "dbg-classic-stackframe";
michael@0 634 container.setAttribute("flex", "1");
michael@0 635
michael@0 636 let frameTitleNode = document.createElement("label");
michael@0 637 frameTitleNode.className = "plain dbg-classic-stackframe-title";
michael@0 638 frameTitleNode.setAttribute("value", aTitle);
michael@0 639 frameTitleNode.setAttribute("crop", "center");
michael@0 640
michael@0 641 let frameDetailsNode = document.createElement("hbox");
michael@0 642 frameDetailsNode.className = "plain dbg-classic-stackframe-details";
michael@0 643
michael@0 644 let frameUrlNode = document.createElement("label");
michael@0 645 frameUrlNode.className = "plain dbg-classic-stackframe-details-url";
michael@0 646 frameUrlNode.setAttribute("value", SourceUtils.getSourceLabel(aUrl));
michael@0 647 frameUrlNode.setAttribute("crop", "center");
michael@0 648 frameDetailsNode.appendChild(frameUrlNode);
michael@0 649
michael@0 650 let frameDetailsSeparator = document.createElement("label");
michael@0 651 frameDetailsSeparator.className = "plain dbg-classic-stackframe-details-sep";
michael@0 652 frameDetailsSeparator.setAttribute("value", SEARCH_LINE_FLAG);
michael@0 653 frameDetailsNode.appendChild(frameDetailsSeparator);
michael@0 654
michael@0 655 let frameLineNode = document.createElement("label");
michael@0 656 frameLineNode.className = "plain dbg-classic-stackframe-details-line";
michael@0 657 frameLineNode.setAttribute("value", aLine);
michael@0 658 frameDetailsNode.appendChild(frameLineNode);
michael@0 659
michael@0 660 container.appendChild(frameTitleNode);
michael@0 661 container.appendChild(frameDetailsNode);
michael@0 662
michael@0 663 return container;
michael@0 664 },
michael@0 665
michael@0 666 /**
michael@0 667 * The select listener for the stackframes container.
michael@0 668 */
michael@0 669 _onSelect: function(e) {
michael@0 670 let stackframeItem = this.selectedItem;
michael@0 671 if (stackframeItem) {
michael@0 672 // The container is not empty and an actual item was selected.
michael@0 673 // Mirror the selected item in the breadcrumbs list.
michael@0 674 let depth = stackframeItem.attachment.depth;
michael@0 675 this._mirror.selectedItem = e => e.attachment.depth == depth;
michael@0 676 }
michael@0 677 },
michael@0 678
michael@0 679 _mirror: null
michael@0 680 });
michael@0 681
michael@0 682 /**
michael@0 683 * Functions handling the filtering UI.
michael@0 684 */
michael@0 685 function FilterView() {
michael@0 686 dumpn("FilterView was instantiated");
michael@0 687
michael@0 688 this._onClick = this._onClick.bind(this);
michael@0 689 this._onInput = this._onInput.bind(this);
michael@0 690 this._onKeyPress = this._onKeyPress.bind(this);
michael@0 691 this._onBlur = this._onBlur.bind(this);
michael@0 692 }
michael@0 693
michael@0 694 FilterView.prototype = {
michael@0 695 /**
michael@0 696 * Initialization function, called when the debugger is started.
michael@0 697 */
michael@0 698 initialize: function() {
michael@0 699 dumpn("Initializing the FilterView");
michael@0 700
michael@0 701 this._searchbox = document.getElementById("searchbox");
michael@0 702 this._searchboxHelpPanel = document.getElementById("searchbox-help-panel");
michael@0 703 this._filterLabel = document.getElementById("filter-label");
michael@0 704 this._globalOperatorButton = document.getElementById("global-operator-button");
michael@0 705 this._globalOperatorLabel = document.getElementById("global-operator-label");
michael@0 706 this._functionOperatorButton = document.getElementById("function-operator-button");
michael@0 707 this._functionOperatorLabel = document.getElementById("function-operator-label");
michael@0 708 this._tokenOperatorButton = document.getElementById("token-operator-button");
michael@0 709 this._tokenOperatorLabel = document.getElementById("token-operator-label");
michael@0 710 this._lineOperatorButton = document.getElementById("line-operator-button");
michael@0 711 this._lineOperatorLabel = document.getElementById("line-operator-label");
michael@0 712 this._variableOperatorButton = document.getElementById("variable-operator-button");
michael@0 713 this._variableOperatorLabel = document.getElementById("variable-operator-label");
michael@0 714
michael@0 715 this._fileSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("fileSearchKey"));
michael@0 716 this._globalSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("globalSearchKey"));
michael@0 717 this._filteredFunctionsKey = ShortcutUtils.prettifyShortcut(document.getElementById("functionSearchKey"));
michael@0 718 this._tokenSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("tokenSearchKey"));
michael@0 719 this._lineSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("lineSearchKey"));
michael@0 720 this._variableSearchKey = ShortcutUtils.prettifyShortcut(document.getElementById("variableSearchKey"));
michael@0 721
michael@0 722 this._searchbox.addEventListener("click", this._onClick, false);
michael@0 723 this._searchbox.addEventListener("select", this._onInput, false);
michael@0 724 this._searchbox.addEventListener("input", this._onInput, false);
michael@0 725 this._searchbox.addEventListener("keypress", this._onKeyPress, false);
michael@0 726 this._searchbox.addEventListener("blur", this._onBlur, false);
michael@0 727
michael@0 728 let placeholder = L10N.getFormatStr("emptySearchText", this._fileSearchKey);
michael@0 729 this._searchbox.setAttribute("placeholder", placeholder);
michael@0 730
michael@0 731 this._globalOperatorButton.setAttribute("label", SEARCH_GLOBAL_FLAG);
michael@0 732 this._functionOperatorButton.setAttribute("label", SEARCH_FUNCTION_FLAG);
michael@0 733 this._tokenOperatorButton.setAttribute("label", SEARCH_TOKEN_FLAG);
michael@0 734 this._lineOperatorButton.setAttribute("label", SEARCH_LINE_FLAG);
michael@0 735 this._variableOperatorButton.setAttribute("label", SEARCH_VARIABLE_FLAG);
michael@0 736
michael@0 737 this._filterLabel.setAttribute("value",
michael@0 738 L10N.getFormatStr("searchPanelFilter", this._fileSearchKey));
michael@0 739 this._globalOperatorLabel.setAttribute("value",
michael@0 740 L10N.getFormatStr("searchPanelGlobal", this._globalSearchKey));
michael@0 741 this._functionOperatorLabel.setAttribute("value",
michael@0 742 L10N.getFormatStr("searchPanelFunction", this._filteredFunctionsKey));
michael@0 743 this._tokenOperatorLabel.setAttribute("value",
michael@0 744 L10N.getFormatStr("searchPanelToken", this._tokenSearchKey));
michael@0 745 this._lineOperatorLabel.setAttribute("value",
michael@0 746 L10N.getFormatStr("searchPanelGoToLine", this._lineSearchKey));
michael@0 747 this._variableOperatorLabel.setAttribute("value",
michael@0 748 L10N.getFormatStr("searchPanelVariable", this._variableSearchKey));
michael@0 749 },
michael@0 750
michael@0 751 /**
michael@0 752 * Destruction function, called when the debugger is closed.
michael@0 753 */
michael@0 754 destroy: function() {
michael@0 755 dumpn("Destroying the FilterView");
michael@0 756
michael@0 757 this._searchbox.removeEventListener("click", this._onClick, false);
michael@0 758 this._searchbox.removeEventListener("select", this._onInput, false);
michael@0 759 this._searchbox.removeEventListener("input", this._onInput, false);
michael@0 760 this._searchbox.removeEventListener("keypress", this._onKeyPress, false);
michael@0 761 this._searchbox.removeEventListener("blur", this._onBlur, false);
michael@0 762 },
michael@0 763
michael@0 764 /**
michael@0 765 * Gets the entered operator and arguments in the searchbox.
michael@0 766 * @return array
michael@0 767 */
michael@0 768 get searchData() {
michael@0 769 let operator = "", args = [];
michael@0 770
michael@0 771 let rawValue = this._searchbox.value;
michael@0 772 let rawLength = rawValue.length;
michael@0 773 let globalFlagIndex = rawValue.indexOf(SEARCH_GLOBAL_FLAG);
michael@0 774 let functionFlagIndex = rawValue.indexOf(SEARCH_FUNCTION_FLAG);
michael@0 775 let variableFlagIndex = rawValue.indexOf(SEARCH_VARIABLE_FLAG);
michael@0 776 let tokenFlagIndex = rawValue.lastIndexOf(SEARCH_TOKEN_FLAG);
michael@0 777 let lineFlagIndex = rawValue.lastIndexOf(SEARCH_LINE_FLAG);
michael@0 778
michael@0 779 // This is not a global, function or variable search, allow file/line flags.
michael@0 780 if (globalFlagIndex != 0 && functionFlagIndex != 0 && variableFlagIndex != 0) {
michael@0 781 // Token search has precedence over line search.
michael@0 782 if (tokenFlagIndex != -1) {
michael@0 783 operator = SEARCH_TOKEN_FLAG;
michael@0 784 args.push(rawValue.slice(0, tokenFlagIndex)); // file
michael@0 785 args.push(rawValue.substr(tokenFlagIndex + 1, rawLength)); // token
michael@0 786 } else if (lineFlagIndex != -1) {
michael@0 787 operator = SEARCH_LINE_FLAG;
michael@0 788 args.push(rawValue.slice(0, lineFlagIndex)); // file
michael@0 789 args.push(+rawValue.substr(lineFlagIndex + 1, rawLength) || 0); // line
michael@0 790 } else {
michael@0 791 args.push(rawValue);
michael@0 792 }
michael@0 793 }
michael@0 794 // Global searches dissalow the use of file or line flags.
michael@0 795 else if (globalFlagIndex == 0) {
michael@0 796 operator = SEARCH_GLOBAL_FLAG;
michael@0 797 args.push(rawValue.slice(1));
michael@0 798 }
michael@0 799 // Function searches dissalow the use of file or line flags.
michael@0 800 else if (functionFlagIndex == 0) {
michael@0 801 operator = SEARCH_FUNCTION_FLAG;
michael@0 802 args.push(rawValue.slice(1));
michael@0 803 }
michael@0 804 // Variable searches dissalow the use of file or line flags.
michael@0 805 else if (variableFlagIndex == 0) {
michael@0 806 operator = SEARCH_VARIABLE_FLAG;
michael@0 807 args.push(rawValue.slice(1));
michael@0 808 }
michael@0 809
michael@0 810 return [operator, args];
michael@0 811 },
michael@0 812
michael@0 813 /**
michael@0 814 * Returns the current search operator.
michael@0 815 * @return string
michael@0 816 */
michael@0 817 get searchOperator() this.searchData[0],
michael@0 818
michael@0 819 /**
michael@0 820 * Returns the current search arguments.
michael@0 821 * @return array
michael@0 822 */
michael@0 823 get searchArguments() this.searchData[1],
michael@0 824
michael@0 825 /**
michael@0 826 * Clears the text from the searchbox and any changed views.
michael@0 827 */
michael@0 828 clearSearch: function() {
michael@0 829 this._searchbox.value = "";
michael@0 830 this.clearViews();
michael@0 831 },
michael@0 832
michael@0 833 /**
michael@0 834 * Clears all the views that may pop up when searching.
michael@0 835 */
michael@0 836 clearViews: function() {
michael@0 837 DebuggerView.GlobalSearch.clearView();
michael@0 838 DebuggerView.FilteredSources.clearView();
michael@0 839 DebuggerView.FilteredFunctions.clearView();
michael@0 840 this._searchboxHelpPanel.hidePopup();
michael@0 841 },
michael@0 842
michael@0 843 /**
michael@0 844 * Performs a line search if necessary.
michael@0 845 * (Jump to lines in the currently visible source).
michael@0 846 *
michael@0 847 * @param number aLine
michael@0 848 * The source line number to jump to.
michael@0 849 */
michael@0 850 _performLineSearch: function(aLine) {
michael@0 851 // Make sure we're actually searching for a valid line.
michael@0 852 if (aLine) {
michael@0 853 DebuggerView.editor.setCursor({ line: aLine - 1, ch: 0 }, "center");
michael@0 854 }
michael@0 855 },
michael@0 856
michael@0 857 /**
michael@0 858 * Performs a token search if necessary.
michael@0 859 * (Search for tokens in the currently visible source).
michael@0 860 *
michael@0 861 * @param string aToken
michael@0 862 * The source token to find.
michael@0 863 */
michael@0 864 _performTokenSearch: function(aToken) {
michael@0 865 // Make sure we're actually searching for a valid token.
michael@0 866 if (!aToken) {
michael@0 867 return;
michael@0 868 }
michael@0 869 DebuggerView.editor.find(aToken);
michael@0 870 },
michael@0 871
michael@0 872 /**
michael@0 873 * The click listener for the search container.
michael@0 874 */
michael@0 875 _onClick: function() {
michael@0 876 // If there's some text in the searchbox, displaying a panel would
michael@0 877 // interfere with double/triple click default behaviors.
michael@0 878 if (!this._searchbox.value) {
michael@0 879 this._searchboxHelpPanel.openPopup(this._searchbox);
michael@0 880 }
michael@0 881 },
michael@0 882
michael@0 883 /**
michael@0 884 * The input listener for the search container.
michael@0 885 */
michael@0 886 _onInput: function() {
michael@0 887 this.clearViews();
michael@0 888
michael@0 889 // Make sure we're actually searching for something.
michael@0 890 if (!this._searchbox.value) {
michael@0 891 return;
michael@0 892 }
michael@0 893
michael@0 894 // Perform the required search based on the specified operator.
michael@0 895 switch (this.searchOperator) {
michael@0 896 case SEARCH_GLOBAL_FLAG:
michael@0 897 // Schedule a global search for when the user stops typing.
michael@0 898 DebuggerView.GlobalSearch.scheduleSearch(this.searchArguments[0]);
michael@0 899 break;
michael@0 900 case SEARCH_FUNCTION_FLAG:
michael@0 901 // Schedule a function search for when the user stops typing.
michael@0 902 DebuggerView.FilteredFunctions.scheduleSearch(this.searchArguments[0]);
michael@0 903 break;
michael@0 904 case SEARCH_VARIABLE_FLAG:
michael@0 905 // Schedule a variable search for when the user stops typing.
michael@0 906 DebuggerView.Variables.scheduleSearch(this.searchArguments[0]);
michael@0 907 break;
michael@0 908 case SEARCH_TOKEN_FLAG:
michael@0 909 // Schedule a file+token search for when the user stops typing.
michael@0 910 DebuggerView.FilteredSources.scheduleSearch(this.searchArguments[0]);
michael@0 911 this._performTokenSearch(this.searchArguments[1]);
michael@0 912 break;
michael@0 913 case SEARCH_LINE_FLAG:
michael@0 914 // Schedule a file+line search for when the user stops typing.
michael@0 915 DebuggerView.FilteredSources.scheduleSearch(this.searchArguments[0]);
michael@0 916 this._performLineSearch(this.searchArguments[1]);
michael@0 917 break;
michael@0 918 default:
michael@0 919 // Schedule a file only search for when the user stops typing.
michael@0 920 DebuggerView.FilteredSources.scheduleSearch(this.searchArguments[0]);
michael@0 921 break;
michael@0 922 }
michael@0 923 },
michael@0 924
michael@0 925 /**
michael@0 926 * The key press listener for the search container.
michael@0 927 */
michael@0 928 _onKeyPress: function(e) {
michael@0 929 // This attribute is not implemented in Gecko at this time, see bug 680830.
michael@0 930 e.char = String.fromCharCode(e.charCode);
michael@0 931
michael@0 932 // Perform the required action based on the specified operator.
michael@0 933 let [operator, args] = this.searchData;
michael@0 934 let isGlobalSearch = operator == SEARCH_GLOBAL_FLAG;
michael@0 935 let isFunctionSearch = operator == SEARCH_FUNCTION_FLAG;
michael@0 936 let isVariableSearch = operator == SEARCH_VARIABLE_FLAG;
michael@0 937 let isTokenSearch = operator == SEARCH_TOKEN_FLAG;
michael@0 938 let isLineSearch = operator == SEARCH_LINE_FLAG;
michael@0 939 let isFileOnlySearch = !operator && args.length == 1;
michael@0 940
michael@0 941 // Depending on the pressed keys, determine to correct action to perform.
michael@0 942 let actionToPerform;
michael@0 943
michael@0 944 // Meta+G and Ctrl+N focus next matches.
michael@0 945 if ((e.char == "g" && e.metaKey) || e.char == "n" && e.ctrlKey) {
michael@0 946 actionToPerform = "selectNext";
michael@0 947 }
michael@0 948 // Meta+Shift+G and Ctrl+P focus previous matches.
michael@0 949 else if ((e.char == "G" && e.metaKey) || e.char == "p" && e.ctrlKey) {
michael@0 950 actionToPerform = "selectPrev";
michael@0 951 }
michael@0 952 // Return, enter, down and up keys focus next or previous matches, while
michael@0 953 // the escape key switches focus from the search container.
michael@0 954 else switch (e.keyCode) {
michael@0 955 case e.DOM_VK_RETURN:
michael@0 956 var isReturnKey = true;
michael@0 957 // If the shift key is pressed, focus on the previous result
michael@0 958 actionToPerform = e.shiftKey ? "selectPrev" : "selectNext";
michael@0 959 break;
michael@0 960 case e.DOM_VK_DOWN:
michael@0 961 actionToPerform = "selectNext";
michael@0 962 break;
michael@0 963 case e.DOM_VK_UP:
michael@0 964 actionToPerform = "selectPrev";
michael@0 965 break;
michael@0 966 }
michael@0 967
michael@0 968 // If there's no action to perform, or no operator, file line or token
michael@0 969 // were specified, then this is either a broken or empty search.
michael@0 970 if (!actionToPerform || (!operator && !args.length)) {
michael@0 971 DebuggerView.editor.dropSelection();
michael@0 972 return;
michael@0 973 }
michael@0 974
michael@0 975 e.preventDefault();
michael@0 976 e.stopPropagation();
michael@0 977
michael@0 978 // Jump to the next/previous entry in the global search, or perform
michael@0 979 // a new global search immediately
michael@0 980 if (isGlobalSearch) {
michael@0 981 let targetView = DebuggerView.GlobalSearch;
michael@0 982 if (!isReturnKey) {
michael@0 983 targetView[actionToPerform]();
michael@0 984 } else if (targetView.hidden) {
michael@0 985 targetView.scheduleSearch(args[0], 0);
michael@0 986 }
michael@0 987 return;
michael@0 988 }
michael@0 989
michael@0 990 // Jump to the next/previous entry in the function search, perform
michael@0 991 // a new function search immediately, or clear it.
michael@0 992 if (isFunctionSearch) {
michael@0 993 let targetView = DebuggerView.FilteredFunctions;
michael@0 994 if (!isReturnKey) {
michael@0 995 targetView[actionToPerform]();
michael@0 996 } else if (targetView.hidden) {
michael@0 997 targetView.scheduleSearch(args[0], 0);
michael@0 998 } else {
michael@0 999 if (!targetView.selectedItem) {
michael@0 1000 targetView.selectedIndex = 0;
michael@0 1001 }
michael@0 1002 this.clearSearch();
michael@0 1003 }
michael@0 1004 return;
michael@0 1005 }
michael@0 1006
michael@0 1007 // Perform a new variable search immediately.
michael@0 1008 if (isVariableSearch) {
michael@0 1009 let targetView = DebuggerView.Variables;
michael@0 1010 if (isReturnKey) {
michael@0 1011 targetView.scheduleSearch(args[0], 0);
michael@0 1012 }
michael@0 1013 return;
michael@0 1014 }
michael@0 1015
michael@0 1016 // Jump to the next/previous entry in the file search, perform
michael@0 1017 // a new file search immediately, or clear it.
michael@0 1018 if (isFileOnlySearch) {
michael@0 1019 let targetView = DebuggerView.FilteredSources;
michael@0 1020 if (!isReturnKey) {
michael@0 1021 targetView[actionToPerform]();
michael@0 1022 } else if (targetView.hidden) {
michael@0 1023 targetView.scheduleSearch(args[0], 0);
michael@0 1024 } else {
michael@0 1025 if (!targetView.selectedItem) {
michael@0 1026 targetView.selectedIndex = 0;
michael@0 1027 }
michael@0 1028 this.clearSearch();
michael@0 1029 }
michael@0 1030 return;
michael@0 1031 }
michael@0 1032
michael@0 1033 // Jump to the next/previous instance of the currently searched token.
michael@0 1034 if (isTokenSearch) {
michael@0 1035 let methods = { selectNext: "findNext", selectPrev: "findPrev" };
michael@0 1036 DebuggerView.editor[methods[actionToPerform]]();
michael@0 1037 return;
michael@0 1038 }
michael@0 1039
michael@0 1040 // Increment/decrement the currently searched caret line.
michael@0 1041 if (isLineSearch) {
michael@0 1042 let [, line] = args;
michael@0 1043 let amounts = { selectNext: 1, selectPrev: -1 };
michael@0 1044
michael@0 1045 // Modify the line number and jump to it.
michael@0 1046 line += !isReturnKey ? amounts[actionToPerform] : 0;
michael@0 1047 let lineCount = DebuggerView.editor.lineCount();
michael@0 1048 let lineTarget = line < 1 ? 1 : line > lineCount ? lineCount : line;
michael@0 1049 this._doSearch(SEARCH_LINE_FLAG, lineTarget);
michael@0 1050 return;
michael@0 1051 }
michael@0 1052 },
michael@0 1053
michael@0 1054 /**
michael@0 1055 * The blur listener for the search container.
michael@0 1056 */
michael@0 1057 _onBlur: function() {
michael@0 1058 this.clearViews();
michael@0 1059 },
michael@0 1060
michael@0 1061 /**
michael@0 1062 * Called when a filtering key sequence was pressed.
michael@0 1063 *
michael@0 1064 * @param string aOperator
michael@0 1065 * The operator to use for filtering.
michael@0 1066 */
michael@0 1067 _doSearch: function(aOperator = "", aText = "") {
michael@0 1068 this._searchbox.focus();
michael@0 1069 this._searchbox.value = ""; // Need to clear value beforehand. Bug 779738.
michael@0 1070
michael@0 1071 if (aText) {
michael@0 1072 this._searchbox.value = aOperator + aText;
michael@0 1073 return;
michael@0 1074 }
michael@0 1075 if (DebuggerView.editor.somethingSelected()) {
michael@0 1076 this._searchbox.value = aOperator + DebuggerView.editor.getSelection();
michael@0 1077 return;
michael@0 1078 }
michael@0 1079 if (SEARCH_AUTOFILL.indexOf(aOperator) != -1) {
michael@0 1080 let cursor = DebuggerView.editor.getCursor();
michael@0 1081 let content = DebuggerView.editor.getText();
michael@0 1082 let location = DebuggerView.Sources.selectedValue;
michael@0 1083 let source = DebuggerController.Parser.get(content, location);
michael@0 1084 let identifier = source.getIdentifierAt({ line: cursor.line+1, column: cursor.ch });
michael@0 1085
michael@0 1086 if (identifier && identifier.name) {
michael@0 1087 this._searchbox.value = aOperator + identifier.name;
michael@0 1088 this._searchbox.select();
michael@0 1089 this._searchbox.selectionStart += aOperator.length;
michael@0 1090 return;
michael@0 1091 }
michael@0 1092 }
michael@0 1093 this._searchbox.value = aOperator;
michael@0 1094 },
michael@0 1095
michael@0 1096 /**
michael@0 1097 * Called when the source location filter key sequence was pressed.
michael@0 1098 */
michael@0 1099 _doFileSearch: function() {
michael@0 1100 this._doSearch();
michael@0 1101 this._searchboxHelpPanel.openPopup(this._searchbox);
michael@0 1102 },
michael@0 1103
michael@0 1104 /**
michael@0 1105 * Called when the global search filter key sequence was pressed.
michael@0 1106 */
michael@0 1107 _doGlobalSearch: function() {
michael@0 1108 this._doSearch(SEARCH_GLOBAL_FLAG);
michael@0 1109 this._searchboxHelpPanel.hidePopup();
michael@0 1110 },
michael@0 1111
michael@0 1112 /**
michael@0 1113 * Called when the source function filter key sequence was pressed.
michael@0 1114 */
michael@0 1115 _doFunctionSearch: function() {
michael@0 1116 this._doSearch(SEARCH_FUNCTION_FLAG);
michael@0 1117 this._searchboxHelpPanel.hidePopup();
michael@0 1118 },
michael@0 1119
michael@0 1120 /**
michael@0 1121 * Called when the source token filter key sequence was pressed.
michael@0 1122 */
michael@0 1123 _doTokenSearch: function() {
michael@0 1124 this._doSearch(SEARCH_TOKEN_FLAG);
michael@0 1125 this._searchboxHelpPanel.hidePopup();
michael@0 1126 },
michael@0 1127
michael@0 1128 /**
michael@0 1129 * Called when the source line filter key sequence was pressed.
michael@0 1130 */
michael@0 1131 _doLineSearch: function() {
michael@0 1132 this._doSearch(SEARCH_LINE_FLAG);
michael@0 1133 this._searchboxHelpPanel.hidePopup();
michael@0 1134 },
michael@0 1135
michael@0 1136 /**
michael@0 1137 * Called when the variable search filter key sequence was pressed.
michael@0 1138 */
michael@0 1139 _doVariableSearch: function() {
michael@0 1140 this._doSearch(SEARCH_VARIABLE_FLAG);
michael@0 1141 this._searchboxHelpPanel.hidePopup();
michael@0 1142 },
michael@0 1143
michael@0 1144 /**
michael@0 1145 * Called when the variables focus key sequence was pressed.
michael@0 1146 */
michael@0 1147 _doVariablesFocus: function() {
michael@0 1148 DebuggerView.showInstrumentsPane();
michael@0 1149 DebuggerView.Variables.focusFirstVisibleItem();
michael@0 1150 },
michael@0 1151
michael@0 1152 _searchbox: null,
michael@0 1153 _searchboxHelpPanel: null,
michael@0 1154 _globalOperatorButton: null,
michael@0 1155 _globalOperatorLabel: null,
michael@0 1156 _functionOperatorButton: null,
michael@0 1157 _functionOperatorLabel: null,
michael@0 1158 _tokenOperatorButton: null,
michael@0 1159 _tokenOperatorLabel: null,
michael@0 1160 _lineOperatorButton: null,
michael@0 1161 _lineOperatorLabel: null,
michael@0 1162 _variableOperatorButton: null,
michael@0 1163 _variableOperatorLabel: null,
michael@0 1164 _fileSearchKey: "",
michael@0 1165 _globalSearchKey: "",
michael@0 1166 _filteredFunctionsKey: "",
michael@0 1167 _tokenSearchKey: "",
michael@0 1168 _lineSearchKey: "",
michael@0 1169 _variableSearchKey: "",
michael@0 1170 };
michael@0 1171
michael@0 1172 /**
michael@0 1173 * Functions handling the filtered sources UI.
michael@0 1174 */
michael@0 1175 function FilteredSourcesView() {
michael@0 1176 dumpn("FilteredSourcesView was instantiated");
michael@0 1177
michael@0 1178 this._onClick = this._onClick.bind(this);
michael@0 1179 this._onSelect = this._onSelect.bind(this);
michael@0 1180 }
michael@0 1181
michael@0 1182 FilteredSourcesView.prototype = Heritage.extend(ResultsPanelContainer.prototype, {
michael@0 1183 /**
michael@0 1184 * Initialization function, called when the debugger is started.
michael@0 1185 */
michael@0 1186 initialize: function() {
michael@0 1187 dumpn("Initializing the FilteredSourcesView");
michael@0 1188
michael@0 1189 this.anchor = document.getElementById("searchbox");
michael@0 1190 this.widget.addEventListener("select", this._onSelect, false);
michael@0 1191 this.widget.addEventListener("click", this._onClick, false);
michael@0 1192 },
michael@0 1193
michael@0 1194 /**
michael@0 1195 * Destruction function, called when the debugger is closed.
michael@0 1196 */
michael@0 1197 destroy: function() {
michael@0 1198 dumpn("Destroying the FilteredSourcesView");
michael@0 1199
michael@0 1200 this.widget.removeEventListener("select", this._onSelect, false);
michael@0 1201 this.widget.removeEventListener("click", this._onClick, false);
michael@0 1202 this.anchor = null;
michael@0 1203 },
michael@0 1204
michael@0 1205 /**
michael@0 1206 * Schedules searching for a source.
michael@0 1207 *
michael@0 1208 * @param string aToken
michael@0 1209 * The function to search for.
michael@0 1210 * @param number aWait
michael@0 1211 * The amount of milliseconds to wait until draining.
michael@0 1212 */
michael@0 1213 scheduleSearch: function(aToken, aWait) {
michael@0 1214 // The amount of time to wait for the requests to settle.
michael@0 1215 let maxDelay = FILE_SEARCH_ACTION_MAX_DELAY;
michael@0 1216 let delay = aWait === undefined ? maxDelay / aToken.length : aWait;
michael@0 1217
michael@0 1218 // Allow requests to settle down first.
michael@0 1219 setNamedTimeout("sources-search", delay, () => this._doSearch(aToken));
michael@0 1220 },
michael@0 1221
michael@0 1222 /**
michael@0 1223 * Finds file matches in all the displayed sources.
michael@0 1224 *
michael@0 1225 * @param string aToken
michael@0 1226 * The string to search for.
michael@0 1227 */
michael@0 1228 _doSearch: function(aToken, aStore = []) {
michael@0 1229 // Don't continue filtering if the searched token is an empty string.
michael@0 1230 // In contrast with function searching, in this case we don't want to
michael@0 1231 // show a list of all the files when no search token was supplied.
michael@0 1232 if (!aToken) {
michael@0 1233 return;
michael@0 1234 }
michael@0 1235
michael@0 1236 for (let item of DebuggerView.Sources.items) {
michael@0 1237 let lowerCaseLabel = item.attachment.label.toLowerCase();
michael@0 1238 let lowerCaseToken = aToken.toLowerCase();
michael@0 1239 if (lowerCaseLabel.match(lowerCaseToken)) {
michael@0 1240 aStore.push(item);
michael@0 1241 }
michael@0 1242
michael@0 1243 // Once the maximum allowed number of results is reached, proceed
michael@0 1244 // with building the UI immediately.
michael@0 1245 if (aStore.length >= RESULTS_PANEL_MAX_RESULTS) {
michael@0 1246 this._syncView(aStore);
michael@0 1247 return;
michael@0 1248 }
michael@0 1249 }
michael@0 1250
michael@0 1251 // Couldn't reach the maximum allowed number of results, but that's ok,
michael@0 1252 // continue building the UI.
michael@0 1253 this._syncView(aStore);
michael@0 1254 },
michael@0 1255
michael@0 1256 /**
michael@0 1257 * Updates the list of sources displayed in this container.
michael@0 1258 *
michael@0 1259 * @param array aSearchResults
michael@0 1260 * The results array, containing search details for each source.
michael@0 1261 */
michael@0 1262 _syncView: function(aSearchResults) {
michael@0 1263 // If there are no matches found, keep the popup hidden and avoid
michael@0 1264 // creating the view.
michael@0 1265 if (!aSearchResults.length) {
michael@0 1266 window.emit(EVENTS.FILE_SEARCH_MATCH_NOT_FOUND);
michael@0 1267 return;
michael@0 1268 }
michael@0 1269
michael@0 1270 for (let item of aSearchResults) {
michael@0 1271 // Create the element node for the location item.
michael@0 1272 let itemView = this._createItemView(
michael@0 1273 SourceUtils.trimUrlLength(item.attachment.label),
michael@0 1274 SourceUtils.trimUrlLength(item.value, 0, "start")
michael@0 1275 );
michael@0 1276
michael@0 1277 // Append a location item to this container for each match.
michael@0 1278 this.push([itemView], {
michael@0 1279 index: -1, /* specifies on which position should the item be appended */
michael@0 1280 attachment: {
michael@0 1281 url: item.value
michael@0 1282 }
michael@0 1283 });
michael@0 1284 }
michael@0 1285
michael@0 1286 // There's at least one item displayed in this container. Don't select it
michael@0 1287 // automatically if not forced (by tests) or in tandem with an operator.
michael@0 1288 if (this._autoSelectFirstItem || DebuggerView.Filtering.searchOperator) {
michael@0 1289 this.selectedIndex = 0;
michael@0 1290 }
michael@0 1291 this.hidden = false;
michael@0 1292
michael@0 1293 // Signal that file search matches were found and displayed.
michael@0 1294 window.emit(EVENTS.FILE_SEARCH_MATCH_FOUND);
michael@0 1295 },
michael@0 1296
michael@0 1297 /**
michael@0 1298 * The click listener for this container.
michael@0 1299 */
michael@0 1300 _onClick: function(e) {
michael@0 1301 let locationItem = this.getItemForElement(e.target);
michael@0 1302 if (locationItem) {
michael@0 1303 this.selectedItem = locationItem;
michael@0 1304 DebuggerView.Filtering.clearSearch();
michael@0 1305 }
michael@0 1306 },
michael@0 1307
michael@0 1308 /**
michael@0 1309 * The select listener for this container.
michael@0 1310 *
michael@0 1311 * @param object aItem
michael@0 1312 * The item associated with the element to select.
michael@0 1313 */
michael@0 1314 _onSelect: function({ detail: locationItem }) {
michael@0 1315 if (locationItem) {
michael@0 1316 DebuggerView.setEditorLocation(locationItem.attachment.url, undefined, {
michael@0 1317 noCaret: true,
michael@0 1318 noDebug: true
michael@0 1319 });
michael@0 1320 }
michael@0 1321 }
michael@0 1322 });
michael@0 1323
michael@0 1324 /**
michael@0 1325 * Functions handling the function search UI.
michael@0 1326 */
michael@0 1327 function FilteredFunctionsView() {
michael@0 1328 dumpn("FilteredFunctionsView was instantiated");
michael@0 1329
michael@0 1330 this._onClick = this._onClick.bind(this);
michael@0 1331 this._onSelect = this._onSelect.bind(this);
michael@0 1332 }
michael@0 1333
michael@0 1334 FilteredFunctionsView.prototype = Heritage.extend(ResultsPanelContainer.prototype, {
michael@0 1335 /**
michael@0 1336 * Initialization function, called when the debugger is started.
michael@0 1337 */
michael@0 1338 initialize: function() {
michael@0 1339 dumpn("Initializing the FilteredFunctionsView");
michael@0 1340
michael@0 1341 this.anchor = document.getElementById("searchbox");
michael@0 1342 this.widget.addEventListener("select", this._onSelect, false);
michael@0 1343 this.widget.addEventListener("click", this._onClick, false);
michael@0 1344 },
michael@0 1345
michael@0 1346 /**
michael@0 1347 * Destruction function, called when the debugger is closed.
michael@0 1348 */
michael@0 1349 destroy: function() {
michael@0 1350 dumpn("Destroying the FilteredFunctionsView");
michael@0 1351
michael@0 1352 this.widget.removeEventListener("select", this._onSelect, false);
michael@0 1353 this.widget.removeEventListener("click", this._onClick, false);
michael@0 1354 this.anchor = null;
michael@0 1355 },
michael@0 1356
michael@0 1357 /**
michael@0 1358 * Schedules searching for a function in all of the sources.
michael@0 1359 *
michael@0 1360 * @param string aToken
michael@0 1361 * The function to search for.
michael@0 1362 * @param number aWait
michael@0 1363 * The amount of milliseconds to wait until draining.
michael@0 1364 */
michael@0 1365 scheduleSearch: function(aToken, aWait) {
michael@0 1366 // The amount of time to wait for the requests to settle.
michael@0 1367 let maxDelay = FUNCTION_SEARCH_ACTION_MAX_DELAY;
michael@0 1368 let delay = aWait === undefined ? maxDelay / aToken.length : aWait;
michael@0 1369
michael@0 1370 // Allow requests to settle down first.
michael@0 1371 setNamedTimeout("function-search", delay, () => {
michael@0 1372 // Start fetching as many sources as possible, then perform the search.
michael@0 1373 let urls = DebuggerView.Sources.values;
michael@0 1374 let sourcesFetched = DebuggerController.SourceScripts.getTextForSources(urls);
michael@0 1375 sourcesFetched.then(aSources => this._doSearch(aToken, aSources));
michael@0 1376 });
michael@0 1377 },
michael@0 1378
michael@0 1379 /**
michael@0 1380 * Finds function matches in all the sources stored in the cache, and groups
michael@0 1381 * them by location and line number.
michael@0 1382 *
michael@0 1383 * @param string aToken
michael@0 1384 * The string to search for.
michael@0 1385 * @param array aSources
michael@0 1386 * An array of [url, text] tuples for each source.
michael@0 1387 */
michael@0 1388 _doSearch: function(aToken, aSources, aStore = []) {
michael@0 1389 // Continue parsing even if the searched token is an empty string, to
michael@0 1390 // cache the syntax tree nodes generated by the reflection API.
michael@0 1391
michael@0 1392 // Make sure the currently displayed source is parsed first. Once the
michael@0 1393 // maximum allowed number of results are found, parsing will be halted.
michael@0 1394 let currentUrl = DebuggerView.Sources.selectedValue;
michael@0 1395 let currentSource = aSources.filter(([sourceUrl]) => sourceUrl == currentUrl)[0];
michael@0 1396 aSources.splice(aSources.indexOf(currentSource), 1);
michael@0 1397 aSources.unshift(currentSource);
michael@0 1398
michael@0 1399 // If not searching for a specific function, only parse the displayed source,
michael@0 1400 // which is now the first item in the sources array.
michael@0 1401 if (!aToken) {
michael@0 1402 aSources.splice(1);
michael@0 1403 }
michael@0 1404
michael@0 1405 for (let [location, contents] of aSources) {
michael@0 1406 let parsedSource = DebuggerController.Parser.get(contents, location);
michael@0 1407 let sourceResults = parsedSource.getNamedFunctionDefinitions(aToken);
michael@0 1408
michael@0 1409 for (let scriptResult of sourceResults) {
michael@0 1410 for (let parseResult of scriptResult) {
michael@0 1411 aStore.push({
michael@0 1412 sourceUrl: scriptResult.sourceUrl,
michael@0 1413 scriptOffset: scriptResult.scriptOffset,
michael@0 1414 functionName: parseResult.functionName,
michael@0 1415 functionLocation: parseResult.functionLocation,
michael@0 1416 inferredName: parseResult.inferredName,
michael@0 1417 inferredChain: parseResult.inferredChain,
michael@0 1418 inferredLocation: parseResult.inferredLocation
michael@0 1419 });
michael@0 1420
michael@0 1421 // Once the maximum allowed number of results is reached, proceed
michael@0 1422 // with building the UI immediately.
michael@0 1423 if (aStore.length >= RESULTS_PANEL_MAX_RESULTS) {
michael@0 1424 this._syncView(aStore);
michael@0 1425 return;
michael@0 1426 }
michael@0 1427 }
michael@0 1428 }
michael@0 1429 }
michael@0 1430
michael@0 1431 // Couldn't reach the maximum allowed number of results, but that's ok,
michael@0 1432 // continue building the UI.
michael@0 1433 this._syncView(aStore);
michael@0 1434 },
michael@0 1435
michael@0 1436 /**
michael@0 1437 * Updates the list of functions displayed in this container.
michael@0 1438 *
michael@0 1439 * @param array aSearchResults
michael@0 1440 * The results array, containing search details for each source.
michael@0 1441 */
michael@0 1442 _syncView: function(aSearchResults) {
michael@0 1443 // If there are no matches found, keep the popup hidden and avoid
michael@0 1444 // creating the view.
michael@0 1445 if (!aSearchResults.length) {
michael@0 1446 window.emit(EVENTS.FUNCTION_SEARCH_MATCH_NOT_FOUND);
michael@0 1447 return;
michael@0 1448 }
michael@0 1449
michael@0 1450 for (let item of aSearchResults) {
michael@0 1451 // Some function expressions don't necessarily have a name, but the
michael@0 1452 // parser provides us with an inferred name from an enclosing
michael@0 1453 // VariableDeclarator, AssignmentExpression, ObjectExpression node.
michael@0 1454 if (item.functionName && item.inferredName &&
michael@0 1455 item.functionName != item.inferredName) {
michael@0 1456 let s = " " + L10N.getStr("functionSearchSeparatorLabel") + " ";
michael@0 1457 item.displayedName = item.inferredName + s + item.functionName;
michael@0 1458 }
michael@0 1459 // The function doesn't have an explicit name, but it could be inferred.
michael@0 1460 else if (item.inferredName) {
michael@0 1461 item.displayedName = item.inferredName;
michael@0 1462 }
michael@0 1463 // The function only has an explicit name.
michael@0 1464 else {
michael@0 1465 item.displayedName = item.functionName;
michael@0 1466 }
michael@0 1467
michael@0 1468 // Some function expressions have unexpected bounds, since they may not
michael@0 1469 // necessarily have an associated name defining them.
michael@0 1470 if (item.inferredLocation) {
michael@0 1471 item.actualLocation = item.inferredLocation;
michael@0 1472 } else {
michael@0 1473 item.actualLocation = item.functionLocation;
michael@0 1474 }
michael@0 1475
michael@0 1476 // Create the element node for the function item.
michael@0 1477 let itemView = this._createItemView(
michael@0 1478 SourceUtils.trimUrlLength(item.displayedName + "()"),
michael@0 1479 SourceUtils.trimUrlLength(item.sourceUrl, 0, "start"),
michael@0 1480 (item.inferredChain || []).join(".")
michael@0 1481 );
michael@0 1482
michael@0 1483 // Append a function item to this container for each match.
michael@0 1484 this.push([itemView], {
michael@0 1485 index: -1, /* specifies on which position should the item be appended */
michael@0 1486 attachment: item
michael@0 1487 });
michael@0 1488 }
michael@0 1489
michael@0 1490 // There's at least one item displayed in this container. Don't select it
michael@0 1491 // automatically if not forced (by tests).
michael@0 1492 if (this._autoSelectFirstItem) {
michael@0 1493 this.selectedIndex = 0;
michael@0 1494 }
michael@0 1495 this.hidden = false;
michael@0 1496
michael@0 1497 // Signal that function search matches were found and displayed.
michael@0 1498 window.emit(EVENTS.FUNCTION_SEARCH_MATCH_FOUND);
michael@0 1499 },
michael@0 1500
michael@0 1501 /**
michael@0 1502 * The click listener for this container.
michael@0 1503 */
michael@0 1504 _onClick: function(e) {
michael@0 1505 let functionItem = this.getItemForElement(e.target);
michael@0 1506 if (functionItem) {
michael@0 1507 this.selectedItem = functionItem;
michael@0 1508 DebuggerView.Filtering.clearSearch();
michael@0 1509 }
michael@0 1510 },
michael@0 1511
michael@0 1512 /**
michael@0 1513 * The select listener for this container.
michael@0 1514 */
michael@0 1515 _onSelect: function({ detail: functionItem }) {
michael@0 1516 if (functionItem) {
michael@0 1517 let sourceUrl = functionItem.attachment.sourceUrl;
michael@0 1518 let scriptOffset = functionItem.attachment.scriptOffset;
michael@0 1519 let actualLocation = functionItem.attachment.actualLocation;
michael@0 1520
michael@0 1521 DebuggerView.setEditorLocation(sourceUrl, actualLocation.start.line, {
michael@0 1522 charOffset: scriptOffset,
michael@0 1523 columnOffset: actualLocation.start.column,
michael@0 1524 align: "center",
michael@0 1525 noDebug: true
michael@0 1526 });
michael@0 1527 }
michael@0 1528 },
michael@0 1529
michael@0 1530 _searchTimeout: null,
michael@0 1531 _searchFunction: null,
michael@0 1532 _searchedToken: ""
michael@0 1533 });
michael@0 1534
michael@0 1535 /**
michael@0 1536 * Preliminary setup for the DebuggerView object.
michael@0 1537 */
michael@0 1538 DebuggerView.Toolbar = new ToolbarView();
michael@0 1539 DebuggerView.Options = new OptionsView();
michael@0 1540 DebuggerView.Filtering = new FilterView();
michael@0 1541 DebuggerView.FilteredSources = new FilteredSourcesView();
michael@0 1542 DebuggerView.FilteredFunctions = new FilteredFunctionsView();
michael@0 1543 DebuggerView.StackFrames = new StackFramesView();
michael@0 1544 DebuggerView.StackFramesClassicList = new StackFramesClassicListView();

mercurial