Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 07 Jun 2017 22:52:40 -0400
changeset 362765 7efda263a842e60cd0cc00b3c4a7058c65590702
parent 362764 a49112c7a5765802096b3fc298069b9495436107 (current diff)
parent 362748 6fd10955e89903caf8f92e2c600aa2aa4a2be496 (diff)
child 362766 42388ab3ebfe247d5ccba418ff7bbb807906b61e
child 362941 a5c1b2aba36c6ca27136cef41995fe53ee500a70
push id91193
push userryanvm@gmail.com
push dateThu, 08 Jun 2017 02:55:43 +0000
treeherdermozilla-inbound@42388ab3ebfe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
7efda263a842 / 55.0a1 / 20170608100220 / files
nightly linux64
7efda263a842 / 55.0a1 / 20170608100220 / files
nightly mac
7efda263a842 / 55.0a1 / 20170608030205 / files
nightly win32
7efda263a842 / 55.0a1 / 20170608030205 / files
nightly win64
7efda263a842 / 55.0a1 / 20170608030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge UPGRADE_NSPR_RELEASE UPGRADE_NSS_RELEASE
dom/filehandle/ActorsChild.cpp
dom/filehandle/ActorsChild.h
dom/filehandle/FileHandleBase.cpp
dom/filehandle/FileHandleBase.h
dom/filehandle/FileHandleCommon.cpp
dom/filehandle/FileHandleCommon.h
dom/filehandle/FileRequestBase.h
dom/filehandle/MutableFileBase.cpp
dom/filehandle/MutableFileBase.h
security/nss/lib/ckfw/builtins/certdata.py
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5053,20 +5053,20 @@ var TabsProgressListener = {
 }
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
 
   _openURIInNewTab(aURI, aReferrer, aReferrerPolicy, aIsPrivate,
-                             aIsExternal, aForceNotRemote = false,
-                             aUserContextId = Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
-                             aOpener = null, aTriggeringPrincipal = null,
-                             aNextTabParentId = 0) {
+                   aIsExternal, aForceNotRemote = false,
+                   aUserContextId = Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID,
+                   aOpener = null, aTriggeringPrincipal = null,
+                   aNextTabParentId = 0, aName = "") {
     let win, needToFocusWin;
 
     // try the current window.  if we're in a popup, fall back on the most recent browser window
     if (window.toolbar.visible)
       win = window;
     else {
       win = RecentWindow.getMostRecentBrowserWindow({private: aIsPrivate});
       needToFocusWin = true;
@@ -5090,16 +5090,17 @@ nsBrowserAccess.prototype = {
                                       referrerURI: aReferrer,
                                       referrerPolicy: aReferrerPolicy,
                                       userContextId: aUserContextId,
                                       fromExternal: aIsExternal,
                                       inBackground: loadInBackground,
                                       forceNotRemote: aForceNotRemote,
                                       opener: aOpener,
                                       nextTabParentId: aNextTabParentId,
+                                      name: aName,
                                       });
     let browser = win.gBrowser.getBrowserForTab(tab);
 
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
   },
@@ -5193,17 +5194,17 @@ nsBrowserAccess.prototype = {
         }
         if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
           window.focus();
     }
     return newWindow;
   },
 
   openURIInFrame: function browser_openURIInFrame(aURI, aParams, aWhere, aFlags,
-                                                  aNextTabParentId) {
+                                                  aNextTabParentId, aName) {
     if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
       dump("Error: openURIInFrame can only open in new tabs");
       return null;
     }
 
     var isExternal = !!(aFlags & Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     var userContextId = aParams.openerOriginAttributes &&
@@ -5213,17 +5214,17 @@ nsBrowserAccess.prototype = {
 
     let referrer = aParams.referrer ? makeURI(aParams.referrer) : null;
     let browser = this._openURIInNewTab(aURI, referrer,
                                         aParams.referrerPolicy,
                                         aParams.isPrivate,
                                         isExternal, false,
                                         userContextId, null,
                                         aParams.triggeringPrincipal,
-                                        aNextTabParentId);
+                                        aNextTabParentId, aName);
     if (browser)
       return browser.QueryInterface(Ci.nsIFrameLoaderOwner);
 
     return null;
   },
 
   isTabContentWindow(aWindow) {
     return gBrowser.browsers.some(browser => browser.contentWindow == aWindow);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1553,16 +1553,17 @@
             var aUserContextId;
             var aSameProcessAsFrameLoader;
             var aOriginPrincipal;
             var aOpener;
             var aIsPrerendered;
             var aCreateLazyBrowser;
             var aNextTabParentId;
             var aFocusUrlBar;
+            var aName;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aTriggeringPrincipal      = params.triggeringPrincipal
               aReferrerURI              = params.referrerURI;
               aReferrerPolicy           = params.referrerPolicy;
               aCharset                  = params.charset;
@@ -1579,16 +1580,17 @@
               aUserContextId            = params.userContextId;
               aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
               aOriginPrincipal          = params.originPrincipal;
               aOpener                   = params.opener;
               aIsPrerendered            = params.isPrerendered;
               aCreateLazyBrowser        = params.createLazyBrowser;
               aNextTabParentId          = params.nextTabParentId;
               aFocusUrlBar              = params.focusUrlBar;
+              aName                     = params.name;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
 
             var tab = this.addTab(aURI, {
                                   triggeringPrincipal: aTriggeringPrincipal,
@@ -1607,17 +1609,18 @@
                                   preferredRemoteType: aPreferredRemoteType,
                                   noReferrer: aNoReferrer,
                                   userContextId: aUserContextId,
                                   originPrincipal: aOriginPrincipal,
                                   sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
                                   opener: aOpener,
                                   isPrerendered: aIsPrerendered,
                                   nextTabParentId: aNextTabParentId,
-                                  focusUrlBar: aFocusUrlBar });
+                                  focusUrlBar: aFocusUrlBar,
+                                  name: aName });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
 
@@ -2053,16 +2056,24 @@
               // Gecko is going to read this attribute and use it.
               b.setAttribute("nextTabParentId", aParams.nextTabParentId.toString());
             }
 
             if (aParams.sameProcessAsFrameLoader) {
               b.sameProcessAsFrameLoader = aParams.sameProcessAsFrameLoader;
             }
 
+            // This will be used by gecko to control the name of the opened
+            // window.
+            if (aParams.name) {
+              // XXX: The `name` property is special in HTML and XUL. Should
+              // we use a different attribute name for this?
+              b.setAttribute("name", aParams.name);
+            }
+
             // Create the browserStack container
             var stack = document.createElementNS(NS_XUL, "stack");
             stack.className = "browserStack";
             stack.appendChild(b);
             stack.setAttribute("flex", "1");
 
             // Create the browserContainer
             var browserContainer = document.createElementNS(NS_XUL, "vbox");
@@ -2313,16 +2324,17 @@
             var aDisallowInheritPrincipal;
             var aOpener;
             var aIsPrerendered;
             var aCreateLazyBrowser;
             var aSkipBackgroundNotify;
             var aNextTabParentId;
             var aNoInitialLabel;
             var aFocusUrlBar;
+            var aName;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aTriggeringPrincipal      = params.triggeringPrincipal;
               aReferrerURI              = params.referrerURI;
               aReferrerPolicy           = params.referrerPolicy;
               aCharset                  = params.charset;
@@ -2343,16 +2355,17 @@
               aDisallowInheritPrincipal = params.disallowInheritPrincipal;
               aOpener                   = params.opener;
               aIsPrerendered            = params.isPrerendered;
               aCreateLazyBrowser        = params.createLazyBrowser;
               aSkipBackgroundNotify     = params.skipBackgroundNotify;
               aNextTabParentId          = params.nextTabParentId;
               aNoInitialLabel           = params.noInitialLabel;
               aFocusUrlBar              = params.focusUrlBar;
+              aName                     = params.name;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -2469,17 +2482,18 @@
             if (!b) {
               // No preloaded browser found, create one.
               b = this._createBrowser({ remoteType,
                                         uriIsAboutBlank,
                                         userContextId: aUserContextId,
                                         sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
                                         opener: aOpener,
                                         isPrerendered: aIsPrerendered,
-                                        nextTabParentId: aNextTabParentId });
+                                        nextTabParentId: aNextTabParentId,
+                                        name: aName });
             }
 
             t.linkedBrowser = b;
 
             if (aFocusUrlBar) {
               b._urlbarFocused = true;
             }
 
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -4,16 +4,17 @@ support-files =
   get_user_media_in_frame.html
   get_user_media_content_script.js
   head.js
 
 [browser_devices_get_user_media.js]
 skip-if = (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_anim.js]
 [browser_devices_get_user_media_in_frame.js]
+skip-if = debug # bug 1369731
 [browser_devices_get_user_media_multi_process.js]
 skip-if = e10s && (asan || debug) # bug 1347625
 [browser_devices_get_user_media_screen.js]
 [browser_devices_get_user_media_tear_off_tab.js]
 [browser_devices_get_user_media_unprompted_access.js]
 [browser_devices_get_user_media_unprompted_access_in_frame.js]
 [browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
 skip-if = (os == "win" && bits == 64) # win8: bug 1334752
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -102,16 +102,18 @@ class BasePopup {
     this.closePopup();
   }
 
   destroy() {
     this.extension.forgetOnClose(this);
 
     this.destroyed = true;
     this.browserLoadedDeferred.reject(new Error("Popup destroyed"));
+    // Ignore unhandled rejections if the "attach" method is not called.
+    this.browserLoaded.catch(() => {});
 
     BasePopup.instances.get(this.window).delete(this.extension);
 
     return this.browserReady.then(() => {
       if (this.browser) {
         this.destroyBrowser(this.browser, true);
         this.browser.remove();
       }
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -1305,17 +1305,17 @@ var SessionStoreInternal = {
         }
         TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
         this.initializeWindow(aWindow, initialState);
         TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
 
         // Let everyone know we're done.
         this._deferredInitialized.resolve();
       }
-    }, console.error);
+    }).catch(console.error);
   },
 
   /**
    * On window close...
    * - remove event listeners from tabs
    * - save all window data
    * @param aWindow
    *        Window reference
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -265,17 +265,19 @@ FormAutofillParent.prototype = {
                                         Services.ppmm.initialProcessData.autofillSavedFieldNames);
     this._updateStatus();
   },
 
   _onFormSubmit(data, target) {
     let {address} = data;
 
     if (address.guid) {
-      // TODO: Show update doorhanger(bug 1303513) and set probe(bug 990200)
-      // if (!profileStorage.addresses.mergeIfPossible(address.guid, address.record)) {
-      // }
+      if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record)) {
+        // TODO: Show update doorhanger(bug 1303513) and set probe(bug 990200)
+        return;
+      }
+      this.profileStorage.addresses.notifyUsed(address.guid);
     } else {
       // TODO: Add first time use probe(bug 990199) and doorhanger(bug 1303510)
       // profileStorage.addresses.add(address.record);
     }
   },
 };
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -374,16 +374,22 @@ class AutofillRecords {
     }
   }
 
   // An interface to be inherited.
   _recordReadProcessor(record, config) {}
 
   // An interface to be inherited.
   _recordWriteProcessor(record) {}
+
+  // An interface to be inherited.
+  mergeIfPossible(guid, record) {}
+
+  // An interface to be inherited.
+  mergeToStorage(targetRecord) {}
 }
 
 class Addresses extends AutofillRecords {
   constructor(store) {
     super(store, "addresses", VALID_PROFILE_FIELDS, ADDRESS_SCHEMA_VERSION);
   }
 
   _recordReadProcessor(profile, {noComputedFields} = {}) {
@@ -449,16 +455,95 @@ class Addresses extends AutofillRecords 
       });
 
       // Concatenate "address-line*" if "street-address" is omitted.
       if (!profile["street-address"]) {
         profile["street-address"] = addressLines.join("\n");
       }
     }
   }
+
+  /**
+   * Merge new address into the specified address if mergeable.
+   *
+   * @param  {string} guid
+   *         Indicates which address to merge.
+   * @param  {Object} address
+   *         The new address used to merge into the old one.
+   * @returns {boolean}
+   *          Return true if address is merged into target with specific guid or false if not.
+   */
+  mergeIfPossible(guid, address) {
+    this.log.debug("mergeIfPossible:", guid, address);
+
+    let addressFound = this._findByGUID(guid);
+    if (!addressFound) {
+      throw new Error("No matching address.");
+    }
+
+    let addressToMerge = this._clone(address);
+    this._normalizeRecord(addressToMerge);
+    let hasMatchingField = false;
+
+    for (let field of this.VALID_FIELDS) {
+      if (addressToMerge[field] !== undefined && addressFound[field] !== undefined) {
+        if (addressToMerge[field] != addressFound[field]) {
+          this.log.debug("Conflicts: field", field, "has different value.");
+          return false;
+        }
+        hasMatchingField = true;
+      }
+    }
+
+    // We merge the address only when at least one field has the same value.
+    if (!hasMatchingField) {
+      this.log.debug("Unable to merge because no field has the same value");
+      return false;
+    }
+
+    // Early return if the data is the same.
+    let exactlyMatch = this.VALID_FIELDS.every((field) =>
+      addressFound[field] === addressToMerge[field]
+    );
+    if (exactlyMatch) {
+      return true;
+    }
+
+    for (let field in addressToMerge) {
+      if (this.VALID_FIELDS.includes(field)) {
+        addressFound[field] = addressToMerge[field];
+      }
+    }
+
+    addressFound.timeLastModified = Date.now();
+    this._store.saveSoon();
+    let str = Cc["@mozilla.org/supports-string;1"]
+                 .createInstance(Ci.nsISupportsString);
+    str.data = guid;
+    Services.obs.notifyObservers(str, "formautofill-storage-changed", "merge");
+    return true;
+  }
+
+  /**
+   * Merge the address if storage has multiple mergeable records.
+   * @param {Object} targetAddress
+   *        The address for merge.
+   * @returns {boolean}
+   *          Return true if the target address is mergeable or false if not.
+   */
+  mergeToStorage(targetAddress) {
+    let merged = false;
+    for (let address of this._store.data[this._collectionName]) {
+      if (this.mergeIfPossible(address.guid, targetAddress)) {
+        merged = true;
+      }
+    }
+    this.log.debug("Existing records matching and merging is", merged);
+    return merged;
+  }
 }
 
 class CreditCards extends AutofillRecords {
   constructor(store) {
     super(store, "creditCards", VALID_CREDIT_CARD_FIELDS, CREDIT_CARD_SCHEMA_VERSION);
   }
 
   _recordReadProcessor(creditCard, {noComputedFields} = {}) {
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -1,13 +1,15 @@
 /**
  * Provides infrastructure for automated formautofill components tests.
  */
 
-/* exported getTempFile, loadFormAutofillContent, runHeuristicsTest, sinon */
+/* exported getTempFile, loadFormAutofillContent, runHeuristicsTest, sinon,
+ *          initProfileStorage
+ */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
@@ -74,16 +76,36 @@ function getTempFile(leafName) {
     if (file.exists()) {
       file.remove(false);
     }
   });
 
   return file;
 }
 
+async function initProfileStorage(fileName, records) {
+  let {ProfileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
+  let path = getTempFile(fileName).path;
+  let profileStorage = new ProfileStorage(path);
+  await profileStorage.initialize();
+
+  if (!records || !Array.isArray(records)) {
+    return profileStorage;
+  }
+
+  let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
+                                          (subject, data) => data == "add");
+  for (let record of records) {
+    do_check_true(profileStorage.addresses.add(record));
+    await onChanged;
+  }
+  await profileStorage._saveImmediately();
+  return profileStorage;
+}
+
 function runHeuristicsTest(patterns, fixturePathPrefix) {
   Cu.import("resource://gre/modules/FormLikeFactory.jsm");
   Cu.import("resource://formautofill/FormAutofillHeuristics.jsm");
   Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
   patterns.forEach(testPattern => {
     add_task(function* () {
       do_print("Starting test fixture: " + testPattern.fixturePath);
@@ -110,17 +132,17 @@ function runHeuristicsTest(patterns, fix
           expectedField.elementWeakRef = field.elementWeakRef;
           Assert.deepEqual(field, expectedField);
         });
       });
     });
   });
 }
 
-add_task(function* head_initialize() {
+add_task(async function head_initialize() {
   Services.prefs.setBoolPref("extensions.formautofill.experimental", true);
   Services.prefs.setBoolPref("extensions.formautofill.heuristics.enabled", true);
   Services.prefs.setBoolPref("dom.forms.autocomplete.experimental", true);
 
   // Clean up after every test.
   do_register_cleanup(function head_cleanup() {
     Services.prefs.clearUserPref("extensions.formautofill.experimental");
     Services.prefs.clearUserPref("extensions.formautofill.heuristics.enabled");
--- a/browser/extensions/formautofill/test/unit/test_addressRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_addressRecords.js
@@ -1,16 +1,14 @@
 /**
  * Tests ProfileStorage object with addresses records.
  */
 
 "use strict";
 
-const {ProfileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
-
 const TEST_STORE_FILE_NAME = "test-profile.json";
 
 const TEST_ADDRESS_1 = {
   "given-name": "Timothy",
   "additional-name": "John",
   "family-name": "Berners-Lee",
   organization: "World Wide Web Consortium",
   "street-address": "32 Vassar Street\nMIT Room 32-G524",
@@ -27,64 +25,115 @@ const TEST_ADDRESS_2 = {
   country: "US",
 };
 
 const TEST_ADDRESS_3 = {
   "street-address": "Other Address",
   "postal-code": "12345",
 };
 
+const TEST_ADDRESS_4 = {
+  "given-name": "Timothy",
+  "additional-name": "John",
+  "family-name": "Berners-Lee",
+  organization: "World Wide Web Consortium",
+};
+
 const TEST_ADDRESS_WITH_INVALID_FIELD = {
   "street-address": "Another Address",
   invalidField: "INVALID",
 };
 
-let prepareTestRecords = async function(path) {
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
-
-  let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
-                                          (subject, data) => data == "add");
-  do_check_true(profileStorage.addresses.add(TEST_ADDRESS_1));
-  await onChanged;
-  do_check_true(profileStorage.addresses.add(TEST_ADDRESS_2));
-  await profileStorage._saveImmediately();
-};
+const MERGE_TESTCASES = [
+  {
+    description: "Merge a superset",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+    },
+    addressToMerge: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+      country: "US",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+      country: "US",
+    },
+  },
+  {
+    description: "Merge a subset",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+      country: "US",
+    },
+    addressToMerge: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+      country: "US",
+    },
+  },
+  {
+    description: "Merge an address with partial overlaps",
+    addressInStorage: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+    },
+    addressToMerge: {
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+      country: "US",
+    },
+    expectedAddress: {
+      "given-name": "Timothy",
+      "street-address": "331 E. Evelyn Avenue",
+      "tel": "1-650-903-0800",
+      country: "US",
+    },
+  },
+];
 
 let do_check_record_matches = (recordWithMeta, record) => {
   for (let key in record) {
     do_check_eq(recordWithMeta[key], record[key]);
   }
 };
 
 add_task(async function test_initialize() {
-  let path = getTempFile(TEST_STORE_FILE_NAME).path;
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME);
 
   do_check_eq(profileStorage._store.data.version, 1);
   do_check_eq(profileStorage._store.data.addresses.length, 0);
 
   let data = profileStorage._store.data;
   Assert.deepEqual(data.addresses, []);
 
   await profileStorage._saveImmediately();
 
-  profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME);
 
   Assert.deepEqual(profileStorage._store.data, data);
 });
 
 add_task(async function test_getAll() {
-  let path = getTempFile(TEST_STORE_FILE_NAME).path;
-  await prepareTestRecords(path);
-
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
 
   let addresses = profileStorage.addresses.getAll();
 
   do_check_eq(addresses.length, 2);
   do_check_record_matches(addresses[0], TEST_ADDRESS_1);
   do_check_record_matches(addresses[1], TEST_ADDRESS_2);
 
   // Check computed fields.
@@ -99,41 +148,35 @@ add_task(async function test_getAll() {
   do_check_eq(addresses[0]["address-line2"], undefined);
 
   // Modifying output shouldn't affect the storage.
   addresses[0].organization = "test";
   do_check_record_matches(profileStorage.addresses.getAll()[0], TEST_ADDRESS_1);
 });
 
 add_task(async function test_get() {
-  let path = getTempFile(TEST_STORE_FILE_NAME).path;
-  await prepareTestRecords(path);
-
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
 
   let addresses = profileStorage.addresses.getAll();
   let guid = addresses[0].guid;
 
   let address = profileStorage.addresses.get(guid);
   do_check_record_matches(address, TEST_ADDRESS_1);
 
   // Modifying output shouldn't affect the storage.
   address.organization = "test";
   do_check_record_matches(profileStorage.addresses.get(guid), TEST_ADDRESS_1);
 
   do_check_eq(profileStorage.addresses.get("INVALID_GUID"), null);
 });
 
 add_task(async function test_getByFilter() {
-  let path = getTempFile(TEST_STORE_FILE_NAME).path;
-  await prepareTestRecords(path);
-
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
 
   let filter = {info: {fieldName: "street-address"}, searchString: "Some"};
   let addresses = profileStorage.addresses.getByFilter(filter);
   do_check_eq(addresses.length, 1);
   do_check_record_matches(addresses[0], TEST_ADDRESS_2);
 
   filter = {info: {fieldName: "country"}, searchString: "u"};
   addresses = profileStorage.addresses.getByFilter(filter);
@@ -156,21 +199,18 @@ add_task(async function test_getByFilter
 
   // Prevent broken while searching the property that does not exist.
   filter = {info: {fieldName: "tel"}, searchString: "1"};
   addresses = profileStorage.addresses.getByFilter(filter);
   do_check_eq(addresses.length, 0);
 });
 
 add_task(async function test_add() {
-  let path = getTempFile(TEST_STORE_FILE_NAME).path;
-  await prepareTestRecords(path);
-
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
 
   let addresses = profileStorage.addresses.getAll();
 
   do_check_eq(addresses.length, 2);
 
   do_check_record_matches(addresses[0], TEST_ADDRESS_1);
   do_check_record_matches(addresses[1], TEST_ADDRESS_2);
 
@@ -181,38 +221,32 @@ add_task(async function test_add() {
   do_check_eq(addresses[0].timeLastUsed, 0);
   do_check_eq(addresses[0].timesUsed, 0);
 
   Assert.throws(() => profileStorage.addresses.add(TEST_ADDRESS_WITH_INVALID_FIELD),
     /"invalidField" is not a valid field\./);
 });
 
 add_task(async function test_update() {
-  let path = getTempFile(TEST_STORE_FILE_NAME).path;
-  await prepareTestRecords(path);
-
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
 
   let addresses = profileStorage.addresses.getAll();
   let guid = addresses[1].guid;
   let timeLastModified = addresses[1].timeLastModified;
 
   let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
                                           (subject, data) => data == "update");
 
   do_check_neq(addresses[1].country, undefined);
 
   profileStorage.addresses.update(guid, TEST_ADDRESS_3);
   await onChanged;
   await profileStorage._saveImmediately();
 
-  profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
-
   let address = profileStorage.addresses.get(guid);
 
   do_check_eq(address.country, undefined);
   do_check_neq(address.timeLastModified, timeLastModified);
   do_check_record_matches(address, TEST_ADDRESS_3);
 
   Assert.throws(
     () => profileStorage.addresses.update("INVALID_GUID", TEST_ADDRESS_3),
@@ -221,66 +255,111 @@ add_task(async function test_update() {
 
   Assert.throws(
     () => profileStorage.addresses.update(guid, TEST_ADDRESS_WITH_INVALID_FIELD),
     /"invalidField" is not a valid field\./
   );
 });
 
 add_task(async function test_notifyUsed() {
-  let path = getTempFile(TEST_STORE_FILE_NAME).path;
-  await prepareTestRecords(path);
-
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
 
   let addresses = profileStorage.addresses.getAll();
   let guid = addresses[1].guid;
   let timeLastUsed = addresses[1].timeLastUsed;
   let timesUsed = addresses[1].timesUsed;
 
   let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
                                           (subject, data) => data == "notifyUsed");
 
   profileStorage.addresses.notifyUsed(guid);
   await onChanged;
   await profileStorage._saveImmediately();
 
-  profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
-
   let address = profileStorage.addresses.get(guid);
 
   do_check_eq(address.timesUsed, timesUsed + 1);
   do_check_neq(address.timeLastUsed, timeLastUsed);
 
   Assert.throws(() => profileStorage.addresses.notifyUsed("INVALID_GUID"),
     /No matching record\./);
 });
 
 add_task(async function test_remove() {
-  let path = getTempFile(TEST_STORE_FILE_NAME).path;
-  await prepareTestRecords(path);
-
-  let profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
 
   let addresses = profileStorage.addresses.getAll();
   let guid = addresses[1].guid;
 
   let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
                                           (subject, data) => data == "remove");
 
   do_check_eq(addresses.length, 2);
 
   profileStorage.addresses.remove(guid);
   await onChanged;
   await profileStorage._saveImmediately();
 
-  profileStorage = new ProfileStorage(path);
-  await profileStorage.initialize();
-
   addresses = profileStorage.addresses.getAll();
 
   do_check_eq(addresses.length, 1);
 
   do_check_eq(profileStorage.addresses.get(guid), null);
 });
+
+MERGE_TESTCASES.forEach((testcase) => {
+  add_task(async function test_merge() {
+    do_print("Starting testcase: " + testcase.description);
+    let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                  [testcase.addressInStorage]);
+    let addresses = profileStorage.addresses.getAll();
+    // Merge address and verify the guid in notifyObservers subject
+    let onMerged = TestUtils.topicObserved(
+      "formautofill-storage-changed",
+      (subject, data) =>
+        data == "merge" && subject.QueryInterface(Ci.nsISupportsString).data == addresses[0].guid
+    );
+    let timeLastModified = addresses[0].timeLastModified;
+    Assert.equal(
+      profileStorage.addresses.mergeIfPossible(addresses[0].guid, testcase.addressToMerge),
+      true);
+    await onMerged;
+    addresses = profileStorage.addresses.getAll();
+    Assert.equal(addresses.length, 1);
+    Assert.notEqual(addresses[0].timeLastModified, timeLastModified);
+    do_check_record_matches(addresses[0], testcase.expectedAddress);
+  });
+});
+
+add_task(async function test_merge_same_address() {
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME, [TEST_ADDRESS_1]);
+  let addresses = profileStorage.addresses.getAll();
+  let timeLastModified = addresses[0].timeLastModified;
+  // Merge same address will still return true but it won't update timeLastModified.
+  Assert.equal(profileStorage.addresses.mergeIfPossible(addresses[0].guid, TEST_ADDRESS_1), true);
+  Assert.equal(addresses[0].timeLastModified, timeLastModified);
+});
+
+add_task(async function test_merge_unable_merge() {
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
+
+  let addresses = profileStorage.addresses.getAll();
+  // Unable to merge because of conflict
+  do_check_eq(profileStorage.addresses.mergeIfPossible(addresses[1].guid, TEST_ADDRESS_3), false);
+
+  // Unable to merge because no overlap
+  do_check_eq(profileStorage.addresses.mergeIfPossible(addresses[1].guid, TEST_ADDRESS_4), false);
+});
+
+add_task(async function test_mergeToStorage() {
+  let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME,
+                                                [TEST_ADDRESS_1, TEST_ADDRESS_2]);
+  // Merge an address to storage
+  let anotherAddress = profileStorage.addresses._clone(TEST_ADDRESS_2);
+  profileStorage.addresses.add(anotherAddress);
+  anotherAddress.email = "timbl@w3.org";
+  do_check_eq(profileStorage.addresses.mergeToStorage(anotherAddress), true);
+  do_check_eq(profileStorage.addresses.getAll()[1].email, anotherAddress.email);
+  do_check_eq(profileStorage.addresses.getAll()[2].email, anotherAddress.email);
+});
--- a/browser/extensions/shield-recipe-client/lib/PreferenceExperiments.jsm
+++ b/browser/extensions/shield-recipe-client/lib/PreferenceExperiments.jsm
@@ -250,17 +250,18 @@ this.PreferenceExperiments = {
         `An observer for the preference experiment ${experimentName} is already active.`
       );
     }
 
     const observerInfo = {
       preferenceName,
       observer(newValue) {
         if (newValue !== preferenceValue) {
-          PreferenceExperiments.stop(experimentName, false);
+          PreferenceExperiments.stop(experimentName, false)
+                               .catch(Cu.reportError);
         }
       },
     };
     experimentObservers.set(experimentName, observerInfo);
     Preferences.observe(preferenceName, observerInfo.observer);
   },
 
   /**
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -378,17 +378,17 @@ this.TabCrashHandler = {
     // with the empty string.
     if (!includeURL) {
       extraExtraKeyVals["URL"] = "";
     }
 
     CrashSubmit.submit(dumpID, {
       recordSubmission: true,
       extraExtraKeyVals,
-    }).then(null, Cu.reportError);
+    }).catch(Cu.reportError);
 
     this.prefs.setBoolPref("sendReport", true);
     this.prefs.setBoolPref("includeURL", includeURL);
     this.prefs.setBoolPref("emailMe", emailMe);
     if (emailMe) {
       this.prefs.setCharPref("email", email);
     } else {
       this.prefs.setCharPref("email", "");
@@ -876,17 +876,17 @@ this.UnsubmittedCrashHandler = {
    *        The array of reportIDs to submit.
    */
   submitReports(reportIDs) {
     for (let reportID of reportIDs) {
       CrashSubmit.submit(reportID, {
         extraExtraKeyVals: {
           "SubmittedFromInfobar": true,
         },
-      });
+      }).catch(Cu.reportError);
     }
   },
 };
 
 this.PluginCrashReporter = {
   /**
    * Makes the PluginCrashReporter ready to hear about and
    * submit crash reports.
@@ -990,17 +990,17 @@ this.PluginCrashReporter = {
     let { pluginDumpID, browserDumpID } = this.crashReports.get(runID);
 
     let submissionPromise = CrashSubmit.submit(pluginDumpID, {
       recordSubmission: true,
       extraExtraKeyVals: keyVals,
     });
 
     if (browserDumpID)
-      CrashSubmit.submit(browserDumpID);
+      CrashSubmit.submit(browserDumpID).catch(Cu.reportError);
 
     this.broadcastState(runID, "submitting");
 
     submissionPromise.then(() => {
       this.broadcastState(runID, "success");
     }, () => {
       this.broadcastState(runID, "failed");
     });
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1022,8 +1022,13 @@ option('--enable-hardening', env='MOZ_SE
 
 @depends('--enable-hardening', c_compiler)
 def security_hardening_cflags(value, c_compiler):
     if value and c_compiler.type in ['gcc', 'clang']:
         return '-fstack-protector-strong'
 
 add_old_configure_assignment('HARDENING_CFLAGS', security_hardening_cflags)
 imply_option('--enable-pie', depends_if('--enable-hardening')(lambda v: v))
+
+option(env='RUSTFLAGS',
+       nargs=1,
+       help='Rust compiler flags')
+set_config('RUSTFLAGS', depends('RUSTFLAGS')(lambda flags: flags))
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -931,25 +931,26 @@ endif
 # Cargo currently supports only two interesting profiles for building:
 # development and release.  Those map (roughly) to --enable-debug and
 # --disable-debug in Gecko, respectively, but there's another axis that we'd
 # like to support: --{disable,enable}-optimize.  Since that would be four
 # choices, and Cargo only supports two, we choose to enable various
 # optimization levels in our Cargo.toml files all the time, and override the
 # optimization level here, if necessary.  (The Cargo.toml files already
 # specify debug-assertions appropriately for --{disable,enable}-debug.)
+default_rustflags =
 ifndef MOZ_OPTIMIZE
-rustflags = -C opt-level=0
+default_rustflags = -C opt-level=0
 # Unfortunately, -C opt-level=0 implies -C debug-assertions, so we need
 # to explicitly disable them when MOZ_DEBUG_RUST is not set.
 ifndef MOZ_DEBUG_RUST
-rustflags += -C debug-assertions=no
+default_rustflags += -C debug-assertions=no
 endif
-rustflags_override = RUSTFLAGS='$(rustflags)'
 endif
+rustflags_override = RUSTFLAGS='$(default_rustflags) $(RUSTFLAGS)'
 
 ifdef MOZ_MSVCBITS
 # If we are building a MozillaBuild shell, we want to clear out the
 # vcvars.bat environment variables for cargo builds. This is because
 # a 32-bit MozillaBuild shell on a 64-bit machine will try to use
 # the 32-bit compiler/linker for everything, while cargo/rustc wants
 # to use the 64-bit linker for build.rs scripts. This conflict results
 # in a build failure (see bug 1350001). So we clear out the environment
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -38,17 +38,25 @@
   color: var(--theme-comment);
   font-size: var(--ui-element-font-size);
   border: 1px solid var(--theme-splitter-color);
   padding: calc(var(--base-spacing) / 2);
   margin: 0 var(--base-spacing);
   transition: var(--base-transition);
 }
 
-.landing-page .panel header input[type=button] {
+.landing-page .panel .hero {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.landing-page .panel input[type=button] {
   background-color: var(--theme-tab-toolbar-background);
   color: var(--theme-comment);
   font-size: var(--ui-element-font-size);
   border: 1px solid var(--theme-splitter-color);
   padding: calc(var(--base-spacing) / 2);
   margin: 0 var(--base-spacing);
   transition: var(--base-transition);
 }
@@ -874,21 +882,16 @@ html .arrow.expanded svg {
   border-bottom: 1px solid var(--theme-splitter-color);
   padding-right: 10px;
   display: flex;
   flex-shrink: 0;
 }
 
 .search-field.big {
   height: 40px;
-  font-size: 14px;
-}
-
-.search-field.big input {
-  font-size: 14px;
 }
 
 .search-field i {
   display: block;
   padding: 0;
   width: 16px;
 }
 
@@ -1058,16 +1061,82 @@ html .arrow.expanded svg {
 
 .search-bar .result-list {
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
 .theme-dark .result-list {
   background-color: var(--theme-body-background);
 }
+.tree {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  -o-user-select: none;
+  user-select: none;
+
+  white-space: nowrap;
+  overflow: auto;
+  min-width: 100%;
+}
+
+.tree button {
+  display: block;
+}
+
+.tree .node {
+  padding: 2px 5px;
+  position: relative;
+  cursor: pointer;
+}
+
+.tree .node.focused {
+  color: white;
+  background-color: var(--theme-selection-background);
+}
+
+.theme-dark .tree .node.focused {
+  background-color: var(--theme-selection-background-semitransparent);
+}
+
+html:not([dir="rtl"]) .tree .node > div {
+  margin-left: 10px;
+}
+
+html[dir="rtl"] .tree .node > div {
+  margin-right: 10px;
+}
+
+.tree .node.focused svg {
+  fill: white;
+}
+
+.tree-node button {
+  position: fixed;
+}
+.project-text-search .result {
+  display: flex;
+  margin-left: 20px;
+}
+
+.project-text-search .file-result {
+  font-weight: bold;
+  margin-top: 20px;
+  line-height: 20px;
+}
+
+.project-text-search .line-match {
+  font-family: monospace;
+  display: "flex";
+  grow: 1;
+}
+
+.project-text-search .result .line-number {
+  padding-right: 5px;
+}
 .sources-panel {
   flex: 1;
   display: flex;
   flex-direction: column;
   overflow: hidden;
   position: relative;
 }
 
@@ -1124,19 +1193,69 @@ html .arrow.expanded svg {
 .no-sources-message {
   font-size: 12px;
   color: var(--theme-comment-alt);
   font-weight: lighter;
   padding-top: 5px;
   text-align: center;
 }
 
+.sources-panel .source-footer {
+  position: relative;
+}
+
+.sources-panel .outline {
+  display: flex;
+  flex: 1;
+}
+
+.sources-panel .outline.hidden {
+  display: none;
+}
+
 .hidden {
   display: none;
 }
+
+.source-footer {
+  width: 100%;
+}
+
+.source-footer .tab {
+  flex: 1;
+  justify-content: center;
+  border: 1px solid transparent;
+  border-bottom-left-radius: 2px;
+  border-bottom-right-radius: 2px;
+  display: inline-flex;
+  position: relative;
+  transition: all 0.25s ease;
+  overflow: hidden;
+  padding: 5px;
+  margin-bottom: 4px;
+  margin-top: -1px;
+}
+
+.source-footer .tab:hover {
+  background-color: var(--theme-toolbar-background-alt);
+  border-color: var(--theme-splitter-color);
+  cursor: pointer;
+}
+
+.source-footer .tab.active {
+  color: var(--theme-body-color);
+  background-color: var(--theme-body-background);
+  border-color: var(--theme-splitter-color);
+  border-top-color: transparent;
+}
+
+.source-footer .tab.active path,
+.source-footer .tab:hover path {
+  fill: var(--theme-body-color);
+}
 .outline-list {
   list-style-type: "-";
 }
 
 .outline-list__element {
   color: blue;
   padding-left: 0.5rem;
 }
@@ -1156,86 +1275,44 @@ html .arrow.expanded svg {
 
 .function-signature .paren {
   color: var(--object-color);
 }
 
 .function-signature .comma {
   color: var(--object-color);
 }
-.tree {
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  -ms-user-select: none;
-  -o-user-select: none;
-  user-select: none;
-
-  white-space: nowrap;
-  overflow: auto;
-  min-width: 100%;
-}
-
-.tree button {
-  display: block;
-}
-
-.tree .node {
-  padding: 2px 5px;
-  position: relative;
-  cursor: pointer;
-}
-
-.tree .node.focused {
-  color: white;
-  background-color: var(--theme-selection-background);
-}
-
-.theme-dark .tree .node.focused {
-  background-color: var(--theme-selection-background-semitransparent);
-}
-
-html:not([dir="rtl"]) .tree .node > div {
-  margin-left: 10px;
-}
-
-html[dir="rtl"] .tree .node > div {
-  margin-right: 10px;
-}
-
-.tree .node.focused svg {
-  fill: white;
-}
-
-.tree-node button {
-  position: fixed;
-}
 .conditional-breakpoint-panel {
   cursor: initial;
   margin: 1em 0;
   position: relative;
   display: flex;
   align-items: center;
   background: var(--theme-toolbar-background);
   border-top: 1px solid var(--theme-splitter-color);
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
 .conditional-breakpoint-panel .prompt {
   font-size: 1.8em;
-  color: var(--theme-comment-alt);
+  color: var(--theme-conditional-breakpoint-color);
   padding-left: 3px;
+  padding-right: 3px;
+  padding-bottom: 3px;
+  text-align: right;
+  width: 30px;
 }
 
 .conditional-breakpoint-panel input {
   margin: 5px 10px;
   width: calc(100% - 4em);
   border: none;
   background: var(--theme-toolbar-background);
   font-size: 14px;
-  color: var(--theme-comment);
+  color: var(--theme-conditional-breakpoint-color);
   line-height: 30px;
 }
 
 .conditional-breakpoint-panel input:focus {
   outline-width: 0;
 }
 .toggle-button-start,
 .toggle-button-end {
@@ -1356,17 +1433,16 @@ html .toggle-button-end.vertical svg {
 
 .source-footer .black-box.blackboxed svg {
   fill: var(--theme-highlight-blue);
 }
 
 .source-footer .blackbox-summary {
   color: var(--theme-body-color);
 }
-
 .search-bar {
   display: flex;
   flex-direction: column;
 }
 
 .search-bar .search-field {
   padding-left: 7px;
   height: var(--editor-searchbar-height);
@@ -1566,17 +1642,17 @@ html .toggle-button-end.vertical svg {
   color: var(--theme-highlight-blue);
   text-decoration: underline;
 }
 .popover .preview .header .link:hover {
   cursor: pointer;
 }
 
 .selected-token {
-  background-color: var(--theme-search-overlays-semitransparent);
+  background-color: var(--theme-highlight-yellow);
   color: var(--theme-selection-color);
 }
 
 .selected-token:hover {
   cursor: pointer;
 }
 
 .popover .preview .function-signature {
@@ -1646,36 +1722,58 @@ html .toggle-button-end.vertical svg {
 .add-to-expression-bar .expression-to-save-button {
   font-size: 14px;
   color: var(--theme-comment);
   cursor: pointer;
 }
 .editor-wrapper {
   --debug-line-background: rgba(226, 236, 247, 0.5);
   --debug-line-border: rgb(145, 188, 219);
+  --editor-searchbar-height: 27px;
+  --editor-second-searchbar-height: 27px;
+
 }
 
 .theme-dark .editor-wrapper {
   --debug-line-background: rgb(73, 82, 103);
   --debug-line-border: rgb(119, 134, 162);
 }
 
+.editor-wrapper .CodeMirror-linewidget {
+  margin-right: -7px;
+}
+
+.theme-dark {
+  --theme-conditional-breakpoint-color: #9fa4a9;
+}
+
+.theme-light {
+  --theme-conditional-breakpoint-color: #ccd1d5;
+}
+
+.theme-firebug {
+  --theme-conditional-breakpoint-color: #ccd1d5;
+}
+
+.out-of-scope .CodeMirror-line,
+.out-of-scope .CodeMirror-linenumber {
+  opacity: 0.8;
+}
+
 /**
  * There's a known codemirror flex issue with chrome that this addresses.
  * BUG https://github.com/devtools-html/debugger.html/issues/63
  */
 .editor-wrapper {
   position: absolute;
   height: calc(100% - 31px);
   width: 100%;
   top: 30px;
   left: 0px;
   --editor-footer-height: 27px;
-  --editor-searchbar-height: 27px;
-  --editor-second-searchbar-height: 27px;
 }
 
 html[dir="rtl"] .editor-mount {
   direction: ltr;
 }
 
 .editor-wrapper .breakpoints {
   position: absolute;
@@ -2280,16 +2378,17 @@ html .breakpoints-list .breakpoint.pause
 .accordion ._header {
   background-color: var(--theme-toolbar-background);
   border-bottom: 1px solid var(--theme-splitter-color);
   cursor: pointer;
   font-size: 12px;
   padding: 5px;
   transition: all 0.25s ease;
   width: 100%;
+  align-items: center;
 
   -webkit-user-select: none;
   -moz-user-select: none;
   -ms-user-select: none;
   -o-user-select: none;
   user-select: none;
 }
 
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -1,18 +1,18 @@
 (function webpackUniversalModuleDefinition(root, factory) {
 	if(typeof exports === 'object' && typeof module === 'object')
-		module.exports = factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor"));
+		module.exports = factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-dom"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor"));
 	else if(typeof define === 'function' && define.amd)
-		define(["devtools/client/shared/vendor/react", "Services", "devtools/shared/flags", "devtools/client/sourceeditor/editor"], factory);
+		define(["devtools/client/shared/vendor/react", "Services", "devtools/client/shared/vendor/react-dom", "devtools/shared/flags", "devtools/client/sourceeditor/editor"], factory);
 	else {
-		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/shared/flags"], root["devtools/client/sourceeditor/editor"]);
+		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-dom"), require("devtools/shared/flags"), require("devtools/client/sourceeditor/editor")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/client/shared/vendor/react-dom"], root["devtools/shared/flags"], root["devtools/client/sourceeditor/editor"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(this, function(__WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_29__, __WEBPACK_EXTERNAL_MODULE_121__, __WEBPACK_EXTERNAL_MODULE_995__) {
+})(this, function(__WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_29__, __WEBPACK_EXTERNAL_MODULE_31__, __WEBPACK_EXTERNAL_MODULE_121__, __WEBPACK_EXTERNAL_MODULE_995__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 
 /******/ 		// Check if module is in cache
@@ -59,17 +59,17 @@ return /******/ (function(modules) { // 
 
 /***/ },
 /* 1 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	var React = __webpack_require__(2);
-	var ReactDOM = __webpack_require__(22);
+	var ReactDOM = __webpack_require__(31);
 	//
 	// if (process.env.NODE_ENV !== "production") {
 	//   const Perf = require("react-addons-perf");
 	//   window.Perf = Perf;
 	// }
 
 	var _require = __webpack_require__(131),
 	    bootstrap = _require.bootstrap,
@@ -1091,78 +1091,36 @@ return /******/ (function(modules) { // 
 	      };
 	    }();
 
 	    if (typeof _ret === "object") return _ret.v;
 	  }
 	}
 
 /***/ },
-/* 22 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/**
-	 * ReactDOM v15.3.2
-	 *
-	 * Copyright 2013-present, Facebook, Inc.
-	 * All rights reserved.
-	 *
-	 * This source code is licensed under the BSD-style license found in the
-	 * LICENSE file in the root directory of this source tree. An additional grant
-	 * of patent rights can be found in the PATENTS file in the same directory.
-	 *
-	 */
-	// Based off https://github.com/ForbesLindesay/umd/blob/master/template.js
-	;(function(f) {
-	  // CommonJS
-	  if (true) {
-	    module.exports = f(__webpack_require__(2));
-
-	  // RequireJS
-	  } else if (typeof define === "function" && define.amd) {
-	    define(['react'], f);
-
-	  // <script>
-	  } else {
-	    var g;
-	    if (typeof window !== "undefined") {
-	      g = window;
-	    } else if (typeof global !== "undefined") {
-	      g = global;
-	    } else if (typeof self !== "undefined") {
-	      g = self;
-	    } else {
-	      // works providing we're not in "use strict";
-	      // needed for Java 8 Nashorn
-	      // see https://github.com/facebook/react/issues/3037
-	      g = this;
-	    }
-	    g.ReactDOM = f(g.React);
-	  }
-
-	})(function(React) {
-	  return React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
-	});
-
-
-/***/ },
+/* 22 */,
 /* 23 */,
 /* 24 */,
 /* 25 */,
 /* 26 */,
 /* 27 */,
 /* 28 */,
 /* 29 */
 /***/ function(module, exports) {
 
 	module.exports = __WEBPACK_EXTERNAL_MODULE_29__;
 
 /***/ },
 /* 30 */,
-/* 31 */,
+/* 31 */
+/***/ function(module, exports) {
+
+	module.exports = __WEBPACK_EXTERNAL_MODULE_31__;
+
+/***/ },
 /* 32 */,
 /* 33 */,
 /* 34 */,
 /* 35 */,
 /* 36 */,
 /* 37 */,
 /* 38 */,
 /* 39 */,
@@ -4109,17 +4067,17 @@ return /******/ (function(modules) { // 
 	});
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;
 	  var action = arguments[1];
 
 	  switch (action.type) {
 	    case constants.CLEAR_TABS:
-	      return state.setIn(["tabs"], Immutable.Map()).setIn("selectedTab", null);
+	      return state.setIn(["tabs"], Immutable.Map()).setIn(["selectedTab"], null);
 
 	    case constants.ADD_TABS:
 	      var tabs = action.value;
 	      if (!tabs) {
 	        return state;
 	      }
 
 	      return state.mergeIn(["tabs"], Immutable.Map(tabs.map(tab => {
@@ -4159,24 +4117,51 @@ return /******/ (function(modules) { // 
 
 	"use strict";
 
 	/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 	/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 	/* This Source Code Form is subject to the terms of the Mozilla Public
 	 * License, v. 2.0. If a copy of the MPL was not distributed with this
 	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+	var sidePanelItems = {
+	  Firefox: {
+	    name: "Firefox",
+	    clientType: "firefox",
+	    paramName: "firefox-tab",
+	    docsUrlPart: "firefox"
+	  },
+	  Chrome: {
+	    name: "Chrome",
+	    clientType: "chrome",
+	    paramName: "chrome-tab",
+	    docsUrlPart: "chrome"
+	  },
+	  Node: {
+	    name: "Node",
+	    clientType: "node",
+	    paramName: "node-tab",
+	    docsUrlPart: "node"
+	  },
+	  Settings: {
+	    name: "Settings",
+	    clientType: "settings",
+	    paramName: "settings-tab",
+	    docsUrlPart: "settings"
+	  }
+	};
 
 	module.exports = {
 	  CLEAR_TABS: "CLEAR_TABS",
 	  ADD_TABS: "ADD_TABS",
 	  SELECT_TAB: "SELECT_TAB",
 	  FILTER_TABS: "FILTER_TABS",
 	  SET_VALUE: "SET_VALUE",
-	  SET_CONFIG: "SET_CONFIG"
+	  SET_CONFIG: "SET_CONFIG",
+	  sidePanelItems
 	};
 
 /***/ },
 /* 146 */
 /***/ function(module, exports, __webpack_require__) {
 
 	/**
 	 *  Copyright (c) 2014-2015, Facebook, Inc.
@@ -10293,17 +10278,20 @@ return /******/ (function(modules) { // 
 	function getTabs(state) {
 	  var tabs = state.tabs.get("tabs");
 	  var filterString = getFilterString(state);
 
 	  if (filterString === "") {
 	    return tabs;
 	  }
 
-	  return tabs.filter(tab => score(tab.get("title"), filterString) + score(tab.get("url"), filterString) > 0);
+	  return tabs.map(tab => {
+	    var _overallScore = score(tab.get("title"), filterString) + score(tab.get("url"), filterString);
+	    return tab.set("filteredOut", _overallScore === 0);
+	  });
 	}
 
 	function getSelectedTab(state) {
 	  return state.tabs.get("selectedTab");
 	}
 
 	function getFilterString(state) {
 	  return state.tabs.get("filterString");
@@ -11238,17 +11226,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	var React = __webpack_require__(2);
 
 	__webpack_require__(168);
 	var dom = React.DOM;
 
 	var ImPropTypes = __webpack_require__(150);
-
+	var configMap = __webpack_require__(145).sidePanelItems;
 	var Tabs = React.createFactory(__webpack_require__(172));
 	var Sidebar = React.createFactory(__webpack_require__(176));
 	var Settings = React.createFactory(__webpack_require__(179));
 
 	var githubUrl = "https://github.com/devtools-html/debugger.html/blob/master";
 
 	function getTabsByClientType(tabs, clientType) {
 	  return tabs.valueSeq().filter(tab => tab.get("clientType") == clientType);
@@ -11272,17 +11260,17 @@ return /******/ (function(modules) { // 
 	    config: React.PropTypes.object.isRequired,
 	    setValue: React.PropTypes.func.isRequired
 	  },
 
 	  displayName: "LandingPage",
 
 	  getInitialState() {
 	    return {
-	      selectedPane: "Firefox",
+	      selectedPane: configMap.Firefox.name,
 	      firefoxConnected: false,
 	      chromeConnected: false
 	    };
 	  },
 
 	  componentDidUpdate() {
 	    if (this.refs.filterInput) {
 	      this.refs.filterInput.focus();
@@ -11294,117 +11282,145 @@ return /******/ (function(modules) { // 
 	  },
 
 	  onSideBarItemClick(itemTitle) {
 	    if (itemTitle !== this.state.selectedPane) {
 	      this.setState({ selectedPane: itemTitle });
 	    }
 	  },
 
-	  renderPanel() {
+	  renderLaunchButton() {
+	    var selectedPane = this.state.selectedPane;
+	    var name = configMap[selectedPane].name;
+
+
+	    var isConnected = name === configMap.Firefox.name ? this.state.firefoxConnected : this.state.chromeConnected;
+	    var isNodeSelected = name === configMap.Node.name;
+
+	    if (isNodeSelected) {
+	      return dom.h3({}, "Run a node script in the terminal with `--inspect`");
+	    }
+
+	    var connectedStateText = isNodeSelected ? null : `Please open a tab in ${name}`;
+
+	    return isConnected ? connectedStateText : dom.input({
+	      type: "button",
+	      value: `Launch ${configMap[selectedPane].name}`,
+	      onClick: () => this.launchBrowser(configMap[selectedPane].name)
+	    });
+	  },
+
+	  launchBrowser(browser) {
+	    fetch("/launch", {
+	      body: JSON.stringify({ browser }),
+	      headers: {
+	        "Content-Type": "application/json"
+	      },
+	      method: "post"
+	    }).then(resp => {
+	      if (browser === configMap.Firefox.name) {
+	        this.setState({ firefoxConnected: true });
+	      } else {
+	        this.setState({ chromeConnected: true });
+	      }
+	    }).catch(err => {
+	      alert(`Error launching ${browser}. ${err.message}`);
+	    });
+	  },
+
+	  renderEmptyPanel() {
+	    return dom.div({ className: "hero" }, this.renderLaunchButton());
+	  },
+
+	  renderSettings() {
 	    var _props = this.props,
-	        onTabClick = _props.onTabClick,
 	        config = _props.config,
 	        setValue = _props.setValue;
 
-	    var configMap = {
-	      Firefox: {
-	        name: "Firefox",
-	        clientType: "firefox",
-	        paramName: "firefox-tab",
-	        docsUrlPart: "firefox"
-	      },
-	      Chrome: {
-	        name: "Chrome",
-	        clientType: "chrome",
-	        paramName: "chrome-tab",
-	        docsUrlPart: "chrome"
-	      },
-	      Node: {
-	        name: "Node",
-	        clientType: "node",
-	        paramName: "node-tab",
-	        docsUrlPart: "node"
-	      },
-	      Settings: {
-	        name: "Settings",
-	        clientType: "settings",
-	        paramName: "settings-tab",
-	        docsUrlPart: "settings"
-	      }
-	    };
-
-	    var _configMap$state$sele = configMap[this.state.selectedPane],
-	        name = _configMap$state$sele.name,
-	        clientType = _configMap$state$sele.clientType,
-	        paramName = _configMap$state$sele.paramName,
-	        docsUrlPart = _configMap$state$sele.docsUrlPart;
+
+	    return dom.div({}, dom.header({}, dom.h1({}, configMap.Settings.name)), Settings({ config, setValue }));
+	  },
+
+	  renderFilter() {
+	    var selectedPane = this.state.selectedPane;
 	    var _props2 = this.props,
 	        tabs = _props2.tabs,
 	        _props2$filterString = _props2.filterString,
 	        filterString = _props2$filterString === undefined ? "" : _props2$filterString;
-	    var selectedPane = this.state.selectedPane;
+	    var _configMap$selectedPa = configMap[selectedPane],
+	        clientType = _configMap$selectedPa.clientType,
+	        paramName = _configMap$selectedPa.paramName;
 
 
 	    var targets = getTabsByClientType(tabs, clientType);
-	    var isSettingsPaneSelected = name === "Settings";
-
-	    var launchBrowser = browser => {
-	      fetch("/launch", {
-	        body: JSON.stringify({ browser }),
-	        headers: {
-	          "Content-Type": "application/json"
-	        },
-	        method: "post"
-	      }).then(resp => {
-	        if (browser == "firefox") {
-	          this.setState({ firefoxConnected: true });
-	        } else {
-	          this.setState({ chromeConnected: true });
-	        }
-	      }).catch(err => {
-	        alert(`Error launching ${browser}. ${err.message}`);
-	      });
-	    };
-
-	    var isConnected = name == "Firefox" ? this.state.firefoxConnected : this.state.chromeConnected;
-	    var launchButton = isConnected ? null : dom.input({
-	      type: "button",
-	      value: `Launch ${selectedPane}`,
-	      onClick: () => launchBrowser(selectedPane)
-	    });
-	    return dom.main({ className: "panel" }, !isSettingsPaneSelected ? dom.header({}, dom.input({
+
+	    return dom.header({}, dom.input({
 	      ref: "filterInput",
 	      placeholder: "Filter tabs",
 	      value: filterString,
 	      autoFocus: true,
 	      type: "search",
 	      onChange: e => this.onFilterChange(e.target.value),
 	      onKeyDown: e => {
 	        if (targets.size === 1 && e.keyCode === 13) {
 	          this.onTabClick(targets.first(), paramName);
 	        }
 	      }
-	    }), launchButton) : dom.header({}, dom.h1({}, "Settings")), isSettingsPaneSelected ? Settings({ config, setValue }) : Tabs({ targets, paramName, onTabClick }), firstTimeMessage(name, docsUrlPart));
+	    }));
+	  },
+
+	  renderPanel() {
+	    var _props3 = this.props,
+	        onTabClick = _props3.onTabClick,
+	        tabs = _props3.tabs;
+	    var selectedPane = this.state.selectedPane;
+	    var _configMap$selectedPa2 = configMap[selectedPane],
+	        name = _configMap$selectedPa2.name,
+	        clientType = _configMap$selectedPa2.clientType,
+	        paramName = _configMap$selectedPa2.paramName;
+
+
+	    var clientTargets = getTabsByClientType(tabs, clientType);
+	    var tabsDetected = clientTargets && clientTargets.count() > 0;
+	    var targets = clientTargets.filter(t => !t.get("filteredOut"));
+
+	    var isSettingsPaneSelected = name === configMap.Settings.name;
+
+	    if (isSettingsPaneSelected) {
+	      return this.renderSettings();
+	    }
+
+	    if (!tabsDetected) {
+	      return this.renderEmptyPanel();
+	    }
+
+	    return dom.div({}, this.renderFilter(), Tabs({ targets, paramName, onTabClick }));
 	  },
 
 	  render() {
-	    var _props3 = this.props,
-	        supportsFirefox = _props3.supportsFirefox,
-	        supportsChrome = _props3.supportsChrome,
-	        title = _props3.title;
+	    var _props4 = this.props,
+	        supportsFirefox = _props4.supportsFirefox,
+	        supportsChrome = _props4.supportsChrome,
+	        title = _props4.title;
 	    var selectedPane = this.state.selectedPane;
 	    var onSideBarItemClick = this.onSideBarItemClick;
+	    var _configMap$selectedPa3 = configMap[selectedPane],
+	        name = _configMap$selectedPa3.name,
+	        docsUrlPart = _configMap$selectedPa3.docsUrlPart;
+
 
 	    return dom.div({
 	      className: "landing-page"
 	    }, Sidebar({
-	      supportsFirefox, supportsChrome, title,
-	      selectedPane, onSideBarItemClick
-	    }), this.renderPanel());
+	      supportsFirefox,
+	      supportsChrome,
+	      title,
+	      selectedPane,
+	      onSideBarItemClick
+	    }), dom.main({ className: "panel" }, this.renderPanel(), firstTimeMessage(name, docsUrlPart)));
 	  }
 	});
 
 	module.exports = LandingPage;
 
 /***/ },
 /* 168 */
 /***/ function(module, exports) {
@@ -11684,20 +11700,20 @@ return /******/ (function(modules) { // 
 
 	  onInputHandler(e, path) {
 	    var setValue = this.props.setValue;
 
 	    setValue(path, e.target.checked);
 	  },
 
 	  renderConfig(config) {
-	    var configs = [{ name: "dir", label: "direction" }, { name: "theme", label: "theme" }
-	    // Hiding hotReloading option for now. See Issue #242
-	    // { name: "hotReloading", label: "hot reloading", bool: true }
-	    ];
+	    var configs = [{ name: "dir", label: "direction" }, { name: "theme", label: "theme"
+	      // Hiding hotReloading option for now. See Issue #242
+	      // { name: "hotReloading", label: "hot reloading", bool: true }
+	    }];
 
 	    return dom.ul({ className: "tab-list" }, configs.map(c => {
 	      return dom.li({ key: c.name, className: "tab tab-sides" }, dom.div({ className: "tab-title" }, c.label), c.bool ? dom.input({
 	        type: "checkbox",
 	        defaultChecked: config[c.name],
 	        onChange: e => this.onInputHandler(e, c.name)
 	      }, null) : dom.div({
 	        className: "tab-value",
@@ -12127,17 +12143,17 @@ return /******/ (function(modules) { // 
 	 * for actions to install a function to be run once when a specific
 	 * condition is met by an action coming through the system. Think of
 	 * it as a thunk that blocks until the condition is met. Example:
 	 *
 	 * ```js
 	 * const services = { WAIT_UNTIL: require('wait-service').NAME };
 	 *
 	 * { type: services.WAIT_UNTIL,
-	 *   predicate: action => action.type === constants.ADD_ITEM,
+	 *   predicate: action => action.type === "ADD_ITEM",
 	 *   run: (dispatch, getState, action) => {
 	 *     // Do anything here. You only need to accept the arguments
 	 *     // if you need them. `action` is the action that satisfied
 	 *     // the predicate.
 	 *   }
 	 * }
 	 * ```
 	 */
@@ -13285,16 +13301,17 @@ return /******/ (function(modules) { // 
 /* 226 */
 /***/ function(module, exports, __webpack_require__) {
 
 	// @flow
 
 	var { PrefsHelper } = __webpack_require__(830);
 	const { Services: { pref } } = __webpack_require__(830);
 	const { isDevelopment } = __webpack_require__(828);
+	const prefsSchemaVersion = "1.0.0";
 
 	if (isDevelopment()) {
 	  pref("devtools.debugger.client-source-maps-enabled", true);
 	  pref("devtools.debugger.pause-on-exceptions", false);
 	  pref("devtools.debugger.ignore-caught-exceptions", false);
 	  pref("devtools.debugger.call-stack-visible", false);
 	  pref("devtools.debugger.scopes-visible", false);
 	  pref("devtools.debugger.start-panel-collapsed", false);
@@ -13302,16 +13319,17 @@ return /******/ (function(modules) { // 
 	  pref("devtools.debugger.tabs", "[]");
 	  pref("devtools.debugger.ui.framework-grouping-on", true);
 	  pref("devtools.debugger.pending-selected-location", "{}");
 	  pref("devtools.debugger.pending-breakpoints", "[]");
 	  pref("devtools.debugger.expressions", "[]");
 	  pref("devtools.debugger.file-search-case-sensitive", true);
 	  pref("devtools.debugger.file-search-whole-word", false);
 	  pref("devtools.debugger.file-search-regex-match", false);
+	  pref("devtools.debugger.prefs-schema-version", "1.0.0");
 	}
 
 	const prefs = new PrefsHelper("devtools", {
 	  clientSourceMapsEnabled: ["Bool", "debugger.client-source-maps-enabled"],
 	  pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
 	  ignoreCaughtExceptions: ["Bool", "debugger.ignore-caught-exceptions"],
 	  callStackVisible: ["Bool", "debugger.call-stack-visible"],
 	  scopesVisible: ["Bool", "debugger.scopes-visible"],
@@ -13319,18 +13337,25 @@ return /******/ (function(modules) { // 
 	  endPanelCollapsed: ["Bool", "debugger.end-panel-collapsed"],
 	  frameworkGroupingOn: ["Bool", "debugger.ui.framework-grouping-on"],
 	  tabs: ["Json", "debugger.tabs"],
 	  pendingSelectedLocation: ["Json", "debugger.pending-selected-location"],
 	  pendingBreakpoints: ["Json", "debugger.pending-breakpoints"],
 	  expressions: ["Json", "debugger.expressions"],
 	  fileSearchCaseSensitive: ["Bool", "debugger.file-search-case-sensitive"],
 	  fileSearchWholeWord: ["Bool", "debugger.file-search-whole-word"],
-	  fileSearchRegexMatch: ["Bool", "debugger.file-search-regex-match"]
-	});
+	  fileSearchRegexMatch: ["Bool", "debugger.file-search-regex-match"],
+	  debuggerPrefsSchemaVersion: ["Char", "debugger.prefs-schema-version"]
+	});
+
+	if (prefs.debuggerPrefsSchemaVersion !== prefsSchemaVersion) {
+	  // clear pending Breakpoints
+	  prefs.pendingBreakpoints = [];
+	  prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion;
+	}
 
 	module.exports = { prefs };
 
 
 /***/ },
 /* 227 */
 /***/ function(module, exports, __webpack_require__) {
 
@@ -13363,53 +13388,52 @@ return /******/ (function(modules) { // 
 	var _pause = __webpack_require__(239);
 
 	var _pause2 = _interopRequireDefault(_pause);
 
 	var _ui = __webpack_require__(240);
 
 	var _ui2 = _interopRequireDefault(_ui);
 
+	var _ast = __webpack_require__(1058);
+
+	var _ast2 = _interopRequireDefault(_ast);
+
 	var _coverage = __webpack_require__(241);
 
 	var _coverage2 = _interopRequireDefault(_coverage);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	/* This Source Code Form is subject to the terms of the Mozilla Public
-	 * License, v. 2.0. If a copy of the MPL was not distributed with this
-	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
 	exports.default = {
 	  expressions: _expressions2.default,
 	  eventListeners: _eventListeners2.default,
 	  sources: _sources2.default,
 	  breakpoints: _breakpoints2.default,
 	  asyncRequests: _asyncRequests2.default,
 	  pause: _pause2.default,
 	  ui: _ui2.default,
+	  ast: _ast2.default,
 	  coverage: _coverage2.default
-	};
+	}; /* This Source Code Form is subject to the terms of the Mozilla Public
+	    * License, v. 2.0. If a copy of the MPL was not distributed with this
+	    * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /***/ },
 /* 228 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.getVisibleExpressions = exports.getExpressions = exports.State = undefined;
 	exports.getExpression = getExpression;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _makeRecord = __webpack_require__(230);
 
 	var _makeRecord2 = _interopRequireDefault(_makeRecord);
 
 	var _immutable = __webpack_require__(146);
 
 	var _reselect = __webpack_require__(993);
 
@@ -13421,42 +13445,42 @@ return /******/ (function(modules) { // 
 	  expressions: (0, _immutable.List)(restoreExpressions())
 	});
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
 	  switch (action.type) {
-	    case _constants2.default.ADD_EXPRESSION:
+	    case "ADD_EXPRESSION":
 	      return appendToList(state, ["expressions"], {
 	        input: action.input,
 	        value: null,
 	        updating: true,
 	        visible: action.visible
 	      });
-	    case _constants2.default.UPDATE_EXPRESSION:
+	    case "UPDATE_EXPRESSION":
 	      var key = action.expression.input;
 	      return updateItemInList(state, ["expressions"], key, {
 	        input: action.input,
 	        value: null,
 	        updating: true,
 	        visible: action.visible
 	      });
-	    case _constants2.default.EVALUATE_EXPRESSION:
+	    case "EVALUATE_EXPRESSION":
 	      if (action.status === "done") {
 	        return updateItemInList(state, ["expressions"], action.input, {
 	          input: action.input,
 	          value: action.value,
 	          updating: false,
 	          visible: action.visible
 	        });
 	      }
 	      break;
-	    case _constants2.default.DELETE_EXPRESSION:
+	    case "DELETE_EXPRESSION":
 	      return deleteExpression(state, action.input);
 	  }
 
 	  return state;
 	}
 
 	function restoreExpressions() {
 	  var exprs = _prefs.prefs.expressions;
@@ -13503,87 +13527,17 @@ return /******/ (function(modules) { // 
 
 	function getExpression(state, input) {
 	  return getExpressions(state).find(exp => exp.input == input);
 	}
 
 	exports.default = update;
 
 /***/ },
-/* 229 */
-/***/ function(module, exports) {
-
-	"use strict";
-
-	Object.defineProperty(exports, "__esModule", {
-	  value: true
-	});
-
-
-	/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-	/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-	/* This Source Code Form is subject to the terms of the Mozilla Public
-	 * License, v. 2.0. If a copy of the MPL was not distributed with this
-	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-	exports.default = {
-	  UPDATE_EVENT_BREAKPOINTS: "UPDATE_EVENT_BREAKPOINTS",
-	  FETCH_EVENT_LISTENERS: "FETCH_EVENT_LISTENERS",
-
-	  TOGGLE_PRETTY_PRINT: "TOGGLE_PRETTY_PRINT",
-	  BLACKBOX: "BLACKBOX",
-
-	  ADD_BREAKPOINT: "ADD_BREAKPOINT",
-	  REMOVE_BREAKPOINT: "REMOVE_BREAKPOINT",
-	  ENABLE_BREAKPOINT: "ENABLE_BREAKPOINT",
-	  DISABLE_BREAKPOINT: "DISABLE_BREAKPOINT",
-	  SET_BREAKPOINT_CONDITION: "SET_BREAKPOINT_CONDITION",
-	  TOGGLE_BREAKPOINTS: "TOGGLE_BREAKPOINTS",
-
-	  ADD_SOURCE: "ADD_SOURCE",
-	  ADD_SOURCES: "ADD_SOURCES",
-	  LOAD_SOURCE_TEXT: "LOAD_SOURCE_TEXT",
-	  SELECT_SOURCE: "SELECT_SOURCE",
-	  SELECT_SOURCE_URL: "SELECT_SOURCE_URL",
-	  HIGHLIGHT_LINES: "HIGHLIGHT_LINES",
-	  CLEAR_HIGHLIGHT_LINES: "CLEAR_HIGHLIGHT_LINES",
-	  CLOSE_TAB: "CLOSE_TAB",
-	  CLOSE_TABS: "CLOSE_TABS",
-	  NAVIGATE: "NAVIGATE",
-	  RELOAD: "RELOAD",
-
-	  ADD_TABS: "ADD_TABS",
-	  SELECT_TAB: "SELECT_TAB",
-
-	  BREAK_ON_NEXT: "BREAK_ON_NEXT",
-	  RESUME: "RESUME",
-	  PAUSED: "PAUSED",
-	  PAUSE_ON_EXCEPTIONS: "PAUSE_ON_EXCEPTIONS",
-	  COMMAND: "COMMAND",
-	  SELECT_FRAME: "SELECT_FRAME",
-	  LOAD_OBJECT_PROPERTIES: "LOAD_OBJECT_PROPERTIES",
-	  ADD_EXPRESSION: "ADD_EXPRESSION",
-	  EVALUATE_EXPRESSION: "EVALUATE_EXPRESSION",
-	  UPDATE_EXPRESSION: "UPDATE_EXPRESSION",
-	  DELETE_EXPRESSION: "DELETE_EXPRESSION",
-
-	  RECORD_COVERAGE: "RECORD_COVERAGE",
-
-	  TOGGLE_PROJECT_SEARCH: "TOGGLE_PROJECT_SEARCH",
-	  TOGGLE_FILE_SEARCH: "TOGGLE_FILE_SEARCH",
-	  TOGGLE_SYMBOL_SEARCH: "TOGGLE_SYMBOL_SEARCH",
-	  TOGGLE_FRAMEWORK_GROUPING: "TOGGLE_FRAMEWORK_GROUPING",
-	  SET_SYMBOL_SEARCH_TYPE: "SET_SYMBOL_SEARCH_TYPE",
-	  UPDATE_FILE_SEARCH_QUERY: "UPDATE_FILE_SEARCH_QUERY",
-	  TOGGLE_FILE_SEARCH_MODIFIER: "TOGGLE_FILE_SEARCH_MODIFIER",
-	  SHOW_SOURCE: "SHOW_SOURCE",
-	  TOGGLE_PANE: "TOGGLE_PANE"
-	};
-
-/***/ },
+/* 229 */,
 /* 230 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
@@ -13619,58 +13573,53 @@ return /******/ (function(modules) { // 
 	 * This depends on a performance fix that will go out in 0.29 though;
 	 * @module utils/makeRecord
 	 */
 
 	exports.default = makeRecord;
 
 /***/ },
 /* 231 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ function(module, exports) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.getEventListeners = getEventListeners;
-
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	var initialState = {
 	  activeEventNames: [],
 	  listeners: [],
 	  fetchingListeners: false
-	}; /* This Source Code Form is subject to the terms of the Mozilla Public
-	    * License, v. 2.0. If a copy of the MPL was not distributed with this
-	    * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+	};
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;
 	  var action = arguments[1];
 	  var emit = arguments[2];
 
 	  switch (action.type) {
-	    case _constants2.default.UPDATE_EVENT_BREAKPOINTS:
+	    case "UPDATE_EVENT_BREAKPOINTS":
 	      state.activeEventNames = action.eventNames;
 	      // emit("activeEventNames", state.activeEventNames);
 	      break;
-	    case _constants2.default.FETCH_EVENT_LISTENERS:
+	    case "FETCH_EVENT_LISTENERS":
 	      if (action.status === "begin") {
 	        state.fetchingListeners = true;
 	      } else if (action.status === "done") {
 	        state.fetchingListeners = false;
 	        state.listeners = action.listeners;
 	      }
 	      break;
-	    case _constants2.default.NAVIGATE:
+	    case "NAVIGATE":
 	      return initialState;
 	  }
 
 	  return state;
 	}
 
 	function getEventListeners(state) {
 	  return state.eventListeners.listeners;
@@ -13682,17 +13631,20 @@ return /******/ (function(modules) { // 
 /* 232 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
-	exports.getSelectedSource = exports.getSelectedLocation = exports.getSourcesForTabs = exports.getSourceTabs = exports.getSources = exports.State = undefined;
+	exports.getSelectedSourceText = exports.getSelectedSource = exports.getSelectedLocation = exports.getSourcesForTabs = exports.getSourceTabs = exports.getSources = exports.State = undefined;
+	exports.removeSourceFromTabList = removeSourceFromTabList;
+	exports.removeSourcesFromTabList = removeSourcesFromTabList;
+	exports.getNewSelectedSourceId = getNewSelectedSourceId;
 	exports.getSource = getSource;
 	exports.getSourceByURL = getSourceByURL;
 	exports.getSourceText = getSourceText;
 	exports.getPendingSelectedLocation = getPendingSelectedLocation;
 	exports.getPrettySource = getPrettySource;
 	exports.getSourceInSources = getSourceInSources;
 
 	var _immutable = __webpack_require__(146);
@@ -13728,129 +13680,136 @@ return /******/ (function(modules) { // 
 	 * Sources reducer
 	 * @module reducers/sources
 	 */
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
-	  var availableTabs = null;
 	  var location = null;
 
 	  switch (action.type) {
 	    case "ADD_SOURCE":
 	      {
 	        var _source = action.source;
-	        return state.mergeIn(["sources", action.source.id], _source);
+	        return updateSource(state, _source);
 	      }
 
 	    case "ADD_SOURCES":
 	      {
 	        action.sources.forEach(source => {
 	          state = state.mergeIn(["sources", source.id], source);
 	        });
 
 	        return state;
 	      }
 
 	    case "SELECT_SOURCE":
+	      if (action.status != "start") {
+	        return state;
+	      }
+
 	      location = {
 	        line: action.line,
 	        url: action.source.url
 	      };
+
 	      _prefs.prefs.pendingSelectedLocation = location;
 
-	      var sourceUrl = action.source.url || "";
 	      return state.set("selectedLocation", {
 	        sourceId: action.source.id,
 	        line: action.line
 	      }).set("pendingSelectedLocation", location).merge({
-	        tabs: updateTabList({ sources: state }, sourceUrl, action.tabIndex)
-	      });
+	        tabs: updateTabList({ sources: state }, action.source.url, action.tabIndex)
+	      });
+
+	    case "CLEAR_SELECTED_SOURCE":
+	      location = { url: "" };
+	      _prefs.prefs.pendingSelectedLocation = location;
+
+	      return state.set("selectedLocation", { sourceId: "" }).set("pendingSelectedLocation", location);
 
 	    case "SELECT_SOURCE_URL":
 	      location = {
 	        url: action.url,
 	        line: action.line
 	      };
 
 	      _prefs.prefs.pendingSelectedLocation = location;
 	      return state.set("pendingSelectedLocation", location);
 
 	    case "CLOSE_TAB":
-	      availableTabs = removeSourceFromTabList(state.tabs, action.url);
-
-	      return state.merge({ tabs: availableTabs }).set("selectedLocation", {
-	        sourceId: getNewSelectedSourceId(state, availableTabs)
-	      });
+	      _prefs.prefs.tabs = action.tabs;
+	      return state.merge({ tabs: action.tabs });
 
 	    case "CLOSE_TABS":
-	      availableTabs = removeSourcesFromTabList(state.tabs, action.urls);
-
-	      return state.merge({ tabs: availableTabs }).set("selectedLocation", {
-	        sourceId: getNewSelectedSourceId(state, availableTabs)
-	      });
+	      _prefs.prefs.tabs = action.tabs;
+	      return state.merge({ tabs: action.tabs });
 
 	    case "LOAD_SOURCE_TEXT":
-	      return _updateText(state, action);
+	      return setSourceTextProps(state, action);
 
 	    case "BLACKBOX":
 	      if (action.status === "done") {
 	        return state.setIn(["sources", action.source.id, "isBlackBoxed"], action.value.isBlackBoxed);
 	      }
 	      break;
 
 	    case "TOGGLE_PRETTY_PRINT":
-	      return _updateText(state, action);
+	      return setSourceTextProps(state, action);
 
 	    case "NAVIGATE":
 	      var source = getSelectedSource({ sources: state });
 	      var _url = source && source.get("url");
 	      _prefs.prefs.pendingSelectedLocation = { url: _url };
 	      return State().set("pendingSelectedLocation", { url: _url });
 	  }
 
 	  return state;
 	}
 
-	// TODO: Action is coerced to `any` unfortunately because how we type
-	// asynchronous actions is wrong. The `value` may be null for the
-	// "start" and "error" states but we don't type it like that. We need
-	// to rethink how we type async actions.
-	function _updateText(state, action) {
+	function getTextPropsFromAction(action) {
 	  var source = action.source;
 	  var sourceText = action.value;
 
 	  if (action.status === "start") {
-	    // Merge this in, don't set it. That way the previous value is
-	    // still stored here, and we can retrieve it if whatever we're
-	    // doing fails.
-	    return state.mergeIn(["sourcesText", source.id], {
-	      loading: true
-	    });
-	  }
-
-	  if (action.status === "error") {
-	    return state.setIn(["sourcesText", source.id], I.Map({
-	      error: action.error
-	    }));
-	  }
-
-	  return state.setIn(["sourcesText", source.id], I.Map({
+	    return { loading: true };
+	  } else if (action.status === "error") {
+	    return { error: action.error, loading: false };
+	  }
+	  return {
 	    text: sourceText.text,
 	    id: source.id,
-	    contentType: sourceText.contentType
-	  }));
+	    contentType: sourceText.contentType,
+	    loading: false
+	  };
+	}
+
+	// TODO: Action is coerced to `any` unfortunately because how we type
+	// asynchronous actions is wrong. The `value` may be null for the
+	// "start" and "error" states but we don't type it like that. We need
+	// to rethink how we type async actions.
+	function setSourceTextProps(state, action) {
+	  var source = action.source;
+	  var text = getTextPropsFromAction(action);
+	  var updatedState = state.setIn(["sourcesText", source.id], I.Map(text));
+	  return updateSource(updatedState, text);
+	}
+
+	function updateSource(state, source) {
+	  if (!source.id) {
+	    return state;
+	  }
+
+	  return state.mergeIn(["sources", source.id], source);
 	}
 
 	function removeSourceFromTabList(tabs, url) {
-	  var newTabs = tabs.filter(tab => tab != url);
-	  _prefs.prefs.tabs = newTabs;
-	  return newTabs;
+	  return tabs.filter(tab => tab != url);
 	}
 
 	function removeSourcesFromTabList(tabs, urls) {
 	  return urls.reduce((t, url) => removeSourceFromTabList(t, url), tabs);
 	}
 
 	function restoreTabs() {
 	  var prefsTabs = _prefs.prefs.tabs || [];
@@ -13889,46 +13848,46 @@ return /******/ (function(modules) { // 
 	 * 1. if the selected tab is available, it remains selected
 	 * 2. if it is gone, the next available tab to the left should be active
 	 * 3. if the first tab is active and closed, select the second tab
 	 *
 	 * @memberof reducers/sources
 	 * @static
 	 */
 	function getNewSelectedSourceId(state, availableTabs) {
-	  var selectedLocation = state.selectedLocation;
+	  var selectedLocation = state.sources.selectedLocation;
 	  if (!selectedLocation) {
 	    return "";
 	  }
 
-	  var selectedTab = state.sources.get(selectedLocation.sourceId);
+	  var selectedTab = state.sources.sources.get(selectedLocation.sourceId);
 
 	  var selectedTabUrl = selectedTab ? selectedTab.get("url") : "";
 
 	  if (availableTabs.includes(selectedTabUrl)) {
-	    var _sources = state.sources;
+	    var _sources = state.sources.sources;
 	    if (!_sources) {
 	      return "";
 	    }
 
 	    var selectedSource = _sources.find(source => source.get("url") == selectedTabUrl);
 
 	    if (selectedSource) {
 	      return selectedSource.get("id");
 	    }
 
 	    return "";
 	  }
 
-	  var tabUrls = state.tabs.toJS();
+	  var tabUrls = state.sources.tabs.toJS();
 	  var leftNeighborIndex = Math.max(tabUrls.indexOf(selectedTabUrl) - 1, 0);
 	  var lastAvailbleTabIndex = availableTabs.size - 1;
 	  var newSelectedTabIndex = Math.min(leftNeighborIndex, lastAvailbleTabIndex);
 	  var availableTab = availableTabs.toJS()[newSelectedTabIndex];
-	  var tabSource = getSourceByUrlInSources(state.sources, availableTab);
+	  var tabSource = getSourceByUrlInSources(state.sources.sources, availableTab);
 
 	  if (tabSource) {
 	    return tabSource.get("id");
 	  }
 
 	  return "";
 	}
 
@@ -13995,16 +13954,21 @@ return /******/ (function(modules) { // 
 	var getSelectedSource = exports.getSelectedSource = (0, _reselect.createSelector)(getSelectedLocation, getSources, (selectedLocation, sources) => {
 	  if (!selectedLocation) {
 	    return;
 	  }
 
 	  return sources.get(selectedLocation.sourceId);
 	});
 
+	var getSelectedSourceText = exports.getSelectedSourceText = (0, _reselect.createSelector)(getSelectedSource, getSourcesState, (selectedSource, sources) => {
+	  var id = selectedSource.get("id");
+	  return id ? sources.sourcesText.get(id) : null;
+	});
+
 	exports.default = update;
 
 /***/ },
 /* 233 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
@@ -14223,20 +14187,16 @@ return /******/ (function(modules) { // 
 	  }
 	  return str;
 	}
 
 	/**
 	 * @memberof utils/utils
 	 * @static
 	 */
-	function updateObj(obj, fields) {
-	  return Object.assign({}, obj, fields);
-	}
-
 	/**
 	 * @memberof utils/utils
 	 * @static
 	 */
 	function throttle(func, ms) {
 	  var timeout = void 0,
 	      _this = void 0;
 	  return function () {
@@ -14256,17 +14216,16 @@ return /******/ (function(modules) { // 
 
 	function waitForMs(ms) {
 	  return new Promise(resolve => setTimeout(resolve, ms));
 	}
 
 	exports.handleError = handleError;
 	exports.promisify = promisify;
 	exports.endTruncateStr = endTruncateStr;
-	exports.updateObj = updateObj;
 	exports.throttle = throttle;
 	exports.waitForMs = waitForMs;
 
 /***/ },
 /* 235 */
 /***/ function(module, exports) {
 
 	"use strict";
@@ -14305,293 +14264,361 @@ return /******/ (function(modules) { // 
 /* 236 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
-	exports.State = undefined;
-	exports.makeLocationId = makeLocationId;
-	exports.makePendingLocationId = makePendingLocationId;
+
+	var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+	/**
+	 * Breakpoints reducer
+	 * @module reducers/breakpoints
+	 */
+
+	exports.initialState = initialState;
 	exports.makePendingBreakpoint = makePendingBreakpoint;
 	exports.getBreakpoint = getBreakpoint;
 	exports.getBreakpoints = getBreakpoints;
 	exports.getBreakpointsForSource = getBreakpointsForSource;
 	exports.getBreakpointsDisabled = getBreakpointsDisabled;
 	exports.getBreakpointsLoading = getBreakpointsLoading;
 	exports.getPendingBreakpoints = getPendingBreakpoints;
 
 	var _fromJS = __webpack_require__(237);
 
 	var _fromJS2 = _interopRequireDefault(_fromJS);
 
-	var _utils = __webpack_require__(234);
-
 	var _immutable = __webpack_require__(146);
 
 	var I = _interopRequireWildcard(_immutable);
 
 	var _makeRecord = __webpack_require__(230);
 
 	var _makeRecord2 = _interopRequireDefault(_makeRecord);
 
 	var _prefs = __webpack_require__(226);
 
+	var _breakpoint = __webpack_require__(1057);
+
 	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	var State = exports.State = (0, _makeRecord2.default)({
-	  breakpoints: I.Map(),
-	  pendingBreakpoints: restorePendingBreakpoints(),
-	  breakpointsDisabled: false
-	});
-
-	// Return the first argument that is a string, or null if nothing is a
-	// string.
-
-	/* This Source Code Form is subject to the terms of the Mozilla Public
-	 * License, v. 2.0. If a copy of the MPL was not distributed with this
-	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-	/**
-	 * Breakpoints reducer
-	 * @module reducers/breakpoints
-	 */
-
-	function firstString() {
-	  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
-	    args[_key] = arguments[_key];
-	  }
-
-	  for (var arg of args) {
-	    if (typeof arg === "string") {
-	      return arg;
-	    }
-	  }
-	  return null;
-	}
-
-	function locationMoved(location, newLocation) {
-	  return location.line !== newLocation.line || location.column != null && location.column !== newLocation.column;
-	}
-
-	function makeLocationId(location) {
-	  var sourceId = location.sourceId,
-	      line = location.line,
-	      column = location.column;
-
-	  var columnString = column || "";
-	  return `${sourceId}:${line}:${columnString}`;
-	}
-
-	function makePendingLocationId(location) {
-	  var sourceUrl = location.sourceUrl,
-	      line = location.line,
-	      column = location.column;
-
-	  var sourceUrlString = sourceUrl || "";
-	  var columnString = column || "";
-	  return `${sourceUrlString}:${line}:${columnString}`;
-	}
-
-	function allBreakpointsDisabled(state) {
-	  return state.breakpoints.every(x => x.disabled);
+	function initialState() {
+	  return (0, _makeRecord2.default)({
+	    breakpoints: I.Map(),
+	    pendingBreakpoints: restorePendingBreakpoints(),
+	    breakpointsDisabled: false
+	  })();
 	}
 
 	function update() {
-	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
+	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState();
 	  var action = arguments[1];
 
 	  switch (action.type) {
 	    case "ADD_BREAKPOINT":
 	      {
 	        var newState = addBreakpoint(state, action);
 	        setPendingBreakpoints(newState);
 	        return newState;
 	      }
 
+	    case "SYNC_BREAKPOINT":
+	      {
+	        var _newState = syncBreakpoint(state, action);
+	        setPendingBreakpoints(_newState);
+	        return _newState;
+	      }
+
+	    case "ENABLE_BREAKPOINT":
+	      {
+	        var _newState2 = enableBreakpoint(state, action);
+	        setPendingBreakpoints(_newState2);
+	        return _newState2;
+	      }
+
 	    case "REMOVE_BREAKPOINT":
 	      {
-	        var _newState = removeOrDisableBreakpoint(state, action);
-	        setPendingBreakpoints(_newState);
-	        return _newState;
+	        var _newState3 = removeBreakpoint(state, action);
+	        setPendingBreakpoints(_newState3);
+	        return _newState3;
+	      }
+
+	    case "DISABLE_BREAKPOINT":
+	      {
+	        var _newState4 = disableBreakpoint(state, action);
+	        setPendingBreakpoints(_newState4);
+	        return _newState4;
 	      }
 
 	    case "TOGGLE_BREAKPOINTS":
 	      {
 	        if (action.status === "start") {
 	          return state.set("breakpointsDisabled", action.shouldDisableBreakpoints);
 	        }
 	        break;
 	      }
 
 	    case "SET_BREAKPOINT_CONDITION":
 	      {
-	        var _newState2 = setCondition(state, action);
-	        setPendingBreakpoints(_newState2);
-	        return _newState2;
+	        var _newState5 = setCondition(state, action);
+	        setPendingBreakpoints(_newState5);
+	        return _newState5;
 	      }
 	  }
 
 	  return state;
 	}
 
 	function addBreakpoint(state, action) {
-	  var id = makeLocationId(action.breakpoint.location);
+	  var id = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
 	  if (action.status === "start") {
-	    var bp = state.breakpoints.get(id) || action.breakpoint;
-
-	    var updatedState = state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, {
-	      disabled: false,
+	    var updatedState = state.setIn(["breakpoints", id], _extends({}, action.breakpoint, {
 	      loading: true,
-	      // We want to do an OR here, but we can't because we need
-	      // empty strings to be truthy, i.e. an empty string is a valid
-	      // condition.
-	      condition: firstString(action.condition, bp.condition)
+	      condition: (0, _breakpoint.firstString)(action.condition, action.breakpoint.condition)
 	    })).set("breakpointsDisabled", false);
 
 	    return updatedState;
 	  }
 
 	  if (action.status === "done") {
 	    var _action$value = action.value,
 	        breakpointId = _action$value.id,
-	        text = _action$value.text;
+	        actualLocation = _action$value.actualLocation,
+	        generatedLocation = _action$value.generatedLocation;
 
 	    var location = action.breakpoint.location;
-	    var actualLocation = action.value.actualLocation;
 
 	    // If the breakpoint moved, update the map
-
-	    if (locationMoved(location, actualLocation)) {
+	    if ((0, _breakpoint.locationMoved)(location, actualLocation)) {
 	      state = slideBreakpoint(state, action);
 	      location = actualLocation;
 	    }
 
-	    var locationId = makeLocationId(location);
-	    var _bp = state.breakpoints.get(locationId);
-	    var _updatedState = state.setIn(["breakpoints", locationId], (0, _utils.updateObj)(_bp, {
+	    var locationId = (0, _breakpoint.makeLocationId)(location);
+	    var bp = state.breakpoints.get(locationId) || action.breakpoint;
+	    var updatedBreakpoint = _extends({}, bp, {
 	      id: breakpointId,
-	      disabled: false,
 	      loading: false,
-	      text: text
-	    }));
-
-	    return updatePendingBreakpoint(_updatedState, _bp);
+	      generatedLocation,
+	      text: ""
+	    });
+	    var _updatedState = state.setIn(["breakpoints", locationId], updatedBreakpoint);
+
+	    return updatePendingBreakpoint(_updatedState, updatedBreakpoint);
 	  }
 
 	  if (action.status === "error") {
 	    // Remove the optimistic update
 	    return state.deleteIn(["breakpoints", id]);
 	  }
 	}
 
-	function disableBreakpoint(state, id) {
+	function syncBreakpoint(state, action) {
+	  if (action.status === "start") {
+	    // add a breakpoint, so we always have something to work with
+	    return optimisticlyAddBreakpoint(state, action.breakpoint);
+	  }
+	  if (action.status === "done") {
+	    // when the action completes, we can commit the breakpoint
+	    var breakpoint = action.breakpoint,
+	        _action$value2 = action.value,
+	        actualLocation = _action$value2.actualLocation,
+	        generatedLocation = _action$value2.generatedLocation;
+
+	    var sameLocation = !(0, _breakpoint.locationMoved)(breakpoint.location, actualLocation);
+
+	    // if the breakpoint is the same as the optimistic breakpoint, we can commit
+	    // to the optimistic value.
+	    if (sameLocation) {
+	      return commitBreakpoint(state, breakpoint, action.value);
+	    }
+
+	    // if the breakpoint is not the same, we will use the actual location sent
+	    // by the server, and correct the breakpoint with that new information.
+	    // Correcting a breakpoint deletes both the pending breakpoint and the
+	    // optimistic breakpoint. Correcting will commit the corrected value
+	    var overrides = { location: actualLocation, generatedLocation };
+	    var updatedState = correctBreakpoint(state, breakpoint, overrides);
+	    var id = (0, _breakpoint.makeLocationId)(actualLocation);
+
+	    // once the corrected breakpoint is added and commited, we can update the
+	    // pending breakpoints with that information.
+	    var correctedBreakpoint = updatedState.breakpoints.get(id);
+	    return updatePendingBreakpoint(updatedState, correctedBreakpoint);
+	  }
+
+	  if (action.status === "error") {
+	    // Remove the optimistic update and pending breakpoint
+	    return deleteBreakpoint(state, action.breakpoint.location);
+	  }
+	}
+
+	function enableBreakpoint(state, action) {
+	  if (action.status != "done") {
+	    return state;
+	  }
+
+	  var id = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
 	  var bp = state.breakpoints.get(id);
-	  var breakpoint = (0, _utils.updateObj)(bp, {
+	  var updatedBreakpoint = _extends({}, bp, {
+	    id: action.value.id,
+	    loading: false,
+	    disabled: false
+	  });
+	  var updatedState = state.setIn(["breakpoints", id], updatedBreakpoint);
+	  return updatePendingBreakpoint(updatedState, updatedBreakpoint);
+	}
+
+	function disableBreakpoint(state, action) {
+	  if (action.status != "done") {
+	    return state;
+	  }
+	  var id = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
+	  var bp = state.breakpoints.get(id);
+	  var breakpoint = _extends({}, bp, {
 	    loading: false,
 	    disabled: true
 	  });
 	  var updatedState = state.setIn(["breakpoints", id], breakpoint);
 	  return updatePendingBreakpoint(updatedState, breakpoint);
 	}
 
-	function deleteBreakpoint(state, id, pendingId) {
+	function deleteBreakpoint(state, location) {
+	  var id = (0, _breakpoint.makeLocationId)(location);
+	  var pendingId = (0, _breakpoint.makePendingLocationId)(location);
+
 	  return state.deleteIn(["breakpoints", id]).deleteIn(["pendingBreakpoints", pendingId]);
 	}
 
-	function removeOrDisableBreakpoint(state, action) {
+	function removeBreakpoint(state, action) {
 	  if (action.status != "done") {
 	    return state;
 	  }
 
-	  var id = makeLocationId(action.breakpoint.location);
-	  var pendingId = makePendingLocationId(action.breakpoint.location);
-
-	  var updatedState = action.disabled ? disableBreakpoint(state, id) : deleteBreakpoint(state, id, pendingId);
-
-	  return updatedState.set("breakpointsDisabled", allBreakpointsDisabled(updatedState));
+	  var updatedState = deleteBreakpoint(state, action.breakpoint.location);
+
+	  return updatedState.set("breakpointsDisabled", (0, _breakpoint.allBreakpointsDisabled)(updatedState));
 	}
 
 	function setCondition(state, action) {
-	  var id = makeLocationId(action.breakpoint.location);
+	  var id = (0, _breakpoint.makeLocationId)(action.breakpoint.location);
 
 	  if (action.status === "start") {
 	    var bp = state.breakpoints.get(id);
-	    return state.setIn(["breakpoints", id], (0, _utils.updateObj)(bp, {
+	    return state.setIn(["breakpoints", id], _extends({}, bp, {
 	      loading: true,
 	      condition: action.condition
 	    }));
 	  }
 
 	  if (action.status === "done") {
-	    var _bp2 = state.breakpoints.get(id);
-	    var updatedBreakpoint = (0, _utils.updateObj)(_bp2, {
+	    var _bp = state.breakpoints.get(id);
+	    var updatedBreakpoint = _extends({}, _bp, {
 	      id: action.value.id,
 	      loading: false
 	    });
-
 	    var updatedState = state.setIn(["breakpoints", id], updatedBreakpoint);
 
 	    return updatePendingBreakpoint(updatedState, updatedBreakpoint);
 	  }
 
 	  if (action.status === "error") {
 	    return state.deleteIn(["breakpoints", id]);
 	  }
 	}
 
+	// Syncing Methods
+	function optimisticlyAddBreakpoint(state, breakpoint) {
+	  var id = (0, _breakpoint.makeLocationId)(breakpoint.location);
+	  var updateOpts = {
+	    loading: true
+	  };
+
+	  return state.setIn(["breakpoints", id], _extends({}, breakpoint, updateOpts));
+	}
+
+	function commitBreakpoint(state, breakpoint) {
+	  var overrides = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+
+	  // A commited breakpoint is no longer loading, and acts like a normal
+	  // breakpoint
+	  var location = overrides.location || breakpoint.location;
+	  var id = (0, _breakpoint.makeLocationId)(location);
+	  var updatedOpts = _extends({}, overrides, { loading: false });
+	  var updatedBreakpoint = _extends({}, breakpoint, updatedOpts);
+
+	  return state.setIn(["breakpoints", id], updatedBreakpoint);
+	}
+
+	function correctBreakpoint(state, breakpoint, overrides) {
+	  var intermState = deleteBreakpoint(state, breakpoint.location);
+	  var newLocationId = (0, _breakpoint.makeLocationId)(overrides.location);
+	  var newProperties = _extends({ id: newLocationId }, overrides);
+
+	  return commitBreakpoint(intermState, breakpoint, newProperties);
+	}
+
+	// TODO: remove this in favor of the correct/commit breakpoint pattern
 	function slideBreakpoint(state, action) {
-	  var _action$value2 = action.value,
-	      actualLocation = _action$value2.actualLocation,
-	      id = _action$value2.id;
+	  var _action$value3 = action.value,
+	      actualLocation = _action$value3.actualLocation,
+	      id = _action$value3.id;
 	  var breakpoint = action.breakpoint;
 
 	  var currentBp = state.breakpoints.get(id) || (0, _fromJS2.default)(breakpoint);
 
-	  var locationId = makeLocationId(breakpoint.location);
-	  var movedLocationId = makeLocationId(actualLocation);
+	  var locationId = (0, _breakpoint.makeLocationId)(breakpoint.location);
+	  var movedLocationId = (0, _breakpoint.makeLocationId)(actualLocation);
 	  var updatedState = state.deleteIn(["breakpoints", locationId]);
 
-	  return updatedState.setIn(["breakpoints", movedLocationId], (0, _utils.updateObj)(currentBp, { location: actualLocation }));
+	  return updatedState.setIn(["breakpoints", movedLocationId], _extends({}, currentBp, {
+	    location: actualLocation
+	  }));
 	}
 
 	function makePendingBreakpoint(bp) {
 	  var _bp$location = bp.location,
 	      sourceUrl = _bp$location.sourceUrl,
 	      line = _bp$location.line,
 	      column = _bp$location.column,
 	      condition = bp.condition,
-	      disabled = bp.disabled;
+	      disabled = bp.disabled,
+	      generatedLocation = bp.generatedLocation;
 
 
 	  var location = { sourceUrl, line, column };
-	  return { condition, disabled, location };
+	  return { condition, disabled, generatedLocation, location };
 	}
 
 	function setPendingBreakpoints(state) {
 	  _prefs.prefs.pendingBreakpoints = state.pendingBreakpoints;
 	}
 
 	function updatePendingBreakpoint(state, breakpoint) {
-	  var id = makePendingLocationId(breakpoint.location);
+	  var id = (0, _breakpoint.makePendingLocationId)(breakpoint.location);
 	  return state.setIn(["pendingBreakpoints", id], makePendingBreakpoint(breakpoint));
 	}
 
 	function restorePendingBreakpoints() {
 	  return I.Map(_prefs.prefs.pendingBreakpoints);
 	}
 
 	// Selectors
 
 	function getBreakpoint(state, location) {
-	  return state.breakpoints.breakpoints.get(makeLocationId(location));
+	  return state.breakpoints.breakpoints.get((0, _breakpoint.makeLocationId)(location));
 	}
 
 	function getBreakpoints(state) {
 	  return state.breakpoints.breakpoints;
 	}
 
 	function getBreakpointsForSource(state, sourceId) {
 	  return state.breakpoints.breakpoints.filter(bp => {
@@ -14724,43 +14751,39 @@ return /******/ (function(modules) { // 
 
 	  return createMap(value);
 	}
 
 	module.exports = fromJS;
 
 /***/ },
 /* 238 */
-/***/ function(module, exports, __webpack_require__) {
-
-	"use strict";
-
-	Object.defineProperty(exports, "__esModule", {
-	  value: true
-	});
-
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /* This Source Code Form is subject to the terms of the Mozilla Public
-	                                                                                                                                                                                                     * License, v. 2.0. If a copy of the MPL was not distributed with this
-	                                                                                                                                                                                                     * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/***/ function(module, exports) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+
+	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	var initialState = [];
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;
 	  var action = arguments[1];
 	  var seqId = action.seqId;
 
 
-	  if (action.type === _constants2.default.NAVIGATE) {
+	  if (action.type === "NAVIGATE") {
 	    return initialState;
 	  } else if (seqId) {
 	    var newState = void 0;
 	    if (action.status === "start") {
 	      newState = [].concat(_toConsumableArray(state), [seqId]);
 	    } else if (action.status === "error" || action.status === "done") {
 	      newState = state.filter(id => id !== seqId);
 	    }
@@ -14797,39 +14820,33 @@ return /******/ (function(modules) { // 
 	exports.getFrames = getFrames;
 	exports.getDebuggeeUrl = getDebuggeeUrl;
 	exports.getChromeScopes = getChromeScopes;
 
 	var _reselect = __webpack_require__(993);
 
 	var _prefs = __webpack_require__(226);
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	var State = exports.State = () => ({
 	  pause: undefined,
 	  isWaitingOnBreak: false,
 	  frames: undefined,
 	  selectedFrameId: undefined,
 	  loadedObjects: {},
 	  shouldPauseOnExceptions: _prefs.prefs.pauseOnExceptions,
 	  shouldIgnoreCaughtExceptions: _prefs.prefs.ignoreCaughtExceptions,
 	  debuggeeUrl: ""
 	});
 
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
 	  switch (action.type) {
-	    case _constants2.default.PAUSED:
+	    case "PAUSED":
 	      {
 	        var _selectedFrameId = action.selectedFrameId,
 	            _frames = action.frames,
 	            _loadedObjects = action.loadedObjects,
 	            pauseInfo = action.pauseInfo;
 
 	        pauseInfo.isInterrupted = pauseInfo.why.type === "interrupted";
 
@@ -14843,43 +14860,43 @@ return /******/ (function(modules) { // 
 	          isWaitingOnBreak: false,
 	          pause: pauseInfo,
 	          selectedFrameId: _selectedFrameId,
 	          frames: _frames,
 	          loadedObjects: objectMap
 	        });
 	      }
 
-	    case _constants2.default.RESUME:
+	    case "RESUME":
 	      return Object.assign({}, state, {
 	        pause: null,
 	        frames: null,
 	        selectedFrameId: null,
 	        loadedObjects: {}
 	      });
 
-	    case _constants2.default.TOGGLE_PRETTY_PRINT:
+	    case "TOGGLE_PRETTY_PRINT":
 	      if (action.status == "done") {
 	        var _frames2 = action.value.frames;
 	        var _pause = state.pause;
 	        if (_pause) {
 	          _pause.frame = _frames2[0];
 	        }
 
 	        return Object.assign({}, state, { pause: _pause, frames: _frames2 });
 	      }
 
 	      break;
-	    case _constants2.default.BREAK_ON_NEXT:
+	    case "BREAK_ON_NEXT":
 	      return Object.assign({}, state, { isWaitingOnBreak: true });
 
-	    case _constants2.default.SELECT_FRAME:
+	    case "SELECT_FRAME":
 	      return Object.assign({}, state, { selectedFrameId: action.frame.id });
 
-	    case _constants2.default.LOAD_OBJECT_PROPERTIES:
+	    case "LOAD_OBJECT_PROPERTIES":
 	      if (action.status === "start") {
 	        return _extends({}, state, {
 	          loadedObjects: _extends({}, state.loadedObjects, {
 	            [action.objectId]: {}
 	          })
 	        });
 	      }
 
@@ -14895,20 +14912,20 @@ return /******/ (function(modules) { // 
 	        return _extends({}, state, {
 	          loadedObjects: _extends({}, state.loadedObjects, {
 	            [action.objectId]: { ownProperties, prototype, ownSymbols }
 	          })
 	        });
 	      }
 	      break;
 
-	    case _constants2.default.NAVIGATE:
+	    case "NAVIGATE":
 	      return Object.assign({}, State(), { debuggeeUrl: action.url });
 
-	    case _constants2.default.PAUSE_ON_EXCEPTIONS:
+	    case "PAUSE_ON_EXCEPTIONS":
 	      var _shouldPauseOnExceptions = action.shouldPauseOnExceptions,
 	          _shouldIgnoreCaughtExceptions = action.shouldIgnoreCaughtExceptions;
 
 
 	      _prefs.prefs.pauseOnExceptions = _shouldPauseOnExceptions;
 	      _prefs.prefs.ignoreCaughtExceptions = _shouldIgnoreCaughtExceptions;
 
 	      return Object.assign({}, state, {
@@ -15003,21 +15020,22 @@ return /******/ (function(modules) { // 
 	exports.getHighlightedLineRange = getHighlightedLineRange;
 
 	var _makeRecord = __webpack_require__(230);
 
 	var _makeRecord2 = _interopRequireDefault(_makeRecord);
 
 	var _prefs = __webpack_require__(226);
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	/**
+	 * UI reducer
+	 * @module reducers/ui
+	 */
 
 	var State = exports.State = (0, _makeRecord2.default)({
 	  fileSearchOn: false,
 	  fileSearchQuery: "",
 	  fileSearchModifiers: (0, _makeRecord2.default)({
 	    caseSensitive: _prefs.prefs.fileSearchCaseSensitive,
 	    wholeWord: _prefs.prefs.fileSearchWholeWord,
 	    regexMatch: _prefs.prefs.fileSearchRegexMatch
@@ -15027,53 +15045,48 @@ return /******/ (function(modules) { // 
 	  symbolSearchType: "functions",
 	  shownSource: "",
 	  startPanelCollapsed: _prefs.prefs.startPanelCollapsed,
 	  endPanelCollapsed: _prefs.prefs.endPanelCollapsed,
 	  frameworkGroupingOn: _prefs.prefs.frameworkGroupingOn,
 	  highlightedLineRange: undefined
 	});
 
-	/**
-	 * UI reducer
-	 * @module reducers/ui
-	 */
-
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
 	  switch (action.type) {
-	    case _constants2.default.TOGGLE_PROJECT_SEARCH:
+	    case "TOGGLE_PROJECT_SEARCH":
 	      {
 	        return state.set("projectSearchOn", action.value);
 	      }
 
-	    case _constants2.default.TOGGLE_FRAMEWORK_GROUPING:
+	    case "TOGGLE_FRAMEWORK_GROUPING":
 	      {
 	        _prefs.prefs.frameworkGroupingOn = action.value;
 	        return state.set("frameworkGroupingOn", action.value);
 	      }
 
-	    case _constants2.default.TOGGLE_FILE_SEARCH:
+	    case "TOGGLE_FILE_SEARCH":
 	      {
 	        return state.set("fileSearchOn", action.value);
 	      }
 
-	    case _constants2.default.TOGGLE_SYMBOL_SEARCH:
+	    case "TOGGLE_SYMBOL_SEARCH":
 	      {
 	        return state.set("symbolSearchOn", action.value);
 	      }
 
-	    case _constants2.default.UPDATE_FILE_SEARCH_QUERY:
+	    case "UPDATE_FILE_SEARCH_QUERY":
 	      {
 	        return state.set("fileSearchQuery", action.query);
 	      }
 
-	    case _constants2.default.TOGGLE_FILE_SEARCH_MODIFIER:
+	    case "TOGGLE_FILE_SEARCH_MODIFIER":
 	      {
 	        var actionVal = !state.getIn(["fileSearchModifiers", action.modifier]);
 
 	        if (action.modifier == "caseSensitive") {
 	          _prefs.prefs.fileSearchCaseSensitive = actionVal;
 	        }
 
 	        if (action.modifier == "wholeWord") {
@@ -15082,27 +15095,27 @@ return /******/ (function(modules) { // 
 
 	        if (action.modifier == "regexMatch") {
 	          _prefs.prefs.fileSearchRegexMatch = actionVal;
 	        }
 
 	        return state.setIn(["fileSearchModifiers", action.modifier], actionVal);
 	      }
 
-	    case _constants2.default.SET_SYMBOL_SEARCH_TYPE:
+	    case "SET_SYMBOL_SEARCH_TYPE":
 	      {
 	        return state.set("symbolSearchType", action.symbolType);
 	      }
 
-	    case _constants2.default.SHOW_SOURCE:
+	    case "SHOW_SOURCE":
 	      {
 	        return state.set("shownSource", action.sourceUrl);
 	      }
 
-	    case _constants2.default.TOGGLE_PANE:
+	    case "TOGGLE_PANE":
 	      {
 	        if (action.position == "start") {
 	          _prefs.prefs.startPanelCollapsed = action.paneCollapsed;
 	          return state.set("startPanelCollapsed", action.paneCollapsed);
 	        }
 
 	        _prefs.prefs.endPanelCollapsed = action.paneCollapsed;
 	        return state.set("endPanelCollapsed", action.paneCollapsed);
@@ -15197,40 +15210,36 @@ return /******/ (function(modules) { // 
 	var _immutable = __webpack_require__(146);
 
 	var I = _interopRequireWildcard(_immutable);
 
 	var _fromJS = __webpack_require__(237);
 
 	var _fromJS2 = _interopRequireDefault(_fromJS);
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
+	var State = exports.State = (0, _makeRecord2.default)({
+	  coverageOn: false,
+	  hitCount: I.Map()
+	});
+
 	/**
 	 * Code Coverage reducer
 	 * @module reducers/coverage
 	 */
 
-	var State = exports.State = (0, _makeRecord2.default)({
-	  coverageOn: false,
-	  hitCount: I.Map()
-	});
-
 	function update() {
 	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : State();
 	  var action = arguments[1];
 
 	  switch (action.type) {
-	    case _constants2.default.RECORD_COVERAGE:
+	    case "RECORD_COVERAGE":
 	      return state.mergeIn(["hitCount"], (0, _fromJS2.default)(action.value.coverage)).setIn(["coverageOn"], true);
 
 	    default:
 	      {
 	        return state;
 	      }
 	  }
 	}
@@ -15249,37 +15258,67 @@ return /******/ (function(modules) { // 
 	exports.default = update;
 
 /***/ },
 /* 242 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
-	var expressions = __webpack_require__(228);
-	var sources = __webpack_require__(232);
-	var pause = __webpack_require__(239);
-	var breakpoints = __webpack_require__(236);
-	var eventListeners = __webpack_require__(231);
-	var ui = __webpack_require__(240);
-	var coverage = __webpack_require__(241);
+	var _expressions = __webpack_require__(228);
+
+	var expressions = _interopRequireWildcard(_expressions);
+
+	var _sources = __webpack_require__(232);
+
+	var sources = _interopRequireWildcard(_sources);
+
+	var _pause = __webpack_require__(239);
+
+	var pause = _interopRequireWildcard(_pause);
+
+	var _breakpoints = __webpack_require__(236);
+
+	var breakpoints = _interopRequireWildcard(_breakpoints);
+
+	var _eventListeners = __webpack_require__(231);
+
+	var eventListeners = _interopRequireWildcard(_eventListeners);
+
+	var _ui = __webpack_require__(240);
+
+	var ui = _interopRequireWildcard(_ui);
+
+	var _ast = __webpack_require__(1058);
+
+	var ast = _interopRequireWildcard(_ast);
+
+	var _coverage = __webpack_require__(241);
+
+	var coverage = _interopRequireWildcard(_coverage);
+
+	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 	/**
 	 * @param object - location
 	 */
 
 	module.exports = {
 	  getSource: sources.getSource,
+	  getNewSelectedSourceId: sources.getNewSelectedSourceId,
+	  removeSourceFromTabList: sources.removeSourceFromTabList,
+	  removeSourcesFromTabList: sources.removeSourcesFromTabList,
 	  getSourceByURL: sources.getSourceByURL,
 	  getSourceInSources: sources.getSourceInSources,
 	  getSources: sources.getSources,
 	  getSourceText: sources.getSourceText,
 	  getSourceTabs: sources.getSourceTabs,
 	  getSourcesForTabs: sources.getSourcesForTabs,
 	  getSelectedSource: sources.getSelectedSource,
+	  getSelectedSourceText: sources.getSelectedSourceText,
 	  getSelectedLocation: sources.getSelectedLocation,
 	  getPendingSelectedLocation: sources.getPendingSelectedLocation,
 	  getPrettySource: sources.getPrettySource,
 
 	  getBreakpoint: breakpoints.getBreakpoint,
 	  getBreakpoints: breakpoints.getBreakpoints,
 	  getPendingBreakpoints: breakpoints.getPendingBreakpoints,
 	  getBreakpointsForSource: breakpoints.getBreakpointsForSource,
@@ -15311,17 +15350,22 @@ return /******/ (function(modules) { // 
 	  getSymbolSearchState: ui.getSymbolSearchState,
 	  getSymbolSearchType: ui.getSymbolSearchType,
 	  getShownSource: ui.getShownSource,
 	  getPaneCollapse: ui.getPaneCollapse,
 
 	  getExpressions: expressions.getExpressions,
 	  getVisibleExpressions: expressions.getVisibleExpressions,
 	  getExpression: expressions.getExpression,
-	  getHighlightedLineRange: ui.getHighlightedLineRange
+	  getHighlightedLineRange: ui.getHighlightedLineRange,
+
+	  getSymbols: ast.getSymbols,
+	  hasSymbols: ast.hasSymbols,
+	  getOutOfScopeLocations: ast.getOutOfScopeLocations,
+	  getSelection: ast.getSelection
 	};
 
 /***/ },
 /* 243 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
@@ -15350,17 +15394,17 @@ return /******/ (function(modules) { // 
 	__webpack_require__(327);
 
 	__webpack_require__(331);
 
 	var _devtoolsSplitter = __webpack_require__(910);
 
 	var _devtoolsSplitter2 = _interopRequireDefault(_devtoolsSplitter);
 
-	var _ProjectSearch2 = __webpack_require__(915);
+	var _ProjectSearch2 = __webpack_require__(1060);
 
 	var _ProjectSearch3 = _interopRequireDefault(_ProjectSearch2);
 
 	var _Sources2 = __webpack_require__(388);
 
 	var _Sources3 = _interopRequireDefault(_Sources2);
 
 	var _Editor2 = __webpack_require__(426);
@@ -15560,214 +15604,357 @@ return /******/ (function(modules) { // 
 	var _navigation = __webpack_require__(320);
 
 	var navigation = _interopRequireWildcard(_navigation);
 
 	var _ui = __webpack_require__(321);
 
 	var ui = _interopRequireWildcard(_ui);
 
+	var _ast = __webpack_require__(1059);
+
+	var ast = _interopRequireWildcard(_ast);
+
 	var _coverage = __webpack_require__(322);
 
 	var coverage = _interopRequireWildcard(_coverage);
 
 	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
-	exports.default = Object.assign({}, navigation, breakpoints, expressions, eventListeners, sources, pause, ui, coverage);
+	exports.default = Object.assign({}, navigation, breakpoints, expressions, eventListeners, sources, pause, ui, ast, coverage);
 
 /***/ },
 /* 245 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
 
+	var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+	var _getGeneratedLocation = (() => {
+	  var _ref = _asyncToGenerator(function* (source, sourceMaps, location) {
+	    if (!sourceMaps.isOriginalId(location.sourceId)) {
+	      return location;
+	    }
+
+	    return yield sourceMaps.getGeneratedLocation(location, source.toJS());
+	  });
+
+	  return function _getGeneratedLocation(_x2, _x3, _x4) {
+	    return _ref.apply(this, arguments);
+	  };
+	})();
+
+	var syncClientBreakpoint = (() => {
+	  var _ref2 = _asyncToGenerator(function* (sourceId, client, sourceMaps, pendingBreakpoint) {
+	    var generatedSourceId = sourceMaps.isOriginalId(sourceId) ? (0, _devtoolsSourceMap.originalToGeneratedId)(sourceId) : sourceId;
+
+	    // this is the generatedLocation of the pending breakpoint, with
+	    // the source id updated to reflect the new connection
+	    var oldGeneratedLocation = _extends({}, pendingBreakpoint.generatedLocation, {
+	      sourceId: generatedSourceId
+	    });
+
+	    // early return if breakpoint is disabled with overrides to update
+	    // the id as expected, without talking to server
+	    if (pendingBreakpoint.disabled) {
+	      return {
+	        id: generatedSourceId,
+	        actualLocation: _extends({}, pendingBreakpoint.location, { id: sourceId }),
+	        oldGeneratedLocation
+	      };
+	    }
+
+	    // If we are not disabled, set the breakpoint on the server and get
+	    // that info so we can set it on our breakpoints.
+	    var clientBreakpoint = yield client.setBreakpoint(oldGeneratedLocation, pendingBreakpoint.condition, sourceMaps.isOriginalId(sourceId));
+
+	    // Original location is the location in the src file
+	    var clientOriginalLocation = yield sourceMaps.getOriginalLocation(clientBreakpoint.actualLocation);
+
+	    // make sure that we are re-adding the same type of breakpoint. Column
+	    // or line
+	    var actualLocation = (0, _breakpoint.equalizeLocationColumn)(clientOriginalLocation, pendingBreakpoint.location);
+
+	    // the generatedLocation might have slid, so now we can adjust it
+	    var generatedLocation = clientBreakpoint.actualLocation;
+
+	    var id = clientBreakpoint.id,
+	        hitCount = clientBreakpoint.hitCount;
+
+	    return { id, actualLocation, hitCount, generatedLocation };
+	  });
+
+	  return function syncClientBreakpoint(_x5, _x6, _x7, _x8) {
+	    return _ref2.apply(this, arguments);
+	  };
+	})();
+
+	var addClientBreakpoint = (() => {
+	  var _ref3 = _asyncToGenerator(function* (state, client, sourceMaps, breakpoint) {
+	    var location = breakpoint.location;
+	    var source = (0, _selectors.getSource)(state, location.sourceId);
+	    var generatedLocation = yield _getGeneratedLocation(source, sourceMaps, location);
+
+	    var clientBreakpoint = yield client.setBreakpoint(generatedLocation, breakpoint.condition, sourceMaps.isOriginalId(breakpoint.location.sourceId));
+
+	    var actualLocation = yield sourceMaps.getOriginalLocation(clientBreakpoint.actualLocation);
+
+	    var id = clientBreakpoint.id,
+	        hitCount = clientBreakpoint.hitCount;
+
+	    return { id, actualLocation, hitCount, generatedLocation };
+	  });
+
+	  return function addClientBreakpoint(_x9, _x10, _x11, _x12) {
+	    return _ref3.apply(this, arguments);
+	  };
+	})();
+
+	/**
+	 * Enabling a breakpoint
+	 * will reuse the existing breakpoint information that is stored.
+	 *
+	 * @memberof actions/breakpoints
+	 * @static
+	 * @param {Location} $1.location Location  value
+	 */
+
+
 	exports.enableBreakpoint = enableBreakpoint;
+	exports.syncBreakpoint = syncBreakpoint;
 	exports.addBreakpoint = addBreakpoint;
 	exports.disableBreakpoint = disableBreakpoint;
 	exports.removeBreakpoint = removeBreakpoint;
 	exports.toggleAllBreakpoints = toggleAllBreakpoints;
 	exports.setBreakpointCondition = setBreakpointCondition;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _promise = __webpack_require__(193);
 
 	var _selectors = __webpack_require__(242);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+	var _devtoolsSourceMap = __webpack_require__(898);
+
+	var _breakpoint = __webpack_require__(1057);
 
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 	/* This Source Code Form is subject to the terms of the Mozilla Public
 	 * License, v. 2.0. If a copy of the MPL was not distributed with this
 	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	/**
 	 * Redux actions for breakpoints
 	 * @module actions/breakpoints
 	 */
 
 	function _breakpointExists(state, location) {
 	  var currentBp = (0, _selectors.getBreakpoint)(state, location);
 	  return currentBp && !currentBp.disabled;
 	}
 
-	function _getOrCreateBreakpoint(state, location, condition) {
-	  return (0, _selectors.getBreakpoint)(state, location) || { location, condition, text: "" };
-	}
-
-	/**
-	 * Enabling a breakpoint calls {@link addBreakpoint}
-	 * which will reuse the existing breakpoint information that is stored.
+	function _createBreakpoint(location) {
+	  var overrides = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+	  var condition = overrides.condition,
+	      disabled = overrides.disabled,
+	      generatedLocation = overrides.generatedLocation;
+
+	  var properties = {
+	    condition: condition || null,
+	    disabled: disabled || false,
+	    generatedLocation,
+	    location
+	  };
+
+	  return properties;
+	}
+
+	function enableBreakpoint(location) {
+	  return (_ref4) => {
+	    var dispatch = _ref4.dispatch,
+	        getState = _ref4.getState,
+	        client = _ref4.client,
+	        sourceMaps = _ref4.sourceMaps;
+
+	    var breakpoint = (0, _selectors.getBreakpoint)(getState(), location);
+	    if (!breakpoint) {
+	      throw new Error("attempted to enable a breakpoint that does not exist");
+	    }
+
+	    return dispatch({
+	      type: "ENABLE_BREAKPOINT",
+	      breakpoint,
+	      [_promise.PROMISE]: addClientBreakpoint(getState(), client, sourceMaps, breakpoint)
+	    });
+	  };
+	}
+
+	/**
+	 * Syncing a breakpoint add breakpoint information that is stored, and
+	 * contact the server for more data.
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
-	 */
-	function enableBreakpoint(location) {
-	  return addBreakpoint(location);
-	}
-
-	/**
-	 * Add a new or enable an existing breakpoint
+	 * @param {String} $1.sourceId String  value
+	 * @param {PendingBreakpoint} $1.location PendingBreakpoint  value
+	 */
+	function syncBreakpoint(sourceId, pendingBreakpoint) {
+	  return (_ref5) => {
+	    var dispatch = _ref5.dispatch,
+	        getState = _ref5.getState,
+	        client = _ref5.client,
+	        sourceMaps = _ref5.sourceMaps;
+	    var _pendingBreakpoint$lo = pendingBreakpoint.location,
+	        line = _pendingBreakpoint$lo.line,
+	        sourceUrl = _pendingBreakpoint$lo.sourceUrl,
+	        column = _pendingBreakpoint$lo.column;
+
+	    var location = { sourceId, sourceUrl, line, column };
+	    var breakpoint = _createBreakpoint(location, pendingBreakpoint);
+
+	    var syncPromise = syncClientBreakpoint(sourceId, client, sourceMaps, pendingBreakpoint);
+
+	    return dispatch({
+	      type: "SYNC_BREAKPOINT",
+	      breakpoint,
+	      [_promise.PROMISE]: syncPromise
+	    });
+	  };
+	}
+
+	/**
+	 * Add a new breakpoint
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
 	 * @param {String} $1.condition Conditional breakpoint condition value
-	 * @param {Function} $1.getTextForLine Get the text to represent the line
+	 * @param {Boolean} $1.disabled Disable value for breakpoint value
 	 */
 	function addBreakpoint(location) {
-	  var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
-	      condition = _ref.condition,
-	      getTextForLine = _ref.getTextForLine;
-
-	  return (_ref2) => {
-	    var dispatch = _ref2.dispatch,
-	        getState = _ref2.getState,
-	        client = _ref2.client,
-	        sourceMaps = _ref2.sourceMaps;
+	  var _ref6 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
+	      condition = _ref6.condition;
+
+	  return (_ref7) => {
+	    var dispatch = _ref7.dispatch,
+	        getState = _ref7.getState,
+	        client = _ref7.client,
+	        sourceMaps = _ref7.sourceMaps;
 
 	    if (_breakpointExists(getState(), location)) {
 	      return Promise.resolve();
 	    }
 
-	    var bp = _getOrCreateBreakpoint(getState(), location, condition);
-
+	    var breakpoint = _createBreakpoint(location, { condition });
 	    return dispatch({
-	      type: _constants2.default.ADD_BREAKPOINT,
-	      breakpoint: bp,
+	      type: "ADD_BREAKPOINT",
+	      breakpoint,
 	      condition: condition,
-	      [_promise.PROMISE]: _asyncToGenerator(function* () {
-	        if (sourceMaps.isOriginalId(bp.location.sourceId)) {
-	          var source = (0, _selectors.getSource)(getState(), bp.location.sourceId);
-	          location = yield sourceMaps.getGeneratedLocation(bp.location, source.toJS());
-	        }
-
-	        var _ref4 = yield client.setBreakpoint(location, bp.condition, sourceMaps.isOriginalId(bp.location.sourceId)),
-	            id = _ref4.id,
-	            actualLocation = _ref4.actualLocation,
-	            hitCount = _ref4.hitCount;
-
-	        actualLocation = yield sourceMaps.getOriginalLocation(actualLocation);
-
-	        // If this breakpoint is being re-enabled, it already has a
-	        // text snippet.
-	        var text = bp.text;
-	        if (!text) {
-	          text = getTextForLine ? getTextForLine(actualLocation.line) : "";
-	        }
-	        return { id, actualLocation, text, hitCount };
-	      })()
+	      [_promise.PROMISE]: addClientBreakpoint(getState(), client, sourceMaps, breakpoint)
 	    });
 	  };
 	}
 
 	/**
 	 * Disable a single breakpoint
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
 	 */
 	function disableBreakpoint(location) {
-	  return _removeOrDisableBreakpoint(location, true);
+	  return (_ref8) => {
+	    var dispatch = _ref8.dispatch,
+	        getState = _ref8.getState,
+	        client = _ref8.client;
+
+	    var bp = (0, _selectors.getBreakpoint)(getState(), location);
+	    if (!bp) {
+	      throw new Error("attempt to disable a breakpoint that does not exist");
+	    }
+	    if (bp.loading) {
+	      // TODO(jwl): make this wait until the breakpoint is saved if it
+	      // is still loading
+	      throw new Error("attempt to disable unsaved breakpoint");
+	    }
+
+	    var action = {
+	      type: "DISABLE_BREAKPOINT",
+	      breakpoint: bp,
+	      disabled: true,
+	      [_promise.PROMISE]: client.removeBreakpoint(bp.id)
+	    };
+
+	    return dispatch(action);
+	  };
 	}
 
 	/**
 	 * Remove a single breakpoint
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
 	 */
 	function removeBreakpoint(location) {
-	  return _removeOrDisableBreakpoint(location);
-	}
-
-	function _removeOrDisableBreakpoint(location) {
-	  var isDisabled = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
-
-	  return (_ref5) => {
-	    var dispatch = _ref5.dispatch,
-	        getState = _ref5.getState,
-	        client = _ref5.client;
+	  return (_ref9) => {
+	    var dispatch = _ref9.dispatch,
+	        getState = _ref9.getState,
+	        client = _ref9.client;
 
 	    var bp = (0, _selectors.getBreakpoint)(getState(), location);
 	    if (!bp) {
 	      throw new Error("attempt to remove breakpoint that does not exist");
 	    }
 	    if (bp.loading) {
 	      // TODO(jwl): make this wait until the breakpoint is saved if it
 	      // is still loading
 	      throw new Error("attempt to remove unsaved breakpoint");
 	    }
 
 	    var action = {
-	      type: _constants2.default.REMOVE_BREAKPOINT,
-	      breakpoint: bp,
-	      disabled: isDisabled
-	    };
-
-	    // If the breakpoint is already disabled, we don't need to remove
-	    // it from the server. We just need to dispatch an action
-	    // simulating a successful server request to remove it, and it
-	    // will be removed completely from the state.
-	    if (!bp.disabled) {
-	      return dispatch(Object.assign({}, action, {
-	        [_promise.PROMISE]: client.removeBreakpoint(bp.id)
-	      }));
-	    }
-	    return dispatch(Object.assign({}, action, { status: "done" }));
+	      type: "REMOVE_BREAKPOINT",
+	      breakpoint: bp
+	    };
+
+	    // If the breakpoint is already disabled, we don't need to communicate
+	    // with the server. We just need to dispatch an action
+	    // simulating a successful server request
+	    if (bp.disabled) {
+	      return dispatch(Object.assign({}, action, { status: "done" }));
+	    }
+
+	    return dispatch(Object.assign({}, action, {
+	      [_promise.PROMISE]: client.removeBreakpoint(bp.id)
+	    }));
 	  };
 	}
 
 	/**
 	 * Toggle All Breakpoints
 	 *
 	 * @memberof actions/breakpoints
 	 * @static
 	 */
 	function toggleAllBreakpoints(shouldDisableBreakpoints) {
-	  return (_ref6) => {
-	    var dispatch = _ref6.dispatch,
-	        getState = _ref6.getState;
+	  return (_ref10) => {
+	    var dispatch = _ref10.dispatch,
+	        getState = _ref10.getState;
 
 	    var breakpoints = (0, _selectors.getBreakpoints)(getState());
 	    return dispatch({
-	      type: _constants2.default.TOGGLE_BREAKPOINTS,
+	      type: "TOGGLE_BREAKPOINTS",
 	      shouldDisableBreakpoints,
 	      [_promise.PROMISE]: _asyncToGenerator(function* () {
-	        for (var _ref8 of breakpoints) {
-	          var _ref9 = _slicedToArray(_ref8, 2);
-
-	          var breakpoint = _ref9[1];
+	        for (var _ref12 of breakpoints) {
+	          var _ref13 = _slicedToArray(_ref12, 2);
+
+	          var breakpoint = _ref13[1];
 
 	          if (shouldDisableBreakpoints) {
 	            yield dispatch(disableBreakpoint(breakpoint.location));
 	          } else {
 	            yield dispatch(enableBreakpoint(breakpoint.location));
 	          }
 	        }
 	      })()
@@ -15780,42 +15967,41 @@ return /******/ (function(modules) { // 
 	 *
 	 * @throws {Error} "not implemented"
 	 * @memberof actions/breakpoints
 	 * @static
 	 * @param {Location} location
 	 *        @see DebuggerController.Breakpoints.addBreakpoint
 	 * @param {string} condition
 	 *        The condition to set on the breakpoint
+	 * @param {Boolean} $1.disabled Disable value for breakpoint value
 	 */
 	function setBreakpointCondition(location) {
-	  var _ref10 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
-	      condition = _ref10.condition,
-	      getTextForLine = _ref10.getTextForLine;
-
-	  // location: Location, condition: string, { getTextForLine }) {
-	  return (_ref11) => {
-	    var dispatch = _ref11.dispatch,
-	        getState = _ref11.getState,
-	        client = _ref11.client,
-	        sourceMaps = _ref11.sourceMaps;
+	  var _ref14 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
+	      condition = _ref14.condition;
+
+	  return (_ref15) => {
+	    var dispatch = _ref15.dispatch,
+	        getState = _ref15.getState,
+	        client = _ref15.client,
+	        sourceMaps = _ref15.sourceMaps;
 
 	    var bp = (0, _selectors.getBreakpoint)(getState(), location);
 	    if (!bp) {
-	      return dispatch(addBreakpoint(location, { condition, getTextForLine }));
+	      return dispatch(addBreakpoint(location, { condition }));
 	    }
 
 	    if (bp.loading) {
 	      // TODO(jwl): when this function is called, make sure the action
 	      // creator waits for the breakpoint to exist
 	      throw new Error("breakpoint must be saved");
 	    }
 
 	    return dispatch({
-	      type: _constants2.default.SET_BREAKPOINT_CONDITION,
+	      type: "SET_BREAKPOINT_CONDITION",
 	      breakpoint: bp,
 	      condition: condition,
 	      [_promise.PROMISE]: client.setBreakpointCondition(bp.id, location, condition, sourceMaps.isOriginalId(bp.location.sourceId))
 	    });
 	  };
 	}
 
 /***/ },
@@ -16163,32 +16349,22 @@ return /******/ (function(modules) { // 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.addExpression = addExpression;
 	exports.updateExpression = updateExpression;
 	exports.deleteExpression = deleteExpression;
 	exports.evaluateExpressions = evaluateExpressions;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _promise = __webpack_require__(193);
 
 	var _selectors = __webpack_require__(242);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
-	function expressionExists(expressions, input) {
-	  return !!expressions.find(e => e.input == input);
-	}
-
 	/**
 	 * Add expression for debugger to watch
 	 *
 	 * @param {object} expression
 	 * @param {number} expression.id
 	 * @memberof actions/pause
 	 * @static
 	 */
@@ -16197,28 +16373,37 @@ return /******/ (function(modules) { // 
 	      _ref$visible = _ref.visible,
 	      visible = _ref$visible === undefined ? true : _ref$visible;
 
 	  return (() => {
 	    var _ref2 = _asyncToGenerator(function* (_ref3) {
 	      var dispatch = _ref3.dispatch,
 	          getState = _ref3.getState;
 
-	      var expressions = (0, _selectors.getExpressions)(getState());
-	      if (!input || expressionExists(expressions, input)) {
-	        var expression = (0, _selectors.getExpression)(getState(), input);
-	        if (!expression.visible && visible) {
-	          yield dispatch(deleteExpression(expression));
-	        } else {
-	          return;
-	        }
+	      if (!input) {
+	        return;
+	      }
+
+	      var expression = (0, _selectors.getExpression)(getState(), input);
+	      if (expression && expression.visible) {
+	        return;
+	      }
+
+	      // Lets make the expression visible
+	      if (expression) {
+	        return dispatch({
+	          type: "UPDATE_EXPRESSION",
+	          expression,
+	          input,
+	          visible: true
+	        });
 	      }
 
 	      dispatch({
-	        type: _constants2.default.ADD_EXPRESSION,
+	        type: "ADD_EXPRESSION",
 	        input,
 	        visible
 	      });
 
 	      var selectedFrame = (0, _selectors.getSelectedFrame)(getState());
 	      var selectedFrameId = selectedFrame ? selectedFrame.id : null;
 	      dispatch(evaluateExpression({ input, visible }, selectedFrameId));
 	    });
@@ -16234,17 +16419,17 @@ return /******/ (function(modules) { // 
 	    var dispatch = _ref4.dispatch,
 	        getState = _ref4.getState;
 
 	    if (!input || input == expression.input) {
 	      return;
 	    }
 
 	    dispatch({
-	      type: _constants2.default.UPDATE_EXPRESSION,
+	      type: "UPDATE_EXPRESSION",
 	      expression,
 	      input: input,
 	      visible: expression.visible
 	    });
 
 	    var selectedFrame = (0, _selectors.getSelectedFrame)(getState());
 	    var selectedFrameId = selectedFrame ? selectedFrame.id : null;
 	    dispatch(evaluateExpressions(selectedFrameId));
@@ -16258,17 +16443,17 @@ return /******/ (function(modules) { // 
 	 * @memberof actions/pause
 	 * @static
 	 */
 	function deleteExpression(expression) {
 	  return (_ref5) => {
 	    var dispatch = _ref5.dispatch;
 
 	    dispatch({
-	      type: _constants2.default.DELETE_EXPRESSION,
+	      type: "DELETE_EXPRESSION",
 	      input: expression.input
 	    });
 	  };
 	}
 
 	/**
 	 *
 	 * @memberof actions/pause
@@ -16305,17 +16490,17 @@ return /******/ (function(modules) { // 
 	        client = _ref8.client;
 
 	    if (!expression.input) {
 	      console.warn("Expressions should not be empty");
 	      return;
 	    }
 
 	    return dispatch({
-	      type: _constants2.default.EVALUATE_EXPRESSION,
+	      type: "EVALUATE_EXPRESSION",
 	      input: expression.input,
 	      visible: expression.visible,
 	      [_promise.PROMISE]: client.evaluate(expression.input, { frameId })
 	    });
 	  };
 	}
 
 /***/ },
@@ -16431,26 +16616,20 @@ return /******/ (function(modules) { // 
 	 * @static
 	 * @param {string} eventNames
 	 */
 
 
 	exports.fetchEventListeners = fetchEventListeners;
 	exports.updateEventBreakpoints = updateEventBreakpoints;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _DevToolsUtils = __webpack_require__(222);
 
 	var _selectors = __webpack_require__(242);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } /* This Source Code Form is subject to the terms of the Mozilla Public
 	                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * License, v. 2.0. If a copy of the MPL was not distributed with this
 	                                                                                                                                                                                                                                                                                                                                                                                                                                                                            * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 	/* global window gThreadClient setNamedTimeout services EVENTS */
 	/* eslint no-shadow: 0  */
 
 	/**
 	 * Redux actions for the event listeners state
@@ -16473,30 +16652,30 @@ return /******/ (function(modules) { // 
 
 	    fetchListenersTimerID = setTimeout(() => {
 	      // In case there is still a request of listeners going on (it
 	      // takes several RDP round trips right now), make sure we wait
 	      // on a currently running request
 	      if (getState().eventListeners.fetchingListeners) {
 	        dispatch({
 	          type: services.WAIT_UNTIL,
-	          predicate: action => action.type === _constants2.default.FETCH_EVENT_LISTENERS && action.status === "done",
+	          predicate: action => action.type === "FETCH_EVENT_LISTENERS" && action.status === "done",
 	          run: dispatch => dispatch(fetchEventListeners())
 	        });
 	        return;
 	      }
 
 	      dispatch({
-	        type: _constants2.default.FETCH_EVENT_LISTENERS,
+	        type: "FETCH_EVENT_LISTENERS",
 	        status: "begin"
 	      });
 
 	      asPaused(getState(), client, _getEventListeners).then(listeners => {
 	        dispatch({
-	          type: _constants2.default.FETCH_EVENT_LISTENERS,
+	          type: "FETCH_EVENT_LISTENERS",
 	          status: "done",
 	          listeners: formatListeners(getState(), listeners)
 	        });
 	      });
 	    }, FETCH_EVENT_LISTENERS_DELAY);
 	  };
 	}
 
@@ -16514,17 +16693,17 @@ return /******/ (function(modules) { // 
 	function updateEventBreakpoints(eventNames) {
 	  return dispatch => {
 	    setNamedTimeout("event-breakpoints-update", 0, () => {
 	      gThreadClient.pauseOnDOMEvents(eventNames, function () {
 	        // Notify that event breakpoints were added/removed on the server.
 	        window.emit(EVENTS.EVENT_BREAKPOINTS_UPDATED);
 
 	        dispatch({
-	          type: _constants2.default.UPDATE_EVENT_BREAKPOINTS,
+	          type: "UPDATE_EVENT_BREAKPOINTS",
 	          eventNames: eventNames
 	        });
 	      });
 	    });
 	  };
 	}
 
 /***/ },
@@ -16539,29 +16718,24 @@ return /******/ (function(modules) { // 
 
 	var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
 
 	var checkPendingBreakpoint = (() => {
 	  var _ref = _asyncToGenerator(function* (state, dispatch, pendingBreakpoint, source) {
 	    var _pendingBreakpoint$lo = pendingBreakpoint.location,
 	        line = _pendingBreakpoint$lo.line,
 	        sourceUrl = _pendingBreakpoint$lo.sourceUrl,
-	        column = _pendingBreakpoint$lo.column,
-	        condition = pendingBreakpoint.condition;
+	        column = _pendingBreakpoint$lo.column;
 
 	    var sameSource = sourceUrl && sourceUrl === source.url;
 	    var location = { sourceId: source.id, sourceUrl, line, column };
 	    var bp = (0, _selectors.getBreakpoint)(state, location);
 
 	    if (sameSource && !bp) {
-	      if (location.column && (0, _devtoolsConfig.isEnabled)("columnBreakpoints")) {
-	        yield dispatch((0, _breakpoints.addBreakpoint)(location, { condition }));
-	      } else {
-	        yield dispatch((0, _breakpoints.addBreakpoint)(location, { condition }));
-	      }
+	      yield dispatch((0, _breakpoints.syncBreakpoint)(source.id, pendingBreakpoint));
 	    }
 	  });
 
 	  return function checkPendingBreakpoint(_x, _x2, _x3, _x4) {
 	    return _ref.apply(this, arguments);
 	  };
 	})();
 
@@ -16609,28 +16783,24 @@ return /******/ (function(modules) { // 
 	var _promise = __webpack_require__(193);
 
 	var _assert = __webpack_require__(223);
 
 	var _assert2 = _interopRequireDefault(_assert);
 
 	var _pause = __webpack_require__(255);
 
+	var _ast = __webpack_require__(1059);
+
 	var _breakpoints = __webpack_require__(245);
 
-	var _devtoolsConfig = __webpack_require__(828);
-
 	var _prettyPrint = __webpack_require__(903);
 
 	var _source = __webpack_require__(233);
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _prefs = __webpack_require__(226);
 
 	var _editor = __webpack_require__(257);
 
 	var _selectors = __webpack_require__(242);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
@@ -16656,17 +16826,17 @@ return /******/ (function(modules) { // 
 	}
 
 	function newSource(source) {
 	  return (() => {
 	    var _ref3 = _asyncToGenerator(function* (_ref4) {
 	      var dispatch = _ref4.dispatch,
 	          getState = _ref4.getState;
 
-	      dispatch({ type: _constants2.default.ADD_SOURCE, source });
+	      dispatch({ type: "ADD_SOURCE", source });
 
 	      if (_prefs.prefs.clientSourceMapsEnabled) {
 	        yield dispatch(loadSourceMap(source));
 	      }
 
 	      checkSelectedSource(getState(), dispatch, source);
 	      yield checkPendingBreakpoints(getState(), dispatch, source);
 	    });
@@ -16719,17 +16889,17 @@ return /******/ (function(modules) { // 
 	      var originalSources = urls.map(function (originalUrl) {
 	        return {
 	          url: originalUrl,
 	          id: sourceMaps.generatedToOriginalId(generatedSource.id, originalUrl),
 	          isPrettyPrinted: false
 	        };
 	      });
 
-	      dispatch({ type: _constants2.default.ADD_SOURCES, sources: originalSources });
+	      dispatch({ type: "ADD_SOURCES", sources: originalSources });
 
 	      originalSources.forEach(function (source) {
 	        checkSelectedSource(state, dispatch, source);
 	        checkPendingBreakpoints(state, dispatch, source);
 	      });
 	    });
 
 	    return function (_x10) {
@@ -16754,17 +16924,17 @@ return /******/ (function(modules) { // 
 	    var dispatch = _ref9.dispatch,
 	        getState = _ref9.getState;
 
 	    var source = (0, _selectors.getSourceByURL)(getState(), url);
 	    if (source) {
 	      dispatch(selectSource(source.get("id"), options));
 	    } else {
 	      dispatch({
-	        type: _constants2.default.SELECT_SOURCE_URL,
+	        type: "SELECT_SOURCE_URL",
 	        url: url,
 	        tabIndex: options.tabIndex,
 	        line: options.line
 	      });
 	    }
 	  };
 	}
 
@@ -16784,46 +16954,46 @@ return /******/ (function(modules) { // 
 	      // No connection, do nothing. This happens when the debugger is
 	      // shut down too fast and it tries to display a default source.
 	      return;
 	    }
 
 	    var source = (0, _selectors.getSource)(getState(), id);
 
 	    if (!source) {
-	      return;
-	    }
-
-	    source = source.toJS();
-
-	    // Make sure to start a request to load the source text.
-	    dispatch(loadSourceText(source));
-
-	    dispatch({ type: _constants2.default.TOGGLE_PROJECT_SEARCH, value: false });
-
-	    dispatch({
-	      type: _constants2.default.SELECT_SOURCE,
-	      source: source,
+	      // If there is no source we deselect the current selected source
+	      return dispatch({ type: "CLEAR_SELECTED_SOURCE" });
+	    }
+
+	    dispatch({ type: "TOGGLE_PROJECT_SEARCH", value: false });
+
+	    return dispatch({
+	      type: "SELECT_SOURCE",
+	      source: source.toJS(),
 	      tabIndex: options.tabIndex,
-	      line: options.line
+	      line: options.line,
+	      [_promise.PROMISE]: _asyncToGenerator(function* () {
+	        yield dispatch(loadSourceText(source.toJS()));
+	        yield dispatch((0, _ast.setOutOfScopeLocations)());
+	      })()
 	    });
 	  };
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function jumpToMappedLocation(sourceLocation) {
 	  return (() => {
-	    var _ref11 = _asyncToGenerator(function* (_ref12) {
-	      var dispatch = _ref12.dispatch,
-	          getState = _ref12.getState,
-	          client = _ref12.client,
-	          sourceMaps = _ref12.sourceMaps;
+	    var _ref12 = _asyncToGenerator(function* (_ref13) {
+	      var dispatch = _ref13.dispatch,
+	          getState = _ref13.getState,
+	          client = _ref13.client,
+	          sourceMaps = _ref13.sourceMaps;
 
 	      if (!client) {
 	        return;
 	      }
 
 	      var source = (0, _selectors.getSource)(getState(), sourceLocation.sourceId);
 	      var pairedLocation = void 0;
 	      if (sourceMaps.isOriginalId(sourceLocation.sourceId)) {
@@ -16831,98 +17001,112 @@ return /******/ (function(modules) { // 
 	      } else {
 	        pairedLocation = yield sourceMaps.getOriginalLocation(sourceLocation, source.toJS());
 	      }
 
 	      return dispatch(selectSource(pairedLocation.sourceId, { line: pairedLocation.line }));
 	    });
 
 	    return function (_x13) {
-	      return _ref11.apply(this, arguments);
+	      return _ref12.apply(this, arguments);
 	    };
 	  })();
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function closeTab(url) {
-	  (0, _editor.removeDocument)(url);
-	  return { type: _constants2.default.CLOSE_TAB, url };
+	  return (_ref14) => {
+	    var dispatch = _ref14.dispatch,
+	        getState = _ref14.getState,
+	        client = _ref14.client;
+
+	    (0, _editor.removeDocument)(url);
+	    var tabs = (0, _selectors.removeSourceFromTabList)((0, _selectors.getSourceTabs)(getState()), url);
+	    var sourceId = (0, _selectors.getNewSelectedSourceId)(getState(), tabs);
+
+	    dispatch({ type: "CLOSE_TAB", url, tabs });
+	    dispatch(selectSource(sourceId));
+	  };
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function closeTabs(urls) {
-	  return (_ref13) => {
-	    var dispatch = _ref13.dispatch,
-	        getState = _ref13.getState,
-	        client = _ref13.client;
+	  return (_ref15) => {
+	    var dispatch = _ref15.dispatch,
+	        getState = _ref15.getState,
+	        client = _ref15.client;
 
 	    urls.forEach(url => {
 	      var source = (0, _selectors.getSourceByURL)(getState(), url);
 	      if (source) {
 	        (0, _editor.removeDocument)(source.get("id"));
 	      }
 	    });
 
-	    dispatch({ type: _constants2.default.CLOSE_TABS, urls });
+	    var tabs = (0, _selectors.removeSourcesFromTabList)((0, _selectors.getSourceTabs)(getState()), urls);
+	    var sourceId = (0, _selectors.getNewSelectedSourceId)(getState(), tabs);
+
+	    dispatch({ type: "CLOSE_TABS", urls, tabs });
+	    dispatch(selectSource(sourceId));
 	  };
 	}
 
 	/**
 	 * Toggle the pretty printing of a source's text. All subsequent calls to
 	 * |getText| will return the pretty-toggled text. Nothing will happen for
 	 * non-javascript files.
 	 *
 	 * @memberof actions/sources
 	 * @static
 	 * @param string id The source form from the RDP.
 	 * @returns Promise
 	 *          A promise that resolves to [aSource, prettyText] or rejects to
 	 *          [aSource, error].
 	 */
 	function togglePrettyPrint(sourceId) {
-	  return (_ref14) => {
-	    var dispatch = _ref14.dispatch,
-	        getState = _ref14.getState,
-	        client = _ref14.client,
-	        sourceMaps = _ref14.sourceMaps;
+	  return (_ref16) => {
+	    var dispatch = _ref16.dispatch,
+	        getState = _ref16.getState,
+	        client = _ref16.client,
+	        sourceMaps = _ref16.sourceMaps;
 
 	    var source = (0, _selectors.getSource)(getState(), sourceId).toJS();
 	    var sourceText = (0, _selectors.getSourceText)(getState(), sourceId);
 	    if (sourceText) {
 	      sourceText = sourceText.toJS();
 	    }
 
 	    if (sourceText && sourceText.loading) {
 	      return {};
 	    }
 
 	    (0, _assert2.default)(sourceMaps.isGeneratedId(sourceId), "Pretty-printing only allowed on generated sources");
 
 	    var url = (0, _source.getPrettySourceURL)(source.url);
 	    var id = sourceMaps.generatedToOriginalId(source.id, url);
 	    var originalSource = { url, id, isPrettyPrinted: false };
-	    dispatch({ type: _constants2.default.ADD_SOURCE, source: originalSource });
+	    dispatch({ type: "ADD_SOURCE", source: originalSource });
 
 	    return dispatch({
-	      type: _constants2.default.TOGGLE_PRETTY_PRINT,
+	      type: "TOGGLE_PRETTY_PRINT",
 	      source: originalSource,
 	      [_promise.PROMISE]: _asyncToGenerator(function* () {
-	        var _ref16 = yield (0, _prettyPrint.prettyPrint)({
+	        var _ref18 = yield (0, _prettyPrint.prettyPrint)({
 	          source,
 	          sourceText,
 	          url
 	        }),
-	            code = _ref16.code,
-	            mappings = _ref16.mappings;
+	            code = _ref18.code,
+	            mappings = _ref18.mappings;
 
 	        yield sourceMaps.applySourceMap(source.id, url, code, mappings);
 
 	        var frames = (0, _selectors.getFrames)(getState());
 	        if (frames) {
 	          frames = yield (0, _pause.updateFrameLocations)(frames, sourceMaps);
 	        }
 
@@ -16931,83 +17115,85 @@ return /******/ (function(modules) { // 
 	        return { text: code, contentType: "text/javascript", frames };
 	      })()
 	    });
 	  };
 	}
 
 	function toggleBlackBox(source) {
 	  return (() => {
-	    var _ref17 = _asyncToGenerator(function* (_ref18) {
-	      var dispatch = _ref18.dispatch,
-	          getState = _ref18.getState,
-	          client = _ref18.client,
-	          sourceMaps = _ref18.sourceMaps;
+	    var _ref19 = _asyncToGenerator(function* (_ref20) {
+	      var dispatch = _ref20.dispatch,
+	          getState = _ref20.getState,
+	          client = _ref20.client,
+	          sourceMaps = _ref20.sourceMaps;
 	      var isBlackBoxed = source.isBlackBoxed,
 	          id = source.id;
 
 
 	      return dispatch({
-	        type: _constants2.default.BLACKBOX,
+	        type: "BLACKBOX",
 	        source,
 	        [_promise.PROMISE]: client.blackBox(id, isBlackBoxed)
 	      });
 	    });
 
 	    return function (_x14) {
-	      return _ref17.apply(this, arguments);
+	      return _ref19.apply(this, arguments);
 	    };
 	  })();
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function loadSourceText(source) {
-	  return (_ref19) => {
-	    var dispatch = _ref19.dispatch,
-	        getState = _ref19.getState,
-	        client = _ref19.client,
-	        sourceMaps = _ref19.sourceMaps;
-
-	    // Fetch the source text only once.
-	    var textInfo = (0, _selectors.getSourceText)(getState(), source.id);
-	    if (textInfo) {
-	      // It's already loaded or is loading
-	      return Promise.resolve(textInfo);
-	    }
-
-	    return dispatch({
-	      type: _constants2.default.LOAD_SOURCE_TEXT,
-	      source: source,
-	      [_promise.PROMISE]: _asyncToGenerator(function* () {
-	        if (sourceMaps.isOriginalId(source.id)) {
-	          return yield sourceMaps.getOriginalSourceText(source);
-	        }
-
-	        var response = yield client.sourceContents(source.id);
-
-	        var sourceText = {
-	          id: source.id,
-	          text: response.source,
-	          contentType: response.contentType || "text/javascript"
-	        };
-
-	        return sourceText;
-	        // Automatically pretty print if enabled and the test is
-	        // detected to be "minified"
-	        // if (Prefs.autoPrettyPrint &&
-	        //     !source.isPrettyPrinted &&
-	        //     SourceUtils.isMinified(source.id, response.source)) {
-	        //   dispatch(togglePrettyPrint(source));
-	        // }
-	      })()
-	    });
-	  };
+	  return (() => {
+	    var _ref21 = _asyncToGenerator(function* (_ref22) {
+	      var dispatch = _ref22.dispatch,
+	          getState = _ref22.getState,
+	          client = _ref22.client,
+	          sourceMaps = _ref22.sourceMaps;
+
+	      // Fetch the source text only once.
+	      var textInfo = (0, _selectors.getSourceText)(getState(), source.id);
+	      if (textInfo) {
+	        // It's already loaded or is loading
+	        return Promise.resolve(textInfo);
+	      }
+
+	      yield dispatch({
+	        type: "LOAD_SOURCE_TEXT",
+	        source: source,
+	        [_promise.PROMISE]: _asyncToGenerator(function* () {
+	          if (sourceMaps.isOriginalId(source.id)) {
+	            return yield sourceMaps.getOriginalSourceText(source);
+	          }
+
+	          var response = yield client.sourceContents(source.id);
+
+	          var sourceText = {
+	            id: source.id,
+	            text: response.source,
+	            contentType: response.contentType || "text/javascript"
+	          };
+
+	          return sourceText;
+	        })()
+	      });
+
+	      // get the symbols for the source as well
+	      return dispatch((0, _ast.setSymbols)(source));
+	    });
+
+	    return function (_x15) {
+	      return _ref21.apply(this, arguments);
+	    };
+	  })();
 	}
 
 	// delay is in ms
 	var FETCH_SOURCE_RESPONSE_DELAY = 200;
 
 	/**
 	 * Starts fetching all the sources, silently.
 	 *
@@ -17015,36 +17201,36 @@ return /******/ (function(modules) { // 
 	 * @static
 	 * @param array actors
 	 *        The urls for the sources to fetch. If fetching a source's text
 	 *        takes too long, it will be discarded.
 	 * @returns {Promise}
 	 *         A promise that is resolved after source texts have been fetched.
 	 */
 	function getTextForSources(actors) {
-	  return (_ref21) => {
-	    var dispatch = _ref21.dispatch,
-	        getState = _ref21.getState;
+	  return (_ref24) => {
+	    var dispatch = _ref24.dispatch,
+	        getState = _ref24.getState;
 
 	    var deferred = (0, _defer2.default)();
 	    var pending = new Set(actors);
 
 	    var fetched = [];
 
 	    // Can't use promise.all, because if one fetch operation is rejected, then
 	    // everything is considered rejected, thus no other subsequent source will
 	    // be getting fetched. We don't want that. Something like Q's allSettled
 	    // would work like a charm here.
 	    // Try to fetch as many sources as possible.
 
 	    var _loop = function (actor) {
 	      var source = (0, _selectors.getSource)(getState(), actor);
-	      dispatch(loadSourceText(source)).then((_ref30) => {
-	        var text = _ref30.text,
-	            contentType = _ref30.contentType;
+	      dispatch(loadSourceText(source)).then((_ref33) => {
+	        var text = _ref33.text,
+	            contentType = _ref33.contentType;
 
 	        onFetch([source, text, contentType]);
 	      }, err => {
 	        onError(source, err);
 	      });
 	    };
 
 	    for (var actor of actors) {
@@ -17055,54 +17241,54 @@ return /******/ (function(modules) { // 
 
 	    /* Called if fetching a source takes too long. */
 	    function onTimeout() {
 	      pending = new Set();
 	      maybeFinish();
 	    }
 
 	    /* Called if fetching a source finishes successfully. */
-	    function onFetch(_ref22) {
-	      var _ref23 = _slicedToArray(_ref22, 3),
-	          aSource = _ref23[0],
-	          aText = _ref23[1],
-	          aContentType = _ref23[2];
+	    function onFetch(_ref25) {
+	      var _ref26 = _slicedToArray(_ref25, 3),
+	          aSource = _ref26[0],
+	          aText = _ref26[1],
+	          aContentType = _ref26[2];
 
 	      // If fetching the source has previously timed out, discard it this time.
 	      if (!pending.has(aSource.actor)) {
 	        return;
 	      }
 	      pending.delete(aSource.actor);
 	      fetched.push([aSource.actor, aText, aContentType]);
 	      maybeFinish();
 	    }
 
 	    /* Called if fetching a source failed because of an error. */
-	    function onError(_ref24) {
-	      var _ref25 = _slicedToArray(_ref24, 2),
-	          aSource = _ref25[0],
-	          aError = _ref25[1];
+	    function onError(_ref27) {
+	      var _ref28 = _slicedToArray(_ref27, 2),
+	          aSource = _ref28[0],
+	          aError = _ref28[1];
 
 	      pending.delete(aSource.actor);
 	      maybeFinish();
 	    }
 
 	    /* Called every time something interesting
 	     *  happens while fetching sources.
 	     */
 	    function maybeFinish() {
 	      if (pending.size == 0) {
 	        // Sort the fetched sources alphabetically by their url.
 	        if (deferred) {
-	          deferred.resolve(fetched.sort((_ref26, _ref27) => {
-	            var _ref29 = _slicedToArray(_ref26, 1),
-	                aFirst = _ref29[0];
-
-	            var _ref28 = _slicedToArray(_ref27, 1),
-	                aSecond = _ref28[0];
+	          deferred.resolve(fetched.sort((_ref29, _ref30) => {
+	            var _ref32 = _slicedToArray(_ref29, 1),
+	                aFirst = _ref32[0];
+
+	            var _ref31 = _slicedToArray(_ref30, 1),
+	                aSecond = _ref31[0];
 
 	            return aFirst > aSecond ? -1 : 1;
 	          }));
 	        }
 	      }
 	    }
 
 	    return deferred.promise;
@@ -17270,16 +17456,17 @@ return /******/ (function(modules) { // 
 
 	  return new _devtoolsSourceEditor.SourceEditor({
 	    mode: "javascript",
 	    foldGutter: (0, _devtoolsConfig.isEnabled)("codeFolding"),
 	    enableCodeFolding: (0, _devtoolsConfig.isEnabled)("codeFolding"),
 	    readOnly: true,
 	    lineNumbers: true,
 	    theme: "mozilla",
+	    styleActiveLine: false,
 	    lineWrapping: false,
 	    matchBrackets: true,
 	    showAnnotationRuler: true,
 	    gutters,
 	    value: " ",
 	    extraKeys: {
 	      // Override code mirror keymap to avoid conflicts with split console.
 	      Esc: false,
@@ -19442,32 +19629,26 @@ return /******/ (function(modules) { // 
 	exports.stepIn = stepIn;
 	exports.stepOver = stepOver;
 	exports.stepOut = stepOut;
 	exports.resume = resume;
 	exports.breakOnNext = breakOnNext;
 	exports.selectFrame = selectFrame;
 	exports.loadObjectProperties = loadObjectProperties;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _sources = __webpack_require__(254);
 
 	var _promise = __webpack_require__(193);
 
 	var _selectors = __webpack_require__(242);
 
 	var _pause = __webpack_require__(255);
 
 	var _expressions = __webpack_require__(252);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
 	/**
 	 * Redux actions for the pause state
 	 * @module actions/pause
 	 */
 
 	/**
@@ -19479,17 +19660,17 @@ return /******/ (function(modules) { // 
 	function resumed() {
 	  return (_ref) => {
 	    var dispatch = _ref.dispatch,
 	        client = _ref.client;
 
 	    // dispatch(evaluateExpressions(null));
 
 	    return dispatch({
-	      type: _constants2.default.RESUME,
+	      type: "RESUME",
 	      value: undefined
 	    });
 	  };
 	}
 
 	/**
 	 * Debugger has just paused
 	 *
@@ -19507,17 +19688,17 @@ return /******/ (function(modules) { // 
 	      var frames = pauseInfo.frames,
 	          why = pauseInfo.why,
 	          loadedObjects = pauseInfo.loadedObjects;
 
 	      frames = yield (0, _pause.updateFrameLocations)(frames, sourceMaps);
 	      var frame = frames[0];
 
 	      dispatch({
-	        type: _constants2.default.PAUSED,
+	        type: "PAUSED",
 	        pauseInfo: { why, frame, frames },
 	        frames: frames,
 	        selectedFrameId: frame.id,
 	        loadedObjects: loadedObjects || []
 	      });
 
 	      dispatch((0, _expressions.evaluateExpressions)(frame.id));
 
@@ -19536,17 +19717,17 @@ return /******/ (function(modules) { // 
 	 * @static
 	 */
 	function pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
 	  return (_ref4) => {
 	    var dispatch = _ref4.dispatch,
 	        client = _ref4.client;
 
 	    dispatch({
-	      type: _constants2.default.PAUSE_ON_EXCEPTIONS,
+	      type: "PAUSE_ON_EXCEPTIONS",
 	      shouldPauseOnExceptions,
 	      shouldIgnoreCaughtExceptions,
 	      [_promise.PROMISE]: client.pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions)
 	    });
 	  };
 	}
 
 	/**
@@ -19562,17 +19743,17 @@ return /******/ (function(modules) { // 
 	  return (_ref6) => {
 	    var dispatch = _ref6.dispatch,
 	        client = _ref6.client;
 
 	    // execute debugger thread command e.g. stepIn, stepOver
 	    client[type]();
 
 	    return dispatch({
-	      type: _constants2.default.COMMAND,
+	      type: "COMMAND",
 	      value: undefined
 	    });
 	  };
 	}
 
 	/**
 	 * StepIn
 	 * @memberof actions/pause
@@ -19652,34 +19833,34 @@ return /******/ (function(modules) { // 
 	function breakOnNext() {
 	  return (_ref11) => {
 	    var dispatch = _ref11.dispatch,
 	        client = _ref11.client;
 
 	    client.breakOnNext();
 
 	    return dispatch({
-	      type: _constants2.default.BREAK_ON_NEXT,
+	      type: "BREAK_ON_NEXT",
 	      value: true
 	    });
 	  };
 	}
 
 	/**
 	 * @memberof actions/pause
 	 * @static
 	 */
 	function selectFrame(frame) {
 	  return (_ref12) => {
 	    var dispatch = _ref12.dispatch;
 
 	    dispatch((0, _expressions.evaluateExpressions)(frame.id));
 	    dispatch((0, _sources.selectSource)(frame.location.sourceId, { line: frame.location.line }));
 	    dispatch({
-	      type: _constants2.default.SELECT_FRAME,
+	      type: "SELECT_FRAME",
 	      frame
 	    });
 	  };
 	}
 
 	/**
 	 * @memberof actions/pause
 	 * @static
@@ -19692,17 +19873,17 @@ return /******/ (function(modules) { // 
 
 	    var objectId = object.actor || object.objectId;
 
 	    if ((0, _selectors.getLoadedObject)(getState(), objectId)) {
 	      return;
 	    }
 
 	    dispatch({
-	      type: _constants2.default.LOAD_OBJECT_PROPERTIES,
+	      type: "LOAD_OBJECT_PROPERTIES",
 	      objectId,
 	      [_promise.PROMISE]: client.getProperties(object)
 	    });
 	  };
 	}
 
 /***/ },
 /* 320 */
@@ -19711,30 +19892,24 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.willNavigate = willNavigate;
 	exports.navigated = navigated;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _editor = __webpack_require__(257);
 
 	var _sources = __webpack_require__(232);
 
 	var _utils = __webpack_require__(234);
 
 	var _sources2 = __webpack_require__(254);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
 	/**
 	 * Redux actions for the navigation state
 	 * @module actions/navigation
 	 */
 
 	/**
@@ -19748,17 +19923,17 @@ return /******/ (function(modules) { // 
 	          getState = _ref2.getState,
 	          client = _ref2.client,
 	          sourceMaps = _ref2.sourceMaps;
 
 	      yield sourceMaps.clearSourceMaps();
 	      (0, _editor.clearDocuments)();
 
 	      dispatch({
-	        type: _constants2.default.NAVIGATE,
+	        type: "NAVIGATE",
 	        url: event.url
 	      });
 	    });
 
 	    return function (_x) {
 	      return _ref.apply(this, arguments);
 	    };
 	  })();
@@ -19804,187 +19979,175 @@ return /******/ (function(modules) { // 
 	exports.setSelectedSymbolType = setSelectedSymbolType;
 	exports.setFileSearchQuery = setFileSearchQuery;
 	exports.toggleFileSearchModifier = toggleFileSearchModifier;
 	exports.showSource = showSource;
 	exports.togglePaneCollapse = togglePaneCollapse;
 	exports.highlightLineRange = highlightLineRange;
 	exports.clearHighlightLineRange = clearHighlightLineRange;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
 	var _selectors = __webpack_require__(242);
 
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function toggleProjectSearch(toggleValue) {
 	  return (_ref) => {
 	    var dispatch = _ref.dispatch,
 	        getState = _ref.getState;
 
 	    var projectSearchState = (0, _selectors.getProjectSearchState)(getState());
 	    if (toggleValue === undefined) {
 	      return dispatch({
-	        type: _constants2.default.TOGGLE_PROJECT_SEARCH,
+	        type: "TOGGLE_PROJECT_SEARCH",
 	        value: !projectSearchState
 	      });
 	    }
 
 	    if (projectSearchState == toggleValue) {
 	      return;
 	    }
 
 	    dispatch({
-	      type: _constants2.default.TOGGLE_PROJECT_SEARCH,
+	      type: "TOGGLE_PROJECT_SEARCH",
 	      value: toggleValue
 	    });
 	  };
 	}
 
 	function toggleFileSearch(toggleValue) {
 	  return (_ref2) => {
 	    var dispatch = _ref2.dispatch,
 	        getState = _ref2.getState;
 
 	    if (toggleValue != null) {
 	      dispatch({
-	        type: _constants2.default.TOGGLE_FILE_SEARCH,
+	        type: "TOGGLE_FILE_SEARCH",
 	        value: toggleValue
 	      });
 	    } else {
 	      dispatch({
-	        type: _constants2.default.TOGGLE_FILE_SEARCH,
+	        type: "TOGGLE_FILE_SEARCH",
 	        value: !(0, _selectors.getFileSearchState)(getState())
 	      });
 	    }
 	  };
 	}
 
 	function toggleSymbolSearch(toggleValue) {
 	  return (_ref3) => {
 	    var dispatch = _ref3.dispatch,
 	        getState = _ref3.getState;
 
 	    dispatch({
-	      type: _constants2.default.TOGGLE_SYMBOL_SEARCH,
+	      type: "TOGGLE_SYMBOL_SEARCH",
 	      value: toggleValue
 	    });
 	  };
 	}
 
 	function toggleFrameworkGrouping(toggleValue) {
 	  return (_ref4) => {
 	    var dispatch = _ref4.dispatch,
 	        getState = _ref4.getState;
 
 	    dispatch({
-	      type: _constants2.default.TOGGLE_FRAMEWORK_GROUPING,
+	      type: "TOGGLE_FRAMEWORK_GROUPING",
 	      value: toggleValue
 	    });
 	  };
 	}
 
 	function setSelectedSymbolType(symbolType) {
 	  return (_ref5) => {
 	    var dispatch = _ref5.dispatch,
 	        getState = _ref5.getState;
 
 	    dispatch({
-	      type: _constants2.default.SET_SYMBOL_SEARCH_TYPE,
+	      type: "SET_SYMBOL_SEARCH_TYPE",
 	      symbolType
 	    });
 	  };
 	}
 
 	function setFileSearchQuery(query) {
 	  return {
-	    type: _constants2.default.UPDATE_FILE_SEARCH_QUERY,
+	    type: "UPDATE_FILE_SEARCH_QUERY",
 	    query
 	  };
 	}
 
 	function toggleFileSearchModifier(modifier) {
-	  return { type: _constants2.default.TOGGLE_FILE_SEARCH_MODIFIER, modifier };
+	  return { type: "TOGGLE_FILE_SEARCH_MODIFIER", modifier };
 	}
 
 	function showSource(sourceId) {
 	  return (_ref6) => {
 	    var dispatch = _ref6.dispatch,
 	        getState = _ref6.getState;
 
 	    var source = (0, _selectors.getSource)(getState(), sourceId);
 	    dispatch({
-	      type: _constants2.default.SHOW_SOURCE,
+	      type: "SHOW_SOURCE",
 	      sourceUrl: source.get("url")
 	    });
 	  };
 	}
 
 	function togglePaneCollapse(position, paneCollapsed) {
 	  return {
-	    type: _constants2.default.TOGGLE_PANE,
+	    type: "TOGGLE_PANE",
 	    position,
 	    paneCollapsed
 	  };
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function highlightLineRange(location) {
 	  return {
-	    type: _constants2.default.HIGHLIGHT_LINES,
+	    type: "HIGHLIGHT_LINES",
 	    location
 	  };
 	}
 
 	/**
 	 * @memberof actions/sources
 	 * @static
 	 */
 	function clearHighlightLineRange() {
 	  return {
-	    type: _constants2.default.CLEAR_HIGHLIGHT_LINES
+	    type: "CLEAR_HIGHLIGHT_LINES"
 	  };
 	}
 
 /***/ },
 /* 322 */
-/***/ function(module, exports, __webpack_require__) {
+/***/ function(module, exports) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.recordCoverage = recordCoverage;
 
-	var _constants = __webpack_require__(229);
-
-	var _constants2 = _interopRequireDefault(_constants);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
 	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
 	function recordCoverage() {
 	  return (() => {
 	    var _ref = _asyncToGenerator(function* (_ref2) {
 	      var dispatch = _ref2.dispatch,
 	          getState = _ref2.getState,
 	          client = _ref2.client;
 
 	      var _ref3 = yield client.recordCoverage(),
 	          coverage = _ref3.coverage;
 
 	      return dispatch({
-	        type: _constants2.default.RECORD_COVERAGE,
+	        type: "RECORD_COVERAGE",
 	        value: { coverage }
 	      });
 	    });
 
 	    return function (_x) {
 	      return _ref.apply(this, arguments);
 	    };
 	  })();
@@ -20043,17 +20206,17 @@ return /******/ (function(modules) { // 
 	// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 	// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 	// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 	// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 	// USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 	'use strict';
 
-	var punycode = __webpack_require__(916);
+	var punycode = __webpack_require__(335);
 	var util = __webpack_require__(336);
 
 	exports.parse = urlParse;
 	exports.resolve = urlResolve;
 	exports.resolveObject = urlResolveObject;
 	exports.format = urlFormat;
 
 	exports.Url = Url;
@@ -20755,17 +20918,551 @@ return /******/ (function(modules) { // 
 	    }
 	    host = host.substr(0, host.length - port.length);
 	  }
 	  if (host) this.hostname = host;
 	};
 
 
 /***/ },
-/* 335 */,
+/* 335 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/*! https://mths.be/punycode v1.3.2 by @mathias */
+	;(function(root) {
+
+		/** Detect free variables */
+		var freeExports = typeof exports == 'object' && exports &&
+			!exports.nodeType && exports;
+		var freeModule = typeof module == 'object' && module &&
+			!module.nodeType && module;
+		var freeGlobal = typeof global == 'object' && global;
+		if (
+			freeGlobal.global === freeGlobal ||
+			freeGlobal.window === freeGlobal ||
+			freeGlobal.self === freeGlobal
+		) {
+			root = freeGlobal;
+		}
+
+		/**
+		 * The `punycode` object.
+		 * @name punycode
+		 * @type Object
+		 */
+		var punycode,
+
+		/** Highest positive signed 32-bit float value */
+		maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+		/** Bootstring parameters */
+		base = 36,
+		tMin = 1,
+		tMax = 26,
+		skew = 38,
+		damp = 700,
+		initialBias = 72,
+		initialN = 128, // 0x80
+		delimiter = '-', // '\x2D'
+
+		/** Regular expressions */
+		regexPunycode = /^xn--/,
+		regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+		regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+		/** Error messages */
+		errors = {
+			'overflow': 'Overflow: input needs wider integers to process',
+			'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+			'invalid-input': 'Invalid input'
+		},
+
+		/** Convenience shortcuts */
+		baseMinusTMin = base - tMin,
+		floor = Math.floor,
+		stringFromCharCode = String.fromCharCode,
+
+		/** Temporary variable */
+		key;
+
+		/*--------------------------------------------------------------------------*/
+
+		/**
+		 * A generic error utility function.
+		 * @private
+		 * @param {String} type The error type.
+		 * @returns {Error} Throws a `RangeError` with the applicable error message.
+		 */
+		function error(type) {
+			throw RangeError(errors[type]);
+		}
+
+		/**
+		 * A generic `Array#map` utility function.
+		 * @private
+		 * @param {Array} array The array to iterate over.
+		 * @param {Function} callback The function that gets called for every array
+		 * item.
+		 * @returns {Array} A new array of values returned by the callback function.
+		 */
+		function map(array, fn) {
+			var length = array.length;
+			var result = [];
+			while (length--) {
+				result[length] = fn(array[length]);
+			}
+			return result;
+		}
+
+		/**
+		 * A simple `Array#map`-like wrapper to work with domain name strings or email
+		 * addresses.
+		 * @private
+		 * @param {String} domain The domain name or email address.
+		 * @param {Function} callback The function that gets called for every
+		 * character.
+		 * @returns {Array} A new string of characters returned by the callback
+		 * function.
+		 */
+		function mapDomain(string, fn) {
+			var parts = string.split('@');
+			var result = '';
+			if (parts.length > 1) {
+				// In email addresses, only the domain name should be punycoded. Leave
+				// the local part (i.e. everything up to `@`) intact.
+				result = parts[0] + '@';
+				string = parts[1];
+			}
+			// Avoid `split(regex)` for IE8 compatibility. See #17.
+			string = string.replace(regexSeparators, '\x2E');
+			var labels = string.split('.');
+			var encoded = map(labels, fn).join('.');
+			return result + encoded;
+		}
+
+		/**
+		 * Creates an array containing the numeric code points of each Unicode
+		 * character in the string. While JavaScript uses UCS-2 internally,
+		 * this function will convert a pair of surrogate halves (each of which
+		 * UCS-2 exposes as separate characters) into a single code point,
+		 * matching UTF-16.
+		 * @see `punycode.ucs2.encode`
+		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
+		 * @memberOf punycode.ucs2
+		 * @name decode
+		 * @param {String} string The Unicode input string (UCS-2).
+		 * @returns {Array} The new array of code points.
+		 */
+		function ucs2decode(string) {
+			var output = [],
+			    counter = 0,
+			    length = string.length,
+			    value,
+			    extra;
+			while (counter < length) {
+				value = string.charCodeAt(counter++);
+				if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+					// high surrogate, and there is a next character
+					extra = string.charCodeAt(counter++);
+					if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+						output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+					} else {
+						// unmatched surrogate; only append this code unit, in case the next
+						// code unit is the high surrogate of a surrogate pair
+						output.push(value);
+						counter--;
+					}
+				} else {
+					output.push(value);
+				}
+			}
+			return output;
+		}
+
+		/**
+		 * Creates a string based on an array of numeric code points.
+		 * @see `punycode.ucs2.decode`
+		 * @memberOf punycode.ucs2
+		 * @name encode
+		 * @param {Array} codePoints The array of numeric code points.
+		 * @returns {String} The new Unicode string (UCS-2).
+		 */
+		function ucs2encode(array) {
+			return map(array, function(value) {
+				var output = '';
+				if (value > 0xFFFF) {
+					value -= 0x10000;
+					output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+					value = 0xDC00 | value & 0x3FF;
+				}
+				output += stringFromCharCode(value);
+				return output;
+			}).join('');
+		}
+
+		/**
+		 * Converts a basic code point into a digit/integer.
+		 * @see `digitToBasic()`
+		 * @private
+		 * @param {Number} codePoint The basic numeric code point value.
+		 * @returns {Number} The numeric value of a basic code point (for use in
+		 * representing integers) in the range `0` to `base - 1`, or `base` if
+		 * the code point does not represent a value.
+		 */
+		function basicToDigit(codePoint) {
+			if (codePoint - 48 < 10) {
+				return codePoint - 22;
+			}
+			if (codePoint - 65 < 26) {
+				return codePoint - 65;
+			}
+			if (codePoint - 97 < 26) {
+				return codePoint - 97;
+			}
+			return base;
+		}
+
+		/**
+		 * Converts a digit/integer into a basic code point.
+		 * @see `basicToDigit()`
+		 * @private
+		 * @param {Number} digit The numeric value of a basic code point.
+		 * @returns {Number} The basic code point whose value (when used for
+		 * representing integers) is `digit`, which needs to be in the range
+		 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+		 * used; else, the lowercase form is used. The behavior is undefined
+		 * if `flag` is non-zero and `digit` has no uppercase form.
+		 */
+		function digitToBasic(digit, flag) {
+			//  0..25 map to ASCII a..z or A..Z
+			// 26..35 map to ASCII 0..9
+			return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+		}
+
+		/**
+		 * Bias adaptation function as per section 3.4 of RFC 3492.
+		 * http://tools.ietf.org/html/rfc3492#section-3.4
+		 * @private
+		 */
+		function adapt(delta, numPoints, firstTime) {
+			var k = 0;
+			delta = firstTime ? floor(delta / damp) : delta >> 1;
+			delta += floor(delta / numPoints);
+			for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+				delta = floor(delta / baseMinusTMin);
+			}
+			return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+		}
+
+		/**
+		 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+		 * symbols.
+		 * @memberOf punycode
+		 * @param {String} input The Punycode string of ASCII-only symbols.
+		 * @returns {String} The resulting string of Unicode symbols.
+		 */
+		function decode(input) {
+			// Don't use UCS-2
+			var output = [],
+			    inputLength = input.length,
+			    out,
+			    i = 0,
+			    n = initialN,
+			    bias = initialBias,
+			    basic,
+			    j,
+			    index,
+			    oldi,
+			    w,
+			    k,
+			    digit,
+			    t,
+			    /** Cached calculation results */
+			    baseMinusT;
+
+			// Handle the basic code points: let `basic` be the number of input code
+			// points before the last delimiter, or `0` if there is none, then copy
+			// the first basic code points to the output.
+
+			basic = input.lastIndexOf(delimiter);
+			if (basic < 0) {
+				basic = 0;
+			}
+
+			for (j = 0; j < basic; ++j) {
+				// if it's not a basic code point
+				if (input.charCodeAt(j) >= 0x80) {
+					error('not-basic');
+				}
+				output.push(input.charCodeAt(j));
+			}
+
+			// Main decoding loop: start just after the last delimiter if any basic code
+			// points were copied; start at the beginning otherwise.
+
+			for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+				// `index` is the index of the next character to be consumed.
+				// Decode a generalized variable-length integer into `delta`,
+				// which gets added to `i`. The overflow checking is easier
+				// if we increase `i` as we go, then subtract off its starting
+				// value at the end to obtain `delta`.
+				for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+					if (index >= inputLength) {
+						error('invalid-input');
+					}
+
+					digit = basicToDigit(input.charCodeAt(index++));
+
+					if (digit >= base || digit > floor((maxInt - i) / w)) {
+						error('overflow');
+					}
+
+					i += digit * w;
+					t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+					if (digit < t) {
+						break;
+					}
+
+					baseMinusT = base - t;
+					if (w > floor(maxInt / baseMinusT)) {
+						error('overflow');
+					}
+
+					w *= baseMinusT;
+
+				}
+
+				out = output.length + 1;
+				bias = adapt(i - oldi, out, oldi == 0);
+
+				// `i` was supposed to wrap around from `out` to `0`,
+				// incrementing `n` each time, so we'll fix that now:
+				if (floor(i / out) > maxInt - n) {
+					error('overflow');
+				}
+
+				n += floor(i / out);
+				i %= out;
+
+				// Insert `n` at position `i` of the output
+				output.splice(i++, 0, n);
+
+			}
+
+			return ucs2encode(output);
+		}
+
+		/**
+		 * Converts a string of Unicode symbols (e.g. a domain name label) to a
+		 * Punycode string of ASCII-only symbols.
+		 * @memberOf punycode
+		 * @param {String} input The string of Unicode symbols.
+		 * @returns {String} The resulting Punycode string of ASCII-only symbols.
+		 */
+		function encode(input) {
+			var n,
+			    delta,
+			    handledCPCount,
+			    basicLength,
+			    bias,
+			    j,
+			    m,
+			    q,
+			    k,
+			    t,
+			    currentValue,
+			    output = [],
+			    /** `inputLength` will hold the number of code points in `input`. */
+			    inputLength,
+			    /** Cached calculation results */
+			    handledCPCountPlusOne,
+			    baseMinusT,
+			    qMinusT;
+
+			// Convert the input in UCS-2 to Unicode
+			input = ucs2decode(input);
+
+			// Cache the length
+			inputLength = input.length;
+
+			// Initialize the state
+			n = initialN;
+			delta = 0;
+			bias = initialBias;
+
+			// Handle the basic code points
+			for (j = 0; j < inputLength; ++j) {
+				currentValue = input[j];
+				if (currentValue < 0x80) {
+					output.push(stringFromCharCode(currentValue));
+				}
+			}
+
+			handledCPCount = basicLength = output.length;
+
+			// `handledCPCount` is the number of code points that have been handled;
+			// `basicLength` is the number of basic code points.
+
+			// Finish the basic string - if it is not empty - with a delimiter
+			if (basicLength) {
+				output.push(delimiter);
+			}
+
+			// Main encoding loop:
+			while (handledCPCount < inputLength) {
+
+				// All non-basic code points < n have been handled already. Find the next
+				// larger one:
+				for (m = maxInt, j = 0; j < inputLength; ++j) {
+					currentValue = input[j];
+					if (currentValue >= n && currentValue < m) {
+						m = currentValue;
+					}
+				}
+
+				// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+				// but guard against overflow
+				handledCPCountPlusOne = handledCPCount + 1;
+				if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+					error('overflow');
+				}
+
+				delta += (m - n) * handledCPCountPlusOne;
+				n = m;
+
+				for (j = 0; j < inputLength; ++j) {
+					currentValue = input[j];
+
+					if (currentValue < n && ++delta > maxInt) {
+						error('overflow');
+					}
+
+					if (currentValue == n) {
+						// Represent delta as a generalized variable-length integer
+						for (q = delta, k = base; /* no condition */; k += base) {
+							t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+							if (q < t) {
+								break;
+							}
+							qMinusT = q - t;
+							baseMinusT = base - t;
+							output.push(
+								stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+							);
+							q = floor(qMinusT / baseMinusT);
+						}
+
+						output.push(stringFromCharCode(digitToBasic(q, 0)));
+						bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+						delta = 0;
+						++handledCPCount;
+					}
+				}
+
+				++delta;
+				++n;
+
+			}
+			return output.join('');
+		}
+
+		/**
+		 * Converts a Punycode string representing a domain name or an email address
+		 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+		 * it doesn't matter if you call it on a string that has already been
+		 * converted to Unicode.
+		 * @memberOf punycode
+		 * @param {String} input The Punycoded domain name or email address to
+		 * convert to Unicode.
+		 * @returns {String} The Unicode representation of the given Punycode
+		 * string.
+		 */
+		function toUnicode(input) {
+			return mapDomain(input, function(string) {
+				return regexPunycode.test(string)
+					? decode(string.slice(4).toLowerCase())
+					: string;
+			});
+		}
+
+		/**
+		 * Converts a Unicode string representing a domain name or an email address to
+		 * Punycode. Only the non-ASCII parts of the domain name will be converted,
+		 * i.e. it doesn't matter if you call it with a domain that's already in
+		 * ASCII.
+		 * @memberOf punycode
+		 * @param {String} input The domain name or email address to convert, as a
+		 * Unicode string.
+		 * @returns {String} The Punycode representation of the given domain name or
+		 * email address.
+		 */
+		function toASCII(input) {
+			return mapDomain(input, function(string) {
+				return regexNonASCII.test(string)
+					? 'xn--' + encode(string)
+					: string;
+			});
+		}
+
+		/*--------------------------------------------------------------------------*/
+
+		/** Define the public API */
+		punycode = {
+			/**
+			 * A string representing the current Punycode.js version number.
+			 * @memberOf punycode
+			 * @type String
+			 */
+			'version': '1.3.2',
+			/**
+			 * An object of methods to convert from JavaScript's internal character
+			 * representation (UCS-2) to Unicode code points, and back.
+			 * @see <https://mathiasbynens.be/notes/javascript-encoding>
+			 * @memberOf punycode
+			 * @type Object
+			 */
+			'ucs2': {
+				'decode': ucs2decode,
+				'encode': ucs2encode
+			},
+			'decode': decode,
+			'encode': encode,
+			'toASCII': toASCII,
+			'toUnicode': toUnicode
+		};
+
+		/** Expose `punycode` */
+		// Some AMD build optimizers, like r.js, check for specific condition patterns
+		// like the following:
+		if (
+			true
+		) {
+			!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
+				return punycode;
+			}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+		} else if (freeExports && freeModule) {
+			if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
+				freeModule.exports = punycode;
+			} else { // in Narwhal or RingoJS v0.7.0-
+				for (key in punycode) {
+					punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+				}
+			}
+		} else { // in Rhino or a web browser
+			root.punycode = punycode;
+		}
+
+	}(this));
+
+	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(51)(module), (function() { return this; }())))
+
+/***/ },
 /* 336 */
 /***/ function(module, exports) {
 
 	'use strict';
 
 	module.exports = {
 	  isString: function(arg) {
 	    return typeof(arg) === 'string';
@@ -20957,17 +21654,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _fuzzaldrinPlus = __webpack_require__(161);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
@@ -21026,16 +21723,17 @@ return /******/ (function(modules) { // 
 	  }
 
 	  getSearchResults() {
 	    var inputValue = this.state.inputValue;
 
 	    if (inputValue == "") {
 	      return [];
 	    }
+
 	    return (0, _fuzzaldrinPlus.filter)(this.props.items, this.state.inputValue, {
 	      key: "value"
 	    });
 	  }
 
 	  onKeyDown(e) {
 	    var searchResults = this.getSearchResults(),
 	        resultCount = searchResults.length;
@@ -21105,31 +21803,20 @@ return /******/ (function(modules) { // 
 	      onBlur: () => this.setState({ focused: false }),
 	      onKeyDown: this.onKeyDown,
 	      handleClose: this.props.close
 	    }), this.renderResults(searchResults));
 	  }
 	}
 
 	exports.default = Autocomplete;
-	Autocomplete.propTypes = {
-	  selectItem: _react.PropTypes.func.isRequired,
-	  onSelectedItem: _react.PropTypes.func,
-	  items: _react.PropTypes.array,
-	  close: _react.PropTypes.func.isRequired,
-	  inputValue: _react.PropTypes.string.isRequired,
-	  placeholder: _react.PropTypes.string,
-	  size: _react.PropTypes.string
-	};
-
-	Autocomplete.displayName = "Autocomplete";
-
 	Autocomplete.defaultProps = {
 	  size: ""
 	};
+	Autocomplete.displayName = "Autocomplete";
 
 /***/ },
 /* 343 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
@@ -21660,22 +22347,16 @@ return /******/ (function(modules) { // 
 	    className,
 	    title: tooltip,
 	    key: type
 	  }, (0, _Svg2.default)(type));
 	};
 
 	class SearchInput extends _react.Component {
 
-	  static get defaultProps() {
-	    return {
-	      size: ""
-	    };
-	  }
-
 	  renderSvg() {
 	    var _props = this.props,
 	        count = _props.count,
 	        query = _props.query;
 
 
 	    if (count == 0 && query.trim() != "") {
 	      return (0, _Svg2.default)("sad-face");
@@ -21741,30 +22422,18 @@ return /******/ (function(modules) { // 
 	      spellCheck: false
 	    }), _react.DOM.div({ className: "summary" }, query != "" ? summaryMsg : ""), this.renderNav(), (0, _Close2.default)({
 	      handleClick: handleClose,
 	      buttonClass: size
 	    }));
 	  }
 	}
 
-	SearchInput.propTypes = {
-	  query: _react.PropTypes.string.isRequired,
-	  count: _react.PropTypes.number.isRequired,
-	  placeholder: _react.PropTypes.string.isRequired,
-	  summaryMsg: _react.PropTypes.string.isRequired,
-	  onChange: _react.PropTypes.func.isRequired,
-	  handleClose: _react.PropTypes.func.isRequired,
-	  onKeyUp: _react.PropTypes.func,
-	  onKeyDown: _react.PropTypes.func,
-	  onFocus: _react.PropTypes.func,
-	  onBlur: _react.PropTypes.func,
-	  size: _react.PropTypes.string,
-	  handleNext: _react.PropTypes.func,
-	  handlePrev: _react.PropTypes.func
+	SearchInput.defaultProps = {
+	  size: ""
 	};
 
 	exports.default = SearchInput;
 
 /***/ },
 /* 378 */
 /***/ function(module, exports, __webpack_require__) {
 
@@ -21840,45 +22509,44 @@ return /******/ (function(modules) { // 
 	class ResultList extends _react.Component {
 
 	  constructor(props) {
 	    super(props);
 	    this.renderListItem = this.renderListItem.bind(this);
 	  }
 
 	  renderListItem(item, index) {
+	    var _props = this.props,
+	        selectItem = _props.selectItem,
+	        selected = _props.selected;
+
 	    return _react.DOM.li({
-	      onClick: event => this.props.selectItem(event, item, index),
+	      onClick: event => selectItem(event, item, index),
 	      key: `${item.id}${item.value}${index}`,
 	      ref: index,
 	      title: item.value,
 	      className: (0, _classnames2.default)({
-	        selected: index === this.props.selected
+	        selected: index === selected
 	      })
 	    }, _react.DOM.div({ className: "title" }, item.title), _react.DOM.div({ className: "subtitle" }, item.subtitle));
 	  }
 
 	  render() {
-	    var size = this.props.size;
+	    var _props2 = this.props,
+	        size = _props2.size,
+	        items = _props2.items;
 
 	    size = size || "";
 	    return _react.DOM.ul({
 	      className: `result-list ${size}`
-	    }, this.props.items.map(this.renderListItem));
+	    }, items.map(this.renderListItem));
 	  }
 	}
 
 	exports.default = ResultList;
-	ResultList.propTypes = {
-	  items: _react.PropTypes.array.isRequired,
-	  selected: _react.PropTypes.number.isRequired,
-	  selectItem: _react.PropTypes.func.isRequired,
-	  size: _react.PropTypes.string
-	};
-
 	ResultList.defaultProps = {
 	  size: ""
 	};
 
 /***/ },
 /* 384 */
 /***/ function(module, exports) {
 
@@ -21913,26 +22581,26 @@ return /******/ (function(modules) { // 
 	var _reactRedux = __webpack_require__(151);
 
 	var _text = __webpack_require__(389);
 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
-	var _Svg = __webpack_require__(344);
-
-	var _Svg2 = _interopRequireDefault(_Svg);
-
 	var _selectors = __webpack_require__(242);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
 	__webpack_require__(424);
 
+	var _classnames = __webpack_require__(175);
+
+	var _classnames2 = _interopRequireDefault(_classnames);
+
 	var _Outline2 = __webpack_require__(921);
 
 	var _Outline3 = _interopRequireDefault(_Outline2);
 
 	var _SourcesTree2 = __webpack_require__(390);
 
 	var _SourcesTree3 = _interopRequireDefault(_SourcesTree2);
 
@@ -21944,52 +22612,48 @@ return /******/ (function(modules) { // 
 
 	class Sources extends _react.Component {
 
 	  constructor(props) {
 	    super(props);
 	    this.state = { selectedPane: "sources" };
 
 	    this.renderShortcut = this.renderShortcut.bind(this);
-	    this.togglePane = this.togglePane.bind(this);
+	    this.showPane = this.showPane.bind(this);
 	    this.renderFooter = this.renderFooter.bind(this);
 	  }
 
-	  togglePane() {
-	    var selectedPane = this.state.selectedPane === "sources" ? "outline" : "sources";
-
+	  showPane(selectedPane) {
 	    this.setState({ selectedPane });
 	  }
 
-	  renderOutlineToggleButton() {
+	  renderOutlineTabs() {
 	    if (!(0, _devtoolsConfig.isEnabled)("outline")) {
 	      return;
 	    }
 
-	    var selectedPane = this.state.selectedPane;
-
-	    var showSourcesTooltip = L10N.getStr("sourcesPane.showSourcesTooltip");
-	    var showOutlineTooltip = L10N.getStr("sourcesPane.showOutlineTooltip");
-
-	    var isSourcesPaneSelected = selectedPane === "sources";
-	    var tooltip = isSourcesPaneSelected ? showOutlineTooltip : showSourcesTooltip;
-	    var type = isSourcesPaneSelected ? "showSources" : "showOutline";
-
-	    return _react.DOM.button({
-	      className: "action",
-	      onClick: this.togglePane,
-	      key: type,
-	      title: tooltip
-	    }, (0, _Svg2.default)(type));
+	    return [_react.DOM.div({
+	      className: (0, _classnames2.default)("tab", {
+	        active: this.state.selectedPane === "sources"
+	      }),
+	      onClick: () => this.showPane("sources"),
+	      key: "sources-tab"
+	    }, "Sources View"), _react.DOM.div({
+	      className: (0, _classnames2.default)("tab", {
+	        active: this.state.selectedPane === "outline"
+	      }),
+	      onClick: () => this.showPane("outline"),
+	      key: "outline-tab"
+	    }, "Outline View")];
 	  }
 
 	  renderFooter() {
 	    return _react.DOM.div({
 	      className: "source-footer"
-	    }, _react.DOM.div({ className: "commands" }, this.renderOutlineToggleButton()));
+	    }, this.renderOutlineTabs());
 	  }
 
 	  renderShortcut() {
 	    if (this.props.horizontal) {
 	      return _react.DOM.span({
 	        className: "sources-header-info",
 	        dir: "ltr",
 	        onClick: () => this.props.toggleProjectSearch()
@@ -22308,17 +22972,17 @@ return /******/ (function(modules) { // 
 	      getChildren: item => {
 	        if ((0, _sourcesTree.nodeHasChildren)(item)) {
 	          return item.contents;
 	        }
 	        return [];
 	      },
 	      getRoots: () => sourceTree.contents,
 	      getKey: (item, i) => item.path,
-	      itemHeight: 18,
+	      itemHeight: 19,
 	      autoExpandDepth: 1,
 	      autoExpandAll: false,
 	      onFocus: this.focusItem,
 	      listItems,
 	      highlightItems,
 	      renderItem: this.renderItem
 	    });
 
@@ -23903,17 +24567,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _reactImmutableProptypes = __webpack_require__(150);
 
 	var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes);
 
 	var _redux = __webpack_require__(3);
@@ -23941,19 +24605,27 @@ return /******/ (function(modules) { // 
 	var _EditorMenu = __webpack_require__(656);
 
 	var _EditorMenu2 = _interopRequireDefault(_EditorMenu);
 
 	var _ConditionalPanel = __webpack_require__(711);
 
 	var _devtoolsLaunchpad = __webpack_require__(131);
 
+	var _range = __webpack_require__(1026);
+
+	var _range2 = _interopRequireDefault(_range);
+
+	var _flatMap = __webpack_require__(1067);
+
+	var _flatMap2 = _interopRequireDefault(_flatMap);
+
 	var _selectors = __webpack_require__(242);
 
-	var _breakpoints = __webpack_require__(236);
+	var _breakpoint = __webpack_require__(1057);
 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
 	var _Footer2 = __webpack_require__(427);
 
 	var _Footer3 = _interopRequireDefault(_Footer2);
@@ -23979,18 +24651,16 @@ return /******/ (function(modules) { // 
 	var _ColumnBreakpoint3 = _interopRequireDefault(_ColumnBreakpoint2);
 
 	var _HitMarker2 = __webpack_require__(715);
 
 	var _HitMarker3 = _interopRequireDefault(_HitMarker2);
 
 	var _editor = __webpack_require__(257);
 
-	var _scopes = __webpack_require__(732);
-
 	__webpack_require__(716);
 
 	__webpack_require__(1046);
 
 	var _devtoolsSourceEditor = __webpack_require__(994);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
@@ -24027,18 +24697,17 @@ return /******/ (function(modules) { // 
 	    this.lastJumpLine = null;
 
 	    this.state = {
 	      searchResults: {
 	        index: -1,
 	        count: 0
 	      },
 	      highlightedLineRange: null,
-	      selectedToken: null,
-	      selectedExpression: null
+	      selectedToken: null
 	    };
 
 	    var self = this;
 	    self.closeConditionalPanel = this.closeConditionalPanel.bind(this);
 	    self.onEscape = this.onEscape.bind(this);
 	    self.onGutterClick = this.onGutterClick.bind(this);
 	    self.onGutterContextMenu = this.onGutterContextMenu.bind(this);
 	    self.onScroll = this.onScroll.bind(this);
@@ -24069,16 +24738,20 @@ return /******/ (function(modules) { // 
 	        this.showMessage("");
 	      }
 	    } else if (!(0, _editor.isTextForSource)(sourceText)) {
 	      this.showMessage(sourceText.get("error") || L10N.getStr("loadingText"));
 	    } else if (this.props.sourceText !== sourceText) {
 	      this.showSourceText(sourceText, selectedLocation);
 	    }
 
+	    if (this.props.outOfScopeLocations !== nextProps.outOfScopeLocations) {
+	      (0, _editor.clearLineClass)(this.editor.codeMirror, "out-of-scope");
+	    }
+
 	    this.setDebugLine(nextProps.selectedFrame, selectedLocation);
 	    (0, _editor.resizeBreakpointGutter)(this.editor.codeMirror);
 	  }
 
 	  setupEditor() {
 	    var editor = (0, _editor.createEditor)();
 
 	    // disables the default search shortcuts
@@ -24225,32 +24898,36 @@ return /******/ (function(modules) { // 
 
 	  /*
 	   * The default Esc command is overridden in the CodeMirror keymap to allow
 	   * the Esc keypress event to be catched by the toolbox and trigger the
 	   * split console. Restore it here, but preventDefault if and only if there
 	   * is a multiselection.
 	   */
 	  onEscape(key, e) {
+	    if (!this.editor) {
+	      return;
+	    }
+
 	    var codeMirror = this.editor.codeMirror;
 
 	    if (codeMirror.listSelections().length > 1) {
 	      codeMirror.execCommand("singleSelection");
 	      e.preventDefault();
 	    }
 	  }
 
-	  onScroll(e) {
-	    return this.setState({ selectedToken: null, selectedExpression: null });
+	  onScroll() {
+	    this.clearPreviewSelection();
 	  }
 
 	  onMouseOver(e) {
 	    var target = e.target;
 
-	    if (!target.parentElement.closest(".CodeMirror-line")) {
+	    if (!this.inSelectedFrameSource() || !target.parentElement.closest(".CodeMirror-line") || target.parentElement.closest(".out-of-scope")) {
 	      return;
 	    }
 	    this.previewSelectedToken(e);
 	  }
 
 	  onTokenClick(e) {
 	    var target = e.target;
 
@@ -24272,66 +24949,42 @@ return /******/ (function(modules) { // 
 	    var codeMirror = this.editor.editor.codeMirror;
 
 	    var ctx = { ed: this.editor, cm: codeMirror };
 
 	    var direction = e.shiftKey ? "prev" : "next";
 	    (0, _editor.traverseResults)(e, ctx, query, direction, searchModifiers.toJS());
 	  }
 
+	  clearPreviewSelection() {
+	    this.props.clearSelection();
+	    return this.setState({ selectedToken: null });
+	  }
+
 	  previewSelectedToken(e) {
 	    var _this = this;
 
 	    return _asyncToGenerator(function* () {
 	      var _props3 = _this.props,
 	          selectedFrame = _props3.selectedFrame,
 	          selectedSource = _props3.selectedSource,
-	          pauseData = _props3.pauseData,
 	          sourceText = _props3.sourceText,
-	          addExpression = _props3.addExpression;
-	      var selectedToken = _this.state.selectedToken;
+	          setSelection = _props3.setSelection,
+	          selection = _props3.selection;
 
 	      var token = e.target;
-
-	      if (!selectedFrame || !sourceText || !selectedSource || selectedFrame.location.sourceId !== selectedSource.get("id")) {
-	        return;
-	      }
-
-	      if (selectedToken) {
-	        selectedToken.classList.remove("selected-token");
-	        _this.setState({ selectedToken: null, selectedExpression: null });
-	      }
-
-	      var _ref = yield (0, _editor.resolveToken)(_this.editor.codeMirror, token, sourceText, selectedFrame),
-	          expression = _ref.expression,
-	          inScope = _ref.inScope;
-
-	      if (!inScope) {
+	      var tokenText = token.innerText.trim();
+
+	      if (selection && selection.updating || !selectedFrame || !sourceText || !selectedSource || tokenText === "" || tokenText.match(/\s/) || selectedFrame.location.sourceId !== selectedSource.get("id")) {
 	        return;
 	      }
 
-	      var variables = (0, _scopes.getVisibleVariablesFromScope)(pauseData, selectedFrame);
-
-	      if (expression) {
-	        addExpression(expression.value, { visible: false });
-	      }
-
-	      var displayedExpression = (0, _editor.previewExpression)({
-	        expression: expression,
-	        variables,
-	        selectedFrame,
-	        tokenText: token.textContent
-	      });
-
-	      if (displayedExpression) {
-	        _this.setState({
-	          selectedToken: token,
-	          selectedExpression: displayedExpression
-	        });
-	      }
+	      var location = (0, _editor.getTokenLocation)(_this.editor.codeMirror, token);
+	      setSelection(tokenText, location);
+	      _this.setState({ selectedToken: token });
 	    })();
 	  }
 
 	  openMenu(event, codeMirror) {
 	    var _props4 = this.props,
 	        selectedSource = _props4.selectedSource,
 	        selectedLocation = _props4.selectedLocation,
 	        showSource = _props4.showSource,
@@ -24348,20 +25001,20 @@ return /******/ (function(modules) { // 
 	      showSource,
 	      jumpToMappedLocation,
 	      addExpression,
 	      toggleBlackBox,
 	      onGutterContextMenu: this.onGutterContextMenu
 	    });
 	  }
 
-	  updateSearchResults(_ref2) {
-	    var count = _ref2.count,
-	        _ref2$index = _ref2.index,
-	        index = _ref2$index === undefined ? -1 : _ref2$index;
+	  updateSearchResults(_ref) {
+	    var count = _ref.count,
+	        _ref$index = _ref.index,
+	        index = _ref$index === undefined ? -1 : _ref$index;
 
 	    this.setState({ searchResults: { count, index } });
 	  }
 
 	  onGutterClick(cm, line, gutter, ev) {
 	    var selectedSource = this.props.selectedSource;
 
 	    // ignore right clicks in the gutter
@@ -24426,17 +25079,17 @@ return /******/ (function(modules) { // 
 	    var panel = (0, _ConditionalPanel.renderConditionalPanel)({
 	      condition,
 	      setBreakpoint,
 	      closePanel: this.closeConditionalPanel
 	    });
 
 	    this.cbPanel = this.editor.codeMirror.addLineWidget(line, panel, {
 	      coverGutter: true,
-	      noHScroll: true
+	      noHScroll: false
 	    });
 	    this.cbPanel.node.querySelector("input").focus();
 	  }
 
 	  closeConditionalPanel() {
 	    this.cbPanel.clear();
 	    this.cbPanel = null;
 	  }
@@ -24615,46 +25268,46 @@ return /******/ (function(modules) { // 
 	        selectedSource = _props7.selectedSource;
 
 	    var isLoading = sourceText && sourceText.get("loading");
 
 	    if (isLoading || !breakpoints || selectedSource && selectedSource.get("isBlackBoxed")) {
 	      return;
 	    }
 
-	    var breakpointMarkers = breakpoints.valueSeq().filter(b => !b.location.column).map(bp => Breakpoint({
-	      key: (0, _breakpoints.makeLocationId)(bp.location),
+	    var breakpointMarkers = breakpoints.valueSeq().filter(b => (0, _devtoolsConfig.isEnabled)("columnBreakpoints") ? !b.location.column : true).map(bp => Breakpoint({
+	      key: (0, _breakpoint.makeLocationId)(bp.location),
 	      breakpoint: bp,
 	      editor: this.editor && this.editor.codeMirror
 	    }));
 
-	    var columnBreakpointBookmarks = breakpoints.valueSeq().filter(b => b.location.column).map(bp => ColumnBreakpoint({
-	      key: (0, _breakpoints.makeLocationId)(bp.location),
+	    var columnBreakpointBookmarks = breakpoints.valueSeq().filter(b => (0, _devtoolsConfig.isEnabled)("columnBreakpoints") ? b.location.column : false).map(bp => ColumnBreakpoint({
+	      key: (0, _breakpoint.makeLocationId)(bp.location),
 	      breakpoint: bp,
 	      editor: this.editor && this.editor.codeMirror
 	    }));
 
 	    return breakpointMarkers.concat(columnBreakpointBookmarks);
 	  }
 
 	  renderHitCounts() {
 	    var _props8 = this.props,
 	        hitCount = _props8.hitCount,
 	        sourceText = _props8.sourceText;
 
 	    var isLoading = sourceText && sourceText.get("loading");
 
-	    if (isLoading || !hitCount) {
+	    if (isLoading || !hitCount || !this.editor) {
 	      return;
 	    }
 
 	    return hitCount.filter(marker => marker.get("count") > 0).map(marker => HitMarker({
 	      key: marker.get("line"),
 	      hitData: marker.toJS(),
-	      editor: this.editor && this.editor.codeMirror
+	      editor: this.editor.codeMirror
 	    }));
 	  }
 
 	  getInlineEditorStyles() {
 	    var _props9 = this.props,
 	        selectedSource = _props9.selectedSource,
 	        horizontal = _props9.horizontal,
 	        searchOn = _props9.searchOn;
@@ -24672,64 +25325,76 @@ return /******/ (function(modules) { // 
 	    }
 
 	    return {
 	      height: subtractions.length === 0 ? "100%" : `calc(100% - ${subtractions.join(" - ")})`
 	    };
 	  }
 
 	  renderPreview() {
-	    var _state = this.state,
-	        selectedToken = _state.selectedToken,
-	        selectedExpression = _state.selectedExpression;
+	    var selectedToken = this.state.selectedToken;
 	    var _props10 = this.props,
-	        selectedFrame = _props10.selectedFrame,
-	        sourceText = _props10.sourceText;
+	        sourceText = _props10.sourceText,
+	        selection = _props10.selection;
 
 
 	    if (!this.editor || !sourceText) {
 	      return null;
 	    }
 
-	    if (!selectedToken || !selectedFrame || !selectedExpression) {
-	      return;
-	    }
-
-	    var token = selectedToken.textContent;
-
-	    var value = (0, _editor.getExpressionValue)(selectedExpression, {
-	      getExpression: this.props.getExpression
-	    });
-
-	    if (typeof value == "undefined" || value.type == "undefined") {
+	    if (!selection || !selectedToken) {
+	      return;
+	    }
+
+	    var result = selection.result,
+	        expression = selection.expression;
+
+	    var value = result;
+	    if (typeof value == "undefined" || value.type == "undefined" || value.optimizedOut) {
 	      return;
 	    }
 
 	    return Preview({
 	      value,
-	      expression: token,
+	      expression: expression,
 	      popoverTarget: selectedToken,
-	      onClose: () => {
-	        this.setState({
-	          selectedToken: null,
-	          selectedExpression: null
-	        });
-	      }
+	      onClose: () => this.clearPreviewSelection()
+	    });
+	  }
+
+	  inSelectedFrameSource() {
+	    var _props11 = this.props,
+	        selectedLocation = _props11.selectedLocation,
+	        selectedFrame = _props11.selectedFrame;
+
+	    return selectedFrame && selectedLocation && selectedFrame.location.sourceId == selectedLocation.sourceId;
+	  }
+
+	  renderOutOfScopedLocations() {
+	    var outOfScopeLocations = this.props.outOfScopeLocations;
+
+
+	    if (!this.inSelectedFrameSource() || !outOfScopeLocations || !this.editor) {
+	      return;
+	    }
+
+	    (0, _flatMap2.default)(outOfScopeLocations, location => (0, _range2.default)(location.start.line, location.end.line)).forEach(line => {
+	      this.editor.codeMirror.addLineClass(line - 1, "line", "out-of-scope");
 	    });
 	  }
 
 	  render() {
-	    var _props11 = this.props,
-	        sourceText = _props11.sourceText,
-	        selectSource = _props11.selectSource,
-	        selectedSource = _props11.selectedSource,
-	        highlightLineRange = _props11.highlightLineRange,
-	        clearHighlightLineRange = _props11.clearHighlightLineRange,
-	        coverageOn = _props11.coverageOn,
-	        horizontal = _props11.horizontal;
+	    var _props12 = this.props,
+	        sourceText = _props12.sourceText,
+	        selectSource = _props12.selectSource,
+	        selectedSource = _props12.selectedSource,
+	        highlightLineRange = _props12.highlightLineRange,
+	        clearHighlightLineRange = _props12.clearHighlightLineRange,
+	        coverageOn = _props12.coverageOn,
+	        horizontal = _props12.horizontal;
 	    var searchResults = this.state.searchResults;
 
 
 	    return _react.DOM.div({
 	      className: (0, _classnames2.default)("editor-wrapper", { "coverage-on": coverageOn })
 	    }, SearchBar({
 	      editor: this.editor,
 	      selectSource,
@@ -24737,17 +25402,17 @@ return /******/ (function(modules) { // 
 	      highlightLineRange,
 	      clearHighlightLineRange,
 	      sourceText,
 	      searchResults,
 	      updateSearchResults: this.updateSearchResults
 	    }), _react.DOM.div({
 	      className: "editor-mount devtools-monospace",
 	      style: this.getInlineEditorStyles()
-	    }), this.renderHighlightLines(), this.renderBreakpoints(), this.renderHitCounts(), Footer({ editor: this.editor, horizontal }), this.renderPreview());
+	    }), this.renderOutOfScopedLocations(), this.renderHighlightLines(), this.renderBreakpoints(), this.renderHitCounts(), Footer({ editor: this.editor, horizontal }), this.renderPreview());
 	  }
 	}
 
 	Editor.displayName = "Editor";
 
 	Editor.propTypes = {
 	  breakpoints: _reactImmutableProptypes2.default.map.isRequired,
 	  hitCount: _react.PropTypes.object,
@@ -24774,18 +25439,21 @@ return /******/ (function(modules) { // 
 	  addExpression: _react.PropTypes.func.isRequired,
 	  horizontal: _react.PropTypes.bool,
 	  query: _react.PropTypes.string.isRequired,
 	  searchModifiers: _reactImmutableProptypes2.default.recordOf({
 	    caseSensitive: _react.PropTypes.bool.isRequired,
 	    regexMatch: _react.PropTypes.bool.isRequired,
 	    wholeWord: _react.PropTypes.bool.isRequired
 	  }).isRequired,
+	  selection: _react.PropTypes.object,
 	  startPanelSize: _react.PropTypes.number,
-	  endPanelSize: _react.PropTypes.number
+	  endPanelSize: _react.PropTypes.number,
+	  clearSelection: _react.PropTypes.func.isRequired,
+	  outOfScopeLocations: _react.PropTypes.array
 	};
 
 	Editor.contextTypes = {
 	  shortcuts: _react.PropTypes.object
 	};
 
 	var expressionsSel = state => state.expressions.expressions;
 	var getExpressionSel = (0, _reselect.createSelector)(expressionsSel, expressions => input => expressions.find(exp => exp.input == input));
@@ -24804,17 +25472,19 @@ return /******/ (function(modules) { // 
 	    loadedObjects: (0, _selectors.getLoadedObjects)(state),
 	    breakpoints: (0, _selectors.getBreakpointsForSource)(state, sourceId || ""),
 	    hitCount: (0, _selectors.getHitCountForSource)(state, sourceId),
 	    selectedFrame: (0, _selectors.getSelectedFrame)(state),
 	    getExpression: getExpressionSel(state),
 	    pauseData: (0, _selectors.getPause)(state),
 	    coverageOn: (0, _selectors.getCoverageEnabled)(state),
 	    query: (0, _selectors.getFileSearchQueryState)(state),
-	    searchModifiers: (0, _selectors.getFileSearchModifierState)(state)
+	    searchModifiers: (0, _selectors.getFileSearchModifierState)(state),
+	    outOfScopeLocations: (0, _selectors.getOutOfScopeLocations)(state),
+	    selection: (0, _selectors.getSelection)(state)
 	  };
 	}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Editor);
 
 /***/ },
 /* 427 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
@@ -24859,20 +25529,19 @@ return /******/ (function(modules) { // 
 
 	var PaneToggleButton = (0, _react.createFactory)(_PaneToggle2.default);
 
 	class SourceFooter extends _react.PureComponent {
 
 	  prettyPrintButton() {
 	    var _props = this.props,
 	        selectedSource = _props.selectedSource,
-	        sourceText = _props.sourceText,
 	        togglePrettyPrint = _props.togglePrettyPrint;
 
-	    var sourceLoaded = selectedSource && sourceText && !sourceText.get("loading");
+	    var sourceLoaded = selectedSource && !selectedSource.get("loading");
 
 	    if (!(0, _editor.shouldShowPrettyPrint)(selectedSource)) {
 	      return;
 	    }
 
 	    var tooltip = L10N.getStr("sourceTabs.prettyPrint");
 	    var type = "prettyPrint";
 
@@ -24886,20 +25555,19 @@ return /******/ (function(modules) { // 
 	      title: tooltip,
 	      "aria-label": tooltip
 	    }, (0, _Svg2.default)(type));
 	  }
 
 	  blackBoxButton() {
 	    var _props2 = this.props,
 	        selectedSource = _props2.selectedSource,
-	        sourceText = _props2.sourceText,
 	        toggleBlackBox = _props2.toggleBlackBox;
 
-	    var sourceLoaded = selectedSource && sourceText && !sourceText.get("loading");
+	    var sourceLoaded = selectedSource && !selectedSource.get("loading");
 
 	    var blackboxed = selectedSource.get("isBlackBoxed");
 
 	    if (!(0, _devtoolsConfig.isEnabled)("blackbox")) {
 	      return;
 	    }
 
 	    var tooltip = L10N.getStr("sourceFooter.blackbox");
@@ -24984,17 +25652,16 @@ return /******/ (function(modules) { // 
 
 	SourceFooter.displayName = "SourceFooter";
 
 	exports.default = (0, _reactRedux.connect)(state => {
 	  var selectedSource = (0, _selectors.getSelectedSource)(state);
 	  var selectedId = selectedSource && selectedSource.get("id");
 	  return {
 	    selectedSource,
-	    sourceText: (0, _selectors.getSourceText)(state, selectedId),
 	    prettySource: (0, _selectors.getPrettySource)(state, selectedId),
 	    endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end")
 	  };
 	}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SourceFooter);
 
 /***/ },
 /* 428 */
 /***/ function(module, exports, __webpack_require__) {
@@ -25015,16 +25682,17 @@ return /******/ (function(modules) { // 
 
 	var _Svg2 = _interopRequireDefault(_Svg);
 
 	__webpack_require__(429);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 	class PaneToggleButton extends _react.Component {
+
 	  shouldComponentUpdate(nextProps) {
 	    var _props = this.props,
 	        collapsed = _props.collapsed,
 	        horizontal = _props.horizontal;
 
 
 	    return horizontal !== nextProps.horizontal || collapsed !== nextProps.collapsed;
 	  }
@@ -25044,23 +25712,16 @@ return /******/ (function(modules) { // 
 	        vertical: horizontal != null ? !horizontal : false
 	      }),
 	      onClick: () => handleClick(position, collapsed),
 	      title
 	    }, (0, _Svg2.default)("togglePanes"));
 	  }
 	}
 
-	PaneToggleButton.propTypes = {
-	  position: _react.PropTypes.string.isRequired,
-	  collapsed: _react.PropTypes.bool.isRequired,
-	  horizontal: _react.PropTypes.bool,
-	  handleClick: _react.PropTypes.func.isRequired
-	};
-
 	PaneToggleButton.displayName = "PaneToggleButton";
 
 	exports.default = PaneToggleButton;
 
 /***/ },
 /* 429 */
 /***/ function(module, exports) {
 
@@ -25081,17 +25742,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactRedux = __webpack_require__(151);
 
 	var _redux = __webpack_require__(3);
 
 	var _fuzzaldrinPlus = __webpack_require__(161);
 
 	var _Svg = __webpack_require__(344);
@@ -25101,18 +25762,16 @@ return /******/ (function(modules) { // 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
 	var _selectors = __webpack_require__(242);
 
 	var _editor = __webpack_require__(257);
 
-	var _parser = __webpack_require__(827);
-
 	var _resultList = __webpack_require__(343);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	var _debounce = __webpack_require__(651);
 
@@ -25127,18 +25786,16 @@ return /******/ (function(modules) { // 
 	var _ResultList2 = __webpack_require__(383);
 
 	var _ResultList3 = _interopRequireDefault(_ResultList2);
 
 	__webpack_require__(653);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
-
 	var SearchInput = (0, _react.createFactory)(_SearchInput3.default);
 
 	var ResultList = (0, _react.createFactory)(_ResultList3.default);
 
 	function formatSymbol(symbol) {
 	  return {
 	    id: `${symbol.name}:${symbol.location.start.line}`,
 	    title: symbol.name,
@@ -25241,17 +25898,16 @@ return /******/ (function(modules) { // 
 	    shortcuts.on(symbolSearchShortcut, (_, e) => this.toggleSymbolSearch(e, {
 	      toggle: false,
 	      searchType: "functions"
 	    }));
 	  }
 
 	  componentDidUpdate(prevProps, prevState) {
 	    var _props = this.props,
-	        sourceText = _props.sourceText,
 	        selectedSource = _props.selectedSource,
 	        query = _props.query,
 	        modifiers = _props.modifiers,
 	        searchOn = _props.searchOn,
 	        symbolSearchOn = _props.symbolSearchOn,
 	        selectedSymbolType = _props.selectedSymbolType;
 
 	    var searchInput = this.searchInput();
@@ -25259,18 +25915,18 @@ return /******/ (function(modules) { // 
 	    if (searchInput) {
 	      searchInput.focus();
 	    }
 
 	    if (this.refs.resultList && this.refs.resultList.refs) {
 	      (0, _resultList.scrollList)(this.refs.resultList.refs, this.state.selectedResultIndex);
 	    }
 
-	    var hasLoaded = sourceText && !sourceText.get("loading");
-	    var wasLoading = prevProps.sourceText && prevProps.sourceText.get("loading");
+	    var hasLoaded = selectedSource && !selectedSource.get("loading");
+	    var wasLoading = prevProps.selectedSource && prevProps.selectedSource.get("loading");
 
 	    var doneLoading = wasLoading && hasLoaded;
 	    var changedFiles = selectedSource != prevProps.selectedSource && hasLoaded;
 	    var modifiersUpdated = modifiers && !modifiers.equals(prevProps.modifiers);
 
 	    var isOpen = searchOn || symbolSearchOn;
 	    var changedSearchType = selectedSymbolType != prevProps.selectedSymbolType || symbolSearchOn != prevProps.symbolSearchOn;
 
@@ -25291,20 +25947,20 @@ return /******/ (function(modules) { // 
 
 	    if (ed && modifiers) {
 	      var ctx = { ed, cm: ed.codeMirror };
 	      (0, _editor.removeOverlay)(ctx, query, modifiers.toJS());
 	    }
 	  }
 
 	  closeSearch(e) {
-	    var ed = this.props.editor;
-
-
-	    if (this.props.searchOn && ed) {
+	    var editor = this.props.editor;
+
+
+	    if (this.props.searchOn && editor) {
 	      this.clearSearch();
 	      this.props.toggleFileSearch(false);
 	      this.props.toggleSymbolSearch(false);
 	      this.props.setSelectedSymbolType("functions");
 	      this.props.clearHighlightLineRange();
 	      e.stopPropagation();
 	      e.preventDefault();
 	    }
@@ -25336,25 +25992,25 @@ return /******/ (function(modules) { // 
 	    }
 	  }
 
 	  toggleSymbolSearch(e) {
 	    var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
 	        toggle = _ref.toggle,
 	        searchType = _ref.searchType;
 
-	    var sourceText = this.props.sourceText;
+	    var selectedSource = this.props.selectedSource;
 
 
 	    if (e) {
 	      e.preventDefault();
 	      e.stopPropagation();
 	    }
 
-	    if (!sourceText) {
+	    if (!selectedSource) {
 	      return;
 	    }
 
 	    if (!this.props.searchOn) {
 	      this.props.toggleFileSearch();
 	    }
 
 	    if (this.props.symbolSearchOn) {
@@ -25396,83 +26052,69 @@ return /******/ (function(modules) { // 
 	      if (input instanceof HTMLInputElement) {
 	        return input;
 	      }
 	    }
 	    return null;
 	  }
 
 	  updateSymbolSearchResults(query) {
-	    var _this = this;
-
-	    return _asyncToGenerator(function* () {
-	      var _props3 = _this.props,
-	          sourceText = _props3.sourceText,
-	          updateSearchResults = _props3.updateSearchResults,
-	          selectedSymbolType = _props3.selectedSymbolType;
-
-
-	      if (query == "" || !sourceText) {
-	        return;
-	      }
-
-	      var _ref2 = yield (0, _parser.getSymbols)(sourceText.toJS()),
-	          functions = _ref2.functions,
-	          variables = _ref2.variables;
-
-	      var formattedSymbolDeclaration = {
-	        variables: variables.map(formatSymbol),
-	        functions: functions.map(formatSymbol)
-	      };
-
-	      var symbolSearchResults = (0, _fuzzaldrinPlus.filter)(formattedSymbolDeclaration[selectedSymbolType], query, { key: "value" });
-
-	      updateSearchResults({ count: symbolSearchResults.length });
-	      return _this.setState({ symbolSearchResults });
-	    })();
+	    var _props3 = this.props,
+	        selectedSource = _props3.selectedSource,
+	        updateSearchResults = _props3.updateSearchResults,
+	        selectedSymbolType = _props3.selectedSymbolType,
+	        symbols = _props3.symbols;
+
+
+	    if (query == "" || !selectedSource) {
+	      return;
+	    }
+
+	    var symbolSearchResults = (0, _fuzzaldrinPlus.filter)(symbols[selectedSymbolType], query, {
+	      key: "value"
+	    });
+
+	    updateSearchResults({ count: symbolSearchResults.length });
+	    return this.setState({ symbolSearchResults });
 	  }
 
 	  doSearch(query) {
-	    var _this2 = this;
-
-	    return _asyncToGenerator(function* () {
-	      var _props4 = _this2.props,
-	          sourceText = _props4.sourceText,
-	          setFileSearchQuery = _props4.setFileSearchQuery,
-	          ed = _props4.editor;
-
-	      if (!sourceText || !sourceText.get("text")) {
-	        return;
-	      }
-
-	      setFileSearchQuery(query);
-
-	      if (_this2.props.symbolSearchOn) {
-	        return yield _this2.updateSymbolSearchResults(query);
-	      } else if (ed) {
-	        _this2.searchContents(query);
-	      }
-	    })();
+	    var _props4 = this.props,
+	        selectedSource = _props4.selectedSource,
+	        setFileSearchQuery = _props4.setFileSearchQuery,
+	        ed = _props4.editor;
+
+	    if (!selectedSource || !selectedSource.get("text")) {
+	      return;
+	    }
+
+	    setFileSearchQuery(query);
+
+	    if (this.props.symbolSearchOn) {
+	      return this.updateSymbolSearchResults(query);
+	    } else if (ed) {
+	      this.searchContents(query);
+	    }
 	  }
 
 	  searchContents(query) {
 	    var _props5 = this.props,
-	        sourceText = _props5.sourceText,
+	        selectedSource = _props5.selectedSource,
 	        modifiers = _props5.modifiers,
 	        ed = _props5.editor,
 	        index = _props5.searchResults.index;
 
 
-	    if (!ed || !sourceText || !sourceText.get("text") || !modifiers) {
+	    if (!ed || !selectedSource || !selectedSource.get("text") || !modifiers) {
 	      return;
 	    }
 
 	    var ctx = { ed, cm: ed.codeMirror };
 
-	    var newCount = (0, _editor.countMatches)(query, sourceText.get("text"), modifiers.toJS());
+	    var newCount = (0, _editor.countMatches)(query, selectedSource.get("text"), modifiers.toJS());
 
 	    if (index == -1) {
 	      (0, _editor.clearIndex)(ctx, query, modifiers.toJS());
 	    }
 
 	    var newIndex = (0, _editor.find)(ctx, query, true, modifiers.toJS());
 	    this.props.updateSearchResults({
 	      count: newCount,
@@ -25592,21 +26234,17 @@ return /******/ (function(modules) { // 
 	        start: item.location.start.line,
 	        end: item.location.end.line,
 	        sourceId: selectedSource.get("id")
 	      });
 	    }
 	  }
 
 	  onChange(e) {
-	    var _this3 = this;
-
-	    return _asyncToGenerator(function* () {
-	      return _this3.doSearch(e.target.value);
-	    })();
+	    return this.doSearch(e.target.value);
 	  }
 
 	  onKeyUp(e) {
 	    if (e.key !== "Enter" && e.key !== "F3") {
 	      return;
 	    }
 
 	    this.traverseResults(e, e.shiftKey);
@@ -25785,22 +26423,39 @@ return /******/ (function(modules) { // 
 	  }
 	}
 
 	SearchBar.displayName = "SearchBar";
 	SearchBar.contextTypes = {
 	  shortcuts: _react.PropTypes.object
 	};
 
+	function _getFormattedSymbols(state) {
+	  var source = (0, _selectors.getSelectedSource)(state);
+	  if (!source) {
+	    return { variables: [], functions: [] };
+	  }
+
+	  var _getSymbols = (0, _selectors.getSymbols)(state, source.toJS()),
+	      variables = _getSymbols.variables,
+	      functions = _getSymbols.functions;
+
+	  return {
+	    variables: variables.map(formatSymbol),
+	    functions: functions.map(formatSymbol)
+	  };
+	}
+
 	exports.default = (0, _reactRedux.connect)(state => {
 	  return {
 	    searchOn: (0, _selectors.getFileSearchState)(state),
 	    query: (0, _selectors.getFileSearchQueryState)(state),
 	    modifiers: (0, _selectors.getFileSearchModifierState)(state),
 	    symbolSearchOn: (0, _selectors.getSymbolSearchState)(state),
+	    symbols: _getFormattedSymbols(state),
 	    selectedSymbolType: (0, _selectors.getSymbolSearchType)(state)
 	  };
 	}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SearchBar);
 
 /***/ },
 /* 434 */,
 /* 435 */,
 /* 436 */,
@@ -26708,19 +27363,21 @@ return /******/ (function(modules) { // 
 	var _get = __webpack_require__(67);
 
 	var _get2 = _interopRequireDefault(_get);
 
 	var _devtoolsReps = __webpack_require__(924);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
+	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
 	var WINDOW_PROPERTIES = {};
 
-	if (typeof window == "object") {
+	if (typeof window === "object") {
 	  WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
 	}
 
 	function getValue(item) {
 	  return (0, _get2.default)(item, "contents.value", undefined);
 	}
 
 	function isBucket(item) {
@@ -26767,24 +27424,41 @@ return /******/ (function(modules) { // 
 	  var value = getValue(item);
 	  return value.class == "Promise";
 	}
 
 	function getPromiseProperties(item) {
 	  var _getValue = getValue(item),
 	      _getValue$promiseStat = _getValue.promiseState,
 	      reason = _getValue$promiseStat.reason,
-	      value = _getValue$promiseStat.value;
-
-	  return createNode("reason", `${item.path}/reason`, {
-	    value: !reason ? value : reason
-	  });
-	}
-
-	function isDefault(item) {
+	      value = _getValue$promiseStat.value,
+	      state = _getValue$promiseStat.state;
+
+	  var properties = [];
+
+	  if (state) {
+	    properties.push(createNode("<state>", `${item.path}/state`, { value: state }));
+	  }
+
+	  if (reason) {
+	    properties.push(createNode("<reason>", `${item.path}/reason`, { value: reason }));
+	  }
+
+	  if (value) {
+	    properties.push(createNode("<value>", `${item.path}/value`, { value: value }));
+	  }
+
+	  return properties;
+	}
+
+	function isDefault(item, roots) {
+	  if (roots && roots.length === 1) {
+	    var value = getValue(roots[0]);
+	    return value.class === "Window";
+	  }
 	  return WINDOW_PROPERTIES.includes(item.name);
 	}
 
 	function sortProperties(properties) {
 	  return properties.sort((a, b) => {
 	    // Sort numbers in ascending order and sort strings lexicographically
 	    var aInt = parseInt(a, 10);
 	    var bInt = parseInt(b, 10);
@@ -26864,25 +27538,34 @@ return /******/ (function(modules) { // 
 	  } else {
 	    nodes = makeNodesForOwnProps(properties, parentPath, ownProperties);
 	  }
 
 	  for (var index in ownSymbols) {
 	    nodes.push(createNode(ownSymbols[index].name, `${parentPath}/##symbol-${index}`, ownSymbols[index].descriptor));
 	  }
 
+	  if (isPromise(parent)) {
+	    var _nodes;
+
+	    (_nodes = nodes).push.apply(_nodes, _toConsumableArray(getPromiseProperties(parent)));
+	  }
+
 	  // Add the prototype if it exists and is not null
 	  if (prototype && prototype.type !== "null") {
 	    nodes.push(createNode("__proto__", `${parentPath}/__proto__`, { value: prototype }));
 	  }
 
 	  return nodes;
 	}
 
 	function createNode(name, path, contents) {
+	  if (contents === undefined) {
+	    return null;
+	  }
 	  // The path is important to uniquely identify the item in the entire
 	  // tree. This helps debugging & optimizes React's rendering of large
 	  // lists. The path will be separated by property name,
 	  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of `foo/bar/baz`
 	  // for the inner object.
 	  return { name, path, contents };
 	}
 
@@ -26926,19 +27609,16 @@ return /******/ (function(modules) { // 
 	      ownProperties = _ref3.ownProperties,
 	      prototype = _ref3.prototype;
 
 	  if (!ownProperties && !prototype) {
 	    return [];
 	  }
 
 	  var children = makeNodesForProperties(loadedProps, item);
-	  if (isPromise(item)) {
-	    children.unshift(getPromiseProperties(item));
-	  }
 	  actors[key] = children;
 	  return children;
 	}
 
 	exports.nodeHasChildren = nodeHasChildren;
 	exports.nodeIsOptimizedOut = nodeIsOptimizedOut;
 	exports.nodeIsMissingArguments = nodeIsMissingArguments;
 	exports.nodeHasProperties = nodeHasProperties;
@@ -27071,16 +27751,21 @@ return /******/ (function(modules) { // 
 
 	    this.actors = {};
 
 	    var self = this;
 	    self.getChildren = this.getChildren.bind(this);
 	    self.renderItem = this.renderItem.bind(this);
 	  }
 
+	  isDefaultProperty(item) {
+	    var roots = this.props.roots;
+	    return (0, _objectInspector.isDefault)(item, roots);
+	  }
+
 	  getChildren(item) {
 	    var getObjectProperties = this.props.getObjectProperties;
 	    var actors = this.actors;
 
 
 	    return (0, _objectInspector.getChildren)({
 	      getObjectProperties,
 	      actors,
@@ -27104,19 +27789,21 @@ return /******/ (function(modules) { // 
 	    } else if ((0, _objectInspector.nodeHasProperties)(item) || (0, _objectInspector.nodeIsPrimitive)(item)) {
 	      var object = item.contents.value;
 	      objectValue = (0, _Rep2.default)({ object, mode: _devtoolsReps.MODE.TINY });
 	    }
 
 	    return _react.DOM.div({
 	      className: (0, _classnames2.default)("node object-node", {
 	        focused,
-	        "default-property": (0, _objectInspector.isDefault)(item)
+	        "default-property": this.isDefaultProperty(item)
 	      }),
-	      style: { marginLeft: depth * 15 },
+	      style: {
+	        marginLeft: depth * 15 + ((0, _objectInspector.nodeIsPrimitive)(item) ? 15 : 0)
+	      },
 	      onClick: e => {
 	        e.stopPropagation();
 	        setExpanded(item, !expanded);
 	      },
 	      onDoubleClick: event => {
 	        event.stopPropagation();
 	        this.props.onDoubleClick(item, {
 	          depth,
@@ -27154,17 +27841,17 @@ return /******/ (function(modules) { // 
 	      getChildren: this.getChildren,
 	      getRoots: () => roots,
 	      getKey: item => item.path,
 	      autoExpand: 0,
 	      autoExpandDepth,
 	      autoExpandAll: false,
 	      disabledFocus: true,
 	      onExpand: item => {
-	        if ((0, _objectInspector.nodeHasProperties)(item)) {
+	        if (item && item.contents && (0, _objectInspector.nodeHasProperties)(item)) {
 	          loadObjectProperties(item.contents.value);
 	        }
 	      },
 	      renderItem: this.renderItem
 	    });
 	  }
 	}
 
@@ -27221,17 +27908,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	var _BracketArrow2 = __webpack_require__(1029);
@@ -27738,17 +28425,17 @@ return /******/ (function(modules) { // 
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 	exports.renderConditionalPanel = undefined;
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _Close = __webpack_require__(378);
 
 	var _Close2 = _interopRequireDefault(_Close);
 
 	__webpack_require__(712);
@@ -27815,17 +28502,17 @@ return /******/ (function(modules) { // 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	var _Svg = __webpack_require__(344);
@@ -27918,16 +28605,17 @@ return /******/ (function(modules) { // 
 
 	function makeMarker() {
 	  var marker = markerEl.cloneNode(true);
 	  marker.className = "editor hit-marker";
 	  return marker;
 	}
 
 	class HitMarker extends _react.Component {
+
 	  addMarker() {
 	    var hitData = this.props.hitData;
 	    var line = hitData.line - 1;
 
 	    this.props.editor.setGutterMarker(line, "hit-markers", makeMarker());
 
 	    this.props.editor.addLineClass(line, "line", "hit-marker");
 	  }
@@ -27962,21 +28650,16 @@ return /******/ (function(modules) { // 
 
 	  render() {
 	    return null;
 	  }
 	}
 
 	HitMarker.displayName = "HitMarker";
 
-	HitMarker.propTypes = {
-	  hitData: _react.PropTypes.object.isRequired,
-	  editor: _react.PropTypes.object.isRequired
-	};
-
 	exports.default = HitMarker;
 
 /***/ },
 /* 716 */
 /***/ function(module, exports) {
 
 	// removed by extract-text-webpack-plugin
 
@@ -28294,16 +28977,23 @@ return /******/ (function(modules) { // 
 
 	  if (value.exception) {
 	    return {
 	      path: value.from,
 	      value: value.exception
 	    };
 	  }
 
+	  if (value.error) {
+	    return {
+	      path: value.from,
+	      value: value.error
+	    };
+	  }
+
 	  if (typeof value.result == "object") {
 	    return {
 	      path: value.result.actor,
 	      value: value.result
 	    };
 	  }
 
 	  return {
@@ -28418,17 +29108,17 @@ return /******/ (function(modules) { // 
 	    var root = {
 	      name: expression.input,
 	      path,
 	      contents: { value }
 	    };
 
 	    return _react.DOM.div({
 	      className: "expression-container",
-	      key: path || input
+	      key: `${path}/${input}`
 	    }, ObjectInspector({
 	      roots: [root],
 	      getObjectProperties: id => loadedObjects[id],
 	      autoExpandDepth: 0,
 	      onDoubleClick: (item, options) => this.editExpression(expression, options),
 	      loadObjectProperties
 	    }), CloseButton({ handleClick: e => this.deleteExpression(e, expression) }));
 	  }
@@ -28608,17 +29298,17 @@ return /******/ (function(modules) { // 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
 	var _selectors = __webpack_require__(242);
 
-	var _breakpoints = __webpack_require__(236);
+	var _breakpoint = __webpack_require__(1057);
 
 	var _utils = __webpack_require__(234);
 
 	var _path = __webpack_require__(235);
 
 	var _Close = __webpack_require__(378);
 
 	var _Close2 = _interopRequireDefault(_Close);
@@ -28630,18 +29320,18 @@ return /******/ (function(modules) { // 
 	var get = __webpack_require__(67);
 
 
 	function isCurrentlyPausedAtBreakpoint(pause, breakpoint) {
 	  if (!pause || pause.isInterrupted) {
 	    return false;
 	  }
 
-	  var bpId = (0, _breakpoints.makeLocationId)(breakpoint.location);
-	  var pausedId = (0, _breakpoints.makeLocationId)(get(pause, "frame.location"));
+	  var bpId = (0, _breakpoint.makeLocationId)(breakpoint.location);
+	  var pausedId = (0, _breakpoint.makeLocationId)(get(pause, "frame.location"));
 	  return bpId === pausedId;
 	}
 
 	function renderSourceLocation(source, line, column) {
 	  var url = source.get("url") ? (0, _path.basename)(source.get("url")) : null;
 	  var bpLocation = line + (column ? `:${column}` : "");
 	  // const line = url !== "" ? `: ${line}` : "";
 	  return url ? _react.DOM.div({ className: "location" }, `${(0, _utils.endTruncateStr)(url, 30)}: ${bpLocation}`) : null;
@@ -28726,17 +29416,17 @@ return /******/ (function(modules) { // 
 	  disableBreakpoint: _react.PropTypes.func.isRequired,
 	  selectSource: _react.PropTypes.func.isRequired,
 	  removeBreakpoint: _react.PropTypes.func.isRequired
 	};
 
 	function updateLocation(sources, pause, bp) {
 	  var source = (0, _selectors.getSourceInSources)(sources, bp.location.sourceId);
 	  var isCurrentlyPaused = isCurrentlyPausedAtBreakpoint(pause, bp);
-	  var locationId = (0, _breakpoints.makeLocationId)(bp.location);
+	  var locationId = (0, _breakpoint.makeLocationId)(bp.location);
 
 	  var location = Object.assign({}, bp.location, { source });
 	  var localBP = Object.assign({}, bp, {
 	    location,
 	    locationId,
 	    isCurrentlyPaused
 	  });
 
@@ -29477,20 +30167,16 @@ return /******/ (function(modules) { // 
 
 	  render() {
 	    return _react.DOM.div({ className: "accordion" }, this.props.items.map(this.renderContainer));
 	  }
 	}
 
 	Accordion.displayName = "Accordion";
 
-	Accordion.propTypes = {
-	  items: _react.PropTypes.array.isRequired
-	};
-
 	exports.default = Accordion;
 
 /***/ },
 /* 740 */
 /***/ function(module, exports) {
 
 	// removed by extract-text-webpack-plugin
 
@@ -29502,17 +30188,17 @@ return /******/ (function(modules) { // 
 	"use strict";
 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactRedux = __webpack_require__(151);
 
 	var _redux = __webpack_require__(3);
 
 	var _selectors = __webpack_require__(242);
 
 	var _Svg = __webpack_require__(344);
@@ -29875,17 +30561,18 @@ return /******/ (function(modules) { // 
 	    var topOffsets = sourceTabEls.map(t => t.getBoundingClientRect().top);
 	    return Math.min.apply(Math, _toConsumableArray(topOffsets));
 	  }
 
 	  var tabTopOffset = getTopOffset();
 	  return sourceTabs.filter((tab, index) => {
 	    // adding 10px helps account for cases where the tab might be offset by
 	    // styling such as selected tabs which don't have a border.
-	    return sourceTabEls[index].getBoundingClientRect().top > tabTopOffset + 10;
+	    var el = sourceTabEls[index];
+	    return el && el.getBoundingClientRect().top > tabTopOffset + 10;
 	  });
 	}
 
 	/**
 	 * Clipboard function taken from
 	 * https://dxr.mozilla.org/mozilla-central/source/devtools/shared/platform/content/clipboard.js
 	 */
 	function copyToTheClipboard(string) {
@@ -30262,20 +30949,16 @@ return /******/ (function(modules) { // 
 	    });
 	  }
 
 	  render() {
 	    return _react.DOM.div({ className: "dropdown-block" }, this.renderPanel(), this.renderButton(), this.renderMask());
 	  }
 	}
 
-	Dropdown.propTypes = {
-	  panel: _react.PropTypes.object
-	};
-
 	Dropdown.displayName = "Dropdown";
 
 	exports.default = Dropdown;
 
 /***/ },
 /* 752 */
 /***/ function(module, exports) {
 
@@ -30293,17 +30976,38 @@ return /******/ (function(modules) { // 
 /* 756 */,
 /* 757 */,
 /* 758 */,
 /* 759 */,
 /* 760 */,
 /* 761 */,
 /* 762 */,
 /* 763 */,
-/* 764 */,
+/* 764 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseFor = __webpack_require__(395),
+	    keys = __webpack_require__(205);
+
+	/**
+	 * The base implementation of `_.forOwn` without support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Object} object The object to iterate over.
+	 * @param {Function} iteratee The function invoked per iteration.
+	 * @returns {Object} Returns `object`.
+	 */
+	function baseForOwn(object, iteratee) {
+	  return object && baseFor(object, iteratee, keys);
+	}
+
+	module.exports = baseForOwn;
+
+
+/***/ },
 /* 765 */,
 /* 766 */,
 /* 767 */,
 /* 768 */,
 /* 769 */,
 /* 770 */,
 /* 771 */,
 /* 772 */,
@@ -30368,24 +31072,26 @@ return /******/ (function(modules) { // 
 
 	var _devtoolsUtils = __webpack_require__(900);
 
 	var WorkerDispatcher = _devtoolsUtils.workerUtils.WorkerDispatcher;
 
 
 	var dispatcher = new WorkerDispatcher();
 
+	var getClosestExpression = dispatcher.task("getClosestExpression");
 	var getSymbols = dispatcher.task("getSymbols");
 	var getVariablesInScope = dispatcher.task("getVariablesInScope");
-	var resolveToken = dispatcher.task("resolveToken");
+	var getOutOfScopeLocations = dispatcher.task("getOutOfScopeLocations");
 
 	module.exports = {
 	  getSymbols,
 	  getVariablesInScope,
-	  resolveToken,
+	  getOutOfScopeLocations,
+	  getClosestExpression,
 	  startParserWorker: dispatcher.start.bind(dispatcher),
 	  stopParserWorker: dispatcher.stop.bind(dispatcher)
 	};
 
 /***/ },
 /* 828 */
 /***/ function(module, exports, __webpack_require__) {
 
@@ -31677,19 +32383,23 @@ return /******/ (function(modules) { // 
 
 	  return {
 	    id: bpClient.actor,
 	    actualLocation
 	  };
 	}
 
 	function removeBreakpoint(breakpointId) {
-	  var bpClient = bpClients[breakpointId];
-	  delete bpClients[breakpointId];
-	  return bpClient.remove();
+	  try {
+	    var bpClient = bpClients[breakpointId];
+	    delete bpClients[breakpointId];
+	    return bpClient.remove();
+	  } catch (_error) {
+	    console.warn("No breakpoint to delete on server");
+	  }
 	}
 
 	function setBreakpointCondition(breakpointId, location, condition, noSliding) {
 	  var bpClient = bpClients[breakpointId];
 	  delete bpClients[breakpointId];
 
 	  return bpClient.setCondition(threadClient, condition, noSliding).then(_bpClient => onNewBreakpoint(location, [{}, _bpClient]));
 	}
@@ -31729,25 +32439,23 @@ return /******/ (function(modules) { // 
 	function getProperties(grip) {
 	  var objClient = threadClient.pauseGrip(grip);
 
 	  return objClient.getPrototypeAndProperties().then(resp => {
 	    var ownProperties = resp.ownProperties,
 	        safeGetterValues = resp.safeGetterValues;
 
 	    for (var name in safeGetterValues) {
-	      if (name in ownProperties) {
-	        var getterValue = safeGetterValues[name].getterValue;
-
-	        ownProperties[name].value = getterValue;
-	      } else {
-	        ownProperties[name] = safeGetterValues[name];
-	      }
-	    }
-
+	      var _safeGetterValues$nam = safeGetterValues[name],
+	          enumerable = _safeGetterValues$nam.enumerable,
+	          writable = _safeGetterValues$nam.writable,
+	          getterValue = _safeGetterValues$nam.getterValue;
+
+	      ownProperties[name] = { enumerable, writable, value: getterValue };
+	    }
 	    return resp;
 	  });
 	}
 
 	function pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions) {
 	  return threadClient.pauseOnExceptions(shouldPauseOnExceptions, shouldIgnoreCaughtExceptions);
 	}
 
@@ -32347,17 +33055,17 @@ return /******/ (function(modules) { // 
 	exports.teardownWorkers = teardownWorkers;
 
 	var _react = __webpack_require__(2);
 
 	var _react2 = _interopRequireDefault(_react);
 
 	var _redux = __webpack_require__(3);
 
-	var _reactDom = __webpack_require__(22);
+	var _reactDom = __webpack_require__(31);
 
 	var _reactDom2 = _interopRequireDefault(_reactDom);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
 	var _devtoolsLaunchpad = __webpack_require__(131);
 
 	var _devtoolsSourceMap = __webpack_require__(898);
@@ -32714,51 +33422,24 @@ return /******/ (function(modules) { // 
 	module.exports = {
 	  prettyPrint,
 	  startPrettyPrintWorker: dispatcher.start.bind(dispatcher),
 	  stopPrettyPrintWorker: dispatcher.stop.bind(dispatcher)
 	};
 
 /***/ },
 /* 904 */
-/***/ function(module, exports, __webpack_require__) {
-
-	"use strict";
-
-	Object.defineProperty(exports, "__esModule", {
-	  value: true
-	});
-	exports.resolveToken = undefined;
-
-	var resolveToken = exports.resolveToken = (() => {
-	  var _ref = _asyncToGenerator(function* (cm, token, sourceText, frame) {
-	    var loc = getTokenLocation(cm, token);
-	    return yield (0, _parser.resolveToken)(sourceText.toJS(), token.textContent || "", loc, frame);
-	  });
-
-	  return function resolveToken(_x, _x2, _x3, _x4) {
-	    return _ref.apply(this, arguments);
-	  };
-	})();
-
+/***/ function(module, exports) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
 	exports.getTokenLocation = getTokenLocation;
-	exports.getThisFromFrame = getThisFromFrame;
-	exports.previewExpression = previewExpression;
-	exports.getExpressionValue = getExpressionValue;
-
-	var _parser = __webpack_require__(827);
-
-	var _get = __webpack_require__(67);
-
-	var _get2 = _interopRequireDefault(_get);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
-
 	function getTokenLocation(codeMirror, tokenEl) {
 	  var lineOffset = 1;
 
 	  var _tokenEl$getBoundingC = tokenEl.getBoundingClientRect(),
 	      left = _tokenEl$getBoundingC.left,
 	      top = _tokenEl$getBoundingC.top;
 
 	  var _codeMirror$coordsCha = codeMirror.coordsChar({ left, top }),
@@ -32766,73 +33447,16 @@ return /******/ (function(modules) { // 
 	      ch = _codeMirror$coordsCha.ch;
 
 	  return {
 	    line: line + lineOffset,
 	    column: ch
 	  };
 	}
 
-	function getThisFromFrame(selectedFrame) {
-	  if ("this" in selectedFrame) {
-	    return { value: selectedFrame.this };
-	  }
-
-	  return null;
-	}
-
-	// TODO Better define the value for `variables` map once we do it in
-	// debugger-html
-	function previewExpression(_ref2) {
-	  var expression = _ref2.expression,
-	      selectedFrame = _ref2.selectedFrame,
-	      variables = _ref2.variables,
-	      tokenText = _ref2.tokenText;
-
-	  if (!tokenText) {
-	    return null;
-	  }
-
-	  if (tokenText === "this") {
-	    return getThisFromFrame(selectedFrame);
-	  }
-
-	  if (variables.has(tokenText)) {
-	    var variableKey = variables.get(tokenText);
-	    if ((0, _get2.default)(variableKey, "contents.value.type") == "undefined") {
-	      return null;
-	    }
-
-	    return variables.get(tokenText);
-	  }
-
-	  return expression || null;
-	}
-
-	// `getExpressionValue` and `previewExpression` are utility functions
-	// for resolving which expression to show in the preview.
-	// Get ExpressionValue, knows how to get the appropriate value for each type:
-	// variable, expression, raw value.
-	function getExpressionValue(selectedExpression, _ref3) {
-	  var getExpression = _ref3.getExpression;
-
-	  var variableValue = (0, _get2.default)(selectedExpression, "contents.value");
-	  if (typeof variableValue === "boolean" || variableValue) {
-	    return variableValue;
-	  }
-
-	  var expressionValue = getExpression(selectedExpression.value);
-	  if (expressionValue) {
-	    return (0, _get2.default)(expressionValue, "value.result");
-	  }
-
-	  var rawValue = selectedExpression.value;
-	  return rawValue;
-	}
-
 /***/ },
 /* 905 */,
 /* 906 */,
 /* 907 */,
 /* 908 */,
 /* 909 */,
 /* 910 */
 /***/ function(module, exports, __webpack_require__) {
@@ -32845,17 +33469,17 @@ return /******/ (function(modules) { // 
 
 /***/ },
 /* 911 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
 	var React = __webpack_require__(2);
-	var ReactDOM = __webpack_require__(22);
+	var ReactDOM = __webpack_require__(31);
 	var Draggable = React.createFactory(__webpack_require__(912));
 	var dom = React.DOM,
 	    PropTypes = React.PropTypes;
 
 
 	__webpack_require__(913);
 
 	/**
@@ -33102,17 +33726,17 @@ return /******/ (function(modules) { // 
 
 	"use strict";
 
 	/* This Source Code Form is subject to the terms of the Mozilla Public
 	 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 	 * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 	var React = __webpack_require__(2);
-	var ReactDOM = __webpack_require__(22);
+	var ReactDOM = __webpack_require__(31);
 	var dom = React.DOM,
 	    PropTypes = React.PropTypes;
 
 
 	var Draggable = React.createClass({
 	  displayName: "Draggable",
 
 	  propTypes: {
@@ -33160,696 +33784,19 @@ return /******/ (function(modules) { // 
 /***/ },
 /* 913 */
 /***/ function(module, exports) {
 
 	// removed by extract-text-webpack-plugin
 
 /***/ },
 /* 914 */,
-/* 915 */
-/***/ function(module, exports, __webpack_require__) {
-
-	"use strict";
-
-	Object.defineProperty(exports, "__esModule", {
-	  value: true
-	});
-
-	var _react = __webpack_require__(2);
-
-	var _reactRedux = __webpack_require__(151);
-
-	var _redux = __webpack_require__(3);
-
-	var _actions = __webpack_require__(244);
-
-	var _actions2 = _interopRequireDefault(_actions);
-
-	var _selectors = __webpack_require__(242);
-
-	var _utils = __webpack_require__(234);
-
-	var _url = __webpack_require__(334);
-
-	var _source = __webpack_require__(233);
-
-	__webpack_require__(917);
-
-	var _Autocomplete2 = __webpack_require__(342);
-
-	var _Autocomplete3 = _interopRequireDefault(_Autocomplete2);
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-	var Autocomplete = (0, _react.createFactory)(_Autocomplete3.default);
-
-	function searchResults(sources) {
-	  function getSourcePath(source) {
-	    var _parseURL = (0, _url.parse)(source.get("url")),
-	        path = _parseURL.path,
-	        href = _parseURL.href;
-	    // for URLs like "about:home" the path is null so we pass the full href
-
-
-	    return path || href;
-	  }
-
-	  return sources.valueSeq().filter(source => !(0, _source.isPretty)(source.toJS()) && source.get("url")).map(source => ({
-	    value: getSourcePath(source),
-	    title: getSourcePath(source).split("/").pop(),
-	    subtitle: (0, _utils.endTruncateStr)(getSourcePath(source), 100),
-	    id: source.get("id")
-	  })).toJS();
-	}
-
-	class ProjectSearch extends _react.Component {
-
-	  constructor(props) {
-	    super(props);
-
-	    this.state = {
-	      inputValue: ""
-	    };
-
-	    this.toggle = this.toggle.bind(this);
-	    this.onEscape = this.onEscape.bind(this);
-	    this.close = this.close.bind(this);
-	  }
-
-	  componentWillUnmount() {
-	    var shortcuts = this.context.shortcuts;
-	    var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.key2")];
-	    searchKeys.forEach(key => shortcuts.off(key, this.toggle));
-	    shortcuts.off("Escape", this.onEscape);
-	  }
-
-	  componentDidMount() {
-	    var shortcuts = this.context.shortcuts;
-	    var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.alt.key")];
-	    searchKeys.forEach(key => shortcuts.on(key, this.toggle));
-	    shortcuts.on("Escape", this.onEscape);
-	  }
-
-	  toggle(key, e) {
-	    e.preventDefault();
-	    this.props.toggleProjectSearch();
-	  }
-
-	  onEscape(shortcut, e) {
-	    if (this.props.searchOn) {
-	      e.preventDefault();
-	      this.close();
-	    }
-	  }
-
-	  close() {
-	    this.setState({ inputValue: "" });
-	    this.props.toggleProjectSearch(false);
-	  }
-
-	  render() {
-	    if (!this.props.searchOn) {
-	      return null;
-	    }
-
-	    return _react.DOM.div({ className: "search-container" }, Autocomplete({
-	      selectItem: (e, result) => {
-	        this.props.selectSource(result.id);
-	        this.close();
-	      },
-	      close: this.close,
-	      items: searchResults(this.props.sources),
-	      inputValue: this.state.inputValue,
-	      placeholder: L10N.getStr("sourceSearch.search"),
-	      size: "big"
-	    }));
-	  }
-	}
-
-	ProjectSearch.propTypes = {
-	  sources: _react.PropTypes.object.isRequired,
-	  selectSource: _react.PropTypes.func.isRequired,
-	  toggleProjectSearch: _react.PropTypes.func.isRequired,
-	  searchOn: _react.PropTypes.bool
-	};
-
-	ProjectSearch.contextTypes = {
-	  shortcuts: _react.PropTypes.object
-	};
-
-	ProjectSearch.displayName = "ProjectSearch";
-
-	exports.default = (0, _reactRedux.connect)(state => ({
-	  sources: (0, _selectors.getSources)(state),
-	  searchOn: (0, _selectors.getProjectSearchState)(state)
-	}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(ProjectSearch);
-
-/***/ },
-/* 916 */
-/***/ function(module, exports, __webpack_require__) {
-
-	var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/*! https://mths.be/punycode v1.3.2 by @mathias */
-	;(function(root) {
-
-		/** Detect free variables */
-		var freeExports = typeof exports == 'object' && exports &&
-			!exports.nodeType && exports;
-		var freeModule = typeof module == 'object' && module &&
-			!module.nodeType && module;
-		var freeGlobal = typeof global == 'object' && global;
-		if (
-			freeGlobal.global === freeGlobal ||
-			freeGlobal.window === freeGlobal ||
-			freeGlobal.self === freeGlobal
-		) {
-			root = freeGlobal;
-		}
-
-		/**
-		 * The `punycode` object.
-		 * @name punycode
-		 * @type Object
-		 */
-		var punycode,
-
-		/** Highest positive signed 32-bit float value */
-		maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
-
-		/** Bootstring parameters */
-		base = 36,
-		tMin = 1,
-		tMax = 26,
-		skew = 38,
-		damp = 700,
-		initialBias = 72,
-		initialN = 128, // 0x80
-		delimiter = '-', // '\x2D'
-
-		/** Regular expressions */
-		regexPunycode = /^xn--/,
-		regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
-		regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
-
-		/** Error messages */
-		errors = {
-			'overflow': 'Overflow: input needs wider integers to process',
-			'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
-			'invalid-input': 'Invalid input'
-		},
-
-		/** Convenience shortcuts */
-		baseMinusTMin = base - tMin,
-		floor = Math.floor,
-		stringFromCharCode = String.fromCharCode,
-
-		/** Temporary variable */
-		key;
-
-		/*--------------------------------------------------------------------------*/
-
-		/**
-		 * A generic error utility function.
-		 * @private
-		 * @param {String} type The error type.
-		 * @returns {Error} Throws a `RangeError` with the applicable error message.
-		 */
-		function error(type) {
-			throw RangeError(errors[type]);
-		}
-
-		/**
-		 * A generic `Array#map` utility function.
-		 * @private
-		 * @param {Array} array The array to iterate over.
-		 * @param {Function} callback The function that gets called for every array
-		 * item.
-		 * @returns {Array} A new array of values returned by the callback function.
-		 */
-		function map(array, fn) {
-			var length = array.length;
-			var result = [];
-			while (length--) {
-				result[length] = fn(array[length]);
-			}
-			return result;
-		}
-
-		/**
-		 * A simple `Array#map`-like wrapper to work with domain name strings or email
-		 * addresses.
-		 * @private
-		 * @param {String} domain The domain name or email address.
-		 * @param {Function} callback The function that gets called for every
-		 * character.
-		 * @returns {Array} A new string of characters returned by the callback
-		 * function.
-		 */
-		function mapDomain(string, fn) {
-			var parts = string.split('@');
-			var result = '';
-			if (parts.length > 1) {
-				// In email addresses, only the domain name should be punycoded. Leave
-				// the local part (i.e. everything up to `@`) intact.
-				result = parts[0] + '@';
-				string = parts[1];
-			}
-			// Avoid `split(regex)` for IE8 compatibility. See #17.
-			string = string.replace(regexSeparators, '\x2E');
-			var labels = string.split('.');
-			var encoded = map(labels, fn).join('.');
-			return result + encoded;
-		}
-
-		/**
-		 * Creates an array containing the numeric code points of each Unicode
-		 * character in the string. While JavaScript uses UCS-2 internally,
-		 * this function will convert a pair of surrogate halves (each of which
-		 * UCS-2 exposes as separate characters) into a single code point,
-		 * matching UTF-16.
-		 * @see `punycode.ucs2.encode`
-		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
-		 * @memberOf punycode.ucs2
-		 * @name decode
-		 * @param {String} string The Unicode input string (UCS-2).
-		 * @returns {Array} The new array of code points.
-		 */
-		function ucs2decode(string) {
-			var output = [],
-			    counter = 0,
-			    length = string.length,
-			    value,
-			    extra;
-			while (counter < length) {
-				value = string.charCodeAt(counter++);
-				if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
-					// high surrogate, and there is a next character
-					extra = string.charCodeAt(counter++);
-					if ((extra & 0xFC00) == 0xDC00) { // low surrogate
-						output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
-					} else {
-						// unmatched surrogate; only append this code unit, in case the next
-						// code unit is the high surrogate of a surrogate pair
-						output.push(value);
-						counter--;
-					}
-				} else {
-					output.push(value);
-				}
-			}
-			return output;
-		}
-
-		/**
-		 * Creates a string based on an array of numeric code points.
-		 * @see `punycode.ucs2.decode`
-		 * @memberOf punycode.ucs2
-		 * @name encode
-		 * @param {Array} codePoints The array of numeric code points.
-		 * @returns {String} The new Unicode string (UCS-2).
-		 */
-		function ucs2encode(array) {
-			return map(array, function(value) {
-				var output = '';
-				if (value > 0xFFFF) {
-					value -= 0x10000;
-					output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
-					value = 0xDC00 | value & 0x3FF;
-				}
-				output += stringFromCharCode(value);
-				return output;
-			}).join('');
-		}
-
-		/**
-		 * Converts a basic code point into a digit/integer.
-		 * @see `digitToBasic()`
-		 * @private
-		 * @param {Number} codePoint The basic numeric code point value.
-		 * @returns {Number} The numeric value of a basic code point (for use in
-		 * representing integers) in the range `0` to `base - 1`, or `base` if
-		 * the code point does not represent a value.
-		 */
-		function basicToDigit(codePoint) {
-			if (codePoint - 48 < 10) {
-				return codePoint - 22;
-			}
-			if (codePoint - 65 < 26) {
-				return codePoint - 65;
-			}
-			if (codePoint - 97 < 26) {
-				return codePoint - 97;
-			}
-			return base;
-		}
-
-		/**
-		 * Converts a digit/integer into a basic code point.
-		 * @see `basicToDigit()`
-		 * @private
-		 * @param {Number} digit The numeric value of a basic code point.
-		 * @returns {Number} The basic code point whose value (when used for
-		 * representing integers) is `digit`, which needs to be in the range
-		 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
-		 * used; else, the lowercase form is used. The behavior is undefined
-		 * if `flag` is non-zero and `digit` has no uppercase form.
-		 */
-		function digitToBasic(digit, flag) {
-			//  0..25 map to ASCII a..z or A..Z
-			// 26..35 map to ASCII 0..9
-			return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
-		}
-
-		/**
-		 * Bias adaptation function as per section 3.4 of RFC 3492.
-		 * http://tools.ietf.org/html/rfc3492#section-3.4
-		 * @private
-		 */
-		function adapt(delta, numPoints, firstTime) {
-			var k = 0;
-			delta = firstTime ? floor(delta / damp) : delta >> 1;
-			delta += floor(delta / numPoints);
-			for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
-				delta = floor(delta / baseMinusTMin);
-			}
-			return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
-		}
-
-		/**
-		 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
-		 * symbols.
-		 * @memberOf punycode
-		 * @param {String} input The Punycode string of ASCII-only symbols.
-		 * @returns {String} The resulting string of Unicode symbols.
-		 */
-		function decode(input) {
-			// Don't use UCS-2
-			var output = [],
-			    inputLength = input.length,
-			    out,
-			    i = 0,
-			    n = initialN,
-			    bias = initialBias,
-			    basic,
-			    j,
-			    index,
-			    oldi,
-			    w,
-			    k,
-			    digit,
-			    t,
-			    /** Cached calculation results */
-			    baseMinusT;
-
-			// Handle the basic code points: let `basic` be the number of input code
-			// points before the last delimiter, or `0` if there is none, then copy
-			// the first basic code points to the output.
-
-			basic = input.lastIndexOf(delimiter);
-			if (basic < 0) {
-				basic = 0;
-			}
-
-			for (j = 0; j < basic; ++j) {
-				// if it's not a basic code point
-				if (input.charCodeAt(j) >= 0x80) {
-					error('not-basic');
-				}
-				output.push(input.charCodeAt(j));
-			}
-
-			// Main decoding loop: start just after the last delimiter if any basic code
-			// points were copied; start at the beginning otherwise.
-
-			for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
-
-				// `index` is the index of the next character to be consumed.
-				// Decode a generalized variable-length integer into `delta`,
-				// which gets added to `i`. The overflow checking is easier
-				// if we increase `i` as we go, then subtract off its starting
-				// value at the end to obtain `delta`.
-				for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
-
-					if (index >= inputLength) {
-						error('invalid-input');
-					}
-
-					digit = basicToDigit(input.charCodeAt(index++));
-
-					if (digit >= base || digit > floor((maxInt - i) / w)) {
-						error('overflow');
-					}
-
-					i += digit * w;
-					t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
-
-					if (digit < t) {
-						break;
-					}
-
-					baseMinusT = base - t;
-					if (w > floor(maxInt / baseMinusT)) {
-						error('overflow');
-					}
-
-					w *= baseMinusT;
-
-				}
-
-				out = output.length + 1;
-				bias = adapt(i - oldi, out, oldi == 0);
-
-				// `i` was supposed to wrap around from `out` to `0`,
-				// incrementing `n` each time, so we'll fix that now:
-				if (floor(i / out) > maxInt - n) {
-					error('overflow');
-				}
-
-				n += floor(i / out);
-				i %= out;
-
-				// Insert `n` at position `i` of the output
-				output.splice(i++, 0, n);
-
-			}
-
-			return ucs2encode(output);
-		}
-
-		/**
-		 * Converts a string of Unicode symbols (e.g. a domain name label) to a
-		 * Punycode string of ASCII-only symbols.
-		 * @memberOf punycode
-		 * @param {String} input The string of Unicode symbols.
-		 * @returns {String} The resulting Punycode string of ASCII-only symbols.
-		 */
-		function encode(input) {
-			var n,
-			    delta,
-			    handledCPCount,
-			    basicLength,
-			    bias,
-			    j,
-			    m,
-			    q,
-			    k,
-			    t,
-			    currentValue,
-			    output = [],
-			    /** `inputLength` will hold the number of code points in `input`. */
-			    inputLength,
-			    /** Cached calculation results */
-			    handledCPCountPlusOne,
-			    baseMinusT,
-			    qMinusT;
-
-			// Convert the input in UCS-2 to Unicode
-			input = ucs2decode(input);
-
-			// Cache the length
-			inputLength = input.length;
-
-			// Initialize the state
-			n = initialN;
-			delta = 0;
-			bias = initialBias;
-
-			// Handle the basic code points
-			for (j = 0; j < inputLength; ++j) {
-				currentValue = input[j];
-				if (currentValue < 0x80) {
-					output.push(stringFromCharCode(currentValue));
-				}
-			}
-
-			handledCPCount = basicLength = output.length;
-
-			// `handledCPCount` is the number of code points that have been handled;
-			// `basicLength` is the number of basic code points.
-
-			// Finish the basic string - if it is not empty - with a delimiter
-			if (basicLength) {
-				output.push(delimiter);
-			}
-
-			// Main encoding loop:
-			while (handledCPCount < inputLength) {
-
-				// All non-basic code points < n have been handled already. Find the next
-				// larger one:
-				for (m = maxInt, j = 0; j < inputLength; ++j) {
-					currentValue = input[j];
-					if (currentValue >= n && currentValue < m) {
-						m = currentValue;
-					}
-				}
-
-				// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
-				// but guard against overflow
-				handledCPCountPlusOne = handledCPCount + 1;
-				if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
-					error('overflow');
-				}
-
-				delta += (m - n) * handledCPCountPlusOne;
-				n = m;
-
-				for (j = 0; j < inputLength; ++j) {
-					currentValue = input[j];
-
-					if (currentValue < n && ++delta > maxInt) {
-						error('overflow');
-					}
-
-					if (currentValue == n) {
-						// Represent delta as a generalized variable-length integer
-						for (q = delta, k = base; /* no condition */; k += base) {
-							t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
-							if (q < t) {
-								break;
-							}
-							qMinusT = q - t;
-							baseMinusT = base - t;
-							output.push(
-								stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
-							);
-							q = floor(qMinusT / baseMinusT);
-						}
-
-						output.push(stringFromCharCode(digitToBasic(q, 0)));
-						bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
-						delta = 0;
-						++handledCPCount;
-					}
-				}
-
-				++delta;
-				++n;
-
-			}
-			return output.join('');
-		}
-
-		/**
-		 * Converts a Punycode string representing a domain name or an email address
-		 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
-		 * it doesn't matter if you call it on a string that has already been
-		 * converted to Unicode.
-		 * @memberOf punycode
-		 * @param {String} input The Punycoded domain name or email address to
-		 * convert to Unicode.
-		 * @returns {String} The Unicode representation of the given Punycode
-		 * string.
-		 */
-		function toUnicode(input) {
-			return mapDomain(input, function(string) {
-				return regexPunycode.test(string)
-					? decode(string.slice(4).toLowerCase())
-					: string;
-			});
-		}
-
-		/**
-		 * Converts a Unicode string representing a domain name or an email address to
-		 * Punycode. Only the non-ASCII parts of the domain name will be converted,
-		 * i.e. it doesn't matter if you call it with a domain that's already in
-		 * ASCII.
-		 * @memberOf punycode
-		 * @param {String} input The domain name or email address to convert, as a
-		 * Unicode string.
-		 * @returns {String} The Punycode representation of the given domain name or
-		 * email address.
-		 */
-		function toASCII(input) {
-			return mapDomain(input, function(string) {
-				return regexNonASCII.test(string)
-					? 'xn--' + encode(string)
-					: string;
-			});
-		}
-
-		/*--------------------------------------------------------------------------*/
-
-		/** Define the public API */
-		punycode = {
-			/**
-			 * A string representing the current Punycode.js version number.
-			 * @memberOf punycode
-			 * @type String
-			 */
-			'version': '1.3.2',
-			/**
-			 * An object of methods to convert from JavaScript's internal character
-			 * representation (UCS-2) to Unicode code points, and back.
-			 * @see <https://mathiasbynens.be/notes/javascript-encoding>
-			 * @memberOf punycode
-			 * @type Object
-			 */
-			'ucs2': {
-				'decode': ucs2decode,
-				'encode': ucs2encode
-			},
-			'decode': decode,
-			'encode': encode,
-			'toASCII': toASCII,
-			'toUnicode': toUnicode
-		};
-
-		/** Expose `punycode` */
-		// Some AMD build optimizers, like r.js, check for specific condition patterns
-		// like the following:
-		if (
-			true
-		) {
-			!(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
-				return punycode;
-			}.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
-		} else if (freeExports && freeModule) {
-			if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
-				freeModule.exports = punycode;
-			} else { // in Narwhal or RingoJS v0.7.0-
-				for (key in punycode) {
-					punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
-				}
-			}
-		} else { // in Rhino or a web browser
-			root.punycode = punycode;
-		}
-
-	}(this));
-
-	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(51)(module), (function() { return this; }())))
-
-/***/ },
-/* 917 */
-/***/ function(module, exports) {
-
-	// removed by extract-text-webpack-plugin
-
-/***/ },
+/* 915 */,
+/* 916 */,
+/* 917 */,
 /* 918 */,
 /* 919 */
 /***/ function(module, exports) {
 
 	module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1792 1792\"><path d=\"M1395 1184q0 13-10 23l-50 50q-10 10-23 10t-23-10l-393-393-393 393q-10 10-23 10t-23-10l-50-50q-10-10-10-23t10-23l466-466q10-10 23-10t23 10l466 466q10 10 10 23z\" fill=\"#696969\"></path></svg>"
 
 /***/ },
 /* 920 */
@@ -33880,120 +33827,80 @@ return /******/ (function(modules) { // 
 	var _actions = __webpack_require__(244);
 
 	var _actions2 = _interopRequireDefault(_actions);
 
 	var _selectors = __webpack_require__(242);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
-	var _parser = __webpack_require__(827);
-
 	__webpack_require__(922);
 
 	var _previewFunction = __webpack_require__(701);
 
 	var _previewFunction2 = _interopRequireDefault(_previewFunction);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
-
 	class Outline extends _react.Component {
 
 	  constructor(props) {
 	    super(props);
-	    var sourceText = props.sourceText,
-	        isHidden = props.isHidden;
-
 	    this.state = {};
-	    if (!isHidden) {
-	      this.setSymbolDeclarations(sourceText);
-	    }
-	  }
-
-	  componentWillReceiveProps(_ref) {
-	    var sourceText = _ref.sourceText;
-
-	    if (sourceText) {
-	      this.setSymbolDeclarations(sourceText);
-	    }
-	  }
-
-	  // TODO: move this logic out of the component and into a reducer
-	  setSymbolDeclarations(sourceText) {
-	    var _this = this;
-
-	    return _asyncToGenerator(function* () {
-	      var symbolDeclarations = yield (0, _parser.getSymbols)(sourceText.toJS());
-
-	      if (symbolDeclarations !== _this.state.symbolDeclarations) {
-	        _this.setState({
-	          symbolDeclarations
-	        });
-	      }
-	    })();
 	  }
 
 	  selectItem(location) {
 	    var _props = this.props,
 	        selectedSource = _props.selectedSource,
 	        selectSource = _props.selectSource;
 
+	    if (!selectedSource) {
+	      return;
+	    }
 	    var selectedSourceId = selectedSource.get("id");
 	    var startLine = location.start.line;
 	    selectSource(selectedSourceId, { line: startLine });
 	  }
 
 	  renderFunction(func) {
+	    var name = func.name,
+	        location = func.location;
+
+
 	    return _react.DOM.li({
-	      key: func.id,
+	      key: `${name}:${location.start.line}:${location.start.column}`,
 	      className: "outline-list__element",
-	      onClick: () => this.selectItem(func.location)
-	    }, (0, _previewFunction2.default)(func));
+	      onClick: () => this.selectItem(location)
+	    }, (0, _previewFunction2.default)({ name }));
 	  }
 
 	  renderFunctions() {
-	    var symbolDeclarations = this.state.symbolDeclarations;
-
-	    if (!symbolDeclarations) {
-	      return;
-	    }
-
-	    var functions = symbolDeclarations.functions;
-
-
-	    return functions.filter(func => func.name != "anonymous").map(func => this.renderFunction(func));
+	    var symbols = this.props.symbols;
+
+
+	    return symbols.functions.filter(func => func.name != "anonymous").map(func => this.renderFunction(func));
 	  }
 
 	  render() {
 	    var isHidden = this.props.isHidden;
 
 	    if (!(0, _devtoolsConfig.isEnabled)("outline")) {
 	      return null;
 	    }
 
 	    return _react.DOM.div({ className: (0, _classnames2.default)("outline", { hidden: isHidden }) }, _react.DOM.ul({ className: "outline-list" }, this.renderFunctions()));
 	  }
 	}
 
-	Outline.propTypes = {
-	  isHidden: _react.PropTypes.bool.isRequired,
-	  sourceText: _react.PropTypes.object,
-	  selectSource: _react.PropTypes.func.isRequired,
-	  selectedSource: _react.PropTypes.object
-	};
-
 	Outline.displayName = "Outline";
 
 	exports.default = (0, _reactRedux.connect)(state => {
 	  var selectedSource = (0, _selectors.getSelectedSource)(state);
-	  var sourceId = selectedSource ? selectedSource.get("id") : null;
 	  return {
-	    sourceText: (0, _selectors.getSourceText)(state, sourceId),
+	    symbols: (0, _selectors.getSymbols)(state, selectedSource && selectedSource.toJS()),
 	    selectedSource
 	  };
 	}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Outline);
 
 /***/ },
 /* 922 */
 /***/ function(module, exports) {
 
@@ -37267,17 +37174,17 @@ return /******/ (function(modules) { // 
 	  rep: wrapRender(GripMap),
 	  supportsObject
 	};
 
 /***/ },
 /* 960 */
 /***/ function(module, exports) {
 
-	module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySourceUrl): This is the text that appears in the\n# context menu to copy the source URL of file open.\ncopySourceUrl=Copy Source Url\n\n# LOCALIZATION NOTE (copySourceUrl.accesskey): Access key to copy the source URL of a file from\n# the context menu.\ncopySourceUrl.accesskey=u\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy Stack Trace\n\n# LOCALIZATION NOTE (copyStackTrace.accesskey): Access key to copy the stack trace data from\n# the context menu.\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step Over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step In %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step Out %S\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event Listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\nsources.search.key2=CmdOrCtrl+P\n\n# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the\n# search for searching all the source files the debugger has seen.\nsources.search.alt.key=CmdOrCtrl+O\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourcesPane.showOutlineTooltip): The text that may appear\n# as a tooltip when hovering over the 'toggle sources' button in\n# left sidebar\nsourcesPane.showSourcesTooltip=Show sources\nsourcesPane.showOutlineTooltip=Show outline\n\n# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\nsourceSearch.search.key2=CmdOrCtrl+F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight\n# the next occurrence of the last search triggered from a source search\nsourceSearch.search.again.key2=CmdOrCtrl+G\n\n# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight\n# the previous occurrence of the last search triggered from a source search\nsourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.deleteAll=Remove all breakpoints\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove Breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call Stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not Paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse Rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand Rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=no results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next Result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous Result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add Breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable Breakpoint\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable Breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove Breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit Breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add Conditional Breakpoint\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\n\n# LOCALIZATION NOTE (framework.disableGrouping): This is the text that appears in the\n# context menu to disable framework grouping.\nframework.disableGrouping=Disable Framework Grouping\n\n# LOCALIZATION NOTE (framework.disableGrouping.accesskey): Access key to toggle\n# framework grouping from the context menu.\nframework.disableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (framework.enableGrouping): This is the text that appears in the\n# context menu to enable framework grouping.\nframework.enableGrouping=Enable Framework Grouping\n\n# LOCALIZATION NOTE (framework.enableGrouping.accesskey): Access key to toggle\n# framework grouping from the context menu.\nframework.enableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add Watch Expression\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.closeTab.accesskey): Access key to close the currently select\n# source tab from the editor context menu item.\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close others\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs.accesskey): Access key to close other source tabs\n# from the editor context menu.\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd.accesskey): Access key to close source tabs\n# after the selected tab from the editor context menu.\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs.accesskey): Access key to close all tabs from the\n# editor context menu.\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in Tree\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree.accesskey): Access key to reveal a source in the\n# tree from the context menu.\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.copyLink): Editor source tab context menu item\n# for copying a link address.\nsourceTabs.copyLink=Copy Link Address\n\n# LOCALIZATION NOTE (sourceTabs.copyLink.accesskey): Access key to copy a link addresss from the\n# editor context menu.\nsourceTabs.copyLink.accesskey=l\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty Print Source\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint.accesskey): Access key to pretty print a source from\n# the editor context menu.\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.unblackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.blackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed Source\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.newTabButtonTooltip): The tooltip that is displayed for\n# new tab button in source tabs.\nsourceTabs.newTabButtonTooltip=Search for sources (%S)\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes Unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not Paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch Expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search Sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults=No files matching %S found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (errorLoadingText2): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText2=Error loading this URL: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\n\n# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for\n# searching for a function or variable\nsymbolSearch.search.key2=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous Sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n"
+	module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySourceUrl): This is the text that appears in the\n# context menu to copy the source URL of file open.\ncopySourceUrl=Copy Source Url\n\n# LOCALIZATION NOTE (copySourceUrl.accesskey): Access key to copy the source URL of a file from\n# the context menu.\ncopySourceUrl.accesskey=u\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy Stack Trace\n\n# LOCALIZATION NOTE (copyStackTrace.accesskey): Access key to copy the stack trace data from\n# the context menu.\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step Over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step In %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step Out %S\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event Listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\nsources.search.key2=CmdOrCtrl+P\n\n# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the\n# search for searching all the source files the debugger has seen.\nsources.search.alt.key=CmdOrCtrl+O\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourcesPane.showSourcesTooltip): The tooltip shown when\n# the user will navigate to the source tree view.\nsourcesPane.showSourcesTooltip=Show sources\n\n# LOCALIZATION NOTE (sourcesPane.showOutlineTooltip): The tooltip shown when\n# the user will navigate to the source outline view.\nsourcesPane.showOutlineTooltip=Show outline\n\n# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\nsourceSearch.search.key2=CmdOrCtrl+F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight\n# the next occurrence of the last search triggered from a source search\nsourceSearch.search.again.key2=CmdOrCtrl+G\n\n# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight\n# the previous occurrence of the last search triggered from a source search\nsourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.deleteAll=Remove all breakpoints\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove Breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call Stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not Paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse Rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand Rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=no results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next Result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous Result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add Breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable Breakpoint\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable Breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove Breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit Breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add Conditional Breakpoint\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\n\n# LOCALIZATION NOTE (framework.disableGrouping): This is the text that appears in the\n# context menu to disable framework grouping.\nframework.disableGrouping=Disable Framework Grouping\n\n# LOCALIZATION NOTE (framework.disableGrouping.accesskey): Access key to toggle\n# framework grouping from the context menu.\nframework.disableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (framework.enableGrouping): This is the text that appears in the\n# context menu to enable framework grouping.\nframework.enableGrouping=Enable Framework Grouping\n\n# LOCALIZATION NOTE (framework.enableGrouping.accesskey): Access key to toggle\n# framework grouping from the context menu.\nframework.enableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add Watch Expression\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.closeTab.accesskey): Access key to close the currently select\n# source tab from the editor context menu item.\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close others\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs.accesskey): Access key to close other source tabs\n# from the editor context menu.\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd.accesskey): Access key to close source tabs\n# after the selected tab from the editor context menu.\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs.accesskey): Access key to close all tabs from the\n# editor context menu.\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in Tree\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree.accesskey): Access key to reveal a source in the\n# tree from the context menu.\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.copyLink): Editor source tab context menu item\n# for copying a link address.\nsourceTabs.copyLink=Copy Link Address\n\n# LOCALIZATION NOTE (sourceTabs.copyLink.accesskey): Access key to copy a link addresss from the\n# editor context menu.\nsourceTabs.copyLink.accesskey=l\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty Print Source\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint.accesskey): Access key to pretty print a source from\n# the editor context menu.\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox Source\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.unblackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackbox.accesskey): Access key to blackbox\n# an associated source\nsourceFooter.blackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed Source\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (sourceTabs.newTabButtonTooltip): The tooltip that is displayed for\n# new tab button in source tabs.\nsourceTabs.newTabButtonTooltip=Search for sources (%S)\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes Unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not Paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch Expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search Sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults=No files matching %S found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (errorLoadingText2): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText2=Error loading this URL: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\n\n# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for\n# searching for a function or variable\nsymbolSearch.search.key2=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous Sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n"
 
 /***/ },
 /* 961 */,
 /* 962 */,
 /* 963 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var baseKeys = __webpack_require__(217),
@@ -46468,16 +46375,20 @@ return /******/ (function(modules) { // 
 	}
 
 /***/ },
 /* 994 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
 
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 	var SourceEditor = __webpack_require__(995);
 	var SourceEditorUtils = __webpack_require__(996);
 
 	module.exports = { SourceEditor, SourceEditorUtils };
 
 /***/ },
 /* 995 */
 /***/ function(module, exports) {
@@ -46485,16 +46396,20 @@ return /******/ (function(modules) { // 
 	module.exports = __WEBPACK_EXTERNAL_MODULE_995__;
 
 /***/ },
 /* 996 */
 /***/ function(module, exports) {
 
 	"use strict";
 
+	/* This Source Code Form is subject to the terms of the Mozilla Public
+	 * License, v. 2.0. If a copy of the MPL was not distributed with this
+	 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 	function forEachLine(codeMirror, iter) {
 	  codeMirror.doc.iter(0, codeMirror.lineCount(), iter);
 	}
 
 	function removeLineClass(codeMirror, line, className) {
 	  codeMirror.removeLineClass(line, "line", className);
 	}
 
@@ -47182,31 +47097,33 @@ return /******/ (function(modules) { // 
 	Object.defineProperty(exports, "__esModule", {
 	  value: true
 	});
 
 	var _react = __webpack_require__(2);
 
 	var _devtoolsConfig = __webpack_require__(828);
 
+	var _reactDom = __webpack_require__(31);
+
+	var _reactDom2 = _interopRequireDefault(_reactDom);
+
 	var _Svg = __webpack_require__(344);
 
 	var _Svg2 = _interopRequireDefault(_Svg);
 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	var ReactDOM = __webpack_require__(22);
-
-
 	var breakpointSvg = document.createElement("span");
-	ReactDOM.render((0, _Svg2.default)("column-breakpoint"), breakpointSvg);
+
+	_reactDom2.default.render((0, _Svg2.default)("column-breakpoint"), breakpointSvg);
 
 	function makeBookmark(isDisabled) {
 	  var bp = breakpointSvg.cloneNode(true);
 	  bp.className = (0, _classnames2.default)("editor column-breakpoint", {
 	    "breakpoint-disabled": isDisabled
 	  });
 
 	  return bp;
@@ -47411,17 +47328,17 @@ return /******/ (function(modules) { // 
 	      key: frameOrGroup[0].id
 	    })));
 	  }
 
 	  renderToggleButton(frames) {
 	    var buttonMessage = this.state.showAllFrames ? L10N.getStr("callStack.collapse") : L10N.getStr("callStack.expand");
 
 	    frames = (0, _frame.collapseFrames)(frames);
-	    if (frames.length < NUM_FRAMES_SHOWN) {
+	    if (frames.length <= NUM_FRAMES_SHOWN) {
 	      return null;
 	    }
 
 	    return _react.DOM.div({ className: "show-more", onClick: this.toggleFramesDisplay }, buttonMessage);
 	  }
 
 	  render() {
 	    var frames = this.props.frames;
@@ -48235,35 +48152,23 @@ return /******/ (function(modules) { // 
 	var _classnames = __webpack_require__(175);
 
 	var _classnames2 = _interopRequireDefault(_classnames);
 
 	__webpack_require__(1030);
 
 	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-	var BracketArrow = (_ref) => {
-	  var orientation = _ref.orientation,
-	      left = _ref.left,
-	      top = _ref.top,
-	      bottom = _ref.bottom;
-
+	var BracketArrow = (orientation, left, top, bottom) => {
 	  return _react.DOM.div({
 	    className: (0, _classnames2.default)("bracket-arrow", orientation || "up"),
 	    style: { left, top, bottom }
 	  }, "");
 	};
 
-	BracketArrow.propTypes = {
-	  orientation: _react.PropTypes.string,
-	  left: _react.PropTypes.number,
-	  top: _react.PropTypes.number,
-	  bottom: _react.PropTypes.number
-	};
-
 	BracketArrow.displayName = "BracketArrow";
 
 	exports.default = BracketArrow;
 
 /***/ },
 /* 1030 */
 /***/ function(module, exports) {
 
@@ -48826,12 +48731,872 @@ return /******/ (function(modules) { // 
 	  }
 	  return string.match(pattern) || [];
 	}
 
 	module.exports = kebabCase;
 
 	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
 
+/***/ },
+/* 1049 */,
+/* 1050 */,
+/* 1051 */,
+/* 1052 */,
+/* 1053 */,
+/* 1054 */,
+/* 1055 */,
+/* 1056 */,
+/* 1057 */
+/***/ function(module, exports) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+
+	var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+	exports.firstString = firstString;
+	exports.locationMoved = locationMoved;
+	exports.makeLocationId = makeLocationId;
+	exports.makePendingLocationId = makePendingLocationId;
+	exports.allBreakpointsDisabled = allBreakpointsDisabled;
+	exports.equalizeLocationColumn = equalizeLocationColumn;
+	// Return the first argument that is a string, or null if nothing is a
+	// string.
+	function firstString() {
+	  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+	    args[_key] = arguments[_key];
+	  }
+
+	  for (var arg of args) {
+	    if (typeof arg === "string") {
+	      return arg;
+	    }
+	  }
+	  return null;
+	}
+
+	function locationMoved(location, newLocation) {
+	  return location.line !== newLocation.line || location.column != null && location.column !== newLocation.column;
+	}
+
+	function makeLocationId(location) {
+	  var sourceId = location.sourceId,
+	      line = location.line,
+	      column = location.column;
+
+	  var columnString = column || "";
+	  return `${sourceId}:${line}:${columnString}`;
+	}
+
+	function makePendingLocationId(location) {
+	  var sourceUrl = location.sourceUrl,
+	      line = location.line,
+	      column = location.column;
+
+	  var sourceUrlString = sourceUrl || "";
+	  var columnString = column || "";
+	  return `${sourceUrlString}:${line}:${columnString}`;
+	}
+
+	function allBreakpointsDisabled(state) {
+	  return state.breakpoints.every(x => x.disabled);
+	}
+
+	// syncing
+	function equalizeLocationColumn(location, hasColumn) {
+	  if (hasColumn) {
+	    return location;
+	  }
+	  return _extends({}, location, { column: undefined });
+	}
+
+/***/ },
+/* 1058 */
+/***/ function(module, exports, __webpack_require__) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+	exports.initialState = initialState;
+	exports.getSymbols = getSymbols;
+	exports.hasSymbols = hasSymbols;
+	exports.getOutOfScopeLocations = getOutOfScopeLocations;
+	exports.getSelection = getSelection;
+
+	var _immutable = __webpack_require__(146);
+
+	var I = _interopRequireWildcard(_immutable);
+
+	var _makeRecord = __webpack_require__(230);
+
+	var _makeRecord2 = _interopRequireDefault(_makeRecord);
+
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+	/**
+	 * UI reducer
+	 * @module reducers/ui
+	 */
+
+	function initialState() {
+	  return (0, _makeRecord2.default)({
+	    symbols: I.Map(),
+	    outOfScopeLocations: null,
+	    selection: null
+	  })();
+	}
+
+	function update() {
+	  var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState();
+	  var action = arguments[1];
+
+	  switch (action.type) {
+	    case "SET_SYMBOLS":
+	      {
+	        var source = action.source,
+	            _symbols = action.symbols;
+
+	        return state.setIn(["symbols", source.id], _symbols);
+	      }
+
+	    case "OUT_OF_SCOPE_LOCATIONS":
+	      {
+	        return state.set("outOfScopeLocations", action.locations);
+	      }
+
+	    case "CLEAR_SELECTION":
+	      {
+	        return state.set("selection", null);
+	      }
+
+	    case "SET_SELECTION":
+	      {
+	        if (action.status == "start") {
+	          return state.set("selection", { updating: true });
+	        }
+
+	        if (!action.value) {
+	          return state.set("selection", null);
+	        }
+
+	        var _action$value = action.value,
+	            expression = _action$value.expression,
+	            location = _action$value.location,
+	            result = _action$value.result;
+
+	        return state.set("selection", {
+	          updating: false,
+	          expression,
+	          location,
+	          result
+	        });
+	      }
+
+	    default:
+	      {
+	        return state;
+	      }
+	  }
+	}
+
+	// NOTE: we'd like to have the app state fully typed
+	// https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js#L179-L185
+	function getSymbols(state, source) {
+	  var emptySet = { variables: [], functions: [] };
+	  if (!source) {
+	    return emptySet;
+	  }
+
+	  var symbols = state.ast.getIn(["symbols", source.id]);
+	  return symbols || emptySet;
+	}
+
+	function hasSymbols(state, source) {
+	  if (!source) {
+	    return false;
+	  }
+
+	  return !!state.ast.getIn(["symbols", source.id]);
+	}
+
+	function getOutOfScopeLocations(state) {
+	  return state.ast.get("outOfScopeLocations");
+	}
+
+	function getSelection(state) {
+	  return state.ast.get("selection");
+	}
+
+	exports.default = update;
+
+/***/ },
+/* 1059 */
+/***/ function(module, exports, __webpack_require__) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+	exports.setSymbols = setSymbols;
+	exports.setOutOfScopeLocations = setOutOfScopeLocations;
+	exports.clearSelection = clearSelection;
+	exports.setSelection = setSelection;
+
+	var _selectors = __webpack_require__(242);
+
+	var _promise = __webpack_require__(193);
+
+	var _parser = __webpack_require__(827);
+
+	var _parser2 = _interopRequireDefault(_parser);
+
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
+
+	function setSymbols(source) {
+	  return (() => {
+	    var _ref = _asyncToGenerator(function* (_ref2) {
+	      var dispatch = _ref2.dispatch,
+	          getState = _ref2.getState;
+
+	      if ((0, _selectors.hasSymbols)(getState(), source)) {
+	        return;
+	      }
+
+	      var sourceText = (0, _selectors.getSourceText)(getState(), source.id);
+	      if (!sourceText) {
+	        return;
+	      }
+
+	      var symbols = yield _parser2.default.getSymbols(sourceText.toJS());
+
+	      dispatch({
+	        type: "SET_SYMBOLS",
+	        source,
+	        symbols
+	      });
+	    });
+
+	    return function (_x) {
+	      return _ref.apply(this, arguments);
+	    };
+	  })();
+	}
+
+	function setOutOfScopeLocations() {
+	  return (() => {
+	    var _ref3 = _asyncToGenerator(function* (_ref4) {
+	      var dispatch = _ref4.dispatch,
+	          getState = _ref4.getState;
+
+	      var location = (0, _selectors.getSelectedLocation)(getState());
+	      var sourceText = (0, _selectors.getSourceText)(getState(), location.sourceId);
+
+	      if (!location.line || !sourceText) {
+	        return dispatch({
+	          type: "OUT_OF_SCOPE_LOCATIONS",
+	          locations: null
+	        });
+	      }
+
+	      var locations = yield _parser2.default.getOutOfScopeLocations(sourceText.toJS(), location);
+
+	      return dispatch({
+	        type: "OUT_OF_SCOPE_LOCATIONS",
+	        locations
+	      });
+	    });
+
+	    return function (_x2) {
+	      return _ref3.apply(this, arguments);
+	    };
+	  })();
+	}
+
+	function clearSelection() {
+	  return (_ref5) => {
+	    var dispatch = _ref5.dispatch,
+	        getState = _ref5.getState,
+	        client = _ref5.client;
+
+	    var currentSelection = (0, _selectors.getSelection)(getState());
+	    if (!currentSelection) {
+	      return;
+	    }
+
+	    return dispatch({
+	      type: "CLEAR_SELECTION"
+	    });
+	  };
+	}
+
+	function setSelection(token, position) {
+	  return (() => {
+	    var _ref6 = _asyncToGenerator(function* (_ref7) {
+	      var dispatch = _ref7.dispatch,
+	          getState = _ref7.getState,
+	          client = _ref7.client;
+
+	      var currentSelection = (0, _selectors.getSelection)(getState());
+	      if (currentSelection && currentSelection.updating) {
+	        return;
+	      }
+
+	      var sourceText = (0, _selectors.getSelectedSourceText)(getState());
+	      var selectedFrame = (0, _selectors.getSelectedFrame)(getState());
+
+	      yield dispatch({
+	        type: "SET_SELECTION",
+	        [_promise.PROMISE]: _asyncToGenerator(function* () {
+	          var closestExpression = yield _parser2.default.getClosestExpression(sourceText.toJS(), token, position);
+
+	          if (!closestExpression) {
+	            return;
+	          }
+
+	          var expression = closestExpression.expression,
+	              location = closestExpression.location;
+
+
+	          if (!expression) {
+	            return;
+	          }
+
+	          var _ref9 = yield client.evaluate(expression, {
+	            frameId: selectedFrame.id
+	          }),
+	              result = _ref9.result;
+
+	          return {
+	            expression,
+	            result,
+	            location
+	          };
+	        })()
+	      });
+	    });
+
+	    return function (_x3) {
+	      return _ref6.apply(this, arguments);
+	    };
+	  })();
+	}
+
+/***/ },
+/* 1060 */
+/***/ function(module, exports, __webpack_require__) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+
+	var _react = __webpack_require__(2);
+
+	var _reactRedux = __webpack_require__(151);
+
+	var _redux = __webpack_require__(3);
+
+	var _actions = __webpack_require__(244);
+
+	var _actions2 = _interopRequireDefault(_actions);
+
+	var _selectors = __webpack_require__(242);
+
+	var _utils = __webpack_require__(234);
+
+	var _url = __webpack_require__(334);
+
+	var _source = __webpack_require__(233);
+
+	var _devtoolsConfig = __webpack_require__(828);
+
+	var _projectSearch = __webpack_require__(1061);
+
+	__webpack_require__(1062);
+
+	var _Autocomplete2 = __webpack_require__(342);
+
+	var _Autocomplete3 = _interopRequireDefault(_Autocomplete2);
+
+	var _TextSearch2 = __webpack_require__(1064);
+
+	var _TextSearch3 = _interopRequireDefault(_TextSearch2);
+
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	var Autocomplete = (0, _react.createFactory)(_Autocomplete3.default);
+
+	var TextSearch = (0, _react.createFactory)(_TextSearch3.default);
+
+	function getSourcePath(source) {
+	  if (!source.url) {
+	    return "";
+	  }
+
+	  var _parseURL = (0, _url.parse)(source.url),
+	      path = _parseURL.path,
+	      href = _parseURL.href;
+	  // for URLs like "about:home" the path is null so we pass the full href
+
+
+	  return path || href;
+	}
+
+	function searchResults(sources, query) {
+	  if ((0, _devtoolsConfig.isEnabled)("projectTextSearch")) {
+	    return projectSearchResults(sources, query);
+	  }
+
+	  return fileSearchResults(sources);
+	}
+
+	function fileSearchResults(sourceMap) {
+	  return sourceMap.valueSeq().toJS().filter(source => !(0, _source.isPretty)(source)).map(source => ({
+	    value: getSourcePath(source),
+	    title: getSourcePath(source).split("/").pop(),
+	    subtitle: (0, _utils.endTruncateStr)(getSourcePath(source), 100),
+	    id: source.id
+	  }));
+	}
+
+	function projectSearchResults(sources, query) {
+	  return sources.valueSeq().toJS().map(source => (0, _projectSearch.searchSource)(source, query).map(result => ({
+	    value: result.match,
+	    title: result.text,
+	    subtitle: (0, _utils.endTruncateStr)(getSourcePath(source), 100),
+	    id: `${result.text}/${result.line}/${result.column}`
+	  })));
+	}
+
+	class ProjectSearch extends _react.Component {
+
+	  constructor(props) {
+	    super(props);
+
+	    this.state = {
+	      inputValue: ""
+	    };
+
+	    this.toggle = this.toggle.bind(this);
+	    this.onEscape = this.onEscape.bind(this);
+	    this.close = this.close.bind(this);
+	  }
+
+	  componentWillUnmount() {
+	    var shortcuts = this.context.shortcuts;
+	    var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.key2")];
+	    searchKeys.forEach(key => shortcuts.off(key, this.toggle));
+	    shortcuts.off("Escape", this.onEscape);
+	  }
+
+	  componentDidMount() {
+	    var shortcuts = this.context.shortcuts;
+	    var searchKeys = [L10N.getStr("sources.search.key2"), L10N.getStr("sources.search.alt.key")];
+	    searchKeys.forEach(key => shortcuts.on(key, this.toggle));
+	    shortcuts.on("Escape", this.onEscape);
+	  }
+
+	  toggle(key, e) {
+	    e.preventDefault();
+	    this.props.toggleProjectSearch();
+	  }
+
+	  onEscape(shortcut, e) {
+	    if (this.props.searchOn) {
+	      e.preventDefault();
+	      this.close();
+	    }
+	  }
+
+	  close() {
+	    this.setState({ inputValue: "" });
+	    this.props.toggleProjectSearch(false);
+	  }
+
+	  renderFileSearch() {
+	    return Autocomplete({
+	      selectItem: (e, result) => {
+	        this.props.selectSource(result.id);
+	        this.close();
+	      },
+	      close: this.close,
+	      items: searchResults(this.props.sources, this.state.inputValue),
+	      inputValue: this.state.inputValue,
+	      placeholder: L10N.getStr("sourceSearch.search"),
+	      size: "big"
+	    });
+	  }
+
+	  renderTextSearch() {
+	    var sources = this.props.sources;
+
+
+	    return TextSearch({
+	      sources
+	    });
+	  }
+
+	  render() {
+	    var searchOn = this.props.searchOn;
+
+	    if (!searchOn) {
+	      return null;
+	    }
+
+	    return _react.DOM.div({ className: "search-container" }, (0, _devtoolsConfig.isEnabled)("projectTextSearch") ? this.renderTextSearch() : this.renderFileSearch());
+	  }
+	}
+
+	ProjectSearch.propTypes = {
+	  sources: _react.PropTypes.object.isRequired,
+	  selectSource: _react.PropTypes.func.isRequired,
+	  toggleProjectSearch: _react.PropTypes.func.isRequired,
+	  searchOn: _react.PropTypes.bool
+	};
+
+	ProjectSearch.contextTypes = {
+	  shortcuts: _react.PropTypes.object
+	};
+
+	ProjectSearch.displayName = "ProjectSearch";
+
+	exports.default = (0, _reactRedux.connect)(state => ({
+	  sources: (0, _selectors.getSources)(state),
+	  searchOn: (0, _selectors.getProjectSearchState)(state)
+	}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(ProjectSearch);
+
+/***/ },
+/* 1061 */
+/***/ function(module, exports) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+	exports.searchSource = searchSource;
+
+	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+	function searchSource(source, queryText) {
+	  var _ref;
+
+	  var text = source.text,
+	      id = source.id,
+	      url = source.url,
+	      loading = source.loading;
+
+	  if (loading || !text || queryText == "") {
+	    return [];
+	  }
+
+	  var lines = text.split("\n");
+	  var result = undefined;
+	  var query = new RegExp(queryText, "g");
+
+	  var matches = lines.map((_text, line) => {
+	    var indices = [];
+
+	    while (result = query.exec(_text)) {
+	      indices.push({
+	        line: line + 1,
+	        column: result.index,
+	        match: result[0],
+	        text: result.input,
+	        id,
+	        url
+	      });
+	    }
+	    return indices;
+	  }).filter(_matches => _matches.length > 0);
+
+	  matches = (_ref = []).concat.apply(_ref, _toConsumableArray(matches));
+	  return matches;
+	}
+
+/***/ },
+/* 1062 */
+/***/ function(module, exports) {
+
+	// removed by extract-text-webpack-plugin
+
+/***/ },
+/* 1063 */,
+/* 1064 */
+/***/ function(module, exports, __webpack_require__) {
+
+	"use strict";
+
+	Object.defineProperty(exports, "__esModule", {
+	  value: true
+	});
+
+	var _react = __webpack_require__(2);
+
+	var _classnames = __webpack_require__(175);
+
+	var _classnames2 = _interopRequireDefault(_classnames);
+
+	var _Svg = __webpack_require__(344);
+
+	var _Svg2 = _interopRequireDefault(_Svg);
+
+	var _ManagedTree2 = __webpack_require__(419);
+
+	var _ManagedTree3 = _interopRequireDefault(_ManagedTree2);
+
+	__webpack_require__(1065);
+
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+	var ManagedTree = (0, _react.createFactory)(_ManagedTree3.default);
+
+	class TextSearch extends _react.Component {
+	  constructor(props) {
+	    super(props);
+	  }
+
+	  renderFile(file, expanded) {
+	    return _react.DOM.div({
+	      className: "file-result"
+	    }, (0, _Svg2.default)("arrow", {
+	      className: (0, _classnames2.default)({
+	        expanded
+	      })
+	    }), file.filepath);
+	  }
+
+	  renderLine(match) {
+	    return _react.DOM.div({ className: "result" }, _react.DOM.span({
+	      className: "line-number"
+	    }, match.line), _react.DOM.span({ className: "line-match" }, match.value));
+	  }
+
+	  renderResults() {
+	    var results = this.props.results;
+
+	    return ManagedTree({
+	      getRoots: () => results,
+	      getChildren: file => {
+	        return file.matches || [];
+	      },
+	      itemHeight: 20,
+	      autoExpand: 1,
+	      autoExpandDepth: 1,
+	      getParent: item => null,
+	      getKey: item => item.filepath || `${item.value}/${item.line}`,
+	      renderItem: (item, depth, focused, _, expanded) => item.filepath ? this.renderFile(item, expanded) : this.renderLine(item)
+	    });
+	  }
+
+	  render() {
+	    return _react.DOM.div({
+	      className: "project-text-search"
+	    }, this.renderResults());
+	  }
+	}
+
+	exports.default = TextSearch;
+	TextSearch.displayName = "TextSearch";
+
+/***/ },
+/* 1065 */
+/***/ function(module, exports) {
+
+	// removed by extract-text-webpack-plugin
+
+/***/ },
+/* 1066 */,
+/* 1067 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseFlatten = __webpack_require__(707),
+	    map = __webpack_require__(1068);
+
+	/**
+	 * Creates a flattened array of values by running each element in `collection`
+	 * thru `iteratee` and flattening the mapped results. The iteratee is invoked
+	 * with three arguments: (value, index|key, collection).
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Collection
+	 * @param {Array|Object} collection The collection to iterate over.
+	 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+	 * @returns {Array} Returns the new flattened array.
+	 * @example
+	 *
+	 * function duplicate(n) {
+	 *   return [n, n];
+	 * }
+	 *
+	 * _.flatMap([1, 2], duplicate);
+	 * // => [1, 1, 2, 2]
+	 */
+	function flatMap(collection, iteratee) {
+	  return baseFlatten(map(collection, iteratee), 1);
+	}
+
+	module.exports = flatMap;
+
+
+/***/ },
+/* 1068 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var arrayMap = __webpack_require__(110),
+	    baseIteratee = __webpack_require__(264),
+	    baseMap = __webpack_require__(1069),
+	    isArray = __webpack_require__(70);
+
+	/**
+	 * Creates an array of values by running each element in `collection` thru
+	 * `iteratee`. The iteratee is invoked with three arguments:
+	 * (value, index|key, collection).
+	 *
+	 * Many lodash methods are guarded to work as iteratees for methods like
+	 * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
+	 *
+	 * The guarded methods are:
+	 * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
+	 * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
+	 * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
+	 * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Collection
+	 * @param {Array|Object} collection The collection to iterate over.
+	 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+	 * @returns {Array} Returns the new mapped array.
+	 * @example
+	 *
+	 * function square(n) {
+	 *   return n * n;
+	 * }
+	 *
+	 * _.map([4, 8], square);
+	 * // => [16, 64]
+	 *
+	 * _.map({ 'a': 4, 'b': 8 }, square);
+	 * // => [16, 64] (iteration order is not guaranteed)
+	 *
+	 * var users = [
+	 *   { 'user': 'barney' },
+	 *   { 'user': 'fred' }
+	 * ];
+	 *
+	 * // The `_.property` iteratee shorthand.
+	 * _.map(users, 'user');
+	 * // => ['barney', 'fred']
+	 */
+	function map(collection, iteratee) {
+	  var func = isArray(collection) ? arrayMap : baseMap;
+	  return func(collection, baseIteratee(iteratee, 3));
+	}
+
+	module.exports = map;
+
+
+/***/ },
+/* 1069 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseEach = __webpack_require__(1070),
+	    isArrayLike = __webpack_require__(220);
+
+	/**
+	 * The base implementation of `_.map` without support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Array|Object} collection The collection to iterate over.
+	 * @param {Function} iteratee The function invoked per iteration.
+	 * @returns {Array} Returns the new mapped array.
+	 */
+	function baseMap(collection, iteratee) {
+	  var index = -1,
+	      result = isArrayLike(collection) ? Array(collection.length) : [];
+
+	  baseEach(collection, function(value, key, collection) {
+	    result[++index] = iteratee(value, key, collection);
+	  });
+	  return result;
+	}
+
+	module.exports = baseMap;
+
+
+/***/ },
+/* 1070 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseForOwn = __webpack_require__(764),
+	    createBaseEach = __webpack_require__(1071);
+
+	/**
+	 * The base implementation of `_.forEach` without support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Array|Object} collection The collection to iterate over.
+	 * @param {Function} iteratee The function invoked per iteration.
+	 * @returns {Array|Object} Returns `collection`.
+	 */
+	var baseEach = createBaseEach(baseForOwn);
+
+	module.exports = baseEach;
+
+
+/***/ },
+/* 1071 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var isArrayLike = __webpack_require__(220);
+
+	/**
+	 * Creates a `baseEach` or `baseEachRight` function.
+	 *
+	 * @private
+	 * @param {Function} eachFunc The function to iterate over a collection.
+	 * @param {boolean} [fromRight] Specify iterating from right to left.
+	 * @returns {Function} Returns the new base function.
+	 */
+	function createBaseEach(eachFunc, fromRight) {
+	  return function(collection, iteratee) {
+	    if (collection == null) {
+	      return collection;
+	    }
+	    if (!isArrayLike(collection)) {
+	      return eachFunc(collection, iteratee);
+	    }
+	    var length = collection.length,
+	        index = fromRight ? length : -1,
+	        iterable = Object(collection);
+
+	    while ((fromRight ? index-- : ++index < length)) {
+	      if (iteratee(iterable[index], index, iterable) === false) {
+	        break;
+	      }
+	    }
+	    return collection;
+	  };
+	}
+
+	module.exports = createBaseEach;
+
+
 /***/ }
 /******/ ])
 });
 ;
\ No newline at end of file
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -3442,19 +3442,149 @@ return /******/ (function(modules) { // 
 	  }
 	  return -1;
 	}
 
 	module.exports = baseFindIndex;
 
 
 /***/ },
-/* 264 */,
-/* 265 */,
-/* 266 */,
+/* 264 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseMatches = __webpack_require__(265),
+	    baseMatchesProperty = __webpack_require__(294),
+	    identity = __webpack_require__(298),
+	    isArray = __webpack_require__(70),
+	    property = __webpack_require__(299);
+
+	/**
+	 * The base implementation of `_.iteratee`.
+	 *
+	 * @private
+	 * @param {*} [value=_.identity] The value to convert to an iteratee.
+	 * @returns {Function} Returns the iteratee.
+	 */
+	function baseIteratee(value) {
+	  // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
+	  // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
+	  if (typeof value == 'function') {
+	    return value;
+	  }
+	  if (value == null) {
+	    return identity;
+	  }
+	  if (typeof value == 'object') {
+	    return isArray(value)
+	      ? baseMatchesProperty(value[0], value[1])
+	      : baseMatches(value);
+	  }
+	  return property(value);
+	}
+
+	module.exports = baseIteratee;
+
+
+/***/ },
+/* 265 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseIsMatch = __webpack_require__(266),
+	    getMatchData = __webpack_require__(291),
+	    matchesStrictComparable = __webpack_require__(293);
+
+	/**
+	 * The base implementation of `_.matches` which doesn't clone `source`.
+	 *
+	 * @private
+	 * @param {Object} source The object of property values to match.
+	 * @returns {Function} Returns the new spec function.
+	 */
+	function baseMatches(source) {
+	  var matchData = getMatchData(source);
+	  if (matchData.length == 1 && matchData[0][2]) {
+	    return matchesStrictComparable(matchData[0][0], matchData[0][1]);
+	  }
+	  return function(object) {
+	    return object === source || baseIsMatch(object, source, matchData);
+	  };
+	}
+
+	module.exports = baseMatches;
+
+
+/***/ },
+/* 266 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var Stack = __webpack_require__(267),
+	    baseIsEqual = __webpack_require__(273);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1,
+	    COMPARE_UNORDERED_FLAG = 2;
+
+	/**
+	 * The base implementation of `_.isMatch` without support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Object} object The object to inspect.
+	 * @param {Object} source The object of property values to match.
+	 * @param {Array} matchData The property names, values, and compare flags to match.
+	 * @param {Function} [customizer] The function to customize comparisons.
+	 * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+	 */
+	function baseIsMatch(object, source, matchData, customizer) {
+	  var index = matchData.length,
+	      length = index,
+	      noCustomizer = !customizer;
+
+	  if (object == null) {
+	    return !length;
+	  }
+	  object = Object(object);
+	  while (index--) {
+	    var data = matchData[index];
+	    if ((noCustomizer && data[2])
+	          ? data[1] !== object[data[0]]
+	          : !(data[0] in object)
+	        ) {
+	      return false;
+	    }
+	  }
+	  while (++index < length) {
+	    data = matchData[index];
+	    var key = data[0],
+	        objValue = object[key],
+	        srcValue = data[1];
+
+	    if (noCustomizer && data[2]) {
+	      if (objValue === undefined && !(key in object)) {
+	        return false;
+	      }
+	    } else {
+	      var stack = new Stack;
+	      if (customizer) {
+	        var result = customizer(objValue, srcValue, key, object, source, stack);
+	      }
+	      if (!(result === undefined
+	            ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)
+	            : result
+	          )) {
+	        return false;
+	      }
+	    }
+	  }
+	  return true;
+	}
+
+	module.exports = baseIsMatch;
+
+
+/***/ },
 /* 267 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var ListCache = __webpack_require__(93),
 	    stackClear = __webpack_require__(268),
 	    stackDelete = __webpack_require__(269),
 	    stackGet = __webpack_require__(270),
 	    stackHas = __webpack_require__(271),
@@ -3603,19 +3733,228 @@ return /******/ (function(modules) { // 
 	  this.size = data.size;
 	  return this;
 	}
 
 	module.exports = stackSet;
 
 
 /***/ },
-/* 273 */,
-/* 274 */,
-/* 275 */,
+/* 273 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var baseIsEqualDeep = __webpack_require__(274),
+	    isObjectLike = __webpack_require__(14);
+
+	/**
+	 * The base implementation of `_.isEqual` which supports partial comparisons
+	 * and tracks traversed objects.
+	 *
+	 * @private
+	 * @param {*} value The value to compare.
+	 * @param {*} other The other value to compare.
+	 * @param {boolean} bitmask The bitmask flags.
+	 *  1 - Unordered comparison
+	 *  2 - Partial comparison
+	 * @param {Function} [customizer] The function to customize comparisons.
+	 * @param {Object} [stack] Tracks traversed `value` and `other` objects.
+	 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+	 */
+	function baseIsEqual(value, other, bitmask, customizer, stack) {
+	  if (value === other) {
+	    return true;
+	  }
+	  if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
+	    return value !== value && other !== other;
+	  }
+	  return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
+	}
+
+	module.exports = baseIsEqual;
+
+
+/***/ },
+/* 274 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var Stack = __webpack_require__(267),
+	    equalArrays = __webpack_require__(275),
+	    equalByTag = __webpack_require__(281),
+	    equalObjects = __webpack_require__(284),
+	    getTag = __webpack_require__(198),
+	    isArray = __webpack_require__(70),
+	    isBuffer = __webpack_require__(210),
+	    isTypedArray = __webpack_require__(212);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1;
+
+	/** `Object#toString` result references. */
+	var argsTag = '[object Arguments]',
+	    arrayTag = '[object Array]',
+	    objectTag = '[object Object]';
+
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+
+	/**
+	 * A specialized version of `baseIsEqual` for arrays and objects which performs
+	 * deep comparisons and tracks traversed objects enabling objects with circular
+	 * references to be compared.
+	 *
+	 * @private
+	 * @param {Object} object The object to compare.
+	 * @param {Object} other The other object to compare.
+	 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+	 * @param {Function} customizer The function to customize comparisons.
+	 * @param {Function} equalFunc The function to determine equivalents of values.
+	 * @param {Object} [stack] Tracks traversed `object` and `other` objects.
+	 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+	 */
+	function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
+	  var objIsArr = isArray(object),
+	      othIsArr = isArray(other),
+	      objTag = objIsArr ? arrayTag : getTag(object),
+	      othTag = othIsArr ? arrayTag : getTag(other);
+
+	  objTag = objTag == argsTag ? objectTag : objTag;
+	  othTag = othTag == argsTag ? objectTag : othTag;
+
+	  var objIsObj = objTag == objectTag,
+	      othIsObj = othTag == objectTag,
+	      isSameTag = objTag == othTag;
+
+	  if (isSameTag && isBuffer(object)) {
+	    if (!isBuffer(other)) {
+	      return false;
+	    }
+	    objIsArr = true;
+	    objIsObj = false;
+	  }
+	  if (isSameTag && !objIsObj) {
+	    stack || (stack = new Stack);
+	    return (objIsArr || isTypedArray(object))
+	      ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
+	      : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
+	  }
+	  if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
+	    var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+	        othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+	    if (objIsWrapped || othIsWrapped) {
+	      var objUnwrapped = objIsWrapped ? object.value() : object,
+	          othUnwrapped = othIsWrapped ? other.value() : other;
+
+	      stack || (stack = new Stack);
+	      return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
+	    }
+	  }
+	  if (!isSameTag) {
+	    return false;
+	  }
+	  stack || (stack = new Stack);
+	  return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
+	}
+
+	module.exports = baseIsEqualDeep;
+
+
+/***/ },
+/* 275 */
+/***/ function(module, exports, __webpack_require__) {
+
+	var SetCache = __webpack_require__(276),
+	    arraySome = __webpack_require__(279),
+	    cacheHas = __webpack_require__(280);
+
+	/** Used to compose bitmasks for value comparisons. */
+	var COMPARE_PARTIAL_FLAG = 1,
+	    COMPARE_UNORDERED_FLAG = 2;
+
+	/**
+	 * A specialized version of `baseIsEqualDeep` for arrays with support for
+	 * partial deep comparisons.
+	 *
+	 * @private
+	 * @param {Array} array The array to compare.
+	 * @param {Array} other The other array to compare.
+	 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+	 * @param {Function} customizer The function to customize comparisons.
+	 * @param {Function} equalFunc The function to determine equivalents of values.
+	 * @param {Object} stack Tracks traversed `array` and `other` objects.
+	 * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+	 */
+	function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
+	  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
+	      arrLength = array.length,
+	      othLength = other.length;
+
+	  if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
+	    return false;
+	  }
+	  // Assume cyclic values are equal.
+	  var stacked = stack.get(array);
+	  if (stacked && stack.get(other)) {
+	    return stacked == other;
+	  }
+	  var index = -1,
+	      result = true,
+	      seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;
+
+	  stack.set(array, other);
+	  stack.set(other, array);
+
+	  // Ignore non-index properties.
+	  while (++index < arrLength) {
+	    var arrValue = array[index],
+	        othValue = other[index];
+
+	    if (customizer) {
+	      var compared = isPartial
+	        ? customizer(othValue, arrValue, index, other, array, stack)
+	        : customizer(arrValue, othValue, index, array, other, stack);
+	    }
+	    if (compared !== undefined) {
+	      if (compared) {
+	        continue;
+	      }
+	      result = false;
+	      break;
+	    }
+	    // Recursively compare arrays (susceptible to call stack limits).
+	    if (seen) {
+	      if (!arraySome(other, function(othValue, othIndex) {
+	            if (!cacheHas(seen, othIndex) &&
+	                (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
+	              return seen.push(othIndex);
+	            }
+	          })) {
+	        result = false;
+	        break;
+	      }
+	    } else if (!(
+	          arrValue === othValue ||
+	            equalFunc(arrValue, othValue, bitmask, customizer, stack)
+	        )) {
+	      result = false;
+	      break;
+	    }
+	  }
+	  stack['delete'](array);
+	  stack['delete'](other);
+	  return result;
+	}
+
+	module.exports = equalArrays;
+
+
+/***/ },
 /* 276 */
 /***/ function(module, exports, __webpack_require__) {
 
 	var MapCache = __webpack_require__(76),
 	    setCacheAdd = __webpack_require__(277),
 	    setCacheHas = __webpack_require__(278);
 
 	/**
@@ -3684,17 +4023,45 @@ return /******/ (function(modules) { // 
 	function setCacheHas(value) {
 	  return this.__data__.has(value);
 	}
 
 	module.exports = setCacheHas;
 
 
 /***/ },
-/* 279 */,
+/* 279 */
+/***/ function(module, exports) {
+
+	/**
+	 * A specialized version of `_.some` for arrays without support for iteratee
+	 * shorthands.
+	 *
+	 * @private
+	 * @param {Array} [array] The array to iterate over.
+	 * @param {Function} predicate The function invoked per iteration.
+	 * @returns {boolean} Returns `true` if any element passes the predicate check,
+	 *  else `false`.
+	 */
+	function arraySome(array, predicate) {
+	  var index = -1,
+	      length = array == null ? 0 : array.length;
+
+	  while (++index < length) {
+	    if (predicate(array[index], index, array)) {
+	      return true;
+	    }
+	  }
+	  return false;
+	}
+
+	module.exports = arraySome;
+
+
+/***/ },
 /* 280 */
 /***/ function(module, exports) {
 
 	/**
 	 * Checks if a `cache` value for `key` exists.
 	 *
 	 * @private
 	 * @param {Object} cache The cache to query.
@@ -3704,17 +4071,134 @@ return /******/ (function(modules) { // 
 	function cacheHas(cache, key) {
 	  return cache.has(key);
 	}