Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Fri, 07 Sep 2018 00:59:25 +0300
changeset 490859 d7e0e17065ca
parent 490858 4903df8facdd (current diff)
parent 490845 a68f7dbde2e7 (diff)
child 490881 ebb99d7564a8
child 490883 ad32f6300ecc
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
d7e0e17065ca / 64.0a1 / 20180906232139 / files
nightly linux64
d7e0e17065ca / 64.0a1 / 20180906232139 / files
nightly mac
d7e0e17065ca / 64.0a1 / 20180906232139 / files
nightly win32
d7e0e17065ca / 64.0a1 / 20180906232139 / files
nightly win64
d7e0e17065ca / 64.0a1 / 20180906232139 / 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 mozilla-central to mozilla-inbound. a=merge CLOSED TREE
browser/base/content/test/plugins/browser_CTP_notificationBar.js
browser/base/content/test/plugins/browser_CTP_remove_navigate.js
browser/base/content/test/plugins/plugin_overlayed.html
browser/base/content/test/plugins/plugin_positioned.html
browser/themes/shared/plugin-doorhanger.inc.css
devtools/client/shared/developer-toolbar.js
devtools/client/shared/test/browser_templater_basic.js
devtools/client/themes/images/gcli_sec_bad.svg
devtools/client/themes/images/gcli_sec_good.svg
devtools/client/themes/images/gcli_sec_moderate.svg
devtools/server/actors/gcli.js
devtools/shared/fronts/gcli.js
devtools/shared/gcli/command-state.js
devtools/shared/gcli/commands/index.js
devtools/shared/gcli/commands/measure.js
devtools/shared/gcli/commands/moz.build
devtools/shared/gcli/commands/paintflashing.js
devtools/shared/gcli/commands/rulers.js
devtools/shared/gcli/commands/screenshot.js
devtools/shared/gcli/moz.build
devtools/shared/gcli/source/LICENSE
devtools/shared/gcli/source/docs/design.md
devtools/shared/gcli/source/docs/developing-gcli.md
devtools/shared/gcli/source/docs/index.md
devtools/shared/gcli/source/docs/running-tests.md
devtools/shared/gcli/source/docs/writing-commands.md
devtools/shared/gcli/source/docs/writing-tests.md
devtools/shared/gcli/source/docs/writing-types.md
devtools/shared/gcli/source/lib/gcli/cli.js
devtools/shared/gcli/source/lib/gcli/commands/clear.js
devtools/shared/gcli/source/lib/gcli/commands/commands.js
devtools/shared/gcli/source/lib/gcli/commands/context.js
devtools/shared/gcli/source/lib/gcli/commands/help.js
devtools/shared/gcli/source/lib/gcli/commands/mocks.js
devtools/shared/gcli/source/lib/gcli/commands/moz.build
devtools/shared/gcli/source/lib/gcli/commands/pref.js
devtools/shared/gcli/source/lib/gcli/commands/preflist.js
devtools/shared/gcli/source/lib/gcli/commands/test.js
devtools/shared/gcli/source/lib/gcli/connectors/connectors.js
devtools/shared/gcli/source/lib/gcli/connectors/moz.build
devtools/shared/gcli/source/lib/gcli/converters/basic.js
devtools/shared/gcli/source/lib/gcli/converters/converters.js
devtools/shared/gcli/source/lib/gcli/converters/html.js
devtools/shared/gcli/source/lib/gcli/converters/moz.build
devtools/shared/gcli/source/lib/gcli/converters/terminal.js
devtools/shared/gcli/source/lib/gcli/fields/delegate.js
devtools/shared/gcli/source/lib/gcli/fields/fields.js
devtools/shared/gcli/source/lib/gcli/fields/moz.build
devtools/shared/gcli/source/lib/gcli/fields/selection.js
devtools/shared/gcli/source/lib/gcli/index.js
devtools/shared/gcli/source/lib/gcli/l10n.js
devtools/shared/gcli/source/lib/gcli/languages/command.html
devtools/shared/gcli/source/lib/gcli/languages/command.js
devtools/shared/gcli/source/lib/gcli/languages/javascript.js
devtools/shared/gcli/source/lib/gcli/languages/languages.js
devtools/shared/gcli/source/lib/gcli/languages/moz.build
devtools/shared/gcli/source/lib/gcli/moz.build
devtools/shared/gcli/source/lib/gcli/mozui/completer.js
devtools/shared/gcli/source/lib/gcli/mozui/inputter.js
devtools/shared/gcli/source/lib/gcli/mozui/moz.build
devtools/shared/gcli/source/lib/gcli/mozui/tooltip.js
devtools/shared/gcli/source/lib/gcli/settings.js
devtools/shared/gcli/source/lib/gcli/system.js
devtools/shared/gcli/source/lib/gcli/types/array.js
devtools/shared/gcli/source/lib/gcli/types/boolean.js
devtools/shared/gcli/source/lib/gcli/types/command.js
devtools/shared/gcli/source/lib/gcli/types/date.js
devtools/shared/gcli/source/lib/gcli/types/delegate.js
devtools/shared/gcli/source/lib/gcli/types/file.js
devtools/shared/gcli/source/lib/gcli/types/fileparser.js
devtools/shared/gcli/source/lib/gcli/types/javascript.js
devtools/shared/gcli/source/lib/gcli/types/moz.build
devtools/shared/gcli/source/lib/gcli/types/node.js
devtools/shared/gcli/source/lib/gcli/types/number.js
devtools/shared/gcli/source/lib/gcli/types/resource.js
devtools/shared/gcli/source/lib/gcli/types/selection.js
devtools/shared/gcli/source/lib/gcli/types/setting.js
devtools/shared/gcli/source/lib/gcli/types/string.js
devtools/shared/gcli/source/lib/gcli/types/types.js
devtools/shared/gcli/source/lib/gcli/types/union.js
devtools/shared/gcli/source/lib/gcli/types/url.js
devtools/shared/gcli/source/lib/gcli/ui/focus.js
devtools/shared/gcli/source/lib/gcli/ui/history.js
devtools/shared/gcli/source/lib/gcli/ui/intro.js
devtools/shared/gcli/source/lib/gcli/ui/menu.css
devtools/shared/gcli/source/lib/gcli/ui/menu.html
devtools/shared/gcli/source/lib/gcli/ui/menu.js
devtools/shared/gcli/source/lib/gcli/ui/moz.build
devtools/shared/gcli/source/lib/gcli/ui/view.js
devtools/shared/gcli/source/lib/gcli/util/domtemplate.js
devtools/shared/gcli/source/lib/gcli/util/fileparser.js
devtools/shared/gcli/source/lib/gcli/util/filesystem.js
devtools/shared/gcli/source/lib/gcli/util/host.js
devtools/shared/gcli/source/lib/gcli/util/l10n.js
devtools/shared/gcli/source/lib/gcli/util/legacy.js
devtools/shared/gcli/source/lib/gcli/util/moz.build
devtools/shared/gcli/source/lib/gcli/util/prism.js
devtools/shared/gcli/source/lib/gcli/util/spell.js
devtools/shared/gcli/source/lib/gcli/util/util.js
devtools/shared/gcli/templater.js
devtools/shared/locales/en-US/gcli.properties
devtools/shared/locales/en-US/gclicommands.properties
devtools/shared/specs/gcli.js
media/ffvpx/libavcodec/audioconvert.c
media/ffvpx/libavcodec/audioconvert.h
media/ffvpx/libavcodec/resample.c
media/ffvpx/libavcodec/resample2.c
media/ffvpx/libavutil/atomic.c
media/ffvpx/libavutil/atomic.h
media/ffvpx/libavutil/atomic_gcc.h
media/ffvpx/libavutil/atomic_win32.h
--- a/.eslintignore
+++ b/.eslintignore
@@ -130,17 +130,16 @@ devtools/shared/preferences/**
 devtools/startup/preferences/devtools-startup.js
 
 # Ignore devtools generated code
 devtools/shared/css/generated/properties-db.js
 
 # Ignore devtools third-party libs
 devtools/shared/jsbeautify/*
 devtools/shared/acorn/*
-devtools/shared/gcli/source/*
 devtools/shared/node-properties/*
 devtools/shared/pretty-fast/*
 devtools/shared/sourcemap/*
 devtools/shared/sprintfjs/*
 devtools/shared/qrcode/decoder/*
 devtools/shared/qrcode/encoder/*
 devtools/client/inspector/markup/test/lib_*
 devtools/client/jsonview/lib/require.js
--- a/browser/actors/PluginChild.jsm
+++ b/browser/actors/PluginChild.jsm
@@ -46,19 +46,16 @@ class PluginChild extends ActorChild {
     this.mm.addEventListener("pageshow", this, {capture: true, mozSystemGroup: true});
   }
 
   receiveMessage(msg) {
     switch (msg.name) {
       case "BrowserPlugins:ActivatePlugins":
         this.activatePlugins(msg.data.pluginInfo, msg.data.newState);
         break;
-      case "BrowserPlugins:NotificationShown":
-        setTimeout(() => this.updateNotificationUI(), 0);
-        break;
       case "BrowserPlugins:ContextMenuCommand":
         switch (msg.data.command) {
           case "play":
             this._showClickToPlayNotification(ContextMenuChild.getTarget(this.mm, msg, "plugin"), true);
             break;
           case "hide":
             this.hideClickToPlayOverlay(ContextMenuChild.getTarget(this.mm, msg, "plugin"));
             break;
@@ -90,17 +87,16 @@ class PluginChild extends ActorChild {
       case "decoder-doctor-notification":
         let data = JSON.parse(aData);
         let type = data.type.toLowerCase();
         if (type == "cannot-play" &&
             this.haveShownNotification &&
             aSubject.top.document == this.content.document &&
             data.formats.toLowerCase().includes("application/x-mpegurl", 0)) {
           this.content.pluginRequiresReload = true;
-          this.updateNotificationUI(this.content.document);
         }
     }
   }
 
   onPageShow(event) {
     // Ignore events that aren't from the main document.
     if (!this.content || event.target != this.content.document) {
       return;
@@ -403,21 +399,16 @@ class PluginChild extends ActorChild {
       return;
     }
 
     if (eventType == "pageshow") {
       this.onPageShow(event);
       return;
     }
 
-    if (eventType == "PluginRemoved") {
-      this.updateNotificationUI(event.target);
-      return;
-    }
-
     if (eventType == "click") {
       this.onOverlayClick(event);
       return;
     }
 
     if (eventType == "PluginCrashed" &&
         !(event.target instanceof Ci.nsIObjectLoadingContent)) {
       // If the event target is not a plugin object (i.e., an <object> or
@@ -511,17 +502,16 @@ class PluginChild extends ActorChild {
     let overlay = this.getPluginUI(plugin, "main");
     if (eventType != "PluginCrashed") {
       if (overlay != null) {
         this.setVisibility(plugin, overlay,
                            this.computeAndAdjustOverlayDisplay(plugin, overlay));
         let resizeListener = () => {
           this.setVisibility(plugin, overlay,
             this.computeAndAdjustOverlayDisplay(plugin, overlay));
-          this.updateNotificationUI();
         };
         plugin.addEventListener("overflow", resizeListener);
         plugin.addEventListener("underflow", resizeListener);
       }
     }
 
     let closeIcon = this.getPluginUI(plugin, "closeIcon");
     if (closeIcon) {
@@ -702,17 +692,16 @@ class PluginChild extends ActorChild {
 
     // If there are no instances of the plugin on the page any more or if we've
     // noted that the content needs to be reloaded due to replacing HLS, what the
     // user probably needs is for us to allow and then refresh.
     if (newState != "block" && newState != "blockalways" && newState != "continueblocking" &&
        (!pluginFound || contentWindow.pluginRequiresReload)) {
       this.reloadPage();
     }
-    this.updateNotificationUI();
   }
 
   _showClickToPlayNotification(plugin, showNow) {
     let plugins = [];
 
     // If plugin is null, that means the user has navigated back to a page with
     // plugins, and we need to collect all the plugins.
     if (plugin === null) {
@@ -768,112 +757,25 @@ class PluginChild extends ActorChild {
 
     this.mm.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
       plugins: [...this.pluginData.values()],
       showNow,
       location,
     }, null, principal);
   }
 
-  /**
-   * Updates the "hidden plugin" notification bar UI.
-   *
-   * @param document (optional)
-   *        Specify the document that is causing the update.
-   *        This is useful when the document is possibly no longer
-   *        the current loaded document (for example, if we're
-   *        responding to a PluginRemoved event for an unloading
-   *        document). If this parameter is omitted, it defaults
-   *        to the current top-level document.
-   */
-  updateNotificationUI(document) {
-    document = document || this.content.document;
-
-    // We're only interested in the top-level document, since that's
-    // the one that provides the Principal that we send back to the
-    // parent.
-    let principal = document.defaultView.top.document.nodePrincipal;
-    let location = document.location.href;
-
-    // Make a copy of the actions from the last popup notification.
-    let haveInsecure = false;
-    let actions = new Map();
-    for (let action of this.pluginData.values()) {
-      switch (action.fallbackType) {
-        // haveInsecure will trigger the red flashing icon and the infobar
-        // styling below
-        case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
-        case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
-          haveInsecure = true;
-          // fall through
-
-        case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET:
-        case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
-          actions.set(action.permissionString, action);
-          continue;
-      }
-    }
-
-    // Remove plugins that are already active, or large enough to show an overlay.
-    let cwu = this.content.windowUtils;
-    for (let plugin of cwu.plugins) {
-      let info = this._getPluginInfo(plugin);
-      if (!actions.has(info.permissionString)) {
-        continue;
-      }
-      let fallbackType = info.fallbackType;
-      if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
-        actions.delete(info.permissionString);
-        if (actions.size == 0) {
-          break;
-        }
-        continue;
-      }
-      if (fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
-          fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET &&
-          fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE &&
-          fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE) {
-        continue;
-      }
-      let overlay = this.getPluginUI(plugin, "main");
-      if (!overlay) {
-        continue;
-      }
-      let overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);
-      this.setVisibility(plugin, overlay, overlayDisplayState);
-      if (overlayDisplayState > OVERLAY_DISPLAY.BLANK) {
-        actions.delete(info.permissionString);
-        if (actions.size == 0) {
-          break;
-        }
-      }
-    }
-
-    // If there are any items remaining in `actions` now, they are hidden
-    // plugins that need a notification bar.
-    this.mm.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
-      haveInsecure,
-      actions: [...actions.values()],
-      location,
-    }, null, principal);
-  }
-
   removeNotification(name) {
     this.mm.sendAsyncMessage("PluginContent:RemoveNotification", { name });
   }
 
   clearPluginCaches() {
     this.pluginData.clear();
     this.pluginCrashData.clear();
   }
 
-  hideNotificationBar(name) {
-    this.mm.sendAsyncMessage("PluginContent:HideNotificationBar", { name });
-  }
-
   /**
    * Determines whether or not the crashed plugin is contained within current
    * full screen DOM element.
    * @param fullScreenElement (DOM element)
    *   The DOM element that is currently full screen, or null.
    * @param domElement
    *   The DOM element which contains the crashed plugin, or the crashed plugin
    *   itself.
@@ -1032,36 +934,28 @@ class PluginChild extends ActorChild {
       overlayDisplayState = this.computeAndAdjustOverlayDisplay(plugin, overlay);
     }
     this.setVisibility(plugin, overlay, overlayDisplayState);
 
     let doc = plugin.ownerDocument;
     let runID = plugin.runID;
 
     if (overlayDisplayState == OVERLAY_DISPLAY.FULL) {
-      // If a previous plugin on the page was too small and resulted in adding a
-      // notification bar, then remove it because this plugin instance it big
-      // enough to serve as in-content notification.
-      this.hideNotificationBar("plugin-crashed");
       doc.mozNoPluginCrashedNotification = true;
 
       // Notify others that the crash reporter UI is now ready.
       // Currently, this event is only used by tests.
       let winUtils = this.content.windowUtils;
       let event = new this.content.CustomEvent("PluginCrashReporterDisplayed", {bubbles: true});
       winUtils.dispatchEventToChromeOnly(plugin, event);
     } else if (!doc.mozNoPluginCrashedNotification) {
       // If another plugin on the page was large enough to show our UI, we don't
       // want to show a notification bar.
       this.mm.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
                                    { messageString: message, pluginID: runID });
-      // Remove the notification when the page is reloaded.
-      doc.defaultView.top.addEventListener("unload", event => {
-        this.hideNotificationBar("plugin-crashed");
-      });
     }
   }
 
   NPAPIPluginCrashReportSubmitted({ runID, state }) {
     this.pluginCrashData.delete(runID);
     let contentWindow = this.content;
     let cwu = contentWindow.windowUtils;
     let plugins = cwu.plugins;
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1535705400394" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1536186868443" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -2363,16 +2363,28 @@
     <emItem blockID="56bd2f99-57eb-4904-840a-23ca155d93ad" id="/^((\{35253b0b-8109-437f-b8fa-d7e690d3bde1\})|(\{0c8d774c-0447-11e7-a3b1-1b43e3911f03\})|(\{c11f85de-0bf8-11e7-9dcd-83433cae2e8e\})|(\{f9f072c8-5357-11e7-bb4c-c37ea2335fb4\})|(\{b6d09408-a35e-11e7-bc48-f3e9438e081e\}))$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="914ec360-d35e-4420-a88f-1bad3513f054" id="/^((application2@fr-metoun\.com)|(application@br-annitop\.com)|(application@br-atoleg\.com)|(application@br-cholty\.com)|(application@br-debozoiz\.com)|(application@br-echite\.com)|(application@br-estracep\.com)|(application@br-exatrom\.com)|(application@br-iginot\.com)|(application@br-imastifi\.com)|(application@br-isobiv\.com)|(application@br-ludimaro\.com)|(application@br-pintoula\.com)|(application@br-proufta\.com)|(application@br-qhirta\.com)|(application@br-qibizar\.com)|(application@br-qopletr\.com)|(application@br-roblaprouf\.com)|(application@br-rosalop\.com)|(application@br-samalag\.com)|(application@br-sopreni\.com)|(application@br-stoumo\.com)|(application@br-villonat\.com)|(application@br-zoobre\.com)|(application@de-barbuna\.com)|(application@de-bicelou\.com)|(application@de-blabuma\.com)|(application@de-dalofir\.com)|(application@de-elplic\.com)|(application@de-erotah\.com)|(application@de-ertuck\.com)|(application@de-eurosty\.com)|(application@de-ezigat\.com)|(application@de-lorelam\.com)|(application@de-losimt\.com)|(application@de-luchil\.com)|(application@de-miligap\.com)|(application@de-open-dog\.com)|(application@de-rydima\.com)|(application@de-slapapi\.com)|(application@de-soqano\.com)|(application@de-treboola\.com)|(application@de-vasurk\.com)|(application@de-ygivas\.com)|(application@es-biloufer\.com)|(application@es-boulass\.com)|(application@es-cemaseur\.com)|(application@es-elixet\.com)|(application@es-gestona\.com)|(application@es-glicalol\.com)|(application@es-griloup\.com)|(application@es-iblep\.com)|(application@es-iglere\.com)|(application@es-jounyl\.com)|(application@es-klepst\.com)|(application@es-nofinaj\.com)|(application@es-ofarnut\.com)|(application@es-phistouquet\.com)|(application@es-pronzal\.com)|(application@es-roterf\.com)|(application@es-taapas\.com)|(application@es-tatoflex\.com)|(application@fr-acomyl\.com)|(application@fr-avortep\.com)|(application@fr-blicac\.com)|(application@fr-bloubil\.com)|(application@fr-carazouco\.com)|(application@fr-cichalou\.com)|(application@fr-consimis\.com)|(application@fr-cropam\.com)|(application@fr-deplitg\.com)|(application@fr-doadoto\.com)|(application@fr-domeoco\.com)|(application@fr-domlaji\.com)|(application@fr-eferif\.com)|(application@fr-eivlot\.com)|(application@fr-eristrass\.com)|(application@fr-ertike\.com)|(application@fr-esiliq\.com)|(application@fr-fedurol\.com)|(application@fr-grilsta\.com)|(application@fr-hyjouco\.com)|(application@fr-intramys\.com)|(application@fr-istrubil\.com)|(application@fr-javelas\.com)|(application@fr-jusftip\.com)|(application@fr-lolaji\.com)|(application@fr-macoulpa\.com)|(application@fr-mareps\.com)|(application@fr-metoun\.com)|(application@fr-metyga\.com)|(application@fr-mimaloy\.com)|(application@fr-monstegou\.com)|(application@fr-oplaff\.com)|(application@fr-ortisul\.com)|(application@fr-pastamicle\.com)|(application@fr-petrlimado\.com)|(application@fr-pinadolada\.com)|(application@fr-raepdi\.com)|(application@fr-soudamo\.com)|(application@fr-stoumo\.com)|(application@fr-stropemer\.com)|(application@fr-tlapel\.com)|(application@fr-tresdumil\.com)|(application@fr-troglit\.com)|(application@fr-troplip\.com)|(application@fr-tropset\.com)|(application@fr-vlouma)|(application@fr-yetras\.com)|(application@fr-zorbil\.com)|(application@fr-zoublet\.com)|(application@it-bipoel\.com)|(application@it-eneude\.com)|(application@it-glucmu\.com)|(application@it-greskof\.com)|(application@it-gripoal\.com)|(application@it-janomirg\.com)|(application@it-lapretofe\.com)|(application@it-oomatie\.com)|(application@it-platoks\.com)|(application@it-plopatic\.com)|(application@it-riploi\.com)|(application@it-sabuf\.com)|(application@it-selbamo\.com)|(application@it-sjilota\.com)|(application@it-stoploco\.com)|(application@it-teryom\.com)|(application@it-tyhfepa\.com)|(application@it-ujdilon\.com)|(application@it-zunelrish\.com)|(application@uk-ablapol\.com)|(application@uk-blamap\.com)|(application@uk-cepamoa\.com)|(application@uk-cloakyz\.com)|(application@uk-crisofil\.com)|(application@uk-donasip\.com)|(application@uk-fanibi\.com)|(application@uk-intramys\.com)|(application@uk-klastaf\.com)|(application@uk-liloust\.com)|(application@uk-logmati\.com)|(application@uk-manulap\.com)|(application@uk-misafou\.com)|(application@uk-nedmaf\.com)|(application@uk-optalme\.com)|(application@uk-plifacil\.com)|(application@uk-poulilax\.com)|(application@uk-rastafroc\.com)|(application@uk-ruflec\.com)|(application@uk-sabrelpt\.com)|(application@uk-sqadipt\.com)|(application@uk-tetsop\.com)|(application@uk-ustif\.com)|(application@uk-vomesq\.com)|(application@uk-vrinotd\.com)|(application@us-estuky\.com)|(application@us-lesgsyo\.com)|(applicationY@search-lesgsyo\.com)|(\{88069ce6-2762-4e02-a994-004b48bd83c1\}))$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="43f11241-88e3-4139-9f02-ac39489a241f" id="/^((\{1cfaec8b-a1cb-4fc5-b139-897a22a71390\})|(\{2ed89659-09c1-4280-9dd7-1daf69272a86\})|(\{5c82f5cc-31f8-4316-bb7d-45a5c05227e6\})|(\{6a98a401-378c-4eac-b93c-da1036a00c6c\})|(\{6d83ebde-6396-483c-b078-57c9d445abfa\})|(\{07efb887-b09f-4028-8f7f-c0036d0485ea\})|(\{36f4882f-ff0b-4865-8674-ef02a937f7da\})|(\{61dea9e9-922d-4218-acdd-cfef0fdf85e7\})|(\{261be583-9695-48e0-bd93-a4feafaa18e6\})|(\{401ae092-6c5c-4771-9a87-a6827be80224\})|(\{534b7a84-9fc6-4d7c-9d67-e3365d2ae088\})|(\{552a949f-6d0e-402d-903d-1550075541ba\})|(\{579b8de8-c461-4301-ab09-695579f9b7c7\})|(\{754d3be3-7337-488e-a5bb-86487e495495\})|(\{2775f69b-75e4-46cb-a5aa-f819624bd9a6\})|(\{41290ec4-b3f0-45ad-b8f3-7bcbca01ed0d\})|(\{0159131f-d76f-4365-81cd-d6831549b90a\})|(\{01527332-1170-4f20-a65b-376e25438f3d\})|(\{760e6ff0-798d-4291-9d5f-12f48ef7658b\})|(\{7e31c21c-156a-4783-b1ce-df0274a89c75\})|(\{8e247308-a68a-4280-b0e2-a14c2f15180a\})|(\{b6d36fe8-eca1-4d85-859e-a4cc74debfed\})|(\{bab0e844-2979-407f-9264-c87ebe279e72\})|(\{d00f78fe-ee73-4589-b120-5723b9a64aa0\})|(\{d59a7294-6c08-4ad5-ba6d-a3bc41851de5\})|(\{d145aa5b-6e66-40cb-8a08-d55a53fc7058\})|(\{d79962e3-4511-4c44-8a40-aed6d32a53b1\})|(\{e3e2a47e-7295-426f-8517-e72c31da3f23\})|(\{e6348f01-841d-419f-8298-93d6adb0b022\})|(\{eb6f8a22-d96e-4727-9167-be68c7d0a7e9\})|(\{fdd72dfe-e10b-468b-8508-4de34f4e95e3\}))$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="67f72634-e170-4860-a5a3-133f160ebc32" id="/^((\{f01a138a-c051-4bc7-a90a-21151ce05755\})|(\{50f78250-63ce-4191-b7c3-e0efc6309b64\})|(\{3d2b2ff4-126b-4874-a57e-ed7dac670230\})|(\{e7c1abd4-ec8e-4519-8f3a-7bd763b8a353\})|(\{4d40bf75-fbe2-45f6-a119-b191c2dd33b0\})|(\{08df7ff2-dee0-453c-b85e-f3369add18ef\}))$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="81eb67a5-3fdb-448c-aadd-5f4d3b7cf281" id="/^((\{686fc9c5-c339-43db-b93a-5181a217f9a6\})|(\{eb4b28c8-7f2d-4327-a00c-40de4299ba44\})|(\{58d735b4-9d6c-4e37-b146-7b9f7e79e318\})|(\{ff608c10-2abc-415c-9fe8-0fdd8e988de8\})|(\{5a8145e2-6cbb-4509-a268-f3121429656c\})|(\{6d451f29-1d6b-4c34-a510-c1234488b0a3\})|(\{de71f09a-3342-48c5-95c1-4b0f17567554\})|(\{df106b04-984e-4e27-97b6-3f3150e98a9e\})|(\{70DE470A-4DC0-11E6-A074-0C08D310C1A8\})|(\{4dcde019-2a1b-499b-a5cd-322828e1279b\})|(\{1ec3563f-1567-49a6-bb5c-75d52334b01c\})|(\{c140c82e-98e6-49fd-ae17-0627e6f7c5e1\})|(\{2581c1f6-5ad9-48d4-8008-4c37dcea1984\})|(\{a2bcc6f7-14f7-4083-b4b0-c335edc68612\})|(\{4c726bb6-a2af-44ed-b498-794cfd8d8838\})|(\{fa6c39a6-cd11-477b-966d-f388f0ba4203\})|(\{26c7bd04-18d3-47f5-aeec-bb54e562acf2\})|(\{7a961c90-2071-4f94-9d9a-d4e3bbf247c0\})|(\{a0481ea2-03f0-4e56-a0e1-030908ecb43e\})|(\{c98fb54e-d25f-43f4-bd72-dfaa736391e2\})|(\{da57263d-adfc-4768-91f7-b3b076c20d63\})|(\{3abb352c-8735-4fb6-9fd6-8117aea3d705\})|(contactus@unzipper\.com)|(\{a1499769-6978-4647-ac0f-78da4652716d\})|(\{581D0A4C-1013-11E7-938B-FCD2A0406E17\})|(\{68feffe4-bfd8-4fc3-8320-8178a3b7aa67\})|(\{823489ae-1bf8-4403-acdd-ea1bdc6431da\})|(\{4c0d11c3-ee81-4f73-a63c-da23d8388abd\})|(\{dc7d2ecc-9cc3-40d7-93ed-ef6f3219bd6f\})|(\{21f29077-6271-46fc-8a79-abaeedb2002b\})|(\{55d15d4d-da76-44ab-95a3-639315be5ef8\})|(\{edfbec6b-8432-4856-930d-feb334fb69c1\})|(\{f81a3bf7-d626-48cf-bd24-64e111ddc580\})|(\{4407ab94-60ae-4526-b1ab-2521ffd285c7\})|(\{4aa2ba11-f87b-4950-8250-cd977252e556\})|(\{646b0c4d-4c6f-429d-9b09-37101b36ed1c\})|(\{1b2d76f1-4906-42d2-9643-0ce928505dab\})|(\{1869f89d-5f15-4c0d-b993-2fa8f09694fb\})|(\{7e4edd36-e3a6-4ddb-9e98-22b4e9eb4721\})|(\{e9c9ad8c-84ba-43f2-9ae2-c1448694a2a0\})|(\{6b2bb4f0-78ea-47c2-a03a-f4bf8f916eda\})|(\{539e1692-5841-4ac6-b0cd-40db15c34738\}))$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p332">
       <match exp="libflashplayer\.so" name="filename"/>
       <match exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -686,19 +686,16 @@ pref("accessibility.support.url", "https
 pref("accessibility.indicator.enabled", false);
 
 pref("plugins.click_to_play", true);
 pref("plugins.testmode", false);
 
 // Should plugins that are hidden show the infobar UI?
 pref("plugins.show_infobar", false);
 
-// Should dismissing the hidden plugin infobar suppress it permanently?
-pref("plugins.remember_infobar_dismissal", true);
-
 pref("plugin.default.state", 1);
 
 // Plugins bundled in XPIs are enabled by default.
 pref("plugin.defaultXpi.state", 2);
 
 
 // Flash is Click-to-Activate by default on all channels.
 pref("plugin.state.flash", 1);
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -4,24 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.defineModuleGetter(this, "Blocklist",
                                "resource://gre/modules/Blocklist.jsm");
 
 var gPluginHandler = {
   PREF_SESSION_PERSIST_MINUTES: "plugin.sessionPermissionNow.intervalInMinutes",
   PREF_PERSISTENT_DAYS: "plugin.persistentPermissionAlways.intervalInDays",
-  PREF_SHOW_INFOBAR: "plugins.show_infobar",
-  PREF_INFOBAR_DISMISSAL_PERMANENT: "plugins.remember_infobar_dismissal",
 
   MESSAGES: [
     "PluginContent:ShowClickToPlayNotification",
     "PluginContent:RemoveNotification",
-    "PluginContent:UpdateHiddenPluginUI",
-    "PluginContent:HideNotificationBar",
     "PluginContent:InstallSinglePlugin",
     "PluginContent:ShowPluginCrashedNotification",
     "PluginContent:SubmitReport",
     "PluginContent:LinkClickCallback",
   ],
 
   init() {
     const mm = window.messageManager;
@@ -49,24 +45,16 @@ var gPluginHandler = {
     switch (msg.name) {
       case "PluginContent:ShowClickToPlayNotification":
         this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow,
                                          msg.principal, msg.data.location);
         break;
       case "PluginContent:RemoveNotification":
         this.removeNotification(msg.target, msg.data.name);
         break;
-      case "PluginContent:UpdateHiddenPluginUI":
-        this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions,
-                                  msg.principal, msg.data.location)
-          .catch(Cu.reportError);
-        break;
-      case "PluginContent:HideNotificationBar":
-        this.hideNotificationBar(msg.target, msg.data.name);
-        break;
       case "PluginContent:InstallSinglePlugin":
         this.installSinglePlugin(msg.data.pluginInfo);
         break;
       case "PluginContent:ShowPluginCrashedNotification":
         this.showPluginCrashedNotification(msg.target, msg.data.messageString,
                                            msg.data.pluginID);
         break;
       case "PluginContent:SubmitReport":
@@ -277,17 +265,16 @@ var gPluginHandler = {
     }
 
     if (notification) {
       // Don't modify the notification UI while it's on the screen, that would be
       // jumpy and might allow clickjacking.
       if (showNow) {
         notification.options.primaryPlugin = primaryPluginPermission;
         notification.reshow();
-        browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
       }
       return;
     }
 
     if (plugins.length == 1) {
       let pluginInfo = plugins[0];
       let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
 
@@ -363,204 +350,40 @@ var gPluginHandler = {
           accessKey: gNavigatorBundle.getString("flashActivate.noAllow.accesskey"),
           dismiss: true,
         }];
       }
 
       PopupNotifications.show(browser, "click-to-play-plugins",
                                              description, "plugins-notification-icon",
                                              mainAction, secondaryActions, options);
-      browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
+
+      // Check if the plugin is insecure and update the notification icon accordingly.
+      let haveInsecure = false;
+      switch (pluginInfo.fallbackType) {
+        // haveInsecure will trigger the red flashing icon and the infobar
+        // styling below
+        case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
+        case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
+          haveInsecure = true;
+      }
+
+      document.getElementById("plugins-notification-icon").classList.
+        toggle("plugin-blocked", haveInsecure);
     } else {
       this.removeNotification(browser, "click-to-play-plugins");
     }
   },
 
   removeNotification(browser, name) {
     let notification = PopupNotifications.getNotification(name, browser);
     if (notification)
       PopupNotifications.remove(notification);
   },
 
-  hideNotificationBar(browser, name) {
-    let notificationBox = gBrowser.getNotificationBox(browser);
-    let notification = notificationBox.getNotificationWithValue(name);
-    if (notification)
-      notificationBox.removeNotification(notification, true);
-  },
-
-  infobarBlockedForURI(uri) {
-    return new Promise((resolve, reject) => {
-      let tableName = Services.prefs.getStringPref("urlclassifier.flashInfobarTable", "");
-      if (!tableName) {
-        resolve(false);
-      }
-      let classifier = Cc["@mozilla.org/url-classifier/dbservice;1"]
-        .getService(Ci.nsIURIClassifier);
-      classifier.asyncClassifyLocalWithTables(uri, tableName, [], [], (c, list) => {
-        resolve(list.length > 0);
-      });
-    });
-  },
-
-  async updateHiddenPluginUI(browser, haveInsecure, actions,
-                                 principal, location) {
-    let origin = principal.originNoSuffix;
-
-    let shouldShowNotification = !(await this.infobarBlockedForURI(browser.documentURI));
-
-    // It is possible that we've received a message from the frame script to show
-    // the hidden plugin notification for a principal that no longer matches the one
-    // that the browser's content now has assigned (ie, the browser has browsed away
-    // after the message was sent, but before the message was received). In that case,
-    // we should just ignore the message.
-    if (!principal.equals(browser.contentPrincipal)) {
-      return;
-    }
-
-    // Data URIs, when linked to from some page, inherit the principal of that
-    // page. That means that we also need to compare the actual locations to
-    // ensure we aren't getting a message from a Data URI that we're no longer
-    // looking at.
-    let receivedURI = Services.io.newURI(location);
-    if (!browser.documentURI.equalsExceptRef(receivedURI)) {
-      return;
-    }
-
-    // Set up the icon
-    document.getElementById("plugins-notification-icon").classList.
-      toggle("plugin-blocked", haveInsecure);
-
-    // Now configure the notification bar
-    let notificationBox = gBrowser.getNotificationBox(browser);
-
-    function hideNotification() {
-      let n = notificationBox.getNotificationWithValue("plugin-hidden");
-      if (n) {
-        notificationBox.removeNotification(n, true);
-      }
-    }
-
-    // There are three different cases when showing an infobar:
-    // 1.  A single type of plugin is hidden on the page. Show the UI for that
-    //     plugin.
-    // 2a. Multiple types of plugins are hidden on the page. Show the multi-UI
-    //     with the vulnerable styling.
-    // 2b. Multiple types of plugins are hidden on the page, but none are
-    //     vulnerable. Show the nonvulnerable multi-UI.
-    function showNotification() {
-      if (!Services.prefs.getBoolPref(gPluginHandler.PREF_SHOW_INFOBAR, true)) {
-        return;
-      }
-
-      let n = notificationBox.getNotificationWithValue("plugin-hidden");
-      if (n) {
-        // If something is already shown, just keep it
-        return;
-      }
-
-      Services.telemetry.getHistogramById("PLUGINS_INFOBAR_SHOWN").
-        add(true);
-
-      let message;
-      // Icons set directly cannot be manipulated using moz-image-region, so
-      // we use CSS classes instead.
-      let brand = document.getElementById("bundle_brand").getString("brandShortName");
-
-      if (actions.length == 1) {
-        let pluginInfo = actions[0];
-        let pluginName = pluginInfo.pluginName;
-
-        switch (pluginInfo.fallbackType) {
-          case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
-          case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET:
-            message = gNavigatorBundle.getFormattedString(
-              "pluginActivationWarning.message",
-              [brand]);
-            break;
-          case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
-            message = gNavigatorBundle.getFormattedString(
-              "pluginActivateOutdated.message",
-              [pluginName, origin, brand]);
-            break;
-          case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
-            message = gNavigatorBundle.getFormattedString(
-              "pluginActivateVulnerable.message",
-              [pluginName, origin, brand]);
-        }
-      } else {
-        // Multi-plugin
-        message = gNavigatorBundle.getFormattedString(
-          "pluginActivateMultiple.message", [origin]);
-      }
-
-      let buttons = [
-        {
-          label: gNavigatorBundle.getString("pluginContinueBlocking.label"),
-          accessKey: gNavigatorBundle.getString("pluginContinueBlocking.accesskey"),
-          callback() {
-            Services.telemetry.getHistogramById("PLUGINS_INFOBAR_BLOCK").
-              add(true);
-
-            Services.perms.addFromPrincipal(principal,
-                                            "plugin-hidden-notification",
-                                            Services.perms.DENY_ACTION);
-          },
-        },
-        {
-          label: gNavigatorBundle.getString("pluginActivateTrigger.label"),
-          accessKey: gNavigatorBundle.getString("pluginActivateTrigger.accesskey"),
-          callback() {
-            Services.telemetry.getHistogramById("PLUGINS_INFOBAR_ALLOW").
-              add(true);
-
-            let curNotification =
-              PopupNotifications.getNotification("click-to-play-plugins",
-                                                 browser);
-            if (curNotification) {
-              curNotification.reshow();
-            }
-          },
-        },
-      ];
-      function notificationCallback(type) {
-        if (type == "dismissed") {
-          Services.telemetry.getHistogramById("PLUGINS_INFOBAR_DISMISSED").
-            add(true);
-          if (Services.prefs.getBoolPref(gPluginHandler.PREF_INFOBAR_DISMISSAL_PERMANENT, false)) {
-            Services.perms.addFromPrincipal(principal,
-                                            "plugin-hidden-notification",
-                                            Services.perms.DENY_ACTION);
-          }
-        }
-      }
-      n = notificationBox.
-        appendNotification(message, "plugin-hidden", null,
-                           notificationBox.PRIORITY_INFO_HIGH, buttons,
-                           notificationCallback);
-      if (haveInsecure) {
-        n.classList.add("pluginVulnerable");
-      }
-    }
-
-    if (actions.length == 0) {
-      shouldShowNotification = false;
-    }
-    if (shouldShowNotification &&
-        Services.perms.testPermissionFromPrincipal(principal, "plugin-hidden-notification") ==
-        Ci.nsIPermissionManager.DENY_ACTION) {
-      shouldShowNotification = false;
-    }
-    if (shouldShowNotification) {
-      showNotification();
-    } else {
-      hideNotification();
-    }
-  },
-
   contextMenuCommand(browser, plugin, command) {
     browser.messageManager.sendAsyncMessage("BrowserPlugins:ContextMenuCommand",
       { command }, { plugin });
   },
 
   // Crashed-plugin observer. Notified once per plugin crash, before events
   // are dispatched to individual plugin instances.
   NPAPIPluginCrashed(subject, topic, data) {
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -22,18 +22,16 @@ support-files =
   plugin_bug820497.html
   plugin_clickToPlayAllow.html
   plugin_clickToPlayDeny.html
   plugin_favorfallback.html
   plugin_hidden_to_visible.html
   plugin_iframe.html
   plugin_outsideScrollArea.html
   plugin_overlay_styles.html
-  plugin_overlayed.html
-  plugin_positioned.html
   plugin_simple_blank.swf
   plugin_shouldShowOverlay.html
   plugin_small.html
   plugin_small_2.html
   plugin_syncRemoved.html
   plugin_test.html
   plugin_test2.html
   plugin_test3.html
@@ -64,23 +62,19 @@ tags = blocklist
 [browser_CTP_favorfallback.js]
 [browser_CTP_hide_overlay.js]
 tags = blocklist
 [browser_CTP_iframe.js]
 tags = blocklist
 [browser_CTP_nonplugins.js]
 skip-if = verify
 tags = blocklist
-[browser_CTP_notificationBar.js]
-tags = blocklist
 [browser_CTP_outsideScrollArea.js]
 tags = blocklist
 [browser_CTP_overlay_styles.js]
-[browser_CTP_remove_navigate.js]
-tags = blocklist
 [browser_CTP_resize.js]
 tags = blocklist
 [browser_CTP_shouldShowOverlay.js]
 [browser_CTP_zoom.js]
 tags = blocklist
 [browser_blocking.js]
 tags = blocklist
 [browser_iterate_hidden_plugins.js]
deleted file mode 100644
--- a/browser/base/content/test/plugins/browser_CTP_notificationBar.js
+++ /dev/null
@@ -1,152 +0,0 @@
-var rootDir = getRootDirectory(gTestPath);
-const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
-var gTestBrowser = null;
-
-add_task(async function() {
-  await SpecialPowers.pushPrefEnv({ set: [
-    ["plugins.show_infobar", true],
-    ["plugins.click_to_play", true],
-    ["extensions.blocklist.supressUI", true],
-  ]});
-
-  registerCleanupFunction(function() {
-    clearAllPluginPermissions();
-    setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
-    setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
-    gBrowser.removeCurrentTab();
-    window.focus();
-    gTestBrowser = null;
-  });
-
-  let newTab = BrowserTestUtils.addTab(gBrowser);
-  gBrowser.selectedTab = newTab;
-  gTestBrowser = gBrowser.selectedBrowser;
-});
-
-add_task(async function() {
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
-
-  await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_small.html");
-
-  // Work around for delayed PluginBindingAttached
-  await promiseUpdatePluginBindings(gTestBrowser);
-
-  await promisePopupNotification("click-to-play-plugins");
-
-  // Expecting a notification bar for hidden plugins
-  await promiseForNotificationBar("plugin-hidden", gTestBrowser);
-});
-
-add_task(async function() {
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
-
-  await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_small.html");
-
-  // Work around for delayed PluginBindingAttached
-  await promiseUpdatePluginBindings(gTestBrowser);
-
-  let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
-  await promiseForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null);
-});
-
-add_task(async function() {
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
-
-  await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_overlayed.html");
-
-  // Work around for delayed PluginBindingAttached
-  await promiseUpdatePluginBindings(gTestBrowser);
-
-  // Expecting a plugin notification bar when plugins are overlaid.
-  await promiseForNotificationBar("plugin-hidden", gTestBrowser);
-});
-
-add_task(async function() {
-  await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_overlayed.html");
-
-  // Work around for delayed PluginBindingAttached
-  await promiseUpdatePluginBindings(gTestBrowser);
-
-  await ContentTask.spawn(gTestBrowser, null, async function() {
-    let doc = content.document;
-    let plugin = doc.getElementById("test");
-    plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    Assert.equal(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-      "Test 3b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
-  });
-
-  let pluginInfo = await promiseForPluginInfo("test");
-  ok(!pluginInfo.activated, "Test 1a, plugin should not be activated");
-
-  await ContentTask.spawn(gTestBrowser, null, async function() {
-    let doc = content.document;
-    let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    Assert.ok(!overlay || overlay.getAttribute("sizing") == "blank",
-      "Test 3b, overlay should be blank.");
-  });
-});
-
-add_task(async function() {
-  await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_positioned.html");
-
-  // Work around for delayed PluginBindingAttached
-  await promiseUpdatePluginBindings(gTestBrowser);
-
-  // Expecting a plugin notification bar when plugins are overlaid offscreen.
-  await promisePopupNotification("click-to-play-plugins");
-  await promiseForNotificationBar("plugin-hidden", gTestBrowser);
-
-  await ContentTask.spawn(gTestBrowser, null, async function() {
-    let doc = content.document;
-    let plugin = doc.getElementById("test");
-    plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    Assert.equal(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-      "Test 4b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
-  });
-
-  await ContentTask.spawn(gTestBrowser, null, async function() {
-    let doc = content.document;
-    let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    Assert.ok(!overlay || overlay.getAttribute("sizing") == "blank",
-      "Test 4b, overlay should be blank.");
-  });
-});
-
-// Test that the notification bar is getting dismissed when directly activating plugins
-// via the doorhanger.
-
-add_task(async function() {
-  await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_small.html");
-
-  // Work around for delayed PluginBindingAttached
-  await promiseUpdatePluginBindings(gTestBrowser);
-
-  // Expecting a plugin notification bar when plugins are overlaid offscreen.
-  await promisePopupNotification("click-to-play-plugins");
-
-  await ContentTask.spawn(gTestBrowser, null, async function() {
-    let doc = content.document;
-    let plugin = doc.getElementById("test");
-    plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    Assert.equal(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
-      "Test 6, Plugin should be click-to-play");
-  });
-
-  await promisePopupNotification("click-to-play-plugins");
-
-  let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
-  ok(notification, "Test 6, Should have a click-to-play notification");
-
-  // simulate "always allow"
-  await promiseForNotificationShown(notification);
-
-  PopupNotifications.panel.firstElementChild.button.click();
-
-  let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
-  await promiseForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null);
-
-  let pluginInfo = await promiseForPluginInfo("test");
-  ok(pluginInfo.activated, "Test 7, plugin should be activated");
-});
deleted file mode 100644
--- a/browser/base/content/test/plugins/browser_CTP_remove_navigate.js
+++ /dev/null
@@ -1,76 +0,0 @@
-const gTestRoot = getRootDirectory(gTestPath);
-const gHttpTestRoot = gTestRoot.replace("chrome://mochitests/content/",
-                                        "http://127.0.0.1:8888/");
-
-add_task(async function() {
-  await SpecialPowers.pushPrefEnv({ set: [
-    ["plugins.click_to_play", true],
-    ["extensions.blocklist.suppressUI", true],
-    ["plugins.show_infobar", true],
-  ]});
-  registerCleanupFunction(function() {
-    clearAllPluginPermissions();
-    setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
-    setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
-    gBrowser.removeCurrentTab();
-    window.focus();
-  });
-
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in");
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Second Test Plug-in");
-});
-
-/**
- * Tests that if a plugin is removed just as we transition to
- * a different page, that we don't show the hidden plugin
- * notification bar on the new page.
- */
-add_task(async function() {
-  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
-
-  // Load up a page with a plugin...
-  let notificationPromise = waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser);
-  await promiseTabLoadEvent(gBrowser.selectedTab, gHttpTestRoot + "plugin_small.html");
-  await promiseUpdatePluginBindings(gBrowser.selectedBrowser);
-  await notificationPromise;
-
-  // Trigger the PluginRemoved event to be fired, and then immediately
-  // browse to a new page.
-  await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
-    let plugin = content.document.getElementById("test");
-    plugin.remove();
-  });
-
-  await promiseTabLoadEvent(gBrowser.selectedTab, "about:mozilla");
-
-  // There should be no hidden plugin notification bar at about:mozilla.
-  let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
-  is(notificationBox.getNotificationWithValue("plugin-hidden"), null,
-     "Expected no notification box");
-});
-
-/**
- * Tests that if a plugin is removed just as we transition to
- * a different page with a plugin, that we show the right notification
- * for the new page.
- */
-add_task(async function() {
-  // Load up a page with a plugin...
-  let notificationPromise = waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser);
-  await promiseTabLoadEvent(gBrowser.selectedTab, gHttpTestRoot + "plugin_small.html");
-  await promiseUpdatePluginBindings(gBrowser.selectedBrowser);
-  await notificationPromise;
-
-  // Trigger the PluginRemoved event to be fired, and then immediately
-  // browse to a new page.
-  await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
-    let plugin = content.document.getElementById("test");
-    plugin.remove();
-  });
-});
-
-add_task(async function() {
-  await promiseTabLoadEvent(gBrowser.selectedTab, gHttpTestRoot + "plugin_small_2.html");
-  let notification = await waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser);
-  ok(notification, "There should be a notification shown for the new page.");
-});
--- a/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js
+++ b/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js
@@ -1,19 +1,17 @@
 "use strict";
 
 const TEST_PLUGIN_NAME = "Test Plug-in";
 const HIDDEN_CTP_PLUGIN_PREF = "plugins.navigator.hidden_ctp_plugin";
 
 /**
  * If a plugin is click-to-play and named in HIDDEN_CTP_PLUGIN_PREF,
  * then the plugin should be hidden in the navigator.plugins list by default
- * when iterating. If, however, JS attempts to access the plugin via
- * navigator.plugins[NAME] directly, we should show the "Hidden Plugin"
- * notification bar.
+ * when iterating.
  */
 
 add_task(async function setup() {
   // We'll make the Test Plugin click-to-play.
   let originalPluginState = getTestPluginEnabledState();
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
   registerCleanupFunction(() => {
     setTestPluginEnabledState(originalPluginState);
@@ -63,66 +61,8 @@ add_task(async function test_plugin_is_h
       let plugins = Array.from(content.navigator.plugins);
       Assert.ok(plugins.some(p => p.name == pluginName),
                 "Should have found the Test Plugin");
     });
   });
 
   await SpecialPowers.popPrefEnv();
 });
