toolkit/components/thumbnails/content/backgroundPageThumbsContent.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,171 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +(function () { // bug 673569 workaround :(
     1.9 +
    1.10 +const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
    1.11 +
    1.12 +Cu.import("resource://gre/modules/PageThumbs.jsm");
    1.13 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.14 +Cu.import("resource://gre/modules/Services.jsm");
    1.15 +
    1.16 +const STATE_LOADING = 1;
    1.17 +const STATE_CAPTURING = 2;
    1.18 +const STATE_CANCELED = 3;
    1.19 +
    1.20 +const backgroundPageThumbsContent = {
    1.21 +
    1.22 +  init: function () {
    1.23 +    Services.obs.addObserver(this, "document-element-inserted", true);
    1.24 +
    1.25 +    // We want a low network priority for this service - lower than b/g tabs
    1.26 +    // etc - so set it to the lowest priority available.
    1.27 +    this._webNav.QueryInterface(Ci.nsIDocumentLoader).
    1.28 +      loadGroup.QueryInterface(Ci.nsISupportsPriority).
    1.29 +      priority = Ci.nsISupportsPriority.PRIORITY_LOWEST;
    1.30 +
    1.31 +    docShell.allowMedia = false;
    1.32 +    docShell.allowPlugins = false;
    1.33 +    docShell.allowContentRetargeting = false;
    1.34 +    let defaultFlags = Ci.nsIRequest.LOAD_ANONYMOUS |
    1.35 +                       Ci.nsIRequest.LOAD_BYPASS_CACHE |
    1.36 +                       Ci.nsIRequest.INHIBIT_CACHING |
    1.37 +                       Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY;
    1.38 +    docShell.defaultLoadFlags = defaultFlags;
    1.39 +
    1.40 +    addMessageListener("BackgroundPageThumbs:capture",
    1.41 +                       this._onCapture.bind(this));
    1.42 +    docShell.
    1.43 +      QueryInterface(Ci.nsIInterfaceRequestor).
    1.44 +      getInterface(Ci.nsIWebProgress).
    1.45 +      addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
    1.46 +  },
    1.47 +
    1.48 +  observe: function (subj, topic, data) {
    1.49 +    // Arrange to prevent (most) popup dialogs for this window - popups done
    1.50 +    // in the parent (eg, auth) aren't prevented, but alert() etc are.
    1.51 +    // disableDialogs only works on the current inner window, so it has
    1.52 +    // to be called every page load, but before scripts run.
    1.53 +    if (subj == content.document) {
    1.54 +      content.
    1.55 +        QueryInterface(Ci.nsIInterfaceRequestor).
    1.56 +        getInterface(Ci.nsIDOMWindowUtils).
    1.57 +        disableDialogs();
    1.58 +    }
    1.59 +  },
    1.60 +
    1.61 +  get _webNav() {
    1.62 +    return docShell.QueryInterface(Ci.nsIWebNavigation);
    1.63 +  },
    1.64 +
    1.65 +  _onCapture: function (msg) {
    1.66 +    this._nextCapture = {
    1.67 +      id: msg.data.id,
    1.68 +      url: msg.data.url,
    1.69 +    };
    1.70 +    if (this._currentCapture) {
    1.71 +      if (this._state == STATE_LOADING) {
    1.72 +        // Cancel the current capture.
    1.73 +        this._state = STATE_CANCELED;
    1.74 +        this._loadAboutBlank();
    1.75 +      }
    1.76 +      // Let the current capture finish capturing, or if it was just canceled,
    1.77 +      // wait for onStateChange due to the about:blank load.
    1.78 +      return;
    1.79 +    }
    1.80 +    this._startNextCapture();
    1.81 +  },
    1.82 +
    1.83 +  _startNextCapture: function () {
    1.84 +    if (!this._nextCapture)
    1.85 +      return;
    1.86 +    this._currentCapture = this._nextCapture;
    1.87 +    delete this._nextCapture;
    1.88 +    this._state = STATE_LOADING;
    1.89 +    this._currentCapture.pageLoadStartDate = new Date();
    1.90 +    this._webNav.loadURI(this._currentCapture.url,
    1.91 +                         Ci.nsIWebNavigation.LOAD_FLAGS_STOP_CONTENT,
    1.92 +                         null, null, null);
    1.93 +  },
    1.94 +
    1.95 +  onStateChange: function (webProgress, req, flags, status) {
    1.96 +    if (webProgress.isTopLevel &&
    1.97 +        (flags & Ci.nsIWebProgressListener.STATE_STOP) &&
    1.98 +        this._currentCapture) {
    1.99 +      if (req.name == "about:blank") {
   1.100 +        if (this._state == STATE_CAPTURING) {
   1.101 +          // about:blank has loaded, ending the current capture.
   1.102 +          this._finishCurrentCapture();
   1.103 +          delete this._currentCapture;
   1.104 +          this._startNextCapture();
   1.105 +        }
   1.106 +        else if (this._state == STATE_CANCELED) {
   1.107 +          // A capture request was received while the current capture's page
   1.108 +          // was still loading.
   1.109 +          delete this._currentCapture;
   1.110 +          this._startNextCapture();
   1.111 +        }
   1.112 +      }
   1.113 +      else if (this._state == STATE_LOADING) {
   1.114 +        // The requested page has loaded.  Capture it.
   1.115 +        this._state = STATE_CAPTURING;
   1.116 +        this._captureCurrentPage();
   1.117 +      }
   1.118 +    }
   1.119 +  },
   1.120 +
   1.121 +  _captureCurrentPage: function () {
   1.122 +    let capture = this._currentCapture;
   1.123 +    capture.finalURL = this._webNav.currentURI.spec;
   1.124 +    capture.pageLoadTime = new Date() - capture.pageLoadStartDate;
   1.125 +
   1.126 +    let canvas = PageThumbs._createCanvas(content);
   1.127 +    let canvasDrawDate = new Date();
   1.128 +    PageThumbs._captureToCanvas(content, canvas);
   1.129 +    capture.canvasDrawTime = new Date() - canvasDrawDate;
   1.130 +
   1.131 +    canvas.toBlob(blob => {
   1.132 +      capture.imageBlob = blob;
   1.133 +      // Load about:blank to finish the capture and wait for onStateChange.
   1.134 +      this._loadAboutBlank();
   1.135 +    });
   1.136 +  },
   1.137 +
   1.138 +  _finishCurrentCapture: function () {
   1.139 +    let capture = this._currentCapture;
   1.140 +    let fileReader = Cc["@mozilla.org/files/filereader;1"].
   1.141 +                     createInstance(Ci.nsIDOMFileReader);
   1.142 +    fileReader.onloadend = () => {
   1.143 +      sendAsyncMessage("BackgroundPageThumbs:didCapture", {
   1.144 +        id: capture.id,
   1.145 +        imageData: fileReader.result,
   1.146 +        finalURL: capture.finalURL,
   1.147 +        telemetry: {
   1.148 +          CAPTURE_PAGE_LOAD_TIME_MS: capture.pageLoadTime,
   1.149 +          CAPTURE_CANVAS_DRAW_TIME_MS: capture.canvasDrawTime,
   1.150 +        },
   1.151 +      });
   1.152 +    };
   1.153 +    fileReader.readAsArrayBuffer(capture.imageBlob);
   1.154 +  },
   1.155 +
   1.156 +  // We load about:blank to finish all captures, even canceled captures.  Two
   1.157 +  // reasons: GC the captured page, and ensure it can't possibly load any more
   1.158 +  // resources.
   1.159 +  _loadAboutBlank: function _loadAboutBlank() {
   1.160 +    this._webNav.loadURI("about:blank",
   1.161 +                         Ci.nsIWebNavigation.LOAD_FLAGS_STOP_CONTENT,
   1.162 +                         null, null, null);
   1.163 +  },
   1.164 +
   1.165 +  QueryInterface: XPCOMUtils.generateQI([
   1.166 +    Ci.nsIWebProgressListener,
   1.167 +    Ci.nsISupportsWeakReference,
   1.168 +    Ci.nsIObserver,
   1.169 +  ]),
   1.170 +};
   1.171 +
   1.172 +backgroundPageThumbsContent.init();
   1.173 +
   1.174 +})();

mercurial