Backed out 2 changesets (bug 1302996) for being the likely cause of browser chrome crashes a=backout
authorWes Kocher <wkocher@mozilla.com>
Mon, 28 Nov 2016 16:38:20 -0800
changeset 324570 fc08c9691328ad666158ffcb80caea747f7c98dc
parent 324569 595cee0bbb15973216f65d58971c77cfa83cac81
child 324571 553e9e8548dee0c89f8f8e3359ed7433465c7ac2
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersbackout
bugs1302996
milestone53.0a1
backs out8a97f217ebe82241078eb93fcb7540ffdef78109
45bcf45afca2fcea5185abdefc8b878f26764a97
Backed out 2 changesets (bug 1302996) for being the likely cause of browser chrome crashes a=backout Backed out changeset 8a97f217ebe8 (bug 1302996) Backed out changeset 45bcf45afca2 (bug 1302996)
devtools/client/definitions.js
devtools/client/framework/devtools-browser.js
devtools/client/framework/devtools.js
devtools/client/jsonview/converter-child.js
devtools/client/jsonview/converter-sniffer.js
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -7,17 +7,16 @@
 const Services = require("Services");
 const osString = Services.appinfo.OS;
 
 // Panels
 loader.lazyGetter(this, "OptionsPanel", () => require("devtools/client/framework/toolbox-options").OptionsPanel);
 loader.lazyGetter(this, "InspectorPanel", () => require("devtools/client/inspector/panel").InspectorPanel);
 loader.lazyGetter(this, "WebConsolePanel", () => require("devtools/client/webconsole/panel").WebConsolePanel);
 loader.lazyGetter(this, "DebuggerPanel", () => require("devtools/client/debugger/panel").DebuggerPanel);
-loader.lazyGetter(this, "NewDebuggerPanel", () => require("devtools/client/debugger/new/panel").DebuggerPanel);
 loader.lazyGetter(this, "StyleEditorPanel", () => require("devtools/client/styleeditor/styleeditor-panel").StyleEditorPanel);
 loader.lazyGetter(this, "ShaderEditorPanel", () => require("devtools/client/shadereditor/panel").ShaderEditorPanel);
 loader.lazyGetter(this, "CanvasDebuggerPanel", () => require("devtools/client/canvasdebugger/panel").CanvasDebuggerPanel);
 loader.lazyGetter(this, "WebAudioEditorPanel", () => require("devtools/client/webaudioeditor/panel").WebAudioEditorPanel);
 loader.lazyGetter(this, "MemoryPanel", () => require("devtools/client/memory/panel").MemoryPanel);
 loader.lazyGetter(this, "PerformancePanel", () => require("devtools/client/performance/panel").PerformancePanel);
 loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/client/netmonitor/panel").NetMonitorPanel);
 loader.lazyGetter(this, "StoragePanel", () => require("devtools/client/storage/panel").StoragePanel);
