From f6776c284a4d508d01b3c206f524d2174167f768 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 9 Jul 2015 15:00:32 -0400 Subject: [PATCH] Bug 1182228 - Update pdf.js to version 1.1.270. r= --HG-- extra : rebase_source : 57b9d7c22a65049ae8396f63b17f54ef7c16a479 --- browser/extensions/pdfjs/README.mozilla | 2 +- .../pdfjs/content/PdfStreamConverter.jsm | 16 +- .../pdfjs/content/PdfjsChromeUtils.jsm | 30 +- .../pdfjs/content/PdfjsContentUtils.jsm | 4 +- browser/extensions/pdfjs/content/build/pdf.js | 79 +++- .../pdfjs/content/build/pdf.worker.js | 336 +++++++++++++----- browser/extensions/pdfjs/content/network.js | 17 +- .../extensions/pdfjs/content/web/viewer.html | 7 +- .../extensions/pdfjs/content/web/viewer.js | 123 ++++--- 9 files changed, 431 insertions(+), 183 deletions(-) diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index ac05dcb0f1cd..05991f6071b0 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,3 +1,3 @@ This is the pdf.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.1.215 +Current extension version is: 1.1.270 diff --git a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm index 54bc9a6c1be1..b088bdf30256 100644 --- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm +++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm @@ -438,9 +438,23 @@ ChromeActions.prototype = { message = getLocalizedString(strings, 'unsupported_feature'); } PdfJsTelemetry.onFallback(); - PdfjsContentUtils.displayWarning(domWindow, message, sendResponse, + 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()) { diff --git a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm index 05beba101b05..65c5f55e3fbd 100644 --- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm +++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm @@ -311,24 +311,30 @@ let PdfjsChromeUtils = { * a pdf displayed correctly. */ _displayWarning: function (aMsg) { - let json = aMsg.data; + let data = aMsg.data; let browser = aMsg.target; - let cpowCallback = aMsg.objects.callback; + let tabbrowser = browser.getTabBrowser(); let notificationBox = tabbrowser.getNotificationBox(browser); - // Flag so we don't call the response callback twice, since if the user - // clicks open with different viewer both the button callback and + + // Flag so we don't send the message twice, since if the user clicks + // "open with different viewer" both the button callback and // eventCallback will be called. - let responseSent = false; + let messageSent = false; + function sendMessage(download) { + let mm = browser.messageManager; + mm.sendAsyncMessage('PDFJS:Child:fallbackDownload', + { download: download }); + } let buttons = [{ - label: json.label, - accessKey: json.accessKey, + label: data.label, + accessKey: data.accessKey, callback: function() { - responseSent = true; - cpowCallback(true); + messageSent = true; + sendMessage(true); } }]; - notificationBox.appendNotification(json.message, 'pdfjs-fallback', null, + notificationBox.appendNotification(data.message, 'pdfjs-fallback', null, notificationBox.PRIORITY_INFO_LOW, buttons, function eventsCallback(eventType) { @@ -339,10 +345,10 @@ let PdfjsChromeUtils = { } // Don't send a response again if we already responded when the button was // clicked. - if (responseSent) { + if (messageSent) { return; } - cpowCallback(false); + sendMessage(false); }); } }; diff --git a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm index 0a4259b449a8..6090a64249d8 100644 --- a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm +++ b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm @@ -112,7 +112,7 @@ let PdfjsContentUtils = { * Request the display of a notification warning in the associated window * when the renderer isn't sure a pdf displayed correctly. */ - displayWarning: function (aWindow, aMessage, aCallback, aLabel, accessKey) { + displayWarning: function (aWindow, aMessage, aLabel, accessKey) { // the child's dom frame mm associated with the window. let winmm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDocShell) @@ -122,8 +122,6 @@ let PdfjsContentUtils = { message: aMessage, label: aLabel, accessKey: accessKey - }, { - callback: aCallback }); }, diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index f2fcd6e03077..ecb846bad489 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.1.215'; -PDFJS.build = 'c9a7498'; +PDFJS.version = '1.1.270'; +PDFJS.build = 'ccfafea'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -81,6 +81,14 @@ var AnnotationType = { LINK: 3 }; +var AnnotationBorderStyleType = { + SOLID: 1, + DASHED: 2, + BEVELED: 3, + INSET: 4, + UNDERLINE: 5 +}; + var StreamType = { UNKNOWN: 0, FLATE: 1, @@ -6083,19 +6091,64 @@ var AnnotationUtils = (function AnnotationUtilsClosure() { var width = item.rect[2] - item.rect[0]; var height = item.rect[3] - item.rect[1]; - var bWidth = item.borderWidth || 0; - if (bWidth) { - width = width - 2 * bWidth; - height = height - 2 * bWidth; - cstyle.borderWidth = bWidth + 'px'; - var color = item.color; - if (drawBorder && color) { - cstyle.borderStyle = 'solid'; - cstyle.borderColor = Util.makeCssRgb(Math.round(color[0] * 255), - Math.round(color[1] * 255), - Math.round(color[2] * 255)); + // Border + if (item.borderStyle.width > 0) { + // Border width + container.style.borderWidth = item.borderStyle.width + 'px'; + if (item.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) { + // Underline styles only have a bottom border, so we do not need + // to adjust for all borders. This yields a similar result as + // Adobe Acrobat/Reader. + width = width - 2 * item.borderStyle.width; + height = height - 2 * item.borderStyle.width; + } + + // Horizontal and vertical border radius + var horizontalRadius = item.borderStyle.horizontalCornerRadius; + var verticalRadius = item.borderStyle.verticalCornerRadius; + if (horizontalRadius > 0 || verticalRadius > 0) { + var radius = horizontalRadius + 'px / ' + verticalRadius + 'px'; + CustomStyle.setProp('borderRadius', container, radius); + } + + // Border style + switch (item.borderStyle.style) { + case AnnotationBorderStyleType.SOLID: + container.style.borderStyle = 'solid'; + break; + + case AnnotationBorderStyleType.DASHED: + container.style.borderStyle = 'dashed'; + break; + + case AnnotationBorderStyleType.BEVELED: + warn('Unimplemented border style: beveled'); + break; + + case AnnotationBorderStyleType.INSET: + warn('Unimplemented border style: inset'); + break; + + case AnnotationBorderStyleType.UNDERLINE: + container.style.borderBottomStyle = 'solid'; + break; + + default: + break; + } + + // Border color + if (item.color) { + container.style.borderColor = + Util.makeCssRgb(Math.round(item.color[0] * 255), + Math.round(item.color[1] * 255), + Math.round(item.color[2] * 255)); + } else { + // Default color is black, but that's not obvious from the spec. + container.style.borderColor = 'rgb(0,0,0)'; } } + cstyle.width = width + 'px'; cstyle.height = height + 'px'; return container; diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 88be9f00cb86..cfd25303360d 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.1.215'; -PDFJS.build = 'c9a7498'; +PDFJS.version = '1.1.270'; +PDFJS.build = 'ccfafea'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -81,6 +81,14 @@ var AnnotationType = { LINK: 3 }; +var AnnotationBorderStyleType = { + SOLID: 1, + DASHED: 2, + BEVELED: 3, + INSET: 4, + UNDERLINE: 5 +}; + var StreamType = { UNKNOWN: 0, FLATE: 1, @@ -4453,45 +4461,8 @@ var Annotation = (function AnnotationClosure() { } } - // Some types of annotations have border style dict which has more - // info than the border array - if (dict.has('BS')) { - var borderStyle = dict.get('BS'); - data.borderWidth = borderStyle.has('W') ? borderStyle.get('W') : 1; - } else { - var borderArray = dict.get('Border') || [0, 0, 1]; - data.borderWidth = borderArray[2] || 0; - - // TODO: implement proper support for annotations with line dash patterns. - var dashArray = borderArray[3]; - if (data.borderWidth > 0 && dashArray) { - if (!isArray(dashArray)) { - // Ignore the border if dashArray is not actually an array, - // this is consistent with the behaviour in Adobe Reader. - data.borderWidth = 0; - } else { - var dashArrayLength = dashArray.length; - if (dashArrayLength > 0) { - // According to the PDF specification: the elements in a dashArray - // shall be numbers that are nonnegative and not all equal to zero. - var isInvalid = false; - var numPositive = 0; - for (var i = 0; i < dashArrayLength; i++) { - var validNumber = (+dashArray[i] >= 0); - if (!validNumber) { - isInvalid = true; - break; - } else if (dashArray[i] > 0) { - numPositive++; - } - } - if (isInvalid || numPositive === 0) { - data.borderWidth = 0; - } - } - } - } - } + this.borderStyle = data.borderStyle = new AnnotationBorderStyle(); + this.setBorderStyle(dict); this.appearance = getDefaultAppearance(dict); data.hasAppearance = !!this.appearance; @@ -4499,6 +4470,48 @@ var Annotation = (function AnnotationClosure() { } Annotation.prototype = { + /** + * Set the border style (as AnnotationBorderStyle object). + * + * @public + * @memberof Annotation + * @param {Dict} borderStyle - The border style dictionary + */ + setBorderStyle: function Annotation_setBorderStyle(borderStyle) { + if (!isDict(borderStyle)) { + return; + } + if (borderStyle.has('BS')) { + var dict = borderStyle.get('BS'); + var dictType; + + if (!dict.has('Type') || (isName(dictType = dict.get('Type')) && + dictType.name === 'Border')) { + this.borderStyle.setWidth(dict.get('W')); + this.borderStyle.setStyle(dict.get('S')); + this.borderStyle.setDashArray(dict.get('D')); + } + } else if (borderStyle.has('Border')) { + var array = borderStyle.get('Border'); + if (isArray(array) && array.length >= 3) { + this.borderStyle.setHorizontalCornerRadius(array[0]); + this.borderStyle.setVerticalCornerRadius(array[1]); + this.borderStyle.setWidth(array[2]); + this.borderStyle.setStyle('S'); + + if (array.length === 4) { // Dash array available + this.borderStyle.setDashArray(array[3]); + } + } + } else { + // There are no border entries in the dictionary. According to the + // specification, we should draw a solid border of width 1 in that + // case, but Adobe Reader did not implement that part of the + // specification and instead draws no border at all, so we do the same. + // See also https://github.com/mozilla/pdf.js/issues/6179. + this.borderStyle.setWidth(0); + } + }, getData: function Annotation_getData() { return this.data; @@ -4685,6 +4698,144 @@ var Annotation = (function AnnotationClosure() { return Annotation; })(); +/** + * Contains all data regarding an annotation's border style. + * + * @class + */ +var AnnotationBorderStyle = (function AnnotationBorderStyleClosure() { + /** + * @constructor + * @private + */ + function AnnotationBorderStyle() { + this.width = 1; + this.style = AnnotationBorderStyleType.SOLID; + this.dashArray = [3]; + this.horizontalCornerRadius = 0; + this.verticalCornerRadius = 0; + } + + AnnotationBorderStyle.prototype = { + /** + * Set the width. + * + * @public + * @memberof AnnotationBorderStyle + * @param {integer} width - The width + */ + setWidth: function AnnotationBorderStyle_setWidth(width) { + if (width === (width | 0)) { + this.width = width; + } + }, + + /** + * Set the style. + * + * @public + * @memberof AnnotationBorderStyle + * @param {Object} style - The style object + * @see {@link shared/util.js} + */ + setStyle: function AnnotationBorderStyle_setStyle(style) { + if (!style) { + return; + } + switch (style.name) { + case 'S': + this.style = AnnotationBorderStyleType.SOLID; + break; + + case 'D': + this.style = AnnotationBorderStyleType.DASHED; + break; + + case 'B': + this.style = AnnotationBorderStyleType.BEVELED; + break; + + case 'I': + this.style = AnnotationBorderStyleType.INSET; + break; + + case 'U': + this.style = AnnotationBorderStyleType.UNDERLINE; + break; + + default: + break; + } + }, + + /** + * Set the dash array. + * + * @public + * @memberof AnnotationBorderStyle + * @param {Array} dashArray - The dash array with at least one element + */ + setDashArray: function AnnotationBorderStyle_setDashArray(dashArray) { + // We validate the dash array, but we do not use it because CSS does not + // allow us to change spacing of dashes. For more information, visit + // http://www.w3.org/TR/css3-background/#the-border-style. + if (isArray(dashArray) && dashArray.length > 0) { + // According to the PDF specification: the elements in a dashArray + // shall be numbers that are nonnegative and not all equal to zero. + var isValid = true; + var allZeros = true; + for (var i = 0, len = dashArray.length; i < len; i++) { + var element = dashArray[i]; + var validNumber = (+element >= 0); + if (!validNumber) { + isValid = false; + break; + } else if (element > 0) { + allZeros = false; + } + } + if (isValid && !allZeros) { + this.dashArray = dashArray; + } else { + this.width = 0; // Adobe behavior when the array is invalid. + } + } else if (dashArray) { + this.width = 0; // Adobe behavior when the array is invalid. + } + }, + + /** + * Set the horizontal corner radius (from a Border dictionary). + * + * @public + * @memberof AnnotationBorderStyle + * @param {integer} radius - The horizontal corner radius + */ + setHorizontalCornerRadius: + function AnnotationBorderStyle_setHorizontalCornerRadius(radius) { + if (radius === (radius | 0)) { + this.horizontalCornerRadius = radius; + } + }, + + /** + * Set the vertical corner radius (from a Border dictionary). + * + * @public + * @memberof AnnotationBorderStyle + * @param {integer} radius - The vertical corner radius + */ + setVerticalCornerRadius: + function AnnotationBorderStyle_setVerticalCornerRadius(radius) { + if (radius === (radius | 0)) { + this.verticalCornerRadius = radius; + } + } + }; + + return AnnotationBorderStyle; +})(); + var WidgetAnnotation = (function WidgetAnnotationClosure() { function WidgetAnnotation(params) { @@ -4785,21 +4936,9 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { return TextWidgetAnnotation; })(); -var InteractiveAnnotation = (function InteractiveAnnotationClosure() { - function InteractiveAnnotation(params) { - Annotation.call(this, params); - - this.data.hasHtml = true; - } - - Util.inherit(InteractiveAnnotation, Annotation, { }); - - return InteractiveAnnotation; -})(); - var TextAnnotation = (function TextAnnotationClosure() { function TextAnnotation(params) { - InteractiveAnnotation.call(this, params); + Annotation.call(this, params); var dict = params.dict; var data = this.data; @@ -4809,6 +4948,7 @@ var TextAnnotation = (function TextAnnotationClosure() { data.annotationType = AnnotationType.TEXT; data.content = stringToPDFString(content || ''); data.title = stringToPDFString(title || ''); + data.hasHtml = true; if (data.hasAppearance) { data.name = 'NoIcon'; @@ -4823,18 +4963,19 @@ var TextAnnotation = (function TextAnnotationClosure() { } } - Util.inherit(TextAnnotation, InteractiveAnnotation, { }); + Util.inherit(TextAnnotation, Annotation, { }); return TextAnnotation; })(); var LinkAnnotation = (function LinkAnnotationClosure() { function LinkAnnotation(params) { - InteractiveAnnotation.call(this, params); + Annotation.call(this, params); var dict = params.dict; var data = this.data; data.annotationType = AnnotationType.LINK; + data.hasHtml = true; var action = dict.get('A'); if (action && isDict(action)) { @@ -4898,7 +5039,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() { return url; } - Util.inherit(LinkAnnotation, InteractiveAnnotation, { }); + Util.inherit(LinkAnnotation, Annotation, { }); return LinkAnnotation; })(); @@ -6305,7 +6446,7 @@ var ColorSpace = (function ColorSpaceClosure() { } break; case 'Pattern': - var basePatternCS = xref.fetchIfRef(cs[1]) || null; + var basePatternCS = cs[1] || null; if (basePatternCS) { basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res); } @@ -6313,7 +6454,7 @@ var ColorSpace = (function ColorSpaceClosure() { case 'Indexed': case 'I': var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res); - var hiVal = cs[2] + 1; + var hiVal = xref.fetchIfRef(cs[2]) + 1; var lookup = xref.fetchIfRef(cs[3]); if (isStream(lookup)) { lookup = lookup.getBytes(); @@ -6321,7 +6462,7 @@ var ColorSpace = (function ColorSpaceClosure() { return ['IndexedCS', baseIndexedCS, hiVal, lookup]; case 'Separation': case 'DeviceN': - var name = cs[1]; + var name = xref.fetchIfRef(cs[1]); numComps = 1; if (isName(name)) { numComps = 1; @@ -10705,6 +10846,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } // eagerly compile XForm objects var name = args[0].name; + if (!name) { + warn('XObject must be referred to by name.'); + continue; + } if (imageCache[name] !== undefined) { operatorList.addOp(imageCache[name].fn, imageCache[name].args); args = null; @@ -16082,6 +16227,32 @@ var OpenTypeFileBuilder = (function OpenTypeFileBuilderClosure() { return OpenTypeFileBuilder; })(); +// Problematic Unicode characters in the fonts that needs to be moved to avoid +// issues when they are painted on the canvas, e.g. complex-script shaping or +// control/whitespace characters. The ranges are listed in pairs: the first item +// is a code of the first problematic code, the second one is the next +// non-problematic code. The ranges must be in sorted order. +var ProblematicCharRanges = new Int32Array([ + // Control characters. + 0x0000, 0x0020, + 0x007F, 0x00A1, + 0x00AD, 0x00AE, + // Chars that is used in complex-script shaping. + 0x0600, 0x0780, + 0x08A0, 0x10A0, + 0x1780, 0x1800, + // General punctuation chars. + 0x2000, 0x2010, + 0x2011, 0x2012, + 0x2028, 0x2030, + 0x205F, 0x2070, + 0x25CC, 0x25CD, + // Chars that is used in complex-script shaping. + 0xAA60, 0xAA80, + // Specials Unicode block. + 0xFFF0, 0x10000 +]); + /** * 'Font' is the class the outside world should use, it encapsulate all the font * decoding logics whatever type it is (assuming the font type is supported). @@ -16365,33 +16536,18 @@ var Font = (function FontClosure() { * @return {boolean} */ function isProblematicUnicodeLocation(code) { - if (code <= 0x1F) { // Control chars - return true; + // Using binary search to find a range start. + var i = 0, j = ProblematicCharRanges.length - 1; + while (i < j) { + var c = (i + j + 1) >> 1; + if (code < ProblematicCharRanges[c]) { + j = c - 1; + } else { + i = c; + } } - if (code >= 0x80 && code <= 0x9F) { // Control chars - return true; - } - if ((code >= 0x2000 && code <= 0x200F) || // General punctuation chars - (code >= 0x2028 && code <= 0x202F) || - (code >= 0x2060 && code <= 0x206F)) { - return true; - } - if (code >= 0xFFF0 && code <= 0xFFFF) { // Specials Unicode block - return true; - } - switch (code) { - case 0x7F: // Control char - case 0xA0: // Non breaking space - case 0xAD: // Soft hyphen - case 0x2011: // Non breaking hyphen - case 0x205F: // Medium mathematical space - case 0x25CC: // Dotted circle (combining mark) - return true; - } - if ((code & ~0xFF) === 0x0E00) { // Thai/Lao chars (with combining mark) - return true; - } - return false; + // Even index means code in problematic range. + return !(i & 1); } /** @@ -17816,13 +17972,17 @@ var Font = (function FontClosure() { var charCodeToGlyphId = [], charCode; var toUnicode = properties.toUnicode, widths = properties.widths; - var isIdentityUnicode = toUnicode instanceof IdentityToUnicodeMap; + var skipToUnicode = (toUnicode instanceof IdentityToUnicodeMap || + toUnicode.length === 0x10000); + // Helper function to try to skip mapping of empty glyphs. + // Note: In some cases, just relying on the glyph data doesn't work, + // hence we also use a few heuristics to fix various PDF files. function hasGlyph(glyphId, charCode, widthCode) { if (!missingGlyphs[glyphId]) { return true; } - if (!isIdentityUnicode && charCode >= 0 && toUnicode.has(charCode)) { + if (!skipToUnicode && charCode >= 0 && toUnicode.has(charCode)) { return true; } if (widths && widthCode >= 0 && isNum(widths[widthCode])) { diff --git a/browser/extensions/pdfjs/content/network.js b/browser/extensions/pdfjs/content/network.js index 7d79c89a7932..4cf5c803c1fc 100644 --- a/browser/extensions/pdfjs/content/network.js +++ b/browser/extensions/pdfjs/content/network.js @@ -69,6 +69,7 @@ var NetworkManager = (function NetworkManagerClosure() { return array.buffer; } + NetworkManager.prototype = { requestRange: function NetworkManager_requestRange(begin, end, listeners) { var args = { @@ -109,17 +110,11 @@ var NetworkManager = (function NetworkManagerClosure() { pendingRequest.expectedStatus = 200; } - if (args.onProgressiveData) { - // Some legacy browsers might throw an exception. - try { - xhr.responseType = 'moz-chunked-arraybuffer'; - } catch(e) {} - if (xhr.responseType === 'moz-chunked-arraybuffer') { - pendingRequest.onProgressiveData = args.onProgressiveData; - pendingRequest.mozChunked = true; - } else { - xhr.responseType = 'arraybuffer'; - } + var useMozChunkedLoading = !!args.onProgressiveData; + if (useMozChunkedLoading) { + xhr.responseType = 'moz-chunked-arraybuffer'; + pendingRequest.onProgressiveData = args.onProgressiveData; + pendingRequest.mozChunked = true; } else { xhr.responseType = 'arraybuffer'; } diff --git a/browser/extensions/pdfjs/content/web/viewer.html b/browser/extensions/pdfjs/content/web/viewer.html index 056bfc9fb675..fe502fdc48c9 100644 --- a/browser/extensions/pdfjs/content/web/viewer.html +++ b/browser/extensions/pdfjs/content/web/viewer.html @@ -14,8 +14,11 @@ 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. -Adobe CMap resources are covered by their own copyright and license: -http://sourceforge.net/adobe/cmap/wiki/License/ +Adobe CMap resources are covered by their own copyright but the same license: + + Copyright 1990-2015 Adobe Systems Incorporated. + +See https://github.com/adobe-type-tools/cmap-resources --> diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index 53a86835afc2..3c5e80ea1132 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -22,8 +22,8 @@ Promise, PDFLinkService, PDFOutlineView, PDFAttachmentView, OverlayManager, PDFFindController, PDFFindBar, getVisibleElements, watchScroll, PDFViewer, PDFRenderingQueue, PresentationModeState, - parseQueryString, RenderingStates, DEFAULT_SCALE, UNKNOWN_SCALE, - IGNORE_CURRENT_POSITION_ON_ZOOM: true */ + parseQueryString, RenderingStates, UNKNOWN_SCALE, + DEFAULT_SCALE_VALUE, IGNORE_CURRENT_POSITION_ON_ZOOM: true */ 'use strict'; @@ -46,7 +46,8 @@ var mozL10n = document.mozL10n || document.webL10n; var CSS_UNITS = 96.0 / 72.0; -var DEFAULT_SCALE = 'auto'; +var DEFAULT_SCALE_VALUE = 'auto'; +var DEFAULT_SCALE = 1.0; var UNKNOWN_SCALE = 0; var MAX_AUTO_SCALE = 1.25; var SCROLLBAR_PADDING = 40; @@ -601,7 +602,6 @@ var Preferences = { - var FirefoxCom = (function FirefoxComClosure() { return { /** @@ -729,7 +729,6 @@ Preferences._readFromStorage = function (prefObj) { * The way that the view parameters are stored depends on how PDF.js is built, * for 'node make ' the following cases exist: * - FIREFOX or MOZCENTRAL - uses sessionStorage. - * - B2G - uses asyncStorage. * - GENERIC or CHROME - uses localStorage, if it is available. */ var ViewHistory = (function ViewHistoryClosure() { @@ -768,7 +767,6 @@ var ViewHistory = (function ViewHistoryClosure() { return new Promise(function (resolve) { var databaseStr = JSON.stringify(this.database); - sessionStorage.setItem('pdfjsHistory', databaseStr); resolve(); @@ -777,7 +775,6 @@ var ViewHistory = (function ViewHistoryClosure() { _readFromStorage: function ViewHistory_readFromStorage() { return new Promise(function (resolve) { - resolve(sessionStorage.getItem('pdfjsHistory')); }); @@ -2153,6 +2150,7 @@ var CONTROLS_SELECTOR = 'pdfPresentationModeControls'; * @typedef {Object} PDFPresentationModeOptions * @property {HTMLDivElement} container - The container for the viewer element. * @property {HTMLDivElement} viewer - (optional) The viewer element. + * @property {PDFViewer} pdfViewer - The document viewer. * @property {PDFThumbnailViewer} pdfThumbnailViewer - (optional) The thumbnail * viewer. * @property {Array} contextMenuItems - (optional) The menuitems that are added @@ -2170,6 +2168,7 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { function PDFPresentationMode(options) { this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; + this.pdfViewer = options.pdfViewer; this.pdfThumbnailViewer = options.pdfThumbnailViewer || null; var contextMenuItems = options.contextMenuItems || null; @@ -2217,8 +2216,8 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { } this.args = { - page: PDFViewerApplication.page, - previousScale: PDFViewerApplication.currentScaleValue + page: this.pdfViewer.currentPageNumber, + previousScale: this.pdfViewer.currentScaleValue, }; return true; @@ -2258,16 +2257,16 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) { var pageSwitchDirection = (this.mouseScrollDelta > 0) ? PageSwitchDirection.UP : PageSwitchDirection.DOWN; - var page = PDFViewerApplication.page; + var page = this.pdfViewer.currentPageNumber; this._resetMouseScrollState(); // If we're at the first/last page, we don't need to do anything. if ((page === 1 && pageSwitchDirection === PageSwitchDirection.UP) || - (page === PDFViewerApplication.pagesCount && + (page === this.pdfViewer.pagesCount && pageSwitchDirection === PageSwitchDirection.DOWN)) { return; } - PDFViewerApplication.page = (page + pageSwitchDirection); + this.pdfViewer.currentPageNumber = (page + pageSwitchDirection); this.mouseScrollTimeStamp = currentTime; } }, @@ -2333,8 +2332,8 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { // Ensure that the correct page is scrolled into view when entering // Presentation Mode, by waiting until fullscreen mode in enabled. setTimeout(function enterPresentationModeTimeout() { - PDFViewerApplication.page = this.args.page; - PDFViewerApplication.setScale('page-fit', true); + this.pdfViewer.currentPageNumber = this.args.page; + this.pdfViewer.currentScaleValue = 'page-fit'; }.bind(this), 0); this._addWindowListeners(); @@ -2352,7 +2351,7 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { * @private */ _exit: function PDFPresentationMode_exit() { - var page = PDFViewerApplication.page; + var page = this.pdfViewer.currentPageNumber; this.container.classList.remove(ACTIVE_SELECTOR); // Ensure that the correct page is scrolled into view when exiting @@ -2362,8 +2361,8 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { this._removeFullscreenChangeListeners(); this._notifyStateChange(); - PDFViewerApplication.setScale(this.args.previousScale, true); - PDFViewerApplication.page = page; + this.pdfViewer.currentScaleValue = this.args.previousScale; + this.pdfViewer.currentPageNumber = page; this.args = null; }.bind(this), 0); @@ -2395,7 +2394,7 @@ var PDFPresentationMode = (function PDFPresentationModeClosure() { if (!isInternalLink) { // Unless an internal link was clicked, advance one page. evt.preventDefault(); - PDFViewerApplication.page += (evt.shiftKey ? -1 : 1); + this.pdfViewer.currentPageNumber += (evt.shiftKey ? -1 : 1); } } }, @@ -4493,6 +4492,18 @@ var PDFViewer = (function pdfViewer() { }; } + function isSameScale(oldScale, newScale) { + if (newScale === oldScale) { + return true; + } + if (Math.abs(newScale - oldScale) < 1e-15) { + // Prevent unnecessary re-rendering of all pages when the scale + // changes only because of limited numerical precision. + return true; + } + return false; + } + /** * @constructs PDFViewer * @param {PDFViewerOptions} options @@ -4556,13 +4567,20 @@ var PDFViewer = (function pdfViewer() { this._currentPageNumber = val; event.pageNumber = val; this.container.dispatchEvent(event); + + // Check if the caller is `PDFViewer_update`, to avoid breaking scrolling. + if (this.updateInProgress) { + return; + } + this.scrollPageIntoView(val); }, /** * @returns {number} */ get currentScale() { - return this._currentScale; + return this._currentScale !== UNKNOWN_SCALE ? this._currentScale : + DEFAULT_SCALE; }, /** @@ -4574,7 +4592,7 @@ var PDFViewer = (function pdfViewer() { } if (!this.pdfDocument) { this._currentScale = val; - this._currentScaleValue = val.toString(); + this._currentScaleValue = val !== UNKNOWN_SCALE ? val.toString() : null; return; } this._setScale(val, false); @@ -4678,7 +4696,7 @@ var PDFViewer = (function pdfViewer() { // Fetch a single page so we can get a viewport that will be the default // viewport for all pages return firstPagePromise.then(function(pdfPage) { - var scale = this._currentScale || 1.0; + var scale = this.currentScale; var viewport = pdfPage.getViewport(scale * CSS_UNITS); for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) { var textLayerFactory = null; @@ -4779,7 +4797,8 @@ var PDFViewer = (function pdfViewer() { _setScaleUpdatePages: function pdfViewer_setScaleUpdatePages( newScale, newValue, noScroll, preset) { this._currentScaleValue = newValue; - if (newScale === this._currentScale) { + + if (isSameScale(this._currentScale, newScale)) { if (preset) { this._setScaleDispatchEvent(newScale, newValue, true); } @@ -4936,10 +4955,10 @@ var PDFViewer = (function pdfViewer() { return; } - if (scale && scale !== this.currentScale) { + if (scale && scale !== this._currentScale) { this.currentScaleValue = scale; - } else if (this.currentScale === UNKNOWN_SCALE) { - this.currentScaleValue = DEFAULT_SCALE; + } else if (this._currentScale === UNKNOWN_SCALE) { + this.currentScaleValue = DEFAULT_SCALE_VALUE; } if (scale === 'page-fit' && !dest[4]) { @@ -4985,7 +5004,7 @@ var PDFViewer = (function pdfViewer() { }; }, - update: function () { + update: function PDFViewer_update() { var visible = this._getVisiblePages(); var visiblePages = visible.views; if (visiblePages.length === 0) { @@ -5000,7 +5019,7 @@ var PDFViewer = (function pdfViewer() { this.renderingQueue.renderHighestPriority(visible); - var currentId = this.currentPageNumber; + var currentId = this._currentPageNumber; var firstPage = visible.first; for (var i = 0, ii = visiblePages.length, stillFullyVisible = false; @@ -6013,6 +6032,7 @@ var PDFViewerApplication = { this.pdfPresentationMode = new PDFPresentationMode({ container: container, viewer: viewer, + pdfViewer: this.pdfViewer, pdfThumbnailViewer: this.pdfThumbnailViewer, contextMenuItems: [ { element: document.getElementById('contextFirstPage'), @@ -6104,10 +6124,6 @@ var PDFViewerApplication = { this.setScale(newScale, true); }, - get currentScaleValue() { - return this.pdfViewer.currentScaleValue; - }, - get pagesCount() { return this.pdfDocument.numPages; }, @@ -6299,7 +6315,6 @@ var PDFViewerApplication = { } var self = this; - self.loading = true; self.downloadComplete = false; var passwordNeeded = function passwordNeeded(updatePassword, reason) { @@ -6316,7 +6331,6 @@ var PDFViewerApplication = { getDocumentProgress).then( function getDocumentCallback(pdfDocument) { self.load(pdfDocument, scale); - self.loading = false; }, function getDocumentError(exception) { var message = exception && exception.message; @@ -6340,7 +6354,6 @@ var PDFViewerApplication = { message: message }; self.error(loadingErrorMessage, moreInfo); - self.loading = false; } ); @@ -6710,10 +6723,10 @@ var PDFViewerApplication = { this.page = 1; } - if (this.pdfViewer.currentScale === UNKNOWN_SCALE) { + if (!this.pdfViewer.currentScaleValue) { // Scale was not initialized: invalid bookmark or scale was not specified. // Setting the default one. - this.setScale(DEFAULT_SCALE, true); + this.setScale(DEFAULT_SCALE_VALUE, true); } }, @@ -7382,16 +7395,6 @@ window.addEventListener('pagechange', function pagechange(evt) { Stats.add(page, pageView.stats); } } - - // checking if the this.page was called from the updateViewarea function - if (evt.updateInProgress) { - return; - } - // Avoid scrolling the first page during loading - if (this.loading && page === 1) { - return; - } - PDFViewerApplication.pdfViewer.scrollPageIntoView(page); }, true); function handleMouseWheel(evt) { @@ -7400,14 +7403,31 @@ function handleMouseWheel(evt) { evt.wheelDelta / MOUSE_WHEEL_DELTA_FACTOR; var direction = (ticks < 0) ? 'zoomOut' : 'zoomIn'; - if (PDFViewerApplication.pdfViewer.isInPresentationMode) { + var pdfViewer = PDFViewerApplication.pdfViewer; + if (pdfViewer.isInPresentationMode) { evt.preventDefault(); PDFViewerApplication.scrollPresentationMode(ticks * MOUSE_WHEEL_DELTA_FACTOR); } else if (evt.ctrlKey || evt.metaKey) { // Only zoom the pages, not the entire viewer. evt.preventDefault(); + + var previousScale = pdfViewer.currentScale; + PDFViewerApplication[direction](Math.abs(ticks)); + + var currentScale = pdfViewer.currentScale; + if (previousScale !== currentScale) { + // After scaling the page via zoomIn/zoomOut, the position of the upper- + // left corner is restored. When the mouse wheel is used, the position + // under the cursor should be restored instead. + var scaleCorrectionFactor = currentScale / previousScale - 1; + var rect = pdfViewer.container.getBoundingClientRect(); + var dx = evt.clientX - rect.left; + var dy = evt.clientY - rect.top; + pdfViewer.container.scrollLeft += dx * scaleCorrectionFactor; + pdfViewer.container.scrollTop += dy * scaleCorrectionFactor; + } } } @@ -7476,7 +7496,7 @@ window.addEventListener('keydown', function keydown(evt) { // keeping it unhandled (to restore page zoom to 100%) setTimeout(function () { // ... and resetting the scale after browser adjusts its scale - PDFViewerApplication.setScale(DEFAULT_SCALE, true); + PDFViewerApplication.setScale(DEFAULT_SCALE_VALUE, true); }); handled = false; } @@ -7524,7 +7544,7 @@ window.addEventListener('keydown', function keydown(evt) { case 33: // pg up case 8: // backspace if (!isViewerInPresentationMode && - PDFViewerApplication.currentScaleValue !== 'page-fit') { + pdfViewer.currentScaleValue !== 'page-fit') { break; } /* in presentation mode */ @@ -7555,7 +7575,7 @@ window.addEventListener('keydown', function keydown(evt) { case 34: // pg down case 32: // spacebar if (!isViewerInPresentationMode && - PDFViewerApplication.currentScaleValue !== 'page-fit') { + pdfViewer.currentScaleValue !== 'page-fit') { break; } /* falls through */ @@ -7600,7 +7620,7 @@ window.addEventListener('keydown', function keydown(evt) { switch (evt.keyCode) { case 32: // spacebar if (!isViewerInPresentationMode && - PDFViewerApplication.currentScaleValue !== 'page-fit') { + pdfViewer.currentScaleValue !== 'page-fit') { break; } PDFViewerApplication.page--; @@ -7669,4 +7689,3 @@ window.addEventListener('afterprint', function afterPrint(evt) { }); })(); -