]> git.parisson.com Git - pdf.js.git/commitdiff
Fix how we're storing settings and change how the save pdf works.
authorBrendan Dahl <brendan.dahl@gmail.com>
Thu, 26 Jan 2012 01:40:08 +0000 (17:40 -0800)
committerBrendan Dahl <brendan.dahl@gmail.com>
Thu, 26 Jan 2012 01:40:08 +0000 (17:40 -0800)
extensions/firefox/bootstrap.js
extensions/firefox/components/pdfContentHandler.js
web/viewer.js

index bbc53195ee6403239f7bc01c2107830ecaf6b846..06fcf182fc520e193fc46aa02add22cb4aba093f 100644 (file)
 
 'use strict';
 
+const EXT_PREFIX = 'extensions.uriloader@pdf.js';
+const PDFJS_EVENT_ID = 'pdf.js.message';
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cm = Components.manager;
 let Cu = Components.utils;
+let application = Cc['@mozilla.org/fuel/application;1']
+                    .getService(Ci.fuelIApplication);
+let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
+                        .getService(Ci.nsIPrivateBrowsingService);
 
 Cu.import('resource://gre/modules/Services.jsm');
 
 function log(str) {
   dump(str + '\n');
 }
+// watchWindows() and unload() are from Ed Lee's examples at
+// https://github.com/Mardak/restartless/blob/watchWindows/bootstrap.js
+/**
+ * Apply a callback to each open and new browser windows.
+ *
+ * @param {function} callback 1-parameter function that gets a browser window.
+ */
+function watchWindows(callback) {
+  // Wrap the callback in a function that ignores failures
+  function watcher(window) {
+    try {
+      // Now that the window has loaded, only handle browser windows
+      let {documentElement} = window.document;
+      if (documentElement.getAttribute('windowtype') == 'navigator:browser')
+        callback(window);
+    }
+    catch (ex) {}
+  }
+
+  // Wait for the window to finish loading before running the callback
+  function runOnLoad(window) {
+    // Listen for one load event before checking the window type
+    window.addEventListener('load', function runOnce() {
+      window.removeEventListener('load', runOnce, false);
+      watcher(window);
+    }, false);
+  }
+
+  // Add functionality to existing windows
+  let windows = Services.wm.getEnumerator(null);
+  while (windows.hasMoreElements()) {
+    // Only run the watcher immediately if the window is completely loaded
+    let window = windows.getNext();
+    if (window.document.readyState == 'complete')
+      watcher(window);
+    // Wait for the window to load before continuing
+    else
+      runOnLoad(window);
+  }
+
+  // Watch for new browser windows opening then wait for it to load
+  function windowWatcher(subject, topic) {
+    if (topic == 'domwindowopened')
+      runOnLoad(subject);
+  }
+  Services.ww.registerNotification(windowWatcher);
+
+  // Make sure to stop watching for windows if we're unloading
+  unload(function() Services.ww.unregisterNotification(windowWatcher));
+}
+
+/**
+ * Save callbacks to run when unloading. Optionally scope the callback to a
+ * container, e.g., window. Provide a way to run all the callbacks.
+ *
+ * @param {function} callback 0-parameter function to call on unload.
+ * @param {node} container Remove the callback when this container unloads.
+ * @return {function} A 0-parameter function that undoes adding the callback.
+ */
+function unload(callback, container) {
+  // Initialize the array of unloaders on the first usage
+  let unloaders = unload.unloaders;
+  if (unloaders == null)
+    unloaders = unload.unloaders = [];
+
+  // Calling with no arguments runs all the unloader callbacks
+  if (callback == null) {
+    unloaders.slice().forEach(function(unloader) unloader());
+    unloaders.length = 0;
+    return;
+  }
+
+  // The callback is bound to the lifetime of the container if we have one
+  if (container != null) {
+    // Remove the unloader when the container unloads
+    container.addEventListener('unload', removeUnloader, false);
 
+    // Wrap the callback to additionally remove the unload listener
+    let origCallback = callback;
+    callback = function() {
+      container.removeEventListener('unload', removeUnloader, false);
+      origCallback();
+    }
+  }
+
+  // Wrap the callback in a function that ignores failures
+  function unloader() {
+    try {
+      callback();
+    }
+    catch (ex) {}
+  }
+  unloaders.push(unloader);
+
+  // Provide a way to remove the unloader
+  function removeUnloader() {
+    let index = unloaders.indexOf(unloader);
+    if (index != -1)
+      unloaders.splice(index, 1);
+  }
+  return removeUnloader;
+}
+
+function messageCallback(event) {
+  log(event.target.ownerDocument.currentScript);
+  var message = event.target, doc = message.ownerDocument;
+  var inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled;
+  // Verify the message came from a PDF.
+  // TODO
+  var action = message.getUserData('action');
+  var data = message.getUserData('data');
+  switch (action) {
+    case 'download':
+      Services.wm.getMostRecentWindow('navigator:browser').saveURL(data);
+      break;
+    case 'setDatabase':
+      if (inPrivateBrowswing)
+        return;
+      application.prefs.setValue(EXT_PREFIX + '.database', data);
+      break;
+    case 'getDatabase':
+      var response;
+      if (inPrivateBrowswing)
+        response = '{}';
+      else
+        response = application.prefs.getValue(EXT_PREFIX + '.database', '{}');
+      message.setUserData('response', response, null);
+      break;
+  }
+}
+
+
+// All the boostrap functions:
 function startup(aData, aReason) {
   let manifestPath = 'chrome.manifest';
   let manifest = Cc['@mozilla.org/file/local;1']
@@ -26,11 +164,22 @@ function startup(aData, aReason) {
   } catch (e) {
     log(e);
   }
+
+  watchWindows(function(window) {
+    window.addEventListener(PDFJS_EVENT_ID, messageCallback, false, true);
+    unload(function() {
+      window.removeEventListener(PDFJS_EVENT_ID, messageCallback, false, true);
+    });
+  });
 }
 
 function shutdown(aData, aReason) {
-  if (Services.prefs.getBoolPref('extensions.pdf.js.active'))
+  if (Services.prefs.getBoolPref('extensions.pdf.js.active')) {
     Services.prefs.setBoolPref('extensions.pdf.js.active', false);
+    // Clean up with unloaders when we're deactivating
+    if (aReason != APP_SHUTDOWN)
+      unload();
+  }
 }
 
 function install(aData, aReason) {
@@ -39,5 +188,6 @@ function install(aData, aReason) {
 
 function uninstall(aData, aReason) {
   Services.prefs.clearUserPref('extensions.pdf.js.active');
+  application.prefs.setValue(EXT_PREFIX + '.database', '{}');
 }
 
index 320d69d303ccc77a9469888acbd485b61932ad13..edd8ee3e056bb94ec7c17a114885896394481069 100644 (file)
@@ -20,7 +20,7 @@ function log(aMsg) {
   dump(msg + '\n');
 }
 
-const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
+const NS_ERROR_NOT_IMPLEMENTED = 0x80004001;
 
 function pdfContentHandler() {
 }
@@ -51,11 +51,13 @@ pdfContentHandler.prototype = {
 
   // nsIStreamConverter::convert
   convert: function(aFromStream, aFromType, aToType, aCtxt) {
-      return aFromStream;
+    return aFromStream;
   },
 
   // nsIStreamConverter::asyncConvertData
   asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
+    if (!Services.prefs.getBoolPref('extensions.pdf.js.active'))
+      throw NS_ERROR_NOT_IMPLEMENTED;
     // Store the listener passed to us
     this.listener = aListener;
   },
@@ -73,15 +75,6 @@ pdfContentHandler.prototype = {
     // Cancel the request so the viewer can handle it.
     aRequest.cancel(Cr.NS_BINDING_ABORTED);
 
-    // Check if we should download.
-    var targetUrl = aRequest.originalURI.spec;
-    var downloadHash = targetUrl.indexOf('?#pdfjs.action=download');
-    if (downloadHash >= 0) {
-      targetUrl = targetUrl.substring(0, downloadHash);
-      Services.wm.getMostRecentWindow("navigator:browser").saveURL(targetUrl);
-      return;
-    }
-
     // Create a new channel that is viewer loaded as a resource.
     var ioService = Cc['@mozilla.org/network/io-service;1']
                       .getService(Ci.nsIIOService);
index 55d0a595ce0a21099ebafdd40989582ed3ac0d70..790b5ae63d6b124b311d7a563b5048bf42a1525e 100644 (file)
@@ -61,6 +61,31 @@ var RenderingQueue = (function RenderingQueueClosure() {
   return RenderingQueue;
 })();
 
+var FirefoxCom = (function FirefoxComClosure() {
+  return {
+    /**
+     * Creates an event that hopefully the extension is listening for and will
+     * synchronously respond to.
+     * @param {String} action The action to trigger.
+     * @param {String} data Optional data to send.
+     * @return {*} The response.
+     */
+    request: function(action, data) {
+      var request = document.createTextNode('');
+      request.setUserData('action', action, null);
+      request.setUserData('data', data, null);
+      document.documentElement.appendChild(request);
+
+      var sender = document.createEvent('Events');
+      sender.initEvent('pdf.js.message', true, false);
+      request.dispatchEvent(sender);
+      var response = request.getUserData('response');
+      document.documentElement.removeChild(request);
+      return response;
+    }
+  };
+})();
+
 // Settings Manager - This is a utility for saving settings
 // First we see if localStorage is available, FF bug #495747
 // If not, we use FUEL in FF
@@ -74,10 +99,14 @@ var Settings = (function SettingsClosure() {
     return true;
   })();
 
+  var isFirefoxExtension = PDFJS.isFirefoxExtension;
+
   function Settings(fingerprint) {
     var database = null;
     var index;
-    if (isLocalStorageEnabled)
+    if (isFirefoxExtension)
+      database = FirefoxCom.request('getDatabase', null);
+    else if (isLocalStorageEnabled)
       database = localStorage.getItem('database') || '{}';
     else
       return false;
@@ -106,8 +135,11 @@ var Settings = (function SettingsClosure() {
     set: function settingsSet(name, val) {
       var file = this.file;
       file[name] = val;
-      if (isLocalStorageEnabled)
-        localStorage.setItem('database', JSON.stringify(this.database));
+      var database = JSON.stringify(this.database);
+      if (isFirefoxExtension)
+        FirefoxCom.request('setDatabase', database);
+      else if (isLocalStorageEnabled)
+        localStorage.setItem('database', database);
     },
 
     get: function settingsGet(name, defaultValue) {
@@ -250,13 +282,12 @@ var PDFView = {
 
   download: function pdfViewDownload() {
     var url = this.url.split('#')[0];
-    // For the extension we add an extra '?' to force the page to reload, its
-    // stripped off by the extension.
-    if (PDFJS.isFirefoxExtension)
-      url += '?#pdfjs.action=download';
-    else
+    if (PDFJS.isFirefoxExtension) {
+      FirefoxCom.request('download', url);
+    } else {
       url += '#pdfjs.action=download', '_parent';
-    window.open(url, '_parent');
+      window.open(url, '_parent');
+    }
   },
 
   navigateTo: function pdfViewNavigateTo(dest) {