Backed out 3 changesets (bug 1382968) for breaking browser_addons_debug_webextension.js a=backout
authorWes Kocher <wkocher@mozilla.com>
Tue, 08 Aug 2017 09:17:56 -0700
changeset 373384 be392d463805
parent 373383 40004a53d1d1
child 373429 1d042bcb2632
push id32301
push userkwierso@gmail.com
push dateTue, 08 Aug 2017 21:06:44 +0000
treeherdermozilla-central@be392d463805 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1382968
milestone57.0a1
backs oute25b4bd21ac8
5a2260bc98ca
869a9b46b5cc
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 3 changesets (bug 1382968) for breaking browser_addons_debug_webextension.js a=backout Backed out changeset e25b4bd21ac8 (bug 1382968) Backed out changeset 5a2260bc98ca (bug 1382968) Backed out changeset 869a9b46b5cc (bug 1382968) MozReview-Commit-ID: DSKWAHxdXI
devtools/client/shared/developer-toolbar.js
devtools/client/webconsole/hudservice.js
devtools/client/webconsole/webconsole-connection-proxy.js
devtools/server/actors/addon.js
devtools/server/actors/moz.build
devtools/server/actors/utils/moz.build
devtools/server/actors/utils/webconsole-listeners.js
devtools/server/actors/utils/webconsole-utils.js
devtools/server/actors/utils/webconsole-worker-listeners.js
devtools/server/actors/webconsole.js
devtools/server/actors/webconsole/content-process-forward.js
devtools/server/actors/webconsole/listeners.js
devtools/server/actors/webconsole/moz.build
devtools/server/actors/webconsole/utils.js
devtools/server/actors/webconsole/worker-listeners.js
devtools/shared/tests/unit/test_console_filtering.js
devtools/shared/webconsole/test/test_cached_messages.html
devtools/shared/webconsole/test/test_commands_registration.html
toolkit/components/processsingleton/ContentProcessSingleton.js
toolkit/components/processsingleton/MainProcessSingleton.js
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -20,17 +20,17 @@ const { PluralForm } = require("devtools
 
 loader.lazyGetter(this, "prefBranch", function () {
   return Services.prefs.getBranch(null)
                     .QueryInterface(Ci.nsIPrefBranch);
 });
 
 loader.lazyRequireGetter(this, "gcliInit", "devtools/shared/gcli/commands/index");
 loader.lazyRequireGetter(this, "util", "gcli/util/util");
-loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners", true);
+loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/utils/webconsole-listeners", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 /**
  * A collection of utilities to help working with commands
  */
--- a/devtools/client/webconsole/hudservice.js
+++ b/devtools/client/webconsole/hudservice.js
@@ -184,18 +184,19 @@ HUD_SERVICE.prototype =
         DebuggerServer.addBrowserActors();
       }
       DebuggerServer.allowChromeProcess = true;
 
       let client = new DebuggerClient(DebuggerServer.connectPipe());
       return client.connect()
         .then(() => client.getProcess())
         .then(aResponse => {
-          // Use a TabActor in order to ensure calling `attach` to the ChromeActor
-          return { form: aResponse.form, client, chrome: true, isTabActor: true };
+          // Set chrome:false in order to attach to the target
+          // (i.e. send an `attach` request to the chrome actor)
+          return { form: aResponse.form, client: client, chrome: false };
         });
     }
 
     let target;
     function getTarget(aConnection)
     {
       return TargetFactory.forRemoteTab(aConnection);
     }
