]> git.parisson.com Git - pdf.js.git/commitdiff
Use already downloaded data for the open with/save as dialog.
authorBrendan Dahl <brendan.dahl@gmail.com>
Fri, 1 Jun 2012 21:17:09 +0000 (14:17 -0700)
committerBrendan Dahl <brendan.dahl@gmail.com>
Fri, 1 Jun 2012 21:17:09 +0000 (14:17 -0700)
extensions/firefox/components/PdfStreamConverter.js
extensions/firefox/tools/l10n.js
src/api.js
src/worker.js
web/viewer.js

index 62d22162b7b903aede61a8363473a1913ff5c9b9..ff63ab52a9af47401743db2c953315ae1f656013 100644 (file)
@@ -104,11 +104,18 @@ function ChromeActions(domWindow) {
 }
 
 ChromeActions.prototype = {
-  download: function(data) {
+  download: function(data, sendResponse) {
+    var originalUrl = data.originalUrl;
+    // The data may not be downloaded so we need just retry getting the pdf with
+    // the original url.
+    var blobUrl = data.blobUrl || originalUrl;
+
+    var originalUri = NetUtil.newURI(originalUrl);
+    var blobUri = NetUtil.newURI(blobUrl);
+
     let mimeService = Cc['@mozilla.org/mime;1'].getService(Ci.nsIMIMEService);
     var handlerInfo = mimeService.
                         getFromTypeAndExtension('application/pdf', 'pdf');
-    var uri = NetUtil.newURI(data);
 
     var extHelperAppSvc =
           Cc['@mozilla.org/uriloader/external-helper-app-service;1'].
@@ -116,26 +123,46 @@ ChromeActions.prototype = {
     var frontWindow = Cc['@mozilla.org/embedcomp/window-watcher;1'].
                         getService(Ci.nsIWindowWatcher).activeWindow;
     var ioService = Services.io;
-    var channel = ioService.newChannel(data, null, null);
-    var listener = {
-      extListener: null,
-      onStartRequest: function(aRequest, aContext) {
-        this.extListener = extHelperAppSvc.doContent('application/pdf',
-                              aRequest, frontWindow, false);
-        this.extListener.onStartRequest(aRequest, aContext);
-      },
-      onStopRequest: function(aRequest, aContext, aStatusCode) {
-        if (this.extListener)
-          this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
-      },
-      onDataAvailable: function(aRequest, aContext, aInputStream, aOffset,
-                                aCount) {
-        this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
-                                         aOffset, aCount);
+    var channel = ioService.newChannel(originalUrl, null, null);
+
+
+    NetUtil.asyncFetch(blobUri, function(aInputStream, aResult) {
+      if (!Components.isSuccessCode(aResult)) {
+        if (sendResponse)
+          sendResponse(true);
+        return;
       }
-    };
+      // Create a nsIInputStreamChannel so we can set the url on the channel
+      // so the filename will be correct.
+      let channel = Cc['@mozilla.org/network/input-stream-channel;1'].
+                      createInstance(Ci.nsIInputStreamChannel);
+      channel.setURI(originalUri);
+      channel.contentStream = aInputStream;
+      channel.QueryInterface(Ci.nsIChannel);
+
+      var listener = {
+        extListener: null,
+        onStartRequest: function(aRequest, aContext) {
+          this.extListener = extHelperAppSvc.doContent('application/pdf',
+                                aRequest, frontWindow, false);
+          this.extListener.onStartRequest(aRequest, aContext);
+        },
+        onStopRequest: function(aRequest, aContext, aStatusCode) {
+          if (this.extListener)
+            this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
+          // Notify the content code we're done downloading.
+          if (sendResponse)
+            sendResponse(false);
+        },
+        onDataAvailable: function(aRequest, aContext, aInputStream, aOffset,
+                                  aCount) {
+          this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
+                                           aOffset, aCount);
+        }
+      };
 
-    channel.asyncOpen(listener, null);
+      channel.asyncOpen(listener, null);
+    });
   },
   setDatabase: function(data) {
     if (inPrivateBrowsing)
@@ -199,21 +226,39 @@ ChromeActions.prototype = {
 function RequestListener(actions) {
   this.actions = actions;
 }
-// Receive an event and synchronously responds.
+// Receive an event and synchronously or asynchronously responds.
 RequestListener.prototype.receive = function(event) {
   var message = event.target;
+  var doc = message.ownerDocument;
   var action = message.getUserData('action');
   var data = message.getUserData('data');
+  var sync = message.getUserData('sync');
   var actions = this.actions;
   if (!(action in actions)) {
     log('Unknown action: ' + action);
     return;
   }
-  var response = actions[action].call(this.actions, data);
-  message.setUserData('response', response, null);
+  if (sync) {
+    var response = actions[action].call(this.actions, data);
+    message.setUserData('response', response, null);
+  } else {
+    var response;
+    if (!message.getUserData('callback')) {
+      doc.documentElement.removeChild(message);
+      response = null;
+    } else {
+      response = function sendResponse(response) {
+        message.setUserData('response', response, null);
+
+        var listener = doc.createEvent('HTMLEvents');
+        listener.initEvent('pdf.js.response', true, false);
+        return message.dispatchEvent(listener);
+      }
+    }
+    actions[action].call(this.actions, data, response);
+  }
 };
 
-
 function PdfStreamConverter() {
 }
 
index b16636e31cbeeb90f3ccedc2d783b84caf9993f6..df20ff57718e4c562e06d612aea3f0e746b21d5f 100644 (file)
@@ -9,7 +9,7 @@
 
   // fetch an l10n objects
   function getL10nData(key) {
-    var response = FirefoxCom.request('getStrings', key);
+    var response = FirefoxCom.requestSync('getStrings', key);
     var data = JSON.parse(response);
     if (!data)
       console.warn('[l10n] #' + key + ' missing for [' + gLanguage + ']');
@@ -78,7 +78,7 @@
   }
 
   window.addEventListener('DOMContentLoaded', function() {
-    gLanguage = FirefoxCom.request('getLocale', null);
+    gLanguage = FirefoxCom.requestSync('getLocale', null);
 
     translateFragment();
 
index 59f9661bf6478b42fd2a08f32ca94c1fcae815a2..7d65f96b4e2d3698e95769144d434b5290c71263 100644 (file)
@@ -151,6 +151,15 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
       promise.resolve(this.pdfInfo.encrypted);
       return promise;
     },
+    /**
+     * @return {Promise} A promise that is resolved with a TypedArray that has
+     * the raw data from the PDF.
+     */
+    getData: function PDFDocumentProxy_getData() {
+      var promise = new PDFJS.Promise();
+      this.transport.getData(promise);
+      return promise;
+    },
     destroy: function PDFDocumentProxy_destroy() {
       this.transport.destroy();
     }
@@ -616,6 +625,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
       this.messageHandler.send('GetDocRequest', {data: data, params: params});
     },
 
+    getData: function WorkerTransport_sendData(promise) {
+      this.messageHandler.send('GetData', null, function(data) {
+        promise.resolve(data);
+      });
+    },
+
     getPage: function WorkerTransport_getPage(pageNumber, promise) {
       var pageIndex = pageNumber - 1;
       if (pageIndex in this.pagePromises)
index a93c36e953c8cc9b229a2a48753d90b44af4df93..c1dfa79af5b1523f74269f17241ec1e589cf9e9e 100644 (file)
@@ -136,6 +136,10 @@ var WorkerMessageHandler = {
       handler.send('GetPage', {pageInfo: page});
     });
 
+    handler.on('GetData', function wphSetupGetData(data, promise) {
+      promise.resolve(pdfModel.stream.bytes);
+    });
+
     handler.on('GetAnnotationsRequest', function wphSetupGetAnnotations(data) {
       var pdfPage = pdfModel.getPage(data.pageIndex + 1);
       handler.send('GetAnnotations', {
index 21a50e96bb0054fb403a58991ad7286b7a9da141..f8837ba898820a71461987f01aa402e9c419fb13 100644 (file)
@@ -116,16 +116,19 @@ var RenderingQueue = (function RenderingQueueClosure() {
 var FirefoxCom = (function FirefoxComClosure() {
   return {
     /**
-     * Creates an event that hopefully the extension is listening for and will
+     * Creates an event that the extension is listening for and will
      * synchronously respond to.
+     * NOTE: It is reccomended to use request() instead since one day we may not
+     * be able to synchronously reply.
      * @param {String} action The action to trigger.
      * @param {String} data Optional data to send.
      * @return {*} The response.
      */
-    request: function(action, data) {
+    requestSync: function(action, data) {
       var request = document.createTextNode('');
       request.setUserData('action', action, null);
       request.setUserData('data', data, null);
+      request.setUserData('sync', true, null);
       document.documentElement.appendChild(request);
 
       var sender = document.createEvent('Events');
@@ -134,6 +137,39 @@ var FirefoxCom = (function FirefoxComClosure() {
       var response = request.getUserData('response');
       document.documentElement.removeChild(request);
       return response;
+    },
+    /**
+     * Creates an event that the extension is listening for and will
+     * asynchronously respond by calling the callback.
+     * @param {String} action The action to trigger.
+     * @param {String} data Optional data to send.
+     * @param {Function} callback Optional response callback that will be called
+     * with one data argument.
+     */
+    request: function(action, data, callback) {
+      var request = document.createTextNode('');
+      request.setUserData('action', action, null);
+      request.setUserData('data', data, null);
+      request.setUserData('sync', false, null);
+      if (callback) {
+        request.setUserData('callback', callback, null);
+
+        document.addEventListener('pdf.js.response', function listener(event) {
+          var node = event.target,
+              callback = node.getUserData('callback'),
+              response = node.getUserData('response');
+
+          document.documentElement.removeChild(node);
+
+          document.removeEventListener('pdf.js.response', listener, false);
+          return callback(response);
+        }, false);
+      }
+      document.documentElement.appendChild(request);
+
+      var sender = document.createEvent('HTMLEvents');
+      sender.initEvent('pdf.js.message', true, false);
+      return request.dispatchEvent(sender);
     }
   };
 })();
@@ -160,7 +196,7 @@ var Settings = (function SettingsClosure() {
     var database = null;
     var index;
     if (isFirefoxExtension)
-      database = FirefoxCom.request('getDatabase', null) || '{}';
+      database = FirefoxCom.requestSync('getDatabase', null) || '{}';
     else if (isLocalStorageEnabled)
       database = localStorage.getItem('database') || '{}';
     else
@@ -193,7 +229,7 @@ var Settings = (function SettingsClosure() {
       file[name] = val;
       var database = JSON.stringify(this.database);
       if (isFirefoxExtension)
-        FirefoxCom.request('setDatabase', database);
+        FirefoxCom.requestSync('setDatabase', database);
       else if (isLocalStorageEnabled)
         localStorage.setItem('database', database);
     },
@@ -224,6 +260,7 @@ var PDFView = {
   container: null,
   initialized: false,
   fellback: false,
+  pdfDocument: null,
   // called once when the document is loaded
   initialize: function pdfViewInitialize() {
     this.container = document.getElementById('viewerContainer');
@@ -348,6 +385,7 @@ var PDFView = {
       PDFView.loadingBar = new ProgressBar('#loadingBar', {});
     }
 
+    this.pdfDocument = null;
     var self = this;
     self.loading = true;
     PDFJS.getDocument(parameters).then(
@@ -384,9 +422,37 @@ var PDFView = {
   },
 
   download: function pdfViewDownload() {
+    function noData() {
+      FirefoxCom.request('download', { originalUrl: url });
+    }
+
     var url = this.url.split('#')[0];
     if (PDFJS.isFirefoxExtension) {
-      FirefoxCom.request('download', url);
+      // Document isn't ready just try to download with the url.
+      if (!this.pdfDocument) {
+        noData();
+        return;
+      }
+      this.pdfDocument.getData().then(
+        function getDataSuccess(data) {
+          var bb = new MozBlobBuilder();
+          bb.append(data.buffer);
+          var blobUrl = window.URL.createObjectURL(
+                          bb.getBlob('application/pdf'));
+
+          FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url },
+            function response(err) {
+              if (err) {
+                // This error won't really be helpful because it's likely the
+                // fallback won't work either (or is already open).
+                PDFView.error('PDF failed to download.');
+              }
+              window.URL.revokeObjectURL(blobUrl);
+            }
+          );
+        },
+        noData // Error ocurred try downloading with just the url.
+      );
     } else {
       url += '#pdfjs.action=download', '_parent';
       window.open(url, '_parent');
@@ -544,6 +610,8 @@ var PDFView = {
       };
     }
 
+    this.pdfDocument = pdfDocument;
+
     var errorWrapper = document.getElementById('errorWrapper');
     errorWrapper.setAttribute('hidden', 'true');
 
@@ -1520,7 +1588,7 @@ window.addEventListener('load', function webViewerLoad(evt) {
     PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true');
 
   if ('pdfBug' in hashParams &&
-      (!PDFJS.isFirefoxExtension || FirefoxCom.request('pdfBugEnabled'))) {
+      (!PDFJS.isFirefoxExtension || FirefoxCom.requestSync('pdfBugEnabled'))) {
     PDFJS.pdfBug = true;
     var pdfBug = hashParams['pdfBug'];
     var enabled = pdfBug.split(',');
@@ -1529,7 +1597,7 @@ window.addEventListener('load', function webViewerLoad(evt) {
   }
 
   if (!PDFJS.isFirefoxExtension ||
-    (PDFJS.isFirefoxExtension && FirefoxCom.request('searchEnabled'))) {
+    (PDFJS.isFirefoxExtension && FirefoxCom.requestSync('searchEnabled'))) {
     document.querySelector('#viewSearch').classList.remove('hidden');
   }