Bug 1306892 - Fetch stylesheets from network monitor. r=tromey
authorJ. Ryan Stinnett <jryans@gmail.com>
Fri, 05 Jan 2018 18:44:25 -0600
changeset 449954 e188cb9fe8f5f3e73e06a13cdebde190b142d432
parent 449953 c2e1ad141fd7e257b4a099812acdfdcf576afa0c
child 449955 cd0a659c02637176c21325b9edfcc528b3640cca
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstromey
bugs1306892
milestone59.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 1306892 - Fetch stylesheets from network monitor. r=tromey If the toolbox is open when a stylesheet is loaded, the network monitor should have recorded the response content. When a tool asks for stylesheet text, try asking the network monitor first before falling back to an extra fetch as a last resort. MozReview-Commit-ID: E2pQ04ARfQo
devtools/client/styleeditor/test/browser_styleeditor_fetch-from-cache.js
devtools/server/actors/stylesheets.js
devtools/server/actors/webconsole.js
--- a/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-cache.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-cache.js
@@ -1,11 +1,11 @@
-/* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+
 "use strict";
 
 // A test to ensure Style Editor doesn't bybass cache when loading style sheet
 // contents (bug 978688).
 
 const TEST_URL = TEST_BASE_HTTP + "doc_uncached.html";
 
 add_task(function* () {
@@ -37,13 +37,11 @@ add_task(function* () {
   info("Checking Netmonitor contents.");
   let items = [];
   for (let item of getSortedRequests(store.getState())) {
     if (item.url.endsWith("doc_uncached.css")) {
       items.push(item);
     }
   }
 
-  is(items.length, 2,
-     "Got two requests for doc_uncached.css after Style Editor was loaded.");
-  ok(items[1].fromCache,
-     "Second request was loaded from browser cache");
+  is(items.length, 1,
+     "Got one request for doc_uncached.css after Style Editor was loaded.");
 });
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -418,16 +418,22 @@ var StyleSheetActor = protocol.ActorClas
    *         The href of the stylesheet to retrieve.
    * @return {Promise} a promise that resolves with an object with the following members
    *         on success:
    *           - content: the document at that URL, as a string,
    *           - contentType: the content type of the document
    *         If an error occurs, the promise is rejected with that error.
    */
   fetchStylesheet: Task.async(function* (href) {
+    // Check if network monitor observed this load, and if so, use that.
+    let result = this.fetchStylesheetFromNetworkMonitor(href);
+    if (result) {
+      return result;
+    }
+
     let options = {
       loadFromCache: true,
       policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
       charset: this._getCSSCharset()
     };
 
     // Bug 1282660 - We use the system principal to load the default internal
     // stylesheets instead of the content principal since such stylesheets
@@ -437,33 +443,69 @@ var StyleSheetActor = protocol.ActorClas
     // chrome|file|resource|moz-extension protocols rely on the system principal.
     let excludedProtocolsRe = /^(chrome|file|resource|moz-extension):\/\//;
     if (!excludedProtocolsRe.test(this.href)) {
       // Stylesheets using other protocols should use the content principal.
       options.window = this.ownerWindow;
       options.principal = this.ownerDocument.nodePrincipal;
     }
 
-    let result;
     try {
       result = yield fetch(this.href, options);
     } catch (e) {
       // The list of excluded protocols can be missing some protocols, try to use the
       // system principal if the first fetch failed.
       console.error(`stylesheets actor: fetch failed for ${this.href},` +
         ` using system principal instead.`);
       options.window = undefined;
       options.principal = undefined;
       result = yield fetch(this.href, options);
     }
 
     return result;
   }),
 
   /**
+   * Try to locate the console actor if it exists via our parent actor (the tab).
+   */
+  get _consoleActor() {
+    if (this.parentActor.exited) {
+      return null;
+    }
+    let form = this.parentActor.form();
+    return this.conn._getOrCreateActor(form.consoleActor);
+  },
+
+  /**
+   * Try to fetch the stylesheet text from the network monitor.  If it was enabled during
+   * the load, it should have a copy of the text saved.
+   *
+   * @param string href
+   *        The URL of the sheet to fetch.
+   */
+  fetchStylesheetFromNetworkMonitor(href) {
+    let consoleActor = this._consoleActor;
+    if (!consoleActor) {
+      return null;
+    }
+    let request = consoleActor.getNetworkEventActorForURL(href);
+    if (!request) {
+      return null;
+    }
+    let content = request._response.content;
+    if (request._discardResponseBody || !content) {
+      return null;
+    }
+    return {
+      content: content.text,
+      contentType: content.mimeType,
+    };
+  },
+
+  /**
    * Protocol method to get the media rules for the stylesheet.
    */
   getMediaRules: function () {
     return this._getMediaRules();
   },
 
   /**
    * Get all the @media rules in this stylesheet.
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -63,16 +63,17 @@ function WebConsoleActor(connection, par
   this._actorPool = new ActorPool(this.conn);
   this.conn.addActorPool(this._actorPool);
 
   this._prefs = {};
 
   this.dbg = this.parentActor.makeDebugger();
 
   this._netEvents = new Map();
+  this._networkEventActorsByURL = new Map();
   this._gripDepth = 0;
   this._listeners = new Set();
   this._lastConsoleInputEvaluation = undefined;
 
   this.objectGrip = this.objectGrip.bind(this);
   this._onWillNavigate = this._onWillNavigate.bind(this);
   this._onChangedToplevelDocument = this._onChangedToplevelDocument.bind(this);
   EventEmitter.on(this.parentActor, "changed-toplevel-document",
@@ -119,24 +120,34 @@ WebConsoleActor.prototype =
    * Web Console-related preferences.
    * @private
    * @type object
    */
   _prefs: null,
 
   /**
    * Holds a map between nsIChannel objects and NetworkEventActors for requests
-   * created with sendHTTPRequest.
+   * created with sendHTTPRequest or found via the network listener.
    *
    * @private
    * @type Map
    */
   _netEvents: null,
 
   /**
+   * Holds a map from URL to NetworkEventActors for requests noticed by the network
+   * listener.  Requests are added when they start, so the actor might not yet have all
+   * data for the request until it has completed.
+   *
+   * @private
+   * @type Map
+   */
+  _networkEventActorsByURL: null,
+
+  /**
    * Holds a set of all currently registered listeners.
    *
    * @private
    * @type Set
    */
   _listeners: null,
 
   /**
@@ -1627,16 +1638,18 @@ WebConsoleActor.prototype =
    * @return object
    *         A new NetworkEventActor is returned. This is used for tracking the
    *         network request and response.
    */
   onNetworkEvent: function (event) {
     let actor = this.getNetworkEventActor(event.channelId);
     actor.init(event);
 
+    this._networkEventActorsByURL.set(actor._request.url, actor);
+
     let packet = {
       from: this.actorID,
       type: "networkEvent",
       eventActor: actor.grip()
     };
 
     this.conn.send(packet);
 
@@ -1661,16 +1674,28 @@ WebConsoleActor.prototype =
     }
 
     actor = new NetworkEventActor(this);
     this._actorPool.addActor(actor);
     return actor;
   },
 
   /**
+   * Get the NetworkEventActor for a given URL that may have been noticed by the network
+   * listener.  Requests are added when they start, so the actor might not yet have all
+   * data for the request until it has completed.
+   *
+   * @param string url
+   *        The URL of the request to search for.
+   */
+  getNetworkEventActorForURL(url) {
+    return this._networkEventActorsByURL.get(url);
+  },
+
+  /**
    * Send a new HTTP request from the target's window.
    *
    * @param object message
    *        Object with 'request' - the HTTP request details.
    */
   onSendHTTPRequest(message) {
     let { url, method, headers, body } = message.request;
 
@@ -1987,16 +2012,19 @@ NetworkEventActor.prototype =
     for (let grip of this._longStringActors) {
       let actor = this.parent.getActorByID(grip.actor);
       if (actor) {
         this.parent.releaseActor(actor);
       }
     }
     this._longStringActors = new Set();
 
+    if (this._request.url) {
+      this.parent._networkEventActorsByURL.delete(this._request.url);
+    }
     if (this.channel) {
       this.parent._netEvents.delete(this.channel);
     }
     this.parent.releaseActor(this);
   },
 
   /**
    * Handle a protocol request to release a grip.