From 178b89342af2185de7771fba8112cb92cd6dfaa9 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 23 Jan 2012 16:50:45 -0800 Subject: [PATCH] Switch to stream converter for extension. --- Makefile | 14 ++- extensions/firefox/bootstrap.js | 3 - extensions/firefox/chrome.manifest | 4 +- .../firefox/components/pdfContentHandler.js | 93 ++++++++++++------- src/core.js | 20 +++- web/viewer-snippet-firefox-extension.html | 14 +++ web/viewer.html | 4 +- web/viewer.js | 35 ++----- 8 files changed, 115 insertions(+), 72 deletions(-) create mode 100644 web/viewer-snippet-firefox-extension.html diff --git a/Makefile b/Makefile index fe5a3fc42..eaaa3e81f 100644 --- a/Makefile +++ b/Makefile @@ -211,6 +211,7 @@ EXTENSION_WEB_FILES = \ web/images \ web/viewer.css \ web/viewer.js \ + web/viewer.html \ web/viewer-production.html \ $(NULL) @@ -249,7 +250,18 @@ extension: | production # Copy a standalone version of pdf.js inside the content directory @cp $(BUILD_TARGET) $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/ @cp -r $(EXTENSION_WEB_FILES) $(FIREFOX_BUILD_CONTENT)/web/ - @mv -f $(FIREFOX_BUILD_CONTENT)/web/viewer-production.html $(FIREFOX_BUILD_CONTENT)/web/viewer.html + @rm $(FIREFOX_BUILD_CONTENT)/web/viewer-production.html + # Copy over the firefox extension snippet so we can inline pdf.js in it + cp web/viewer-snippet-firefox-extension.html $(FIREFOX_BUILD_CONTENT)/web/ + # Modify the viewer so it does all the extension only stuff. + cd $(FIREFOX_BUILD_CONTENT)/web; \ + sed -i.bak '/PDFJSSCRIPT_INCLUDE_BUNDLE/ r ../build/pdf.js' viewer-snippet-firefox-extension.html; \ + sed -i.bak '/PDFJSSCRIPT_REMOVE/d' viewer.html; \ + sed -i.bak '/PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION/d' viewer.html; \ + sed -i.bak '/PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION/ r viewer-snippet-firefox-extension.html' viewer.html; \ + rm -f *.bak; + # We don't need pdf.js anymore since its inlined + rm -Rf $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/; # Update the build version number @sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/install.rdf @sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/update.rdf diff --git a/extensions/firefox/bootstrap.js b/extensions/firefox/bootstrap.js index e51df28f8..bbc53195e 100644 --- a/extensions/firefox/bootstrap.js +++ b/extensions/firefox/bootstrap.js @@ -34,13 +34,10 @@ function shutdown(aData, aReason) { } function install(aData, aReason) { - let url = 'chrome://pdf.js/content/web/viewer.html?file=%s'; - Services.prefs.setCharPref('extensions.pdf.js.url', url); Services.prefs.setBoolPref('extensions.pdf.js.active', false); } function uninstall(aData, aReason) { - Services.prefs.clearUserPref('extensions.pdf.js.url'); Services.prefs.clearUserPref('extensions.pdf.js.active'); } diff --git a/extensions/firefox/chrome.manifest b/extensions/firefox/chrome.manifest index d7db20b38..ec7c9a964 100644 --- a/extensions/firefox/chrome.manifest +++ b/extensions/firefox/chrome.manifest @@ -1,5 +1,5 @@ -content pdf.js content/ +resource pdf.js content/ component {2278dfd0-b75c-11e0-8257-1ba3d93c9f1a} components/pdfContentHandler.js -contract @mozilla.org/uriloader/content-handler;1?type=application/pdf {2278dfd0-b75c-11e0-8257-1ba3d93c9f1a} +contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {2278dfd0-b75c-11e0-8257-1ba3d93c9f1a} diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js index 67459b759..fa9f329fc 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/pdfContentHandler.js @@ -21,47 +21,74 @@ function log(aMsg) { } const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001; + function pdfContentHandler() { -} +}; pdfContentHandler.prototype = { - handleContent: function handleContent(aMimetype, aContext, aRequest) { - if (aMimetype != PDF_CONTENT_TYPE) - throw NS_ERROR_WONT_HANDLE_CONTENT; - if (!(aRequest instanceof Ci.nsIChannel)) - throw NS_ERROR_WONT_HANDLE_CONTENT; + // properties required for XPCOM registration: + classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'), + classDescription: 'pdf.js Component', + contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*', + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsISupports, + Ci.nsIStreamConverter, + Ci.nsIStreamListener, + Ci.nsIRequestObserver + ]), - if (!Services.prefs.getBoolPref('extensions.pdf.js.active')) - throw NS_ERROR_WONT_HANDLE_CONTENT; + /* + * This component works as such: + * 1. asyncConvertData stores the listener + * 2. onStartRequest creates a new channel, streams the viewer and cancels + * the request so pdf.js can do the request + * Since the request is cancelled onDataAvailable should not be called. The + * onStopRequest does nothing. The convert function just returns the stream, + * it's just the synchronous version of asyncConvertData. + */ - let window = null; - let callbacks = aRequest.notificationCallbacks || - aRequest.loadGroup.notificationCallbacks; - if (!callbacks) - return; - - window = callbacks.getInterface(Ci.nsIDOMWindow); - - let url = null; - try { - url = Services.prefs.getCharPref('extensions.pdf.js.url'); - } catch (e) { - log('Error retrieving the pdf.js base url - ' + e); - throw NS_ERROR_WONT_HANDLE_CONTENT; - } - - let targetUrl = aRequest.URI.spec; - if (targetUrl.indexOf('#pdfjs.action=download') >= 0) - throw NS_ERROR_WONT_HANDLE_CONTENT; - - aRequest.cancel(Cr.NS_BINDING_ABORTED); - window.location = url.replace('%s', encodeURIComponent(targetUrl)); + // nsIStreamConverter::convert + convert: function (aFromStream, aFromType, aToType, aCtxt) { + return aFromStream; }, - classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler]) + // nsIStreamConverter::asyncConvertData + asyncConvertData: function (aFromType, aToType, aListener, aCtxt) { + // Store the listener passed to us + this.listener = aListener; + }, + + // nsIStreamListener::onDataAvailable + onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) { + // Do nothing since all the data loading is handled by the viewer. + log("SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!"); + }, + + // nsIRequestObserver::onStartRequest + onStartRequest: function (aRequest, aContext) { + // Setup the request so we can use it below. + aRequest.QueryInterface(Ci.nsIChannel); + + // Create a new channel that is viewer loaded as a resource. + var ioService = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var channel = ioService.newChannel( + 'resource://pdf.js/web/viewer.html', null, null); + // Keep the URL the same so the browser sees it as the same. + channel.originalURI = aRequest.originalURI; + channel.asyncOpen(this.listener, aContext); + + // Cancel the request so the viewer can handle it. + aRequest.cancel(Cr.NS_BINDING_ABORTED); + }, + + // nsIRequestObserver::onStopRequest + onStopRequest: function (aRequest, aContext, aStatusCode) { + // Do nothing. + return; + } }; var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]); - diff --git a/src/core.js b/src/core.js index 7a9f3ee03..3e3d991a9 100644 --- a/src/core.js +++ b/src/core.js @@ -624,9 +624,19 @@ var PDFDoc = (function PDFDocClosure() { } try { - // Some versions of FF can't create a worker on localhost, see: - // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 - var worker = new Worker(workerSrc); + var worker; + if (PDFJS.isFirefoxExtension) { + // The firefox extension can't load the worker from the resource:// + // url so we have to inline the script and then use the blob loader. + var bb = new MozBlobBuilder(); + bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent); + var blobUrl = window.URL.createObjectURL(bb.getBlob()); + worker = new Worker(blobUrl); + } else { + // Some versions of FF can't create a worker on localhost, see: + // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 + worker = new Worker(workerSrc); + } var messageHandler = new MessageHandler('main', worker); @@ -645,7 +655,9 @@ var PDFDoc = (function PDFDocClosure() { // serializing the typed array. messageHandler.send('test', testObj); return; - } catch (e) {} + } catch (e) { + warn('The worker has been disabled.') + } } // Either workers are disabled, not supported or have thrown an exception. // Thus, we fallback to a faked worker. diff --git a/web/viewer-snippet-firefox-extension.html b/web/viewer-snippet-firefox-extension.html new file mode 100644 index 000000000..a3d3502a8 --- /dev/null +++ b/web/viewer-snippet-firefox-extension.html @@ -0,0 +1,14 @@ + + + + diff --git a/web/viewer.html b/web/viewer.html index 40e99004f..f395292f9 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -2,9 +2,11 @@ Simple pdf.js page viewer + + - + diff --git a/web/viewer.js b/web/viewer.js index 6c72df3d0..f65c75434 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -73,23 +73,11 @@ var Settings = (function SettingsClosure() { } return true; })(); - var extPrefix = 'extensions.uriloader@pdf.js'; - var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled; - var inPrivateBrowsing = false; - if (isExtension) { - var pbs = Components.classes['@mozilla.org/privatebrowsing;1'] - .getService(Components.interfaces.nsIPrivateBrowsingService); - inPrivateBrowsing = pbs.privateBrowsingEnabled; - } function Settings(fingerprint) { var database = null; var index; - if (inPrivateBrowsing) - return false; - else if (isExtension) - database = Application.prefs.getValue(extPrefix + '.database', '{}'); - else if (isLocalStorageEnabled) + if (isLocalStorageEnabled) database = localStorage.getItem('database') || '{}'; else return false; @@ -110,31 +98,20 @@ var Settings = (function SettingsClosure() { index = database.files.push({fingerprint: fingerprint}) - 1; this.file = database.files[index]; this.database = database; - if (isExtension) - Application.prefs.setValue(extPrefix + '.database', - JSON.stringify(database)); - else if (isLocalStorageEnabled) + if (isLocalStorageEnabled) localStorage.setItem('database', JSON.stringify(database)); } Settings.prototype = { set: function settingsSet(name, val) { - if (inPrivateBrowsing) - return false; var file = this.file; file[name] = val; - if (isExtension) - Application.prefs.setValue(extPrefix + '.database', - JSON.stringify(this.database)); - else if (isLocalStorageEnabled) + if (isLocalStorageEnabled) localStorage.setItem('database', JSON.stringify(this.database)); }, get: function settingsGet(name, defaultValue) { - if (inPrivateBrowsing) - return defaultValue; - else - return this.file[name] || defaultValue; + return this.file[name] || defaultValue; } }; @@ -1011,7 +988,9 @@ window.addEventListener('load', function webViewerLoad(evt) { } var scale = ('scale' in params) ? params.scale : 0; - PDFView.open(params.file || kDefaultURL, parseFloat(scale)); + var file = PDFJS.isFirefoxExtension ? + window.location.toString() : params.file || kDefaultURL; + PDFView.open(file, parseFloat(scale)); if (!window.File || !window.FileReader || !window.FileList || !window.Blob) document.getElementById('fileInput').setAttribute('hidden', 'true');