Bug 1217591 - Make JS autocompletion in the console work inside of a worker toolbox;r=fitzgen
authorBrian Grinstead <bgrinstead@mozilla.com>
Mon, 26 Oct 2015 11:55:40 -0700
changeset 269482 15acf2adefd0b290084399db1f4b2256cedcae62
parent 269481 81d08a1039202a9966009ea282dea10a69bf68d2
child 269483 5b9e910d10b36246bea85e61ff43989ecdd7775c
push id15886
push userbgrinstead@mozilla.com
push dateMon, 26 Oct 2015 18:56:21 +0000
treeherderfx-team@5b9e910d10b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs1217591
milestone44.0a1
Bug 1217591 - Make JS autocompletion in the console work inside of a worker toolbox;r=fitzgen
devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
devtools/client/webconsole/test/browser_webconsole_property_provider.js
devtools/server/actors/webconsole.js
devtools/shared/webconsole/js-property-provider.js
devtools/shared/webconsole/moz.build
devtools/shared/webconsole/test/test_jsterm.html
devtools/shared/webconsole/test/unit/test_js_property_provider.js
devtools/shared/webconsole/utils.js
devtools/shared/webconsole/worker-utils.js
--- a/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
@@ -12,17 +12,17 @@ function test() {
   requestLongerTimeout(6);
 
   loadTab(TEST_URI).then(() => {
     openConsole().then(consoleOpened);
   });
 }
 
 function consoleOpened(HUD) {
-  let {JSPropertyProvider} = require("devtools/shared/webconsole/utils");
+  let {JSPropertyProvider} = require("devtools/shared/webconsole/js-property-provider");
 
   let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
   tmp.addDebuggerToGlobal(tmp);
   let dbg = new tmp.Debugger();
 
   let jsterm = HUD.jsterm;
   let win = content.wrappedJSObject;
   let dbgWindow = dbg.makeGlobalObjectReference(win);
--- a/devtools/client/webconsole/test/browser_webconsole_property_provider.js
+++ b/devtools/client/webconsole/test/browser_webconsole_property_provider.js
@@ -11,17 +11,17 @@
 const TEST_URI = "data:text/html;charset=utf8,<p>test the JS property provider";
 
 function test() {
   loadTab(TEST_URI).then(testPropertyProvider);
 }
 
 function testPropertyProvider({browser}) {
   browser.removeEventListener("load", testPropertyProvider, true);
-  let {JSPropertyProvider} = require("devtools/shared/webconsole/utils");
+  let {JSPropertyProvider} = require("devtools/shared/webconsole/js-property-provider");
 
   let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
   tmp.addDebuggerToGlobal(tmp);
   let dbg = new tmp.Debugger();
   let dbgWindow = dbg.makeGlobalObjectReference(content);
 
   let completion = JSPropertyProvider(dbgWindow, null, "thisIsNotDefined");
   is(completion.matches.length, 0, "no match for 'thisIsNotDefined");
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -13,19 +13,20 @@ const { EnvironmentActor, ThreadActor } 
 const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 loader.lazyRequireGetter(this, "NetworkMonitor", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 loader.lazyRequireGetter(this, "ServerLoggingListener", "devtools/shared/webconsole/server-logger", true);
+loader.lazyRequireGetter(this, "JSPropertyProvider", "devtools/shared/webconsole/js-property-provider", true);
 
 for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
-    "ConsoleAPIListener", "addWebConsoleCommands", "JSPropertyProvider",
+    "ConsoleAPIListener", "addWebConsoleCommands",
     "ConsoleReflowListener", "CONSOLE_WORKER_IDS"]) {
   Object.defineProperty(this, name, {
     get: function(prop) {
       if (prop == "WebConsoleUtils") {
         prop = "Utils";
       }
       if (isWorker) {
         return require("devtools/shared/webconsole/worker-utils")[prop];
copy from devtools/shared/webconsole/utils.js
copy to devtools/shared/webconsole/js-property-provider.js
--- a/devtools/shared/webconsole/utils.js
+++ b/devtools/shared/webconsole/js-property-provider.js
@@ -2,705 +2,29 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, Cu, components} = require("chrome");
-const {isWindowIncluded} = require("devtools/shared/layout/utils");
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
-loader.lazyImporter(this, "Parser", "resource://devtools/shared/Parser.jsm");
-
-// TODO: Bug 842672 - browser/ imports modules from toolkit/.
-// Note that these are only used in WebConsoleCommands, see $0 and pprint().
-loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
-// Match the function name from the result of toString() or toSource().
-//
-// Examples:
-// (function foobar(a, b) { ...
-// function foobar2(a) { ...
-// function() { ...
-const REGEX_MATCH_FUNCTION_NAME = /^\(?function\s+([^(\s]+)\s*\(/;
-
-// Match the function arguments from the result of toString() or toSource().
-const REGEX_MATCH_FUNCTION_ARGS = /^\(?function\s*[^\s(]*\s*\((.+?)\)/;
-
-// Number of terminal entries for the self-xss prevention to go away
-const CONSOLE_ENTRY_THRESHOLD = 5;
+if (!isWorker) {
+  loader.lazyImporter(this, "Parser", "resource://devtools/shared/Parser.jsm");
+}
 
 // Provide an easy way to bail out of even attempting an autocompletion
 // if an object has way too many properties. Protects against large objects
 // with numeric values that wouldn't be tallied towards MAX_AUTOCOMPLETIONS.
 const MAX_AUTOCOMPLETE_ATTEMPTS = exports.MAX_AUTOCOMPLETE_ATTEMPTS = 100000;
-
-const CONSOLE_WORKER_IDS = exports.CONSOLE_WORKER_IDS = [ 'SharedWorker', 'ServiceWorker', 'Worker' ];
-
 // Prevent iterating over too many properties during autocomplete suggestions.
 const MAX_AUTOCOMPLETIONS = exports.MAX_AUTOCOMPLETIONS = 1500;
 
-var WebConsoleUtils = {
-
-  /**
-   * Wrap a string in an nsISupportsString object.
-   *
-   * @param string aString
-   * @return nsISupportsString
-   */
-  supportsString: function WCU_supportsString(aString)
-  {
-    let str = Cc["@mozilla.org/supports-string;1"].
-              createInstance(Ci.nsISupportsString);
-    str.data = aString;
-    return str;
-  },
-
-  /**
-   * Clone an object.
-   *
-   * @param object aObject
-   *        The object you want cloned.
-   * @param boolean aRecursive
-   *        Tells if you want to dig deeper into the object, to clone
-   *        recursively.
-   * @param function [aFilter]
-   *        Optional, filter function, called for every property. Three
-   *        arguments are passed: key, value and object. Return true if the
-   *        property should be added to the cloned object. Return false to skip
-   *        the property.
-   * @return object
-   *         The cloned object.
-   */
-  cloneObject: function WCU_cloneObject(aObject, aRecursive, aFilter)
-  {
-    if (typeof aObject != "object") {
-      return aObject;
-    }
-
-    let temp;
-
-    if (Array.isArray(aObject)) {
-      temp = [];
-      Array.forEach(aObject, function(aValue, aIndex) {
-        if (!aFilter || aFilter(aIndex, aValue, aObject)) {
-          temp.push(aRecursive ? WCU_cloneObject(aValue) : aValue);
-        }
-      });
-    }
-    else {
-      temp = {};
-      for (let key in aObject) {
-        let value = aObject[key];
-        if (aObject.hasOwnProperty(key) &&
-            (!aFilter || aFilter(key, value, aObject))) {
-          temp[key] = aRecursive ? WCU_cloneObject(value) : value;
-        }
-      }
-    }
-
-    return temp;
-  },
-
-  /**
-   * Copies certain style attributes from one element to another.
-   *
-   * @param nsIDOMNode aFrom
-   *        The target node.
-   * @param nsIDOMNode aTo
-   *        The destination node.
-   */
-  copyTextStyles: function WCU_copyTextStyles(aFrom, aTo)
-  {
-    let win = aFrom.ownerDocument.defaultView;
-    let style = win.getComputedStyle(aFrom);
-    aTo.style.fontFamily = style.getPropertyCSSValue("font-family").cssText;
-    aTo.style.fontSize = style.getPropertyCSSValue("font-size").cssText;
-    aTo.style.fontWeight = style.getPropertyCSSValue("font-weight").cssText;
-    aTo.style.fontStyle = style.getPropertyCSSValue("font-style").cssText;
-  },
-
-  /**
-   * Gets the ID of the inner window of this DOM window.
-   *
-   * @param nsIDOMWindow aWindow
-   * @return integer
-   *         Inner ID for the given aWindow.
-   */
-  getInnerWindowId: function WCU_getInnerWindowId(aWindow)
-  {
-    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-             getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
-  },
-
-  /**
-   * Recursively gather a list of inner window ids given a
-   * top level window.
-   *
-   * @param nsIDOMWindow aWindow
-   * @return Array
-   *         list of inner window ids.
-   */
-  getInnerWindowIDsForFrames: function WCU_getInnerWindowIDsForFrames(aWindow)
-  {
-    let innerWindowID = this.getInnerWindowId(aWindow);
-    let ids = [innerWindowID];
-
-    if (aWindow.frames) {
-      for (let i = 0; i < aWindow.frames.length; i++) {
-        let frame = aWindow.frames[i];
-        ids = ids.concat(this.getInnerWindowIDsForFrames(frame));
-      }
-    }
-
-    return ids;
-  },
-
-
-  /**
-   * Gets the ID of the outer window of this DOM window.
-   *
-   * @param nsIDOMWindow aWindow
-   * @return integer
-   *         Outer ID for the given aWindow.
-   */
-  getOuterWindowId: function WCU_getOuterWindowId(aWindow)
-  {
-    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-           getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
-  },
-
-  /**
-   * Abbreviates the given source URL so that it can be displayed flush-right
-   * without being too distracting.
-   *
-   * @param string aSourceURL
-   *        The source URL to shorten.
-   * @param object [aOptions]
-   *        Options:
-   *        - onlyCropQuery: boolean that tells if the URL abbreviation function
-   *        should only remove the query parameters and the hash fragment from
-   *        the given URL.
-   * @return string
-   *         The abbreviated form of the source URL.
-   */
-  abbreviateSourceURL:
-  function WCU_abbreviateSourceURL(aSourceURL, aOptions = {})
-  {
-    if (!aOptions.onlyCropQuery && aSourceURL.substr(0, 5) == "data:") {
-      let commaIndex = aSourceURL.indexOf(",");
-      if (commaIndex > -1) {
-        aSourceURL = "data:" + aSourceURL.substring(commaIndex + 1);
-      }
-    }
-
-    // Remove any query parameters.
-    let hookIndex = aSourceURL.indexOf("?");
-    if (hookIndex > -1) {
-      aSourceURL = aSourceURL.substring(0, hookIndex);
-    }
-
-    // Remove any hash fragments.
-    let hashIndex = aSourceURL.indexOf("#");
-    if (hashIndex > -1) {
-      aSourceURL = aSourceURL.substring(0, hashIndex);
-    }
-
-    // Remove a trailing "/".
-    if (aSourceURL[aSourceURL.length - 1] == "/") {
-      aSourceURL = aSourceURL.replace(/\/+$/, "");
-    }
-
-    // Remove all but the last path component.
-    if (!aOptions.onlyCropQuery) {
-      let slashIndex = aSourceURL.lastIndexOf("/");
-      if (slashIndex > -1) {
-        aSourceURL = aSourceURL.substring(slashIndex + 1);
-      }
-    }
-
-    return aSourceURL;
-  },
-
-  /**
-   * Tells if the given function is native or not.
-   *
-   * @param function aFunction
-   *        The function you want to check if it is native or not.
-   * @return boolean
-   *         True if the given function is native, false otherwise.
-   */
-  isNativeFunction: function WCU_isNativeFunction(aFunction)
-  {
-    return typeof aFunction == "function" && !("prototype" in aFunction);
-  },
-
-  /**
-   * Tells if the given property of the provided object is a non-native getter or
-   * not.
-   *
-   * @param object aObject
-   *        The object that contains the property.
-   * @param string aProp
-   *        The property you want to check if it is a getter or not.
-   * @return boolean
-   *         True if the given property is a getter, false otherwise.
-   */
-  isNonNativeGetter: function WCU_isNonNativeGetter(aObject, aProp)
-  {
-    if (typeof aObject != "object") {
-      return false;
-    }
-    let desc = this.getPropertyDescriptor(aObject, aProp);
-    return desc && desc.get && !this.isNativeFunction(desc.get);
-  },
-
-  /**
-   * Get the property descriptor for the given object.
-   *
-   * @param object aObject
-   *        The object that contains the property.
-   * @param string aProp
-   *        The property you want to get the descriptor for.
-   * @return object
-   *         Property descriptor.
-   */
-  getPropertyDescriptor: function WCU_getPropertyDescriptor(aObject, aProp)
-  {
-    let desc = null;
-    while (aObject) {
-      try {
-        if ((desc = Object.getOwnPropertyDescriptor(aObject, aProp))) {
-          break;
-        }
-      } catch (ex) {
-        // Native getters throw here. See bug 520882.
-        // null throws TypeError.
-        if (ex.name != "NS_ERROR_XPC_BAD_CONVERT_JS" &&
-            ex.name != "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO" &&
-            ex.name != "TypeError") {
-          throw ex;
-        }
-      }
-
-      try {
-        aObject = Object.getPrototypeOf(aObject);
-      } catch (ex) {
-        if (ex.name == "TypeError") {
-          return desc;
-        }
-        throw ex;
-      }
-    }
-    return desc;
-  },
-
-  /**
-   * Sort function for object properties.
-   *
-   * @param object a
-   *        Property descriptor.
-   * @param object b
-   *        Property descriptor.
-   * @return integer
-   *         -1 if a.name < b.name,
-   *         1 if a.name > b.name,
-   *         0 otherwise.
-   */
-  propertiesSort: function WCU_propertiesSort(a, b)
-  {
-    // Convert the pair.name to a number for later sorting.
-    let aNumber = parseFloat(a.name);
-    let bNumber = parseFloat(b.name);
-
-    // Sort numbers.
-    if (!isNaN(aNumber) && isNaN(bNumber)) {
-      return -1;
-    }
-    else if (isNaN(aNumber) && !isNaN(bNumber)) {
-      return 1;
-    }
-    else if (!isNaN(aNumber) && !isNaN(bNumber)) {
-      return aNumber - bNumber;
-    }
-    // Sort string.
-    else if (a.name < b.name) {
-      return -1;
-    }
-    else if (a.name > b.name) {
-      return 1;
-    }
-    else {
-      return 0;
-    }
-  },
-
-  /**
-   * Create a grip for the given value. If the value is an object,
-   * an object wrapper will be created.
-   *
-   * @param mixed aValue
-   *        The value you want to create a grip for, before sending it to the
-   *        client.
-   * @param function aObjectWrapper
-   *        If the value is an object then the aObjectWrapper function is
-   *        invoked to give us an object grip. See this.getObjectGrip().
-   * @return mixed
-   *         The value grip.
-   */
-  createValueGrip: function WCU_createValueGrip(aValue, aObjectWrapper)
-  {
-    switch (typeof aValue) {
-      case "boolean":
-        return aValue;
-      case "string":
-        return aObjectWrapper(aValue);
-      case "number":
-        if (aValue === Infinity) {
-          return { type: "Infinity" };
-        }
-        else if (aValue === -Infinity) {
-          return { type: "-Infinity" };
-        }
-        else if (Number.isNaN(aValue)) {
-          return { type: "NaN" };
-        }
-        else if (!aValue && 1 / aValue === -Infinity) {
-          return { type: "-0" };
-        }
-        return aValue;
-      case "undefined":
-        return { type: "undefined" };
-      case "object":
-        if (aValue === null) {
-          return { type: "null" };
-        }
-      case "function":
-        return aObjectWrapper(aValue);
-      default:
-        Cu.reportError("Failed to provide a grip for value of " + typeof aValue
-                       + ": " + aValue);
-        return null;
-    }
-  },
-
-  /**
-   * Check if the given object is an iterator or a generator.
-   *
-   * @param object aObject
-   *        The object you want to check.
-   * @return boolean
-   *         True if the given object is an iterator or a generator, otherwise
-   *         false is returned.
-   */
-  isIteratorOrGenerator: function WCU_isIteratorOrGenerator(aObject)
-  {
-    if (aObject === null) {
-      return false;
-    }
-
-    if (typeof aObject == "object") {
-      if (typeof aObject.__iterator__ == "function" ||
-          aObject.constructor && aObject.constructor.name == "Iterator") {
-        return true;
-      }
-
-      try {
-        let str = aObject.toString();
-        if (typeof aObject.next == "function" &&
-            str.indexOf("[object Generator") == 0) {
-          return true;
-        }
-      }
-      catch (ex) {
-        // window.history.next throws in the typeof check above.
-        return false;
-      }
-    }
-
-    return false;
-  },
-
-  /**
-   * Determine if the given request mixes HTTP with HTTPS content.
-   *
-   * @param string aRequest
-   *        Location of the requested content.
-   * @param string aLocation
-   *        Location of the current page.
-   * @return boolean
-   *         True if the content is mixed, false if not.
-   */
-  isMixedHTTPSRequest: function WCU_isMixedHTTPSRequest(aRequest, aLocation)
-  {
-    try {
-      let requestURI = Services.io.newURI(aRequest, null, null);
-      let contentURI = Services.io.newURI(aLocation, null, null);
-      return (contentURI.scheme == "https" && requestURI.scheme != "https");
-    }
-    catch (ex) {
-      return false;
-    }
-  },
-
-  /**
-   * Helper function to deduce the name of the provided function.
-   *
-   * @param funtion aFunction
-   *        The function whose name will be returned.
-   * @return string
-   *         Function name.
-   */
-  getFunctionName: function WCF_getFunctionName(aFunction)
-  {
-    let name = null;
-    if (aFunction.name) {
-      name = aFunction.name;
-    }
-    else {
-      let desc;
-      try {
-        desc = aFunction.getOwnPropertyDescriptor("displayName");
-      }
-      catch (ex) { }
-      if (desc && typeof desc.value == "string") {
-        name = desc.value;
-      }
-    }
-    if (!name) {
-      try {
-        let str = (aFunction.toString() || aFunction.toSource()) + "";
-        name = (str.match(REGEX_MATCH_FUNCTION_NAME) || [])[1];
-      }
-      catch (ex) { }
-    }
-    return name;
-  },
-
-  /**
-   * Get the object class name. For example, the |window| object has the Window
-   * class name (based on [object Window]).
-   *
-   * @param object aObject
-   *        The object you want to get the class name for.
-   * @return string
-   *         The object class name.
-   */
-  getObjectClassName: function WCU_getObjectClassName(aObject)
-  {
-    if (aObject === null) {
-      return "null";
-    }
-    if (aObject === undefined) {
-      return "undefined";
-    }
-
-    let type = typeof aObject;
-    if (type != "object") {
-      // Grip class names should start with an uppercase letter.
-      return type.charAt(0).toUpperCase() + type.substr(1);
-    }
-
-    let className;
-
-    try {
-      className = ((aObject + "").match(/^\[object (\S+)\]$/) || [])[1];
-      if (!className) {
-        className = ((aObject.constructor + "").match(/^\[object (\S+)\]$/) || [])[1];
-      }
-      if (!className && typeof aObject.constructor == "function") {
-        className = this.getFunctionName(aObject.constructor);
-      }
-    }
-    catch (ex) { }
-
-    return className;
-  },
-
-  /**
-   * Check if the given value is a grip with an actor.
-   *
-   * @param mixed aGrip
-   *        Value you want to check if it is a grip with an actor.
-   * @return boolean
-   *         True if the given value is a grip with an actor.
-   */
-  isActorGrip: function WCU_isActorGrip(aGrip)
-  {
-    return aGrip && typeof(aGrip) == "object" && aGrip.actor;
-  },
-  /**
-   * Value of devtools.selfxss.count preference
-   *
-   * @type number
-   * @private
-   */
-  _usageCount: 0,
-  get usageCount() {
-    if (WebConsoleUtils._usageCount < CONSOLE_ENTRY_THRESHOLD) {
-      WebConsoleUtils._usageCount = Services.prefs.getIntPref("devtools.selfxss.count");
-      if (Services.prefs.getBoolPref("devtools.chrome.enabled")) {
-        WebConsoleUtils.usageCount = CONSOLE_ENTRY_THRESHOLD;
-      }
-    }
-    return WebConsoleUtils._usageCount;
-  },
-  set usageCount(newUC) {
-    if (newUC <= CONSOLE_ENTRY_THRESHOLD) {
-      WebConsoleUtils._usageCount = newUC;
-      Services.prefs.setIntPref("devtools.selfxss.count", newUC);
-    }
-  },
-  /**
-   * The inputNode "paste" event handler generator. Helps prevent self-xss attacks
-   *
-   * @param nsIDOMElement inputField
-   * @param nsIDOMElement notificationBox
-   * @returns A function to be added as a handler to 'paste' and 'drop' events on the input field
-   */
-  pasteHandlerGen: function WCU_pasteHandlerGen(inputField, notificationBox, msg, okstring) {
-    let handler = function WCU_pasteHandler(aEvent) {
-      if (WebConsoleUtils.usageCount >= CONSOLE_ENTRY_THRESHOLD) {
-        inputField.removeEventListener("paste", handler);
-        inputField.removeEventListener("drop", handler);
-        return true;
-      }
-      if (notificationBox.getNotificationWithValue("selfxss-notification")) {
-        aEvent.preventDefault();
-        aEvent.stopPropagation();
-        return false;
-      }
-
-
-      let notification = notificationBox.appendNotification(msg,
-        "selfxss-notification", null, notificationBox.PRIORITY_WARNING_HIGH, null,
-        function(eventType) {
-          // Cleanup function if notification is dismissed
-          if (eventType == "removed") {
-            inputField.removeEventListener("keyup", pasteKeyUpHandler);
-          }
-        });
-
-      function pasteKeyUpHandler(aEvent2) {
-        let value = inputField.value || inputField.textContent;
-        if (value.includes(okstring)) {
-          notificationBox.removeNotification(notification);
-          inputField.removeEventListener("keyup", pasteKeyUpHandler);
-          WebConsoleUtils.usageCount = CONSOLE_ENTRY_THRESHOLD;
-        }
-      }
-      inputField.addEventListener("keyup", pasteKeyUpHandler);
-
-      aEvent.preventDefault();
-      aEvent.stopPropagation();
-      return false;
-    };
-    return handler;
-  },
-
-
-};
-
-exports.Utils = WebConsoleUtils;
-
-//////////////////////////////////////////////////////////////////////////
-// Localization
-//////////////////////////////////////////////////////////////////////////
-
-WebConsoleUtils.l10n = function WCU_l10n(aBundleURI)
-{
-  this._bundleUri = aBundleURI;
-};
-
-WebConsoleUtils.l10n.prototype = {
-  _stringBundle: null,
-
-  get stringBundle()
-  {
-    if (!this._stringBundle) {
-      this._stringBundle = Services.strings.createBundle(this._bundleUri);
-    }
-    return this._stringBundle;
-  },
-
-  /**
-   * Generates a formatted timestamp string for displaying in console messages.
-   *
-   * @param integer [aMilliseconds]
-   *        Optional, allows you to specify the timestamp in milliseconds since
-   *        the UNIX epoch.
-   * @return string
-   *         The timestamp formatted for display.
-   */
-  timestampString: function WCU_l10n_timestampString(aMilliseconds)
-  {
-    let d = new Date(aMilliseconds ? aMilliseconds : null);
-    let hours = d.getHours(), minutes = d.getMinutes();
-    let seconds = d.getSeconds(), milliseconds = d.getMilliseconds();
-    let parameters = [hours, minutes, seconds, milliseconds];
-    return this.getFormatStr("timestampFormat", parameters);
-  },
-
-  /**
-   * Retrieve a localized string.
-   *
-   * @param string aName
-   *        The string name you want from the Web Console string bundle.
-   * @return string
-   *         The localized string.
-   */
-  getStr: function WCU_l10n_getStr(aName)
-  {
-    let result;
-    try {
-      result = this.stringBundle.GetStringFromName(aName);
-    }
-    catch (ex) {
-      Cu.reportError("Failed to get string: " + aName);
-      throw ex;
-    }
-    return result;
-  },
-
-  /**
-   * Retrieve a localized string formatted with values coming from the given
-   * array.
-   *
-   * @param string aName
-   *        The string name you want from the Web Console string bundle.
-   * @param array aArray
-   *        The array of values you want in the formatted string.
-   * @return string
-   *         The formatted local string.
-   */
-  getFormatStr: function WCU_l10n_getFormatStr(aName, aArray)
-  {
-    let result;
-    try {
-      result = this.stringBundle.formatStringFromName(aName, aArray, aArray.length);
-    }
-    catch (ex) {
-      Cu.reportError("Failed to format string: " + aName);
-      throw ex;
-    }
-    return result;
-  },
-};
-
-
-//////////////////////////////////////////////////////////////////////////
-// JS Completer
-//////////////////////////////////////////////////////////////////////////
-
-(function _JSPP(WCU) {
 const STATE_NORMAL = 0;
 const STATE_QUOTE = 2;
 const STATE_DQUOTE = 3;
 
 const OPEN_BODY = "{[(".split("");
 const CLOSE_BODY = "}])".split("");
 const OPEN_CLOSE_BODY = {
   "{": "}",
@@ -866,17 +190,19 @@ function JSPropertyProvider(aDbgObject, 
 
   // Don't complete on just an empty string.
   if (completionPart.trim() == "") {
     return null;
   }
 
   // Catch literals like [1,2,3] or "foo" and return the matches from
   // their prototypes.
-  if (lastDot > 0) {
+  // Don't run this is a worker, migrating to acorn should allow this
+  // to run in a worker - Bug 1217198.
+  if (!isWorker && lastDot > 0) {
     let parser = new Parser();
     parser.logExceptions = false;
     let syntaxTree = parser.get(completionPart.slice(0, lastDot));
     let lastTree = syntaxTree.getLastSyntaxTree();
     let lastBody = lastTree && lastTree.AST.body[lastTree.AST.body.length - 1];
 
     // Finding the last expression since we've sliced up until the dot.
     // If there were parse errors this won't exist.
@@ -1189,858 +515,9 @@ var DebuggerEnvironmentSupport = {
       return null;
     }
     return { value: result };
   },
 };
 
 
 exports.JSPropertyProvider = DevToolsUtils.makeInfallible(JSPropertyProvider);
-})(WebConsoleUtils);
 
-///////////////////////////////////////////////////////////////////////////////
-// The page errors listener
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * The nsIConsoleService listener. This is used to send all of the console
- * messages (JavaScript, CSS and more) to the remote Web Console instance.
- *
- * @constructor
- * @param nsIDOMWindow [aWindow]
- *        Optional - the window object for which we are created. This is used
- *        for filtering out messages that belong to other windows.
- * @param object aListener
- *        The listener object must have one method:
- *        - onConsoleServiceMessage(). This method is invoked with one argument,
- *        the nsIConsoleMessage, whenever a relevant message is received.
- */
-function ConsoleServiceListener(aWindow, aListener)
-{
-  this.window = aWindow;
-  this.listener = aListener;
-}
-exports.ConsoleServiceListener = ConsoleServiceListener;
-
-ConsoleServiceListener.prototype =
-{
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]),
-
-  /**
-   * The content window for which we listen to page errors.
-   * @type nsIDOMWindow
-   */
-  window: null,
-
-  /**
-   * The listener object which is notified of messages from the console service.
-   * @type object
-   */
-  listener: null,
-
-  /**
-   * Initialize the nsIConsoleService listener.
-   */
-  init: function CSL_init()
-  {
-    Services.console.registerListener(this);
-  },
-
-  /**
-   * The nsIConsoleService observer. This method takes all the script error
-   * messages belonging to the current window and sends them to the remote Web
-   * Console instance.
-   *
-   * @param nsIConsoleMessage aMessage
-   *        The message object coming from the nsIConsoleService.
-   */
-  observe: function CSL_observe(aMessage)
-  {
-    if (!this.listener) {
-      return;
-    }
-
-    if (this.window) {
-      if (!(aMessage instanceof Ci.nsIScriptError) ||
-          !aMessage.outerWindowID ||
-          !this.isCategoryAllowed(aMessage.category)) {
-        return;
-      }
-
-      let errorWindow = Services.wm.getOuterWindowWithId(aMessage .outerWindowID);
-      if (!errorWindow || !isWindowIncluded(this.window, errorWindow)) {
-        return;
-      }
-    }
-
-    this.listener.onConsoleServiceMessage(aMessage);
-  },
-
-  /**
-   * Check if the given message category is allowed to be tracked or not.
-   * We ignore chrome-originating errors as we only care about content.
-   *
-   * @param string aCategory
-   *        The message category you want to check.
-   * @return boolean
-   *         True if the category is allowed to be logged, false otherwise.
-   */
-  isCategoryAllowed: function CSL_isCategoryAllowed(aCategory)
-  {
-    if (!aCategory) {
-      return false;
-    }
-
-    switch (aCategory) {
-      case "XPConnect JavaScript":
-      case "component javascript":
-      case "chrome javascript":
-      case "chrome registration":
-      case "XBL":
-      case "XBL Prototype Handler":
-      case "XBL Content Sink":
-      case "xbl javascript":
-        return false;
-    }
-
-    return true;
-  },
-
-  /**
-   * Get the cached page errors for the current inner window and its (i)frames.
-   *
-   * @param boolean [aIncludePrivate=false]
-   *        Tells if you want to also retrieve messages coming from private
-   *        windows. Defaults to false.
-   * @return array
-   *         The array of cached messages. Each element is an nsIScriptError or
-   *         an nsIConsoleMessage
-   */
-  getCachedMessages: function CSL_getCachedMessages(aIncludePrivate = false)
-  {
-    let errors = Services.console.getMessageArray() || [];
-
-    // if !this.window, we're in a browser console. Still need to filter
-    // private messages.
-    if (!this.window) {
-      return errors.filter((aError) => {
-        if (aError instanceof Ci.nsIScriptError) {
-          if (!aIncludePrivate && aError.isFromPrivateWindow) {
-            return false;
-          }
-        }
-
-        return true;
-      });
-    }
-
-    let ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
-
-    return errors.filter((aError) => {
-      if (aError instanceof Ci.nsIScriptError) {
-        if (!aIncludePrivate && aError.isFromPrivateWindow) {
-          return false;
-        }
-        if (ids &&
-            (ids.indexOf(aError.innerWindowID) == -1 ||
-             !this.isCategoryAllowed(aError.category))) {
-          return false;
-        }
-      }
-      else if (ids && ids[0]) {
-        // If this is not an nsIScriptError and we need to do window-based
-        // filtering we skip this message.
-        return false;
-      }
-
-      return true;
-    });
-  },
-
-  /**
-   * Remove the nsIConsoleService listener.
-   */
-  destroy: function CSL_destroy()
-  {
-    Services.console.unregisterListener(this);
-    this.listener = this.window = null;
-  },
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// The window.console API observer
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * The window.console API observer. This allows the window.console API messages
- * to be sent to the remote Web Console instance.
- *
- * @constructor
- * @param nsIDOMWindow aWindow
- *        Optional - the window object for which we are created. This is used
- *        for filtering out messages that belong to other windows.
- * @param object aOwner
- *        The owner object must have the following methods:
- *        - onConsoleAPICall(). This method is invoked with one argument, the
- *        Console API message that comes from the observer service, whenever
- *        a relevant console API call is received.
- * @param string aConsoleID
- *        Options - The consoleID that this listener should listen to
- */
-function ConsoleAPIListener(aWindow, aOwner, aConsoleID)
-{
-  this.window = aWindow;
-  this.owner = aOwner;
-  this.consoleID = aConsoleID;
-}
-exports.ConsoleAPIListener = ConsoleAPIListener;
-
-ConsoleAPIListener.prototype =
-{
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
-
-  /**
-   * The content window for which we listen to window.console API calls.
-   * @type nsIDOMWindow
-   */
-  window: null,
-
-  /**
-   * The owner object which is notified of window.console API calls. It must
-   * have a onConsoleAPICall method which is invoked with one argument: the
-   * console API call object that comes from the observer service.
-   *
-   * @type object
-   * @see WebConsoleActor
-   */
-  owner: null,
-
-  /**
-   * The consoleID that we listen for. If not null then only messages from this
-   * console will be returned.
-   */
-  consoleID: null,
-
-  /**
-   * Initialize the window.console API observer.
-   */
-  init: function CAL_init()
-  {
-    // Note that the observer is process-wide. We will filter the messages as
-    // needed, see CAL_observe().
-    Services.obs.addObserver(this, "console-api-log-event", false);
-  },
-
-  /**
-   * The console API message observer. When messages are received from the
-   * observer service we forward them to the remote Web Console instance.
-   *
-   * @param object aMessage
-   *        The message object receives from the observer service.
-   * @param string aTopic
-   *        The message topic received from the observer service.
-   */
-  observe: function CAL_observe(aMessage, aTopic)
-  {
-    if (!this.owner) {
-      return;
-    }
-
-    // Here, wrappedJSObject is not a security wrapper but a property defined
-    // by the XPCOM component which allows us to unwrap the XPCOM interface and
-    // access the underlying JSObject.
-    let apiMessage = aMessage.wrappedJSObject;
-    if (this.window && CONSOLE_WORKER_IDS.indexOf(apiMessage.innerID) == -1) {
-      let msgWindow = Services.wm.getCurrentInnerWindowWithId(apiMessage.innerID);
-      if (!msgWindow || !isWindowIncluded(this.window, msgWindow)) {
-        // Not the same window!
-        return;
-      }
-    }
-    if (this.consoleID && apiMessage.consoleID != this.consoleID) {
-      return;
-    }
-
-    this.owner.onConsoleAPICall(apiMessage);
-  },
-
-  /**
-   * Get the cached messages for the current inner window and its (i)frames.
-   *
-   * @param boolean [aIncludePrivate=false]
-   *        Tells if you want to also retrieve messages coming from private
-   *        windows. Defaults to false.
-   * @return array
-   *         The array of cached messages.
-   */
-  getCachedMessages: function CAL_getCachedMessages(aIncludePrivate = false)
-  {
-    let messages = [];
-    let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
-                              .getService(Ci.nsIConsoleAPIStorage);
-
-    // if !this.window, we're in a browser console. Retrieve all events
-    // for filtering based on privacy.
-    if (!this.window) {
-      messages = ConsoleAPIStorage.getEvents();
-    } else {
-      let ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
-      ids.forEach((id) => {
-        messages = messages.concat(ConsoleAPIStorage.getEvents(id));
-      });
-    }
-
-    CONSOLE_WORKER_IDS.forEach((id) => {
-      messages = messages.concat(ConsoleAPIStorage.getEvents(id));
-    });
-
-    if (this.consoleID) {
-      messages = messages.filter((m) => m.consoleID == this.consoleID);
-    }
-
-    if (aIncludePrivate) {
-      return messages;
-    }
-
-    return messages.filter((m) => !m.private);
-  },
-
-  /**
-   * Destroy the console API listener.
-   */
-  destroy: function CAL_destroy()
-  {
-    Services.obs.removeObserver(this, "console-api-log-event");
-    this.window = this.owner = null;
-  },
-};
-
-/**
- * WebConsole commands manager.
- *
- * Defines a set of functions /variables ("commands") that are available from
- * the Web Console but not from the web page.
- *
- */
-var WebConsoleCommands = {
-  _registeredCommands: new Map(),
-  _originalCommands: new Map(),
-
-  /**
-   * @private
-   * Reserved for built-in commands. To register a command from the code of an
-   * add-on, see WebConsoleCommands.register instead.
-   *
-   * @see WebConsoleCommands.register
-   */
-  _registerOriginal: function (name, command) {
-    this.register(name, command);
-    this._originalCommands.set(name, this.getCommand(name));
-  },
-
-  /**
-   * Register a new command.
-   * @param {string} name The command name (exemple: "$")
-   * @param {(function|object)} command The command to register.
-   *  It can be a function so the command is a function (like "$()"),
-   *  or it can also be a property descriptor to describe a getter / value (like
-   *  "$0").
-   *
-   *  The command function or the command getter are passed a owner object as
-   *  their first parameter (see the example below).
-   *
-   *  Note that setters don't work currently and "enumerable" and "configurable"
-   *  are forced to true.
-   *
-   * @example
-   *
-   *   WebConsoleCommands.register("$", function JSTH_$(aOwner, aSelector)
-   *   {
-   *     return aOwner.window.document.querySelector(aSelector);
-   *   });
-   *
-   *   WebConsoleCommands.register("$0", {
-   *     get: function(aOwner) {
-   *       return aOwner.makeDebuggeeValue(aOwner.selectedNode);
-   *     }
-   *   });
-   */
-  register: function(name, command) {
-    this._registeredCommands.set(name, command);
-  },
-
-  /**
-   * Unregister a command.
-   *
-   * If the command being unregister overrode a built-in command,
-   * the latter is restored.
-   *
-   * @param {string} name The name of the command
-   */
-  unregister: function(name) {
-    this._registeredCommands.delete(name);
-    if (this._originalCommands.has(name)) {
-      this.register(name, this._originalCommands.get(name));
-    }
-  },
-
-  /**
-   * Returns a command by its name.
-   *
-   * @param {string} name The name of the command.
-   *
-   * @return {(function|object)} The command.
-   */
-  getCommand: function(name) {
-    return this._registeredCommands.get(name);
-  },
-
-  /**
-   * Returns true if a command is registered with the given name.
-   *
-   * @param {string} name The name of the command.
-   *
-   * @return {boolean} True if the command is registered.
-   */
-  hasCommand: function(name) {
-    return this._registeredCommands.has(name);
-  },
-};
-
-exports.WebConsoleCommands = WebConsoleCommands;
-
-
-/*
- * Built-in commands.
-  *
-  * A list of helper functions used by Firebug can be found here:
-  *   http://getfirebug.com/wiki/index.php/Command_Line_API
- */
-
-/**
- * Find a node by ID.
- *
- * @param string aId
- *        The ID of the element you want.
- * @return nsIDOMNode or null
- *         The result of calling document.querySelector(aSelector).
- */
-WebConsoleCommands._registerOriginal("$", function JSTH_$(aOwner, aSelector)
-{
-  return aOwner.window.document.querySelector(aSelector);
-});
-
-/**
- * Find the nodes matching a CSS selector.
- *
- * @param string aSelector
- *        A string that is passed to window.document.querySelectorAll.
- * @return nsIDOMNodeList
- *         Returns the result of document.querySelectorAll(aSelector).
- */
-WebConsoleCommands._registerOriginal("$$", function JSTH_$$(aOwner, aSelector)
-{
-  let nodes = aOwner.window.document.querySelectorAll(aSelector);
-
-  // Calling aOwner.window.Array.from() doesn't work without accessing the
-  // wrappedJSObject, so just loop through the results instead.
-  let result = new aOwner.window.Array();
-  for (let i = 0; i < nodes.length; i++) {
-    result.push(nodes[i]);
-  }
-  return result;
-});
-
-/**
- * Returns the result of the last console input evaluation
- *
- * @return object|undefined
- * Returns last console evaluation or undefined
- */
-WebConsoleCommands._registerOriginal("$_", {
-  get: function(aOwner) {
-    return aOwner.consoleActor.getLastConsoleInputEvaluation();
-  }
-});
-
-
-/**
- * Runs an xPath query and returns all matched nodes.
- *
- * @param string aXPath
- *        xPath search query to execute.
- * @param [optional] nsIDOMNode aContext
- *        Context to run the xPath query on. Uses window.document if not set.
- * @return array of nsIDOMNode
- */
-WebConsoleCommands._registerOriginal("$x", function JSTH_$x(aOwner, aXPath, aContext)
-{
-  let nodes = new aOwner.window.Array();
-
-  // Not waiving Xrays, since we want the original Document.evaluate function,
-  // instead of anything that's been redefined.
-  let doc =  aOwner.window.document;
-  aContext = aContext || doc;
-
-  let results = doc.evaluate(aXPath, aContext, null,
-                             Ci.nsIDOMXPathResult.ANY_TYPE, null);
-  let node;
-  while ((node = results.iterateNext())) {
-    nodes.push(node);
-  }
-
-  return nodes;
-});
-
-/**
- * Returns the currently selected object in the highlighter.
- *
- * @return Object representing the current selection in the
- *         Inspector, or null if no selection exists.
- */
-WebConsoleCommands._registerOriginal("$0", {
-  get: function(aOwner) {
-    return aOwner.makeDebuggeeValue(aOwner.selectedNode);
-  }
-});
-
-/**
- * Clears the output of the WebConsole.
- */
-WebConsoleCommands._registerOriginal("clear", function JSTH_clear(aOwner)
-{
-  aOwner.helperResult = {
-    type: "clearOutput",
-  };
-});
-
-/**
- * Clears the input history of the WebConsole.
- */
-WebConsoleCommands._registerOriginal("clearHistory", function JSTH_clearHistory(aOwner)
-{
-  aOwner.helperResult = {
-    type: "clearHistory",
-  };
-});
-
-/**
- * Returns the result of Object.keys(aObject).
- *
- * @param object aObject
- *        Object to return the property names from.
- * @return array of strings
- */
-WebConsoleCommands._registerOriginal("keys", function JSTH_keys(aOwner, aObject)
-{
-  // Need to waive Xrays so we can iterate functions and accessor properties
-  return Cu.cloneInto(Object.keys(Cu.waiveXrays(aObject)), aOwner.window);
-});
-
-/**
- * Returns the values of all properties on aObject.
- *
- * @param object aObject
- *        Object to display the values from.
- * @return array of string
- */
-WebConsoleCommands._registerOriginal("values", function JSTH_values(aOwner, aObject)
-{
-  let values = [];
-  // Need to waive Xrays so we can iterate functions and accessor properties
-  let waived = Cu.waiveXrays(aObject);
-  let names = Object.getOwnPropertyNames(waived);
-
-  for (let name of names) {
-    values.push(waived[name]);
-  }
-
-  return Cu.cloneInto(values, aOwner.window);
-});
-
-/**
- * Opens a help window in MDN.
- */
-WebConsoleCommands._registerOriginal("help", function JSTH_help(aOwner)
-{
-  aOwner.helperResult = { type: "help" };
-});
-
-/**
- * Change the JS evaluation scope.
- *
- * @param DOMElement|string|window aWindow
- *        The window object to use for eval scope. This can be a string that
- *        is used to perform document.querySelector(), to find the iframe that
- *        you want to cd() to. A DOMElement can be given as well, the
- *        .contentWindow property is used. Lastly, you can directly pass
- *        a window object. If you call cd() with no arguments, the current
- *        eval scope is cleared back to its default (the top window).
- */
-WebConsoleCommands._registerOriginal("cd", function JSTH_cd(aOwner, aWindow)
-{
-  if (!aWindow) {
-    aOwner.consoleActor.evalWindow = null;
-    aOwner.helperResult = { type: "cd" };
-    return;
-  }
-
-  if (typeof aWindow == "string") {
-    aWindow = aOwner.window.document.querySelector(aWindow);
-  }
-  if (aWindow instanceof Ci.nsIDOMElement && aWindow.contentWindow) {
-    aWindow = aWindow.contentWindow;
-  }
-  if (!(aWindow instanceof Ci.nsIDOMWindow)) {
-    aOwner.helperResult = { type: "error", message: "cdFunctionInvalidArgument" };
-    return;
-  }
-
-  aOwner.consoleActor.evalWindow = aWindow;
-  aOwner.helperResult = { type: "cd" };
-});
-
-/**
- * Inspects the passed aObject. This is done by opening the PropertyPanel.
- *
- * @param object aObject
- *        Object to inspect.
- */
-WebConsoleCommands._registerOriginal("inspect", function JSTH_inspect(aOwner, aObject)
-{
-  let dbgObj = aOwner.makeDebuggeeValue(aObject);
-  let grip = aOwner.createValueGrip(dbgObj);
-  aOwner.helperResult = {
-    type: "inspectObject",
-    input: aOwner.evalInput,
-    object: grip,
-  };
-});
-
-/**
- * Prints aObject to the output.
- *
- * @param object aObject
- *        Object to print to the output.
- * @return string
- */
-WebConsoleCommands._registerOriginal("pprint", function JSTH_pprint(aOwner, aObject)
-{
-  if (aObject === null || aObject === undefined || aObject === true ||
-      aObject === false) {
-    aOwner.helperResult = {
-      type: "error",
-      message: "helperFuncUnsupportedTypeError",
-    };
-    return null;
-  }
-
-  aOwner.helperResult = { rawOutput: true };
-
-  if (typeof aObject == "function") {
-    return aObject + "\n";
-  }
-
-  let output = [];
-
-  let obj = aObject;
-  for (let name in obj) {
-    let desc = WebConsoleUtils.getPropertyDescriptor(obj, name) || {};
-    if (desc.get || desc.set) {
-      // TODO: Bug 842672 - toolkit/ imports modules from browser/.
-      let getGrip = VariablesView.getGrip(desc.get);
-      let setGrip = VariablesView.getGrip(desc.set);
-      let getString = VariablesView.getString(getGrip);
-      let setString = VariablesView.getString(setGrip);
-      output.push(name + ":", "  get: " + getString, "  set: " + setString);
-    }
-    else {
-      let valueGrip = VariablesView.getGrip(obj[name]);
-      let valueString = VariablesView.getString(valueGrip);
-      output.push(name + ": " + valueString);
-    }
-  }
-
-  return "  " + output.join("\n  ");
-});
-
-/**
- * Print the String representation of a value to the output, as-is.
- *
- * @param any aValue
- *        A value you want to output as a string.
- * @return void
- */
-WebConsoleCommands._registerOriginal("print", function JSTH_print(aOwner, aValue)
-{
-  aOwner.helperResult = { rawOutput: true };
-  if (typeof aValue === "symbol") {
-    return Symbol.prototype.toString.call(aValue);
-  }
-  // Waiving Xrays here allows us to see a closer representation of the
-  // underlying object. This may execute arbitrary content code, but that
-  // code will run with content privileges, and the result will be rendered
-  // inert by coercing it to a String.
-  return String(Cu.waiveXrays(aValue));
-});
-
-/**
- * Copy the String representation of a value to the clipboard.
- *
- * @param any aValue
- *        A value you want to copy as a string.
- * @return void
- */
-WebConsoleCommands._registerOriginal("copy", function JSTH_copy(aOwner, aValue)
-{
-  let payload;
-  try {
-    if (aValue instanceof Ci.nsIDOMElement) {
-      payload = aValue.outerHTML;
-    } else if (typeof aValue == "string") {
-      payload = aValue;
-    } else {
-      payload = JSON.stringify(aValue, null, "  ");
-    }
-  } catch (ex) {
-    payload = "/* " + ex  + " */";
-  }
-  aOwner.helperResult = {
-    type: "copyValueToClipboard",
-    value: payload,
-  };
-});
-
-
-/**
- * (Internal only) Add the bindings to |owner.sandbox|.
- * This is intended to be used by the WebConsole actor only.
-  *
-  * @param object aOwner
-  *        The owning object.
-  */
-function addWebConsoleCommands(owner) {
-  if (!owner) {
-    throw new Error("The owner is required");
-  }
-  for (let [name, command] of WebConsoleCommands._registeredCommands) {
-    if (typeof command === "function") {
-      owner.sandbox[name] = command.bind(undefined, owner);
-    }
-    else if (typeof command === "object") {
-      let clone = Object.assign({}, command, {
-        // We force the enumerability and the configurability (so the
-        // WebConsoleActor can reconfigure the property).
-        enumerable: true,
-        configurable: true
-      });
-
-      if (typeof command.get === "function") {
-        clone.get = command.get.bind(undefined, owner);
-      }
-      if (typeof command.set === "function") {
-        clone.set = command.set.bind(undefined, owner);
-      }
-
-      Object.defineProperty(owner.sandbox, name, clone);
-    }
-  }
-}
-
-exports.addWebConsoleCommands = addWebConsoleCommands;
-
-/**
- * A ReflowObserver that listens for reflow events from the page.
- * Implements nsIReflowObserver.
- *
- * @constructor
- * @param object aWindow
- *        The window for which we need to track reflow.
- * @param object aOwner
- *        The listener owner which needs to implement:
- *        - onReflowActivity(aReflowInfo)
- */
-
-function ConsoleReflowListener(aWindow, aListener)
-{
-  this.docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShell);
-  this.listener = aListener;
-  this.docshell.addWeakReflowObserver(this);
-}
-
-exports.ConsoleReflowListener = ConsoleReflowListener;
-
-ConsoleReflowListener.prototype =
-{
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
-                                         Ci.nsISupportsWeakReference]),
-  docshell: null,
-  listener: null,
-
-  /**
-   * Forward reflow event to listener.
-   *
-   * @param DOMHighResTimeStamp aStart
-   * @param DOMHighResTimeStamp aEnd
-   * @param boolean aInterruptible
-   */
-  sendReflow: function CRL_sendReflow(aStart, aEnd, aInterruptible)
-  {
-    let frame = components.stack.caller.caller;
-
-    let filename = frame ? frame.filename : null;
-
-    if (filename) {
-      // Because filename could be of the form "xxx.js -> xxx.js -> xxx.js",
-      // we only take the last part.
-      filename = filename.split(" ").pop();
-    }
-
-    this.listener.onReflowActivity({
-      interruptible: aInterruptible,
-      start: aStart,
-      end: aEnd,
-      sourceURL: filename,
-      sourceLine: frame ? frame.lineNumber : null,
-      functionName: frame ? frame.name : null
-    });
-  },
-
-  /**
-   * On uninterruptible reflow
-   *
-   * @param DOMHighResTimeStamp aStart
-   * @param DOMHighResTimeStamp aEnd
-   */
-  reflow: function CRL_reflow(aStart, aEnd)
-  {
-    this.sendReflow(aStart, aEnd, false);
-  },
-
-  /**
-   * On interruptible reflow
-   *
-   * @param DOMHighResTimeStamp aStart
-   * @param DOMHighResTimeStamp aEnd
-   */
-  reflowInterruptible: function CRL_reflowInterruptible(aStart, aEnd)
-  {
-    this.sendReflow(aStart, aEnd, true);
-  },
-
-  /**
-   * Unregister listener.
-   */
-  destroy: function CRL_destroy()
-  {
-    this.docshell.removeWeakReflowObserver(this);
-    this.listener = this.docshell = null;
-  },
-};
-
-function gSequenceId()
-{
-  return gSequenceId.n++;
-}
-gSequenceId.n = 0;
--- a/devtools/shared/webconsole/moz.build
+++ b/devtools/shared/webconsole/moz.build
@@ -5,15 +5,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['OS_TARGET'] != 'Android':
     MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
     XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 DevToolsModules(
     'client.js',
+    'js-property-provider.js',
     'network-helper.js',
     'network-monitor.js',
     'server-logger-monitor.js',
     'server-logger.js',
     'utils.js',
     'worker-utils.js',
 )
--- a/devtools/shared/webconsole/test/test_jsterm.html
+++ b/devtools/shared/webconsole/test/test_jsterm.html
@@ -13,17 +13,17 @@
 
 <iframe id="content-iframe" src="http://example.com/chrome/devtools/shared/webconsole/test/sandboxed_iframe.html"></iframe>
 
 <script class="testbody" type="text/javascript;version=1.8">
 SimpleTest.waitForExplicitFinish();
 
 let gState;
 
-let {MAX_AUTOCOMPLETE_ATTEMPTS,MAX_AUTOCOMPLETIONS} = require("devtools/shared/webconsole/utils");
+let {MAX_AUTOCOMPLETE_ATTEMPTS,MAX_AUTOCOMPLETIONS} = require("devtools/shared/webconsole/js-property-provider");
 
 // This test runs all of its assertions twice - once with
 // evaluateJS and once with evaluateJSAsync.
 let evaluatingSync = true;
 function evaluateJS(input, options = {}) {
   return new Promise((resolve, reject) => {
     if (evaluatingSync) {
       gState.client.evaluateJS(input, resolve, options);
--- a/devtools/shared/webconsole/test/unit/test_js_property_provider.js
+++ b/devtools/shared/webconsole/test/unit/test_js_property_provider.js
@@ -1,15 +1,15 @@
 /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/publicdomain/zero/1.0/
 
 "use strict";
 const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
-const { JSPropertyProvider } = require("devtools/shared/webconsole/utils");
+const { JSPropertyProvider } = require("devtools/shared/webconsole/js-property-provider");
 
 Components.utils.import("resource://gre/modules/jsdebugger.jsm");
 addDebuggerToGlobal(this);
 
 function run_test() {
   const testArray = 'var testArray = [\
     {propA: "A"},\
     {\
--- a/devtools/shared/webconsole/utils.js
+++ b/devtools/shared/webconsole/utils.js
@@ -7,17 +7,16 @@
 "use strict";
 
 const {Cc, Ci, Cu, components} = require("chrome");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
-loader.lazyImporter(this, "Parser", "resource://devtools/shared/Parser.jsm");
 
 // TODO: Bug 842672 - browser/ imports modules from toolkit/.
 // Note that these are only used in WebConsoleCommands, see $0 and pprint().
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 // Match the function name from the result of toString() or toSource().
 //
@@ -28,25 +27,18 @@ const DevToolsUtils = require("devtools/
 const REGEX_MATCH_FUNCTION_NAME = /^\(?function\s+([^(\s]+)\s*\(/;
 
 // Match the function arguments from the result of toString() or toSource().
 const REGEX_MATCH_FUNCTION_ARGS = /^\(?function\s*[^\s(]*\s*\((.+?)\)/;
 
 // Number of terminal entries for the self-xss prevention to go away
 const CONSOLE_ENTRY_THRESHOLD = 5;
 
-// Provide an easy way to bail out of even attempting an autocompletion
-// if an object has way too many properties. Protects against large objects
-// with numeric values that wouldn't be tallied towards MAX_AUTOCOMPLETIONS.
-const MAX_AUTOCOMPLETE_ATTEMPTS = exports.MAX_AUTOCOMPLETE_ATTEMPTS = 100000;
-
 const CONSOLE_WORKER_IDS = exports.CONSOLE_WORKER_IDS = [ 'SharedWorker', 'ServiceWorker', 'Worker' ];
 
-// Prevent iterating over too many properties during autocomplete suggestions.
-const MAX_AUTOCOMPLETIONS = exports.MAX_AUTOCOMPLETIONS = 1500;
 
 var WebConsoleUtils = {
 
   /**
    * Wrap a string in an nsISupportsString object.
    *
    * @param string aString
    * @return nsISupportsString
@@ -685,522 +677,16 @@ WebConsoleUtils.l10n.prototype = {
     catch (ex) {
       Cu.reportError("Failed to format string: " + aName);
       throw ex;
     }
     return result;
   },
 };
 
-
-//////////////////////////////////////////////////////////////////////////
-// JS Completer
-//////////////////////////////////////////////////////////////////////////
-
-(function _JSPP(WCU) {
-const STATE_NORMAL = 0;
-const STATE_QUOTE = 2;
-const STATE_DQUOTE = 3;
-
-const OPEN_BODY = "{[(".split("");
-const CLOSE_BODY = "}])".split("");
-const OPEN_CLOSE_BODY = {
-  "{": "}",
-  "[": "]",
-  "(": ")",
-};
-
-/**
- * Analyses a given string to find the last statement that is interesting for
- * later completion.
- *
- * @param   string aStr
- *          A string to analyse.
- *
- * @returns object
- *          If there was an error in the string detected, then a object like
- *
- *            { err: "ErrorMesssage" }
- *
- *          is returned, otherwise a object like
- *
- *            {
- *              state: STATE_NORMAL|STATE_QUOTE|STATE_DQUOTE,
- *              startPos: index of where the last statement begins
- *            }
- */
-function findCompletionBeginning(aStr)
-{
-  let bodyStack = [];
-
-  let state = STATE_NORMAL;
-  let start = 0;
-  let c;
-  for (let i = 0; i < aStr.length; i++) {
-    c = aStr[i];
-
-    switch (state) {
-      // Normal JS state.
-      case STATE_NORMAL:
-        if (c == '"') {
-          state = STATE_DQUOTE;
-        }
-        else if (c == "'") {
-          state = STATE_QUOTE;
-        }
-        else if (c == ";") {
-          start = i + 1;
-        }
-        else if (c == " ") {
-          start = i + 1;
-        }
-        else if (OPEN_BODY.indexOf(c) != -1) {
-          bodyStack.push({
-            token: c,
-            start: start
-          });
-          start = i + 1;
-        }
-        else if (CLOSE_BODY.indexOf(c) != -1) {
-          var last = bodyStack.pop();
-          if (!last || OPEN_CLOSE_BODY[last.token] != c) {
-            return {
-              err: "syntax error"
-            };
-          }
-          if (c == "}") {
-            start = i + 1;
-          }
-          else {
-            start = last.start;
-          }
-        }
-        break;
-
-      // Double quote state > " <
-      case STATE_DQUOTE:
-        if (c == "\\") {
-          i++;
-        }
-        else if (c == "\n") {
-          return {
-            err: "unterminated string literal"
-          };
-        }
-        else if (c == '"') {
-          state = STATE_NORMAL;
-        }
-        break;
-
-      // Single quote state > ' <
-      case STATE_QUOTE:
-        if (c == "\\") {
-          i++;
-        }
-        else if (c == "\n") {
-          return {
-            err: "unterminated string literal"
-          };
-        }
-        else if (c == "'") {
-          state = STATE_NORMAL;
-        }
-        break;
-    }
-  }
-
-  return {
-    state: state,
-    startPos: start
-  };
-}
-
-/**
- * Provides a list of properties, that are possible matches based on the passed
- * Debugger.Environment/Debugger.Object and inputValue.
- *
- * @param object aDbgObject
- *        When the debugger is not paused this Debugger.Object wraps the scope for autocompletion.
- *        It is null if the debugger is paused.
- * @param object anEnvironment
- *        When the debugger is paused this Debugger.Environment is the scope for autocompletion.
- *        It is null if the debugger is not paused.
- * @param string aInputValue
- *        Value that should be completed.
- * @param number [aCursor=aInputValue.length]
- *        Optional offset in the input where the cursor is located. If this is
- *        omitted then the cursor is assumed to be at the end of the input
- *        value.
- * @returns null or object
- *          If no completion valued could be computed, null is returned,
- *          otherwise a object with the following form is returned:
- *            {
- *              matches: [ string, string, string ],
- *              matchProp: Last part of the inputValue that was used to find
- *                         the matches-strings.
- *            }
- */
-function JSPropertyProvider(aDbgObject, anEnvironment, aInputValue, aCursor)
-{
-  if (aCursor === undefined) {
-    aCursor = aInputValue.length;
-  }
-
-  let inputValue = aInputValue.substring(0, aCursor);
-
-  // Analyse the inputValue and find the beginning of the last part that
-  // should be completed.
-  let beginning = findCompletionBeginning(inputValue);
-
-  // There was an error analysing the string.
-  if (beginning.err) {
-    return null;
-  }
-
-  // If the current state is not STATE_NORMAL, then we are inside of an string
-  // which means that no completion is possible.
-  if (beginning.state != STATE_NORMAL) {
-    return null;
-  }
-
-  let completionPart = inputValue.substring(beginning.startPos);
-  let lastDot = completionPart.lastIndexOf(".");
-
-  // Don't complete on just an empty string.
-  if (completionPart.trim() == "") {
-    return null;
-  }
-
-  // Catch literals like [1,2,3] or "foo" and return the matches from
-  // their prototypes.
-  if (lastDot > 0) {
-    let parser = new Parser();
-    parser.logExceptions = false;
-    let syntaxTree = parser.get(completionPart.slice(0, lastDot));
-    let lastTree = syntaxTree.getLastSyntaxTree();
-    let lastBody = lastTree && lastTree.AST.body[lastTree.AST.body.length - 1];
-
-    // Finding the last expression since we've sliced up until the dot.
-    // If there were parse errors this won't exist.
-    if (lastBody) {
-      let expression = lastBody.expression;
-      let matchProp = completionPart.slice(lastDot + 1);
-      if (expression.type === "ArrayExpression") {
-        return getMatchedProps(Array.prototype, matchProp);
-      } else if (expression.type === "Literal" &&
-                 (typeof expression.value === "string")) {
-        return getMatchedProps(String.prototype, matchProp);
-      }
-    }
-  }
-
-  // We are completing a variable / a property lookup.
-  let properties = completionPart.split(".");
-  let matchProp = properties.pop().trimLeft();
-  let obj = aDbgObject;
-
-  // The first property must be found in the environment if the debugger is
-  // paused.
-  if (anEnvironment) {
-    if (properties.length == 0) {
-      return getMatchedPropsInEnvironment(anEnvironment, matchProp);
-    }
-    obj = getVariableInEnvironment(anEnvironment, properties.shift());
-  }
-
-  if (!isObjectUsable(obj)) {
-    return null;
-  }
-
-  // We get the rest of the properties recursively starting from the Debugger.Object
-  // that wraps the first property
-  for (let prop of properties) {
-    prop = prop.trim();
-    if (!prop) {
-      return null;
-    }
-
-    if (/\[\d+\]$/.test(prop)) {
-      // The property to autocomplete is a member of array. For example
-      // list[i][j]..[n]. Traverse the array to get the actual element.
-      obj = getArrayMemberProperty(obj, prop);
-    }
-    else {
-      obj = DevToolsUtils.getProperty(obj, prop);
-    }
-
-    if (!isObjectUsable(obj)) {
-      return null;
-    }
-  }
-
-  // If the final property is a primitive
-  if (typeof obj != "object") {
-    return getMatchedProps(obj, matchProp);
-  }
-
-  return getMatchedPropsInDbgObject(obj, matchProp);
-}
-
-/**
- * Get the array member of aObj for the given aProp. For example, given
- * aProp='list[0][1]' the element at [0][1] of aObj.list is returned.
- *
- * @param object aObj
- *        The object to operate on.
- * @param string aProp
- *        The property to return.
- * @return null or Object
- *         Returns null if the property couldn't be located. Otherwise the array
- *         member identified by aProp.
- */
-function getArrayMemberProperty(aObj, aProp)
-{
-  // First get the array.
-  let obj = aObj;
-  let propWithoutIndices = aProp.substr(0, aProp.indexOf("["));
-  obj = DevToolsUtils.getProperty(obj, propWithoutIndices);
-  if (!isObjectUsable(obj)) {
-    return null;
-  }
-
-  // Then traverse the list of indices to get the actual element.
-  let result;
-  let arrayIndicesRegex = /\[[^\]]*\]/g;
-  while ((result = arrayIndicesRegex.exec(aProp)) !== null) {
-    let indexWithBrackets = result[0];
-    let indexAsText = indexWithBrackets.substr(1, indexWithBrackets.length - 2);
-    let index = parseInt(indexAsText);
-
-    if (isNaN(index)) {
-      return null;
-    }
-
-    obj = DevToolsUtils.getProperty(obj, index);
-
-    if (!isObjectUsable(obj)) {
-      return null;
-    }
-  }
-
-  return obj;
-}
-
-/**
- * Check if the given Debugger.Object can be used for autocomplete.
- *
- * @param Debugger.Object aObject
- *        The Debugger.Object to check.
- * @return boolean
- *         True if further inspection into the object is possible, or false
- *         otherwise.
- */
-function isObjectUsable(aObject)
-{
-  if (aObject == null) {
-    return false;
-  }
-
-  if (typeof aObject == "object" && aObject.class == "DeadObject") {
-    return false;
-  }
-
-  return true;
-}
-
-/**
- * @see getExactMatch_impl()
- */
-function getVariableInEnvironment(anEnvironment, aName)
-{
-  return getExactMatch_impl(anEnvironment, aName, DebuggerEnvironmentSupport);
-}
-
-/**
- * @see getMatchedProps_impl()
- */
-function getMatchedPropsInEnvironment(anEnvironment, aMatch)
-{
-  return getMatchedProps_impl(anEnvironment, aMatch, DebuggerEnvironmentSupport);
-}
-
-/**
- * @see getMatchedProps_impl()
- */
-function getMatchedPropsInDbgObject(aDbgObject, aMatch)
-{
-  return getMatchedProps_impl(aDbgObject, aMatch, DebuggerObjectSupport);
-}
-
-/**
- * @see getMatchedProps_impl()
- */
-function getMatchedProps(aObj, aMatch)
-{
-  if (typeof aObj != "object") {
-    aObj = aObj.constructor.prototype;
-  }
-  return getMatchedProps_impl(aObj, aMatch, JSObjectSupport);
-}
-
-/**
- * Get all properties in the given object (and its parent prototype chain) that
- * match a given prefix.
- *
- * @param mixed aObj
- *        Object whose properties we want to filter.
- * @param string aMatch
- *        Filter for properties that match this string.
- * @return object
- *         Object that contains the matchProp and the list of names.
- */
-function getMatchedProps_impl(aObj, aMatch, {chainIterator, getProperties})
-{
-  let matches = new Set();
-  let numProps = 0;
-
-  // We need to go up the prototype chain.
-  let iter = chainIterator(aObj);
-  for (let obj of iter) {
-    let props = getProperties(obj);
-    numProps += props.length;
-
-    // If there are too many properties to event attempt autocompletion,
-    // or if we have already added the max number, then stop looping
-    // and return the partial set that has already been discovered.
-    if (numProps >= MAX_AUTOCOMPLETE_ATTEMPTS ||
-        matches.size >= MAX_AUTOCOMPLETIONS) {
-      break;
-    }
-
-    for (let i = 0; i < props.length; i++) {
-      let prop = props[i];
-      if (prop.indexOf(aMatch) != 0) {
-        continue;
-      }
-      if (prop.indexOf('-') > -1) {
-        continue;
-      }
-      // If it is an array index, we can't take it.
-      // This uses a trick: converting a string to a number yields NaN if
-      // the operation failed, and NaN is not equal to itself.
-      if (+prop != +prop) {
-        matches.add(prop);
-      }
-
-      if (matches.size >= MAX_AUTOCOMPLETIONS) {
-        break;
-      }
-    }
-  }
-
-  return {
-    matchProp: aMatch,
-    matches: [...matches],
-  };
-}
-
-/**
- * Returns a property value based on its name from the given object, by
- * recursively checking the object's prototype.
- *
- * @param object aObj
- *        An object to look the property into.
- * @param string aName
- *        The property that is looked up.
- * @returns object|undefined
- *        A Debugger.Object if the property exists in the object's prototype
- *        chain, undefined otherwise.
- */
-function getExactMatch_impl(aObj, aName, {chainIterator, getProperty})
-{
-  // We need to go up the prototype chain.
-  let iter = chainIterator(aObj);
-  for (let obj of iter) {
-    let prop = getProperty(obj, aName, aObj);
-    if (prop) {
-      return prop.value;
-    }
-  }
-  return undefined;
-}
-
-
-var JSObjectSupport = {
-  chainIterator: function*(aObj)
-  {
-    while (aObj) {
-      yield aObj;
-      aObj = Object.getPrototypeOf(aObj);
-    }
-  },
-
-  getProperties: function(aObj)
-  {
-    return Object.getOwnPropertyNames(aObj);
-  },
-
-  getProperty: function()
-  {
-    // getProperty is unsafe with raw JS objects.
-    throw "Unimplemented!";
-  },
-};
-
-var DebuggerObjectSupport = {
-  chainIterator: function*(aObj)
-  {
-    while (aObj) {
-      yield aObj;
-      aObj = aObj.proto;
-    }
-  },
-
-  getProperties: function(aObj)
-  {
-    return aObj.getOwnPropertyNames();
-  },
-
-  getProperty: function(aObj, aName, aRootObj)
-  {
-    // This is left unimplemented in favor to DevToolsUtils.getProperty().
-    throw "Unimplemented!";
-  },
-};
-
-var DebuggerEnvironmentSupport = {
-  chainIterator: function*(aObj)
-  {
-    while (aObj) {
-      yield aObj;
-      aObj = aObj.parent;
-    }
-  },
-
-  getProperties: function(aObj)
-  {
-    return aObj.names();
-  },
-
-  getProperty: function(aObj, aName)
-  {
-    // TODO: we should use getVariableDescriptor() here - bug 725815.
-    let result = aObj.getVariable(aName);
-    // FIXME: Need actual UI, bug 941287.
-    if (result === undefined || result.optimizedOut || result.missingArguments) {
-      return null;
-    }
-    return { value: result };
-  },
-};
-
-
-exports.JSPropertyProvider = DevToolsUtils.makeInfallible(JSPropertyProvider);
-})(WebConsoleUtils);
-
 ///////////////////////////////////////////////////////////////////////////////
 // The page errors listener
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * The nsIConsoleService listener. This is used to send all of the console
  * messages (JavaScript, CSS and more) to the remote Web Console instance.
  *
--- a/devtools/shared/webconsole/worker-utils.js
+++ b/devtools/shared/webconsole/worker-utils.js
@@ -3,11 +3,10 @@
 // have access to Services / Components.  This functionality
 // is stubbed out to prevent errors, and will need to implemented
 // for Bug 1209353.
 
 exports.Utils = { l10n: function() {} };
 exports.ConsoleServiceListener = function() {};
 exports.ConsoleAPIListener = function() {};
 exports.addWebConsoleCommands = function() {};
-exports.JSPropertyProvider = function() {};
 exports.ConsoleReflowListener = function() {};
 exports.CONSOLE_WORKER_IDS = [];