Bug 1316526 - [jsplugins][UI] Implement download feature; r=evelyn
authorLuke Chang <lchang@mozilla.com>
Thu, 29 Dec 2016 18:37:02 +0800
changeset 374516 75cc309e84434fdd6e11b0306285dc7b98fbceb1
parent 374515 92383422009d4d19bb359055c734323a62fb5a41
child 374517 da1dd080a5e165a1e02a639ff3a3c840c8e0d944
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevelyn
bugs1316526
milestone53.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 1316526 - [jsplugins][UI] Implement download feature; r=evelyn MozReview-Commit-ID: 55RLoqTgHw9
browser/extensions/mortar/host/common/ppapi-runtime.jsm
browser/extensions/mortar/host/pdf/chrome/js/toolbar.js
browser/extensions/mortar/host/pdf/chrome/js/viewport.js
browser/extensions/mortar/host/pdf/ppapi-content-sandbox.js
--- a/browser/extensions/mortar/host/common/ppapi-runtime.jsm
+++ b/browser/extensions/mortar/host/common/ppapi-runtime.jsm
@@ -1663,16 +1663,19 @@ class PPAPIInstance {
     return this.id;
   }
 
   viewportActionHandler(message) {
     switch(message.type) {
       case 'setFullscreen':
         this.mm.sendAsyncMessage("ppapi.js:setFullscreen", message.fullscreen);
         break;
+      case 'save':
+        this.mm.sendAsyncMessage("ppapipdf.js:save");
+        break;
       case 'viewport':
       case 'rotateClockwise':
       case 'rotateCounterclockwise':
       case 'selectAll':
       case 'getSelectedText':
       case 'getNamedDestination':
       case 'getPasswordComplete':
         let data = PP_Var.fromJSValue(new Dictionary(message), this);
--- a/browser/extensions/mortar/host/pdf/chrome/js/toolbar.js
+++ b/browser/extensions/mortar/host/pdf/chrome/js/toolbar.js
@@ -189,16 +189,20 @@ class Toolbar {
         this._viewport.page++;
         break;
       case 'zoomIn':
         this._zoomIn();
         break;
       case 'zoomOut':
         this._zoomOut();
         break;
+      case 'download':
+      case 'secondaryDownload':
+        this._viewport.save();
+        break;
       case 'pageRotateCw':
         this._viewport.rotateClockwise();
         break;
       case 'pageRotateCcw':
         this._viewport.rotateCounterClockwise();
         break;
       case 'secondaryToolbarToggle':
         this._secondaryToolbar.toggle();
--- a/browser/extensions/mortar/host/pdf/chrome/js/viewport.js
+++ b/browser/extensions/mortar/host/pdf/chrome/js/viewport.js
@@ -414,16 +414,22 @@ class Viewport {
   }
 
   rotateCounterClockwise() {
     this._doAction({
       type: 'rotateCounterclockwise'
     });
   }
 
+  save() {
+    this._doAction({
+      type: 'save'
+    });
+  }
+
   // A handler for delivering messages to runtime.
   registerActionHandler(handler) {
     if (typeof handler === 'function') {
       this._actionHandler = handler;
     }
   }
 
   /***************************/
--- a/browser/extensions/mortar/host/pdf/ppapi-content-sandbox.js
+++ b/browser/extensions/mortar/host/pdf/ppapi-content-sandbox.js
@@ -4,17 +4,24 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * This code runs in the sandbox in the content process where the page that
  * loaded the plugin lives. It communicates with the process where the PPAPI
  * implementation lives.
  */
-const { utils: Cu } = Components;
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+                                          "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+                             "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 let mm = pluginElement.frameLoader.messageManager;
 let containerWindow = pluginElement.ownerDocument.defaultView;
 // Prevent the drag event's default action on the element, to avoid dragging of
 // that element while the user selects text.
 pluginElement.addEventListener("dragstart",
                                function(event) { event.preventDefault(); });
 // For synthetic documents only, prevent the select event's default action on
@@ -87,9 +94,82 @@ mm.addMessageListener("ppapi.js:frameLoa
 mm.addMessageListener("ppapi.js:setFullscreen", ({ data }) => {
   if (data) {
     pluginElement.requestFullscreen();
   } else {
     containerWindow.document.exitFullscreen();
   }
 });
 
+mm.addMessageListener("ppapipdf.js:save", () => {
+  let url = containerWindow.document.location;
+  let filename = "document.pdf";
+  let regex = /[^\/#\?]+\.pdf$/i;
+
+  let result = regex.exec(url.hash) ||
+               regex.exec(url.search) ||
+               regex.exec(url.pathname);
+  if (result) {
+    filename = result[0];
+  }
+
+  let originalUri = NetUtil.newURI(url.href);
+  let extHelperAppSvc =
+        Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
+           getService(Ci.nsIExternalHelperAppService);
+
+  let docIsPrivate =
+    PrivateBrowsingUtils.isContentWindowPrivate(containerWindow);
+  let netChannel = NetUtil.newChannel({
+    uri: originalUri,
+    loadUsingSystemPrincipal: true,
+  });
+
+  if ("nsIPrivateBrowsingChannel" in Ci &&
+      netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
+    netChannel.setPrivate(docIsPrivate);
+  }
+  NetUtil.asyncFetch(netChannel, function(aInputStream, aResult) {
+    if (!Components.isSuccessCode(aResult)) {
+      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.QueryInterface(Ci.nsIChannel);
+    channel.contentDisposition = Ci.nsIChannel.DISPOSITION_ATTACHMENT;
+    channel.contentDispositionFilename = filename;
+    channel.setURI(originalUri);
+    channel.loadInfo = netChannel.loadInfo;
+    channel.contentStream = aInputStream;
+    if ("nsIPrivateBrowsingChannel" in Ci &&
+        channel instanceof Ci.nsIPrivateBrowsingChannel) {
+      channel.setPrivate(docIsPrivate);
+    }
+
+    let listener = {
+      extListener: null,
+      onStartRequest(aRequest, aContext) {
+        var loadContext = containerWindow
+                            .QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIWebNavigation)
+                            .QueryInterface(Ci.nsILoadContext);
+        this.extListener = extHelperAppSvc.doContent(
+          "application/pdf", aRequest, loadContext, false);
+        this.extListener.onStartRequest(aRequest, aContext);
+      },
+      onStopRequest(aRequest, aContext, aStatusCode) {
+        if (this.extListener) {
+          this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
+        }
+      },
+      onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount) {
+        this.extListener
+          .onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
+      }
+    };
+
+    channel.asyncOpen2(listener);
+  });
+});
+
 mm.loadFrameScript("resource://ppapi.js/ppapi-instance.js", true);