--- a/devtools/client/webconsole/webconsole-connection-proxy.js
+++ b/devtools/client/webconsole/webconsole-connection-proxy.js
@@ -173,21 +173,16 @@ WebConsoleConnectionProxy.prototype = {
 
   /**
    * Attach to the Web Console actor.
    * @private
    */
   _attachConsole: function () {
     let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
                      "FileActivity"];
-    // Enable the forwarding of console messages to the parent process
-    // when we open the Browser Console or Toolbox.
-    if (this.target.chrome && !this.target.isAddon) {
-      listeners.push("ContentProcessMessages");
-    }
     this.client.attachConsole(this._consoleActor, listeners,
                               this._onAttachConsole);
   },
 
   /**
    * The "attachConsole" response handler.
    *
    * @private
--- a/devtools/server/actors/addon.js
+++ b/devtools/server/actors/addon.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 var { Ci, Cu } = require("chrome");
 var Services = require("Services");
 var { ActorPool } = require("devtools/server/actors/common");
 var { TabSources } = require("./utils/TabSources");
 var makeDebugger = require("./utils/make-debugger");
-var { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners");
+var { ConsoleAPIListener } = require("devtools/server/actors/utils/webconsole-listeners");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { assert, update } = DevToolsUtils;
 
 loader.lazyRequireGetter(this, "AddonThreadActor", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
 loader.lazyRequireGetter(this, "WebConsoleActor", "devtools/server/actors/webconsole", true);
 
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -2,17 +2,16 @@
 # 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 += [
     'highlighters',
     'utils',
-    'webconsole',
 ]
 
 DevToolsModules(
     'actor-registry.js',
     'addon.js',
     'addons.js',
     'animation.js',
     'breakpoint.js',
--- a/devtools/server/actors/utils/moz.build
+++ b/devtools/server/actors/utils/moz.build
@@ -10,9 +10,12 @@ DevToolsModules(
     'automation-timeline.js',
     'css-grid-utils.js',
     'make-debugger.js',
     'map-uri-to-addon-id.js',
     'shapes-geometry-utils.js',
     'stack.js',
     'TabSources.js',
     'walker-search.js',
+    'webconsole-listeners.js',
+    'webconsole-utils.js',
+    'webconsole-worker-listeners.js',
 )
rename from devtools/server/actors/webconsole/listeners.js
rename to devtools/server/actors/utils/webconsole-listeners.js
--- a/devtools/server/actors/webconsole/listeners.js
+++ b/devtools/server/actors/utils/webconsole-listeners.js
@@ -3,26 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, components} = require("chrome");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
 const Services = require("Services");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
-const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/webconsole/utils");
+const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/utils/webconsole-utils");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "swm",
                                    "@mozilla.org/serviceworkers/manager;1",
                                    "nsIServiceWorkerManager");
 
-// Process script used to forward console calls from content processes to parent process
-const CONTENT_PROCESS_SCRIPT = "resource://devtools/server/actors/webconsole/content-process-forward.js";
-
 // 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 [window]
@@ -449,44 +446,8 @@ ConsoleReflowListener.prototype =
   /**
    * Unregister listener.
    */
   destroy: function () {
     this.docshell.removeWeakReflowObserver(this);
     this.listener = this.docshell = null;
   },
 };
