Blame devtools/client/debugger/debugger-controller.js

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");