Bug 819945 - Global searches choke when a source takes too long to fetch, r=past
authorVictor Porof <vporof@mozilla.com>
Thu, 20 Dec 2012 20:00:34 +0200
changeset 116656 3cc5ceca28af40d6b10bfa0489d46e7876d06b4c
parent 116655 4b7b934b358a3e02402371877443653ff910781a
child 116657 4d709ea729b30e2e6a72dc35c3fe168fcabb819c
push idunknown
push userunknown
push dateunknown
reviewerspast
bugs819945
milestone20.0a1
Bug 819945 - Global searches choke when a source takes too long to fetch, r=past
browser/devtools/debugger/debugger-controller.js
browser/devtools/debugger/debugger-panes.js
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -6,16 +6,17 @@
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
 const NEW_SCRIPT_DISPLAY_DELAY = 200; // ms
+const FETCH_SOURCE_RESPONSE_DELAY = 50; // ms
 const FRAME_STEP_CLEAR_DELAY = 100; // ms
 const CALL_STACK_PAGE_SIZE = 25; // frames
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 Cu.import("resource:///modules/source-editor.jsm");
@@ -1173,26 +1174,36 @@ SourceScripts.prototype = {
 
   /**
    * Gets a specified source's text.
    *
    * @param object aSource
    *        The source object coming from the active thread.
    * @param function aCallback
    *        Function called after the source text has been loaded.
+   * @param function aOnTimeout
+   *        Function called when the source text takes too long to fetch.
    */
-  getText: function SS_getText(aSource, aCallback) {
+  getText: function SS_getText(aSource, aCallback, aOnTimeout) {
     // If already loaded, return the source text immediately.
     if (aSource.loaded) {
       aCallback(aSource.url, aSource.text);
       return;
     }
 
+    // If the source text takes too long to fetch, invoke a timeout to
+    // avoid blocking any operations.
+    if (aOnTimeout) {
+      var fetchTimeout = window.setTimeout(aOnTimeout, FETCH_SOURCE_RESPONSE_DELAY);
+    }
+
     // Get the source text from the active thread.
     this.activeThread.source(aSource.source).source(function(aResponse) {
+      window.clearTimeout(fetchTimeout);
+
       if (aResponse.error) {
         Cu.reportError("Error loading " + aSource.url + "\n" + aResponse.error);
         aCallback(aSource.url, "");
         return;
       }
       aSource.loaded = true;
       aSource.text = aResponse.source;
       aCallback(aSource.url, aResponse.source);
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -1255,16 +1255,17 @@ create({ constructor: WatchExpressionsVi
 /**
  * Functions handling the global search UI.
  */
 function GlobalSearchView() {
   dumpn("GlobalSearchView was instantiated");
   MenuContainer.call(this);
   this._startSearch = this._startSearch.bind(this);
   this._onFetchSourceFinished = this._onFetchSourceFinished.bind(this);
+  this._onFetchSourceTimeout = this._onFetchSourceTimeout.bind(this);
   this._onFetchSourcesFinished = this._onFetchSourcesFinished.bind(this);
   this._createItemView = this._createItemView.bind(this);
   this._onScroll = this._onScroll.bind(this);
   this._onHeaderClick = this._onHeaderClick.bind(this);
   this._onLineClick = this._onLineClick.bind(this);
   this._onMatchClick = this._onMatchClick.bind(this);
 }
 
@@ -1397,45 +1398,50 @@ create({ constructor: GlobalSearchView, 
    * @param string aQuery
    *        The string to search for.
    */
   _startSearch: function DVGS__startSearch(aQuery) {
     let locations = DebuggerView.Sources.values;
     this._sourcesCount = locations.length;
     this._searchedToken = aQuery;
 
-    this._fetchSources(
-      this._onFetchSourceFinished,
-      this._onFetchSourcesFinished, locations);
+    this._fetchSources(locations, {
+      onFetch: this._onFetchSourceFinished,
+      onTimeout: this._onFetchSourceTimeout,
+      onFinished: this._onFetchSourcesFinished
+    });
   },
 
   /**
    * Starts fetching all the sources, silently.
    *
-   * @param function aFetchCallback
-   *        Called after each source is fetched.
-   * @param function aFetchedCallback
-   *        Called if all the sources were already fetched.
    * @param array aLocations
    *        The locations for the sources to fetch.
+   * @param object aCallbacks
+   *        An object containing the callback functions to invoke:
+   *          - onFetch: called after each source is fetched
+   *          - onTimeout: called when a source's text takes too long to fetch
+   *          - onFinished: called if all the sources were already fetched
    */
-  _fetchSources: function DVGS__fetchSources(aFetchCallback, aFetchedCallback, aLocations) {
+  _fetchSources:
+  function DVGS__fetchSources(aLocations, { onFetch, onTimeout, onFinished }) {
     // If all the sources were already fetched, then don't do anything.
     if (this._cache.size == aLocations.length) {
-      aFetchedCallback();
+      onFinished();
       return;
     }
 
     // Fetch each new source.
     for (let location of aLocations) {
       if (this._cache.has(location)) {
         continue;
       }
       let sourceItem = DebuggerView.Sources.getItemByValue(location);
-      DebuggerController.SourceScripts.getText(sourceItem.attachment, aFetchCallback);
+      let sourceObject = sourceItem.attachment;
+      DebuggerController.SourceScripts.getText(sourceObject, onFetch, onTimeout);
     }
   },
 
   /**
    * Called when a source has been fetched.
    *
    * @param string aLocation
    *        The location of the source.
@@ -1448,19 +1454,33 @@ create({ constructor: GlobalSearchView, 
 
     // Check if all sources were fetched and stored in the cache.
     if (this._cache.size == this._sourcesCount) {
       this._onFetchSourcesFinished();
     }
   },
 
   /**
+   * Called when a source's text takes too long to fetch.
+   */
+  _onFetchSourceTimeout: function DVGS__onFetchSourceTimeout() {
+    // Remove the source from the load queue.
+    this._sourcesCount--;
+
+    // Check if the remaining sources were fetched and stored in the cache.
+    if (this._cache.size == this._sourcesCount) {
+      this._onFetchSourcesFinished();
+    }
+  },
+
+  /**
    * Called when all the sources have been fetched.
    */
   _onFetchSourcesFinished: function DVGS__onFetchSourcesFinished() {
+    // At least one source needs to be present to perform a global search.
     if (!this._sourcesCount) {
       return;
     }
     // All sources are fetched and stored in the cache, we can start searching.
     this._performGlobalSearch();
     this._sourcesCount = 0;
   },