diff options
Diffstat (limited to 'browser/extensions/pdfjs/content/PdfStreamConverter.jsm')
-rw-r--r-- | browser/extensions/pdfjs/content/PdfStreamConverter.jsm | 1054 |
1 files changed, 0 insertions, 1054 deletions
diff --git a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm deleted file mode 100644 index 5e337bbc5..000000000 --- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm +++ /dev/null @@ -1,1054 +0,0 @@ -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* jshint esnext:true */ -/* globals Components, Services, XPCOMUtils, NetUtil, PrivateBrowsingUtils, - dump, NetworkManager, PdfJsTelemetry, PdfjsContentUtils */ - -'use strict'; - -var EXPORTED_SYMBOLS = ['PdfStreamConverter']; - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; -// True only if this is the version of pdf.js that is included with firefox. -const MOZ_CENTRAL = JSON.parse('true'); -const PDFJS_EVENT_ID = 'pdf.js.message'; -const PDF_CONTENT_TYPE = 'application/pdf'; -const PREF_PREFIX = 'pdfjs'; -const PDF_VIEWER_ORIGIN = "resource://pdf.js"; -const PDF_VIEWER_WEB_PAGE = "resource://pdf.js/web/viewer.html"; -const MAX_NUMBER_OF_PREFS = 50; -const MAX_STRING_PREF_LENGTH = 128; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/Services.jsm'); -Cu.import('resource://gre/modules/NetUtil.jsm'); - -XPCOMUtils.defineLazyModuleGetter(this, 'NetworkManager', - 'resource://pdf.js/PdfJsNetwork.jsm'); - -XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils', - 'resource://gre/modules/PrivateBrowsingUtils.jsm'); - -XPCOMUtils.defineLazyModuleGetter(this, 'PdfJsTelemetry', - 'resource://pdf.js/PdfJsTelemetry.jsm'); - -XPCOMUtils.defineLazyModuleGetter(this, 'PdfjsContentUtils', - 'resource://pdf.js/PdfjsContentUtils.jsm'); - -var Svc = {}; -XPCOMUtils.defineLazyServiceGetter(Svc, 'mime', - '@mozilla.org/mime;1', - 'nsIMIMEService'); - -function getContainingBrowser(domWindow) { - return domWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler; -} - -function getFindBar(domWindow) { - if (PdfjsContentUtils.isRemote) { - throw new Error('FindBar is not accessible from the content process.'); - } - try { - var browser = getContainingBrowser(domWindow); - var tabbrowser = browser.getTabBrowser(); - var tab = tabbrowser.getTabForBrowser(browser); - return tabbrowser.getFindBar(tab); - } catch (e) { - // Suppress errors for PDF files opened in the bookmark sidebar, see - // https://bugzilla.mozilla.org/show_bug.cgi?id=1248959. - return null; - } -} - -function getBoolPref(pref, def) { - try { - return Services.prefs.getBoolPref(pref); - } catch (ex) { - return def; - } -} - -function getIntPref(pref, def) { - try { - return Services.prefs.getIntPref(pref); - } catch (ex) { - return def; - } -} - -function getStringPref(pref, def) { - try { - return Services.prefs.getComplexValue(pref, Ci.nsISupportsString).data; - } catch (ex) { - return def; - } -} - -function log(aMsg) { - if (!getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false)) { - return; - } - var msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg); - Services.console.logStringMessage(msg); - dump(msg + '\n'); -} - -function getDOMWindow(aChannel, aPrincipal) { - var requestor = aChannel.notificationCallbacks ? - aChannel.notificationCallbacks : - aChannel.loadGroup.notificationCallbacks; - var win = requestor.getInterface(Components.interfaces.nsIDOMWindow); - // Ensure the window wasn't navigated to something that is not PDF.js. - if (!win.document.nodePrincipal.equals(aPrincipal)) { - return null; - } - return win; -} - -function getLocalizedStrings(path) { - var stringBundle = Cc['@mozilla.org/intl/stringbundle;1']. - getService(Ci.nsIStringBundleService). - createBundle('chrome://pdf.js/locale/' + path); - - var map = {}; - var enumerator = stringBundle.getSimpleEnumeration(); - while (enumerator.hasMoreElements()) { - var string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement); - var key = string.key, property = 'textContent'; - var i = key.lastIndexOf('.'); - if (i >= 0) { - property = key.substring(i + 1); - key = key.substring(0, i); - } - if (!(key in map)) { - map[key] = {}; - } - map[key][property] = string.value; - } - return map; -} -function getLocalizedString(strings, id, property) { - property = property || 'textContent'; - if (id in strings) { - return strings[id][property]; - } - return id; -} - -function createNewChannel(uri, node) { - return NetUtil.newChannel({ - uri: uri, - loadUsingSystemPrincipal: true, - }); -} - -function asyncOpenChannel(channel, listener, context) { - return channel.asyncOpen2(listener); -} - -function asyncFetchChannel(channel, callback) { - return NetUtil.asyncFetch(channel, callback); -} - -// PDF data storage -function PdfDataListener(length) { - this.length = length; // less than 0, if length is unknown - this.buffer = null; - this.loaded = 0; -} - -PdfDataListener.prototype = { - append: function PdfDataListener_append(chunk) { - // In most of the cases we will pass data as we receive it, but at the - // beginning of the loading we may accumulate some data. - if (!this.buffer) { - this.buffer = new Uint8Array(chunk); - } else { - var buffer = this.buffer; - var newBuffer = new Uint8Array(buffer.length + chunk.length); - newBuffer.set(buffer); - newBuffer.set(chunk, buffer.length); - this.buffer = newBuffer; - } - this.loaded += chunk.length; - if (this.length >= 0 && this.length < this.loaded) { - this.length = -1; // reset the length, server is giving incorrect one - } - this.onprogress(this.loaded, this.length >= 0 ? this.length : void(0)); - }, - readData: function PdfDataListener_readData() { - var result = this.buffer; - this.buffer = null; - return result; - }, - finish: function PdfDataListener_finish() { - this.isDataReady = true; - if (this.oncompleteCallback) { - this.oncompleteCallback(this.readData()); - } - }, - error: function PdfDataListener_error(errorCode) { - this.errorCode = errorCode; - if (this.oncompleteCallback) { - this.oncompleteCallback(null, errorCode); - } - }, - onprogress: function() {}, - get oncomplete() { - return this.oncompleteCallback; - }, - set oncomplete(value) { - this.oncompleteCallback = value; - if (this.isDataReady) { - value(this.readData()); - } - if (this.errorCode) { - value(null, this.errorCode); - } - } -}; - -// All the priviledged actions. -function ChromeActions(domWindow, contentDispositionFilename) { - this.domWindow = domWindow; - this.contentDispositionFilename = contentDispositionFilename; - this.telemetryState = { - documentInfo: false, - firstPageInfo: false, - streamTypesUsed: [], - fontTypesUsed: [], - startAt: Date.now() - }; -} - -ChromeActions.prototype = { - isInPrivateBrowsing: function() { - return PrivateBrowsingUtils.isContentWindowPrivate(this.domWindow); - }, - download: function(data, sendResponse) { - var self = this; - var originalUrl = data.originalUrl; - var blobUrl = data.blobUrl || originalUrl; - // The data may not be downloaded so we need just retry getting the pdf with - // the original url. - var originalUri = NetUtil.newURI(originalUrl); - var filename = data.filename; - if (typeof filename !== 'string' || - (!/\.pdf$/i.test(filename) && !data.isAttachment)) { - filename = 'document.pdf'; - } - var blobUri = NetUtil.newURI(blobUrl); - var extHelperAppSvc = - Cc['@mozilla.org/uriloader/external-helper-app-service;1']. - getService(Ci.nsIExternalHelperAppService); - - var docIsPrivate = this.isInPrivateBrowsing(); - var netChannel = createNewChannel(blobUri, this.domWindow.document); - if ('nsIPrivateBrowsingChannel' in Ci && - netChannel instanceof Ci.nsIPrivateBrowsingChannel) { - netChannel.setPrivate(docIsPrivate); - } - asyncFetchChannel(netChannel, function(aInputStream, aResult) { - if (!Components.isSuccessCode(aResult)) { - if (sendResponse) { - sendResponse(true); - } - return; - } - // Create a nsIInputStreamChannel so we can set the url on the channel - // so the filename will be correct. - var channel = Cc['@mozilla.org/network/input-stream-channel;1']. - createInstance(Ci.nsIInputStreamChannel); - channel.QueryInterface(Ci.nsIChannel); - try { - // contentDisposition/contentDispositionFilename is readonly before FF18 - channel.contentDisposition = Ci.nsIChannel.DISPOSITION_ATTACHMENT; - if (self.contentDispositionFilename && !data.isAttachment) { - channel.contentDispositionFilename = self.contentDispositionFilename; - } else { - channel.contentDispositionFilename = filename; - } - } catch (e) {} - channel.setURI(originalUri); - channel.loadInfo = netChannel.loadInfo; - channel.contentStream = aInputStream; - if ('nsIPrivateBrowsingChannel' in Ci && - channel instanceof Ci.nsIPrivateBrowsingChannel) { - channel.setPrivate(docIsPrivate); - } - - var listener = { - extListener: null, - onStartRequest: function(aRequest, aContext) { - var loadContext = self.domWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsILoadContext); - this.extListener = extHelperAppSvc.doContent( - (data.isAttachment ? 'application/octet-stream' : - 'application/pdf'), - aRequest, loadContext, false); - this.extListener.onStartRequest(aRequest, aContext); - }, - onStopRequest: function(aRequest, aContext, aStatusCode) { - if (this.extListener) { - this.extListener.onStopRequest(aRequest, aContext, aStatusCode); - } - // Notify the content code we're done downloading. - if (sendResponse) { - sendResponse(false); - } - }, - onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, - aCount) { - this.extListener.onDataAvailable(aRequest, aContext, aInputStream, - aOffset, aCount); - } - }; - - asyncOpenChannel(channel, listener, null); - }); - }, - getLocale: function() { - return getStringPref('general.useragent.locale', 'en-US'); - }, - getStrings: function(data) { - try { - // Lazy initialization of localizedStrings - if (!('localizedStrings' in this)) { - this.localizedStrings = getLocalizedStrings('viewer.properties'); - } - var result = this.localizedStrings[data]; - return JSON.stringify(result || null); - } catch (e) { - log('Unable to retrieve localized strings: ' + e); - return 'null'; - } - }, - supportsIntegratedFind: function() { - // Integrated find is only supported when we're not in a frame - if (this.domWindow.frameElement !== null) { - return false; - } - - // ... and we are in a child process - if (PdfjsContentUtils.isRemote) { - return true; - } - - // ... or when the new find events code exists. - var findBar = getFindBar(this.domWindow); - return !!findBar && ('updateControlState' in findBar); - }, - supportsDocumentFonts: function() { - var prefBrowser = getIntPref('browser.display.use_document_fonts', 1); - var prefGfx = getBoolPref('gfx.downloadable_fonts.enabled', true); - return (!!prefBrowser && prefGfx); - }, - supportsDocumentColors: function() { - return getIntPref('browser.display.document_color_use', 0) !== 2; - }, - supportedMouseWheelZoomModifierKeys: function() { - return { - ctrlKey: getIntPref('mousewheel.with_control.action', 3) === 3, - metaKey: getIntPref('mousewheel.with_meta.action', 1) === 3, - }; - }, - reportTelemetry: function (data) { - var probeInfo = JSON.parse(data); - switch (probeInfo.type) { - case 'documentInfo': - if (!this.telemetryState.documentInfo) { - PdfJsTelemetry.onDocumentVersion(probeInfo.version | 0); - PdfJsTelemetry.onDocumentGenerator(probeInfo.generator | 0); - if (probeInfo.formType) { - PdfJsTelemetry.onForm(probeInfo.formType === 'acroform'); - } - this.telemetryState.documentInfo = true; - } - break; - case 'pageInfo': - if (!this.telemetryState.firstPageInfo) { - var duration = Date.now() - this.telemetryState.startAt; - PdfJsTelemetry.onTimeToView(duration); - this.telemetryState.firstPageInfo = true; - } - break; - case 'documentStats': - // documentStats can be called several times for one documents. - // if stream/font types are reported, trying not to submit the same - // enumeration value multiple times. - var documentStats = probeInfo.stats; - if (!documentStats || typeof documentStats !== 'object') { - break; - } - var i, streamTypes = documentStats.streamTypes; - if (Array.isArray(streamTypes)) { - var STREAM_TYPE_ID_LIMIT = 20; - for (i = 0; i < STREAM_TYPE_ID_LIMIT; i++) { - if (streamTypes[i] && - !this.telemetryState.streamTypesUsed[i]) { - PdfJsTelemetry.onStreamType(i); - this.telemetryState.streamTypesUsed[i] = true; - } - } - } - var fontTypes = documentStats.fontTypes; - if (Array.isArray(fontTypes)) { - var FONT_TYPE_ID_LIMIT = 20; - for (i = 0; i < FONT_TYPE_ID_LIMIT; i++) { - if (fontTypes[i] && - !this.telemetryState.fontTypesUsed[i]) { - PdfJsTelemetry.onFontType(i); - this.telemetryState.fontTypesUsed[i] = true; - } - } - } - break; - case 'print': - PdfJsTelemetry.onPrint(); - break; - } - }, - fallback: function(args, sendResponse) { - var featureId = args.featureId; - var url = args.url; - - var self = this; - var domWindow = this.domWindow; - var strings = getLocalizedStrings('chrome.properties'); - var message; - if (featureId === 'forms') { - message = getLocalizedString(strings, 'unsupported_feature_forms'); - } else { - message = getLocalizedString(strings, 'unsupported_feature'); - } - PdfJsTelemetry.onFallback(); - PdfjsContentUtils.displayWarning(domWindow, message, - getLocalizedString(strings, 'open_with_different_viewer'), - getLocalizedString(strings, 'open_with_different_viewer', 'accessKey')); - - let winmm = domWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIContentFrameMessageManager); - - winmm.addMessageListener('PDFJS:Child:fallbackDownload', - function fallbackDownload(msg) { - let data = msg.data; - sendResponse(data.download); - - winmm.removeMessageListener('PDFJS:Child:fallbackDownload', - fallbackDownload); - }); - }, - updateFindControlState: function(data) { - if (!this.supportsIntegratedFind()) { - return; - } - // Verify what we're sending to the findbar. - var result = data.result; - var findPrevious = data.findPrevious; - var findPreviousType = typeof findPrevious; - if ((typeof result !== 'number' || result < 0 || result > 3) || - (findPreviousType !== 'undefined' && findPreviousType !== 'boolean')) { - return; - } - - var winmm = this.domWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIContentFrameMessageManager); - - winmm.sendAsyncMessage('PDFJS:Parent:updateControlState', data); - }, - setPreferences: function(prefs, sendResponse) { - var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); - var numberOfPrefs = 0; - var prefValue, prefName; - for (var key in prefs) { - if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) { - log('setPreferences - Exceeded the maximum number of preferences ' + - 'that is allowed to be set at once.'); - break; - } else if (!defaultBranch.getPrefType(key)) { - continue; - } - prefValue = prefs[key]; - prefName = (PREF_PREFIX + '.' + key); - switch (typeof prefValue) { - case 'boolean': - PdfjsContentUtils.setBoolPref(prefName, prefValue); - break; - case 'number': - PdfjsContentUtils.setIntPref(prefName, prefValue); - break; - case 'string': - if (prefValue.length > MAX_STRING_PREF_LENGTH) { - log('setPreferences - Exceeded the maximum allowed length ' + - 'for a string preference.'); - } else { - PdfjsContentUtils.setStringPref(prefName, prefValue); - } - break; - } - } - if (sendResponse) { - sendResponse(true); - } - }, - getPreferences: function(prefs, sendResponse) { - var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); - var currentPrefs = {}, numberOfPrefs = 0; - var prefValue, prefName; - for (var key in prefs) { - if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) { - log('getPreferences - Exceeded the maximum number of preferences ' + - 'that is allowed to be fetched at once.'); - break; - } else if (!defaultBranch.getPrefType(key)) { - continue; - } - prefValue = prefs[key]; - prefName = (PREF_PREFIX + '.' + key); - switch (typeof prefValue) { - case 'boolean': - currentPrefs[key] = getBoolPref(prefName, prefValue); - break; - case 'number': - currentPrefs[key] = getIntPref(prefName, prefValue); - break; - case 'string': - currentPrefs[key] = getStringPref(prefName, prefValue); - break; - } - } - if (sendResponse) { - sendResponse(JSON.stringify(currentPrefs)); - } else { - return JSON.stringify(currentPrefs); - } - } -}; - -var RangedChromeActions = (function RangedChromeActionsClosure() { - /** - * This is for range requests - */ - function RangedChromeActions( - domWindow, contentDispositionFilename, originalRequest, - rangeEnabled, streamingEnabled, dataListener) { - - ChromeActions.call(this, domWindow, contentDispositionFilename); - this.dataListener = dataListener; - this.originalRequest = originalRequest; - this.rangeEnabled = rangeEnabled; - this.streamingEnabled = streamingEnabled; - - this.pdfUrl = originalRequest.URI.spec; - this.contentLength = originalRequest.contentLength; - - // Pass all the headers from the original request through - var httpHeaderVisitor = { - headers: {}, - visitHeader: function(aHeader, aValue) { - if (aHeader === 'Range') { - // When loading the PDF from cache, firefox seems to set the Range - // request header to fetch only the unfetched portions of the file - // (e.g. 'Range: bytes=1024-'). However, we want to set this header - // manually to fetch the PDF in chunks. - return; - } - this.headers[aHeader] = aValue; - } - }; - if (originalRequest.visitRequestHeaders) { - originalRequest.visitRequestHeaders(httpHeaderVisitor); - } - - var self = this; - var xhr_onreadystatechange = function xhr_onreadystatechange() { - if (this.readyState === 1) { // LOADING - var netChannel = this.channel; - if ('nsIPrivateBrowsingChannel' in Ci && - netChannel instanceof Ci.nsIPrivateBrowsingChannel) { - var docIsPrivate = self.isInPrivateBrowsing(); - netChannel.setPrivate(docIsPrivate); - } - } - }; - var getXhr = function getXhr() { - const XMLHttpRequest = Components.Constructor( - '@mozilla.org/xmlextras/xmlhttprequest;1'); - var xhr = new XMLHttpRequest(); - xhr.addEventListener('readystatechange', xhr_onreadystatechange); - return xhr; - }; - - this.networkManager = new NetworkManager(this.pdfUrl, { - httpHeaders: httpHeaderVisitor.headers, - getXhr: getXhr - }); - - // If we are in range request mode, this means we manually issued xhr - // requests, which we need to abort when we leave the page - domWindow.addEventListener('unload', function unload(e) { - domWindow.removeEventListener(e.type, unload); - self.abortLoading(); - }); - } - - RangedChromeActions.prototype = Object.create(ChromeActions.prototype); - var proto = RangedChromeActions.prototype; - proto.constructor = RangedChromeActions; - - proto.initPassiveLoading = function RangedChromeActions_initPassiveLoading() { - var self = this; - var data; - if (!this.streamingEnabled) { - this.originalRequest.cancel(Cr.NS_BINDING_ABORTED); - this.originalRequest = null; - data = this.dataListener.readData(); - this.dataListener = null; - } else { - data = this.dataListener.readData(); - - this.dataListener.onprogress = function (loaded, total) { - self.domWindow.postMessage({ - pdfjsLoadAction: 'progressiveRead', - loaded: loaded, - total: total, - chunk: self.dataListener.readData() - }, PDF_VIEWER_ORIGIN); - }; - this.dataListener.oncomplete = function () { - self.dataListener = null; - }; - } - - this.domWindow.postMessage({ - pdfjsLoadAction: 'supportsRangedLoading', - rangeEnabled: this.rangeEnabled, - streamingEnabled: this.streamingEnabled, - pdfUrl: this.pdfUrl, - length: this.contentLength, - data: data - }, PDF_VIEWER_ORIGIN); - - return true; - }; - - proto.requestDataRange = function RangedChromeActions_requestDataRange(args) { - if (!this.rangeEnabled) { - return; - } - - var begin = args.begin; - var end = args.end; - var domWindow = this.domWindow; - // TODO(mack): Support error handler. We're not currently not handling - // errors from chrome code for non-range requests, so this doesn't - // seem high-pri - this.networkManager.requestRange(begin, end, { - onDone: function RangedChromeActions_onDone(args) { - domWindow.postMessage({ - pdfjsLoadAction: 'range', - begin: args.begin, - chunk: args.chunk - }, PDF_VIEWER_ORIGIN); - }, - onProgress: function RangedChromeActions_onProgress(evt) { - domWindow.postMessage({ - pdfjsLoadAction: 'rangeProgress', - loaded: evt.loaded, - }, PDF_VIEWER_ORIGIN); - } - }); - }; - - proto.abortLoading = function RangedChromeActions_abortLoading() { - this.networkManager.abortAllRequests(); - if (this.originalRequest) { - this.originalRequest.cancel(Cr.NS_BINDING_ABORTED); - this.originalRequest = null; - } - this.dataListener = null; - }; - - return RangedChromeActions; -})(); - -var StandardChromeActions = (function StandardChromeActionsClosure() { - - /** - * This is for a single network stream - */ - function StandardChromeActions(domWindow, contentDispositionFilename, - originalRequest, dataListener) { - - ChromeActions.call(this, domWindow, contentDispositionFilename); - this.originalRequest = originalRequest; - this.dataListener = dataListener; - } - - StandardChromeActions.prototype = Object.create(ChromeActions.prototype); - var proto = StandardChromeActions.prototype; - proto.constructor = StandardChromeActions; - - proto.initPassiveLoading = - function StandardChromeActions_initPassiveLoading() { - - if (!this.dataListener) { - return false; - } - - var self = this; - - this.dataListener.onprogress = function ChromeActions_dataListenerProgress( - loaded, total) { - self.domWindow.postMessage({ - pdfjsLoadAction: 'progress', - loaded: loaded, - total: total - }, PDF_VIEWER_ORIGIN); - }; - - this.dataListener.oncomplete = - function StandardChromeActions_dataListenerComplete(data, errorCode) { - self.domWindow.postMessage({ - pdfjsLoadAction: 'complete', - data: data, - errorCode: errorCode - }, PDF_VIEWER_ORIGIN); - - self.dataListener = null; - self.originalRequest = null; - }; - - return true; - }; - - proto.abortLoading = function StandardChromeActions_abortLoading() { - if (this.originalRequest) { - this.originalRequest.cancel(Cr.NS_BINDING_ABORTED); - this.originalRequest = null; - } - this.dataListener = null; - }; - - return StandardChromeActions; -})(); - -// Event listener to trigger chrome privileged code. -function RequestListener(actions) { - this.actions = actions; -} -// Receive an event and synchronously or asynchronously responds. -RequestListener.prototype.receive = function(event) { - var message = event.target; - var doc = message.ownerDocument; - var action = event.detail.action; - var data = event.detail.data; - var sync = event.detail.sync; - var actions = this.actions; - if (!(action in actions)) { - log('Unknown action: ' + action); - return; - } - var response; - if (sync) { - response = actions[action].call(this.actions, data); - event.detail.response = Cu.cloneInto(response, doc.defaultView); - } else { - if (!event.detail.responseExpected) { - doc.documentElement.removeChild(message); - response = null; - } else { - response = function sendResponse(response) { - try { - var listener = doc.createEvent('CustomEvent'); - let detail = Cu.cloneInto({ response: response }, doc.defaultView); - listener.initCustomEvent('pdf.js.response', true, false, detail); - return message.dispatchEvent(listener); - } catch (e) { - // doc is no longer accessible because the requestor is already - // gone. unloaded content cannot receive the response anyway. - return false; - } - }; - } - actions[action].call(this.actions, data, response); - } -}; - -// Forwards events from the eventElement to the contentWindow only if the -// content window matches the currently selected browser window. -function FindEventManager(contentWindow) { - this.contentWindow = contentWindow; - this.winmm = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIContentFrameMessageManager); -} - -FindEventManager.prototype.bind = function() { - var unload = function(e) { - this.unbind(); - this.contentWindow.removeEventListener(e.type, unload); - }.bind(this); - this.contentWindow.addEventListener('unload', unload); - - // We cannot directly attach listeners to for the find events - // since the FindBar is in the parent process. Instead we're - // asking the PdfjsChromeUtils to do it for us and forward - // all the find events to us. - this.winmm.sendAsyncMessage('PDFJS:Parent:addEventListener'); - this.winmm.addMessageListener('PDFJS:Child:handleEvent', this); -}; - -FindEventManager.prototype.receiveMessage = function(msg) { - var detail = msg.data.detail; - var type = msg.data.type; - var contentWindow = this.contentWindow; - - detail = Cu.cloneInto(detail, contentWindow); - var forward = contentWindow.document.createEvent('CustomEvent'); - forward.initCustomEvent(type, true, true, detail); - contentWindow.dispatchEvent(forward); -}; - -FindEventManager.prototype.unbind = function() { - this.winmm.sendAsyncMessage('PDFJS:Parent:removeEventListener'); -}; - -function PdfStreamConverter() { -} - -PdfStreamConverter.prototype = { - - // properties required for XPCOM registration: - classID: Components.ID('{d0c5195d-e798-49d4-b1d3-9324328b2291}'), - classDescription: 'pdf.js Component', - contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*', - - classID2: Components.ID('{d0c5195d-e798-49d4-b1d3-9324328b2292}'), - contractID2: '@mozilla.org/streamconv;1?from=application/pdf&to=text/html', - - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsISupports, - Ci.nsIStreamConverter, - Ci.nsIStreamListener, - Ci.nsIRequestObserver - ]), - - /* - * This component works as such: - * 1. asyncConvertData stores the listener - * 2. onStartRequest creates a new channel, streams the viewer - * 3. If range requests are supported: - * 3.1. Leave the request open until the viewer is ready to switch to - * range requests. - * - * If range rquests are not supported: - * 3.1. Read the stream as it's loaded in onDataAvailable to send - * to the viewer - * - * The convert function just returns the stream, it's just the synchronous - * version of asyncConvertData. - */ - - // nsIStreamConverter::convert - convert: function(aFromStream, aFromType, aToType, aCtxt) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - // 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) { - if (!this.dataListener) { - return; - } - - var binaryStream = this.binaryStream; - binaryStream.setInputStream(aInputStream); - var chunk = binaryStream.readByteArray(aCount); - this.dataListener.append(chunk); - }, - - // nsIRequestObserver::onStartRequest - onStartRequest: function(aRequest, aContext) { - // Setup the request so we can use it below. - var isHttpRequest = false; - try { - aRequest.QueryInterface(Ci.nsIHttpChannel); - isHttpRequest = true; - } catch (e) {} - - var rangeRequest = false; - var streamRequest = false; - if (isHttpRequest) { - var contentEncoding = 'identity'; - try { - contentEncoding = aRequest.getResponseHeader('Content-Encoding'); - } catch (e) {} - - var acceptRanges; - try { - acceptRanges = aRequest.getResponseHeader('Accept-Ranges'); - } catch (e) {} - - var hash = aRequest.URI.ref; - var isPDFBugEnabled = getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false); - rangeRequest = contentEncoding === 'identity' && - acceptRanges === 'bytes' && - aRequest.contentLength >= 0 && - !getBoolPref(PREF_PREFIX + '.disableRange', false) && - (!isPDFBugEnabled || - hash.toLowerCase().indexOf('disablerange=true') < 0); - streamRequest = contentEncoding === 'identity' && - aRequest.contentLength >= 0 && - !getBoolPref(PREF_PREFIX + '.disableStream', false) && - (!isPDFBugEnabled || - hash.toLowerCase().indexOf('disablestream=true') < 0); - } - - aRequest.QueryInterface(Ci.nsIChannel); - - aRequest.QueryInterface(Ci.nsIWritablePropertyBag); - - var contentDispositionFilename; - try { - contentDispositionFilename = aRequest.contentDispositionFilename; - } catch (e) {} - - // Change the content type so we don't get stuck in a loop. - aRequest.setProperty('contentType', aRequest.contentType); - aRequest.contentType = 'text/html'; - if (isHttpRequest) { - // We trust PDF viewer, using no CSP - aRequest.setResponseHeader('Content-Security-Policy', '', false); - aRequest.setResponseHeader('Content-Security-Policy-Report-Only', '', - false); - // The viewer does not need to handle HTTP Refresh header. - aRequest.setResponseHeader('Refresh', '', false); - } - - PdfJsTelemetry.onViewerIsUsed(); - PdfJsTelemetry.onDocumentSize(aRequest.contentLength); - - // Creating storage for PDF data - var contentLength = aRequest.contentLength; - this.dataListener = new PdfDataListener(contentLength); - this.binaryStream = Cc['@mozilla.org/binaryinputstream;1'] - .createInstance(Ci.nsIBinaryInputStream); - - // Create a new channel that is viewer loaded as a resource. - var channel = createNewChannel(PDF_VIEWER_WEB_PAGE, null); - - var listener = this.listener; - var dataListener = this.dataListener; - // Proxy all the request observer calls, when it gets to onStopRequest - // we can get the dom window. We also intentionally pass on the original - // request(aRequest) below so we don't overwrite the original channel and - // trigger an assertion. - var proxy = { - onStartRequest: function(request, context) { - listener.onStartRequest(aRequest, aContext); - }, - onDataAvailable: function(request, context, inputStream, offset, count) { - listener.onDataAvailable(aRequest, aContext, inputStream, - offset, count); - }, - onStopRequest(request, context, statusCode) { - var domWindow = getDOMWindow(channel, resourcePrincipal); - if (!Components.isSuccessCode(statusCode) || !domWindow) { - // The request may have been aborted and the document may have been - // replaced with something that is not PDF.js, abort attaching. - listener.onStopRequest(aRequest, context, statusCode); - return; - } - var actions; - if (rangeRequest || streamRequest) { - actions = new RangedChromeActions( - domWindow, contentDispositionFilename, aRequest, - rangeRequest, streamRequest, dataListener); - } else { - actions = new StandardChromeActions( - domWindow, contentDispositionFilename, aRequest, dataListener); - } - var requestListener = new RequestListener(actions); - domWindow.document.addEventListener(PDFJS_EVENT_ID, function(event) { - requestListener.receive(event); - }, false, true); - if (actions.supportsIntegratedFind()) { - var findEventManager = new FindEventManager(domWindow); - findEventManager.bind(); - } - listener.onStopRequest(aRequest, aContext, statusCode); - - if (domWindow.frameElement) { - var isObjectEmbed = domWindow.frameElement.tagName !== 'IFRAME' || - domWindow.frameElement.className === 'previewPluginContentFrame'; - PdfJsTelemetry.onEmbed(isObjectEmbed); - } - } - }; - - // Keep the URL the same so the browser sees it as the same. - channel.originalURI = aRequest.URI; - channel.loadGroup = aRequest.loadGroup; - channel.loadInfo.originAttributes = aRequest.loadInfo.originAttributes; - - // We can use the resource principal when data is fetched by the chrome, - // e.g. useful for NoScript. Make make sure we reuse the origin attributes - // from the request channel to keep isolation consistent. - var ssm = Cc['@mozilla.org/scriptsecuritymanager;1'] - .getService(Ci.nsIScriptSecurityManager); - var uri = NetUtil.newURI(PDF_VIEWER_WEB_PAGE, null, null); - var resourcePrincipal; - resourcePrincipal = - ssm.createCodebasePrincipal(uri, aRequest.loadInfo.originAttributes); - aRequest.owner = resourcePrincipal; - asyncOpenChannel(channel, proxy, aContext); - }, - - // nsIRequestObserver::onStopRequest - onStopRequest: function(aRequest, aContext, aStatusCode) { - if (!this.dataListener) { - // Do nothing - return; - } - - if (Components.isSuccessCode(aStatusCode)) { - this.dataListener.finish(); - } else { - this.dataListener.error(aStatusCode); - } - delete this.dataListener; - delete this.binaryStream; - } -}; - |