'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']
} 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) {
function uninstall(aData, aReason) {
Services.prefs.clearUserPref('extensions.pdf.js.active');
+ application.prefs.setValue(EXT_PREFIX + '.database', '{}');
}
dump(msg + '\n');
}
-const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
+const NS_ERROR_NOT_IMPLEMENTED = 0x80004001;
function pdfContentHandler() {
}
// 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;
},
// 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);
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
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;
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) {
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) {