-
-/**
- * Forward console message calls from content processes to the parent process.
- * Used by Browser console and toolbox to see messages from all processes.
- *
- * @constructor
- * @param object owner
- *        The listener owner which needs to implement:
- *        - onConsoleAPICall(message)
- */
-function ContentProcessListener(listener) {
-  this.listener = listener;
-
-  Services.ppmm.addMessageListener("Console:Log", this);
-  Services.ppmm.loadProcessScript(CONTENT_PROCESS_SCRIPT, true);
-}
-
-exports.ContentProcessListener = ContentProcessListener;
-
-ContentProcessListener.prototype = {
-  receiveMessage(message) {
-    let logMsg = message.data;
-    logMsg.wrappedJSObject = logMsg;
-    this.listener.onConsoleAPICall(logMsg);
-  },
-
-  destroy() {
-    // Tell the content processes to stop listening and forwarding messages
-    Services.ppmm.broadcastAsyncMessage("DevTools:StopForwardingContentProcessMessage");
-
-    Services.ppmm.removeMessageListener("Console:Log", this);
-    Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_SCRIPT);
-
-    this.listener = null;
-  }
-};
rename from devtools/server/actors/webconsole/utils.js
rename to devtools/server/actors/utils/webconsole-utils.js
rename from devtools/server/actors/webconsole/worker-listeners.js
rename to devtools/server/actors/utils/webconsole-worker-listeners.js
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -20,31 +20,30 @@ loader.lazyRequireGetter(this, "NetworkM
 loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "StackTraceCollector", "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);
 loader.lazyRequireGetter(this, "Parser", "resource://devtools/shared/Parser.jsm", true);
 loader.lazyRequireGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm", true);
-loader.lazyRequireGetter(this, "addWebConsoleCommands", "devtools/server/actors/webconsole/utils", true);
-loader.lazyRequireGetter(this, "CONSOLE_WORKER_IDS", "devtools/server/actors/webconsole/utils", true);
-loader.lazyRequireGetter(this, "WebConsoleUtils", "devtools/server/actors/webconsole/utils", true);
+loader.lazyRequireGetter(this, "addWebConsoleCommands", "devtools/server/actors/utils/webconsole-utils", true);
+loader.lazyRequireGetter(this, "CONSOLE_WORKER_IDS", "devtools/server/actors/utils/webconsole-utils", true);
+loader.lazyRequireGetter(this, "WebConsoleUtils", "devtools/server/actors/utils/webconsole-utils", true);
 loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
 
 // Overwrite implemented listeners for workers so that we don't attempt
 // to load an unsupported module.
 if (isWorker) {
-  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/worker-listeners", true);
-  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/worker-listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/utils/webconsole-worker-listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/utils/webconsole-worker-listeners", true);
 } else {
-  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/webconsole/listeners", true);
-  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners", true);
-  loader.lazyRequireGetter(this, "ConsoleReflowListener", "devtools/server/actors/webconsole/listeners", true);
-  loader.lazyRequireGetter(this, "ContentProcessListener", "devtools/server/actors/webconsole/listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleAPIListener", "devtools/server/actors/utils/webconsole-listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/utils/webconsole-listeners", true);
+  loader.lazyRequireGetter(this, "ConsoleReflowListener", "devtools/server/actors/utils/webconsole-listeners", true);
 }
 
 /**
  * The WebConsoleActor implements capabilities needed for the Web Console
  * feature.
  *
  * @constructor
  * @param object connection
@@ -364,20 +363,16 @@ WebConsoleActor.prototype =
     if (this.consoleReflowListener) {
       this.consoleReflowListener.destroy();
       this.consoleReflowListener = null;
     }
     if (this.serverLoggingListener) {
       this.serverLoggingListener.destroy();
       this.serverLoggingListener = null;
     }
-    if (this.contentProcessListener) {
-      this.contentProcessListener.destroy();
-      this.contentProcessListener = null;
-    }
 
     events.off(this.parentActor, "changed-toplevel-document",
                this._onChangedToplevelDocument);
 
     this.conn.removeActorPool(this._actorPool);
 
     if (this.parentActor.isRootActor) {
       Services.obs.removeObserver(this._onObserverNotification,
@@ -664,26 +659,16 @@ WebConsoleActor.prototype =
             break;
           }
           if (!this.serverLoggingListener) {
             this.serverLoggingListener =
               new ServerLoggingListener(this.window, this);
           }
           startedListeners.push(listener);
           break;
-        case "ContentProcessMessages":
-          // Workers don't support this message type
-          if (isWorker) {
-            break;
-          }
-          if (!this.contentProcessListener) {
-            this.contentProcessListener = new ContentProcessListener(this);
-          }
-          startedListeners.push(listener);
-          break;
       }
     }
 
     // Update the live list of running listeners
     startedListeners.forEach(this._listeners.add, this._listeners);
 
     return {
       startedListeners: startedListeners,
@@ -703,17 +688,17 @@ WebConsoleActor.prototype =
    */
   onStopListeners: function (request) {
     let stoppedListeners = [];
 
     // If no specific listeners are requested to be detached, we stop all
     // listeners.
     let toDetach = request.listeners ||
       ["PageError", "ConsoleAPI", "NetworkActivity",
-       "FileActivity", "ServerLogging", "ContentProcessMessages"];
+       "FileActivity", "ServerLogging"];
 
     while (toDetach.length > 0) {
       let listener = toDetach.shift();
       switch (listener) {
         case "PageError":
           if (this.consoleServiceListener) {
             this.consoleServiceListener.destroy();
             this.consoleServiceListener = null;
@@ -759,23 +744,16 @@ WebConsoleActor.prototype =
           break;
         case "ServerLogging":
           if (this.serverLoggingListener) {
             this.serverLoggingListener.destroy();
             this.serverLoggingListener = null;
           }
           stoppedListeners.push(listener);
           break;
-        case "ContentProcessMessages":
-          if (this.contentProcessListener) {
-            this.contentProcessListener.destroy();
-            this.contentProcessListener = null;
-          }
-          stoppedListeners.push(listener);
-          break;
       }
     }
 
     // Update the live list of running listeners
     stoppedListeners.forEach(this._listeners.delete, this._listeners);
 
     return { stoppedListeners: stoppedListeners };
   },
deleted file mode 100644
--- a/devtools/server/actors/webconsole/content-process-forward.js
+++ /dev/null
@@ -1,136 +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 Cu = Components.utils;
-const Ci = Components.interfaces;
-
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
-                                  "resource:///modules/E10SUtils.jsm");
-
-/*
- * The message manager has an upper limit on message sizes that it can
- * reliably forward to the parent so we limit the size of console log event
- * messages that we forward here. The web console is local and receives the
- * full console message, but addons subscribed to console event messages
- * in the parent receive the truncated version. Due to fragmentation,
- * messages as small as 1MB have resulted in IPC allocation failures on
- * 32-bit platforms. To limit IPC allocation sizes, console.log messages
- * with arguments with total size > MSG_MGR_CONSOLE_MAX_SIZE (bytes) have
- * their arguments completely truncated. MSG_MGR_CONSOLE_VAR_SIZE is an
- * approximation of how much space (in bytes) a JS non-string variable will
- * require in the manager's implementation. For strings, we use 2 bytes per
- * char. The console message URI and function name are limited to
- * MSG_MGR_CONSOLE_INFO_MAX characters. We don't attempt to calculate
- * the exact amount of space the message manager implementation will require
- * for a given message so this is imperfect.
- */
-const MSG_MGR_CONSOLE_MAX_SIZE = 1024 * 1024; // 1MB
-const MSG_MGR_CONSOLE_VAR_SIZE = 8;
-const MSG_MGR_CONSOLE_INFO_MAX = 1024;
-
-function ContentProcessForward() {
-  Services.obs.addObserver(this, "console-api-log-event");
-  Services.obs.addObserver(this, "xpcom-shutdown");
-  cpmm.addMessageListener("DevTools:StopForwardingContentProcessMessage", this);
-}
-ContentProcessForward.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
-
-  receiveMessage(message) {
-    if (message.name == "DevTools:StopForwardingContentProcessMessage") {
-      this.uninit();
-    }
-  },
-
-  observe(subject, topic, data) {
-    switch (topic) {
-      case "console-api-log-event": {
-        let consoleMsg = subject.wrappedJSObject;
-
-        let msgData = {
-          level: consoleMsg.level,
-          filename: consoleMsg.filename.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
-          lineNumber: consoleMsg.lineNumber,
-          functionName: consoleMsg.functionName &&
-            consoleMsg.functionName.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
-          timeStamp: consoleMsg.timeStamp,
-          addonId: consoleMsg.addonId,
-          arguments: [],
-        };
-
-        // We can't send objects over the message manager, so we sanitize
-        // them out, replacing those arguments with "<unavailable>".
-        let unavailString = "<unavailable>";
-        let unavailStringLength = unavailString.length * 2; // 2-bytes per char
-
-        // When the sum of argument sizes reaches MSG_MGR_CONSOLE_MAX_SIZE,
-        // replace all arguments with "<truncated>".
-        let totalArgLength = 0;
-
-        // Walk through the arguments, checking the type and size.
-        for (let arg of consoleMsg.arguments) {
-          if ((typeof arg == "object" || typeof arg == "function") &&
-              arg !== null) {
-            if (Services.appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE) {
-              // For OOP extensions: we want the developer to be able to see the
-              // logs in the Browser Console. When the Addon Toolbox will be more
-              // prominent we can revisit.
-              try {
-                // If the argument is clonable, then send it as-is. If
-                // cloning fails, fall back to the unavailable string.
-                arg = Cu.cloneInto(arg, {});
-              } catch (e) {
-                arg = unavailString;
-              }
-            } else {
-              arg = unavailString;
-            }
-            totalArgLength += unavailStringLength;
-          } else if (typeof arg == "string") {
-            totalArgLength += arg.length * 2; // 2-bytes per char
-          } else {
-            totalArgLength += MSG_MGR_CONSOLE_VAR_SIZE;
-          }
-
-          if (totalArgLength <= MSG_MGR_CONSOLE_MAX_SIZE) {
-            msgData.arguments.push(arg);
-          } else {
-            // arguments take up too much space
-            msgData.arguments = ["<truncated>"];
-            break;
-          }
-        }
-
-        cpmm.sendAsyncMessage("Console:Log", msgData);
-        break;
-      }
-
-      case "xpcom-shutdown":
-        this.uninit();
-        break;
-    }
-  },
-
-  uninit() {
-    Services.obs.removeObserver(this, "console-api-log-event");
-    Services.obs.removeObserver(this, "xpcom-shutdown");
-    cpmm.removeMessageListener("DevTools:StopForwardingContentProcessMessage", this);
-  }
-};
-
-// loadProcessScript loads in all processes, including the parent,
-// in which we don't need any forwarding
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-  new ContentProcessForward();
-}
-
deleted file mode 100644
--- a/devtools/server/actors/webconsole/moz.build
+++ /dev/null
@@ -1,12 +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(
-    'content-process-forward.js',
-    'listeners.js',
-    'utils.js',
-    'worker-listeners.js',
-)
--- a/devtools/shared/tests/unit/test_console_filtering.js
+++ b/devtools/shared/tests/unit/test_console_filtering.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const { console, ConsoleAPI } = require("resource://gre/modules/Console.jsm");
-const { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners");
+const { ConsoleAPIListener } = require("devtools/server/actors/utils/webconsole-listeners");
 const Services = require("Services");
 
 var seenMessages = 0;
 var seenTypes = 0;
 
 var callback = {
   onConsoleAPICall: function (message) {
     if (message.consoleID && message.consoleID == "addon/foo") {
--- a/devtools/shared/webconsole/test/test_cached_messages.html
+++ b/devtools/shared/webconsole/test/test_cached_messages.html
@@ -92,17 +92,17 @@ function doConsoleCalls() {
     },
   ];
 }
 </script>
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 var {ConsoleServiceListener, ConsoleAPIListener} =
-  require("devtools/server/actors/webconsole/listeners");
+  require("devtools/server/actors/utils/webconsole-listeners");
 
 let consoleAPIListener, consoleServiceListener;
 let consoleAPICalls = 0;
 let pageErrors = 0;
 
 let handlers = {
   onConsoleAPICall: function onConsoleAPICall(message) {
     for (let msg of expectedConsoleCalls) {
--- a/devtools/shared/webconsole/test/test_commands_registration.html
+++ b/devtools/shared/webconsole/test/test_commands_registration.html
@@ -13,17 +13,17 @@
 <p id="quack"></p>
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 let gState;
 let tests;
 
-let {WebConsoleCommands} = require("devtools/server/actors/webconsole/utils");
+let {WebConsoleCommands} = require("devtools/server/actors/utils/webconsole-utils");
 
 function evaluateJS(input) {
   return new Promise((resolve) => gState.client.evaluateJS(input, resolve));
 }
 
 function* evaluateJSAndCheckResult(input, result) {
   let response = yield evaluateJS(input);
   checkObject(response, {result});
--- a/toolkit/components/processsingleton/ContentProcessSingleton.js
+++ b/toolkit/components/processsingleton/ContentProcessSingleton.js
@@ -5,32 +5,122 @@
 "use strict";
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
+
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
                                   "resource://gre/modules/TelemetryController.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
+                                  "resource:///modules/E10SUtils.jsm");
+
+/*
+ * The message manager has an upper limit on message sizes that it can
+ * reliably forward to the parent so we limit the size of console log event
+ * messages that we forward here. The web console is local and receives the
+ * full console message, but addons subscribed to console event messages
+ * in the parent receive the truncated version. Due to fragmentation,
+ * messages as small as 1MB have resulted in IPC allocation failures on
+ * 32-bit platforms. To limit IPC allocation sizes, console.log messages
+ * with arguments with total size > MSG_MGR_CONSOLE_MAX_SIZE (bytes) have
+ * their arguments completely truncated. MSG_MGR_CONSOLE_VAR_SIZE is an
+ * approximation of how much space (in bytes) a JS non-string variable will
+ * require in the manager's implementation. For strings, we use 2 bytes per
+ * char. The console message URI and function name are limited to
+ * MSG_MGR_CONSOLE_INFO_MAX characters. We don't attempt to calculate
+ * the exact amount of space the message manager implementation will require
+ * for a given message so this is imperfect.
+ */
+const MSG_MGR_CONSOLE_MAX_SIZE = 1024 * 1024; // 1MB
+const MSG_MGR_CONSOLE_VAR_SIZE = 8;
+const MSG_MGR_CONSOLE_INFO_MAX = 1024;
 
 function ContentProcessSingleton() {}
 ContentProcessSingleton.prototype = {
   classID: Components.ID("{ca2a8470-45c7-11e4-916c-0800200c9a66}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   observe(subject, topic, data) {
     switch (topic) {
     case "app-startup": {
+      Services.obs.addObserver(this, "console-api-log-event");
       Services.obs.addObserver(this, "xpcom-shutdown");
       TelemetryController.observe(null, topic, null);
       break;
     }
+    case "console-api-log-event": {
+      let consoleMsg = subject.wrappedJSObject;
+
+      let msgData = {
+        level: consoleMsg.level,
+        filename: consoleMsg.filename.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
+        lineNumber: consoleMsg.lineNumber,
+        functionName: consoleMsg.functionName &&
+          consoleMsg.functionName.substring(0, MSG_MGR_CONSOLE_INFO_MAX),
+        timeStamp: consoleMsg.timeStamp,
+        addonId: consoleMsg.addonId,
+        arguments: [],
+      };
+
+      // We can't send objects over the message manager, so we sanitize
+      // them out, replacing those arguments with "<unavailable>".
+      let unavailString = "<unavailable>";
+      let unavailStringLength = unavailString.length * 2; // 2-bytes per char
+
+      // When the sum of argument sizes reaches MSG_MGR_CONSOLE_MAX_SIZE,
+      // replace all arguments with "<truncated>".
+      let totalArgLength = 0;
+
+      // Walk through the arguments, checking the type and size.
+      for (let arg of consoleMsg.arguments) {
+        if ((typeof arg == "object" || typeof arg == "function") &&
+            arg !== null) {
+          if (Services.appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE) {
+            // For OOP extensions: we want the developer to be able to see the
+            // logs in the Browser Console. When the Addon Toolbox will be more
+            // prominent we can revisit.
+            try {
+              // If the argument is clonable, then send it as-is. If
+              // cloning fails, fall back to the unavailable string.
+              arg = Cu.cloneInto(arg, {});
+            } catch (e) {
+              arg = unavailString;
+            }
+          } else {
+            arg = unavailString;
+          }
+          totalArgLength += unavailStringLength;
+        } else if (typeof arg == "string") {
+          totalArgLength += arg.length * 2; // 2-bytes per char
+        } else {
+          totalArgLength += MSG_MGR_CONSOLE_VAR_SIZE;
+        }
+
+        if (totalArgLength <= MSG_MGR_CONSOLE_MAX_SIZE) {
+          msgData.arguments.push(arg);
+        } else {
+          // arguments take up too much space
+          msgData.arguments = ["<truncated>"];
+          break;
+        }
+      }
+
+      cpmm.sendAsyncMessage("Console:Log", msgData);
+      break;
+    }
+
     case "xpcom-shutdown":
+      Services.obs.removeObserver(this, "console-api-log-event");
       Services.obs.removeObserver(this, "xpcom-shutdown");
       break;
     }
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentProcessSingleton]);
--- a/toolkit/components/processsingleton/MainProcessSingleton.js
+++ b/toolkit/components/processsingleton/MainProcessSingleton.js
@@ -13,16 +13,22 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/NetUtil.jsm");
 
 function MainProcessSingleton() {}
 MainProcessSingleton.prototype = {
   classID: Components.ID("{0636a680-45cb-11e4-916c-0800200c9a66}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
+  logConsoleMessage(message) {
+    let logMsg = message.data;
+    logMsg.wrappedJSObject = logMsg;
+    Services.obs.notifyObservers(logMsg, "console-api-log-event");
+  },
+
   // Called when a webpage calls window.external.AddSearchProvider
   addSearchEngine({ target: browser, data: { pageURL, engineURL } }) {
     pageURL = NetUtil.newURI(pageURL);
     engineURL = NetUtil.newURI(engineURL, null, pageURL);
 
     let iconURL;
     let tabbrowser = browser.getTabBrowser();
     if (browser.mIconURL && (!tabbrowser || tabbrowser.shouldLoadFavIcon(pageURL)))
@@ -62,21 +68,23 @@ MainProcessSingleton.prototype = {
     switch (topic) {
     case "app-startup": {
       Services.obs.addObserver(this, "xpcom-shutdown");
 
       // Load this script early so that console.* is initialized
       // before other frame scripts.
       Services.mm.loadFrameScript("chrome://global/content/browser-content.js", true);
       Services.ppmm.loadProcessScript("chrome://global/content/process-content.js", true);
+      Services.ppmm.addMessageListener("Console:Log", this.logConsoleMessage);
       Services.mm.addMessageListener("Search:AddEngine", this.addSearchEngine);
       Services.ppmm.loadProcessScript("resource:///modules/ContentObservers.js", true);
       break;
     }
 
     case "xpcom-shutdown":
+      Services.ppmm.removeMessageListener("Console:Log", this.logConsoleMessage);
       Services.mm.removeMessageListener("Search:AddEngine", this.addSearchEngine);
       break;
     }
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MainProcessSingleton]);