@@ -151,16 +150,18 @@ Tools.jsdebugger = {
 
   build: function (iframeWindow, toolbox) {
     return new DebuggerPanel(iframeWindow, toolbox);
   }
 };
 
 function switchDebugger() {
   if (Services.prefs.getBoolPref("devtools.debugger.new-debugger-frontend")) {
+    const NewDebuggerPanel = require("devtools/client/debugger/new/panel").DebuggerPanel;
+
     Tools.jsdebugger.url = "chrome://devtools/content/debugger/new/index.html";
     Tools.jsdebugger.build = function (iframeWindow, toolbox) {
       return new NewDebuggerPanel(iframeWindow, toolbox);
     };
   } else {
     Tools.jsdebugger.url = "chrome://devtools/content/debugger/debugger.xul";
     Tools.jsdebugger.build = function (iframeWindow, toolbox) {
       return new DebuggerPanel(iframeWindow, toolbox);
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -13,16 +13,17 @@
  **/
 
 const {Cc, Ci, Cu} = require("chrome");
 const Services = require("Services");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const Telemetry = require("devtools/client/shared/telemetry");
 const { gDevTools } = require("./devtools");
+const { when: unload } = require("sdk/system/unload");
 
 // Load target and toolbox lazily as they need gDevTools to be fully initialized
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "BrowserMenus", "devtools/client/framework/browser-menus");
 loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
@@ -136,26 +137,16 @@ var gDevToolsBrowser = exports.gDevTools
         break;
       case "nsPref:changed":
         if (prefName.endsWith("enabled")) {
           for (let win of this._trackedBrowserWindows) {
             this.updateCommandAvailability(win);
           }
         }
         break;
-      case "quit-application":
-        gDevToolsBrowser.destroy({ shuttingDown: true });
-        break;
-      case "sdk:loader:destroy":
-        // This event is fired when the devtools loader unloads, which happens
-        // only when the add-on workflow ask devtools to be reloaded.
-        if (subject.wrappedJSObject == require('@loader/unload')) {
-          gDevToolsBrowser.destroy({ shuttingDown: false });
-        }
-        break;
     }
   },
 
   _prefObserverRegistered: false,
 
   ensurePrefObserver: function () {
     if (!this._prefObserverRegistered) {
       this._prefObserverRegistered = true;
@@ -734,38 +725,29 @@ var gDevToolsBrowser = exports.gDevTools
     let tabStats = gDevToolsBrowser._tabStats;
     this._telemetry.log(TABS_OPEN_PEAK_HISTOGRAM, tabStats.peakOpen);
     this._telemetry.log(TABS_OPEN_AVG_HISTOGRAM, mean(tabStats.histOpen));
     this._telemetry.log(TABS_PINNED_PEAK_HISTOGRAM, tabStats.peakPinned);
     this._telemetry.log(TABS_PINNED_AVG_HISTOGRAM, mean(tabStats.histPinned));
   },
 
   /**
-   * Either the SDK Loader has been destroyed by the add-on contribution
-   * workflow, or firefox is shutting down.
-
-   * @param {boolean} shuttingDown
-   *        True if firefox is currently shutting down. We may prevent doing
-   *        some cleanups to speed it up. Otherwise everything need to be
-   *        cleaned up in order to be able to load devtools again.
+   * All browser windows have been closed, tidy up remaining objects.
    */
-  destroy: function ({ shuttingDown }) {
+  destroy: function () {
     Services.prefs.removeObserver("devtools.", gDevToolsBrowser);
     Services.obs.removeObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
-    Services.obs.removeObserver(gDevToolsBrowser, "quit-application");
-    Services.obs.removeObserver(gDevToolsBrowser, "sdk:loader:destroy");
+    Services.obs.removeObserver(gDevToolsBrowser.destroy, "quit-application");
 
     gDevToolsBrowser._pingTelemetry();
     gDevToolsBrowser._telemetry = null;
 
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
       gDevToolsBrowser._forgetBrowserWindow(win);
     }
-
-    gDevTools.destroy({ shuttingDown });
   },
 };
 
 // Handle all already registered tools,
 gDevTools.getToolDefinitionArray()
          .forEach(def => gDevToolsBrowser._addToolToWindows(def));
 // and the new ones.
 gDevTools.on("tool-registered", function (ev, toolId) {
@@ -779,22 +761,25 @@ gDevTools.on("tool-registered", function
 
 gDevTools.on("tool-unregistered", function (ev, toolId) {
   gDevToolsBrowser._removeToolFromWindows(toolId);
 });
 
 gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
 gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);
 
-Services.obs.addObserver(gDevToolsBrowser, "quit-application", false);
+Services.obs.addObserver(gDevToolsBrowser.destroy, "quit-application", false);
 Services.obs.addObserver(gDevToolsBrowser, "browser-delayed-startup-finished", false);
-// Watch for module loader unload. Fires when the tools are reloaded.
-Services.obs.addObserver(gDevToolsBrowser, "sdk:loader:destroy", false);
 
 // Fake end of browser window load event for all already opened windows
 // that is already fully loaded.
 let enumerator = Services.wm.getEnumerator(gDevTools.chromeWindowType);
 while (enumerator.hasMoreElements()) {
   let win = enumerator.getNext();
   if (win.gBrowserInit && win.gBrowserInit.delayedStartupFinished) {
     gDevToolsBrowser._registerBrowserWindow(win);
   }
 }
+
+// Watch for module loader unload. Fires when the tools are reloaded.
+unload(function () {
+  gDevToolsBrowser.destroy();
+});
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -13,39 +13,45 @@ loader.lazyRequireGetter(this, "Toolbox"
 loader.lazyRequireGetter(this, "ToolboxHostManager", "devtools/client/framework/toolbox-host-manager", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 
 const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} =
   require("devtools/client/definitions");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {JsonView} = require("devtools/client/jsonview/main");
 const AboutDevTools = require("devtools/client/framework/about-devtools-toolbox");
+const {when: unload} = require("sdk/system/unload");
 const {Task} = require("devtools/shared/task");
 
 const FORBIDDEN_IDS = new Set(["toolbox", ""]);
 const MAX_ORDINAL = 99;
 
 /**
  * DevTools is a class that represents a set of developer tools, it holds a
  * set of tools and keeps track of open toolboxes in the browser.
  */
 this.DevTools = function DevTools() {
   this._tools = new Map();     // Map<toolId, tool>
   this._themes = new Map();    // Map<themeId, theme>
   this._toolboxes = new Map(); // Map<target, toolbox>
   // List of toolboxes that are still in process of creation
   this._creatingToolboxes = new Map(); // Map<target, toolbox Promise>
 
+  // destroy() is an observer's handler so we need to preserve context.
+  this.destroy = this.destroy.bind(this);
+
   // JSON Viewer for 'application/json' documents.
   JsonView.initialize();
 
   AboutDevTools.register();
 
   EventEmitter.decorate(this);
 
+  Services.obs.addObserver(this.destroy, "quit-application", false);
+
   // This is important step in initialization codepath where we are going to
   // start registering all default tools and themes: create menuitems, keys, emit
   // related events.
   this.registerDefaults();
 };
 
 DevTools.prototype = {
   // The windowtype of the main window, used in various tools. This may be set
@@ -477,33 +483,30 @@ DevTools.prototype = {
     let toolbox = this._toolboxes.get(target);
     if (toolbox == null) {
       return promise.resolve(false);
     }
     return toolbox.destroy().then(() => true);
   },
 
   /**
-   * Either the SDK Loader has been destroyed by the add-on contribution
-   * workflow, or firefox is shutting down.
-
-   * @param {boolean} shuttingDown
-   *        True if firefox is currently shutting down. We may prevent doing
-   *        some cleanups to speed it up. Otherwise everything need to be
-   *        cleaned up in order to be able to load devtools again.
+   * Called to tear down a tools provider.
    */
-  destroy: function ({ shuttingDown }) {
-    // Do not cleanup everything during firefox shutdown, but only when
-    // devtools are reloaded via the add-on contribution workflow.
-    if (!shuttingDown) {
-      for (let [target, toolbox] of this._toolboxes) {
-        toolbox.destroy();
-      }
-      AboutDevTools.unregister();
+  _teardown: function DT_teardown() {
+    for (let [target, toolbox] of this._toolboxes) {
+      toolbox.destroy();
     }
+    AboutDevTools.unregister();
+  },
+
+  /**
+   * All browser windows have been closed, tidy up remaining objects.
+   */
+  destroy: function () {
+    Services.obs.removeObserver(this.destroy, "quit-application");
 
     for (let [key, tool] of this.getToolDefinitionMap()) {
       this.unregisterTool(key, true);
     }
 
     JsonView.destroy();
 
     gDevTools.unregisterDefaults();
@@ -519,8 +522,13 @@ DevTools.prototype = {
   *[Symbol.iterator ]() {
     for (let toolbox of this._toolboxes) {
       yield toolbox;
     }
   }
 };
 
 const gDevTools = exports.gDevTools = new DevTools();
+
+// Watch for module loader unload. Fires when the tools are reloaded.
+unload(function () {
+  gDevTools._teardown();
+});
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -1,65 +1,61 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {Cc, Ci, Cu, Cm, Cr, components} = require("chrome");
-const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
-const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
+const {Cu, Cc, Ci, components} = require("chrome");
 const Services = require("Services");
+const {Class} = require("sdk/core/heritage");
+const {Unknown} = require("sdk/platform/xpcom");
+const xpcom = require("sdk/platform/xpcom");
+const Events = require("sdk/dom/events");
+const Clipboard = require("sdk/clipboard");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                                "devtools/shared/webconsole/network-helper");
-loader.lazyRequireGetter(this, "Events",
-                               "sdk/dom/events");
-loader.lazyRequireGetter(this, "Clipboard",
-                               "sdk/clipboard");
 loader.lazyRequireGetter(this, "JsonViewUtils",
                                "devtools/client/jsonview/utils");
 
 const childProcessMessageManager =
   Cc["@mozilla.org/childprocessmessagemanager;1"]
     .getService(Ci.nsISyncMessageSender);
 
 // Amount of space that will be allocated for the stream's backing-store.
 // Must be power of 2. Used to copy the data stream in onStopRequest.
 const SEGMENT_SIZE = Math.pow(2, 17);
 
 const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
 const CONTRACT_ID = "@mozilla.org/streamconv;1?from=" +
   JSON_VIEW_MIME_TYPE + "&to=*/*";
-const CLASS_ID = components.ID("{d8c9acee-dec5-11e4-8c75-1681e6b88ec1}");
-const CLASS_DESCRIPTION = "JSONView converter";
+const CLASS_ID = "{d8c9acee-dec5-11e4-8c75-1681e6b88ec1}";
 
 // Localization
-loader.lazyGetter(this, "jsonViewStrings", () => {
-  return Services.strings.createBundle(
-    "chrome://devtools/locale/jsonview.properties");
-});
+let jsonViewStrings = Services.strings.createBundle(
+  "chrome://devtools/locale/jsonview.properties");
 
 /**
  * This object detects 'application/vnd.mozilla.json.view' content type
  * and converts it into a JSON Viewer application that allows simple
  * JSON inspection.
  *
  * Inspired by JSON View: https://github.com/bhollis/jsonview/
  */
-function Converter() {}
+let Converter = Class({
+  extends: Unknown,
 
-Converter.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIStreamConverter,
-    Ci.nsIStreamListener,
-    Ci.nsIRequestObserver
-  ]),
+  interfaces: [
+    "nsIStreamConverter",
+    "nsIStreamListener",
+    "nsIRequestObserver"
+  ],
 
   get wrappedJSObject() {
     return this;
   },
 
   /**
    * This component works as such:
    * 1. asyncConvertData captures the listener
@@ -300,42 +296,38 @@ Converter.prototype = {
     let requestHeaders = headers.request;
     for (let i = 0; i < requestHeaders.length; i++) {
       let header = requestHeaders[i];
       value += header.name + ": " + header.value + eol;
     }
 
     Clipboard.set(value, "text");
   }
-};
+});
 
-const Factory = {
-  createInstance: function (outer, iid) {
-    if (outer) {
-      throw Cr.NS_ERROR_NO_AGGREGATION;
-    }
-    return new Converter();
-  }
-};
+// Stream converter component definition
+let service = xpcom.Service({
+  id: components.ID(CLASS_ID),
+  contract: CONTRACT_ID,
+  Component: Converter,
+  register: false,
+  unregister: false
+});
 
 function register() {
-  if (!registrar.isCIDRegistered(CLASS_ID)) {
-    registrar.registerFactory(CLASS_ID,
-      CLASS_DESCRIPTION,
-      CONTRACT_ID,
-      Factory);
+  if (!xpcom.isRegistered(service)) {
+    xpcom.register(service);
     return true;
   }
-
   return false;
 }
 
 function unregister() {
-  if (registrar.isCIDRegistered(CLASS_ID)) {
-    registrar.unregisterFactory(CLASS_ID, Factory);
+  if (xpcom.isRegistered(service)) {
+    xpcom.unregister(service);
     return true;
   }
   return false;
 }
 
 exports.JsonViewService = {
   register: register,
   unregister: unregister
--- a/devtools/client/jsonview/converter-sniffer.js
+++ b/devtools/client/jsonview/converter-sniffer.js
@@ -1,63 +1,61 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {Cc, Ci, Cu, Cm, Cr, components} = require("chrome");
-const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
-const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
+const {Cc, Ci, components} = require("chrome");
+const xpcom = require("sdk/platform/xpcom");
+const {Unknown} = require("sdk/platform/xpcom");
+const {Class} = require("sdk/core/heritage");
 
 const categoryManager = Cc["@mozilla.org/categorymanager;1"]
   .getService(Ci.nsICategoryManager);
 
+loader.lazyRequireGetter(this, "NetworkHelper",
+  "devtools/shared/webconsole/network-helper");
+
 // Constants
 const JSON_TYPE = "application/json";
 const CONTRACT_ID = "@mozilla.org/devtools/jsonview-sniffer;1";
-const CLASS_ID = components.ID("{4148c488-dca1-49fc-a621-2a0097a62422}");
-const CLASS_DESCRIPTION = "JSONView content sniffer";
+const CLASS_ID = "{4148c488-dca1-49fc-a621-2a0097a62422}";
 const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
 const JSON_VIEW_TYPE = "JSON View";
 const CONTENT_SNIFFER_CATEGORY = "net-content-sniffers";
 
-function isTopLevelLoad(request) {
-  let loadInfo = request.loadInfo;
-  if (loadInfo && loadInfo.isTopLevelLoad) {
-    return (request.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI);
-  }
-  return false;
-}
-
 /**
  * This component represents a sniffer (implements nsIContentSniffer
  * interface) responsible for changing top level 'application/json'
  * document types to: 'application/vnd.mozilla.json.view'.
  *
  * This internal type is consequently rendered by JSON View component
  * that represents the JSON through a viewer interface.
  */
-function Sniffer() {}
+var Sniffer = Class({
+  extends: Unknown,
 
-Sniffer.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentSniffer]),
+  interfaces: [
+    "nsIContentSniffer",
+  ],
 
   get wrappedJSObject() {
     return this;
   },
 
   getMIMETypeFromContent: function (request, data, length) {
+    // JSON View is enabled only for top level loads only.
+    if (!NetworkHelper.isTopLevelLoad(request)) {
+      return "";
+    }
+
     if (request instanceof Ci.nsIChannel) {
-      // JSON View is enabled only for top level loads only.
-      if (!isTopLevelLoad(request)) {
-        return "";
-      }
       try {
         if (request.contentDisposition ==
           Ci.nsIChannel.DISPOSITION_ATTACHMENT) {
           return "";
         }
       } catch (e) {
         // Channel doesn't support content dispositions
       }
@@ -66,46 +64,42 @@ Sniffer.prototype = {
       // change it to new internal type consumed by JSON View.
       if (request.contentType == JSON_TYPE) {
         return JSON_VIEW_MIME_TYPE;
       }
     }
 
     return "";
   }
-};
+});
 
-const Factory = {
-  createInstance: function (outer, iid) {
-    if (outer) {
-      throw Cr.NS_ERROR_NO_AGGREGATION;
-    }
-    return new Sniffer();
-  }
-};
+var service = xpcom.Service({
+  id: components.ID(CLASS_ID),
+  contract: CONTRACT_ID,
+  Component: Sniffer,
+  register: false,
+  unregister: false
+});
 
 function register() {
-  if (!registrar.isCIDRegistered(CLASS_ID)) {
-    registrar.registerFactory(CLASS_ID,
-      CLASS_DESCRIPTION,
-      CONTRACT_ID,
-      Factory);
+  if (!xpcom.isRegistered(service)) {
+    xpcom.register(service);
     categoryManager.addCategoryEntry(CONTENT_SNIFFER_CATEGORY, JSON_VIEW_TYPE,
       CONTRACT_ID, false, false);
     return true;
   }
 
   return false;
 }
 
 function unregister() {
-  if (registrar.isCIDRegistered(CLASS_ID)) {
-    registrar.unregisterFactory(CLASS_ID, Factory);
+  if (xpcom.isRegistered(service)) {
     categoryManager.deleteCategoryEntry(CONTENT_SNIFFER_CATEGORY,
       JSON_VIEW_TYPE, false);
+    xpcom.unregister(service);
     return true;
   }
   return false;
 }
 
 exports.JsonViewSniffer = {
   register: register,
   unregister: unregister