Browse Source

Implements loading PDF data by extension/chrome

Yury Delendik 13 years ago
parent
commit
c905191de2
  1. 114
      extensions/firefox/components/PdfStreamConverter.js
  2. 39
      web/viewer.js

114
extensions/firefox/components/PdfStreamConverter.js

@ -14,6 +14,7 @@ const MOZ_CENTRAL = PDFJSSCRIPT_MOZ_CENTRAL;
const PDFJS_EVENT_ID = 'pdf.js.message'; const PDFJS_EVENT_ID = 'pdf.js.message';
const PDF_CONTENT_TYPE = 'application/pdf'; const PDF_CONTENT_TYPE = 'application/pdf';
const PREF_PREFIX = 'PDFJSSCRIPT_PREF_PREFIX'; const PREF_PREFIX = 'PDFJSSCRIPT_PREF_PREFIX';
const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
const MAX_DATABASE_LENGTH = 4096; const MAX_DATABASE_LENGTH = 4096;
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}'; const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}'; const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
@ -122,9 +123,39 @@ function getLocalizedString(strings, id, property) {
return id; return id;
} }
// PDF data storage
function PdfDataListener(length) {
this.length = length;
this.data = new Uint8Array(length);
}
PdfDataListener.prototype = {
set: function PdfDataListener_set(chunk, offset) {
this.data.set(chunk, offset);
var loaded = offset + chunk.length;
this.onprogress(loaded, this.length);
},
finish: function PdfDataListener_finish() {
this.isDataReady = true;
if (this.oncompleteCallback) {
this.oncompleteCallback(this.data);
delete this.data;
}
},
onprogress: function() {},
set oncomplete(value) {
this.oncompleteCallback = value;
if (this.isDataReady) {
value(this.data);
delete this.data; // releasing temporary storage
}
}
};
// All the priviledged actions. // All the priviledged actions.
function ChromeActions(domWindow) { function ChromeActions(domWindow, dataListener) {
this.domWindow = domWindow; this.domWindow = domWindow;
this.dataListener = dataListener;
} }
ChromeActions.prototype = { ChromeActions.prototype = {
@ -194,6 +225,36 @@ ChromeActions.prototype = {
getLocale: function() { getLocale: function() {
return getStringPref('general.useragent.locale', 'en-US'); return getStringPref('general.useragent.locale', 'en-US');
}, },
getLoadingType: function() {
return this.dataListener ? 'passive' : 'active';
},
initPassiveLoading: function() {
if (!this.dataListener)
return false;
var domWindow = this.domWindow;
this.dataListener.onprogress =
function ChromeActions_dataListenerProgress(loaded, total) {
domWindow.postMessage({
pdfjsLoadAction: 'progress',
loaded: loaded,
total: total
}, '*');
};
this.dataListener.oncomplete =
function ChromeActions_dataListenerComplete(data) {
domWindow.postMessage({
pdfjsLoadAction: 'complete',
data: data
}, '*');
delete this.dataListener;
};
return true;
},
getStrings: function(data) { getStrings: function(data) {
try { try {
// Lazy initialization of localizedStrings // Lazy initialization of localizedStrings
@ -324,6 +385,9 @@ PdfStreamConverter.prototype = {
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) { asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
if (!isEnabled()) if (!isEnabled())
throw Cr.NS_ERROR_NOT_IMPLEMENTED; throw Cr.NS_ERROR_NOT_IMPLEMENTED;
var useFetchByChrome = getBoolPref(PREF_PREFIX + '.fetchByChrome', true);
if (!useFetchByChrome) {
// Ignoring HTTP POST requests -- pdf.js has to repeat the request. // Ignoring HTTP POST requests -- pdf.js has to repeat the request.
var skipConversion = false; var skipConversion = false;
try { try {
@ -335,6 +399,7 @@ PdfStreamConverter.prototype = {
} }
if (skipConversion) if (skipConversion)
throw Cr.NS_ERROR_NOT_IMPLEMENTED; throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
// Store the listener passed to us // Store the listener passed to us
this.listener = aListener; this.listener = aListener;
@ -342,8 +407,14 @@ PdfStreamConverter.prototype = {
// nsIStreamListener::onDataAvailable // nsIStreamListener::onDataAvailable
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) { onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
if (!this.dataListener) {
// Do nothing since all the data loading is handled by the viewer. // Do nothing since all the data loading is handled by the viewer.
log('SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!'); return;
}
var binaryStream = this.binaryStream;
binaryStream.setInputStream(aInputStream);
this.dataListener.set(binaryStream.readByteArray(aCount), aOffset);
}, },
// nsIRequestObserver::onStartRequest // nsIRequestObserver::onStartRequest
@ -351,15 +422,30 @@ PdfStreamConverter.prototype = {
// Setup the request so we can use it below. // Setup the request so we can use it below.
aRequest.QueryInterface(Ci.nsIChannel); aRequest.QueryInterface(Ci.nsIChannel);
var useFetchByChrome = getBoolPref(PREF_PREFIX + '.fetchByChrome', true);
var dataListener;
if (useFetchByChrome) {
// Creating storage for PDF data
var contentLength = aRequest.contentLength;
if (contentLength < 0)
throw new 'Unknown length is not supported';
dataListener = new PdfDataListener(contentLength);
this.dataListener = dataListener;
this.binaryStream = Cc['@mozilla.org/binaryinputstream;1']
.createInstance(Ci.nsIBinaryInputStream);
} else {
// Cancel the request so the viewer can handle it. // Cancel the request so the viewer can handle it.
aRequest.cancel(Cr.NS_BINDING_ABORTED); aRequest.cancel(Cr.NS_BINDING_ABORTED);
}
// Create a new channel that is viewer loaded as a resource. // Create a new channel that is viewer loaded as a resource.
var ioService = Services.io; var ioService = Services.io;
var channel = ioService.newChannel( var channel = ioService.newChannel(
'resource://pdf.js/web/viewer.html', null, null); PDF_VIEWER_WEB_PAGE, null, null);
var listener = this.listener; var listener = this.listener;
var self = this;
// Proxy all the request observer calls, when it gets to onStopRequest // Proxy all the request observer calls, when it gets to onStopRequest
// we can get the dom window. // we can get the dom window.
var proxy = { var proxy = {
@ -373,8 +459,8 @@ PdfStreamConverter.prototype = {
var domWindow = getDOMWindow(channel); var domWindow = getDOMWindow(channel);
// Double check the url is still the correct one. // Double check the url is still the correct one.
if (domWindow.document.documentURIObject.equals(aRequest.URI)) { if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
let requestListener = new RequestListener( let actions = new ChromeActions(domWindow, dataListener);
new ChromeActions(domWindow)); let requestListener = new RequestListener(actions);
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) { domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
requestListener.receive(event); requestListener.receive(event);
}, false, true); }, false, true);
@ -386,11 +472,27 @@ PdfStreamConverter.prototype = {
// Keep the URL the same so the browser sees it as the same. // Keep the URL the same so the browser sees it as the same.
channel.originalURI = aRequest.URI; channel.originalURI = aRequest.URI;
channel.asyncOpen(proxy, aContext); channel.asyncOpen(proxy, aContext);
if (useFetchByChrome) {
// We can use resource principal when data is fetched by the chrome
// e.g. useful for NoScript
var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
.getService(Ci.nsIScriptSecurityManager);
var uri = ioService.newURI(PDF_VIEWER_WEB_PAGE, null, null);
var resourcePrincipal = securityManager.getCodebasePrincipal(uri);
channel.owner = resourcePrincipal;
}
}, },
// nsIRequestObserver::onStopRequest // nsIRequestObserver::onStopRequest
onStopRequest: function(aRequest, aContext, aStatusCode) { onStopRequest: function(aRequest, aContext, aStatusCode) {
// Do nothing. if (!this.dataListener) {
// Do nothing
return;
}
this.dataListener.finish();
delete this.dataListener;
delete this.binaryStream;
} }
}; };

39
web/viewer.js

@ -390,11 +390,37 @@ var PDFView = {
return value; return value;
}, },
initPassiveLoading: function pdfViewInitPassiveLoading() {
if (!PDFView.loadingBar) {
PDFView.loadingBar = new ProgressBar('#loadingBar', {});
}
window.addEventListener('message', function window_message(e) {
var args = e.data;
if (!('pdfjsLoadAction' in args))
return;
switch (args.pdfjsLoadAction) {
case 'progress':
PDFView.progress(args.loaded / args.total);
break;
case 'complete':
PDFView.open(args.data, 0);
break;
}
});
FirefoxCom.requestSync('initPassiveLoading', null);
},
setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
this.url = url;
document.title = decodeURIComponent(getFileName(url)) || url;
},
open: function pdfViewOpen(url, scale, password) { open: function pdfViewOpen(url, scale, password) {
var parameters = {password: password}; var parameters = {password: password};
if (typeof url === 'string') { // URL if (typeof url === 'string') { // URL
this.url = url; this.setTitleUsingUrl(url);
document.title = decodeURIComponent(getFileName(url)) || url;
parameters.url = url; parameters.url = url;
} else if (url && 'byteLength' in url) { // ArrayBuffer } else if (url && 'byteLength' in url) { // ArrayBuffer
parameters.data = url; parameters.data = url;
@ -1736,7 +1762,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
}; };
}; };
window.addEventListener('load', function webViewerLoad(evt) { document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) {
PDFView.initialize(); PDFView.initialize();
var params = PDFView.parseQueryString(document.location.search.substring(1)); var params = PDFView.parseQueryString(document.location.search.substring(1));
@ -1813,6 +1839,11 @@ window.addEventListener('load', function webViewerLoad(evt) {
PDFView.renderHighestPriority(); PDFView.renderHighestPriority();
}); });
if (PDFJS.isFirefoxExtension &&
FirefoxCom.requestSync('getLoadingType') == 'passive') {
PDFView.setTitleUsingUrl(file);
PDFView.initPassiveLoading();
} else
PDFView.open(file, 0); PDFView.open(file, 0);
}, true); }, true);
@ -1887,7 +1918,7 @@ window.addEventListener('change', function webViewerChange(evt) {
// implemented in Firefox. // implemented in Firefox.
var file = files[0]; var file = files[0];
fileReader.readAsBinaryString(file); fileReader.readAsBinaryString(file);
document.title = file.name; PDFView.setTitleUsingUrl(file.name);
// URL does not reflect proper document location - hiding some icons. // URL does not reflect proper document location - hiding some icons.
document.getElementById('viewBookmark').setAttribute('hidden', 'true'); document.getElementById('viewBookmark').setAttribute('hidden', 'true');

Loading…
Cancel
Save