-
-/**
- * Tests that if a click-to-play plugin is hidden, that we show the
- * Hidden Plugin notification bar when accessed directly by name
- * via navigator.plugins.
- */
-add_task(async function test_plugin_shows_hidden_notification_on_access() {
-  await BrowserTestUtils.withNewTab({
-    gBrowser,
-    url: "http://example.com",
-  }, async function(browser) {
-    let notificationPromise = waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser);
-
-    await ContentTask.spawn(browser, TEST_PLUGIN_NAME, async function(pluginName) {
-      let plugins = content.navigator.plugins;
-      // Just accessing the plugin should be enough to trigger the notification.
-      // We'll also do a sanity check and make sure that the HiddenPlugin event
-      // is firing (which is the event that triggers the notification bar).
-      let sawEvent = false;
-      addEventListener("HiddenPlugin", function onHiddenPlugin(e) {
-        sawEvent = true;
-        removeEventListener("HiddenPlugin", onHiddenPlugin, true);
-      }, true);
-      plugins[pluginName];
-      Assert.ok(sawEvent, "Should have seen the HiddenPlugin event.");
-    });
-
-    let notification = await notificationPromise;
-    notification.close();
-  });
-
-  // Make sure that if the plugin wasn't hidden that touching it
-  // does _NOT_ show the notification bar.
-  await SpecialPowers.pushPrefEnv({
-    set: [[HIDDEN_CTP_PLUGIN_PREF, ""]],
-  });
-
-  await BrowserTestUtils.withNewTab({
-    gBrowser,
-    url: "http://example.com",
-  }, async function(browser) {
-    await ContentTask.spawn(browser, TEST_PLUGIN_NAME, async function(pluginName) {
-      let plugins = content.navigator.plugins;
-      // Instead of waiting for a notification bar that should never come,
-      // we'll make sure that the HiddenPlugin event never fires in content
-      // (which is the event that triggers the notification bar).
-      let sawEvent = false;
-      addEventListener("HiddenPlugin", function onHiddenPlugin(e) {
-        sawEvent = true;
-        removeEventListener("HiddenPlugin", onHiddenPlugin, true);
-      }, true);
-      plugins[pluginName];
-      Assert.ok(!sawEvent, "Should not have seen the HiddenPlugin event.");
-    });
-  });
-
-  await SpecialPowers.popPrefEnv();
-});
--- a/browser/base/content/test/plugins/browser_subframe_access_hidden_plugins.js
+++ b/browser/base/content/test/plugins/browser_subframe_access_hidden_plugins.js
@@ -5,17 +5,17 @@ const HIDDEN_CTP_PLUGIN_PREF = "plugins.
 const DOMAIN_1 = "http://example.com";
 const DOMAIN_2 = "http://mochi.test:8888";
 
 /**
  * If a plugin is click-to-play and named in HIDDEN_CTP_PLUGIN_PREF,
  * then the plugin should be hidden in the navigator.plugins list by
  * default. However, if a plugin has been allowed on a top-level
  * document, we should let subframes of that document access
- * navigator.plugins without showing the notification bar.
+ * navigator.plugins.
  */
 add_task(async function setup() {
   // We'll make the Test Plugin click-to-play.
   let originalPluginState = getTestPluginEnabledState();
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
   registerCleanupFunction(() => {
     setTestPluginEnabledState(originalPluginState);
     clearAllPluginPermissions();
@@ -52,19 +52,17 @@ add_task(async function test_plugin_acce
 
       // Now manually create a subframe hosted at domain2...
       let subframe = content.document.createElement("iframe");
       subframe.src = domain2;
       let loadedPromise = ContentTaskUtils.waitForEvent(subframe, "load");
       content.document.body.appendChild(subframe);
       await loadedPromise;
 
-      // Instead of waiting for a notification bar that should never come,
-      // we'll make sure that the HiddenPlugin event never fires in content
-      // (which is the event that triggers the notification bar).
+      // Make sure that the HiddenPlugin event never fires in content.
       let sawEvent = false;
       addEventListener("HiddenPlugin", function onHiddenPlugin(e) {
         sawEvent = true;
         removeEventListener("HiddenPlugin", onHiddenPlugin, true);
       }, true);
 
       Assert.ok(subframe.contentWindow.navigator.plugins[pluginName],
                 "Subframe should find Test Plugin");
deleted file mode 100644
--- a/browser/base/content/test/plugins/plugin_overlayed.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<head>
-  <meta charset="utf-8">
-  <style type="text/css">
-    .absthing {
-      width: 400px;
-      height: 400px;
-      position: absolute;
-      left: 20px;
-      top: 20px;
-    }
-    #d1 {
-      z-index: 1;
-    }
-    #d2 {
-      z-index: 2;
-      background-color: rgba(0,0,255,0.5);
-      border: 1px solid red;
-    }
-  </style>
-<body>
-  <div class="absthing" id="d1">
-    <embed id="test" type="application/x-test">
-  </div>
-  <div class="absthing" id="d2">
-    <p>This is overlaying
-  </div>
deleted file mode 100644
--- a/browser/base/content/test/plugins/plugin_positioned.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<head>
-  <meta charset="utf-8">
-  <style type="text/css">
-    #test {
-      position: absolute;
-      left: -1000px;
-      top: -1000px;
-    }
-  </style>
-<body>
-  <embed id="test" type="application/x-test">
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -118,16 +118,20 @@ var whitelist = [
   // browser/extensions/pdfjs/content/web/viewer.js#7450
   {file: "resource://pdf.js/web/debugger.js"},
 
   // resource://app/modules/translation/TranslationContentHandler.jsm
   {file: "resource://app/modules/translation/BingTranslator.jsm"},
   {file: "resource://app/modules/translation/GoogleTranslator.jsm"},
   {file: "resource://app/modules/translation/YandexTranslator.jsm"},
 
+  // The Quantum Bar files are not in use yet, but we plan to start using them
+  // soon in parallel to the old implementation.
+  {file: "resource://app/modules/UrlbarTokenizer.jsm"},
+
   // Starting from here, files in the whitelist are bugs that need fixing.
   // Bug 1339424 (wontfix?)
   {file: "chrome://browser/locale/taskbar.properties",
    platforms: ["linux", "macosx"]},
   // Bug 1356031 (only used by devtools)
   {file: "chrome://global/skin/icons/error-16.png"},
   // Bug 1348362
   {file: "chrome://global/skin/icons/warning-64.png", platforms: ["linux"]},
@@ -404,24 +408,23 @@ function parseCodeFile(fileUri) {
         let match = line.match("(?:src|href)=[\"']([^$&\"']+)");
         if (match && match[1]) {
           let url = Services.io.newURI(match[1], null, fileUri).spec;
           addCodeReference(convertToCodeURI(url), fileUri);
         }
 
         if (isDevtools) {
           let rules = [
-            ["gcli", "resource://devtools/shared/gcli/source/lib/gcli"],
             ["devtools/client/locales", "chrome://devtools/locale"],
             ["devtools/shared/locales", "chrome://devtools-shared/locale"],
             ["devtools/shared/platform", "resource://devtools/shared/platform/chrome"],
             ["devtools", "resource://devtools"],
           ];
 
-          match = line.match(/["']((?:devtools|gcli)\/[^\\#"']+)["']/);
+          match = line.match(/["']((?:devtools)\/[^\\#"']+)["']/);
           if (match && match[1]) {
             let path = match[1];
             for (let rule of rules) {
               if (path.startsWith(rule[0] + "/")) {
                 path = path.replace(rule[0], rule[1]);
                 if (!/\.(properties|js|jsm|json|css)$/.test(path))
                   path += ".js";
                 addCodeReference(path, fileUri);
--- a/browser/base/content/test/trackingUI/browser_trackingUI_state.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_state.js
@@ -136,63 +136,69 @@ function testTrackingPage(window) {
       "shield is" + (blockedByTP ? "" : " not") + " active");
   ok(!ContentBlocking.iconBox.hasAttribute("hasException"), "icon box shows no exception");
   is(ContentBlocking.iconBox.getAttribute("tooltiptext"),
      blockedByTP ? gNavigatorBundle.getString("trackingProtection.icon.activeTooltip") : "",
      "correct tooltip");
 
   ok(hidden("#tracking-action-block"), "blockButton is hidden");
 
-  let cbEnabled = Services.prefs.getBoolPref(CB_PREF);
-  if (PrivateBrowsingUtils.isWindowPrivate(window)) {
+  let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
+  let cbUIEnabled = Services.prefs.getBoolPref(CB_UI_PREF);
+  let tpEnabled = isWindowPrivate ? Services.prefs.getBoolPref(TP_PB_PREF) : Services.prefs.getBoolPref(TP_PREF);
+  let blockingEnabled = cbUIEnabled ? Services.prefs.getBoolPref(CB_PREF) : tpEnabled;
+  if (isWindowPrivate) {
     ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
-    is(hidden("#tracking-action-unblock-private"), !cbEnabled,
-       "unblockButtonPrivate is" + (cbEnabled ? "" : " not") + " visible");
+    is(!hidden("#tracking-action-unblock-private"), blockingEnabled,
+       "unblockButtonPrivate is" + (blockingEnabled ? "" : " not") + " visible");
   } else {
-    ok(!hidden("#tracking-action-unblock"), "unblockButton is visible");
-    is(hidden("#tracking-action-unblock-private"), cbEnabled,
-       "unblockButtonPrivate is" + (cbEnabled ? "" : " not") + " hidden");
+    ok(hidden("#tracking-action-unblock-private"), "unblockButtonPrivate is hidden");
+    is(!hidden("#tracking-action-unblock"), blockingEnabled,
+       "unblockButton is" + (blockingEnabled ? "" : " not") + " hidden");
   }
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 
-  if (Services.prefs.getBoolPref(CB_UI_PREF)) {
+  if (cbUIEnabled) {
     ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible");
     let category;
     if (Services.prefs.getBoolPref(FB_PREF)) {
       category = "#identity-popup-content-blocking-category-fastblock";
     } else {
       category = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER ?
                    "#identity-popup-content-blocking-category-3rdpartycookies" :
                    "#identity-popup-content-blocking-category-tracking-protection";
     }
     is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP,
       "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking");
     is(hidden(category + " > .identity-popup-content-blocking-category-state-label"), !blockedByTP,
       "Category item is" + (blockedByTP ? "" : " not") + " set to blocked");
   }
 }
 
-function testTrackingPageUnblocked(blockedByTP) {
+function testTrackingPageUnblocked(blockedByTP, window) {
   info("Tracking content must be white-listed and not blocked");
   ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
   ok(ContentBlocking.content.hasAttribute("hasException"), "content shows exception");
 
-  let cbEnabled = Services.prefs.getBoolPref(CB_PREF);
+  let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
+  let cbUIEnabled = Services.prefs.getBoolPref(CB_UI_PREF);
+  let tpEnabled = isWindowPrivate ? Services.prefs.getBoolPref(TP_PB_PREF) : Services.prefs.getBoolPref(TP_PREF);
+  let blockingEnabled = cbUIEnabled ? Services.prefs.getBoolPref(CB_PREF) : tpEnabled;
   ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is active");
-  is(ContentBlocking.iconBox.hasAttribute("hasException"), cbEnabled,
-     "shield" + (cbEnabled ? " shows" : " doesn't show") + " exception");
+  is(ContentBlocking.iconBox.hasAttribute("hasException"), blockingEnabled,
+     "shield" + (blockingEnabled ? " shows" : " doesn't show") + " exception");
   is(ContentBlocking.iconBox.getAttribute("tooltiptext"),
      gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip");
 
-  is(BrowserTestUtils.is_visible(ContentBlocking.iconBox), cbEnabled,
-     "icon box is" + (cbEnabled ? "" : " not") + " visible");
-  is(hidden("#tracking-action-block"), !cbEnabled,
-     "blockButton is" + (cbEnabled ? " not" : "") + " visible");
+  is(BrowserTestUtils.is_visible(ContentBlocking.iconBox), blockingEnabled,
+     "icon box is" + (blockingEnabled ? "" : " not") + " visible");
+  is(hidden("#tracking-action-block"), !blockingEnabled,
+     "blockButton is" + (blockingEnabled ? " not" : "") + " visible");
   ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
   ok(!hidden("#identity-popup-content-blocking-disabled-label"), "disabled label is visible");
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 
   if (Services.prefs.getBoolPref(CB_UI_PREF)) {
     ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible");
@@ -267,17 +273,17 @@ async function testContentBlockingEnable
   await promiseTabLoadEvent(tab, gTrackingPageURL);
   testTrackingPage(tab.ownerGlobal);
 
   info("Disable CB for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
   await tabReloadPromise;
   let blockedByTP = areTrackersBlocked(isPrivateBrowsing);
-  testTrackingPageUnblocked(blockedByTP);
+  testTrackingPageUnblocked(blockedByTP, tab.ownerGlobal);
 
   info("Re-enable TP for the page (which reloads the page)");
   tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-block");
   await tabReloadPromise;
   testTrackingPage(tab.ownerGlobal);
 }
 
@@ -378,17 +384,17 @@ add_task(async function testPrivateBrows
      "TP.enabled is based on the pb pref value");
 
   await testContentBlockingEnabled(tab);
 
   if (Services.prefs.getBoolPref(CB_UI_PREF)) {
     Services.prefs.setBoolPref(CB_PREF, false);
     ok(!ContentBlocking.enabled, "CB is disabled after setting the pref");
   } else {
-    Services.prefs.setBoolPref(TP_PREF, false);
+    Services.prefs.setBoolPref(TP_PB_PREF, false);
     ok(!TrackingProtection.enabled, "TP is disabled after setting the pref");
   }
 
   await testContentBlockingDisabled(tab);
 
   Services.prefs.setBoolPref(TP_PB_PREF, true);
   ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
   Services.prefs.setBoolPref(CB_PREF, true);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -234,17 +234,16 @@ let ACTORS = {
         "PluginOutdated": {capture: true},
         "PluginInstantiated": {capture: true},
         "PluginRemoved": {capture: true},
         "HiddenPlugin": {capture: true},
       },
 
       messages: [
         "BrowserPlugins:ActivatePlugins",
-        "BrowserPlugins:NotificationShown",
         "BrowserPlugins:ContextMenuCommand",
         "BrowserPlugins:NPAPIPluginProcessCrashed",
         "BrowserPlugins:CrashReportSubmitted",
         "BrowserPlugins:Test:ClearCrashData",
       ],
 
       observers: [
         "decoder-doctor-notification",
--- a/browser/components/payments/res/containers/completion-error-page.js
+++ b/browser/components/payments/res/containers/completion-error-page.js
@@ -15,16 +15,18 @@ import paymentRequest from "../paymentRe
  * as it will be much easier to implement and share the logic once we switch to Fluent.
  */
 
 export default class CompletionErrorPage extends PaymentStateSubscriberMixin(PaymentRequestPage) {
   constructor() {
     super();
 
     this.classList.add("error-page");
+    this.suggestionHeading = document.createElement("p");
+    this.body.append(this.suggestionHeading);
     this.suggestionsList = document.createElement("ul");
     this.suggestions = [];
     this.body.append(this.suggestionsList);
 
     this.doneButton = document.createElement("button");
     this.doneButton.classList.add("done-button", "primary");
     this.doneButton.addEventListener("click", this);
 
@@ -36,27 +38,33 @@ export default class CompletionErrorPage
 
     if (this.id && page && page.id !== this.id) {
       log.debug(`CompletionErrorPage: no need to further render inactive page: ${page.id}`);
       return;
     }
 
     let {request} = this.requestStore.getState();
     let {displayHost} = request.topLevelPrincipal.URI;
-    for (let key of ["pageTitle", "suggestion-1", "suggestion-2", "suggestion-3"]) {
-      this.dataset[key] = this.dataset[key].replace("**host-name**", displayHost);
+    for (let key of ["pageTitle", "suggestion-heading", "suggestion-1", "suggestion-2"]) {
+      if (this.dataset[key]) {
+        this.dataset[key] = this.dataset[key].replace("**host-name**", displayHost);
+      }
     }
 
     this.pageTitleHeading.textContent = this.dataset.pageTitle;
+    this.suggestionHeading.textContent = this.dataset.suggestionHeading;
     this.doneButton.textContent = this.dataset.doneButtonLabel;
 
     this.suggestionsList.textContent = "";
-    this.suggestions[0] = this.dataset["suggestion-1"];
-    this.suggestions[1] = this.dataset["suggestion-2"];
-    this.suggestions[2] = this.dataset["suggestion-3"];
+    if (this.dataset["suggestion-1"]) {
+      this.suggestions[0] = this.dataset["suggestion-1"];
+    }
+    if (this.dataset["suggestion-2"]) {
+      this.suggestions[1] = this.dataset["suggestion-2"];
+    }
 
     let suggestionsFragment = document.createDocumentFragment();
     for (let suggestionText of this.suggestions) {
       let listNode = document.createElement("li");
       listNode.textContent = suggestionText;
       suggestionsFragment.appendChild(listNode);
     }
     this.suggestionsList.appendChild(suggestionsFragment);
--- a/browser/components/payments/res/paymentRequest.css
+++ b/browser/components/payments/res/paymentRequest.css
@@ -93,16 +93,17 @@ payment-dialog > header > .page-error {
 }
 
 .page-error {
   color: #D70022;
 }
 
 .page > footer {
   align-items: center;
+  justify-content: end;
   background-color: #eaeaee;
   display: flex;
   /* from visual spec: */
   padding-top: 20px;
   padding-bottom: 18px;
 }
 
 #order-details-overlay {
--- a/browser/components/payments/res/paymentRequest.xhtml
+++ b/browser/components/payments/res/paymentRequest.xhtml
@@ -14,19 +14,19 @@
   <!ENTITY fieldRequiredSymbol        "*">
 
   <!ENTITY shippingAddressLabel       "Shipping Address">
   <!ENTITY deliveryAddressLabel       "Delivery Address">
   <!ENTITY pickupAddressLabel         "Pickup Address">
   <!ENTITY shippingOptionsLabel       "Shipping Options">
   <!ENTITY deliveryOptionsLabel       "Delivery Options">
   <!ENTITY pickupOptionsLabel         "Pickup Options">
-  <!ENTITY shippingGenericError       "Can't ship to this address. Select a different address.">
-  <!ENTITY deliveryGenericError       "Can't deliver to this address. Select a different address.">
-  <!ENTITY pickupGenericError         "Can't pick up from this address. Select a different address.">
+  <!ENTITY shippingGenericError       "Can’t ship to this address. Select a different address.">
+  <!ENTITY deliveryGenericError       "Can’t deliver to this address. Select a different address.">
+  <!ENTITY pickupGenericError         "Can’t pick up from this address. Select a different address.">
   <!ENTITY paymentMethodsLabel        "Payment Method">
   <!ENTITY address.addLink.label      "Add">
   <!ENTITY address.editLink.label     "Edit">
   <!ENTITY basicCard.addLink.label    "Add">
   <!ENTITY basicCard.editLink.label   "Edit">
   <!ENTITY payer.addLink.label        "Add">
   <!ENTITY payer.editLink.label       "Edit">
   <!ENTITY shippingAddress.addPage.title  "Add Shipping Address">
@@ -58,26 +58,24 @@
   <!ENTITY basicCardPage.updateButton.label   "Update">
   <!ENTITY basicCardPage.persistCheckbox.label     "Save credit card to &brandShortName; (Security code will not be saved)">
   <!ENTITY addressPage.error.genericSave      "There was an error saving the address.">
   <!ENTITY addressPage.cancelButton.label     "Cancel">
   <!ENTITY addressPage.backButton.label       "Back">
   <!ENTITY addressPage.addButton.label        "Add">
   <!ENTITY addressPage.updateButton.label     "Update">
   <!ENTITY addressPage.persistCheckbox.label  "Save address to &brandShortName;">
-  <!ENTITY failErrorPage.title  "Sorry! Something went wrong with the payment process.">
-  <!ENTITY failErrorPage.suggestion1  "Check your credit card has not expired.">
-  <!ENTITY failErrorPage.suggestion2  "Make sure your credit card information is accurate.">
-  <!ENTITY failErrorPage.suggestion3  "If no other solutions work, check with **host-name**.">
-  <!ENTITY failErrorPage.doneButton.label     "OK">
-  <!ENTITY timeoutErrorPage.title  "Whoops! **host-name** took too long to respond.">
-  <!ENTITY timeoutErrorPage.suggestion1  "Try again later.">
-  <!ENTITY timeoutErrorPage.suggestion2  "Check your network connection." >
-  <!ENTITY timeoutErrorPage.suggestion3  "If no other solutions work, check with **host-name**.">
-  <!ENTITY timeoutErrorPage.doneButton.label     "OK">
+  <!ENTITY failErrorPage.title                "We couldn’t complete your payment to **host-name**">
+  <!ENTITY failErrorPage.suggestionHeading    "The most likely cause is a hiccup with your credit card.">
+  <!ENTITY failErrorPage.suggestion1          "Make sure the card you’re using hasn’t expired">
+  <!ENTITY failErrorPage.suggestion2          "Double check the card number and expiration date">
+  <!ENTITY failErrorPage.doneButton.label     "Close">
+  <!ENTITY timeoutErrorPage.title             "**host-name** is taking too long to respond.">
+  <!ENTITY timeoutErrorPage.suggestionHeading "The most likely cause is a temporary connection hiccup. Open a new tab to check your network connection or click “Close” to try again.">
+  <!ENTITY timeoutErrorPage.doneButton.label     "Close">
   <!ENTITY webPaymentsBranding.label             "&brandShortName; Checkout">
   <!ENTITY invalidOption.label                   "Missing or invalid information">
 ]>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <title>&paymentSummaryTitle;</title>
 
   <!-- chrome: is needed for global.dtd -->
@@ -194,26 +192,24 @@
                     data-add-button-label="&addressPage.addButton.label;"
                     data-update-button-label="&addressPage.updateButton.label;"
                     data-persist-checkbox-label="&addressPage.persistCheckbox.label;"
                     data-field-required-symbol="&fieldRequiredSymbol;"
                     hidden="hidden"></address-form>
 
       <completion-error-page id="completion-timeout-error" class="illustrated"
                   data-page-title="&timeoutErrorPage.title;"
-                  data-suggestion-1="&timeoutErrorPage.suggestion1;"
-                  data-suggestion-2="&timeoutErrorPage.suggestion2;"
-                  data-suggestion-3="&timeoutErrorPage.suggestion3;"
+                  data-suggestion-heading="&timeoutErrorPage.suggestionHeading;"
                   data-done-button-label="&timeoutErrorPage.doneButton.label;"
                   hidden="hidden"></completion-error-page>
       <completion-error-page id="completion-fail-error" class="illustrated"
                   data-page-title="&failErrorPage.title;"
+                  data-suggestion-heading="&failErrorPage.suggestionHeading;"
                   data-suggestion-1="&failErrorPage.suggestion1;"
                   data-suggestion-2="&failErrorPage.suggestion2;"
-                  data-suggestion-3="&failErrorPage.suggestion3;"
                   data-done-button-label="&failErrorPage.doneButton.label;"
                   hidden="hidden"></completion-error-page>
     </div>
 
     <div id="disabled-overlay" hidden="hidden">
       <!-- overlay to prevent changes while waiting for a response from the merchant -->
     </div>
   </template>
--- a/browser/components/payments/test/mochitest/test_completion_error_page.html
+++ b/browser/components/payments/test/mochitest/test_completion_error_page.html
@@ -13,19 +13,19 @@ Test the completion-error-page component
   <script src="../../res/unprivileged-fallbacks.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <completion-error-page id="completion-timeout-error" class="illustrated"
             data-page-title="Sample Title"
+            data-suggestion-heading="Sample suggestion heading"
             data-suggestion-1="Sample suggestion"
             data-suggestion-2="Sample suggestion"
-            data-suggestion-3="Sample suggestion"
             data-done-button-label="OK"></completion-error-page>
   </p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="module">
@@ -33,19 +33,22 @@ Test the completion-error-page component
 
 import "../../res/containers/completion-error-page.js";
 
 let page = document.getElementById("completion-timeout-error");
 
 add_task(async function test_no_values() {
   ok(page, "page exists");
   is(page.dataset.pageTitle, "Sample Title", "Title set on page");
-  is(page.dataset["suggestion-1"], "Sample suggestion", "Suggestion 1 set on page");
-  is(page.dataset["suggestion-2"], "Sample suggestion", "Suggestion 2 set on page");
-  is(page.dataset["suggestion-3"], "Sample suggestion", "Suggestion 3 set on page");
+  is(page.dataset.suggestionHeading, "Sample suggestion heading",
+     "Suggestion heading set on page");
+  is(page.dataset["suggestion-1"], "Sample suggestion",
+     "Suggestion 1 set on page");
+  is(page.dataset["suggestion-2"], "Sample suggestion",
+     "Suggestion 2 set on page");
 
   page.dataset.pageTitle = "Oh noes! **host-name** is having an issue";
   page.dataset["suggestion-2"] = "You should probably blame **host-name**, not us";
   const displayHost = "allizom.com";
   let request = { topLevelPrincipal: { URI: { displayHost } } };
   await page.requestStore.setState({
     changesPrevented: false,
     request: Object.assign({}, request, {completeStatus: ""}),
@@ -53,19 +56,24 @@ add_task(async function test_no_values()
     page: {
       id: "completion-timeout-error",
     },
   });
   await asyncElementRendered();
 
   is(page.requestStore.getState().request.topLevelPrincipal.URI.displayHost, displayHost,
      "State should have the displayHost set properly");
-  is(page.dataset.pageTitle, `Oh noes! ${displayHost} is having an issue`,
+  is(page.querySelector("h2").textContent,
+     `Oh noes! ${displayHost} is having an issue`,
      "Title includes host-name");
-  is(page.dataset["suggestion-1"], "Sample suggestion", "Suggestion 1 set on page");
-  is(page.dataset["suggestion-2"], `You should probably blame ${displayHost}, not us`,
+  is(page.querySelector("p").textContent,
+     "Sample suggestion heading",
+     "Suggestion heading set on page");
+  is(page.querySelector("li:nth-child(1)").textContent, "Sample suggestion",
+     "Suggestion 1 set on page");
+  is(page.querySelector("li:nth-child(2)").textContent,
+     `You should probably blame ${displayHost}, not us`,
      "Suggestion 2 includes host-name");
-  is(page.dataset["suggestion-3"], "Sample suggestion", "Suggestion 3 set on page");
 });
 </script>
 
 </body>
 </html>
--- a/browser/components/payments/test/mochitest/test_payment_dialog.html
+++ b/browser/components/payments/test/mochitest/test_payment_dialog.html
@@ -244,17 +244,17 @@ add_task(async function test_timeout_fai
     let payButton = document.getElementById("pay");
     let primaryButton = pageElem.querySelector("button.primary");
 
     ok(pageElem && !isHidden(pageElem, `page element for ${page.id} exists and is visible`));
     ok(!isHidden(primaryButton), "Primary button is visible");
     ok(payButton != primaryButton,
        `Primary button is the not pay button in the ${completeStatus} state`);
     ok(isHidden(payButton), "Pay button is not visible");
-    is(primaryButton.textContent, "OK", "Check button label");
+    is(primaryButton.textContent, "Close", "Check button label");
 
     let rect = primaryButton.getBoundingClientRect();
     let visibleElement =
       document.elementFromPoint(rect.x + rect.width / 2, rect.y + rect.height / 2);
     ok(primaryButton === visibleElement, "Primary button is on top of the overlay");
   }
 });
 
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -60,18 +60,23 @@ document.addEventListener("DOMContentLoa
     return;
   }
 
   contentBlockingUIEnabled = RPMGetBoolPref(CB_UI_ENABLED_PREF);
 
   document.getElementById("startTour").addEventListener("click", function() {
     RPMSendAsyncMessage("DontShowIntroPanelAgain");
   });
-  document.getElementById("startTour").setAttribute("href",
-    RPMGetFormatURLPref("privacy.trackingprotection.introURL"));
+
+  let introURL = RPMGetFormatURLPref("privacy.trackingprotection.introURL");
+  // If the CB UI is enabled, tell the tour page to show a different variation
+  // that is updated to reflect the CB control center UI.
+  let variation = contentBlockingUIEnabled ? "?variation=1" : "";
+
+  document.getElementById("startTour").setAttribute("href", introURL + variation);
 
   document.getElementById("learnMore").setAttribute("href",
     RPMGetFormatURLPref("app.support.baseURL") + "private-browsing");
 
   let tpToggle = document.getElementById("tpToggle");
   document.getElementById("tpButton").addEventListener("click", () => {
     tpToggle.click();
   });
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
@@ -49,17 +49,19 @@ async function testLinkOpensUrl({ win, t
 /**
  * Tests the links in "about:privatebrowsing".
  */
 add_task(async function test_links() {
   // Use full version and change the remote URLs to prevent network access.
   Services.prefs.setCharPref("app.support.baseURL", "https://example.com/");
   Services.prefs.setCharPref("privacy.trackingprotection.introURL",
                              "https://example.com/tour");
+  Services.prefs.setBoolPref(CB_UI_ENABLED_PREF, false);
   registerCleanupFunction(function() {
+    Services.prefs.clearUserPref(CB_UI_ENABLED_PREF);
     Services.prefs.clearUserPref("privacy.trackingprotection.introURL");
     Services.prefs.clearUserPref("app.support.baseURL");
   });
 
   let { win, tab } = await openAboutPrivateBrowsing();
 
   await testLinkOpensTab({ win, tab,
     elementId: "learnMore",
@@ -69,16 +71,43 @@ add_task(async function test_links() {
   await testLinkOpensUrl({ win, tab,
     elementId: "startTour",
     expectedUrl: "https://example.com/tour",
   });
 
   await BrowserTestUtils.closeWindow(win);
 });
 
+add_task(async function test_links_CB() {
+  // Use full version and change the remote URLs to prevent network access.
+  Services.prefs.setCharPref("app.support.baseURL", "https://example.com/");
+  Services.prefs.setCharPref("privacy.trackingprotection.introURL",
+                             "https://example.com/tour");
+  Services.prefs.setBoolPref(CB_UI_ENABLED_PREF, true);
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref(CB_UI_ENABLED_PREF);
+    Services.prefs.clearUserPref("privacy.trackingprotection.introURL");
+    Services.prefs.clearUserPref("app.support.baseURL");
+  });
+
+  let { win, tab } = await openAboutPrivateBrowsing();
+
+  await testLinkOpensTab({ win, tab,
+    elementId: "learnMore",
+    expectedUrl: "https://example.com/private-browsing",
+  });
+
+  await testLinkOpensUrl({ win, tab,
+    elementId: "startTour",
+    expectedUrl: "https://example.com/tour?variation=1",
+  });
+
+  await BrowserTestUtils.closeWindow(win);
+});
+
 function waitForPrefChanged(pref) {
   return new Promise(resolve => {
     let prefObserver = {
       QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
       observe() {
         Services.prefs.removeObserver(pref, prefObserver);
         resolve();
       },
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/UrlbarTokenizer.jsm
@@ -0,0 +1,269 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * This module exports a tokenizer to be used by the urlbar model.
+ * Emitted tokens are objects in the shape { type, value }, where type is one
+ * of UrlbarTokenizer.TYPE.
+ */
+
+var EXPORTED_SYMBOLS = ["UrlbarTokenizer"];
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ChromeUtils.defineModuleGetter(this, "Log",
+                               "resource://gre/modules/Log.jsm");
+XPCOMUtils.defineLazyGetter(this, "logger", () =>
+  Log.repository.getLogger("Places.Urlbar.Tokenizer"));
+
+var UrlbarTokenizer = {
+  // Regex matching on whitespaces.
+  REGEXP_SPACES: /\s+/,
+
+  // Regex used to guess url-like strings.
+  // These are not expected to cover 100% of the cases.
+  REGEXP_PROTOCOL: /^[A-Z+.-]+:(\/\/)?(?!\/)/i,
+  REGEXP_USERINFO_INVALID_CHARS: /[^\w.~%!$&'()*+,;=:-]/,
+  REGEXP_HOSTPORT_INVALID_CHARS: /[^\[\]A-Z0-9.:-]/i,
+  REGEXP_HOSTPORT_IP_LIKE: /^[a-f0-9\.\[\]:]+$/i,
+  REGEXP_HOSTPORT_INVALID_IP: /\.{2,}|\d{5,}|\d{4,}(?![:\]])|^\.|\.$|^(\d+\.){4,}\d+$|^\d+$/,
+  REGEXP_HOSTPORT_IPV4: /^(\d{1,3}\.){3,}\d{1,3}(:\d+)?$/,
+  REGEXP_HOSTPORT_IPV6: /^[0-9A-F:\[\]]{1,4}$/i,
+  REGEXP_COMMON_EMAIL: /^[\w!#$%&'*+\/=?^`{|}~-]+@[\[\]A-Z0-9.-]+$/i,
+
+  TYPE: {
+    TEXT: 1,
+    POSSIBLE_ORIGIN: 2, // It may be an ip, a domain, but even just a single word used as host.
+    POSSIBLE_URL: 3, // Consumers should still check this with a fixup.
+    RESTRICT_HISTORY: 4,
+    RESTRICT_BOOKMARK: 5,
+    RESTRICT_TAG: 6,
+    RESTRICT_OPENPAGE: 7,
+    RESTRICT_TYPED: 8,
+    RESTRICT_SEARCH: 9,
+    RESTRICT_TITLE: 10,
+    RESTRICT_URL: 11,
+  },
+
+  /**
+   * Returns whether the passed in token looks like a URL.
+   * This is based on guessing and heuristics, that means if this function
+   * returns false, it's surely not a URL, if it returns true, the result must
+   * still be verified through URIFixup.
+   *
+   * @param {string} token
+   *        The string token to verify
+   * @param {object} options {
+   *          requirePath: the url must have a path
+   *        }
+   * @returns {boolean} whether the token looks like a URL.
+   */
+  looksLikeUrl(token, options = {}) {
+    if (token.length < 2)
+      return false;
+    // It should be a single word.
+    if (this.REGEXP_SPACES.test(token))
+      return false;
+    // If it starts with something that looks like a protocol, it's likely a url.
+    if (this.REGEXP_PROTOCOL.test(token))
+      return true;
+    // Guess path and prePath. At this point we should be analyzing strings not
+    // having a protocol.
+    let slashIndex = token.indexOf("/");
+    let prePath = slashIndex != -1 ? token.slice(0, slashIndex) : token;
+    if (!this.looksLikeOrigin(prePath))
+      return false;
+
+    let path = slashIndex != -1 ? token.slice(slashIndex) : "";
+    logger.debug("path", path);
+    if (options.requirePath && !path)
+      return false;
+    // If there are both path and userinfo, it's likely a url.
+    let atIndex = prePath.indexOf("@");
+    let userinfo = atIndex != -1 ? prePath.slice(0, atIndex) : "";
+    if (path.length && userinfo.length)
+      return true;
+
+    // If the path contains special chars, it is likely a url.
+    if (["%", "?", "#"].some(c => path.includes(c)))
+      return true;
+
+    // The above looksLikeOrigin call told us the prePath looks like an origin,
+    // now we go into details checking some common origins.
+    let hostPort = atIndex != -1 ? prePath.slice(atIndex + 1) : prePath;
+    if (this.REGEXP_HOSTPORT_IPV4.test(hostPort))
+      return true;
+    // ipv6 is very complex to support, just check for a few chars.
+    if (this.REGEXP_HOSTPORT_IPV6.test(hostPort) &&
+        ["[", "]", ":"].some(c => hostPort.includes(c)))
+      return true;
+    if (Services.uriFixup.isDomainWhitelisted(hostPort, -1))
+      return true;
+    return false;
+  },
+
+  /**
+   * Returns whether the passed in token looks like an origin.
+   * This is based on guessing and heuristics, that means if this function
+   * returns false, it's surely not an origin, if it returns true, the result
+   * must still be verified through URIFixup.
+   *
+   * @param {string} token
+   *        The string token to verify
+   * @returns {boolean} whether the token looks like an origin.
+   */
+  looksLikeOrigin(token) {
+    let atIndex = token.indexOf("@");
+    if (atIndex != -1 && this.REGEXP_COMMON_EMAIL.test(token)) {
+      // We prefer handling it as an email rather than an origin with userinfo.
+      return false;
+    }
+    let userinfo = atIndex != -1 ? token.slice(0, atIndex) : "";
+    let hostPort = atIndex != -1 ? token.slice(atIndex + 1) : token;
+    logger.debug("userinfo", userinfo);
+    logger.debug("hostPort", hostPort);
+    if (this.REGEXP_HOSTPORT_IPV4.test(hostPort))
+      return true;
+    if (this.REGEXP_HOSTPORT_IPV6.test(hostPort))
+      return true;
+    // Check for invalid chars.
+    return !this.REGEXP_USERINFO_INVALID_CHARS.test(userinfo) &&
+           !this.REGEXP_HOSTPORT_INVALID_CHARS.test(hostPort) &&
+           (!this.REGEXP_HOSTPORT_IP_LIKE.test(hostPort) ||
+            !this.REGEXP_HOSTPORT_INVALID_IP.test(hostPort));
+  },
+
+  /**
+   * Tokenizes the searchString from a QueryContext.
+   * @param {object} queryContext
+   *        The QueryContext object to tokenize
+   * @returns {object} the same QueryContext object with a new tokens property.
+   */
+  tokenize(queryContext) {
+    logger.info("Tokenizing", queryContext);
+    let searchString = queryContext.searchString;
+    if (searchString.length == 0) {
+      queryContext.tokens = [];
+      return queryContext;
+    }
+
+    let unfiltered = splitString(searchString);
+    let tokens = filterTokens(unfiltered);
+    queryContext.tokens = tokens;
+    return queryContext;
+  },
+
+  /**
+   * Given a token, tells if it's a restriction token.
+   * @param {string} token
+   * @returns {boolean} Whether the token is a restriction character.
+   */
+  isRestrictionToken(token) {
+    return token.type >= this.TYPE.RESTRICT_HISTORY &&
+           token.type <= this.TYPE.RESTRICT_URL;
+  },
+};
+
+// The special characters below can be typed into the urlbar to restrict
+// the search to a certain category, like history, bookmarks or open pages; or
+// to force a match on just the title or url.
+// These restriction characters can be typed alone, or at word boundaries,
+// provided their meaning cannot be confused, for example # could be present
+// in a valid url, and thus it should not be interpreted as a restriction.
+UrlbarTokenizer.CHAR_TO_TYPE_MAP = new Map([
+  ["^", UrlbarTokenizer.TYPE.RESTRICT_HISTORY],
+  ["*", UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK],
+  ["+", UrlbarTokenizer.TYPE.RESTRICT_TAG],
+  ["%", UrlbarTokenizer.TYPE.RESTRICT_OPENPAGE],
+  ["~", UrlbarTokenizer.TYPE.RESTRICT_TYPED],
+  ["$", UrlbarTokenizer.TYPE.RESTRICT_SEARCH],
+  ["#", UrlbarTokenizer.TYPE.RESTRICT_TITLE],
+  ["@", UrlbarTokenizer.TYPE.RESTRICT_URL],
+]);
+
+/**
+ * Given a search string, splits it into string tokens.
+ * @param {string} searchString
+ *        The search string to split
+ * @returns {array} An array of string tokens.
+ */
+function splitString(searchString) {
+  // The first step is splitting on unicode whitespaces.
+  let tokens = searchString.trim().split(UrlbarTokenizer.REGEXP_SPACES);
+  let accumulator = [];
+  let hasRestrictionToken = tokens.some(t => UrlbarTokenizer.CHAR_TO_TYPE_MAP.has(t));
+  let chars = Array.from(UrlbarTokenizer.CHAR_TO_TYPE_MAP.keys()).join("");
+  logger.debug("Restriction chars", chars);
+  for (let token of tokens) {
+    // It's possible we have to split a token, if there's no separate restriction
+    // character and a token starts or ends with a restriction character, and it's
+    // not confusable (for example # at the end of an url.
+    // If the token looks like a url, certain characters may appear at the end
+    // of the path or the query string, thus ignore those.
+    if (!hasRestrictionToken &&
+        token.length > 1 &&
+        !UrlbarTokenizer.looksLikeUrl(token, {requirePath: true})) {
+      // Check for a restriction char at the beginning.
+      if (chars.includes(token[0])) {
+        hasRestrictionToken = true;
+        accumulator.push(token[0]);
+        accumulator.push(token.slice(1));
+        continue;
+      } else if (chars.includes(token[token.length - 1])) {
+        hasRestrictionToken = true;
+        accumulator.push(token.slice(0, token.length - 1));
+        accumulator.push(token[token.length - 1]);
+        continue;
+      }
+    }
+    accumulator.push(token);
+  }
+  logger.info("Found tokens", accumulator);
+  return accumulator;
+}
+
+/**
+ * Given an array of unfiltered tokens, this function filters them and converts
+ * to token objects with a type.
+ *
+ * @param {array} tokens
+ *        An array of strings, representing search tokens.
+ * @returns {array} An array of token objects.
+ */
+function filterTokens(tokens) {
+  let filtered = [];
+  let foundRestriction = [];
+  // Tokens that can be combined with others (but not with themselves).
+  // We can have a maximum of 2 tokens, one combinable and one non-combinable.
+  let combinables = new Set([
+    UrlbarTokenizer.TYPE.RESTRICT_TITLE,
+    UrlbarTokenizer.TYPE.RESTRICT_URL,
+  ]);
+  for (let token of tokens) {
+    let tokenObj = {
+      value: token,
+      type: UrlbarTokenizer.TYPE.TEXT,
+    };
+    let restrictionType = UrlbarTokenizer.CHAR_TO_TYPE_MAP.get(token);
+    if (tokens.length > 1 &&
+        restrictionType &&
+        foundRestriction.length == 0 ||
+        (foundRestriction.length == 1 &&
+         (combinables.has(foundRestriction[0]) && !combinables.has(restrictionType)) ||
+         (!combinables.has(foundRestriction[0]) && combinables.has(restrictionType)))) {
+      tokenObj.type = restrictionType;
+      foundRestriction.push(restrictionType);
+    } else if (UrlbarTokenizer.looksLikeOrigin(token)) {
+      tokenObj.type = UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN;
+    } else if (UrlbarTokenizer.looksLikeUrl(token, {requirePath: true})) {
+      tokenObj.type = UrlbarTokenizer.TYPE.POSSIBLE_URL;
+    }
+    filtered.push(tokenObj);
+  }
+  logger.info("Filtered Tokens", tokens);
+  return filtered;
+}
--- a/browser/components/urlbar/moz.build
+++ b/browser/components/urlbar/moz.build
@@ -1,11 +1,12 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Firefox", "Address Bar")
 
 EXTRA_JS_MODULES += [
+    'UrlbarTokenizer.jsm',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/tests/unit/head.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+// Import common head.
+/* import-globals-from ../../../../../toolkit/components/places/tests/head_common.js */
+var commonFile = do_get_file("../../../../../toolkit/components/places/tests/head_common.js", false);
+if (commonFile) {
+  let uri = Services.io.newFileURI(commonFile);
+  Services.scriptloader.loadSubScript(uri.spec, this);
+}
+
+// Put any other stuff relative to this test folder below.
+
+ChromeUtils.defineModuleGetter(this, "UrlbarTokenizer",
+                               "resource:///modules/UrlbarTokenizer.jsm");
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/tests/unit/test_tokenizer.js
@@ -0,0 +1,174 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function test_tokenizer() {
+  let testContexts = [
+    { desc: "Empty string",
+      searchString: "test",
+      expectedTokens: [
+        { value: "test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "Single word string",
+      searchString: "test",
+      expectedTokens: [
+        { value: "test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "Multi word string with mixed whitespace types",
+      searchString: " test1 test2\u1680test3\u2004test4\u1680",
+      expectedTokens: [
+        { value: "test1", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+        { value: "test2", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+        { value: "test3", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+        { value: "test4", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "separate restriction char at beginning",
+      searchString: "* test",
+      expectedTokens: [
+        { value: "*", type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK },
+        { value: "test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "separate restriction char at end",
+      searchString: "test *",
+      expectedTokens: [
+        { value: "test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+        { value: "*", type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK },
+      ],
+    },
+    { desc: "boundary restriction char at end",
+      searchString: "test*",
+      expectedTokens: [
+        { value: "test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+        { value: "*", type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK },
+      ],
+    },
+    { desc: "double boundary restriction char",
+      searchString: "*test#",
+      expectedTokens: [
+        { value: "*", type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK },
+        { value: "test#", type: UrlbarTokenizer.TYPE.TEXT },
+      ],
+    },
+    { desc: "double non-combinable restriction char, single char string",
+      searchString: "t*$",
+      expectedTokens: [
+        { value: "t*", type: UrlbarTokenizer.TYPE.TEXT },
+        { value: "$", type: UrlbarTokenizer.TYPE.RESTRICT_SEARCH },
+      ],
+    },
+    { desc: "only boundary restriction chars",
+      searchString: "*#",
+      expectedTokens: [
+        { value: "*", type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK },
+        { value: "#", type: UrlbarTokenizer.TYPE.RESTRICT_TITLE },
+      ],
+    },
+    { desc: "only the boundary restriction char",
+      searchString: "*",
+      expectedTokens: [
+        { value: "*", type: UrlbarTokenizer.TYPE.TEXT },
+      ],
+    },
+    { desc: "boundary restriction char on path",
+      searchString: "test/#",
+      expectedTokens: [
+        { value: "test/#", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
+      ],
+    },
+    { desc: "multiple boundary restriction chars suffix",
+      searchString: "test ^ ~",
+      expectedTokens: [
+        { value: "test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+        { value: "^", type: UrlbarTokenizer.TYPE.RESTRICT_HISTORY },
+        { value: "~", type: UrlbarTokenizer.TYPE.TEXT },
+      ],
+    },
+    { desc: "multiple boundary restriction chars prefix",
+      searchString: "^ ~ test",
+      expectedTokens: [
+        { value: "^", type: UrlbarTokenizer.TYPE.RESTRICT_HISTORY },
+        { value: "~", type: UrlbarTokenizer.TYPE.TEXT },
+        { value: "test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "Math with division",
+      searchString: "3.6/1.2",
+      expectedTokens: [
+        { value: "3.6/1.2", type: UrlbarTokenizer.TYPE.TEXT },
+      ],
+    },
+    { desc: "ipv4 in bookmarks",
+      searchString: "* 192.168.1.1:8",
+      expectedTokens: [
+        { value: "*", type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK },
+        { value: "192.168.1.1:8", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "email",
+      searchString: "test@mozilla.com",
+      expectedTokens: [
+        { value: "test@mozilla.com", type: UrlbarTokenizer.TYPE.TEXT },
+      ],
+    },
+    { desc: "protocol",
+      searchString: "http://test",
+      expectedTokens: [
+        { value: "http://test", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
+      ],
+    },
+    { desc: "bogus protocol",
+      searchString: "http:///",
+      expectedTokens: [
+        { value: "http:///", type: UrlbarTokenizer.TYPE.TEXT },
+      ],
+    },
+    { desc: "userinfo",
+      searchString: "user:pass@test",
+      expectedTokens: [
+        { value: "user:pass@test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "domain",
+      searchString: "www.mozilla.org",
+      expectedTokens: [
+        { value: "www.mozilla.org", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "data uri",
+      searchString: "data:text/plain,Content",
+      expectedTokens: [
+        { value: "data:text/plain,Content", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
+      ],
+    },
+    { desc: "ipv6",
+      searchString: "[2001:db8::1]",
+      expectedTokens: [
+        { value: "[2001:db8::1]", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "numeric domain",
+      searchString: "test1001.com",
+      expectedTokens: [
+        { value: "test1001.com", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
+      ],
+    },
+    { desc: "invalid ip",
+      searchString: "192.2134.1.2",
+      expectedTokens: [
+        { value: "192.2134.1.2", type: UrlbarTokenizer.TYPE.TEXT },
+      ],
+    },
+  ];
+
+  for (let queryContext of testContexts) {
+    info(queryContext.desc);
+    let newQueryContext = UrlbarTokenizer.tokenize(queryContext);
+    Assert.equal(queryContext, newQueryContext,
+                 "The queryContext object is the same");
+    Assert.deepEqual(queryContext.tokens, queryContext.expectedTokens,
+                     "Check the expected tokens");
+  }
+});
--- a/browser/components/urlbar/tests/unit/xpcshell.ini
+++ b/browser/components/urlbar/tests/unit/xpcshell.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
-head =
+head = head.js
 firefox-appdir = browser
 
 [test_QueryContext.js]
+[test_tokenizer.js]
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -286,38 +286,16 @@ crashedpluginsMessage.learnMore=Learn More…
 # loaded a search page for the given word.  An infobar then asks to the user
 # whether he rather wanted to visit the host.  %S is the recognized host.
 keywordURIFixup.message=Did you mean to go to %S?
 keywordURIFixup.goTo=Yes, take me to %S
 keywordURIFixup.goTo.accesskey=Y
 keywordURIFixup.dismiss=No thanks
 keywordURIFixup.dismiss.accesskey=N
 
-## Plugin doorhanger strings
-# LOCALIZATION NOTE (pluginActivate2.message):
-# Used for normal plugin activation if we don't know of a specific security issue.
-# %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
-pluginActivate2.message=Would you like to allow %2$S to run %1$S? Plugins may slow %3$S.
-pluginActivateMultiple.message=Allow %S to run plugins?
-
-# LOCALIZATION NOTE (pluginActivationWarning.message): this should use the
-# same string as "pluginActivationWarning" in pluginproblem.dtd
-pluginActivationWarning.message=This site uses a plugin that may slow %S.
-
-pluginActivate.learnMore=Learn More…
-# LOCALIZATION NOTE (pluginActivateOutdated.message, pluginActivateOutdated.label):
-# These strings are used when an unsafe plugin has an update available.
-# %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
-pluginActivateOutdated.message=%3$S has prevented the outdated plugin “%1$S” from running on %2$S.
-pluginActivateOutdated.label=Outdated plugin
-pluginActivate.updateLabel=Update now…
-# LOCALIZATION NOTE (pluginActivateVulnerable.message):
-# This string is used when an unsafe plugin has no update available.
-# %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
-pluginActivateVulnerable.message=%3$S has prevented the unsafe plugin “%1$S” from running on %2$S.
 pluginInfo.unknownPlugin=Unknown
 
 # LOCALIZATION NOTE (pluginActivateNow.label, pluginActivateAlways.label, pluginBlockNow.label): These should be the same as the matching strings in browser.dtd
 # LOCALIZATION NOTE (pluginActivateNow.label): This button will enable the
 # plugin in the current session for an short time (about an hour), auto-renewed
 # if the site keeps using the plugin.
 pluginActivateNow.label=Allow Now
 pluginActivateNow.accesskey=N
@@ -348,24 +326,16 @@ flashActivate.allow.accesskey=A
 # the best one.
 # The second change is that we removed the period at the end of the phrase, because
 # it's not natural in our UI, and the underline was removed from this, so it doesn't
 # look like a link anymore. We suggest that everyone removes that period too.
 PluginClickToActivate2=Run %S
 PluginVulnerableUpdatable=This plugin is vulnerable and should be updated.
 PluginVulnerableNoUpdate=This plugin has security vulnerabilities.
 
-# infobar UI
-pluginContinueBlocking.label=Continue Blocking
-pluginContinueBlocking.accesskey=B
-# LOCALIZATION NOTE (pluginActivateTrigger): Use the unicode ellipsis char, \u2026,
-# or use "..." if \u2026 doesn't suit traditions in your locale.
-pluginActivateTrigger.label=Allow…
-pluginActivateTrigger.accesskey=A
-
 # Sanitize
 # LOCALIZATION NOTE (sanitizeDialog2.everything.title): When "Time range to
 # clear" is set to "Everything", the Clear Recent History dialog's title is
 # changed to this.  See UI mockup and comment 11 at bug 480169 -->
 sanitizeDialog2.everything.title=Clear All History
 sanitizeButtonOK=Clear Now
 # LOCALIZATION NOTE (sanitizeButtonClearing): The label for the default
 # button between the user clicking it and the window closing.  Indicates the
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -566,17 +566,16 @@ notification[value="translation"] menuli
 #statuspanel[mirror] > #statuspanel-inner > #statuspanel-label:-moz-locale-dir(ltr) {
   border-left-style: solid;
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
-%include ../shared/plugin-doorhanger.inc.css
 
 /* Customization mode */
 
 %include ../shared/customizableui/customizeMode.inc.css
 
 /* End customization mode */
 
 %include ../shared/UITour.inc.css
@@ -633,16 +632,27 @@ notification[value="translation"] menuli
 @media (-moz-gtk-csd-available: 0) {
   #titlebar {
     display: none;
   }
 }
 
 /* We draw to titlebar when Gkt+ CSD is available */
 @media (-moz-gtk-csd-available) {
+  /* Some Gtk+ themes use non-rectangular toplevel windows. To fully support
+   * such themes we need to make toplevel window transparent with ARGB visual.
+   * It may cause performanance issue so let's put it under a preference
+   * and allow distros to enable it per default theme. */
+  @supports -moz-bool-pref("mozilla.widget.use-argb-visuals") {
+    :root[tabsintitlebar]:not(:-moz-lwtheme) {
+      background-color: transparent;
+      -moz-appearance: none;
+    }
+  }
+
   :root[tabsintitlebar] > #titlebar:-moz-lwtheme {
     visibility: hidden;
   }
   :root[tabsintitlebar] #titlebar-content:-moz-lwtheme {
     visibility: visible;
   }
 
   :root[tabsintitlebar] > #titlebar {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -968,17 +968,16 @@ html|*.addon-webext-perm-list {
 #statuspanel[mirror] > #statuspanel-inner > #statuspanel-label:-moz-locale-dir(ltr) {
   border-left-style: solid;
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
-%include ../shared/plugin-doorhanger.inc.css
 
 /* On mac, the popup notification contents are indented by default and so
   the default closebutton margins from notification.css require adjustment */
 
 .click-to-play-plugins-notification-description-box > .popup-notification-closebutton {
   margin-inline-end: -6px;
   margin-top: -7px;
 }
deleted file mode 100644
--- a/browser/themes/shared/plugin-doorhanger.inc.css
+++ /dev/null
@@ -1,14 +0,0 @@
-.messageImage[value="plugin-hidden"] {
-  list-style-image: url(chrome://mozapps/skin/plugins/plugin.svg);
-}
-
-/* Keep any changes to this style in sync with pluginProblem.css */
-notification.pluginVulnerable {
-  background-color: rgb(72,72,72);
-  background-image: url(chrome://mozapps/skin/plugins/contentPluginStripe.png);
-  color: white;
-}
-
-notification.pluginVulnerable .messageImage {
-  list-style-image: url(chrome://mozapps/skin/plugins/plugin-blocked.svg);
-}
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -935,17 +935,16 @@ notification[value="translation"] {
   /* disabled for triggering grayscale AA (bug 659213)
   border-top-left-radius: .3em;
   */
   margin-left: 1em;
 }
 
 %include ../shared/fullscreen/warning.inc.css
 %include ../shared/ctrlTab.inc.css
-%include ../shared/plugin-doorhanger.inc.css
 
 /* Customization mode */
 
 %include ../shared/customizableui/customizeMode.inc.css
 
 /**
  * This next rule is a hack to disable subpixel anti-aliasing on all
  * labels during the customize mode transition. Subpixel anti-aliasing
--- a/caps/ContentPrincipal.cpp
+++ b/caps/ContentPrincipal.cpp
@@ -346,16 +346,18 @@ ContentPrincipal::GetDomain(nsIURI** aDo
 
   NS_ADDREF(*aDomain = mDomain);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ContentPrincipal::SetDomain(nsIURI* aDomain)
 {
+  MOZ_ASSERT(aDomain);
+
   mDomain = aDomain;
   SetHasExplicitDomain();
 
   // Recompute all wrappers between compartments using this principal and other
   // non-chrome compartments.
   AutoSafeJSContext cx;
   JSPrincipals *principals = nsJSPrincipals::get(static_cast<nsIPrincipal*>(this));
   bool success = js::RecomputeWrappers(cx, js::ContentCompartmentsOnly(),
@@ -527,17 +529,22 @@ ContentPrincipal::Read(nsIObjectInputStr
 
   mCSP = do_QueryInterface(supports, &rv);
   // make sure setRequestContext is called after Init(),
   // to make sure  the principals URI been initalized.
   if (mCSP) {
     mCSP->SetRequestContext(nullptr, this);
   }
 
-  SetDomain(domain);
+  // Note: we don't call SetDomain here because we don't need the wrapper
+  // recomputation code there (we just created this principal).
+  mDomain = domain;
+  if (mDomain) {
+    SetHasExplicitDomain();
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ContentPrincipal::Write(nsIObjectOutputStream* aStream)
 {
   NS_ENSURE_STATE(mCodebase);
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -57,17 +57,16 @@ registerCleanupFunction(async function()
   info("Forcing GC/CC after debugger test.");
   await new Promise(resolve => {
     Cu.forceGC();
     Cu.forceCC();
     Cu.schedulePreciseGC(resolve);
   });
 });
 
-// Import the GCLI test helper
 var testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 testDir = testDir.replace(/\/\//g, "/");
 testDir = testDir.replace("chrome:/mochitest", "chrome://mochitest");
 
 function addWindow(aUrl) {
   info("Adding window: " + aUrl);
   return promise.resolve(getChromeWindow(window.open(aUrl)));
 }
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -542,18 +542,17 @@ exports.ToolboxButtons = [
   },
   { id: "command-button-responsive",
     description: l10n("toolbox.buttons.responsive",
                       osString == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M"),
     isTargetSupported: target => target.isLocalTab,
     onClick(event, toolbox) {
       const tab = toolbox.target.tab;
       const browserWindow = tab.ownerDocument.defaultView;
-      ResponsiveUIManager.handleGcliCommand(browserWindow, tab,
-        "resize toggle", null);
+      ResponsiveUIManager.toggle(browserWindow, tab, { trigger: "toolbox" });
     },
     isChecked(toolbox) {
       if (!toolbox.target.tab) {
         return false;
       }
       return ResponsiveUIManager.isActiveForTab(toolbox.target.tab);
     },
     setup(toolbox, onChange) {
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -122,28 +122,28 @@ var gDevToolsBrowser = exports.gDevTools
       toggleMenuItem("menu_devtools_replayExecution", recordReplayEnabled);
     } catch (e) {
       // devtools.recordreplay.enabled only exists on certain platforms.
     }
   },
 
   /**
    * This function makes sure that the "devtoolstheme" attribute is set on the browser
-   * window to make it possible to change colors on elements in the browser (like gcli,
-   * or the splitter between the toolbox and web content).
+   * window to make it possible to change colors on elements in the browser (like the
+   * splitter between the toolbox and web content).
    */
   updateDevtoolsThemeAttribute(win) {
     // Set an attribute on root element of each window to make it possible
     // to change colors based on the selected devtools theme.
     let devtoolsTheme = Services.prefs.getCharPref("devtools.theme");
     if (devtoolsTheme != "dark") {
       devtoolsTheme = "light";
     }
 
-    // Style gcli and the splitter between the toolbox and page content.  This used to
+    // Style the splitter between the toolbox and page content.  This used to
     // set the attribute on the browser's root node but that regressed tpaint:
     // bug 1331449.
     win.document.getElementById("browser-bottombox")
        .setAttribute("devtoolstheme", devtoolsTheme);
     win.document.getElementById("appcontent")
        .setAttribute("devtoolstheme", devtoolsTheme);
   },
 
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -2317,17 +2317,17 @@ Inspector.prototype = {
 
     this.telemetry.scalarSet("devtools.copy.xpath.opened", 1);
     this.selection.nodeFront.getXPath().then(path => {
       clipboardHelper.copyString(path);
     }).catch(console.error);
   },
 
   /**
-   * Initiate gcli screenshot command on selected node.
+   * Initiate screenshot command on selected node.
    */
   async screenshotNode() {
     // Bug 1332936 - it's possible to call `screenshotNode` while the BoxModel highlighter
     // is still visible, therefore showing it in the picture.
     // To avoid that, we have to hide it before taking the screenshot. The `hideBoxModel`
     // will do that, calling `hide` for the highlighter only if previously shown.
     await this.highlighter.hideBoxModel();
 
--- a/devtools/client/netmonitor/README.md
+++ b/devtools/client/netmonitor/README.md
@@ -1,91 +1,16 @@
 # Network Monitor
 
 The Network Monitor (netmonitor) shows you all the network requests Firefox makes (for example, when a page is loaded or when an XMLHttpRequest is performed) , how long each request takes, and details of each request. You can edit the method, query, header and resend the request as well. Read [MDN](https://developer.mozilla.org/en-US/docs/Tools/Network_Monitor) to learn all the features and how to use the tool.
 
 ## Prerequisite
 
 If you want to build the Network Monitor inside of the DevTools toolbox (Firefox Devtools Panels), follow the [simple Firefox build](https://developer.mozilla.org/docs/Mozilla/Developer_guide/Build_Instructions/Simple_Firefox_build) document in MDN. Start your compiled firefox and open the Firefox developer tool, you can see the Network Monitor inside.
 
-If you would like to run the Network Monitor in the browser tab (experimental), you need following packages:
-
-* [node](https://nodejs.org/) >= 6.9.x JavaScript runtime.
-* [yarn](https://yarnpkg.com/docs/install) >= 0.21.x the package dependency management tool.
-* [Firefox](https://www.mozilla.org/firefox/new/) any version or build from the source code.
-
-## Quick Setup
-
-Navigate to the `mozilla-central/devtools/client/netmonitor` folder with your terminal.
-Note that this folder is available after `mozilla-central` was cloned in order to get a local copy of the repository. Then run the following commands:
-
-```bash
-# Install packages
-yarn install
-
-# Create a dev server instance for hosting netmonitor on browser
-yarn start
-
-# Run firefox
-firefox http://localhost:8000 --start-debugger-server 6080
-```
-
-Then open `localhost:8000` in any browser to see all tabs in Firefox.
-
-### More detailed setup
-
-Instead of running command to open a new Firefox window like
-
-```
-firefox http://localhost:8000 --start-debugger-server 6080
-```
-
-If you have an opened Firefox browser, you can manually configure Firefox via type `about:config` in Firefox URL field, grant the warning to access preferences. And set these two preferences:
-
-* disable `devtools.debugger.prompt-connection` to remove the connection prompt.
-* enable `devtools.debugger.remote-enabled` to allow remote debugging a browser tab via the Mozilla remote debugging protocol (RDP)
-
-Go to the Web Developer menu in Firefox and select [Developer Toolbar](https://developer.mozilla.org/docs/Tools/GCLI). Run the command
-
-`listen 6080 mozilla-rdp`
-
-The command will make Firefox act as a remote debugging server.
-
-Run the command
-
-`yarn start`
-
-Then open `localhost:8000` in any browser to see all tabs in Firefox.
-
-### How it works
-
-The Network Monitor uses [webpack](https://webpack.js.org/) and several packages from [devtools-core](https://github.com/devtools-html/devtools-core) to run the Network Monitor as a normal web page. The Network Monitor uses [Mozilla remote debugging protocol](http://searchfox.org/mozilla-central/source/devtools/docs/backend/protocol.md) to fetch result and execute commands from Firefox browser.
-
-![](https://hacks.mozilla.org/files/2017/06/image4.png)
-
-Open `localhost:8000` in any browser to see the [launchpad](https://github.com/devtools-html/devtools-core/tree/master/packages/devtools-launchpad) interface. Devtools Launchpad will communicate with Firefox (the remote debugging server) and list all opened tabs from Firefox. Click one of the browser tab entry, now you can see the Network Monitor runs in a browser tab.
-
-### Develop with related modules
-
-When working on make the Network Monitor running in the browser tab, you may need to work on external modules. Besides the third party modules, here are modules required for the Network Monitor and is hosted under `devtools-html` (modules shared across Devtools):
-
-* [devtools-config](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-config/#readme) config used in dev server
-* [devtools-launchpad](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-launchpad/#readme) provide the dev server, landing page and the bootstrap functions to run devtools in the browser tab.
-* [devtools-modules](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-modules/#readme) Devtools shared and shim modules.
-* [devtools-source-editor](https://github.com/devtools-html/devtools-core/blob/master/packages/devtools-source-editor/#readme) Source Editor component.
-* [devtools-reps](https://github.com/devtools-html/debugger.html/blob/master/packages/devtools-reps/#readme) remote object formatter for variables representation.
-
-Do `yarn link` modules in related module directory, then do `yarn link [module-name]` after `yarn install` modules.
-
-## Code Structure
-
-Top level files are used to launch the Network Monitor inside of the DevTools toolbox or run in the browser tab (experimental). The Network Monitor source is mainly located in the `src/` folder, the same code base is used to run in both environments.
-
-We prefer use web standard API instead of FIrefox specific API, to make the Network Monitor can be opened in any browser tab.
-
 ### Run inside of the DevTools toolbox
 
 Files used to run the Network Monitor inside of the DevTools toolbox.
 
 * `panel.js` called by devtools toolbox to launch the Network Monitor panel.
 * `index.html` panel UI and launch scripts.
 * `src/connector/` wrap function call for Browser specific API. Current support Firefox and Chrome(experimental).
 
--- a/devtools/client/responsive.html/index.js
+++ b/devtools/client/responsive.html/index.js
@@ -132,17 +132,17 @@ window.getViewportSize = () => {
     return null;
   }
 
   const { width, height } = viewports[0];
   return { width, height };
 };
 
 /**
- * Called by manager.js to set viewport size from tests, GCLI, etc.
+ * Called by manager.js to set viewport size from tests, etc.
  */
 window.setViewportSize = ({ width, height }) => {
   try {
     bootstrap.dispatch(resizeViewport(0, width, height));
   } catch (e) {
     console.error(e);
   }
 };
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -48,17 +48,17 @@ const ResponsiveUIManager = exports.Resp
    *
    * @param window
    *        The main browser chrome window.
    * @param tab
    *        The browser tab.
    * @param options
    *        Other options associated with toggling.  Currently includes:
    *        - `trigger`: String denoting the UI entry point, such as:
-   *          - `command`:  GCLI command bar or toolbox button
+   *          - `toolbox`:  Toolbox Button
    *          - `menu`:     Web Developer menu item
    *          - `shortcut`: Keyboard shortcut
    * @return Promise
    *         Resolved when the toggling has completed.  If the UI has opened,
    *         it is resolved to the ResponsiveUI instance for this tab.  If the
    *         the UI has closed, there is no resolution value.
    */
   toggle(window, tab, options = {}) {
@@ -73,17 +73,17 @@ const ResponsiveUIManager = exports.Resp
    *
    * @param window
    *        The main browser chrome window.
    * @param tab
    *        The browser tab.
    * @param options
    *        Other options associated with opening.  Currently includes:
    *        - `trigger`: String denoting the UI entry point, such as:
-   *          - `command`:  GCLI command bar or toolbox button
+   *          - `toolbox`:  Toolbox Button
    *          - `menu`:     Web Developer menu item
    *          - `shortcut`: Keyboard shortcut
    * @return Promise
    *         Resolved to the ResponsiveUI instance for this tab when opening is
    *         complete.
    */
   async openIfNeeded(window, tab, options = {}) {
     if (!tab.linkedBrowser.isRemoteBrowser) {
@@ -130,17 +130,17 @@ const ResponsiveUIManager = exports.Resp
    *
    * @param window
    *        The main browser chrome window.
    * @param tab
    *        The browser tab.
    * @param options
    *        Other options associated with closing.  Currently includes:
    *        - `trigger`: String denoting the UI entry point, such as:
-   *          - `command`:  GCLI command bar or toolbox button
+   *          - `toolbox`:  Toolbox Button
    *          - `menu`:     Web Developer menu item
    *          - `shortcut`: Keyboard shortcut
    *        - `reason`: String detailing the specific cause for closing
    * @return Promise
    *         Resolved (with no value) when closing is complete.
    */
   async closeIfNeeded(window, tab, options = {}) {
     if (this.isActiveForTab(tab)) {
@@ -207,49 +207,16 @@ const ResponsiveUIManager = exports.Resp
    *        The browser tab.
    * @return ResponsiveUI
    *         The UI instance for this tab.
    */
   getResponsiveUIForTab(tab) {
     return this.activeTabs.get(tab);
   },
 
-  /**
-   * Handle GCLI commands.
-   *
-   * @param window
-   *        The main browser chrome window.
-   * @param tab
-   *        The browser tab.
-   * @param command
-   *        The GCLI command name.
-   * @param args
-   *        The GCLI command arguments.
-   */
-  handleGcliCommand(window, tab, command, args) {
-    let completed;
-    switch (command) {
-      case "resize to":
-        completed = this.openIfNeeded(window, tab, { trigger: "command" });
-        this.activeTabs.get(tab).setViewportSize(args);
-        break;
-      case "resize on":
-        completed = this.openIfNeeded(window, tab, { trigger: "command" });
-        break;
-      case "resize off":
-        completed = this.closeIfNeeded(window, tab, { trigger: "command" });
-        break;
-      case "resize toggle":
-        completed = this.toggle(window, tab, { trigger: "command" });
-        break;
-      default:
-    }
-    completed.catch(console.error);
-  },
-
   handleMenuCheck({target}) {
     ResponsiveUIManager.setMenuCheckFor(target);
   },
 
   initMenuCheckListenerFor(window) {
     const { tabContainer } = window.gBrowser;
     tabContainer.addEventListener("TabSelect", this.handleMenuCheck);
   },
@@ -267,25 +234,23 @@ const ResponsiveUIManager = exports.Resp
     const menu = window.document.getElementById("menu_responsiveUI");
     if (menu) {
       menu.setAttribute("checked", this.isActiveForTab(tab));
     }
   },
 
   showRemoteOnlyNotification(window, tab, { trigger } = {}) {
     showNotification(window, tab, {
-      command: trigger == "command",
+      toolboxButton: trigger == "toolbox",
       msg: l10n.getStr("responsive.remoteOnly"),
       priority: PriorityLevels.PRIORITY_CRITICAL_MEDIUM,
     });
   },
 };
 
-// GCLI commands in ./commands.js listen for events from this object to know
-// when the UI for a tab has opened or closed.
 EventEmitter.decorate(ResponsiveUIManager);
 
 /**
  * ResponsiveUI manages the responsive design tool for a specific tab.  The
  * actual tool itself lives in a separate chrome:// document that is loaded into
  * the tab upon opening responsive design.  This object acts a helper to
  * integrate the tool into the surrounding browser UI as needed.
  */
@@ -684,17 +649,17 @@ ResponsiveUI.prototype = {
   /**
    * Helper for tests. Assumes a single viewport for now.
    */
   getViewportSize() {
     return this.toolWindow.getViewportSize();
   },
 
   /**
-   * Helper for tests, GCLI, etc. Assumes a single viewport for now.
+   * Helper for tests, etc. Assumes a single viewport for now.
    */
   async setViewportSize(size) {
     await this.inited;
     this.toolWindow.setViewportSize(size);
   },
 
   /**
    * Helper for tests/reloading the viewport. Assumes a single viewport for now.
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -3,16 +3,18 @@ tags = devtools
 subsuite = devtools
 # !e10s: RDM only works for remote tabs
 # Win: Bug 1319248
 skip-if = !e10s || os == "win"
 support-files =
   contextual_identity.html
   devices.json
   doc_page_state.html
+  doc_toolbox_rule_view.css
+  doc_toolbox_rule_view.html
   favicon.html
   favicon.ico
   geolocation.html
   head.js
   touch.html
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/shared-redux-head.js
@@ -50,14 +52,15 @@ skip-if = true # Bug 1413765
 [browser_preloaded_newtab.js]
 [browser_screenshot_button.js]
 [browser_tab_close.js]
 [browser_tab_remoteness_change.js]
 [browser_target_blank.js]
 [browser_telemetry_activate_rdm.js]
 [browser_toolbox_computed_view.js]
 [browser_toolbox_rule_view.js]
+[browser_toolbox_rule_view_reload.js]
 [browser_toolbox_swap_browsers.js]
 [browser_toolbox_swap_inspector.js]
 [browser_touch_device.js]
 [browser_touch_simulation.js]
 [browser_viewport_basics.js]
 [browser_window_close.js]
--- a/devtools/client/responsive.html/test/browser/browser_toolbox_rule_view.js
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_rule_view.js
@@ -1,27 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Check that when the viewport is resized, the rule-view refreshes.
 
-const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
-                 "div {" +
-                 "  width: 500px;" +
-                 "  height: 10px;" +
-                 "  background: purple;" +
-                 "} " +
-                 "@media screen and (max-width: 200px) {" +
-                 "  div { " +
-                 "    width: 100px;" +
-                 "  }" +
-                 "};" +
-                 "</style><div></div></html>";
+const TEST_URI = `${URL_ROOT}doc_toolbox_rule_view.html`;
 
 addRDMTask(TEST_URI, async function({ ui, manager }) {
   info("Open the responsive design mode and set its size to 500x500 to start");
   await setViewportSize(ui, manager, 500, 500);
 
   info("Open the inspector, rule-view and select the test node");
   const { inspector, view } = await openRuleView();
   await selectNode("div", inspector);
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_rule_view_reload.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check that the ruleview is still correctly displayed after reloading the page.
+ * See Bug 1487284.
+ */
+
+// To trigger the initial issue, the stylesheet needs to be fetched from the network
+// monitor, so we can not use a data:uri with inline styles here.
+const TEST_URI = `${URL_ROOT}doc_toolbox_rule_view.html`;
+
+add_task(async function() {
+  const tab = await addTab(TEST_URI);
+
+  info("Open the rule-view and select the test node before opening RDM");
+  const { inspector, testActor, view } = await openRuleView();
+  await selectNode("div", inspector);
+
+  is(numberOfRules(view), 2, "Rule view has two rules.");
+
+  info("Open RDM");
+  await openRDM(tab);
+
+  info("Reload the current page");
+  const onNewRoot = inspector.once("new-root");
+  await testActor.reload();
+  await onNewRoot;
+  await inspector.markup._waitForChildren();
+
+  is(numberOfRules(view), 2, "Rule view still has two rules and is not empty.");
+
+  await closeRDM(tab);
+  await removeTab(tab);
+});
+
+function numberOfRules(ruleView) {
+  return ruleView.element.querySelectorAll(".ruleview-code").length;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/doc_toolbox_rule_view.css
@@ -0,0 +1,10 @@
+div {
+  width: 500px;
+  height: 10px;
+  background: purple;
+}
+@media screen and (max-width: 200px) {
+  div {
+    width: 100px;
+  }
+};
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/doc_toolbox_rule_view.html
@@ -0,0 +1,4 @@
+<html>
+  <link rel="stylesheet" charset="UTF-8" type="text/css" media="screen" href="doc_toolbox_rule_view.css"/>
+  <div></div>
+</html>
\ No newline at end of file
--- a/devtools/client/responsive.html/utils/notification.js
+++ b/devtools/client/responsive.html/utils/notification.js
@@ -12,29 +12,29 @@ loader.lazyRequireGetter(this, "gDevTool
  * a toolbox is currently open for this tab.
  *
  * @param window
  *        The main browser chrome window.
  * @param tab
  *        The browser tab.
  * @param options
  *        Other options associated with opening.  Currently includes:
- *        - `command`: Whether initiated via GCLI command bar or toolbox button
+ *        - `toolbox`: Whether initiated via toolbox button
  *        - `msg`: String to show in the notification
  *        - `priority`: Priority level for the notification, which affects the icon and
  *                      overall appearance.
  */
-function showNotification(window, tab, { command, msg, priority } = {}) {
+function showNotification(window, tab, { toolboxButton, msg, priority } = {}) {
   // Default to using the browser's per-tab notification box
   let nbox = window.gBrowser.getNotificationBox(tab.linkedBrowser);
 
-  // If opening was initiated by GCLI command bar or toolbox button, check for an open
+  // If opening was initiated by a toolbox button, check for an open
   // toolbox for the tab.  If one exists, use the toolbox's notification box so that the
   // message is placed closer to the action taken by the user.
-  if (command) {
+  if (toolboxButton) {
     const target = TargetFactory.forTab(tab);
     const toolbox = gDevTools.getToolbox(target);
     if (toolbox) {
       nbox = toolbox.notificationBox;
     }
   }
 
   const value = "devtools-responsive";
deleted file mode 100644
--- a/devtools/client/shared/developer-toolbar.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const promise = require("promise");
-
-loader.lazyRequireGetter(this, "gcliInit", "devtools/shared/gcli/commands/index");
-
-/**
- * A collection of utilities to help working with commands
- */
-var CommandUtils = {
-  /**
-   * Caches requisitions created when calling executeOnTarget:
-   * Target => Requisition Promise
-   */
-  _requisitions: new WeakMap(),
-
-  /**
-   * Utility to execute a command string on a given target
-   */
-  async executeOnTarget(target, command) {
-    let requisitionPromise = this._requisitions.get(target);
-    if (!requisitionPromise) {
-      requisitionPromise = this.createRequisition(target, {
-        environment: CommandUtils.createEnvironment({ target }, "target")
-      });
-      // Store the promise to avoid races by storing the promise immediately
-      this._requisitions.set(target, requisitionPromise);
-    }
-    const requisition = await requisitionPromise;
-    requisition.updateExec(command);
-  },
-
-  /**
-   * Utility to ensure that things are loaded in the correct order
-   */
-  createRequisition: function(target, options) {
-    if (!gcliInit) {
-      return promise.reject("Unable to load gcli");
-    }
-    return gcliInit.getSystem(target).then(system => {
-      const Requisition = require("gcli/cli").Requisition;
-      return new Requisition(system, options);
-    });
-  },
-
-  /**
-   * Destroy the remote side of the requisition as well as the local side
-   */
-  destroyRequisition: function(requisition, target) {
-    requisition.destroy();
-    gcliInit.releaseSystem(target);
-  },
-
-  /**
-   * A helper function to create the environment object that is passed to
-   * GCLI commands.
-   * @param targetContainer An object containing a 'target' property which
-   * reflects the current debug target
-   */
-  createEnvironment: function(container, targetProperty = "target") {
-    if (!container[targetProperty].toString ||
-        !/TabTarget/.test(container[targetProperty].toString())) {
-      throw new Error("Missing target");
-    }
-
-    return {
-      get target() {
-        if (!container[targetProperty].toString ||
-            !/TabTarget/.test(container[targetProperty].toString())) {
-          throw new Error("Removed target");
-        }
-
-        return container[targetProperty];
-      },
-
-      get chromeWindow() {
-        return this.target.tab.ownerDocument.defaultView;
-      },
-
-      get chromeDocument() {
-        return this.target.tab.ownerDocument.defaultView.document;
-      },
-
-      get window() {
-        // throw new
-        //    Error("environment.window is not available in runAt:client commands");
-        return this.chromeWindow.gBrowser.contentWindowAsCPOW;
-      },
-
-      get document() {
-        // throw new
-        //    Error("environment.document is not available in runAt:client commands");
-        return this.chromeWindow.gBrowser.contentDocumentAsCPOW;
-      }
-    };
-  },
-};
-
-exports.CommandUtils = CommandUtils;
--- a/devtools/client/shared/keycodes.js
+++ b/devtools/client/shared/keycodes.js
@@ -1,28 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-// This was copied (and slightly modified) from
-// devtools/shared/gcli/source/lib/gcli/util/util.js, which in turn
-// says:
-
 /**
  * Keyboard handling is a mess. http://unixpapa.com/js/key.html
  * It would be good to use DOM L3 Keyboard events,
  * http://www.w3.org/TR/2010/WD-DOM-Level-3-Events-20100907/#events-keyboardevents
  * however only Webkit supports them, and there isn't a shim on Modernizr:
  * https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills
  * and when the code that uses this KeyEvent was written, nothing was clear,
  * so instead, we're using this unmodern shim:
  * http://stackoverflow.com/questions/5681146/chrome-10-keyevent-or-something-similar-to-firefoxs-keyevent
- * See BUG 664991: GCLI's keyboard handling should be updated to use DOM-L3
+ * See BUG 664991: keyboard handling should be updated to use DOM-L3
  * https://bugzilla.mozilla.org/show_bug.cgi?id=664991
  */
 
 exports.KeyCodes = {
   DOM_VK_CANCEL: 3,
   DOM_VK_HELP: 6,
   DOM_VK_BACK_SPACE: 8,
   DOM_VK_TAB: 9,
@@ -131,16 +127,13 @@ exports.KeyCodes = {
   DOM_VK_PERIOD: 190,
   DOM_VK_SLASH: 191,
   DOM_VK_BACK_QUOTE: 192,
   DOM_VK_OPEN_BRACKET: 219,
   DOM_VK_BACK_SLASH: 220,
   DOM_VK_CLOSE_BRACKET: 221,
   DOM_VK_QUOTE: 222,
   DOM_VK_META: 224,
-
-  // A few that did not appear in gcli, but that are apparently used
-  // in devtools.
   DOM_VK_COLON: 58,
   DOM_VK_VOLUME_MUTE: 181,
   DOM_VK_VOLUME_DOWN: 182,
   DOM_VK_VOLUME_UP: 183,
 };
--- a/devtools/client/shared/moz.build
+++ b/devtools/client/shared/moz.build
@@ -20,17 +20,16 @@ DIRS += [
 ]
 
 DevToolsModules(
     'autocomplete-popup.js',
     'browser-loader.js',
     'css-angle.js',
     'curl.js',
     'demangle.js',
-    'developer-toolbar.js',
     'devices.js',
     'DOMHelpers.jsm',
     'enum.js',
     'file-saver.js',
     'focus.js',
     'getjson.js',
     'inplace-editor.js',
     'key-shortcuts.js',
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -667,17 +667,16 @@ function getChartsFromToolId(id) {
   if (id === "NEWANIMATIONINSPECTOR") {
     id = "ANIMATIONINSPECTOR";
   }
 
   switch (id) {
     case "ABOUTDEBUGGING":
     case "BROWSERCONSOLE":
     case "CANVASDEBUGGER":
-    case "DEVELOPERTOOLBAR":
     case "DOM":
     case "INSPECTOR":
     case "JSBROWSERDEBUGGER":
     case "JSDEBUGGER":
     case "JSPROFILER":
     case "MEMORY":
     case "NETMONITOR":
     case "OPTIONS":
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -193,15 +193,14 @@ skip-if = !e10s || os == "win" # RDM onl
 [browser_telemetry_toolboxtabs_jsprofiler.js]
 [browser_telemetry_toolboxtabs_netmonitor.js]
 [browser_telemetry_toolboxtabs_options.js]
 [browser_telemetry_toolboxtabs_shadereditor.js]
 [browser_telemetry_toolboxtabs_storage.js]
 [browser_telemetry_toolboxtabs_styleeditor.js]
 [browser_telemetry_toolboxtabs_webaudioeditor.js]
 [browser_telemetry_toolboxtabs_webconsole.js]
-[browser_templater_basic.js]
 [browser_treeWidget_basic.js]
 [browser_treeWidget_keyboard_interaction.js]
 [browser_treeWidget_mouse_interaction.js]
 [browser_devices.js]
 skip-if = verify
 [browser_theme_switching.js]
deleted file mode 100644
--- a/devtools/client/shared/test/browser_templater_basic.js
+++ /dev/null
@@ -1,287 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Tests that the DOM Template engine works properly
-
-/*
- * These tests run both in Mozilla/Mochitest and plain browsers (as does
- * domtemplate)
- * We should endevour to keep the source in sync.
- */
-
-const {template} = require("devtools/shared/gcli/templater");
-
-const TEST_URI = TEST_URI_ROOT + "doc_templater_basic.html";
-
-var test = async function() {
-  await addTab("about:blank");
-  const [host,, doc] = await createHost("bottom", TEST_URI);
-
-  info("Starting DOM Templater Tests");
-  runTest(0, host, doc);
-};
-
-function runTest(index, host, doc) {
-  const options = tests[index] = tests[index]();
-  const holder = doc.createElement("div");
-  holder.id = options.name;
-  const body = doc.body;
-  body.appendChild(holder);
-  // eslint-disable-next-line no-unsanitized/property
-  holder.innerHTML = options.template;
-
-  info("Running " + options.name);
-  template(holder, options.data, options.options);
-
-  if (typeof options.result == "string") {
-    is(holder.innerHTML, options.result, options.name);
-  } else {
-    ok(holder.innerHTML.match(options.result) != null,
-       options.name + " result='" + holder.innerHTML + "'");
-  }
-
-  if (options.also) {
-    options.also(options);
-  }
-
-  function runNextTest() {
-    index++;
-    if (index < tests.length) {
-      runTest(index, host, doc);
-    } else {
-      finished(host);
-    }
-  }
-
-  if (options.later) {
-    const ais = is.bind(this);
-
-    function createTester(testHolder, testOptions) {
-      return () => {
-        ais(testHolder.innerHTML, testOptions.later, testOptions.name + " later");
-        runNextTest();
-      };
-    }
-
-    executeSoon(createTester(holder, options));
-  } else {
-    runNextTest();
-  }
-}
-
-function finished(host) {
-  host.destroy();
-  gBrowser.removeCurrentTab();
-  info("Finishing DOM Templater Tests");
-  tests = null;
-  finish();
-}
-
-/**
- * Why have an array of functions that return data rather than just an array
- * of the data itself? Some of these tests contain calls to delayReply() which
- * sets up async processing using executeSoon(). Since the execution of these
- * tests is asynchronous, the delayed reply will probably arrive before the
- * test is executed, making the test be synchronous. So we wrap the data in a
- * function so we only set it up just before we use it.
- */
-var tests = [
-  () => ({
-    name: "simpleNesting",
-    template: '<div id="ex1">${nested.value}</div>',
-    data: { nested: { value: "pass 1" } },
-    result: '<div id="ex1">pass 1</div>'
-  }),
-
-  () => ({
-    name: "returnDom",
-    template: '<div id="ex2">${__element.ownerDocument.createTextNode(\'pass 2\')}</div>',
-    options: { allowEval: true },
-    data: {},
-    result: '<div id="ex2">pass 2</div>'
-  }),
-
-  () => ({
-    name: "srcChange",
-    template: '<img _src="${fred}" id="ex3">',
-    data: { fred: "green.png" },
-    result: /<img( id="ex3")? src="green.png"( id="ex3")?>/
-  }),
-
-  () => ({
-    name: "ifTrue",
-    template: '<p if="${name !== \'jim\'}">hello ${name}</p>',
-    options: { allowEval: true },
-    data: { name: "fred" },
-    result: "<p>hello fred</p>"
-  }),
-
-  () => ({
-    name: "ifFalse",
-    template: '<p if="${name !== \'jim\'}">hello ${name}</p>',
-    options: { allowEval: true },
-    data: { name: "jim" },
-    result: ""
-  }),
-
-  () => ({
-    name: "simpleLoop",
-    template: '<p foreach="index in ${[ 1, 2, 3 ]}">${index}</p>',
-    options: { allowEval: true },
-    data: {},
-    result: "<p>1</p><p>2</p><p>3</p>"
-  }),
-
-  () => ({
-    name: "loopElement",
-    template: '<loop foreach="i in ${array}">${i}</loop>',
-    data: { array: [ 1, 2, 3 ] },
-    result: "123"
-  }),
-
-  // Bug 692028: DOMTemplate memory leak with asynchronous arrays
-  // Bug 692031: DOMTemplate async loops do not drop the loop element
-  () => ({
-    name: "asyncLoopElement",
-    template: '<loop foreach="i in ${array}">${i}</loop>',
-    data: { array: delayReply([1, 2, 3]) },
-    result: "<span></span>",
-    later: "123"
-  }),
-
-  () => ({
-    name: "saveElement",
-    template: '<p save="${element}">${name}</p>',
-    data: { name: "pass 8" },
-    result: "<p>pass 8</p>",
-    also: function(options) {
-      is(options.data.element.innerHTML, "pass 8", "saveElement saved");
-      delete options.data.element;
-    }
-  }),
-
-  () => ({
-    name: "useElement",
-    template: '<p id="pass9">${adjust(__element)}</p>',
-    options: { allowEval: true },
-    data: {
-      adjust: function(element) {
-        is("pass9", element.id, "useElement adjust");
-        return "pass 9b";
-      }
-    },
-    result: '<p id="pass9">pass 9b</p>'
-  }),
-
-  () => ({
-    name: "asyncInline",
-    template: "${delayed}",
-    data: { delayed: delayReply("inline") },
-    result: "<span></span>",
-    later: "inline"
-  }),
-
-  // Bug 692028: DOMTemplate memory leak with asynchronous arrays
-  () => ({
-    name: "asyncArray",
-    template: '<p foreach="i in ${delayed}">${i}</p>',
-    data: { delayed: delayReply([1, 2, 3]) },
-    result: "<span></span>",
-    later: "<p>1</p><p>2</p><p>3</p>"
-  }),
-
-  () => ({
-    name: "asyncMember",
-    template: '<p foreach="i in ${delayed}">${i}</p>',
-    data: { delayed: [delayReply(4), delayReply(5), delayReply(6)] },
-    result: "<span></span><span></span><span></span>",
-    later: "<p>4</p><p>5</p><p>6</p>"
-  }),
-
-  // Bug 692028: DOMTemplate memory leak with asynchronous arrays
-  () => ({
-    name: "asyncBoth",
-    template: '<p foreach="i in ${delayed}">${i}</p>',
-    data: {
-      delayed: delayReply([
-        delayReply(4),
-        delayReply(5),
-        delayReply(6)
-      ])
-    },
-    result: "<span></span>",
-    later: "<p>4</p><p>5</p><p>6</p>"
-  }),
-
-  // Bug 701762: DOMTemplate fails when ${foo()} returns undefined
-  () => ({
-    name: "functionReturningUndefiend",
-    template: "<p>${foo()}</p>",
-    options: { allowEval: true },
-    data: {
-      foo: function() {}
-    },
-    result: "<p>undefined</p>"
-  }),
-
-  // Bug 702642: DOMTemplate is relatively slow when evaluating JS ${}
-  () => ({
-    name: "propertySimple",
-    template: "<p>${a.b.c}</p>",
-    data: { a: { b: { c: "hello" } } },
-    result: "<p>hello</p>"
-  }),
-
-  () => ({
-    name: "propertyPass",
-    template: "<p>${Math.max(1, 2)}</p>",
-    options: { allowEval: true },
-    result: "<p>2</p>"
-  }),
-
-  () => ({
-    name: "propertyFail",
-    template: "<p>${Math.max(1, 2)}</p>",
-    result: "<p>${Math.max(1, 2)}</p>"
-  }),
-
-  // Bug 723431: DOMTemplate should allow customisation of display of
-  // null/undefined values
-  () => ({
-    name: "propertyUndefAttrFull",
-    template: "<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>",
-    data: { nullvar: null, undefinedvar1: undefined },
-    result: "<p>null|undefined|undefined</p>"
-  }),
-
-  () => ({
-    name: "propertyUndefAttrBlank",
-    template: "<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>",
-    data: { nullvar: null, undefinedvar1: undefined },
-    options: { blankNullUndefined: true },
-    result: "<p>||</p>"
-  }),
-
-  /* eslint-disable max-len */
-  () => ({
-    name: "propertyUndefAttrFull",
-    template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>',
-    data: { nullvar: null, undefinedvar1: undefined },
-    result: '<div><p value="null"></p><p value="undefined"></p><p value="undefined"></p></div>'
-  }),
-
-  () => ({
-    name: "propertyUndefAttrBlank",
-    template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>',
-    data: { nullvar: null, undefinedvar1: undefined },
-    options: { blankNullUndefined: true },
-    result: '<div><p value=""></p><p value=""></p><p value=""></p></div>'
-  })
-  /* eslint-enable max-len */
-];
-
-function delayReply(data) {
-  return new Promise(resolve => resolve(data));
-}
deleted file mode 100644
--- a/devtools/client/themes/images/gcli_sec_bad.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg">
-  <circle cx="15" cy="15" r="15" fill="#e74c3c"/>
-  <g stroke="white" stroke-width="3">
-    <line x1="9" y1="9" x2="21" y2="21"/>
-    <line x1="21" y1="9" x2="9" y2="21"/>
-  </g>
-</svg>
\ No newline at end of file
deleted file mode 100644
--- a/devtools/client/themes/images/gcli_sec_good.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60">
-  <circle cx="30" cy="30" r="30" fill="#2CBB0F"/>
-  <polygon points="17,32 25,39 26,39 44,18 45,18 48,21 27,46 25,46 14,36 13,36 16,33 16,32" fill="white"/>
-</svg>
\ No newline at end of file
deleted file mode 100644
--- a/devtools/client/themes/images/gcli_sec_moderate.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-<svg width="30" height="30" xmlns="http://www.w3.org/2000/svg">
-  <circle cx="15" cy="15" r="15" fill="#F5B400"/>
-  <rect x="7.5" y="13" width="15" height="4" fill="white"/>
-</svg>
\ No newline at end of file
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -74,16 +74,24 @@ a {
    * Use vertical margins on children instead. */
   padding-inline-start: 1px;
   padding-inline-end: 8px;
   border-inline-start: solid 3px transparent;
   font-size: var(--console-output-font-size);
   line-height: var(--console-output-line-height);
 }
 
+/*
+ * By default, prevent any element in message to overflow.
+ * This makes console reflows faster (See Bug 1487457).
+ */
+.message * {
+  overflow: hidden;
+}
+
 .message:hover {
   border-inline-start-color: var(--theme-highlight-blue);
 }
 
 .message.error {
   color: var(--error-color);
   background-color: var(--error-background-color);
 }
@@ -488,20 +496,16 @@ a.learn-more-link.webconsole-learn-more-
 .open-inspector:hover {
   filter: var(--theme-icon-checked-filter);
 }
 
 .open-inspector:active {
   filter: var(--theme-icon-checked-filter) brightness(0.9);
 }
 
-#output-container {
-  height: 100%;
-}
-
 /*
   This element contains the different toolbars in the console
     - primary, containing the clear messages button and the text search input.
       It can expand as much as it need.
     - filtered messages, containing the "X items hidden by filters" and the reset filters button.
       It should be on the same row than the primary bar if it fits there, or on its own 100% row if it is wrapped.
     - close button, close the split console panel. This button will be displayed on righ-top of tool bar always.
     - secondary, containing the filter buttons (Error, Warning, …).
@@ -807,34 +811,25 @@ a.learn-more-link.webconsole-learn-more-
   direction: ltr;
   overflow: auto;
   -moz-user-select: text;
   position: relative;
 }
 
 html,
 body {
-  height: 100%;
+  height: 100vh;
   margin: 0;
   padding: 0;
-}
-
-body {
   overflow: hidden;
 }
 
 #app-wrapper {
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-}
-
-body #output-container {
-  flex: 1;
-  overflow: hidden;
+  height: 100vh;
+  max-height: 100vh;
 }
 
 
 /*
  * Here's what the layout of the console looks like:
  *
  *  +------------------------------+--------------+
  *  |                              |              |
@@ -862,33 +857,36 @@ body #output-container {
   flex-direction: column;
   height: 100vh;
   max-height: 100vh;
   overflow: hidden;
 }
 
 .webconsole-flex-wrapper .webconsole-filteringbar-wrapper {
   flex-shrink: 0;
+  overflow: hidden;
 }
 
 .webconsole-flex-wrapper .webconsole-output {
   flex-shrink: 100000;
+  overflow-x: hidden;
 }
 
 .webconsole-flex-wrapper > .webconsole-output:not(:empty) {
   min-height: 19px;
 }
 
 .webconsole-output-wrapper #webconsole-notificationbox {
   flex-shrink: 0;
 }
 
 .webconsole-output-wrapper .jsterm-input-container {
   min-height: 28px;
-  overflow: auto;
+  overflow-y: auto;
+  overflow-x: hidden;
 }
 
 .jsterm-cm .jsterm-input-container {
   padding-block-start: 2px;
 }
 
 .webconsole-flex-wrapper > .webconsole-output:empty ~ .jsterm-input-container {
   border-top: none;
--- a/devtools/client/webconsole/index.html
+++ b/devtools/client/webconsole/index.html
@@ -21,13 +21,12 @@
     <link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/httpi.css"/>
 
     <script type="text/javascript" src="chrome://global/content/l10n.js"></script>
     <script src="chrome://devtools/content/shared/theme-switching.js"></script>
     <script type="application/javascript"
             src="resource://devtools/client/webconsole/main.js"></script>
   </head>
   <body class="theme-sidebar" role="application">
-    <div id="app-wrapper" class="theme-body">
-      <div id="output-container" role="document" aria-live="polite"></div>
-    </div>
+    <main id="app-wrapper" class="theme-body" role="document" aria-live="polite">
+    </main>
   </body>
 </html>
--- a/devtools/client/webconsole/webconsole-frame.js
+++ b/devtools/client/webconsole/webconsole-frame.js
@@ -223,17 +223,17 @@ WebConsoleFrame.prototype = {
 
     return this._initDefer.promise;
   },
 
   _initUI: function() {
     this.document = this.window.document;
     this.rootElement = this.document.documentElement;
 
-    this.outputNode = this.document.getElementById("output-container");
+    this.outputNode = this.document.getElementById("app-wrapper");
 
     const toolbox = gDevTools.getToolbox(this.owner.target);
 
     this.consoleOutput = new this.window.WebConsoleOutput(
       this.outputNode, this, toolbox, this.owner, this.document);
     // Toggle the timestamp on preference change
     Services.prefs.addObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
     this._onToolboxPrefChanged();
--- a/devtools/docs/files/adding-files.md
+++ b/devtools/docs/files/adding-files.md
@@ -146,18 +146,18 @@ Locale URLs differ somewhat based on whe
 
 Example:
 
 * File: `/devtools/client/locales/en-US/debugger.dtd`
 * Usage: `chrome://devtools/locale/debugger.dtd`
 
 Example:
 
-* File: `/devtools/shared/locales/en-US/gcli.properties`
-* Usage: `chrome://devtools-shared/locale/gcli.properties`
+* File: `/devtools/shared/locales/en-US/screenshot.properties`
+* Usage: `chrome://devtools-shared/locale/screenshot.properties`
 
 ### Guidelines
 
 Localization files should follow a set of guidelines aimed at making it easier for people to translate the labels in these files in many languages.
 
 [Find these guidelines on MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_content_best_practices).
 
 In particular, it's important to write self-explanatory comments for new keys, deleting unused keys, changing the key name when changing the meaning of a string, and more. So make sure you read through these guidelines should you have to modify a localization file in your patch.
--- a/devtools/docs/frontend/telemetry.md
+++ b/devtools/docs/frontend/telemetry.md
@@ -116,17 +116,17 @@ devtools.main:
     objects: ["tools"]
     bug_numbers: [1416024]
     notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
     record_in_processes: ["main"]
     description: User opens devtools toolbox.
     release_channel_collection: opt-out
     expiry_version: never
     extra_keys:
-      entrypoint: How was the toolbox opened? CommandLine, ContextMenu, DeveloperToolbar, HamburgerMenu, KeyShortcut, SessionRestore or SystemMenu
+      entrypoint: How was the toolbox opened? CommandLine, ContextMenu, HamburgerMenu, KeyShortcut, SessionRestore or SystemMenu
       first_panel: The name of the first panel opened.
       host: "Toolbox host (positioning): bottom, side, window or other."
       splitconsole: Indicates whether the split console was open.
       width: Toolbox width (px).
 ```
 
 ### 2. Using Histograms.json probes in DevTools code
 
deleted file mode 100644
--- a/devtools/server/actors/gcli.js
+++ /dev/null
@@ -1,228 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
-const { gcliSpec } = require("devtools/shared/specs/gcli");
-const { createSystem } = require("gcli/system");
-
-/**
- * Manage remote connections that want to talk to GCLI
- */
-const GcliActor = ActorClassWithSpec(gcliSpec, {
-  initialize: function(conn, targetActor) {
-    Actor.prototype.initialize.call(this, conn);
-
-    this._commandsChanged = this._commandsChanged.bind(this);
-
-    this._targetActor = targetActor;
-    // see _getRequisition()
-    this._requisitionPromise = undefined;
-  },
-
-  destroy: function() {
-    Actor.prototype.destroy.call(this);
-
-    // If _getRequisition has not been called, just bail quickly
-    if (this._requisitionPromise == null) {
-      this._commandsChanged = undefined;
-      this._targetActor = undefined;
-      return Promise.resolve();
-    }
-
-    return this._getRequisition().then(requisition => {
-      requisition.destroy();
-
-      this._system.commands.onCommandsChange.remove(this._commandsChanged);
-      this._system.destroy();
-      this._system = undefined;
-
-      this._requisitionPromise = undefined;
-      this._targetActor = undefined;
-
-      this._commandsChanged = undefined;
-    });
-  },
-
-  /**
-   * Load a module into the requisition
-   */
-  _testOnlyAddItemsByModule: function(names) {
-    return this._getRequisition().then(requisition => {
-      return requisition.system.addItemsByModule(names);
-    });
-  },
-
-  /**
-   * Unload a module from the requisition
-   */
-  _testOnlyRemoveItemsByModule: function(names) {
-    return this._getRequisition().then(requisition => {
-      return requisition.system.removeItemsByModule(names);
-    });
-  },
-
-  /**
-   * Retrieve a list of the remotely executable commands
-   * @param customProps Array of strings containing additional properties which,
-   * if specified in the command spec, will be included in the JSON. Normally we
-   * transfer only the properties required for GCLI to function.
-   */
-  specs: function(customProps) {
-    return this._getRequisition().then(requisition => {
-      return requisition.system.commands.getCommandSpecs(customProps);
-    });
-  },
-
-  /**
-   * Execute a GCLI command
-   * @return a promise of an object with the following properties:
-   * - data: The output of the command
-   * - type: The type of the data to allow selection of a converter
-   * - error: True if the output was considered an error
-   */
-  execute: function(typed) {
-    return this._getRequisition().then(requisition => {
-      return requisition.updateExec(typed).then(output => output.toJson());
-    });
-  },
-
-  /**
-   * Get the state of an input string. i.e. requisition.getStateData()
-   */
-  state: function(typed, start, rank) {
-    return this._getRequisition().then(requisition => {
-      return requisition.update(typed).then(() => {
-        return requisition.getStateData(start, rank);
-      });
-    });
-  },
-
-  /**
-   * Call type.parse to check validity. Used by the remote type
-   * @return a promise of an object with the following properties:
-   * - status: Of of the following strings: VALID|INCOMPLETE|ERROR
-   * - message: The message to display to the user
-   * - predictions: An array of suggested values for the given parameter
-   */
-  parseType: function(typed, paramName) {
-    return this._getRequisition().then(requisition => {
-      return requisition.update(typed).then(() => {
-        const assignment = requisition.getAssignment(paramName);
-        return Promise.resolve(assignment.predictions).then(predictions => {
-          return {
-            status: assignment.getStatus().toString(),
-            message: assignment.message,
-            predictions: predictions
-          };
-        });
-      });
-    });
-  },
-
-  /**
-   * Get the incremented/decremented value of some type
-   * @return a promise of a string containing the new argument text
-   */
-  nudgeType: function(typed, by, paramName) {
-    return this.requisition.update(typed).then(() => {
-      const assignment = this.requisition.getAssignment(paramName);
-      return this.requisition.nudge(assignment, by).then(() => {
-        return assignment.arg == null ? undefined : assignment.arg.text;
-      });
-    });
-  },
-
-  /**
-   * Perform a lookup on a selection type to get the allowed values
-   */
-  getSelectionLookup: function(commandName, paramName) {
-    return this._getRequisition().then(requisition => {
-      const command = requisition.system.commands.get(commandName);
-      if (command == null) {
-        throw new Error("No command called '" + commandName + "'");
-      }
-
-      let type;
-      command.params.forEach(param => {
-        if (param.name === paramName) {
-          type = param.type;
-        }
-      });
-
-      if (type == null) {
-        throw new Error("No parameter called '" + paramName + "' in '" +
-                        commandName + "'");
-      }
-
-      const reply = type.getLookup(requisition.executionContext);
-      return Promise.resolve(reply).then(lookup => {
-        // lookup returns an array of objects with name/value properties and
-        // the values might not be JSONable, so remove them
-        return lookup.map(info => ({ name: info.name }));
-      });
-    });
-  },
-
-  /**
-   * Lazy init for a Requisition
-   */
-  _getRequisition: function() {
-    if (this._targetActor == null) {
-      throw new Error("GcliActor used post-destroy");
-    }
-
-    if (this._requisitionPromise != null) {
-      return this._requisitionPromise;
-    }
-
-    const Requisition = require("gcli/cli").Requisition;
-    const targetActor = this._targetActor;
-
-    this._system = createSystem({ location: "server" });
-    this._system.commands.onCommandsChange.add(this._commandsChanged);
-
-    const gcliInit = require("devtools/shared/gcli/commands/index");
-    gcliInit.addAllItemsByModule(this._system);
-
-    // this._requisitionPromise should be created synchronously with the call
-    // to _getRequisition so that destroy can tell whether there is an async
-    // init in progress
-    this._requisitionPromise = this._system.load().then(() => {
-      const environment = {
-        get chromeWindow() {
-          throw new Error("environment.chromeWindow is not available in runAt:server" +
-            " commands");
-        },
-
-        get chromeDocument() {
-          throw new Error("environment.chromeDocument is not available in runAt:server" +
-            " commands");
-        },
-
-        get window() {
-          return targetActor.window;
-        },
-
-        get document() {
-          return targetActor.window && targetActor.window.document;
-        }
-      };
-
-      return new Requisition(this._system, { environment: environment });
-    });
-
-    return this._requisitionPromise;
-  },
-
-  /**
-   * Pass events from requisition.system.commands.onCommandsChange upwards
-   */
-  _commandsChanged: function() {
-    this.emit("commands-changed");
-  },
-});
-
-exports.GcliActor = GcliActor;
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -548,18 +548,17 @@ exports.CustomHighlighterActor = protoco
  * highlighters to work: the window, docShell, event listener target, ...
  * It also emits "will-navigate", "navigate" and "window-ready" events,
  * similarly to the BrowsingContextTargetActor.
  *
  * It can be initialized either from a BrowsingContextTargetActor (which is the
  * most frequent way of using it, since highlighters are usually initialized by
  * the HighlighterActor or CustomHighlighterActor, which have a targetActor
  * reference). It can also be initialized just with a window object (which is
- * useful for when a highlighter is used outside of the debugger server context,
- * for instance from a gcli command).
+ * useful for when a highlighter is used outside of the debugger server context.
  */
 function HighlighterEnvironment() {
   this.relayTargetActorWindowReady = this.relayTargetActorWindowReady.bind(this);
   this.relayTargetActorNavigate = this.relayTargetActorNavigate.bind(this);
   this.relayTargetActorWillNavigate = this.relayTargetActorWillNavigate.bind(this);
 
   EventEmitter.decorate(this);
 }
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -33,17 +33,16 @@ DevToolsModules(
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'emulation.js',
     'environment.js',
     'errordocs.js',
     'frame.js',
     'framerate.js',
-    'gcli.js',
     'heap-snapshot-file.js',
     'highlighters.css',
     'highlighters.js',
     'layout.js',
     'memory.js',
     'network-event.js',
     'network-monitor.js',
     'object.js',
--- a/devtools/server/actors/network-monitor.js
+++ b/devtools/server/actors/network-monitor.js
@@ -40,63 +40,84 @@ const NetworkMonitorActor = ActorClassWi
     this.messageManager = messageManager;
 
     // Immediately start watching for new request according to `filters`.
     // NetworkMonitor will call `onNetworkEvent` method.
     this.observer = new NetworkObserver(filters, this);
     this.observer.init();
 
     this.stackTraces = new Set();
+
     this.onStackTraceAvailable = this.onStackTraceAvailable.bind(this);
-    this.messageManager.addMessageListener("debug:request-stack-available",
-      this.onStackTraceAvailable);
     this.onRequestContent = this.onRequestContent.bind(this);
-    this.messageManager.addMessageListener("debug:request-content:request",
-      this.onRequestContent);
     this.onSetPreference = this.onSetPreference.bind(this);
-    this.messageManager.addMessageListener("debug:netmonitor-preference",
-      this.onSetPreference);
     this.onGetNetworkEventActor = this.onGetNetworkEventActor.bind(this);
-    this.messageManager.addMessageListener("debug:get-network-event-actor:request",
-      this.onGetNetworkEventActor);
     this.onDestroyMessage = this.onDestroyMessage.bind(this);
-    this.messageManager.addMessageListener("debug:destroy-network-monitor",
-      this.onDestroyMessage);
+
+    this.startListening();
   },
 
   onDestroyMessage({ data }) {
     if (data.actorID == this.parentID) {
       this.destroy();
     }
   },
 
+  startListening() {
+    this.messageManager.addMessageListener("debug:request-stack-available",
+      this.onStackTraceAvailable);
+    this.messageManager.addMessageListener("debug:request-content:request",
+      this.onRequestContent);
+    this.messageManager.addMessageListener("debug:netmonitor-preference",
+      this.onSetPreference);
+    this.messageManager.addMessageListener("debug:get-network-event-actor:request",
+      this.onGetNetworkEventActor);
+    this.messageManager.addMessageListener("debug:destroy-network-monitor",
+      this.onDestroyMessage);
+  },
+
+  stopListening() {
+    this.messageManager.removeMessageListener("debug:request-stack-available",
+      this.onStackTraceAvailable);
+    this.messageManager.removeMessageListener("debug:request-content:request",
+      this.onRequestContent);
+    this.messageManager.removeMessageListener("debug:netmonitor-preference",
+      this.onSetPreference);
+    this.messageManager.removeMessageListener("debug:get-network-event-actor:request",
+      this.onGetNetworkEventActor);
+    this.messageManager.removeMessageListener("debug:destroy-network-monitor",
+      this.onDestroyMessage);
+  },
+
   destroy() {
     Actor.prototype.destroy.call(this);
 
     if (this.observer) {
       this.observer.destroy();
       this.observer = null;
     }
 
     this.stackTraces.clear();
     if (this.messageManager) {
-      this.messageManager.removeMessageListener("debug:request-stack-available",
-        this.onStackTraceAvailable);
-      this.messageManager.removeMessageListener("debug:request-content:request",
-        this.onRequestContent);
-      this.messageManager.removeMessageListener("debug:netmonitor-preference",
-        this.onSetPreference);
-      this.messageManager.removeMessageListener("debug:get-network-event-actor:request",
-        this.onGetNetworkEventActor);
-      this.messageManager.removeMessageListener("debug:destroy-network-monitor",
-        this.onDestroyMessage);
+      this.stopListening();
       this.messageManager = null;
     }
   },
 
+  /**
+   * onBrowserSwap is called by the server when a browser frame swap occurs (typically
+   * switching on/off RDM) and a new message manager should be used.
+   */
+  onBrowserSwap(mm) {
+    this.stopListening();
+    this.messageManager = mm;
+    this.stackTraces = new Set();
+    this.startListening();
+  },
+
   onStackTraceAvailable(msg) {
     const { channelId } = msg.data;
     if (!msg.data.stacktrace) {
       this.stackTraces.delete(channelId);
     } else {
       this.stackTraces.add(channelId);
     }
   },
--- a/devtools/server/actors/webconsole/commands.js
+++ b/devtools/server/actors/webconsole/commands.js
@@ -111,17 +111,17 @@ function parseCommand(tokens) {
     if (token.type === KEY) {
       const nextTokenIndex = i + 1;
       const nextToken = tokens[nextTokenIndex];
       let values = args[token.value] || DEFAULT_VALUE;
       if (nextToken && nextToken.type === ARG) {
         const { value, offset } = collectString(nextToken, tokens, nextTokenIndex);
         // in order for JSON.stringify to correctly output values, they must be correctly
         // typed
-        // As per the GCLI documentation, we can only have one value associated with a
+        // As per the old GCLI documentation, we can only have one value associated with a
         // flag but multiple flags with the same name can exist and should be combined
         // into and array.  Here we are associating only the value on the right hand
         // side if it is of type `arg` as a single value; the second case initializes
         // an array, and the final case pushes a value to an existing array
         const typedValue = getTypedValue(value);
         if (values === DEFAULT_VALUE) {
           values = typedValue;
         } else if (!Array.isArray(values)) {
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -353,21 +353,16 @@ var DebuggerServer = {
       constructor: "StyleSheetsActor",
       type: { target: true }
     });
     this.registerModule("devtools/server/actors/storage", {
       prefix: "storage",
       constructor: "StorageActor",
       type: { target: true }
     });
-    this.registerModule("devtools/server/actors/gcli", {
-      prefix: "gcli",
-      constructor: "GcliActor",
-      type: { target: true }
-    });
     this.registerModule("devtools/server/actors/memory", {
       prefix: "memory",
       constructor: "MemoryActor",
       type: { target: true }
     });
     this.registerModule("devtools/server/actors/framerate", {
       prefix: "framerate",
       constructor: "FramerateActor",
@@ -934,16 +929,17 @@ var DebuggerServer = {
             "Exception during actor module setup running in the parent process: ";
           DevToolsUtils.reportException(errorMessage + e);
           dumpn(`ERROR: ${errorMessage}\n\t module: '${module}'\n\t ` +
                 `setupParent: '${setupParent}'\n${DevToolsUtils.safeErrorString(e)}`);
           return false;
         }
       };
 
+      const parentActors = [];
       const onSpawnActorInParent = function(msg) {
         // We may have multiple connectToFrame instance running for the same tab
         // and need to filter the messages.
         if (msg.json.prefix != connPrefix) {
           return;
         }
 
         const { module, constructor, args, spawnedByActorID } = msg.json;
@@ -972,16 +968,18 @@ var DebuggerServer = {
                                                 .replace("/", "-");
           instance.actorID = connection.allocID(contentPrefix + "/" + instance.typeName);
           connection.addActor(instance);
 
           mm.sendAsyncMessage("debug:spawn-actor-in-parent:actor", {
             prefix: connPrefix,
             actorID: instance.actorID
           });
+
+          parentActors.push(instance);
         } catch (e) {
           const errorMessage =
             "Exception during actor module setup running in the parent process: ";
           DevToolsUtils.reportException(errorMessage + e + "\n" + e.stack);
           dumpn(`ERROR: ${errorMessage}\n\t module: '${module}'\n\t ` +
                 `constructor: '${constructor}'\n${DevToolsUtils.safeErrorString(e)}`);
         }
       };
@@ -1025,16 +1023,23 @@ var DebuggerServer = {
         // provides hook to actor modules that need to exchange messages
         // between e10s parent and child processes
         parentModules.forEach(mod => {
           if (mod.onBrowserSwap) {
             mod.onBrowserSwap(mm);
           }
         });
 
+        // Also notify actors spawned in the parent process about the new message manager.
+        parentActors.forEach(parentActor => {
+          if (parentActor.onBrowserSwap) {
+            parentActor.onBrowserSwap(mm);
+          }
+        });
+
         if (childTransport) {
           childTransport.swapBrowser(mm);
         }
       };
 
       const destroy = DevToolsUtils.makeInfallible(function() {
         EventEmitter.off(connection, "closed", destroy);
         Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
--- a/devtools/shared/Loader.jsm
+++ b/devtools/shared/Loader.jsm
@@ -28,18 +28,16 @@ this.EXPORTED_SYMBOLS = ["DevToolsLoader
  */
 function BuiltinProvider() {}
 BuiltinProvider.prototype = {
   load: function() {
     const paths = {
       // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
       "devtools": "resource://devtools",
       // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-      "gcli": "resource://devtools/shared/gcli/source/lib/gcli",
-      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
       "acorn": "resource://devtools/shared/acorn",
       // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
       "acorn/util/walk": "resource://devtools/shared/acorn/walk.js",
       // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
       "source-map": "resource://devtools/shared/sourcemap/source-map.js",
       // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
       // Allow access to xpcshell test items from the loader.
       "xpcshell-test": "resource://test",
deleted file mode 100644
--- a/devtools/shared/fronts/gcli.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
-const { gcliSpec } = require("devtools/shared/specs/gcli");
-
-/**
- *
- */
-const GcliFront = exports.GcliFront = FrontClassWithSpec(gcliSpec, {
-  initialize: function(client, tabForm) {
-    Front.prototype.initialize.call(this, client);
-    this.actorID = tabForm.gcliActor;
-
-    // XXX: This is the first actor type in its hierarchy to use the protocol
-    // library, so we're going to self-own on the client side for now.
-    this.manage(this);
-  },
-});
-
-// A cache of created fronts: WeakMap<Client, Front>
-const knownFronts = new WeakMap();
-
-/**
- * Create a GcliFront only when needed (returns a promise)
- * For notes on target.makeRemote(), see
- * https://bugzilla.mozilla.org/show_bug.cgi?id=1016330#c7
- */
-exports.GcliFront.create = function(target) {
-  return target.makeRemote().then(() => {
-    let front = knownFronts.get(target.client);
-    if (front == null && target.form.gcliActor != null) {
-      front = new GcliFront(target.client, target.form);
-      knownFronts.set(target.client, front);
-    }
-    return front;
-  });
-};
--- a/devtools/shared/fronts/moz.build
+++ b/devtools/shared/fronts/moz.build
@@ -14,17 +14,16 @@ DevToolsModules(
     'animation.js',
     'call-watcher.js',
     'canvas.js',
     'css-properties.js',
     'csscoverage.js',
     'device.js',
     'emulation.js',
     'framerate.js',
-    'gcli.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
     'memory.js',
     'node.js',
     'perf.js',
     'performance-recording.js',
     'performance.js',
deleted file mode 100644
--- a/devtools/shared/gcli/command-state.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const EventEmitter = require("devtools/shared/event-emitter");
-
-const getTargetId = ({tab}) => tab.linkedBrowser.outerWindowID;
-const enabledCommands = new Map();
-
-/**
- * The `CommandState` is a singleton that provides utility methods to keep the commands'
- * state in sync between the toolbox, the toolbar and the content.
- */
-const CommandState = EventEmitter.decorate({
-  /**
-   * Returns if a command is enabled on a given target.
-   *
-   * @param {Object} target
-   *                  The target object must have a tab's reference.
-   * @param {String} command
-   *                  The command's name used in gcli.
-   * @ returns {Boolean} returns `false` if the command is not enabled for the target
-   *                    given, or if the target given hasn't a tab; `true` otherwise.
-   */
-  isEnabledForTarget(target, command) {
-    if (!target.tab || !enabledCommands.has(command)) {
-      return false;
-    }
-
-    return enabledCommands.get(command).has(getTargetId(target));
-  },
-
-  /**
-   * Enables a command on a given target.
-   * Emits a "changed" event to notify potential observers about the new commands state.
-   *
-   * @param {Object} target
-   *                  The target object must have a tab's reference.
-   * @param {String} command
-   *                  The command's name used in gcli.
-   */
-  enableForTarget(target, command) {
-    if (!target.tab) {
-      return;
-    }
-
-    if (!enabledCommands.has(command)) {
-      enabledCommands.set(command, new Set());
-    }
-
-    enabledCommands.get(command).add(getTargetId(target));
-
-    CommandState.emit("changed", {target, command});
-  },
-
-  /**
-   * Disabled a command on a given target.
-   * Emits a "changed" event to notify potential observers about the new commands state.
-   *
-   * @param {Object} target
-   *                  The target object must have a tab's reference.
-   * @param {String} command
-   *                  The command's name used in gcli.
-   */
-  disableForTarget(target, command) {
-    if (!target.tab || !enabledCommands.has(command)) {
-      return;
-    }
-
-    enabledCommands.get(command).delete(getTargetId(target));
-
-    CommandState.emit("changed", {target, command});
-  },
-});
-exports.CommandState = CommandState;
-
deleted file mode 100644
--- a/devtools/shared/gcli/commands/index.js
+++ /dev/null
@@ -1,159 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { createSystem, connectFront, disconnectFront } = require("gcli/system");
-const { GcliFront } = require("devtools/shared/fronts/gcli");
-
-/**
- * This is the basic list of modules that should be loaded into each
- * requisition instance whether server side or client side
- */
-exports.baseModules = [
-  "gcli/types/delegate",
-  "gcli/types/selection",
-  "gcli/types/array",
-
-  "gcli/types/boolean",
-  "gcli/types/command",
-  "gcli/types/date",
-  "gcli/types/file",
-  "gcli/types/javascript",
-  "gcli/types/node",
-  "gcli/types/number",
-  "gcli/types/resource",
-  "gcli/types/setting",
-  "gcli/types/string",
-  "gcli/types/union",
-  "gcli/types/url",
-
-  "gcli/fields/fields",
-  "gcli/fields/delegate",
-  "gcli/fields/selection",
-
-  "gcli/ui/focus",
-  "gcli/ui/intro",
-
-  "gcli/converters/converters",
-  "gcli/converters/basic",
-  "gcli/converters/terminal",
-
-  "gcli/languages/command",
-  "gcli/languages/javascript",
-
-  "gcli/commands/clear",
-  "gcli/commands/context",
-  "gcli/commands/help",
-  "gcli/commands/pref",
-];
-
-/**
- * Some commands belong to a tool (see getToolModules). This is a list of the
- * modules that are *not* owned by a tool.
- */
-exports.devtoolsModules = [
-  "devtools/shared/gcli/commands/measure",
-  "devtools/shared/gcli/commands/paintflashing",
-  "devtools/shared/gcli/commands/rulers",
-  "devtools/shared/gcli/commands/screenshot",
-];
-
-/**
- * Register commands from tools with 'command: [ "some/module" ]' definitions.
- * The map/reduce incantation squashes the array of arrays to a single array.
- */
-try {
-  const { defaultTools } = require("devtools/client/definitions");
-  exports.devtoolsToolModules = defaultTools.map(def => def.commands || [])
-                                   .reduce((prev, curr) => prev.concat(curr), []);
-} catch (e) {
-  // "devtools/client/definitions" is only accessible from Firefox
-  exports.devtoolsToolModules = [];
-}
-
-/**
- * Register commands from toolbox buttons with 'command: [ "some/module" ]'
- * definitions.  The map/reduce incantation squashes the array of arrays to a
- * single array.
- */
-try {
-  const { ToolboxButtons } = require("devtools/client/definitions");
-  exports.devtoolsButtonModules = ToolboxButtons.map(def => def.commands || [])
-                                     .reduce((prev, curr) => prev.concat(curr), []);
-} catch (e) {
-  // "devtools/client/definitions" is only accessible from Firefox
-  exports.devtoolsButtonModules = [];
-}
-
-/**
- * Add modules to a system for use in a content process (but don't call load)
- */
-exports.addAllItemsByModule = function(system) {
-  system.addItemsByModule(exports.baseModules, { delayedLoad: true });
-  system.addItemsByModule(exports.devtoolsModules, { delayedLoad: true });
-  system.addItemsByModule(exports.devtoolsToolModules, { delayedLoad: true });
-  system.addItemsByModule(exports.devtoolsButtonModules, { delayedLoad: true });
-};
-
-/**
- * This is WeakMap<Target, Links> where Links is an object that looks like
- *   { refs: number, promise: Promise<System>, front: GcliFront }
- */
-var linksForTarget = new WeakMap();
-
-/**
- * The toolbox uses the following properties on a command to allow it to be
- * added to the toolbox toolbar
- */
-var customProperties = [ "buttonId", "buttonClass", "tooltipText" ];
-
-/**
- * Create a system which connects to a GCLI in a remote target
- * @return Promise<System> for the given target
- */
-exports.getSystem = function(target) {
-  const existingLinks = linksForTarget.get(target);
-  if (existingLinks != null) {
-    existingLinks.refs++;
-    return existingLinks.promise;
-  }
-
-  const system = createSystem({ location: "client" });
-
-  exports.addAllItemsByModule(system);
-
-  // Load the client system
-  const links = {
-    refs: 1,
-    system,
-    promise: system.load().then(() => {
-      return GcliFront.create(target).then(front => {
-        links.front = front;
-        return connectFront(system, front, customProperties).then(() => system);
-      });
-    })
-  };
-
-  linksForTarget.set(target, links);
-  return links.promise;
-};
-
-/**
- * Someone that called getSystem doesn't need it any more, so decrement the
- * count of users of the system for that target, and destroy if needed
- */
-exports.releaseSystem = function(target) {
-  const links = linksForTarget.get(target);
-  if (links == null) {
-    throw new Error("releaseSystem called for unknown target");
-  }
-
-  links.refs--;
-  if (links.refs === 0) {
-    disconnectFront(links.system, links.front);
-    links.system.destroy();
-    linksForTarget.delete(target);
-  }
-};
deleted file mode 100644
--- a/devtools/shared/gcli/commands/measure.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const EventEmitter = require("devtools/shared/event-emitter");
-
-loader.lazyRequireGetter(this, "CommandState",
-  "devtools/shared/gcli/command-state", true);
-
-const l10n = require("gcli/l10n");
-require("devtools/server/actors/inspector/inspector");
-const { HighlighterEnvironment } =
-  require("devtools/server/actors/highlighters");
-const { MeasuringToolHighlighter } =
-  require("devtools/server/actors/highlighters/measuring-tool");
-
-const highlighters = new WeakMap();
-
-exports.items = [
-  // The client measure command is used to maintain the toolbar button state
-  // only and redirects to the server command to actually toggle the measuring
-  // tool (see `measure_server` below).
-  {
-    name: "measure",
-    runAt: "client",
-    description: l10n.lookup("measureDesc"),
-    manual: l10n.lookup("measureManual"),
-    buttonId: "command-button-measure",
-    buttonClass: "command-button",
-    tooltipText: l10n.lookup("measureTooltip"),
-    state: {
-      isChecked: (target) => CommandState.isEnabledForTarget(target, "measure"),
-      onChange: (target, handler) => CommandState.on("changed", handler),
-      offChange: (target, handler) => CommandState.off("changed", handler)
-    },
-    exec: function* (args, context) {
-      const { target } = context.environment;
-
-      // Pipe the call to the server command.
-      const response = yield context.updateExec("measure_server");
-      const isEnabled = response.data;
-
-      if (isEnabled) {
-        CommandState.enableForTarget(target, "measure");
-      } else {
-        CommandState.disableForTarget(target, "measure");
-      }
-
-      // Toggle off the button when the page navigates because the measuring
-      // tool is removed automatically by the MeasuringToolHighlighter on the
-      // server then.
-      target.once("will-navigate", () =>
-        CommandState.disableForTarget(target, "measure"));
-    }
-  },
-  // The server measure command is hidden by default, it's just used by the
-  // client command.
-  {
-    name: "measure_server",
-    runAt: "server",
-    hidden: true,
-    returnType: "highlighterVisibility",
-    exec: function(args, context) {
-      const env = context.environment;
-      const { document } = env;
-
-      // Calling the command again after the measuring tool has been shown once,
-      // hides it.
-      if (highlighters.has(document)) {
-        const { highlighter } = highlighters.get(document);
-        highlighter.destroy();
-        return false;
-      }
-
-      // Otherwise, display the measuring tool.
-      const environment = new HighlighterEnvironment();
-      environment.initFromWindow(env.window);
-      const highlighter = new MeasuringToolHighlighter(environment);
-
-      // Store the instance of the measuring tool highlighter for this document
-      // so we can hide it later.
-      highlighters.set(document, { highlighter, environment });
-
-      // Listen to the highlighter's destroy event which may happen if the
-      // window is refreshed or closed with the measuring tool shown.
-      EventEmitter.once(highlighter, "destroy", () => {
-        if (highlighters.has(document)) {
-          const { environment: toDestroy } = highlighters.get(document);
-          toDestroy.destroy();
-          highlighters.delete(document);
-        }
-      });
-
-      highlighter.show();
-      return true;
-    }
-  }
-];
deleted file mode 100644
--- a/devtools/shared/gcli/commands/moz.build
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-DevToolsModules(
-    'index.js',
-    'measure.js',
-    'paintflashing.js',
-    'rulers.js',
-    'screenshot.js',
-)
deleted file mode 100644
--- a/devtools/shared/gcli/commands/paintflashing.js
+++ /dev/null
@@ -1,182 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-loader.lazyRequireGetter(this, "CommandState",
-  "devtools/shared/gcli/command-state", true);
-
-var telemetry;
-try {
-  const Telemetry = require("devtools/client/shared/telemetry");
-  telemetry = new Telemetry();
-} catch (e) {
-  // DevTools Telemetry module only available in Firefox
-}
-
-const gcli = require("gcli/index");
-const l10n = require("gcli/l10n");
-
-/**
- * Fire events and telemetry when paintFlashing happens
- */
-function onPaintFlashingChanged(target, flashing) {
-  if (flashing) {
-    CommandState.enableForTarget(target, "paintflashing");
-  } else {
-    CommandState.disableForTarget(target, "paintflashing");
-  }
-
-  target.once("will-navigate", () =>
-    CommandState.disableForTarget(target, "paintflashing"));
-
-  if (!telemetry) {
-    return;
-  }
-  if (flashing) {
-    telemetry.toolOpened("paintflashing");
-  } else {
-    telemetry.toolClosed("paintflashing");
-  }
-}
-
-/**
- * Alter the paintFlashing state of a window and report on the new value.
- * This works with chrome or content windows.
- *
- * This is a bizarre method that you could argue should be broken up into
- * separate getter and setter functions, however keeping it as one helps
- * to simplify the commands below.
- *
- * @param state {string} One of:
- * - "on" which does window.paintFlashing = true
- * - "off" which does window.paintFlashing = false
- * - "toggle" which does window.paintFlashing = !window.paintFlashing
- * - "query" which does nothing
- * @return The new value of the window.paintFlashing flag
- */
-function setPaintFlashing(window, state) {
-  const winUtils = window.windowUtils;
-
-  if (!["on", "off", "toggle", "query"].includes(state)) {
-    throw new Error(`Unsupported state: ${state}`);
-  }
-
-  if (state === "on") {
-    winUtils.paintFlashing = true;
-  } else if (state === "off") {
-    winUtils.paintFlashing = false;
-  } else if (state === "toggle") {
-    winUtils.paintFlashing = !winUtils.paintFlashing;
-  }
-
-  return winUtils.paintFlashing;
-}
-
-exports.items = [
-  {
-    name: "paintflashing",
-    description: l10n.lookup("paintflashingDesc")
-  },
-  {
-    item: "command",
-    runAt: "client",
-    name: "paintflashing on",
-    description: l10n.lookup("paintflashingOnDesc"),
-    manual: l10n.lookup("paintflashingManual"),
-    params: [{
-      group: "options",
-      params: [
-        {
-          type: "boolean",
-          name: "chrome",
-          get hidden() {
-            return gcli.hiddenByChromePref();
-          },
-          description: l10n.lookup("paintflashingChromeDesc"),
-        }
-      ]
-    }],
-    exec: function* (args, context) {
-      if (!args.chrome) {
-        const output = yield context.updateExec("paintflashing_server --state on");
-
-        onPaintFlashingChanged(context.environment.target, output.data);
-      } else {
-        setPaintFlashing(context.environment.chromeWindow, "on");
-      }
-    }
-  },
-  {
-    item: "command",
-    runAt: "client",
-    name: "paintflashing off",
-    description: l10n.lookup("paintflashingOffDesc"),
-    manual: l10n.lookup("paintflashingManual"),
-    params: [{
-      group: "options",
-      params: [
-        {
-          type: "boolean",
-          name: "chrome",
-          get hidden() {
-            return gcli.hiddenByChromePref();
-          },
-          description: l10n.lookup("paintflashingChromeDesc"),
-        }
-      ]
-    }],
-    exec: function* (args, context) {
-      if (!args.chrome) {
-        const output = yield context.updateExec("paintflashing_server --state off");
-
-        onPaintFlashingChanged(context.environment.target, output.data);
-      } else {
-        setPaintFlashing(context.environment.chromeWindow, "off");
-      }
-    }
-  },
-  {
-    item: "command",
-    runAt: "client",
-    name: "paintflashing toggle",
-    hidden: true,
-    buttonId: "command-button-paintflashing",
-    buttonClass: "command-button",
-    state: {
-      isChecked: (target) => CommandState.isEnabledForTarget(target, "paintflashing"),
-      onChange: (_, handler) => CommandState.on("changed", handler),
-      offChange: (_, handler) => CommandState.off("changed", handler),
-    },
-    tooltipText: l10n.lookup("paintflashingTooltip"),
-    description: l10n.lookup("paintflashingToggleDesc"),
-    manual: l10n.lookup("paintflashingManual"),
-    exec: function* (args, context) {
-      const output = yield context.updateExec("paintflashing_server --state toggle");
-
-      onPaintFlashingChanged(context.environment.target, output.data);
-    }
-  },
-  {
-    item: "command",
-    runAt: "server",
-    name: "paintflashing_server",
-    hidden: true,
-    params: [
-      {
-        name: "state",
-        type: {
-          name: "selection",
-          data: [ "on", "off", "toggle", "query" ]
-        }
-      },
-    ],
-    returnType: "paintFlashingState",
-    exec: function(args, context) {
-      const { window } = context.environment;
-
-      return setPaintFlashing(window, args.state);
-    }
-  }
-];
deleted file mode 100644
--- a/devtools/shared/gcli/commands/rulers.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const EventEmitter = require("devtools/shared/event-emitter");
-
-loader.lazyRequireGetter(this, "CommandState",
-  "devtools/shared/gcli/command-state", true);
-
-const l10n = require("gcli/l10n");
-require("devtools/server/actors/inspector/inspector");
-const { HighlighterEnvironment } =
-  require("devtools/server/actors/highlighters");
-const { RulersHighlighter } =
-  require("devtools/server/actors/highlighters/rulers");
-
-const highlighters = new WeakMap();
-
-exports.items = [
-  // The client rulers command is used to maintain the toolbar button state only
-  // and redirects to the server command to actually toggle the rulers (see
-  // rulers_server below).
-  {
-    name: "rulers",
-    runAt: "client",
-    description: l10n.lookup("rulersDesc"),
-    manual: l10n.lookup("rulersManual"),
-    buttonId: "command-button-rulers",
-    buttonClass: "command-button",
-    tooltipText: l10n.lookup("rulersTooltip"),
-    state: {
-      isChecked: (target) => CommandState.isEnabledForTarget(target, "rulers"),
-      onChange: (target, handler) => CommandState.on("changed", handler),
-      offChange: (target, handler) => CommandState.off("changed", handler)
-    },
-    exec: function* (args, context) {
-      const { target } = context.environment;
-
-      // Pipe the call to the server command.
-      const response = yield context.updateExec("rulers_server");
-      const isEnabled = response.data;
-
-      if (isEnabled) {
-        CommandState.enableForTarget(target, "rulers");
-      } else {
-        CommandState.disableForTarget(target, "rulers");
-      }
-
-      // Toggle off the button when the page navigates because the rulers are
-      // removed automatically by the RulersHighlighter on the server then.
-      target.once("will-navigate", () => CommandState.disableForTarget(target, "rulers"));
-    }
-  },
-  // The server rulers command is hidden by default, it's just used by the
-  // client command.
-  {
-    name: "rulers_server",
-    runAt: "server",
-    hidden: true,
-    returnType: "highlighterVisibility",
-    exec: function(args, context) {
-      const env = context.environment;
-      const { document } = env;
-
-      // Calling the command again after the rulers have been shown once hides
-      // them.
-      if (highlighters.has(document)) {
-        const { highlighter } = highlighters.get(document);
-        highlighter.destroy();
-        return false;
-      }
-
-      // Otherwise, display the rulers.
-      const environment = new HighlighterEnvironment();
-      environment.initFromWindow(env.window);
-      const highlighter = new RulersHighlighter(environment);
-
-      // Store the instance of the rulers highlighter for this document so we
-      // can hide it later.
-      highlighters.set(document, { highlighter, environment });
-
-      // Listen to the highlighter's destroy event which may happen if the
-      // window is refreshed or closed with the rulers shown.
-      EventEmitter.once(highlighter, "destroy", () => {
-        if (highlighters.has(document)) {
-          const { environment: toDestroy } = highlighters.get(document);
-          toDestroy.destroy();
-          highlighters.delete(document);
-        }
-      });
-
-      highlighter.show();
-      return true;
-    }
-  }
-];
deleted file mode 100644
--- a/devtools/shared/gcli/commands/screenshot.js
+++ /dev/null
@@ -1,593 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { Cc, Ci, Cr, Cu } = require("chrome");
-const ChromeUtils = require("ChromeUtils");
-const l10n = require("gcli/l10n");
-const Services = require("Services");
-const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
-const { getRect } = require("devtools/shared/layout/utils");
-const defer = require("devtools/shared/defer");
-const { Task } = require("devtools/shared/task");
-
-loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true);
-
-loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
-loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
-loader.lazyImporter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
-loader.lazyImporter(this, "PrivateBrowsingUtils",
-                          "resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-// String used as an indication to generate default file name in the following
-// format: "Screen Shot yyyy-mm-dd at HH.MM.SS.png"
-const FILENAME_DEFAULT_VALUE = " ";
-const CONTAINER_FLASHING_DURATION = 500;
-
-/*
- * There are 2 commands and 1 converter here. The 2 commands are nearly
- * identical except that one runs on the client and one in the server.
- *
- * The server command is hidden, and is designed to be called from the client
- * command.
- */
-
-/**
- * Both commands have the same initial filename parameter
- */
-const filenameParam = {
-  name: "filename",
-  type: "string",
-  defaultValue: FILENAME_DEFAULT_VALUE,
-  description: l10n.lookup("screenshotFilenameDesc"),
-  manual: l10n.lookup("screenshotFilenameManual")
-};
-
-/**
- * Both commands have almost the same set of standard optional parameters, except for the
- * type of the --selector option, which can be a node only on the server.
- */
-const getScreenshotCommandParams = function(isClient) {
-  return {
-    group: l10n.lookup("screenshotGroupOptions"),
-    params: [
-      {
-        name: "clipboard",
-        type: "boolean",
-        description: l10n.lookup("screenshotClipboardDesc"),
-        manual: l10n.lookup("screenshotClipboardManual")
-      },
-      {
-        name: "imgur",
-        type: "boolean",
-        description: l10n.lookup("screenshotImgurDesc"),
-        manual: l10n.lookup("screenshotImgurManual")
-      },
-      {
-        name: "delay",
-        type: { name: "number", min: 0 },
-        defaultValue: 0,
-        description: l10n.lookup("screenshotDelayDesc"),
-        manual: l10n.lookup("screenshotDelayManual")
-      },
-      {
-        name: "dpr",
-        type: { name: "number", min: 0, allowFloat: true },
-        defaultValue: 0,
-        description: l10n.lookup("screenshotDPRDesc"),
-        manual: l10n.lookup("screenshotDPRManual")
-      },
-      {
-        name: "fullpage",
-        type: "boolean",
-        description: l10n.lookup("screenshotFullPageDesc"),
-        manual: l10n.lookup("screenshotFullPageManual")
-      },
-      {
-        name: "selector",
-        // On the client side, don't try to parse the selector as a node as it will
-        // trigger an unsafe CPOW.
-        type: isClient ? "string" : "node",
-        defaultValue: null,
-        description: l10n.lookup("inspectNodeDesc"),
-        manual: l10n.lookup("inspectNodeManual")
-      },
-      {
-        name: "file",
-        type: "boolean",
-        description: l10n.lookup("screenshotFileDesc"),
-        manual: l10n.lookup("screenshotFileManual"),
-      },
-    ]
-  };
-};
-
-const clientScreenshotParams = getScreenshotCommandParams(true);
-const serverScreenshotParams = getScreenshotCommandParams(false);
-
-exports.items = [
-  {
-    /**
-     * Format an 'imageSummary' (as output by the screenshot command).
-     * An 'imageSummary' is a simple JSON object that looks like this:
-     *
-     * {
-     *   destinations: [ "..." ], // Required array of descriptions of the
-     *                            // locations of the result image (the command
-     *                            // can have multiple outputs)
-     *   data: "...",             // Optional Base64 encoded image data
-     *   width:1024, height:768,  // Dimensions of the image data, required
-     *                            // if data != null
-     *   filename: "...",         // If set, clicking the image will open the
-     *                            // folder containing the given file
-     *   href: "...",             // If set, clicking the image will open the
-     *                            // link in a new tab
-     * }
-     */
-    item: "converter",
-    from: "imageSummary",
-    to: "dom",
-    exec: function(imageSummary, context) {
-      const document = context.document;
-      const root = document.createElement("div");
-
-      // Add a line to the result for each destination
-      imageSummary.destinations.forEach(destination => {
-        const title = document.createElement("div");
-        title.textContent = destination;
-        root.appendChild(title);
-      });
-
-      // Add the thumbnail image
-      if (imageSummary.data != null) {
-        const image = context.document.createElement("div");
-        const previewHeight = parseInt(256 * imageSummary.height / imageSummary.width,
-                                       10);
-        const style = "" +
-            "width: 256px;" +
-            "height: " + previewHeight + "px;" +
-            "max-height: 256px;" +
-            "background-image: url('" + imageSummary.data + "');" +
-            "background-size: 256px " + previewHeight + "px;" +
-            "margin: 4px;" +
-            "display: block;";
-        image.setAttribute("style", style);
-        root.appendChild(image);
-      }
-
-      // Click handler
-      if (imageSummary.href || imageSummary.filename) {
-        root.style.cursor = "pointer";
-        root.addEventListener("click", () => {
-          if (imageSummary.href) {
-            openContentLink(imageSummary.href);
-          } else if (imageSummary.filename) {
-            const file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
-            file.initWithPath(imageSummary.filename);
-            file.reveal();
-          }
-        });
-      }
-
-      return root;
-    }
-  },
-  {
-    item: "command",
-    runAt: "client",
-    name: "screenshot",
-    description: l10n.lookup("screenshotDesc"),
-    manual: l10n.lookup("screenshotManual"),
-    returnType: "imageSummary",
-    buttonId: "command-button-screenshot",
-    buttonClass: "command-button",
-    tooltipText: l10n.lookup("screenshotTooltipPage"),
-    params: [
-      filenameParam,
-      clientScreenshotParams,
-    ],
-    exec: function(args, context) {
-      // Re-execute the command on the server
-      const command = context.typed.replace(/^screenshot/, "screenshot_server");
-      const capture = context.updateExec(command).then(output => {
-        return output.error ? Promise.reject(output.data) : output.data;
-      });
-
-      simulateCameraEffect(context.environment.chromeDocument, "shutter");
-      return capture.then(saveScreenshot.bind(null, args, context));
-    },
-  },
-  {
-    item: "command",
-    runAt: "server",
-    name: "screenshot_server",
-    hidden: true,
-    returnType: "imageSummary",
-    params: [
-      filenameParam,
-      serverScreenshotParams,
-    ],
-    exec: function(args, context) {
-      return captureScreenshot(args, context.environment.document);
-    },
-  }
-];
-
-/**
- * This function is called to simulate camera effects
- */
-function simulateCameraEffect(document, effect) {
-  const window = document.defaultView;
-  if (effect === "shutter") {
-    if (Services.prefs.getBoolPref("devtools.screenshot.audio.enabled")) {
-      const audioCamera = new window.Audio("resource://devtools/client/themes/audio/shutter.wav");
-      audioCamera.play();
-    }
-  }
-  if (effect == "flash") {
-    const frames = Cu.cloneInto({ opacity: [ 0, 1 ] }, window);
-    document.documentElement.animate(frames, CONTAINER_FLASHING_DURATION);
-  }
-}
-
-/**
- * This function simply handles the --delay argument before calling
- * createScreenshotData
- */
-function captureScreenshot(args, document) {
-  if (args.delay > 0) {
-    return new Promise((resolve, reject) => {
-      document.defaultView.setTimeout(() => {
-        createScreenshotData(document, args).then(resolve, reject);
-      }, args.delay * 1000);
-    });
-  }
-  return createScreenshotData(document, args);
-}
-
-/**
- * There are several possible destinations for the screenshot, SKIP is used
- * in saveScreenshot() whenever one of them is not used
- */
-const SKIP = Promise.resolve();
-
-/**
- * Save the captured screenshot to one of several destinations.
- */
-function saveScreenshot(args, context, reply) {
-  const fileNeeded = args.filename != FILENAME_DEFAULT_VALUE ||
-    (!args.imgur && !args.clipboard) || args.file;
-
-  return Promise.all([
-    args.clipboard ? saveToClipboard(context, reply) : SKIP,
-    args.imgur ? uploadToImgur(reply) : SKIP,
-    fileNeeded ? saveToFile(context, reply) : SKIP,
-  ]).then(() => reply);
-}
-
-/**
- * This does the dirty work of creating a base64 string out of an
- * area of the browser window
- */
-function createScreenshotData(document, args) {
-  const window = document.defaultView;
-  let left = 0;
-  let top = 0;
-  let width;
-  let height;
-  const currentX = window.scrollX;
-  const currentY = window.scrollY;
-
-  let filename = getFilename(args.filename);
-
-  if (args.fullpage) {
-    // Bug 961832: GCLI screenshot shows fixed position element in wrong
-    // position if we don't scroll to top
-    window.scrollTo(0, 0);
-    width = window.innerWidth + window.scrollMaxX - window.scrollMinX;
-    height = window.innerHeight + window.scrollMaxY - window.scrollMinY;
-    filename = filename.replace(".png", "-fullpage.png");
-  } else if (args.selector) {
-    ({ top, left, width, height } = getRect(window, args.selector, window));
-  } else {
-    left = window.scrollX;
-    top = window.scrollY;
-    width = window.innerWidth;
-    height = window.innerHeight;
-  }
-
-  // Only adjust for scrollbars when considering the full window
-  if (!args.selector) {
-    const winUtils = window.windowUtils;
-    const scrollbarHeight = {};
-    const scrollbarWidth = {};
-    winUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight);
-    width -= scrollbarWidth.value;
-    height -= scrollbarHeight.value;
-  }
-
-  const canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-  const ctx = canvas.getContext("2d");
-  const ratio = args.dpr ? args.dpr : window.devicePixelRatio;
-  canvas.width = width * ratio;
-  canvas.height = height * ratio;
-  ctx.scale(ratio, ratio);
-  ctx.drawWindow(window, left, top, width, height, "#fff");
-  const data = canvas.toDataURL("image/png", "");
-
-  // See comment above on bug 961832
-  if (args.fullpage) {
-    window.scrollTo(currentX, currentY);
-  }
-
-  simulateCameraEffect(document, "flash");
-
-  return Promise.resolve({
-    destinations: [],
-    data: data,
-    height: height,
-    width: width,
-    filename: filename,
-  });
-}
-
-/**
- * We may have a filename specified in args, or we might have to generate
- * one.
- */
-function getFilename(defaultName) {
-  // Create a name for the file if not present
-  if (defaultName != FILENAME_DEFAULT_VALUE) {
-    return defaultName;
-  }
-
-  const date = new Date();
-  let dateString = date.getFullYear() + "-" + (date.getMonth() + 1) +
-                  "-" + date.getDate();
-  dateString = dateString.split("-").map(function(part) {
-    if (part.length == 1) {
-      part = "0" + part;
-    }
-    return part;
-  }).join("-");
-
-  const timeString = date.toTimeString().replace(/:/g, ".").split(" ")[0];
-  return l10n.lookupFormat("screenshotGeneratedFilename",
-                           [ dateString, timeString ]) + ".png";
-}
-
-/**
- * Save the image data to the clipboard. This returns a promise, so it can
- * be treated exactly like imgur / file processing, but it's really sync
- * for now.
- */
-function saveToClipboard(context, reply) {
-  return new Promise(resolve => {
-    try {
-      const channel = NetUtil.newChannel({
-        uri: reply.data,
-        loadUsingSystemPrincipal: true,
-        contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
-      });
-      const input = channel.open2();
-
-      const loadContext = context.environment.chromeWindow.docShell
-                                 .QueryInterface(Ci.nsILoadContext);
-
-      const callback = {
-        onImageReady(container, status) {
-          if (!container) {
-            console.error("imgTools.decodeImageAsync failed");
-            reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
-            resolve();
-            return;
-          }
-
-          try {
-            const wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
-                              .createInstance(Ci.nsISupportsInterfacePointer);
-            wrapped.data = container;
-
-            const trans = Cc["@mozilla.org/widget/transferable;1"]
-                            .createInstance(Ci.nsITransferable);
-            trans.init(loadContext);
-            trans.addDataFlavor(channel.contentType);
-            trans.setTransferData(channel.contentType, wrapped, -1);
-
-            Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
-
-            reply.destinations.push(l10n.lookup("screenshotCopied"));
-          } catch (ex) {
-            console.error(ex);
-            reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
-          }
-          resolve();
-        }
-      };
-
-      const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
-      const imgTools = Cc["@mozilla.org/image/tools;1"]
-                          .getService(Ci.imgITools);
-      imgTools.decodeImageAsync(input, channel.contentType, callback,
-                                threadManager.currentThread);
-    } catch (ex) {
-      console.error(ex);
-      reply.destinations.push(l10n.lookup("screenshotErrorCopying"));
-      resolve();
-    }
-  });
-}
-
-/**
- * Upload screenshot data to Imgur, returning a promise of a URL (as a string)
- */
-function uploadToImgur(reply) {
-  return new Promise((resolve, reject) => {
-    const xhr = new XMLHttpRequest();
-    const fd = new FormData();
-    fd.append("image", reply.data.split(",")[1]);
-    fd.append("type", "base64");
-    fd.append("title", reply.filename);
-
-    const postURL = Services.prefs.getCharPref("devtools.gcli.imgurUploadURL");
-    const clientID = "Client-ID " +
-                     Services.prefs.getCharPref("devtools.gcli.imgurClientID");
-
-    xhr.open("POST", postURL);
-    xhr.setRequestHeader("Authorization", clientID);
-    xhr.send(fd);
-    xhr.responseType = "json";
-
-    xhr.onreadystatechange = function() {
-      if (xhr.readyState == 4) {
-        if (xhr.status == 200) {
-          reply.href = xhr.response.data.link;
-          reply.destinations.push(l10n.lookupFormat("screenshotImgurUploaded",
-                                                    [ reply.href ]));
-        } else {
-          reply.destinations.push(l10n.lookup("screenshotImgurError"));
-        }
-
-        resolve();
-      }
-    };
-  });
-}
-
-/**
- * Progress listener that forwards calls to a transfer object.
- *
- * This is used below in saveToFile to forward progress updates from the
- * nsIWebBrowserPersist object that does the actual saving to the nsITransfer
- * which just represents the operation for the Download Manager.  This keeps the
- * Download Manager updated on saving progress and completion, so that it gives
- * visual feedback from the downloads toolbar button when the save is done.
- *
- * It also allows the browser window to show auth prompts if needed (should not
- * be needed for saving screenshots).
- *
- * This code is borrowed directly from contentAreaUtils.js.
- */
-function DownloadListener(win, transfer) {
-  this.window = win;
-  this.transfer = transfer;
-
-  // For most method calls, forward to the transfer object.
-  for (const name in transfer) {
-    if (name != "QueryInterface" &&
-        name != "onStateChange") {
-      this[name] = (...args) => transfer[name].apply(transfer, args);
-    }
-  }
-
-  // Allow saveToFile to await completion for error handling
-  this._completedDeferred = defer();
-  this.completed = this._completedDeferred.promise;
-}
-
-DownloadListener.prototype = {
-  QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor",
-                                          "nsIWebProgressListener",
-                                          "nsIWebProgressListener2"]),
-
-  getInterface: function(iid) {
-    if (iid.equals(Ci.nsIAuthPrompt) ||
-        iid.equals(Ci.nsIAuthPrompt2)) {
-      const ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
-                 .getService(Ci.nsIPromptFactory);
-      return ww.getPrompt(this.window, iid);
-    }
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-  onStateChange: function(webProgress, request, state, status) {
-    // Check if the download has completed
-    if ((state & Ci.nsIWebProgressListener.STATE_STOP) &&
-        (state & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
-      if (status == Cr.NS_OK) {
-        this._completedDeferred.resolve();
-      } else {
-        this._completedDeferred.reject();
-      }
-    }
-
-    this.transfer.onStateChange.apply(this.transfer, arguments);
-  }
-};
-
-/**
- * Save the screenshot data to disk, returning a promise which is resolved on
- * completion.
- */
-var saveToFile = Task.async(function* (context, reply) {
-  const document = context.environment.chromeDocument;
-  const window = context.environment.chromeWindow;
-
-  // Check there is a .png extension to filename
-  if (!reply.filename.match(/.png$/i)) {
-    reply.filename += ".png";
-  }
-
-  const downloadsDir = yield Downloads.getPreferredDownloadsDirectory();
-  const downloadsDirExists = yield OS.File.exists(downloadsDir);
-  if (downloadsDirExists) {
-    // If filename is absolute, it will override the downloads directory and
-    // still be applied as expected.
-    reply.filename = OS.Path.join(downloadsDir, reply.filename);
-  }
-
-  const sourceURI = Services.io.newURI(reply.data);
-  const targetFile = new FileUtils.File(reply.filename);
-  const targetFileURI = Services.io.newFileURI(targetFile);
-
-  // Create download and track its progress.
-  // This is adapted from saveURL in contentAreaUtils.js, but simplified greatly
-  // and modified to allow saving to arbitrary paths on disk.  Using these
-  // objects as opposed to just writing with OS.File allows us to tie into the
-  // download manager to record a download entry and to get visual feedback from
-  // the downloads toolbar button when the save is done.
-  const nsIWBP = Ci.nsIWebBrowserPersist;
-  const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
-                nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
-                nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
-  const isPrivate =
-    PrivateBrowsingUtils.isContentWindowPrivate(document.defaultView);
-  const persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
-                  .createInstance(Ci.nsIWebBrowserPersist);
-  persist.persistFlags = flags;
-  const tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
-  tr.init(sourceURI,
-          targetFileURI,
-          "",
-          null,
-          null,
-          null,
-          persist,
-          isPrivate);
-  const listener = new DownloadListener(window, tr);
-  persist.progressListener = listener;
-  const principal = Services.scriptSecurityManager.getSystemPrincipal();
-  persist.savePrivacyAwareURI(sourceURI,
-                              principal,
-                              0,
-                              document.documentURIObject,
-                              Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
-                              null,
-                              null,
-                              targetFileURI,
-                              isPrivate);
-
-  try {
-    // Await successful completion of the save via the listener
-    yield listener.completed;
-    reply.destinations.push(l10n.lookup("screenshotSavedToFile") +
-                            ` "${reply.filename}"`);
-  } catch (ex) {
-    console.error(ex);
-    reply.destinations.push(l10n.lookup("screenshotErrorSavingToFile") + " " +
-                            reply.filename);
-  }
-});
deleted file mode 100644
--- a/devtools/shared/gcli/moz.build
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-DIRS += [
-    'commands',
-    'source/lib/gcli',
-    'source/lib/gcli/connectors',
-    'source/lib/gcli/converters',
-    'source/lib/gcli/commands',
-    'source/lib/gcli/fields',
-    'source/lib/gcli/languages',
-    'source/lib/gcli/mozui',
-    'source/lib/gcli/types',
-    'source/lib/gcli/ui',
-    'source/lib/gcli/util',
-]
-
-DevToolsModules(
-    'command-state.js',
-    'templater.js'
-)
deleted file mode 100644
--- a/devtools/shared/gcli/source/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
deleted file mode 100644
--- a/devtools/shared/gcli/source/docs/design.md
+++ /dev/null
@@ -1,102 +0,0 @@
-
-# The Design of GCLI
-
-## Design Goals
-
-GCLI should be:
-
-- primarily for technical users.
-- as fast as a traditional CLI. It should be possible to put your head down,
-  and look at the keyboard and use GCLI 'blind' at full speed without making
-  mistakes.
-- principled about the way it encourages people to build commands. There is
-  benefit from unifying the underlying concepts.
-- automatically helpful.
-
-GCLI should not attempt to:
-
-- convert existing GUI users to a CLI.
-- use natural language input. The closest we should get to natural language is
-  thinking of commands as ```verb noun --adjective```.
-- gain a touch based interface. Whilst it's possible (even probable) that touch
-  can provide further benefits to command line users, that can wait while we
-  catch up with 1985.
-- slavishly follow the syntax of existing commands, predictability is more
-  important.
-- be a programming language. Shell scripts are mini programming languages but
-  we have JavaScript sat just next door. It's better to integrate than compete.
-
-
-## Design Challenges
-
-What has changed since 1970 that might cause us to make changes to the design
-of the command line?
-
-
-### Connection limitations
-
-Unix pre-dates the Internet and treats almost everything as a file. Since the
-Internet it could be more useful to use URIs as ways to identify sources of data.
-
-
-### Memory limitations
-
-Modern computers have something like 6 orders of magnitude more memory than the
-PDP-7 on which Unix was developed. Innovations like stdin/stdout and pipes are
-ways to connect systems without long-term storage of the results. The ability
-to store results for some time (potentially in more than one format)
-significantly reduces the need for these concepts. We should make the results
-of past commands addressable for re-use at a later time.
-
-There are a number of possible policies for eviction of items from the history.
-We should investigate options other than a simple stack.
-
-
-### Multi-tasking limitations
-
-Multi-tasking was a problem in 1970; the problem was getting a computer to do
-many jobs on 1 core. Today the problem is getting a computer to do one job on
-many cores. However we're stuck with this legacy in 2 ways. Firstly that the
-default is to force everything to wait until the previous job is finished, but
-more importantly that output from parallel jobs frequently collides
-
-    $ find / -ctime 5d -print &
-    $ find / -uid 0 -print &
-    // good luck working out what came from where
-
-    $ tail -f logfile.txt &
-    $ vi main.c
-    // have a nice time editing that file
-
-GCLI should allow commands to be asynchronous and will provide UI elements to
-inform the user of job completion. It will also keep asynchronous command
-output contained within it's own display area.
-
-
-### Output limitations
-
-The PDP-7 had a teletype. There is something like 4 orders of magnitude more
-information that can be displayed on a modern display than a 80x24 character
-based console. We can use this flexibility to provide better help to the user
-in entering their command.
-
-The additional display richness can also allow interaction with result output.
-Command output can include links to follow-up commands, and even ask for
-additional input. (e.g. "your search returned zero results do you want to try
-again with a different search string")
-
-There is no reason why output must be static. For example, it could be
-informative to see the results of an "ls" command alter given changes made by
-subsequent commands. (It should be noted that there are times when historical
-information is important too)
-
-
-### Integration limitations
-
-In 1970, command execution meant retrieving a program from storage, and running
-it. This required minimal interaction between the command line processor and
-the program being run, and was good for resource constrained systems.
-This lack of interaction resulted in the processing of command line arguments
-being done everywhere, when the task was better suited to command line.
-We should provide metadata about the commands being run, to allow the command
-line to process, interpret and provide help on the input.
deleted file mode 100644
--- a/devtools/shared/gcli/source/docs/developing-gcli.md
+++ /dev/null
@@ -1,213 +0,0 @@
-
-# Developing GCLI
-
-## About the code
-
-The majority of the GCLI source is stored in the ``lib`` directory.
-
-The ``docs`` directory contains documentation.
-The ``scripts`` directory contains RequireJS that GCLI uses.
-The ``build`` directory contains files used when creating builds.
-The ``mozilla`` directory contains the mercurial patch queue of patches to apply
-to mozilla-central.
-The ``selenium-tests`` directory contains selenium web-page integration tests.
-
-The source in the ``lib`` directory is split into 4 sections:
-
-- ``lib/demo`` contains commands used in the demo page. It is not needed except
-  for demo purposes.
-- ``lib/test`` contains a small test harness for testing GCLI.
-- ``lib/gclitest`` contains tests that run in the test harness
-- ``lib/gcli`` contains the actual meat
-
-GCLI is split into a UI portion and a Model/Controller portion.
-
-
-## The GCLI Model
-
-The heart of GCLI is a ``Requisition``, which is an AST for the input. A
-``Requisition`` is a command that we'd like to execute, and we're filling out
-all the inputs required to execute the command.
-
-A ``Requisition`` has a ``Command`` that is to be executed. Each Command has a
-number of ``Parameter``s, each of which has a name and a type as detailed
-above.
-
-As you type, your input is split into ``Argument``s, which are then assigned to
-``Parameter``s using ``Assignment``s. Each ``Assignment`` has a ``Conversion``
-which stores the input argument along with the value that is was converted into
-according to the type of the parameter.
-
-There are special assignments called ``CommandAssignment`` which the
-``Requisition`` uses to link to the command to execute, and
-``UnassignedAssignment``used to store arguments that do not have a parameter
-to be assigned to.
-
-
-## The GCLI UI
-
-There are several components of the GCLI UI. Each can have a script portion,
-some template HTML and a CSS file. The template HTML is processed by
-``domtemplate`` before use.
-
-DomTemplate is fully documented in [it's own repository]
-(https://github.com/joewalker/domtemplate).
-
-The components are:
-
-- ``Inputter`` controls the input field, processing special keyboard events and
-  making sure that it stays in sync with the Requisition.
-- ``Completer`` updates a div that is located behind the input field and used
-  to display completion advice and hint highlights. It is stored in
-  completer.js.
-- ``Display`` is responsible for containing the popup hints that are displayed
-  above the command line. Typically Display contains a Hinter and a RequestsView
-  although these are not both required. Display itself is optional, and isn't
-  planned for use in the first release of GCLI in Firefox.
-- ``Hinter`` Is used to display input hints. It shows either a Menu or an
-  ArgFetch component depending on the state of the Requisition
-- ``Menu`` is used initially to select the command to be executed. It can act
-  somewhat like the Start menu on windows.
-- ``ArgFetch`` Once the command to be executed has been selected, ArgFetch
-  shows a 'dialog' allowing the user to enter the parameters to the selected
-  command.
-- ``RequestsView`` Contains a set of ``RequestView`` components, each of which
-  displays a command that has been invoked. RequestsView is a poor name, and
-  should better be called ReportView
-
-ArgFetch displays a number of Fields. There are fields for most of the Types
-discussed earlier. See 'Writing Fields' above for more information.
-
-
-## Testing
-
-GCLI contains 2 test suites:
-
-- JS level testing is run with the ``test`` command. The tests are located in
-  ``lib/gclitest`` and they use the test runner in ``lib/test``. This is fairly
-  comprehensive, however it does not do UI level testing.
-  If writing a new test it needs to be registered in ``lib/gclitest/index``.
-  For an example of how to write tests, see ``lib/gclitest/testSplit.js``.
-  The test functions are implemented in ``lib/test/assert``.
-- Browser integration tests are included in ``browser_webconsole_gcli_*.js``,
-  in ``toolkit/components/console/hudservice/tests/browser``. These are
-  run with the rest of the Mozilla test suite.
-
-
-## Coding Conventions
-
-The coding conventions for the GCLI project come from the Bespin/Skywriter and
-Ace projects. They are roughly [Crockford]
-(http://javascript.crockford.com/code.html) with a few exceptions and
-additions:
-
-* ``var`` does not need to be at the top of each function, we'd like to move
-  to ``let`` when it's generally available, and ``let`` doesn't have the same
-  semantic twists as ``var``.
-
-* Strings are generally enclosed in single quotes.
-
-* ``eval`` is to be avoided, but we don't declare it evil.
-
-The [Google JavaScript conventions]
-(https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) are
-more detailed, we tend to deviate in:
-
-* Custom exceptions: We generally just use ``throw new Error('message');``
-
-* Multi-level prototype hierarchies: Allowed; we don't have ``goog.inherits()``
-
-* ``else`` begins on a line by itself:
-
-        if (thing) {
-          doThis();
-        }
-        else {
-          doThat();
-        }
-
-
-## Startup
-
-Internally GCLI modules have ``startup()``/``shutdown()`` functions which are
-called on module init from the top level ``index.js`` of that 'package'.
-
-In order to initialize a package all that is needed is to require the package
-index (e.g. ``require('package/index')``).
-
-The ``shutdown()`` function was useful when GCLI was used in Bespin as part of
-dynamic registration/de-registration. It is not known if this feature will be
-useful in the future. So it has not been entirely removed, it may be at some
-future date.
-
-
-## Running the Unit Tests
-
-Start the GCLI static server:
-
-    cd path/to/gcli
-    node gcli.js
-
-Now point your browser to http://localhost:9999/localtest.html. When the page
-loads the tests will be automatically run outputting to the console, or you can
-enter the ``test`` command to run the unit tests.
-
-
-## Contributing Code
-
-Please could you do the following to help minimize the amount of rework that we
-do:
-
-1. Check the unit tests run correctly (see **Running the Unit Tests** above)
-2. Check the code follows the style guide. At a minimum it should look like the
-   code around it. For more detailed notes, see **Coding Conventions** above
-3. Help me review your work by using good commit comments. Which means 2 things
-   * Well formatted messages, i.e. 50 char summary including bug tag, followed
-     by a blank line followed by a more in-depth message wrapped to 72 chars
-     per line. This is basically the format used by the Linux Kernel. See the
-     [commit log](https://github.com/joewalker/gcli/commits/master) for
-     examples. The be extra helpful, please use the "shortdesc-BUGNUM: " if
-     possible which also helps in reviews.
-   * Commit your changes as a story. Make it easy for me to understand the
-     changes that you've made.
-4. Sign your work. To improve tracking of who did what, we follow the sign-off
-   procedure used in the Linux Kernel.
-   The sign-off is a simple line at the end of the explanation for the
-   patch, which certifies that you wrote it or otherwise have the right to
-   pass it on as an open-source patch.  The rules are pretty simple: if you
-   can certify the below:
-
-        Developer's Certificate of Origin 1.1
-
-        By making a contribution to this project, I certify that:
-
-        (a) The contribution was created in whole or in part by me and I
-            have the right to submit it under the open source license
-            indicated in the file; or
-
-        (b) The contribution is based upon previous work that, to the best
-            of my knowledge, is covered under an appropriate open source
-            license and I have the right under that license to submit that
-            work with modifications, whether created in whole or in part
-            by me, under the same open source license (unless I am
-            permitted to submit under a different license), as indicated
-            in the file; or
-
-        (c) The contribution was provided directly to me by some other
-            person who certified (a), (b) or (c) and I have not modified
-            it.
-
-        (d) I understand and agree that this project and the contribution
-            are public and that a record of the contribution (including all
-            personal information I submit with it, including my sign-off) is
-            maintained indefinitely and may be redistributed consistent with
-            this project or the open source license(s) involved.
-
-   then you just add a line saying
-
-        Signed-off-by: Random J Developer <random@developer.example.org>
-
-   using your real name (sorry, no pseudonyms or anonymous contributions.)
-
-Thanks for wanting to contribute code.
-
deleted file mode 100644
--- a/devtools/shared/gcli/source/docs/index.md
+++ /dev/null
@@ -1,150 +0,0 @@
-
-# About GCLI
-
-## GCLI is a Graphical Command Line Interpreter.
-
-GCLI is a command line for modern computers. When command lines were invented,
-computers were resource-limited, disconnected systems with slow multi-tasking
-and poor displays. The design of the Unix CLI made sense in 1970, but over 40
-years on, considering the pace of change, there are many improvements we can
-make.
-
-CLIs generally suffer from poor discoverability; It's hard when faced with a
-blank command line to work out what to do. As a result the majority of programs
-today use purely graphical user interfaces, however in doing so, they lose some
-of the benefits of CLIs. CLIs are still used because generally, in the hands of
-a skilled user they are faster, and have a wider range of available options.
-
-GCLI attempts to get the best of the GUI world and the CLI world to produce
-something that is both easy to use and learn as well as fast and powerful.
-
-GCLI has a type system to help ensure that users are inputting valid commands
-and to enable us to provide sensible context sensitive help. GCLI provides
-integration with JavaScript rather than being an alternative (like CoffeeScript).
-
-
-## History
-
-GCLI was born as part of the
-[Bespin](http://ajaxian.com/archives/canvas-for-a-text-editor) project and was
-[discussed at the time](http://j.mp/bespin-cli). The command line component
-survived the rename of Bepsin to Skywriter and the merger with Ace, got a name
-of it's own (Cockpit) which didn't last long before the project was named GCLI.
-It is now being used in the Firefox's web console where it doesn't have a
-separate identity but it's still called GCLI outside of Firefox. It is also
-used in [Eclipse Orion](http://www.eclipse.org/orion/).
-
-
-## Environments
-
-GCLI is designed to work in a number of environments:
-
-1. As a component of Firefox developer tools.
-2. As an adjunct to Orion/Ace and other online editors.
-3. As a plugin to any web-page wishing to provide its own set of commands.
-4. As part of a standalone web browser extension with it's own set of commands.
-
-
-## Related Pages
-
-Other sources of GCLI documentation:
-
-- [Writing Commands](writing-commands.md)
-- [Writing Types](writing-types.md)
-- [Developing GCLI](developing-gcli.md)
-- [Writing Tests](writing-tests.md) / [Running Tests](running-tests.md)
-- [The Design of GCLI](design.md)
-- Source
-  - The most up-to-date source is in [this Github repository](https://github.com/joewalker/gcli/).
-  - When a feature is 'done' it's merged into the [Mozilla clone](https://github.com/mozilla/gcli/).
-  - From which it flows into [Mozilla Central](https://hg.mozilla.org/mozilla-central/file/tip/devtools/client/commandline).
-- [Demo of GCLI](http://mozilla.github.com/gcli/) with an arbitrary set of demo
-  commands
-- Other Documentation
-  - [Embedding docs](https://github.com/mozilla/gcli/blob/master/docs/index.md)
-  - [Status page](http://mozilla.github.com/devtools/2011/status.html#gcli)
-
-
-## Accessibility
-
-GCLI uses ARIA roles to guide a screen-reader as to the important sections to
-voice. We welcome [feedback on how these roles are implemented](https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Developer+Tools:+Graphic+Commandline+and+Toolbar&rep_platform=All&op_sys=All&short_desc=GCLI).
-
-The command line uses TAB as a method of completing current input, this
-prevents use of TAB for keyboard navigation. Instead of using TAB to move to
-the next field you can use F6. In addition to F6, ALT+TAB, CTRL+TAB, META+TAB
-make an attempt to move the focus on. How well this works depends on your
-OS/browser combination.
-
-
-## Embedding GCLI
-
-There are 3 basic steps in using GCLI in your system.
-
-1. Import a GCLI JavaScript file.
-   For serious use of GCLI you are likely to be creating a custom build (see
-   below) however if you just want to have a quick play, you can use
-   ``gcli-uncompressed.js`` from [the gh-pages branch of GCLI]
-   (https://github.com/mozilla/gcli/tree/gh-pages)
-   Just place the following wherever you place your script files.
-
-        <script src="path/to/gcli-uncompressed.js" type="text/javascript"></script>
-
-2. Having imported GCLI, we need to tell it where to display. The simplest
-   method is to include an elements with the id of ``gcli-input`` and
-   ``gcli-display``.
-
-        <input id="gcli-input" type="text"/>
-        <div id="gcli-display"></div>
-
-3. Tell GCLI what commands to make available. See the sections on Writing
-   Commands, Writing Types and Writing Fields for more information.
-
-   GCLI uses the CommonJS AMD format for it's files, so a 'require' statement
-   is needed to get started.
-
-        require([ 'gcli/index' ], function(gcli) {
-          gcli.add(...);         // Register custom commands/types/etc
-          gcli.createTerminal(); // Create a user interface
-        });
-
-   The createTerminal() function takes an ``options`` objects which allows
-   customization. At the current time the documentation of these object is left
-   to the source.
-
-
-## Backwards Compatibility
-
-The goals of the GCLI project are:
-
-- Aim for very good backwards compatibility with code required from an
-  'index' module. This means we will not break code without a cycle of
-  deprecation warnings.
-
-  There are currently 3 'index' modules:
-  - gcli/index (all you need to get started with GCLI)
-  - demo/index (a number of demo commands)
-  - gclitest/index (GCLI test suite)
-
-  Code from these modules uses the module pattern to prevent access to internal
-  functions, so in essence, if you can get to it from an index module, you
-  should be ok.
-
-- We try to avoid needless change to other modules, however we don't make any
-  promises, and don't provide a deprecation cycle.
-
-  Code from other modules uses classes rather than modules, so member variables
-  are exposed. Many classes mark private members using the `_underscorePrefix`
-  pattern. Particular care should be taken if access is needed to a private
-  member.
-
-
-## Creating Custom Builds
-
-GCLI uses [DryIce](https://github.com/mozilla/dryice) to create custom builds.
-If dryice is installed (``npm install .``) then you can create a built
-version of GCLI simply using ``node gcli.js standard``. DryIce supplies a custom
-module loader to replace RequireJS for built applications.
-
-The build will be output to the ``built`` directory. The directory will be
-created if it doesn't exist.
deleted file mode 100644
--- a/devtools/shared/gcli/source/docs/running-tests.md
+++ /dev/null
@@ -1,60 +0,0 @@
-
-# Running Tests
-
-GCLI has a test suite that can be run in a number of different environments.
-Some of the tests don't work in all environments. These should be automatically
-skipped when not applicable.
-
-
-## Web
-
-Running a limited set of test from the web is the easiest. Simply load
-'localtest.html' and the unit tests should be run automatically, with results
-displayed on the console. Tests can be re-run using the 'test' command.
-
-It also creates a function 'testCommands()' to be run at a JS prompt, which
-enables the test commands for debugging purposes.
-
-
-## Firefox
-
-GCLI's test suite integrates with Mochitest and runs automatically on each test
-run. Dryice packages the tests to format them for the Firefox build system.
-
-For more information about running Mochitest on Firefox (including GCLI) see
-[the MDN, Mochitest docs](https://developer.mozilla.org/en/Mochitest)
-
-
-# Node
-
-Running the test suite under node can be done as follows:
-
-    $ node gcli.js test
-
-Or, using the `test` command:
-
-    $ node gcli.js
-    Serving GCLI to http://localhost:9999/
-    This is also a limited GCLI prompt.
-    Type 'help' for a list of commands, CTRL+C twice to exit:
-    : test
-    
-    testCli: Pass (funcs=9, checks=208)
-    testCompletion: Pass (funcs=1, checks=139)
-    testExec: Pass (funcs=1, checks=133)
-    testHistory: Pass (funcs=3, checks=13)
-    ....
-    
-    Summary: Pass (951 checks)
-
-
-# Travis CI
-
-GCLI check-ins are automatically tested by [Travis CI](https://travis-ci.org/joewalker/gcli).
-
-
-# Test Case Generation
-
-GCLI can generate test cases automagically. Load ```localtest.html```, type a
-command to be tested into GCLI, and the press F2. GCLI will output to the
-console a template test case for the entered command.
deleted file mode 100644
--- a/devtools/shared/gcli/source/docs/writing-commands.md
+++ /dev/null
@@ -1,757 +0,0 @@
-
-# Writing Commands
-
-## Basics
-
-GCLI has opinions about how commands should be written, and it encourages you
-to do The Right Thing. The opinions are based on helping users convert their
-intentions to commands and commands to what's actually going to happen.
-
-- Related commands should be sub-commands of a parent command. One of the goals
-  of GCLI is to support a large number of commands without things becoming
-  confusing, this will require some sort of namespacing or there will be
-  many people wanting to implement the ``add`` command. This style of
-  writing commands has become common place in Unix as the number of commands
-  has gone up.
-  The ```context``` command allows users to focus on a parent command, promoting
-  its sub-commands above others.
-
-- Each command should do exactly and only one thing. An example of a Unix
-  command that breaks this principle is the ``tar`` command
-
-        $ tar -zcf foo.tar.gz .
-        $ tar -zxf foo.tar.gz .
-
-  These 2 commands do exactly opposite things. Many a file has died as a result
-  of a x/c typo. In GCLI this would be better expressed:
-
-        $ tar create foo.tar.gz -z .
-        $ tar extract foo.tar.gz -z .
-
-  There may be commands (like tar) which have enough history behind them
-  that we shouldn't force everyone to re-learn a new syntax. The can be achieved
-  by having a single string parameter and parsing the input in the command)
-
-- Avoid errors. We try to avoid the user having to start again with a command
-  due to some problem. The majority of problems are simple typos which we can
-  catch using command metadata, but there are 2 things command authors can do
-  to prevent breakage.
-
-  - Where possible avoid the need to validate command line parameters in the
-    exec function. This can be done by good parameter design (see 'do exactly
-    and only one thing' above)
-
-  - If there is an obvious fix for an unpredictable problem, offer the
-    solution in the command output. So rather than use request.error (see
-    Request Object below) output some HTML which contains a link to a fixed
-    command line.
-
-Currently these concepts are not enforced at a code level, but they could be in
-the future.
-
-
-## How commands work
-
-This is how to create a basic ``greet`` command:
-
-    gcli.addItems([{
-      name: 'greet',
-      description: 'Show a greeting',
-      params: [
-        {
-          name: 'name',
-          type: 'string',
-          description: 'The name to greet'
-        }
-      ],
-      returnType: 'string',
-      exec: function(args, context) {
-        return 'Hello, ' + args.name;
-      }
-    }]);
-
-This command is used as follows:
-
-    : greet Joe
-    Hello, Joe
-
-Some terminology that isn't always obvious: a function has 'parameters', and
-when you call a function, you pass 'arguments' to it.
-
-
-## Internationalization (i18n)
-
-There are several ways that GCLI commands can be localized. The best method
-depends on what context you are writing your command for.
-
-### Firefox Embedding
-
-GCLI supports Mozilla style localization. To add a command that will only ever
-be used embedded in Firefox, this is the way to go. Your strings should be
-stored in ``toolkit/locales/en-US/chrome/global/devtools/gclicommands.properties``,
-And you should access them using ``let l10n = require("gcli/l10n")`` and then
-``l10n.lookup(...)`` or ``l10n.lookupFormat()``
-
-For examples of existing commands, take a look in
-``devtools/client/webconsole/GcliCommands.jsm``, which contains most of the
-current GCLI commands. If you will be adding a number of new commands, then
-consider starting a new JSM.
-
-Your command will then look something like this:
-
-    gcli.addItems([{
-      name: 'greet',
-      description: gcli.lookup("greetDesc")
-      ...
-    }]);
-
-### Web Commands
-
-There are 2 ways to provide translated strings for web use. The first is to
-supply the translated strings in the description:
-
-    gcli.addItems([{
-      name: 'greet',
-      description: {
-        'root': 'Show a greeting',
-        'fr-fr': 'Afficher un message d'accueil',
-        'de-de': 'Zeige einen Gruß',
-        'gk-gk': 'Εμφάνιση ένα χαιρετισμό',
-        ...
-      }
-      ...
-    }]);
-
-Each description should contain at least a 'root' entry which is the
-default if no better match is found. This method has the benefit of being
-compact and simple, however it has the significant drawback of being wasteful
-of memory and bandwidth to transmit and store a significant number of strings,
-the majority of which will never be used.
-
-More efficient is to supply a lookup key and ask GCLI to lookup the key from an
-appropriate localized strings file:
-
-    gcli.addItems([{
-      name: 'greet',
-      description: { 'key': 'demoGreetingDesc' }
-      ...
-    }]);
-
-For web usage, the central store of localized strings is
-``lib/gcli/nls/strings.js``. Other string files can be added using the
-``l10n.registerStringsSource(...)`` function.
-
-This method can be used both in Firefox and on the Web (see the help command
-for an example). However this method has the drawback that it will not work
-with DryIce built files until we fix bug 683844.
-
-
-## Default argument values
-
-The ``greet`` command requires the entry of the ``name`` parameter. This
-parameter can be made optional with the addition of a ``defaultValue`` to the
-parameter:
-
-    gcli.addItems([{
-      name: 'greet',
-      description: 'Show a message to someone',
-      params: [
-        {
-          name: 'name',
-          type: 'string',
-          description: 'The name to greet',
-          defaultValue: 'World!'
-        }
-      ],
-      returnType: 'string',
-      exec: function(args, context) {
-        return "Hello, " + args.name;
-      }
-    }]);
-
-Now we can also use the ``greet`` command as follows:
-
-    : greet
-    Hello, World!
-
-
-## Positional vs. named arguments
-
-Arguments can be entered either positionally or as named arguments. Generally
-users will prefer to type the positional version, however the named alternative
-can be more self documenting.
-
-For example, we can also invoke the greet command as follows:
-
-    : greet --name Joe
-    Hello, Joe
-
-
-## Short argument names
-
-GCLI allows you to specify a 'short' character for any parameter:
-
-    gcli.addItems([{
-      name: 'greet',
-      params: [
-        {
-          name: 'name',
-          short: 'n',
-          type: 'string',
-          ...
-        }
-      ],
-      ...
-    }]);
-
-This is used as follows:
-
-    : greet -n Fred
-    Hello, Fred
-
-Currently GCLI does not allow short parameter merging (i.e. ```ls -la```)
-however this is planned.
-
-
-## Parameter types
-
-Initially the available types are:
-
-- string
-- boolean
-- number
-- selection
-- delegate
-- date
-- array
-- file
-- node
-- nodelist
-- resource
-- command
-- setting
-
-This list can be extended. See [Writing Types](writing-types.md) on types for
-more information.
-
-The following examples assume the following definition of the ```greet```
-command:
-
-    gcli.addItems([{
-      name: 'greet',
-      params: [
-        { name: 'name', type: 'string' },
-        { name: 'repeat', type: 'number' }
-      ],
-      ...
-    }]);
-
-Parameters can be specified either with named arguments:
-
-    : greet --name Joe --repeat 2
-
-And sometimes positionally:
-
-    : greet Joe 2
-
-Parameters can be specified positionally if they are considered 'important'.
-Unimportant parameters must be specified with a named argument.
-
-Named arguments can be specified anywhere on the command line (after the
-command itself) however positional arguments must be in order. So
-these examples are the same:
-
-    : greet --name Joe --repeat 2
-    : greet --repeat 2 --name Joe
-
-However (obviously) these are not the same:
-
-    : greet Joe 2
-    : greet 2 Joe
-
-(The second would be an error because 'Joe' is not a number).
-
-Named arguments are assigned first, then the remaining arguments are assigned
-to the remaining parameters. So the following is valid and unambiguous:
-
-    : greet 2 --name Joe
-
-Positional parameters quickly become unwieldy with long parameter lists so we
-recommend only having 2 or 3 important parameters. GCLI provides hints for
-important parameters more obviously than unimportant ones.
-
-Parameters are 'important' if they are not in a parameter group. The easiest way
-to achieve this is to use the ```option: true``` property.
-
-For example, using:
-
-    gcli.addItems([{
-      name: 'greet',
-      params: [
-        { name: 'name', type: 'string' },
-        { name: 'repeat', type: 'number', option: true, defaultValue: 1 }
-      ],
-      ...
-    }]);
-
-Would mean that this is an error
-
-    : greet Joe 2
-
-You would instead need to do the following:
-
-    : greet Joe --repeat 2
-
-For more on parameter groups, see below.
-
-In addition to being 'important' and 'unimportant' parameters can also be
-optional. If is possible to be important and optional, but it is not possible
-to be unimportant and non-optional.
-
-Parameters are optional if they either:
-- Have a ```defaultValue``` property
-- Are of ```type=boolean``` (boolean arguments automatically default to being false)
-
-There is currently no way to make parameters mutually exclusive.
-
-
-## Selection types
-
-Parameters can have a type of ``selection``. For example:
-
-    gcli.addItems([{
-      name: 'greet',
-      params: [
-        { name: 'name', ... },
-        {
-          name: 'lang',
-          description: 'In which language should we greet',
-          type: { name: 'selection', data: [ 'en', 'fr', 'de', 'es', 'gk' ] },
-          defaultValue: 'en'
-        }
-      ],
-      ...
-    }]);
-
-GCLI will enforce that the value of ``arg.lang`` was one of the values
-specified. Alternatively ``data`` can be a function which returns an array of
-strings.
-
-The ``data`` property is useful when the underlying type is a string but it
-doesn't work when the underlying type is something else. For this use the
-``lookup`` property as follows:
-
-      type: {
-        name: 'selection',
-        lookup: {
-          'en': Locale.EN,
-          'fr': Locale.FR,
-          ...
-        }
-      },
-
-Similarly, ``lookup`` can be a function returning the data of this type.
-
-
-## Number types
-
-Number types are mostly self explanatory, they have one special property which
-is the ability to specify upper and lower bounds for the number:
-
-    gcli.addItems([{
-      name: 'volume',
-      params: [
-        {
-          name: 'vol',
-          description: 'How loud should we go',
-          type: { name: 'number', min: 0, max: 11 }
-        }
-      ],
-      ...
-    }]);
-
-You can also specify a ``step`` property which specifies by what amount we
-should increment and decrement the values. The ``min``, ``max``, and ``step``
-properties are used by the command line when up and down are pressed and in
-the input type of a dialog generated from this command.
-
-
-## Delegate types
-
-Delegate types are needed when the type of some parameter depends on the type
-of another parameter. For example:
-
-    : set height 100
-    : set name "Joe Walker"
-
-We can achieve this as follows:
-
-    gcli.addItems([{
-      name: 'set',
-      params: [
-        {
-          name: 'setting',
-          type: { name: 'selection', values: [ 'height', 'name' ] }
-        },
-        {
-          name: 'value',
-          type: {
-            name: 'delegate',
-            delegateType: function() { ... }
-          }
-        }
-      ],
-      ...
-    }]);
-
-Several details are left out of this example, like how the delegateType()
-function knows what the current setting is. See the ``pref`` command for an
-example.
-
-
-## Array types
-
-Parameters can have a type of ``array``. For example:
-
-    gcli.addItems([{
-      name: 'greet',
-      params: [
-        {
-          name: 'names',
-          type: { name: 'array', subtype: 'string' },
-          description: 'The names to greet',
-          defaultValue: [ 'World!' ]
-        }
-      ],
-      ...
-      exec: function(args, context) {
-        return "Hello, " + args.names.join(', ') + '.';
-      }
-    }]);
-
-This would be used as follows:
-
-    : greet Fred Jim Shiela
-    Hello, Fred, Jim, Shiela.
-
-Or using named arguments:
-
-    : greet --names Fred --names Jim --names Shiela
-    Hello, Fred, Jim, Shiela.
-
-There can only be one ungrouped parameter with an array type, and it must be
-at the end of the list of parameters (i.e. just before any parameter groups).
-This avoids confusion as to which parameter an argument should be assigned.
-
-
-## Sub-commands
-
-It is common for commands to be groups into those with similar functionality.
-Examples include virtually all VCS commands, ``apt-get``, etc. There are many
-examples of commands that should be structured as in a sub-command style -
-``tar`` being the obvious example, but others include ``crontab``.
-
-Groups of commands are specified with the top level command not having an
-exec function:
-
-    gcli.addItems([
-      {
-        name: 'tar',
-        description: 'Commands to manipulate archives',
-      },
-      {
-        name: 'tar create',
-        description: 'Create a new archive',
-        exec: function(args, context) { ... },
-        ...
-      },
-      {
-        name: 'tar extract',
-        description: 'Extract from an archive',
-        exec: function(args, context) { ... },
-        ...
-      }
-    ]);
-
-
-## Parameter groups
-
-Parameters can be grouped into sections.
-
-There are 3 ways to assign a parameter to a group.
-
-The simplest uses ```option: true``` to put a parameter into the default
-'Options' group:
-
-    gcli.addItems([{
-      name: 'greet',
-      params: [
-        { name: 'repeat', type: 'number', option: true }
-      ],
-      ...
-    }]);
-
-The ```option``` property can also take a string to use an alternative parameter
-group:
-
-    gcli.addItems([{
-      name: 'greet',
-      params: [
-        { name: 'repeat', type: 'number', option: 'Advanced' }
-      ],
-      ...
-    }]);
-
-An example of how this can be useful is 'git' which categorizes parameters into
-'porcelain' and 'plumbing'.
-
-Finally, parameters can be grouped together as follows:
-
-    gcli.addItems([{
-      name: 'greet',
-      params: [
-        { name: 'name', type: 'string', description: 'The name to greet' },
-        {
-          group: 'Advanced Options',
-          params: [
-            { name: 'repeat', type: 'number', defaultValue: 1 },
-            { name: 'debug', type: 'boolean' }
-          ]
-        }
-      ],
-      ...
-    }]);
-
-This could be used as follows:
-
-    : greet Joe --repeat 2 --debug
-    About to send greeting
-    Hello, Joe
-    Hello, Joe
-    Done!
-
-Parameter groups must come after non-grouped parameters because non-grouped
-parameters can be assigned positionally, so their index is important. We don't
-want 'holes' in the order caused by parameter groups.
-
-
-## Command metadata
-
-Each command should have the following properties:
-
-- A string ``name``.
-- A short ``description`` string. Generally no more than 20 characters without
-  a terminating period/fullstop.
-- A function to ``exec``ute. (Optional for the parent containing sub-commands)
-  See below for more details.
-
-And optionally the following extra properties:
-
-- A declaration of the accepted ``params``.
-- A ``hidden`` property to stop the command showing up in requests for help.
-- A ``context`` property which defines the scope of the function that we're
-  calling. Rather than simply call ``exec()``, we do ``exec.call(context)``.
-- A ``manual`` property which allows a fuller description of the purpose of the
-  command.
-- A ``returnType`` specifying how we should handle the value returned from the
-  exec function.
-
-The ``params`` property is an array of objects, one for each parameter. Each
-parameter object should have the following 3 properties:
-
-- A string ``name``.
-- A short string ``description`` as for the command.
-- A ``type`` which refers to an existing Type (see Writing Types).
-
-Optionally each parameter can have these properties:
-
-- A ``defaultValue`` (which should be in the type specified in ``type``).
-  The defaultValue will be used when there is no argument supplied for this
-  parameter on the command line.
-  If the parameter has a ``defaultValue``, other than ``undefined`` then the
-  parameter is optional, and if unspecified on the command line, the matching
-  argument will have this value when the function is called.
-  If ``defaultValue`` is missing, or if it is set to ``undefined``, then the
-  system will ensure that a value is provided before anything is executed.
-  There are 2 special cases:
-  - If the type is ``selection``, then defaultValue must not be undefined.
-    The defaultValue must either be ``null`` (meaning that a value must be
-    supplied by the user) or one of the selection values.
-  - If the type is ``boolean``, then ``defaultValue:false`` is implied and
-    can't be changed. Boolean toggles are assumed to be off by default, and
-    should be named to match.
-- A ``manual`` property for parameters is exactly analogous to the ``manual``
-  property for commands - descriptive text that is longer than than 20
-  characters.
-
-
-## The Command Function (exec)
-
-The parameters to the exec function are designed to be useful when you have a
-large number of parameters, and to give direct access to the environment (if
-used).
-
-    gcli.addItems([{
-      name: 'echo',
-      description: 'The message to display.',
-      params: [
-        {
-          name: 'message',
-          type: 'string',
-          description: 'The message to display.'
-        }
-      ],
-      returnType: 'string',
-      exec: function(args, context) {
-        return args.message;
-      }
-    }]);
-
-The ``args`` object contains the values specified on the params section and
-provided on the command line. In this example it would contain the message for
-display as ``args.message``.
-
-The ``context`` object has the following signature:
-
-    {
-      environment: ..., // environment object passed to createTerminal()
-      exec: ...,        // function to execute a command
-      update: ...,      // function to alter the text of the input area
-      createView: ...,  // function to help creating rich output
-      defer: ...,       // function to create a deferred promise
-    }
-
-The ``environment`` object is opaque to GCLI. It can be used for providing
-arbitrary data to your commands about their environment. It is most useful
-when more than one command line exists on a page with similar commands in both
-which should act in their own ways.
-An example use for ``environment`` would be a page with several tabs, each
-containing an editor with a command line. Commands executed in those editors
-should apply to the relevant editor.
-The ``environment`` object is passed to GCLI at startup (probably in the
-``createTerminal()`` function).
-
-The ``document`` object is also passed to GCLI at startup. In some environments
-(e.g. embedded in Firefox) there is no global ``document``. This object
-provides a way to create DOM nodes.
-
-``defer()`` allows commands to execute asynchronously.
-
-
-## Returning data
-
-The command meta-data specifies the type of data returned by the command using
-the ``returnValue`` setting.
-
-``returnValue`` processing is currently functioning, but incomplete, and being
-tracked in [Bug 657595](http://bugzil.la/657595). Currently you should specify
-a ``returnType`` of ``string`` or ``html``. If using HTML, you can return
-either an HTML string or a DOM node.
-
-In the future, JSON will be strongly encouraged as the return type, with some
-formatting functions to convert the JSON to HTML.
-
-Asynchronous output is achieved using a promise created from the ``context``
-parameter: ``context.defer()``.
-
-Some examples of this is practice:
-
-    { returnType: "string" }
-    ...
-    return "example";
-
-GCLI interprets the output as a plain string. It will be escaped before display
-and available as input to other commands as a plain string.
-
-    { returnType: "html" }
-    ...
-    return "<p>Hello</p>";
-
-GCLI will interpret this as HTML, and parse it for display.
-
-    { returnType: "dom" }
-    ...
-    return util.createElement(context.document, 'div');
-
-``util.createElement`` is a utility to ensure use of the XHTML namespace in XUL
-and other XML documents. In an HTML document it's functionally equivalent to
-``context.document.createElement('div')``. If your command is likely to be used
-in Firefox or another XML environment, you should use it. You can import it
-with ``var util = require('util/util');``.
-
-GCLI will use the returned HTML element as returned. See notes on ``context``
-above.
-
-    { returnType: "number" }
-    ...
-    return 42;
-
-GCLI will display the element in a similar way to a string, but it the value
-will be available to future commands as a number.
-
-    { returnType: "date" }
-    ...
-    return new Date();
-
-    { returnType: "file" }
-    ...
-    return new File();
-
-Both these examples return data as a given type, for which a converter will
-be required before the value can be displayed. The type system is likely to
-change before this is finalized. Please contact the author for more
-information.
-
-    { returnType: "string" }
-    ...
-    var deferred = context.defer();
-    setTimeout(function() {
-      deferred.resolve("hello");
-    }, 500);
-    return deferred.promise;
-
-Errors can be signaled by throwing an exception. GCLI will display the message
-property (or the toString() value if there is no message property). (However
-see *3 principles for writing commands* above for ways to avoid doing this).
-
-
-## Specifying Types
-
-Types are generally specified by a simple string, e.g. ``'string'``. For most
-types this is enough detail. There are a number of exceptions:
-
-* Array types. We declare a parameter to be an array of things using ``[]``,
-  for example: ``number[]``.
-* Selection types. There are 3 ways to specify the options in a selection:
-  * Using a lookup map
-
-            type: {
-              name: 'selection',
-              lookup: { one:1, two:2, three:3 }
-            }
-
-    (The boolean type is effectively just a selection that uses
-    ``lookup:{ 'true': true, 'false': false }``)
-
-  * Using given strings
-
-            type: {
-              name: 'selection',
-              data: [ 'left', 'center', 'right' ]
-            }
-
-  * Using named objects, (objects with a ``name`` property)
-
-            type: {
-              name: 'selection',
-              data: [
-                { name: 'Google', url: 'http://www.google.com/' },
-                { name: 'Microsoft', url: 'http://www.microsoft.com/' },
-                { name: 'Yahoo', url: 'http://www.yahoo.com/' }
-              ]
-            }
-
-* Delegate type. It is generally best to inherit from Delegate in order to
-  provide a customization of this type. See settingValue for an example.
-
-See below for more information.
deleted file mode 100644
--- a/devtools/shared/gcli/source/docs/writing-tests.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-# Writing Tests
-
-There are several sources of GCLI tests and several environments in which they
-are run.
-
-The majority of GCLI tests are stored in
-[this repository](https://github.com/joewalker/gcli/) in files named like
-```./lib/gclitest/test*.js```. These tests run in Firefox, Chrome, Opera,
-and NodeJS/JsDom
-
-See [Running Tests](running-tests.md) for further details.
-
-GCLI comes with a generic unit test harness (in ```./lib/test/```) and a
-set of helpers for creating GCLI tests (in ```./lib/gclitest/helpers.js```).
-
-# GCLI tests in Firefox
-
-The build process converts the GCLI tests to run under Mochitest inside the
-Firefox unit tests. It also adds some
deleted file mode 100644
--- a/devtools/shared/gcli/source/docs/writing-types.md
+++ /dev/null
@@ -1,106 +0,0 @@
-
-# Writing Types
-
-Commands are a fundamental building block because they are what the users
-directly interacts with, however they are built on ``Type``s. There are a
-number of built in types:
-
-* string. This is a JavaScript string
-* number. A JavaScript number
-* boolean. A JavaScript boolean
-* selection. This is an selection from a number of alternatives
-* delegate. This type could change depending on other factors, but is well
-  defined when one of the conversion routines is called.
-
-There are a number of additional types defined by Pilot and GCLI as
-extensions to the ``selection`` and ``delegate`` types
-
-* setting. One of the defined settings
-* settingValue. A value that can be applied to an associated setting.
-* command. One of the defined commands
-
-Most of our types are 'static' e.g. there is only one type of 'string', however
-some types like 'selection' and 'delegate' are customizable.
-
-All types must inherit from Type and have the following methods:
-
-    /**
-     * Convert the given <tt>value</tt> to a string representation.
-     * Where possible, there should be round-tripping between values and their
-     * string representations.
-     */
-    stringify: function(value) { return 'string version of value'; },
-
-    /**
-     * Convert the given <tt>str</tt> to an instance of this type.
-     * Where possible, there should be round-tripping between values and their
-     * string representations.
-     * @return Conversion
-     */
-    parse: function(str) { return new Conversion(...); },
-
-    /**
-     * The plug-in system, and other things need to know what this type is
-     * called. The name alone is not enough to fully specify a type. Types like
-     * 'selection' and 'delegate' need extra data, however this function returns
-     * only the name, not the extra data.
-     * <p>In old bespin, equality was based on the name. This may turn out to be
-     * important in Ace too.
-     */
-    name: 'example',
-
-In addition, defining the following function can be helpful, although Type
-contains default implementations:
-
-* nudge(value, by)
-
-Type, Conversion and Status are all declared by commands.js.
-
-The values produced by the parse function can be of any type, but if you are
-producing your own, you are strongly encouraged to include properties called
-``name`` and ``description`` where it makes sense. There are a number of
-places in GCLI where the UI will be able to provide better help to users if
-your values include these properties.
-
-
-# Writing Fields
-
-Fields are visual representations of types. For simple types like string it is
-enough to use ``<input type=...>``, however more complex types we may wish to
-provide a custom widget to allow the user to enter values of the given type.
-
-This is an example of a very simple new password field type:
-
-    function PasswordField(doc) {
-      this.doc = doc;
-    }
-
-    PasswordField.prototype = Object.create(Field.prototype);
-
-    PasswordField.prototype.createElement = function(assignment) {
-      this.assignment = assignment;
-      this.input = dom.createElement(this.doc, 'input');
-      this.input.type = 'password';
-      this.input.value = assignment.arg ? assignment.arg.text : '';
-
-      this.onKeyup = function() {
-          this.assignment.setValue(this.input.value);
-      }.bind(this);
-      this.input.addEventListener('keyup', this.onKeyup, false);
-
-      this.onChange = function() {
-          this.input.value = this.assignment.arg.text;
-      };
-      this.assignment.onAssignmentChange.add(this.onChange, this);
-
-      return this.input;
-    };
-
-    PasswordField.prototype.destroy = function() {
-      this.input.removeEventListener('keyup', this.onKeyup, false);
-      this.assignment.onAssignmentChange.remove(this.onChange, this);
-    };
-
-    PasswordField.claim = function(type) {
-      return type.name === 'password' ? Field.claim.MATCH : Field.claim.NO_MATCH;
-    };
deleted file mode 100644
--- a/devtools/shared/gcli/source/lib/gcli/cli.js
+++ /dev/null
@@ -1,2209 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var util = require('./util/util');
-var host = require('./util/host');
-var l10n = require('./util/l10n');
-
-var view = require('./ui/view');
-var Parameter = require('./commands/commands').Parameter;
-var CommandOutputManager = require('./commands/commands').CommandOutputManager;
-
-var Status = require('./types/types').Status;
-var Conversion = require('./types/types').Conversion;
-var commandModule = require('./types/command');
-var selectionModule = require('./types/selection');
-
-var Argument = require('./types/types').Argument;
-var ArrayArgument = require('./types/types').ArrayArgument;
-var NamedArgument = require('./types/types').NamedArgument;
-var TrueNamedArgument = require('./types/types').TrueNamedArgument;
-var MergedArgument = require('./types/types').MergedArgument;
-var ScriptArgument = require('./types/types').ScriptArgument;
-
-var RESOLVED = Promise.resolve(undefined);
-
-// Helper to produce a `deferred` object
-// using DOM Promise
-function defer() {
-  let resolve, reject;
-  let p = new Promise((a, b) => {
-    resolve = a;
-    reject = b;
-  });
-  return {
-    promise: p,
-    resolve: resolve,
-    reject: reject
-  };
-}
-
-/**
- * This is a list of the known command line components to enable certain
- * privileged commands to alter parts of a running command line. It is an array
- * of objects shaped like:
- *   { conversionContext:..., executionContext:..., mapping:... }
- * So lookup is O(n) where 'n' is the number of command lines.
- */
-var instances = [];
-
-/**
- * An indexOf that looks-up both types of context
- */
-function instanceIndex(context) {
-  for (var i = 0; i < instances.length; i++) {
-    var instance = instances[i];
-    if (instance.conversionContext === context ||
-        instance.executionContext === context) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-/**
- * findInstance gets access to a Terminal object given a conversionContext or
- * an executionContext (it doesn't have to be a terminal object, just whatever
- * was passed into addMapping()
- */
-exports.getMapping = function(context) {
-  var index = instanceIndex(context);
-  if (index === -1) {
-    console.log('Missing mapping for context: ', context);
-    console.log('Known contexts: ', instances);
-    throw new Error('Missing mapping for context');
-  }
-  return instances[index].mapping;
-};
-
-/**
- * Add a requisition context->terminal mapping
- */
-var addMapping = function(requisition) {
-  if (instanceIndex(requisition.conversionContext) !== -1) {
-    throw new Error('Remote existing mapping before adding a new one');
-  }
-
-  instances.push({
-    conversionContext: requisition.conversionContext,
-    executionContext: requisition.executionContext,
-    mapping: { requisition: requisition }
-  });
-};
-
-/**
- * Remove a requisition context->terminal mapping
- */
-var removeMapping = function(requisition) {
-  var index = instanceIndex(requisition.conversionContext);
-  instances.splice(index, 1);
-};
-
-/**
- * Assignment is a link between a parameter and the data for that parameter.
- * The data for the parameter is available as in the preferred type and as
- * an Argument for the CLI.
- * <p>We also record validity information where applicable.
- * <p>For values, null and undefined have distinct definitions. null means
- * that a value has been provided, undefined means that it has not.
- * Thus, null is a valid default value, and common because it identifies an
- * parameter that is optional. undefined means there is no value from
- * the command line.
- * @constructor
- */
-function Assignment(param) {
-  // The parameter that we are assigning to
-  this.param = param;
-  this.conversion = undefined;
-}
-
-/**
- * Easy accessor for conversion.arg.
- * This is a read-only property because writes to arg should be done through
- * the 'conversion' property.
- */
-Object.defineProperty(Assignment.prototype, 'arg', {
-  get: function() {
-    return this.conversion == null ? undefined : this.conversion.arg;
-  },
-  enumerable: true
-});
-
-/**
- * Easy accessor for conversion.value.
- * This is a read-only property because writes to value should be done through
- * the 'conversion' property.
- */
-Object.defineProperty(Assignment.prototype, 'value', {
-  get: function() {
-    return this.conversion == null ? undefined : this.conversion.value;
-  },
-  enumerable: true
-});
-
-/**
- * Easy (and safe) accessor for conversion.message
- */
-Object.defineProperty(Assignment.prototype, 'message', {
-  get: function() {
-    if (this.conversion != null && this.conversion.message) {
-      return this.conversion.message;
-    }
-    // ERROR conversions have messages, VALID conversions don't need one, so
-    // we just need to consider INCOMPLETE conversions.
-    if (this.getStatus() === Status.INCOMPLETE) {
-      return l10n.lookupFormat('cliIncompleteParam', [ this.param.name ]);
-    }
-    return '';
-  },
-  enumerable: true
-});
-
-/**
- * Easy (and safe) accessor for conversion.getPredictions()
- * @return An array of objects with name and value elements. For example:
- * [ { name:'bestmatch', value:foo1 }, { name:'next', value:foo2 }, ... ]
- */
-Assignment.prototype.getPredictions = function(context) {
-  return this.conversion == null ? [] : this.conversion.getPredictions(context);
-};
-
-/**
- * Accessor for a prediction by index.
- * This is useful above <tt>getPredictions()[index]</tt> because it normalizes
- * index to be within the bounds of the predictions, which means that the UI
- * can maintain an index of which prediction to choose without caring how many
- * predictions there are.
- * @param rank The index of the prediction to choose
- */
-Assignment.prototype.getPredictionRanked = function(context, rank) {
-  if (rank == null) {
-    rank = 0;
-  }
-
-  if (this.isInName()) {
-    return Promise.resolve(undefined);
-  }
-
-  return this.getPredictions(context).then(predictions => {
-    if (predictions.length === 0) {
-      return undefined;
-    }
-
-    rank = rank % predictions.length;
-    if (rank < 0) {
-      rank = predictions.length + rank;
-    }
-    return predictions[rank];
-  });
-};
-
-/**
- * Some places want to take special action if we are in the name part of a
- * named argument (i.e. the '--foo' bit).
- * Currently this does not take actual cursor position into account, it just
- * assumes that the cursor is at the end. In the future we will probably want
- * to take this into account.
- */
-Assignment.prototype.isInName = function() {
-  return this.conversion.arg.type === 'NamedArgument' &&
-         this.conversion.arg.prefix.slice(-1) !== ' ';
-};
-
-/**
- * Work out what the status of the current conversion is which involves looking
- * not only at the conversion, but also checking if data has been provided
- * where it should.
- * @param arg For assignments with multiple args (e.g. array assignments) we
- * can narrow the search for status to a single argument.
- */
-Assignment.prototype.getStatus = function(arg) {
-  if (this.param.isDataRequired && !this.conversion.isDataProvided()) {
-    return Status.INCOMPLETE;
-  }
-
-  // Selection/Boolean types with a defined range of values will say that
-  // '' is INCOMPLETE, but the parameter may be optional, so we don't ask
-  // if the user doesn't need to enter something and hasn't done so.
-  if (!this.param.isDataRequired && this.arg.type === 'BlankArgument') {
-    return Status.VALID;
-  }
-
-  return this.conversion.getStatus(arg);
-};
-
-/**
- * Helper when we're rebuilding command lines.
- */
-Assignment.prototype.toString = function() {
-  return this.conversion.toString();
-};
-
-/**
- * For test/debug use only. The output from this function is subject to wanton
- * random change without notice, and should not be relied upon to even exist
- * at some later date.
- */
-Object.defineProperty(Assignment.prototype, '_summaryJson', {
-  get: function() {
-    return {
-      param: this.param.name + '/' + this.param.type.name,
-      defaultValue: this.param.defaultValue,
-      arg: this.conversion.arg._summaryJson,
-      value: this.value,
-      message: this.message,
-      status: this.getStatus().toString()
-    };
-  },
-  enumerable: true
-});
-
-exports.Assignment = Assignment;
-
-
-/**
- * How to dynamically execute JavaScript code
- */
-var customEval = eval;
-
-/**
- * Setup a function to be called in place of 'eval', generally for security
- * reasons
- */
-exports.setEvalFunction = function(newCustomEval) {
-  customEval = newCustomEval;
-};
-
-/**
- * Remove the binding done by setEvalFunction().
- * We purposely set customEval to undefined rather than to 'eval' because there
- * is an implication of setEvalFunction that we're in a security sensitive
- * situation. What if we can trick GCLI into calling unsetEvalFunction() at the
- * wrong time?
- * So to properly undo the effects of setEvalFunction(), you need to call
- * setEvalFunction(eval) rather than unsetEvalFunction(), however the latter is
- * preferred in most cases.
- */
-exports.unsetEvalFunction = function() {
-  customEval = undefined;
-};
-
-/**
- * 'eval' command
- */
-var evalCmd = {
-  item: 'command',
-  name: '{',
-  params: [
-    {
-      name: 'javascript',
-      type: 'javascript',
-      description: ''
-    }
-  ],
-  hidden: true,
-  description: { key: 'cliEvalJavascript' },
-  exec: function(args, context) {
-    var reply = customEval(args.javascript);
-    return context.typedData(typeof reply, reply);
-  },
-  isCommandRegexp: /^\s*\{\s*/
-};
-
-exports.items = [ evalCmd ];
-
-/**
- * This is a special assignment to reflect the command itself.
- */
-function CommandAssignment(requisition) {
-  var commandParamMetadata = {
-    name: '__command',
-    type: { name: 'command', allowNonExec: false }
-  };
-  // This is a hack so that rather than reply with a generic description of the
-  // command assignment, we reply with the description of the assigned command,
-  // (using a generic term if there is no assigned command)
-  var self = this;
-  Object.defineProperty(commandParamMetadata, 'description', {
-    get: function() {
-      var value = self.value;
-      return value && value.description ?
-          value.description :
-          'The command to execute';
-    },
-    enumerable: true
-  });
-  this.param = new Parameter(requisition.system.types, commandParamMetadata);
-}
-
-CommandAssignment.prototype = Object.create(Assignment.prototype);
-
-CommandAssignment.prototype.getStatus = function(arg) {
-  return Status.combine(
-    Assignment.prototype.getStatus.call(this, arg),
-    this.conversion.value && this.conversion.value.exec ?
-            Status.VALID : Status.INCOMPLETE
-  );
-};
-
-exports.CommandAssignment = CommandAssignment;
-
-
-/**
- * Special assignment used when ignoring parameters that don't have a home
- */
-function UnassignedAssignment(requisition, arg) {
-  var isIncompleteName = (arg.text.charAt(0) === '-');
-  this.param = new Parameter(requisition.system.types, {
-    name: '__unassigned',
-    description: l10n.lookup('cliOptions'),
-    type: {
-      name: 'param',
-      requisition: requisition,
-      isIncompleteName: isIncompleteName
-    }
-  });
-
-  // It would be nice to do 'conversion = parm.type.parse(arg, ...)' except
-  // that type.parse returns a promise (even though it's synchronous in this
-  // case)
-  if (isIncompleteName) {
-    var lookup = commandModule.getDisplayedParamLookup(requisition);
-    var predictions = selectionModule.findPredictions(arg, lookup);
-    this.conversion = selectionModule.convertPredictions(arg, predictions);
-  }
-  else {
-    var message = l10n.lookup('cliUnusedArg');
-    this.conversion = new Conversion(undefined, arg, Status.ERROR, message);
-  }
-
-  this.conversion.assignment = this;
-}
-
-UnassignedAssignment.prototype = Object.create(Assignment.prototype);
-
-UnassignedAssignment.prototype.getStatus = function(arg) {
-  return this.conversion.getStatus();
-};
-
-var logErrors = true;
-
-/**
- * Allow tests that expect failures to avoid clogging up the console
- */
-Object.defineProperty(exports, 'logErrors', {
-  get: function() {
-    return logErrors;
-  },
-  set: function(val) {
-    logErrors = val;
-  },
-  enumerable: true
-});
-
-/**
- * A Requisition collects the information needed to execute a command.
- *
- * (For a definition of the term, see http://en.wikipedia.org/wiki/Requisition)
- * This term is used because carries the notion of a work-flow, or process to
- * getting the information to execute a command correct.
- * There is little point in a requisition for parameter-less commands because
- * there is no information to collect. A Requisition is a collection of
- * assignments of values to parameters, each handled by an instance of
- * Assignment.
- *
- * @param system Allows access to the various plug-in points in GCLI. At a
- * minimum it must contain commands and types objects.
- * @param options A set of options to customize how GCLI is used. Includes:
- * - environment An optional opaque object passed to commands in the
- *   Execution Context.
- * - document A DOM Document passed to commands using the Execution Context in
- *   order to allow creation of DOM nodes. If missing Requisition will use the
- *   global 'document', or leave undefined.
- * - commandOutputManager A custom commandOutputManager to which output should
- *   be sent
- * @constructor
- */
-function Requisition(system, options) {
-  options = options || {};
-
-  this.environment = options.environment || {};
-  this.document = options.document;
-  if (this.document == null) {
-    try {
-      this.document = document;
-    }
-    catch (ex) {
-      // Ignore
-    }
-  }
-
-  this.commandOutputManager = options.commandOutputManager || new CommandOutputManager();
-  this.system = system;
-
-  this.shell = {
-    cwd: '/', // Where we store the current working directory
-    env: {}   // Where we store the current environment
-  };
-
-  // The command that we are about to execute.
-  // @see setCommandConversion()
-  this.commandAssignment = new CommandAssignment(this);
-
-  // The object that stores of Assignment objects that we are filling out.
-  // The Assignment objects are stored under their param.name for named
-  // lookup. Note: We make use of the property of Javascript objects that
-  // they are not just hashmaps, but linked-list hashmaps which iterate in
-  // insertion order.
-  // _assignments excludes the commandAssignment.
-  this._assignments = {};
-
-  // The count of assignments. Excludes the commandAssignment
-  this.assignmentCount = 0;
-
-  // Used to store cli arguments in the order entered on the cli
-  this._args = [];
-
-  // Used to store cli arguments that were not assigned to parameters
-  this._unassigned = [];
-
-  // Changes can be asynchronous, when one update starts before another
-  // finishes we abandon the former change
-  this._nextUpdateId = 0;
-
-  // We can set a prefix to typed commands to make it easier to focus on
-  // Allowing us to type "add -a; commit" in place of "git add -a; git commit"
-  this.prefix = '';
-
-  addMapping(this);
-  this._setBlankAssignment(this.commandAssignment);
-
-  // If a command calls context.update then the UI needs some way to be
-  // informed of the change
-  this.onExternalUpdate = util.createEvent('Requisition.onExternalUpdate');
-}
-
-/**
- * Avoid memory leaks
- */
-Requisition.prototype.destroy = function() {
-  this.document = undefined;
-  this.environment = undefined;
-  removeMapping(this);
-};
-
-/**
- * If we're about to make an asynchronous change when other async changes could
- * overtake this one, then we want to be able to bail out if overtaken. The
- * value passed back from beginChange should be passed to endChangeCheckOrder
- * on completion of calculation, before the results are applied in order to
- * check that the calculation has not been overtaken
- */
-Requisition.prototype._beginChange = function() {
-  var updateId = this._nextUpdateId;
-  this._nextUpdateId++;
-  return updateId;
-};
-
-/**
- * Check to see if another change has started since updateId started.
- * This allows us to bail out of an update.
- * It's hard to make updates atomic because until you've responded to a parse
- * of the command argument, you don't know how to parse the arguments to that
- * command.
- */
-Requisition.prototype._isChangeCurrent = function(updateId) {
-  return updateId + 1 === this._nextUpdateId;
-};
-
-/**
- * See notes on beginChange
- */
-Requisition.prototype._endChangeCheckOrder = function(updateId) {
-  if (updateId + 1 !== this._nextUpdateId) {
-    // An update that started after we did has already finished, so our
-    // changes are out of date. Abandon further work.
-    return false;
-  }
-
-  return true;
-};
-
-var legacy = false;
-
-/**
- * Functions and data related to the execution of a command
- */
-Object.defineProperty(Requisition.prototype, 'executionContext', {
-  get: function() {
-    if (this._executionContext == null) {
-      this._executionContext = {
-        defer: defer,
-        typedData: function(type, data) {
-          return {
-            isTypedData: true,
-            data: data,
-            type: type
-          };
-        },
-        getArgsObject: this.getArgsObject.bind(this)
-      };
-
-      // Alias requisition so we're clear about what's what
-      var requisition = this;
-      Object.defineProperty(this._executionContext, 'prefix', {
-        get: function() { return requisition.prefix; },
-        enumerable: true
-      });
-      Object.defineProperty(this._executionContext, 'typed', {
-        get: function() { return requisition.toString(); },
-        enumerable: true
-      });
-      Object.defineProperty(this._executionContext, 'environment', {
-        get: function() { return requisition.environment; },
-        enumerable: true
-      });
-      Object.defineProperty(this._executionContext, 'shell', {
-        get: function() { return requisition.shell; },
-        enumerable: true
-      });
-      Object.defineProperty(this._executionContext, 'system', {
-        get: function() { return requisition.system; },
-        enumerable: true
-      });
-
-      this._executionContext.updateExec = this._contextUpdateExec.bind(this);
-
-      if (legacy) {
-        this._executionContext.createView = view.createView;
-        this._executionContext.exec = this.exec.bind(this);
-        this._executionContext.update = this._contextUpdate.bind(this);
-
-        Object.defineProperty(this._executionContext, 'document', {
-          get: function() { return requisition.document; },
-          enumerable: true
-        });
-      }
-    }
-
-    return this._executionContext;
-  },
-  enumerable: true
-});
-
-/**
- * Functions and data related to the conversion of the output of a command
- */
-Object.defineProperty(Requisition.prototype, 'conversionContext', {
-  get: function() {
-    if (this._conversionContext == null) {
-      this._conversionContext = {
-        defer: defer,
-
-        createView: view.createView,
-        exec: this.exec.bind(this),
-        update: this._contextUpdate.bind(this),
-        updateExec: this._contextUpdateExec.bind(this)
-      };
-
-      // Alias requisition so we're clear about what's what
-      var requisition = this;
-
-      Object.defineProperty(this._conversionContext, 'document', {
-        get: function() { return requisition.document; },
-        enumerable: true
-      });
-      Object.defineProperty(this._conversionContext, 'environment', {
-        get: function() { return requisition.environment; },
-        enumerable: true
-      });
-      Object.defineProperty(this._conversionContext, 'system', {
-        get: function() { return requisition.system; },
-        enumerable: true
-      });
-    }
-
-    return this._conversionContext;
-  },
-  enumerable: true
-});
-
-/**
- * Assignments have an order, so we need to store them in an array.
- * But we also need named access ...
- * @return The found assignment, or undefined, if no match was found
- */
-Requisition.prototype.getAssignment = function(nameOrNumber) {
-  var name = (typeof nameOrNumber === 'string') ?
-    nameOrNumber :
-    Object.keys(this._assignments)[nameOrNumber];
-  return this._assignments[name] || undefined;
-};
-
-/**
- * Where parameter name == assignment names - they are the same
- */
-Requisition.prototype.getParameterNames = function() {
-  return Object.keys(this._assignments);
-};
-
-/**
- * The overall status is the most severe status.
- * There is no such thing as an INCOMPLETE overall status because the
- * definition of INCOMPLETE takes into account the cursor position to say 'this
- * isn't quite ERROR because the user can fix it by typing', however overall,
- * this is still an error status.
- */
-Object.defineProperty(Requisition.prototype, 'status', {
-  get: function() {
-    var status = Status.VALID;
-    if (this._unassigned.length !== 0) {
-      var isAllIncomplete = true;
-      this._unassigned.forEach(function(assignment) {
-        if (!assignment.param.type.isIncompleteName) {
-          isAllIncomplete = false;
-        }
-      });
-      status = isAllIncomplete ? Status.INCOMPLETE : Status.ERROR;
-    }
-
-    this.getAssignments(true).forEach(function(assignment) {
-      var assignStatus = assignment.getStatus();
-      if (assignStatus > status) {
-        status = assignStatus;
-      }
-    }, this);
-    if (status === Status.INCOMPLETE) {
-      status = Status.ERROR;
-    }
-    return status;
-  },
-  enumerable : true
-});
-
-/**
- * If ``requisition.status != VALID`` message then return a string which
- * best describes what is wrong. Generally error messages are delivered by
- * looking at the error associated with the argument at the cursor, but there
- * are times when you just want to say 'tell me the worst'.
- * If ``requisition.status != VALID`` then return ``null``.
- */
-Requisition.prototype.getStatusMessage = function() {
-  if (this.commandAssignment.getStatus() !== Status.VALID) {
-    return l10n.lookupFormat('cliUnknownCommand2',
-                             [ this.commandAssignment.arg.text ]);
-  }
-
-  var assignments = this.getAssignments();
-  for (var i = 0; i < assignments.length; i++) {
-    if (assignments[i].getStatus() !== Status.VALID) {
-      return assignments[i].message;
-    }
-  }
-
-  if (this._unassigned.length !== 0) {
-    return l10n.lookup('cliUnusedArg');
-  }
-
-  return null;
-};
-
-/**
- * Extract the names and values of all the assignments, and return as
- * an object.
- */
-Requisition.prototype.getArgsObject = function() {
-  var args = {};
-  this.getAssignments().forEach(function(assignment) {
-    args[assignment.param.name] = assignment.conversion.isDataProvided() ?
-            assignment.value :
-            assignment.param.defaultValue;
-  }, this);
-  return args;
-};
-
-/**
- * Access the arguments as an array.
- * @param includeCommand By default only the parameter arguments are
- * returned unless (includeCommand === true), in which case the list is
- * prepended with commandAssignment.arg
- */
-Requisition.prototype.getAssignments = function(includeCommand) {
-  var assignments = [];
-  if (includeCommand === true) {
-    assignments.push(this.commandAssignment);
-  }
-  Object.keys(this._assignments).forEach(function(name) {
-    assignments.push(this.getAssignment(name));
-  }, this);
-  return assignments;
-};
-
-/**
- * There are a few places where we need to know what the 'next thing' is. What
- * is the user going to be filling out next (assuming they don't enter a named
- * argument). The next argument is the first in line that is both blank, and
- * that can be filled in positionally.
- * @return The next assignment to be used, or null if all the positional
- * parameters have values.
- */
-Requisition.prototype._getFirstBlankPositionalAssignment = function() {
-  var reply = null;
-  Object.keys(this._assignments).some(function(name) {
-    var assignment = this.getAssignment(name);
-    if (assignment.arg.type === 'BlankArgument' &&
-            assignment.param.isPositionalAllowed) {
-      reply = assignment;
-      return true; // i.e. break
-    }
-    return false;
-  }, this);
-  return reply;
-};
-
-/**
- * The update process is asynchronous, so there is (unavoidably) a window
- * where we've worked out the command but don't yet understand all the params.
- * If we try to do things to a requisition in this window we may get
- * inconsistent results. Asynchronous promises have made the window bigger.
- * The only time we've seen this in practice is during focus events due to
- * clicking on a shortcut. The focus want to check the cursor position while
- * the shortcut is updating the command line.
- * This function allows us to detect and back out of this problem.
- * We should be able to remove this function when all the state in a
- * requisition can be encapsulated and updated atomically.
- */
-Requisition.prototype.isUpToDate = function() {
-  if (!this._args) {
-    return false;
-  }
-  for (var i = 0; i < this._args.length; i++) {
-    if (this._args[i].assignment == null) {
-      return false;
-    }
-  }
-  return true;
-};
-
-/**
- * Look through the arguments attached to our assignments for the assignment
- * at the given position.
- * @param {number} cursor The cursor position to query
- */
-Requisition.prototype.getAssignmentAt = function(cursor) {
-  // We short circuit this one because we may have no args, or no args with
-  // any size and the alg below only finds arguments with size.
-  if (cursor === 0) {
-    return this.commandAssignment;
-  }
-
-  var assignForPos = [];
-  var i, j;
-  for (i = 0; i < this._args.length; i++) {
-    var arg = this._args[i];
-    var assignment = arg.assignment;
-
-    // prefix and text are clearly part of the argument
-    for (j = 0; j < arg.prefix.length; j++) {
-      assignForPos.push(assignment);
-    }
-    for (j = 0; j < arg.text.length; j++) {
-      assignForPos.push(assignment);
-    }
-
-    // suffix is part of the argument only if this is a named parameter,
-    // otherwise it looks forwards
-    if (arg.assignment.arg.type === 'NamedArgument') {
-      // leave the argument as it is
-    }
-    else if (this._args.length > i + 1) {
-      // first to the next argument
-      assignment = this._args[i + 1].assignment;
-    }
-    else {
-      // then to the first blank positional parameter, leaving 'as is' if none
-      var nextAssignment = this._getFirstBlankPositionalAssignment();
-      if (nextAssignment != null) {
-        assignment = nextAssignment;
-      }
-    }
-
-    for (j = 0; j < arg.suffix.length; j++) {
-      assignForPos.push(assignment);
-    }
-  }
-
-  // Possible shortcut, we don't really need to go through all the args
-  // to work out the solution to this
-
-  return assignForPos[cursor - 1];
-};
-
-/**
- * Extract a canonical version of the input
- * @return a promise of a string which is the canonical version of what was
- * typed
- */
-Requisition.prototype.toCanonicalString = function() {
-  var cmd = this.commandAssignment.value ?
-      this.commandAssignment.value.name :
-      this.commandAssignment.arg.text;
-
-  // Canonically, if we've opened with a { then we should have a } to close
-  var lineSuffix = '';
-  if (cmd === '{') {
-    var scriptSuffix = this.getAssignment(0).arg.suffix;
-    lineSuffix = (!scriptSuffix.includes('}')) ? ' }' : '';
-  }
-
-  var ctx = this.executionContext;
-
-  // First stringify all the arguments
-  var argPromise = util.promiseEach(this.getAssignments(), assignment => {
-    // Bug 664377: This will cause problems if there is a non-default value
-    // after a default value. Also we need to decide when to use
-    // named parameters in place of positional params. Both can wait.
-    if (assignment.value === assignment.param.defaultValue) {
-      return '';
-    }
-
-    var val = assignment.param.type.stringify(assignment.value, ctx);
-    return Promise.resolve(val).then(str => {
-      return ' ' + str;
-    });
-  });
-
-  return argPromise.then(strings => {
-    return cmd + strings.join('') + lineSuffix;
-  });
-};
-
-/**
- * Reconstitute the input from the args
- */
-Requisition.prototype.toString = function() {
-  if (!this._args) {
-    throw new Error('toString requires a command line. See source.');
-  }
-
-  return this._args.map(function(arg) {
-    return arg.toString();
-  }).join('');
-};
-
-/**
- * For test/debug use only. The output from this function is subject to wanton
- * random change without notice, and should not be relied upon to even exist
- * at some later date.
- */
-Object.defineProperty(Requisition.prototype, '_summaryJson', {
-  get: function() {
-    var summary = {
-      $args: this._args.map(function(arg) {
-        return arg._summaryJson;
-      }),
-      _command: this.commandAssignment._summaryJson,
-      _unassigned: this._unassigned.forEach(function(assignment) {
-        return assignment._summaryJson;
-      })
-    };
-
-    Object.keys(this._assignments).forEach(name => {
-      summary[name] = this.getAssignment(name)._summaryJson;
-    });
-
-    return summary;
-  },
-  enumerable: true
-});
-
-/**
- * When any assignment changes, we might need to update the _args array to
- * match and inform people of changes to the typed input text.
- */
-Requisition.prototype._setAssignmentInternal = function(assignment, conversion) {
-  var oldConversion = assignment.conversion;
-
-  assignment.conversion = conversion;
-  assignment.conversion.assignment = assignment;
-
-  // Do nothing if the conversion is unchanged
-  if (assignment.conversion.equals(oldConversion)) {
-    if (assignment === this.commandAssignment) {
-      this._setBlankArguments();
-    }
-    return;
-  }
-
-  // When the command changes, we need to keep a bunch of stuff in sync
-  if (assignment === this.commandAssignment) {
-    this._assignments = {};
-
-    var command = this.commandAssignment.value;
-    if (command) {
-      for (var i = 0; i < command.params.length; i++) {
-        var param = command.params[i];
-        var newAssignment = new Assignment(param);
-        this._setBlankAssignment(newAssignment);
-        this._assignments[param.name] = newAssignment;
-      }
-    }
-    this.assignmentCount = Object.keys(this._assignments).length;
-  }
-};
-
-/**
- * Internal function to alter the given assignment using the given arg.
- * @param assignment The assignment to alter
- * @param arg The new value for the assignment. An instance of Argument, or an
- * instance of Conversion, or null to set the blank value.
- * @param options There are a number of ways to customize how the assignment
- * is made, including:
- * - internal: (default:false) External updates are required to do more work,
- *   including adjusting the args in this requisition to stay in sync.
- *   On the other hand non internal changes use beginChange to back out of
- *   changes when overtaken asynchronously.
- *   Setting internal:true effectively means this is being called as part of
- *   the update process.
- * - matchPadding: (default:false) Alter the whitespace on the prefix and
- *   suffix of the new argument to match that of the old argument. This only
- *   makes sense with internal=false
- * @return A promise that resolves to undefined when the assignment is complete
- */
-Requisition.prototype.setAssignment = function(assignment, arg, options) {
-  options = options || {};
-  if (!options.internal) {
-    var originalArgs = assignment.arg.getArgs();
-
-    // Update the args array
-    var replacementArgs = arg.getArgs();
-    var maxLen = Math.max(originalArgs.length, replacementArgs.length);
-    for (var i = 0; i < maxLen; i++) {
-      // If there are no more original args, or if the original arg was blank
-      // (i.e. not typed by the user), we'll just need to add at the end
-      if (i >= originalArgs.length || originalArgs[i].type === 'BlankArgument') {
-        this._args.push(replacementArgs[i]);
-        continue;
-      }
-
-      var index = this._args.indexOf(originalArgs[i]);
-      if (index === -1) {
-        console.error('Couldn\'t find ', originalArgs[i], ' in ', this._args);
-        throw new Error('Couldn\'t find ' + originalArgs[i]);
-      }
-
-      // If there are no more replacement args, we just remove the original args
-      // Otherwise swap original args and replacements
-      if (i >= replacementArgs.length) {
-        this._args.splice(index, 1);
-      }
-      else {
-        if (options.matchPadding) {
-          if (replacementArgs[i].prefix.length === 0 &&
-              this._args[index].prefix.length !== 0) {
-            replacementArgs[i].prefix = this._args[index].prefix;
-          }
-          if (replacementArgs[i].suffix.length === 0 &&
-              this._args[index].suffix.length !== 0) {
-            replacementArgs[i].suffix = this._args[index].suffix;
-          }
-        }
-        this._args[index] = replacementArgs[i];
-      }
-    }
-  }
-
-  var updateId = options.internal ? null : this._beginChange();
-
-  var setAssignmentInternal = conversion => {
-    if (options.internal || this._isChangeCurrent(updateId)) {
-      this._setAssignmentInternal(assignment, conversion);
-    }
-
-    if (!options.internal) {
-      this._endChangeCheckOrder(updateId);
-    }
-
-    return Promise.resolve(undefined);
-  };
-
-  if (arg == null) {
-    var blank = assignment.param.type.getBlank(this.executionContext);
-    return setAssignmentInternal(blank);
-  }
-
-  if (typeof arg.getStatus === 'function') {
-    // It's not really an arg, it's a conversion already
-    return setAssignmentInternal(arg);
-  }
-
-  var parsed = assignment.param.type.parse(arg, this.executionContext);
-  return parsed.then(setAssignmentInternal);
-};
-
-/**
- * Reset an assignment to its default value.
- * For internal use only.
- * Happens synchronously.
- */
-Requisition.prototype._setBlankAssignment = function(assignment) {
-  var blank = assignment.param.type.getBlank(this.executionContext);
-  this._setAssignmentInternal(assignment, blank);
-};
-
-/**
- * Reset all the assignments to their default values.
- * For internal use only.
- * Happens synchronously.
- */
-Requisition.prototype._setBlankArguments = function() {
-  this.getAssignments().forEach(this._setBlankAssignment.bind(this));
-};
-
-/**
- * Input trace gives us an array of Argument tracing objects, one for each
- * character in the typed input, from which we can derive information about how
- * to display this typed input. It's a bit like toString on steroids.
- * <p>
- * The returned object has the following members:<ul>
- * <li>character: The character to which this arg trace refers.
- * <li>arg: The Argument to which this character is assigned.
- * <li>part: One of ['prefix'|'text'|suffix'] - how was this char understood
- * </ul>
- * <p>
- * The Argument objects are as output from tokenize() rather than as applied
- * to Assignments by _assign() (i.e. they are not instances of NamedArgument,
- * ArrayArgument, etc).
- * <p>
- * To get at the arguments applied to the assignments simply call
- * <tt>arg.assignment.arg</tt>. If <tt>arg.assignment.arg !== arg</tt> then
- * the arg applied to the assignment will contain the original arg.
- * See _assign() for details.
- */
-Requisition.prototype.createInputArgTrace = function() {
-  if (!this._args) {
-    throw new Error('createInputMap requires a command line. See source.');
-  }
-
-  var args = [];
-  var i;
-  this._args.forEach(function(arg) {
-    for (i = 0; i < arg.prefix.length; i++) {
-      args.push({ arg: arg, character: arg.prefix[i], part: 'prefix' });
-    }
-    for (i = 0; i < arg.text.length; i++) {
-      args.push({ arg: arg, character: arg.text[i], part: 'text' });
-    }
-    for (i = 0; i < arg.suffix.length; i++) {
-      args.push({ arg: arg, character: arg.suffix[i], part: 'suffix' });
-    }
-  });
-
-  return args;
-};
-
-/**
- * If the last character is whitespace then things that we suggest to add to
- * the end don't need a space prefix.
- * While this is quite a niche function, it has 2 benefits:
- * - it's more correct because we can distinguish between final whitespace that
- *   is part of an unclosed string, and parameter separating whitespace.
- * - also it's faster than toString() the whole thing and checking the end char
- * @return true iff the last character is interpreted as parameter separating
- * whitespace
- */
-Requisition.prototype.typedEndsWithSeparator = function() {
-  if (!this._args) {
-    throw new Error('typedEndsWithSeparator requires a command line. See source.');
-  }
-
-  if (this._args.length === 0) {
-    return false;
-  }
-
-  // This is not as easy as doing (this.toString().slice(-1) === ' ')
-  // See the doc comments above; We're checking for separators, not spaces
-  var lastArg = this._args.slice(-1)[0];
-  if (lastArg.suffix.slice(-1) === ' ') {
-    return true;
-  }
-
-  return lastArg.text === '' && lastArg.suffix === ''
-      && lastArg.prefix.slice(-1) === ' ';
-};
-
-/**
- * Return an array of Status scores so we can create a marked up
- * version of the command line input.
- * @param cursor We only take a status of INCOMPLETE to be INCOMPLETE when the
- * cursor is actually in the argument. Otherwise it's an error.
- * @return Array of objects each containing <tt>status</tt> property and a
- * <tt>string</tt> property containing the characters to which the status
- * applies. Concatenating the strings in order gives the original input.
- */
-Requisition.prototype.getInputStatusMarkup = function(cursor) {
-  var argTraces = this.createInputArgTrace();
-  // Generally the 'argument at the cursor' is the argument before the cursor
-  // unless it is before the first char, in which case we take the first.
-  cursor = cursor === 0 ? 0 : cursor - 1;
-  var cTrace = argTraces[cursor];
-
-  var markup = [];
-  for (var i = 0; i < argTraces.length; i++) {
-    var argTrace = argTraces[i];
-    var arg = argTrace.arg;
-    var status = Status.VALID;
-    // When things get very async we can get here while something else is
-    // doing an update, in which case arg.assignment == null, so we check first
-    if (argTrace.part === 'text' && arg.assignment != null) {
-      status = arg.assignment.getStatus(arg);
-      // Promote INCOMPLETE to ERROR  ...
-      if (status === Status.INCOMPLETE) {
-        // If the cursor is in the prefix or suffix of an argument then we
-        // don't consider it in the argument for the purposes of preventing
-        // the escalation to ERROR. However if this is a NamedArgument, then we
-        // allow the suffix (as space between 2 parts of the argument) to be in.
-        // We use arg.assignment.arg not arg because we're looking at the arg
-        // that got put into the assignment not as returned by tokenize()
-        var isNamed = (cTrace.arg.assignment.arg.type === 'NamedArgument');
-        var isInside = cTrace.part === 'text' ||
-                        (isNamed && cTrace.part === 'suffix');
-        if (arg.assignment !== cTrace.arg.assignment || !isInside) {
-          // And if we're not in the command
-          if (!(arg.assignment instanceof CommandAssignment)) {
-            status = Status.ERROR;
-          }
-        }
-      }
-    }
-
-    markup.push({ status: status, string: argTrace.character });
-  }
-
-  // De-dupe: merge entries where 2 adjacent have same status
-  i = 0;
-  while (i < markup.length - 1) {
-    if (markup[i].status === markup[i + 1].status) {
-      markup[i].string += markup[i + 1].string;
-      markup.splice(i + 1, 1);
-    }
-    else {
-      i++;
-    }
-  }
-
-  return markup;
-};
-
-/**
- * Describe the state of the current input in a way that allows display of
- * predictions and completion hints
- * @param start The location of the cursor
- * @param rank The index of the chosen prediction
- * @return A promise of an object containing the following properties:
- * - statusMarkup: An array of Status scores so we can create a marked up
- *   version of the command line input. See getInputStatusMarkup() for details
- * - unclosedJs: Is the entered command a JS command with no closing '}'?
- * - directTabText: A promise of the text that we *add* to the command line
- *   when TAB is pressed, to be displayed directly after the cursor. See also
- *   arrowTabText.
- * - emptyParameters: A promise of the text that describes the arguments that
- *   the user is yet to type.
- * - arrowTabText: A promise of the text that *replaces* the current argument
- *   when TAB is pressed, generally displayed after a "|->" symbol. See also
- *   directTabText.
- */
-Requisition.prototype.getStateData = function(start, rank) {
-  var typed = this.toString();
-  var current = this.getAssignmentAt(start);
-  var context = this.executionContext;
-  var predictionPromise = (typed.trim().length !== 0) ?
-                          current.getPredictionRanked(context, rank) :
-                          Promise.resolve(null);
-
-  return predictionPromise.then(prediction => {
-    // directTabText is for when the current input is a prefix of the completion
-    // arrowTabText is for when we need to use an -> to show what will be used
-    var directTabText = '';
-    var arrowTabText = '';
-    var emptyParameters = [];
-
-    if (typed.trim().length !== 0) {
-      var cArg = current.arg;
-
-      if (prediction) {
-        var tabText = prediction.name;
-        var existing = cArg.text;
-
-        // Normally the cursor being just before whitespace means that you are
-        // 'in' the previous argument, which means that the prediction is based
-        // on that argument, however NamedArguments break this by having 2 parts
-        // so we need to prepend the tabText with a space for NamedArguments,
-        // but only when there isn't already a space at the end of the prefix
-        // (i.e. ' --name' not ' --name ')
-        if (current.isInName()) {
-          tabText = ' ' + tabText;
-        }
-
-        if (existing !== tabText) {
-          // Decide to use directTabText or arrowTabText
-          // Strip any leading whitespace from the user inputted value because
-          // the tabText will never have leading whitespace.
-          var inputValue = existing.replace(/^\s*/, '');
-          var isStrictCompletion = tabText.indexOf(inputValue) === 0;
-          if (isStrictCompletion && start === typed.length) {
-            // Display the suffix of the prediction as the completion
-            var numLeadingSpaces = existing.match(/^(\s*)/)[0].length;
-
-            directTabText = tabText.slice(existing.length - numLeadingSpaces);
-          }
-          else {
-            // Display the '-> prediction' at the end of the completer element
-            // \u21E5 is the JS escape right arrow
-            arrowTabText = '\u21E5 ' + tabText;
-          }
-        }
-      }
-      else {
-        // There's no prediction, but if this is a named argument that needs a
-        // value (that is without any) then we need to show that one is needed
-        // For example 'git commit --message ', clearly needs some more text
-        if (cArg.type === 'NamedArgument' && cArg.valueArg == null) {
-          emptyParameters.push('<' + current.param.type.name + '>\u00a0');
-        }
-      }
-    }
-
-    // Add a space between the typed text (+ directTabText) and the hints,
-    // making sure we don't add 2 sets of padding
-    if (directTabText !== '') {
-      directTabText += '\u00a0'; // a.k.a &nbsp;
-    }
-    else if (!this.typedEndsWithSeparator()) {
-      emptyParameters.unshift('\u00a0');
-    }
-
-    // Calculate the list of parameters to be filled in
-    // We generate an array of emptyParameter markers for each positional
-    // parameter to the current command.
-    // Generally each emptyParameter marker begins with a space to separate it
-    // from whatever came before, unless what comes before ends in a space.
-
-    this.getAssignments().forEach(assignment => {
-      // Named arguments are handled with a group [options] marker
-      if (!assignment.param.isPositionalAllowed) {
-        return;
-      }
-
-      // No hints if we've got content for this parameter
-      if (assignment.arg.toString().trim() !== '') {
-        return;
-      }
-
-      // No hints if we have a prediction
-      if (directTabText !== '' && current === assignment) {
-        return;
-      }
-
-      var text = (assignment.param.isDataRequired) ?
-          '<' + assignment.param.name + '>\u00a0' :
-          '[' + assignment.param.name + ']\u00a0';
-
-      emptyParameters.push(text);
-    });
-
-    var command = this.commandAssignment.value;
-    var addOptionsMarker = false;
-
-    // We add an '[options]' marker when there are named parameters that are
-    // not filled in and not hidden, and we don't have any directTabText
-    if (command && command.hasNamedParameters) {
-      command.params.forEach(function(param) {
-        var arg = this.getAssignment(param.name).arg;
-        if (!param.isPositionalAllowed && !param.hidden
-                && arg.type === 'BlankArgument') {
-          addOptionsMarker = true;
-        }
-      }, this);
-    }
-
-    if (addOptionsMarker) {
-      // Add an nbsp if we don't have one at the end of the input or if
-      // this isn't the first param we've mentioned
-      emptyParameters.push('[options]\u00a0');
-    }
-
-    // Is the entered command a JS command with no closing '}'?
-    var unclosedJs = command && command.name === '{' &&
-                     !this.getAssignment(0).arg.suffix.includes('}');
-
-    return {
-      statusMarkup: this.getInputStatusMarkup(start),
-      unclosedJs: unclosedJs,
-      directTabText: directTabText,
-      arrowTabText: arrowTabText,
-      emptyParameters: emptyParameters
-    };
-  });
-};
-
-/**
- * Pressing TAB sometimes requires that we add a space to denote that we're on
- * to the 'next thing'.
- * @param assignment The assignment to which to append the space
- */
-Requisition.prototype._addSpace = function(assignment) {
-  var arg = assignment.arg.beget({ suffixSpace: true });
-  if (arg !== assignment.arg) {
-    return this.setAssignment(assignment, arg);
-  }
-  else {
-    return Promise.resolve(undefined);
-  }
-};
-
-/**
- * Complete the argument at <tt>cursor</tt>.
- * Basically the same as:
- *   assignment = getAssignmentAt(cursor);
- *   assignment.value = assignment.conversion.predictions[0];
- * Except it's done safely, and with particular care to where we place the
- * space, which is complex, and annoying if we get it wrong.
- *
- * WARNING: complete() can happen asynchronously.
- *
- * @param cursor The cursor configuration. Should have start and end properties
- * which should be set to start and end of the selection.
- * @param rank The index of the prediction that we should choose.
- * This number is not bounded by the size of the prediction array, we take the
- * modulus to get it within bounds
- * @return A promise which completes (with undefined) when any outstanding
- * completion tasks are done.
- */
-Requisition.prototype.complete = function(cursor, rank) {
-  var assignment = this.getAssignmentAt(cursor.start);
-
-  var context = this.executionContext;
-  var predictionPromise = assignment.getPredictionRanked(context, rank);
-  return predictionPromise.then(prediction => {
-    var outstanding = [];
-
-    // Note: Since complete is asynchronous we should perhaps have a system to
-    // bail out of making changes if the command line has changed since TAB
-    // was pressed. It's not yet clear if this will be a problem.
-
-    if (prediction == null) {
-      // No predictions generally means we shouldn't change anything on TAB,
-      // but TAB has the connotation of 'next thing' and when we're at the end
-      // of a thing that implies that we should add a space. i.e.
-      // 'help<TAB>' -> 'help '
-      // But we should only do this if the thing that we're 'completing' is
-      // valid and doesn't already end in a space.
-      if (assignment.arg.suffix.slice(-1) !== ' ' &&
-              assignment.getStatus() === Status.VALID) {
-        outstanding.push(this._addSpace(assignment));
-      }
-
-      // Also add a space if we are in the name part of an assignment, however
-      // this time we don't want the 'push the space to the next assignment'
-      // logic, so we don't use addSpace
-      if (assignment.isInName()) {
-        var newArg = assignment.arg.beget({ prefixPostSpace: true });
-        outstanding.push(this.setAssignment(assignment, newArg));
-      }
-    }
-    else {
-      // Mutate this argument to hold the completion
-      var arg = assignment.arg.beget({
-        text: prediction.name,
-        dontQuote: (assignment === this.commandAssignment)
-      });
-      var assignPromise = this.setAssignment(assignment, arg);
-
-      if (!prediction.incomplete) {
-        assignPromise = assignPromise.then(() => {
-          // The prediction is complete, add a space to let the user move-on
-          return this._addSpace(assignment).then(() => {
-            // Bug 779443 - Remove or explain the re-parse
-            if (assignment instanceof UnassignedAssignment) {
-              return this.update(this.toString());
-            }
-          });
-        });
-      }
-
-      outstanding.push(assignPromise);
-    }
-
-    return Promise.all(outstanding).then(() => {
-      return true;
-    });
-  });
-};
-
-/**
- * Replace the current value with the lower value if such a concept exists.
- */
-Requisition.prototype.nudge = function(assignment, by) {
-  var ctx = this.executionContext;
-  var val = assignment.param.type.nudge(assignment.value, by, ctx);
-  return Promise.resolve(val).then(replacement => {
-    if (replacement != null) {
-      var val = assignment.param.type.stringify(replacement, ctx);
-      return Promise.resolve(val).then(str => {
-        var arg = assignment.arg.beget({ text: str });
-        return this.setAssignment(assignment, arg);
-      });
-    }
-  });
-};
-
-/**
- * Helper to find the 'data-command' attribute, used by |update()|
- */
-function getDataCommandAttribute(element) {
-  var command = element.getAttribute('data-command');
-  if (!command) {
-    command = element.querySelector('*[data-command]')
-                     .getAttribute('data-command');
-  }
-  return command;
-}
-
-/**
- * Designed to be called from context.update(). Acts just like update() except
- * that it also calls onExternalUpdate() to inform the UI of an unexpected
- * change to the current command.
- */
-Requisition.prototype._contextUpdate = function(typed) {
-  return this.update(typed).then(reply => {
-    this.onExternalUpdate({ typed: typed });
-    return reply;
-  });
-};
-
-/**
- * Called by the UI when ever the user interacts with a command line input
- * @param typed The contents of the input field OR an HTML element (or an event
- * that targets an HTML element) which has a data-command attribute or a child
- * with the same that contains the command to update with
- */
-Requisition.prototype.update = function(typed) {
-  // Should be "if (typed instanceof HTMLElement)" except Gecko
-  if (typeof typed.querySelector === 'function') {
-    typed = getDataCommandAttribute(typed);
-  }
-  // Should be "if (typed instanceof Event)" except Gecko
-  if (typeof typed.currentTarget === 'object') {
-    typed = getDataCommandAttribute(typed.currentTarget);
-  }
-
-  var updateId = this._beginChange();
-
-  this._args = exports.tokenize(typed);
-  var args = this._args.slice(0); // i.e. clone
-
-  this._split(args);
-
-  return this._assign(args).then(() => {
-    return this._endChangeCheckOrder(updateId);
-  });
-};
-
-/**
- * Similar to update('') except that it's guaranteed to execute synchronously
- */
-Requisition.prototype.clear = function() {
-  var arg = new Argument('', '', '');
-  this._args = [ arg ];
-
-  var conversion = commandModule.parse(this.executionContext, arg, false);
-  this.setAssignment(this.commandAssignment, conversion, { internal: true });
-};
-
-/**
- * tokenize() is a state machine. These are the states.
- */
-var In = {
-  /**
-   * The last character was ' '.
-   * Typing a ' ' character will not change the mode
-   * Typing one of '"{ will change mode to SINGLE_Q, DOUBLE_Q or SCRIPT.
-   * Anything else goes into SIMPLE mode.
-   */
-  WHITESPACE: 1,
-
-  /**
-   * The last character was part of a parameter.
-   * Typing ' ' returns to WHITESPACE mode. Any other character
-   * (including '"{} which are otherwise special) does not change the mode.
-   */
-  SIMPLE: 2,
-
-  /**
-   * We're inside single quotes: '
-   * Typing ' returns to WHITESPACE mode. Other characters do not change mode.
-   */
-  SINGLE_Q: 3,
-
-  /**
-   * We're inside double quotes: "
-   * Typing " returns to WHITESPACE mode. Other characters do not change mode.
-   */
-  DOUBLE_Q: 4,
-
-  /**
-   * We're inside { and }
-   * Typing } returns to WHITESPACE mode. Other characters do not change mode.
-   * SCRIPT mode is slightly different from other modes in that spaces between
-   * the {/} delimiters and the actual input are not considered significant.
-   * e.g: " x " is a 3 character string, delimited by double quotes, however
-   * { x } is a 1 character JavaScript surrounded by whitespace and {}
-   * delimiters.
-   * In the short term we assume that the JS routines can make sense of the
-   * extra whitespace, however at some stage we may need to move the space into
-   * the Argument prefix/suffix.
-   * Also we don't attempt to handle nested {}. See bug 678961
-   */
-  SCRIPT: 5
-};
-
-/**
- * Split up the input taking into account ', " and {.
- * We don't consider \t or other classical whitespace characters to split
- * arguments apart. For one thing these characters are hard to type, but also
- * if the user has gone to the trouble of pasting a TAB character into the
- * input field (or whatever it takes), they probably mean it.
- */
-exports.tokenize = function(typed) {
-  // For blank input, place a dummy empty argument into the list
-  if (typed == null || typed.length === 0) {
-    return [ new Argument('', '', '') ];
-  }
-
-  if (isSimple(typed)) {
-    return [ new Argument(typed, '', '') ];
-  }
-
-  var mode = In.WHITESPACE;
-
-  // First we swap out escaped characters that are special to the tokenizer.
-  // So a backslash followed by any of ['"{} ] is turned into a unicode private
-  // char so we can swap back later
-  typed = typed
-      .replace(/\\\\/g, '\uF000')
-      .replace(/\\ /g, '\uF001')
-      .replace(/\\'/g, '\uF002')
-      .replace(/\\"/g, '\uF003')
-      .replace(/\\{/g, '\uF004')
-      .replace(/\\}/g, '\uF005');
-
-  function unescape2(escaped) {
-    return escaped
-        .replace(/\uF000/g, '\\\\')
-        .replace(/\uF001/g, '\\ ')
-        .replace(/\uF002/g, '\\\'')
-        .replace(/\uF003/g, '\\\"')
-        .replace(/\uF004/g, '\\{')
-        .replace(/\uF005/g, '\\}');
-  }
-
-  var i = 0;          // The index of the current character
-  var start = 0;      // Where did this section start?
-  var prefix = '';    // Stuff that comes before the current argument
-  var args = [];      // The array that we're creating
-  var blockDepth = 0; // For JS with nested {}
-
-  // This is just a state machine. We're going through the string char by char
-  // The 'mode' is one of the 'In' states. As we go, we're adding Arguments
-  // to the 'args' array.
-
-  while (true) {
-    var c = typed[i];
-    var str;
-    switch (mode) {
-      case In.WHITESPACE:
-        if (c === '\'') {
-          prefix = typed.substring(start, i + 1);
-          mode = In.SINGLE_Q;
-          start = i + 1;
-        }
-        else if (c === '"') {
-          prefix = typed.substring(start, i + 1);
-          mode = In.DOUBLE_Q;
-          start = i + 1;
-        }
-        else if (c === '{') {
-          prefix = typed.substring(start, i + 1);
-          mode = In.SCRIPT;
-          blockDepth++;
-          start = i + 1;
-        }
-        else if (/ /.test(c)) {
-          // Still whitespace, do nothing
-        }
-        else {
-          prefix = typed.substring(start, i);
-          mode = In.SIMPLE;
-          start = i;
-        }
-        break;
-
-      case In.SIMPLE:
-        // There is an edge case of xx'xx which we are assuming to
-        // be a single parameter (and same with ")
-        if (c === ' ') {
-          str = unescape2(typed.substring(start, i));
-          args.push(new Argument(str, prefix, ''));
-          mode = In.WHITESPACE;
-          start = i;
-          prefix = '';
-        }
-        break;
-
-      case In.SINGLE_Q:
-        if (c === '\'') {
-          str = unescape2(typed.substring(start, i));
-          args.push(new Argument(str, prefix, c));
-          mode = In.WHITESPACE;
-          start = i + 1;
-          prefix = '';
-        }
-        break;
-
-      case In.DOUBLE_Q:
-        if (c === '"') {
-          str = unescape2(typed.substring(start, i));
-          args.push(new Argument(str, prefix, c));
-          mode = In.WHITESPACE;
-          start = i + 1;
-          prefix = '';
-        }
-        break;
-
-      case In.SCRIPT:
-        if (c === '{') {
-          blockDepth++;
-        }
-        else if (c === '}') {
-          blockDepth--;
-          if (blockDepth === 0) {
-            str = unescape2(typed.substring(start, i));
-            args.push(new ScriptArgument(str, prefix, c));
-            mode = In.WHITESPACE;
-            start = i + 1;
-            prefix = '';
-          }
-        }
-        break;
-    }
-
-    i++;
-
-    if (i >= typed.length) {
-      // There is nothing else to read - tidy up
-      if (mode === In.WHITESPACE) {
-        if (i !== start) {
-          // There's whitespace at the end of the typed string. Add it to the
-          // last argument's suffix, creating an empty argument if needed.
-          var extra = typed.substring(start, i);
-          var lastArg = args[args.length - 1];
-          if (!lastArg) {
-            args.push(new Argument('', extra, ''));
-          }
-          else {
-            lastArg.suffix += extra;
-          }
-        }
-      }
-      else if (mode === In.SCRIPT) {
-        str = unescape2(typed.substring(start, i + 1));
-        args.push(new ScriptArgument(str, prefix, ''));
-      }
-      else {
-        str = unescape2(typed.substring(start, i + 1));
-        args.push(new Argument(str, prefix, ''));
-      }
-      break;
-    }
-  }
-
-  return args;
-};
-
-/**
- * If the input has no spaces, quotes, braces or escapes,
- * we can take the fast track.
- */
-function isSimple(typed) {
-   for (var i = 0; i < typed.length; i++) {
-     var c = typed.charAt(i);
-     if (c === ' ' || c === '"' || c === '\'' ||
-         c === '{' || c === '}' || c === '\\') {
-       return false;
-     }
-   }
-   return true;
-}
-
-/**
- * Looks in the commands for a command extension that matches what has been
- * typed at the command line.
- */
-Requisition.prototype._split = function(args) {
-  // Handle the special case of the user typing { javascript(); }
-  // We use the hidden 'eval' command directly rather than shift()ing one of
-  // the parameters, and parse()ing it.
-  var conversion;
-  if (args[0].type === 'ScriptArgument') {
-    // Special case: if the user enters { console.log('foo'); } then we need to
-    // use the hidden 'eval' command
-    var command = this.system.commands.get(evalCmd.name);
-    conversion = new Conversion(command, new ScriptArgument());
-    this._setAssignmentInternal(this.commandAssignment, conversion);
-    return;
-  }
-
-  var argsUsed = 1;
-
-  while (argsUsed <= args.length) {
-    var arg = (argsUsed === 1) ?
-              args[0] :
-              new MergedArgument(args, 0, argsUsed);
-
-    if (this.prefix != null && this.prefix !== '') {
-      var prefixArg = new Argument(this.prefix, '', ' ');
-      var prefixedArg = new MergedArgument([ prefixArg, arg ]);
-
-      conversion = commandModule.parse(this.executionContext, prefixedArg, false);
-      if (conversion.value == null) {
-        conversion = commandModule.parse(this.executionContext, arg, false);
-      }
-    }
-    else {
-      conversion = commandModule.parse(this.executionContext, arg, false);
-    }
-
-    // We only want to carry on if this command is a parent command,
-    // which means that there is a commandAssignment, but not one with
-    // an exec function.
-    if (!conversion.value || conversion.value.exec) {
-      break;
-    }
-
-    // Previously we needed a way to hide commands depending context.
-    // We have not resurrected that feature yet, but if we do we should
-    // insert code here to ignore certain commands depending on the
-    // context/environment
-
-    argsUsed++;
-  }
-
-  // This could probably be re-written to consume args as we go
-  for (var i = 0; i < argsUsed; i++) {
-    args.shift();
-  }
-
-  this._setAssignmentInternal(this.commandAssignment, conversion);
-};
-
-/**
- * Add all the passed args to the list of unassigned assignments.
- */
-Requisition.prototype._addUnassignedArgs = function(args) {
-  args.forEach(arg => {
-    this._unassigned.push(new UnassignedAssignment(this, arg));
-  });
-
-  return RESOLVED;
-};
-
-/**
- * Work out which arguments are applicable to which parameters.
- */
-Requisition.prototype._assign = function(args) {
-  // See comment in _split. Avoid multiple updates
-  var noArgUp = { internal: true };
-
-  this._unassigned = [];
-
-  if (!this.commandAssignment.value) {
-    return this._addUnassignedArgs(args);
-  }
-
-  if (args.length === 0) {
-    this._setBlankArguments();
-    return RESOLVED;
-  }
-
-  // Create an error if the command does not take parameters, but we have
-  // been given them ...
-  if (this.assignmentCount === 0) {
-    return this._addUnassignedArgs(args);
-  }
-
-  // Special case: if there is only 1 parameter, and that's of type
-  // text, then we put all the params into the first param
-  if (this.assignmentCount === 1) {
-    var assignment = this.getAssignment(0);
-    if (assignment.param.type.name === 'string') {
-      var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
-      return this.setAssignment(assignment, arg, noArgUp);
-    }
-  }
-
-  // Positional arguments can still be specified by name, but if they are
-  // then we need to ignore them when working them out positionally
-  var unassignedParams = this.getParameterNames();
-
-  // We collect the arguments used in arrays here before assigning
-  var arrayArgs = {};
-
-  // Extract all the named parameters
-  var assignments = this.getAssignments(false);
-  var namedDone = util.promiseEach(assignments, function(assignment) {
-    // Loop over the arguments
-    // Using while rather than loop because we remove args as we go
-    var i = 0;
-    while (i < args.length) {
-      if (!assignment.param.isKnownAs(args[i].text)) {
-        // Skip this parameter and handle as a positional parameter
-        i++;
-        continue;
-      }
-
-      var arg = args.splice(i, 1)[0];
-      /* jshint loopfunc:true */
-      unassignedParams = unassignedParams.filter(function(test) {
-        return test !== assignment.param.name;
-      });
-
-      // boolean parameters don't have values, default to false
-      if (assignment.param.type.name === 'boolean') {
-        arg = new TrueNamedArgument(arg);
-      }
-      else {
-        var valueArg = null;
-        if (i + 1 <= args.length) {
-          valueArg = args.splice(i, 1)[0];
-        }
-        arg = new NamedArgument(arg, valueArg);
-      }
-
-      if (assignment.param.type.name === 'array') {
-        var arrayArg = arrayArgs[assignment.param.name];
-        if (!arrayArg) {
-          arrayArg = new ArrayArgument();
-          arrayArgs[assignment.param.name] = arrayArg;
-        }
-        arrayArg.addArgument(arg);
-        return RESOLVED;
-      }
-      else {
-        if (assignment.arg.type === 'BlankArgument') {
-          return this.setAssignment(assignment, arg, noArgUp);
-        }
-        else {
-          return this._addUnassignedArgs(arg.getArgs());
-        }
-      }
-    }
-  }, this);
-
-  // What's left are positional parameters: assign in order
-  var positionalDone = namedDone.then(() => {
-    return util.promiseEach(unassignedParams, function(name) {
-      var assignment = this.getAssignment(name);
-
-      // If not set positionally, and we can't set it non-positionally,
-      // we have to default it to prevent previous values surviving
-      if (!assignment.param.isPositionalAllowed) {
-        this._setBlankAssignment(assignment);
-        return RESOLVED;
-      }
-
-      // If this is a positional array argument, then it swallows the
-      // rest of the arguments.
-      if (assignment.param.type.name === 'array') {
-        var arrayArg = arrayArgs[assignment.param.name];
-        if (!arrayArg) {
-          arrayArg = new ArrayArgument();
-          arrayArgs[assignment.param.name] = arrayArg;
-        }
-        arrayArg.addArguments(args);
-        args = [];
-        // The actual assignment to the array parameter is done below
-        return RESOLVED;
-      }
-
-      // Set assignment to defaults if there are no more arguments
-      if (args.length === 0) {
-        this._setBlankAssignment(assignment);
-        return RESOLVED;
-      }
-
-      var arg = args.splice(0, 1)[0];
-      // --foo and -f are named parameters, -4 is a number. So '-' is either
-      // the start of a named parameter or a number depending on the context
-      var isIncompleteName = assignment.param.type.name === 'number' ?
-          /-[-a-zA-Z_]/.test(arg.text) :
-          arg.text.charAt(0) === '-';
-
-      if (isIncompleteName) {
-        this._unassigned.push(new UnassignedAssignment(this, arg));
-        return RESOLVED;
-      }
-      else {
-        return this.setAssignment(assignment, arg, noArgUp);
-      }
-    }, this);
-  });
-
-  // Now we need to assign the array argument (if any)
-  var arrayDone = positionalDone.then(() => {
-    return util.promiseEach(Object.keys(arrayArgs), function(name) {
-      var assignment = this.getAssignment(name);
-      return this.setAssignment(assignment, arrayArgs[name], noArgUp);
-    }, this);
-  });
-
-  // What's left is can't be assigned, but we need to officially unassign them
-  return arrayDone.then(() => {
-    return this._addUnassignedArgs(args);
-  });
-};
-
-/**
- * Entry point for keyboard accelerators or anything else that wants to execute
- * a command.
- * @param options Object describing how the execution should be handled.
- * (optional). Contains some of the following properties:
- * - hidden (boolean, default=false) Should the output be hidden from the
- *   commandOutputManager for this requisition
- * - command/args A fast shortcut to executing a known command with a known
- *   set of parsed arguments.
- */
-Requisition.prototype.exec = function(options) {
-  var command = null;
-  var args = null;
-  var hidden = false;
-
-  if (options) {
-    if (options.hidden) {
-      hidden = true;
-    }
-
-    if (options.command != null) {
-      // Fast track by looking up the command directly since passed args
-      // means there is no command line to parse.
-      command = this.system.commands.get(options.command);
-      if (!command) {
-        console.error('Command not found: ' + options.command);
-      }
-      args = options.args;
-    }
-  }
-