Bug 868321 - Sites with no stylesheets just show loading indicator forever; r=dcamp
authorHeather Arthur <fayearthur@gmail.com>
Tue, 07 May 2013 15:49:52 -0700
changeset 142489 ea2b0db82289f2f62d9773a61161cffbdd912a2e
parent 142488 51d6aba6b9d7a65ed41b82028532bb5c3cbe66af
child 142490 b3a6bee35493d41d82c4159f5be649b83de3e7b5
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdcamp
bugs868321
milestone23.0a1
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
Bug 868321 - Sites with no stylesheets just show loading indicator forever; r=dcamp
browser/devtools/styleeditor/StyleEditorDebuggee.jsm
browser/devtools/styleeditor/StyleEditorPanel.jsm
browser/devtools/styleeditor/StyleEditorUI.jsm
browser/devtools/styleeditor/test/Makefile.in
browser/devtools/styleeditor/test/browser_styleeditor_nostyle.js
browser/devtools/styleeditor/test/nostyle.html
toolkit/devtools/debugger/dbg-client.jsm
toolkit/devtools/styleeditor/dbg-styleeditor-actors.js
--- a/browser/devtools/styleeditor/StyleEditorDebuggee.jsm
+++ b/browser/devtools/styleeditor/StyleEditorDebuggee.jsm
@@ -18,36 +18,36 @@ XPCOMUtils.defineLazyModuleGetter(this, 
     "resource://gre/modules/commonjs/sdk/core/promise.js");
 
 /**
  * A StyleEditorDebuggee represents the document the style editor is debugging.
  * It maintains a list of StyleSheet objects that represent the stylesheets in
  * the target's document. It wraps remote debugging protocol comunications.
  *
  * It emits these events:
- *   'stylesheet-added': A stylesheet has been added to the debuggee's document
+ *   'document-load': debuggee's document is loaded, style sheets are argument
  *   'stylesheets-cleared': The debuggee's stylesheets have been reset (e.g. the
  *                          page navigated)
  *
  * @param {Target} target
  *         The target the debuggee is listening to
  */
 let StyleEditorDebuggee = function(target) {
   EventEmitter.decorate(this);
 
   this.styleSheets = [];
 
   this.clear = this.clear.bind(this);
   this._onNewDocument = this._onNewDocument.bind(this);
-  this._onStyleSheetsAdded = this._onStyleSheetsAdded.bind(this);
+  this._onDocumentLoad = this._onDocumentLoad.bind(this);
 
   this._target = target;
   this._actor = this.target.form.styleEditorActor;
 
-  this.client.addListener("styleSheetsAdded", this._onStyleSheetsAdded);
+  this.client.addListener("documentLoad", this._onDocumentLoad);
   this._target.on("navigate", this._onNewDocument);
 
   this._onNewDocument();
 }
 
 StyleEditorDebuggee.prototype = {
   /**
    * list of StyleSheet objects for this target
@@ -123,28 +123,31 @@ StyleEditorDebuggee.prototype = {
   _getBaseURI: function() {
     let message = { type: "getBaseURI" };
     this._sendRequest(message, (response) => {
       this.baseURI = response.baseURI;
     });
   },
 
   /**
-   * Handle stylesheet-added event from the target
+   * Handler for document load, forward event with
+   * all the stylesheets available on load.
    *
-   * @param {string} type
-   *        Type of event
-   * @param {object} request
-   *        Event details
+   * @param  {string} type
+   *         Event type
+   * @param  {object} request
+   *         Object with 'styleSheets' array of actor forms
    */
-  _onStyleSheetsAdded: function(type, request) {
+  _onDocumentLoad: function(type, request) {
+    let sheets = [];
     for (let form of request.styleSheets) {
       let sheet = this._addStyleSheet(form);
-      this.emit("stylesheet-added", sheet);
+      sheets.push(sheet);
     }
+    this.emit("document-load", sheets);
   },
 
   /**
    * Create a new StyleSheet object from the form
    * and add to our stylesheet list.
    *
    * @param {object} form
    *        Initial properties of the stylesheet
@@ -186,17 +189,16 @@ StyleEditorDebuggee.prototype = {
   },
 
   /**
    * Clean up and remove listeners
    */
   destroy: function() {
     this.clear();
 
-    this._target.off("will-navigate", this.clear);
     this._target.off("navigate", this._onNewDocument);
   }
 }
 
 /**
  * A StyleSheet object represents a stylesheet on the debuggee. It wraps
  * communication with a complimentary StyleSheetActor on the server.
  *
--- a/browser/devtools/styleeditor/StyleEditorPanel.jsm
+++ b/browser/devtools/styleeditor/StyleEditorPanel.jsm
@@ -105,17 +105,16 @@ StyleEditorPanel.prototype = {
 
   /**
    * Destroy the style editor.
    */
   destroy: function() {
     if (!this._destroyed) {
       this._destroyed = true;
 
-      this._target.off("will-navigate", this.beforeNavigate);
       this._target.off("close", this.destroy);
       this._target = null;
       this._toolbox = null;
       this._panelDoc = null;
 
       this._debuggee.destroy();
       this.UI.destroy();
     }
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -44,22 +44,22 @@ function StyleEditorUI(debuggee, panelDo
   this._debuggee = debuggee;
   this._panelDoc = panelDoc;
   this._window = this._panelDoc.defaultView;
   this._root = this._panelDoc.getElementById("style-editor-chrome");
 
   this.editors = [];
   this.selectedStyleSheetIndex = -1;
 
-  this._onStyleSheetAdded = this._onStyleSheetAdded.bind(this);
   this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this);
   this._onStyleSheetsCleared = this._onStyleSheetsCleared.bind(this);
+  this._onDocumentLoad = this._onDocumentLoad.bind(this);
   this._onError = this._onError.bind(this);
 
-  debuggee.on("stylesheet-added", this._onStyleSheetAdded);
+  debuggee.on("document-load", this._onDocumentLoad);
   debuggee.on("stylesheets-cleared", this._onStyleSheetsCleared);
 
   this.createUI();
 }
 
 StyleEditorUI.prototype = {
   /**
    * Get whether any of the editors have unsaved changes.
@@ -151,27 +151,31 @@ StyleEditorUI.prototype = {
    * When a new or imported stylesheet has been added to the document.
    * Add an editor for it.
    */
   _onStyleSheetCreated: function(styleSheet, file) {
     this._addStyleSheetEditor(styleSheet, file, true);
   },
 
   /**
-   * Handler for debuggee's 'stylesheet-added' event. Add an editor.
+   * Handler for debuggee's 'document-load' event. Add editors
+   * for all style sheets in the document
    *
    * @param {string} event
    *        Event name
    * @param {StyleSheet} styleSheet
    *        StyleSheet object for new sheet
    */
-  _onStyleSheetAdded: function(event, styleSheet) {
+  _onDocumentLoad: function(event, styleSheets) {
+    for (let sheet of styleSheets) {
+      this._addStyleSheetEditor(sheet);
+    }
     // this might be the first stylesheet, so remove loading indicator
     this._root.classList.remove("loading");
-    this._addStyleSheetEditor(styleSheet);
+    this.emit("document-load");
   },
 
   /**
    * Forward any error from a stylesheet.
    *
    * @param  {string} event
    *         Event name
    * @param  {string} errorCode
@@ -416,12 +420,12 @@ StyleEditorUI.prototype = {
     text(summary, ".stylesheet-rule-count",
       PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount));
     text(summary, ".stylesheet-error-message", editor.errorMessage);
   },
 
   destroy: function() {
     this._clearStyleSheetEditors();
 
-    this._debuggee.off("stylesheet-added", this._onStyleSheetAdded);
+    this._debuggee.off("document-load", this._onDocumentLoad);
     this._debuggee.off("stylesheets-cleared", this._onStyleSheetsCleared);
   }
 }
--- a/browser/devtools/styleeditor/test/Makefile.in
+++ b/browser/devtools/styleeditor/test/Makefile.in
@@ -22,28 +22,30 @@ include $(topsrcdir)/config/rules.mk
                  browser_styleeditor_loading.js \
                  browser_styleeditor_new.js \
                  browser_styleeditor_pretty.js \
                  browser_styleeditor_private_perwindowpb.js \
                  browser_styleeditor_sv_keynav.js \
                  browser_styleeditor_sv_resize.js \
                  browser_styleeditor_bug_740541_iframes.js \
                  browser_styleeditor_bug_851132_middle_click.js \
+                 browser_styleeditor_nostyle.js \
                  head.js \
                  helpers.js \
                  four.html \
                  head.js \
                  helpers.js \
                  import.css \
                  import.html \
                  import2.css \
                  longload.html \
                  media.html \
                  media-small.css \
                  minified.html \
+                 nostyle.html \
                  resources_inpage.jsi \
                  resources_inpage1.css \
                  resources_inpage2.css \
                  simple.css \
                  simple.css.gz \
                  simple.css.gz^headers^ \
                  simple.gz.html \
                  simple.html \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_nostyle.js
@@ -0,0 +1,41 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "nostyle.html";
+
+
+function test()
+{
+  waitForExplicitFinish();
+
+  // launch Style Editor right when the tab is created (before load)
+  // this checks that the Style Editor still launches correctly when it is opened
+  // *while* the page is still loading. The Style Editor should not signal that
+  // it is loaded until the accompanying content page is loaded.
+
+  addTabAndOpenStyleEditor(function(panel) {
+    panel.UI.once("document-load", testDocumentLoad);
+
+    content.location = TESTCASE_URI;
+  });
+}
+
+function testDocumentLoad(event)
+{
+  let root = gPanelWindow.document.querySelector(".splitview-root");
+  ok(!root.classList.contains("loading"),
+     "style editor root element does not have 'loading' class name anymore");
+
+  ok(root.querySelector(".empty.placeholder"), "showing 'no style' indicator");
+
+  let button = gPanelWindow.document.querySelector(".style-editor-newButton");
+  ok(!button.hasAttribute("disabled"),
+     "new style sheet button is enabled");
+
+  button = gPanelWindow.document.querySelector(".style-editor-importButton");
+  ok(!button.hasAttribute("disabled"),
+     "import button is enabled");
+
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/nostyle.html
@@ -0,0 +1,5 @@
+<html>
+  <div>
+    Page with no stylesheets
+  </div>
+</html>
\ No newline at end of file
--- a/toolkit/devtools/debugger/dbg-client.jsm
+++ b/toolkit/devtools/debugger/dbg-client.jsm
@@ -178,17 +178,17 @@ const UnsolicitedNotifications = {
   "networkEventUpdate": "networkEventUpdate",
   "newGlobal": "newGlobal",
   "newScript": "newScript",
   "newSource": "newSource",
   "tabDetached": "tabDetached",
   "tabNavigated": "tabNavigated",
   "pageError": "pageError",
   "webappsEvent": "webappsEvent",
-  "styleSheetsAdded": "styleSheetsAdded"
+  "documentLoad": "documentLoad"
 };
 
 /**
  * Set of pause types that are sent by the server and not as an immediate
  * response to a client request.
  */
 const UnsolicitedPauses = {
   "resumeLimit": "resumeLimit",
--- a/toolkit/devtools/styleeditor/dbg-styleeditor-actors.js
+++ b/toolkit/devtools/styleeditor/dbg-styleeditor-actors.js
@@ -138,101 +138,70 @@ StyleEditorActor.prototype = {
     }
     else {
       this.win.addEventListener("load", this._onDocumentLoaded, false);
     }
     return {};
   },
 
   /**
-   * Event handler for document loaded event.
+   * Event handler for document loaded event. Add actor for each stylesheet
+   * and send an event notifying of the load
    */
   _onDocumentLoaded: function(event) {
     if (event) {
       this.win.removeEventListener("load", this._onDocumentLoaded, false);
     }
 
     let documents = [this.doc];
+    var forms = [];
     for (let doc of documents) {
-      this._addStyleSheets(doc.styleSheets);
+      let sheetForms = this._addStyleSheets(doc.styleSheets);
+      forms = forms.concat(sheetForms);
       // Recursively handle style sheets of the documents in iframes.
       for (let iframe of doc.getElementsByTagName("iframe")) {
         documents.push(iframe.contentDocument);
       }
     }
-  },
 
-  /**
-   * Clear all the current stylesheet actors in map.
-   */
-  _clearStyleSheetActors: function() {
-    for (let actor in this._sheets) {
-      this.releaseActor(this._sheets[actor]);
-    }
-    this._sheets.clear();
-  },
-
-  /**
-   * Get the actors of all the stylesheets in the current document.
-   *
-   * @return {object} JSON message with the stylesheet actors' forms
-   */
-  onGetStyleSheets: function() {
-    let styleSheets = [];
-
-    for (let i = 0; i < this.doc.styleSheets.length; ++i) {
-      let styleSheet = this.doc.styleSheets[i];
-      let actor = this._createStyleSheetActor(styleSheet);
-      styleSheets.push(actor.form());
-    }
-
-    return { "styleSheets": styleSheets };
+    this.conn.send({
+      from: this.actorID,
+      type: "documentLoad",
+      styleSheets: forms
+    });
   },
 
   /**
    * Add all the stylesheets to the map and create an actor
    * for each one if not already created. Send event that there
    * are new stylesheets.
    *
    * @param {[DOMStyleSheet]} styleSheets
    *        Stylesheets to add
+   * @return {[object]}
+   *         Array of forms for each StyleSheetActor created
    */
   _addStyleSheets: function(styleSheets)
   {
     let sheets = [];
     for (let i = 0; i < styleSheets.length; i++) {
       let styleSheet = styleSheets[i];
       sheets.push(styleSheet);
 
       // Get all sheets, including imported ones
       let imports = this._getImported(styleSheet);
       sheets = sheets.concat(imports);
     }
 
-    let actors = sheets.map((sheet) => {
+    let forms = sheets.map((sheet) => {
       let actor = this._createStyleSheetActor(sheet);
       return actor.form();
     });
 
-    this._notifyStyleSheetsAdded(actors);
-  },
-
-  /**
-   * Send an event notifying that there are new style sheets
-   *
-   * @param  {[object]} actors
-   *         Forms of the new style sheet actors
-   */
-  _notifyStyleSheetsAdded: function(actors)
-  {
-    this.conn.send({
-      from: this.actorID,
-      type: "styleSheetsAdded",
-      styleSheets: actors
-    });
+    return forms;
   },
 
   /**
    * Get all the stylesheets @imported from a stylesheet.
    *
    * @param  {DOMStyleSheet} styleSheet
    *         Style sheet to search
    * @return {array}
@@ -278,16 +247,36 @@ StyleEditorActor.prototype = {
     }
     let actor = new StyleSheetActor(aStyleSheet, this);
     this._actorPool.addActor(actor);
     this._sheets.set(aStyleSheet, actor);
     return actor;
   },
 
   /**
+   * Clear all the current stylesheet actors in map.
+   */
+  _clearStyleSheetActors: function() {
+    for (let actor in this._sheets) {
+      this.releaseActor(this._sheets[actor]);
+    }
+    this._sheets.clear();
+  },
+
+  /**
+   * Get the actors of all the stylesheets in the current document.
+   *
+   * @return {object} JSON message with the stylesheet actors' forms
+   */
+  onGetStyleSheets: function() {
+    let forms = this._addStyleSheets(this.doc.styleSheets);
+    return { "styleSheets": forms };
+  },
+
+  /**
    * Handler for style sheet loading event. Add
    * a new actor for the sheet and notify.
    *
    * @param  {Event} event
    */
   _onSheetLoaded: function(event) {
     let style = event.target;
     style.removeEventListener("load", this._onSheetLoaded, false);