From f3d3ede2fd145117ee15497ca425229e5c69ee7f Mon Sep 17 00:00:00 2001 From: Pdf Bot Date: Wed, 19 Oct 2016 21:55:41 +0100 Subject: [PATCH] PDF.js version 1.6.263 - See mozilla/pdf.js@7e392c0205e8f3590976fe2008df2a6ecd71c942 --- bower.json | 2 +- build/pdf.combined.js | 353 +++++++++++++++++++++++++----------------- build/pdf.js | 69 ++++++--- build/pdf.worker.js | 334 +++++++++++++++++++++++---------------- package.json | 2 +- web/pdf_viewer.js | 2 +- 6 files changed, 460 insertions(+), 302 deletions(-) diff --git a/bower.json b/bower.json index d2e9179ba..af3811b63 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "pdfjs-dist", - "version": "1.6.258", + "version": "1.6.263", "main": [ "build/pdf.js", "build/pdf.worker.js" diff --git a/build/pdf.combined.js b/build/pdf.combined.js index eaf453926..3afd4283a 100644 --- a/build/pdf.combined.js +++ b/build/pdf.combined.js @@ -24,8 +24,8 @@ }(this, function (exports) { // Use strict in our context only - users might not want it 'use strict'; - var pdfjsVersion = '1.6.258'; - var pdfjsBuild = '6906623'; + var pdfjsVersion = '1.6.263'; + var pdfjsBuild = '7e392c0'; var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null; var pdfjsLibs = {}; (function pdfjsWrapper() { @@ -3877,29 +3877,41 @@ var other = new URL(otherUrl, base); return base.origin === other.origin; } - // Validates if URL is safe and allowed, e.g. to avoid XSS. - function isValidUrl(url, allowRelative) { - if (!url || typeof url !== 'string') { + // Checks if URLs use one of the whitelisted protocols, e.g. to avoid XSS. + function isValidProtocol(url) { + if (!url) { return false; } - // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1) - // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url); - if (!protocol) { - return allowRelative; - } - protocol = protocol[0].toLowerCase(); - switch (protocol) { - case 'http': - case 'https': - case 'ftp': - case 'mailto': - case 'tel': + switch (url.protocol) { + case 'http:': + case 'https:': + case 'ftp:': + case 'mailto:': + case 'tel:': return true; default: return false; } } + /** + * Attempts to create a valid absolute URL (utilizing `isValidProtocol`). + * @param {URL|string} url - An absolute, or relative, URL. + * @param {URL|string} baseUrl - An absolute URL. + * @returns Either a valid {URL}, or `null` otherwise. + */ + function createValidAbsoluteUrl(url, baseUrl) { + if (!url) { + return null; + } + try { + var absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url); + if (isValidProtocol(absoluteUrl)) { + return absoluteUrl; + } + } catch (ex) { + } + return null; + } function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, @@ -6006,7 +6018,7 @@ exports.isString = isString; exports.isSpace = isSpace; exports.isSameOrigin = isSameOrigin; - exports.isValidUrl = isValidUrl; + exports.createValidAbsoluteUrl = createValidAbsoluteUrl; exports.isLittleEndian = isLittleEndian; exports.isEvalSupported = isEvalSupported; exports.loadJpegStream = loadJpegStream; @@ -24077,6 +24089,8 @@ }(this, function (exports, sharedUtil) { var removeNullCharacters = sharedUtil.removeNullCharacters; var warn = sharedUtil.warn; + var deprecated = sharedUtil.deprecated; + var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl; /** * Optimised CSS custom property getter/setter. * @class @@ -24255,9 +24269,15 @@ return true; } } + function isValidUrl(url, allowRelative) { + deprecated('isValidUrl(), please use createValidAbsoluteUrl() instead.'); + var baseUrl = allowRelative ? 'http://example.com' : null; + return createValidAbsoluteUrl(url, baseUrl) !== null; + } exports.CustomStyle = CustomStyle; exports.addLinkAttributes = addLinkAttributes; exports.isExternalLinkTargetSet = isExternalLinkTargetSet; + exports.isValidUrl = isValidUrl; exports.getFilenameFromUrl = getFilenameFromUrl; exports.LinkTarget = LinkTarget; exports.hasCanvasTypedArrays = hasCanvasTypedArrays; @@ -49616,6 +49636,9 @@ * 2^16 = 65536. * @property {PDFWorker} worker - The worker that will be used for the loading * and parsing of the PDF data. + * @property {string} docBaseUrl - (optional) The base URL of the document, + * used when attempting to recover valid absolute URLs for annotations, and + * outline items, that (incorrectly) only specify relative URLs. */ /** * @typedef {Object} PDFDocumentStats @@ -49770,7 +49793,8 @@ cMapPacked: getDefaultSetting('cMapPacked'), disableFontFace: getDefaultSetting('disableFontFace'), disableCreateObjectURL: getDefaultSetting('disableCreateObjectURL'), - postMessageTransfers: getDefaultSetting('postMessageTransfers') && !isPostMessageTransfersDisabled + postMessageTransfers: getDefaultSetting('postMessageTransfers') && !isPostMessageTransfersDisabled, + docBaseUrl: source.docBaseUrl }).then(function (workerId) { if (worker.destroyed) { throw new Error('Worker was destroyed'); @@ -52499,7 +52523,7 @@ PDFJS.VERBOSITY_LEVELS = sharedUtil.VERBOSITY_LEVELS; PDFJS.OPS = sharedUtil.OPS; PDFJS.UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES; - PDFJS.isValidUrl = sharedUtil.isValidUrl; + PDFJS.isValidUrl = displayDOMUtils.isValidUrl; PDFJS.shadow = sharedUtil.shadow; PDFJS.createBlob = sharedUtil.createBlob; PDFJS.createObjectURL = function PDFJS_createObjectURL(data, contentType) { @@ -53256,13 +53280,14 @@ var error = sharedUtil.error; var info = sharedUtil.info; var isArray = sharedUtil.isArray; + var isBool = sharedUtil.isBool; var isInt = sharedUtil.isInt; var isString = sharedUtil.isString; var shadow = sharedUtil.shadow; var stringToPDFString = sharedUtil.stringToPDFString; var stringToUTF8String = sharedUtil.stringToUTF8String; var warn = sharedUtil.warn; - var isValidUrl = sharedUtil.isValidUrl; + var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl; var Util = sharedUtil.Util; var Ref = corePrimitives.Ref; var RefSet = corePrimitives.RefSet; @@ -53359,23 +53384,15 @@ continue; } assert(outlineDict.has('Title'), 'Invalid outline item'); - var actionDict = outlineDict.get('A'), dest = null, url = null; - if (actionDict) { - var destEntry = actionDict.get('D'); - if (destEntry) { - dest = destEntry; - } else { - var uriEntry = actionDict.get('URI'); - if (isString(uriEntry) && isValidUrl(uriEntry, false)) { - url = uriEntry; - } - } - } else if (outlineDict.has('Dest')) { - dest = outlineDict.getRaw('Dest'); - if (isName(dest)) { - dest = dest.name; - } - } + var data = { + url: null, + dest: null + }; + Catalog.parseDestDictionary({ + destDict: outlineDict, + resultObj: data, + docBaseUrl: this.pdfManager.docBaseUrl + }); var title = outlineDict.get('Title'); var flags = outlineDict.get('F') || 0; var color = outlineDict.getArray('C'), rgbColor = blackColor; @@ -53384,8 +53401,9 @@ rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0); } var outlineItem = { - dest: dest, - url: url, + dest: data.dest, + url: data.url, + newWindow: data.newWindow, title: stringToPDFString(title), color: rgbColor, count: outlineDict.get('Count'), @@ -53772,6 +53790,124 @@ return next(pageRef); } }; + /** + * Helper function used to parse the contents of destination dictionaries. + * @param {Dict} destDict - The dictionary containing the destination. + * @param {Object} resultObj - The object where the parsed destination + * properties will be placed. + * @param {string} docBaseUrl - (optional) The document base URL that is used + * when attempting to recover valid absolute URLs from relative ones. + */ + Catalog.parseDestDictionary = function Catalog_parseDestDictionary(params) { + // Lets URLs beginning with 'www.' default to using the 'http://' protocol. + function addDefaultProtocolToUrl(url) { + if (url.indexOf('www.') === 0) { + return 'http://' + url; + } + return url; + } + // According to ISO 32000-1:2008, section 12.6.4.7, URIs should be encoded + // in 7-bit ASCII. Some bad PDFs use UTF-8 encoding, see Bugzilla 1122280. + function tryConvertUrlEncoding(url) { + try { + return stringToUTF8String(url); + } catch (e) { + return url; + } + } + var destDict = params.destDict; + if (!isDict(destDict)) { + warn('Catalog_parseDestDictionary: "destDict" must be a dictionary.'); + return; + } + var resultObj = params.resultObj; + if (typeof resultObj !== 'object') { + warn('Catalog_parseDestDictionary: "resultObj" must be an object.'); + return; + } + var docBaseUrl = params.docBaseUrl || null; + var action = destDict.get('A'), url, dest; + if (isDict(action)) { + var linkType = action.get('S').name; + switch (linkType) { + case 'URI': + url = action.get('URI'); + if (isName(url)) { + // Some bad PDFs do not put parentheses around relative URLs. + url = '/' + url.name; + } else if (isString(url)) { + url = addDefaultProtocolToUrl(url); + } + // TODO: pdf spec mentions urls can be relative to a Base + // entry in the dictionary. + break; + case 'GoTo': + dest = action.get('D'); + break; + case 'GoToR': + var urlDict = action.get('F'); + if (isDict(urlDict)) { + // We assume that we found a FileSpec dictionary + // and fetch the URL without checking any further. + url = urlDict.get('F') || null; + } else if (isString(urlDict)) { + url = urlDict; + } + // NOTE: the destination is relative to the *remote* document. + var remoteDest = action.get('D'); + if (remoteDest) { + if (isName(remoteDest)) { + remoteDest = remoteDest.name; + } + if (isString(url)) { + var baseUrl = url.split('#')[0]; + if (isString(remoteDest)) { + // In practice, a named destination may contain only a number. + // If that happens, use the '#nameddest=' form to avoid the link + // redirecting to a page, instead of the correct destination. + url = baseUrl + '#' + (/^\d+$/.test(remoteDest) ? 'nameddest=' : '') + remoteDest; + } else if (isArray(remoteDest)) { + url = baseUrl + '#' + JSON.stringify(remoteDest); + } + } + } + // The 'NewWindow' property, equal to `LinkTarget.BLANK`. + var newWindow = action.get('NewWindow'); + if (isBool(newWindow)) { + resultObj.newWindow = newWindow; + } + break; + case 'Named': + var namedAction = action.get('N'); + if (isName(namedAction)) { + resultObj.action = namedAction.name; + } + break; + default: + warn('Catalog_parseDestDictionary: Unrecognized link type "' + linkType + '".'); + break; + } + } else if (destDict.has('Dest')) { + // Simple destination link. + dest = destDict.get('Dest'); + } + if (isString(url)) { + url = tryConvertUrlEncoding(url); + var absoluteUrl = createValidAbsoluteUrl(url, docBaseUrl); + if (absoluteUrl) { + resultObj.url = absoluteUrl.href; + } + resultObj.unsafeUrl = url; + } + if (dest) { + if (isName(dest)) { + dest = dest.name; + } + if (isString(dest) || isArray(dest)) { + resultObj.dest = dest; + } + } + }; return Catalog; }(); var XRef = function XRefClosure() { @@ -58879,14 +59015,11 @@ var AnnotationType = sharedUtil.AnnotationType; var OPS = sharedUtil.OPS; var Util = sharedUtil.Util; - var isBool = sharedUtil.isBool; var isString = sharedUtil.isString; var isArray = sharedUtil.isArray; var isInt = sharedUtil.isInt; - var isValidUrl = sharedUtil.isValidUrl; var stringToBytes = sharedUtil.stringToBytes; var stringToPDFString = sharedUtil.stringToPDFString; - var stringToUTF8String = sharedUtil.stringToUTF8String; var warn = sharedUtil.warn; var Dict = corePrimitives.Dict; var isDict = corePrimitives.isDict; @@ -58894,6 +59027,7 @@ var isRef = corePrimitives.isRef; var Stream = coreStream.Stream; var ColorSpace = coreColorSpace.ColorSpace; + var Catalog = coreObj.Catalog; var ObjectLoader = coreObj.ObjectLoader; var FileSpec = coreObj.FileSpec; var OperatorList = coreEvaluator.OperatorList; @@ -58908,11 +59042,12 @@ /** * @param {XRef} xref * @param {Object} ref + * @param {PDFManager} pdfManager * @param {string} uniquePrefix * @param {Object} idCounters * @returns {Annotation} */ - create: function AnnotationFactory_create(xref, ref, uniquePrefix, idCounters) { + create: function AnnotationFactory_create(xref, ref, pdfManager, uniquePrefix, idCounters) { var dict = xref.fetchIfRef(ref); if (!isDict(dict)) { return; @@ -58927,7 +59062,8 @@ dict: dict, ref: isRef(ref) ? ref : null, subtype: subtype, - id: id + id: id, + pdfManager: pdfManager }; switch (subtype) { case 'Link': @@ -59594,95 +59730,13 @@ var LinkAnnotation = function LinkAnnotationClosure() { function LinkAnnotation(params) { Annotation.call(this, params); - var dict = params.dict; var data = this.data; data.annotationType = AnnotationType.LINK; - var action = dict.get('A'), url, dest; - if (action && isDict(action)) { - var linkType = action.get('S').name; - switch (linkType) { - case 'URI': - url = action.get('URI'); - if (isName(url)) { - // Some bad PDFs do not put parentheses around relative URLs. - url = '/' + url.name; - } else if (url) { - url = addDefaultProtocolToUrl(url); - } - // TODO: pdf spec mentions urls can be relative to a Base - // entry in the dictionary. - break; - case 'GoTo': - dest = action.get('D'); - break; - case 'GoToR': - var urlDict = action.get('F'); - if (isDict(urlDict)) { - // We assume that we found a FileSpec dictionary - // and fetch the URL without checking any further. - url = urlDict.get('F') || null; - } else if (isString(urlDict)) { - url = urlDict; - } - // NOTE: the destination is relative to the *remote* document. - var remoteDest = action.get('D'); - if (remoteDest) { - if (isName(remoteDest)) { - remoteDest = remoteDest.name; - } - if (isString(url)) { - var baseUrl = url.split('#')[0]; - if (isString(remoteDest)) { - // In practice, a named destination may contain only a number. - // If that happens, use the '#nameddest=' form to avoid the link - // redirecting to a page, instead of the correct destination. - url = baseUrl + '#' + (/^\d+$/.test(remoteDest) ? 'nameddest=' : '') + remoteDest; - } else if (isArray(remoteDest)) { - url = baseUrl + '#' + JSON.stringify(remoteDest); - } - } - } - // The 'NewWindow' property, equal to `LinkTarget.BLANK`. - var newWindow = action.get('NewWindow'); - if (isBool(newWindow)) { - data.newWindow = newWindow; - } - break; - case 'Named': - data.action = action.get('N').name; - break; - default: - warn('unrecognized link type: ' + linkType); - } - } else if (dict.has('Dest')) { - // Simple destination link. - dest = dict.get('Dest'); - } - if (url) { - if (isValidUrl(url, /* allowRelative = */ - false)) { - data.url = tryConvertUrlEncoding(url); - } - } - if (dest) { - data.dest = isName(dest) ? dest.name : dest; - } - } - // Lets URLs beginning with 'www.' default to using the 'http://' protocol. - function addDefaultProtocolToUrl(url) { - if (isString(url) && url.indexOf('www.') === 0) { - return 'http://' + url; - } - return url; - } - function tryConvertUrlEncoding(url) { - // According to ISO 32000-1:2008, section 12.6.4.7, URIs should be encoded - // in 7-bit ASCII. Some bad PDFs use UTF-8 encoding, see Bugzilla 1122280. - try { - return stringToUTF8String(url); - } catch (e) { - return url; - } + Catalog.parseDestDictionary({ + destDict: params.dict, + resultObj: data, + docBaseUrl: params.pdfManager.docBaseUrl + }); } Util.inherit(LinkAnnotation, Annotation, {}); return LinkAnnotation; @@ -60032,7 +60086,7 @@ var annotationFactory = new AnnotationFactory(); for (var i = 0, n = annotationRefs.length; i < n; ++i) { var annotationRef = annotationRefs[i]; - var annotation = annotationFactory.create(this.xref, annotationRef, this.uniquePrefix, this.idCounters); + var annotation = annotationFactory.create(this.xref, annotationRef, this.pdfManager, this.uniquePrefix, this.idCounters); if (annotation) { annotations.push(annotation); } @@ -60303,6 +60357,9 @@ (function (root, factory) { factory(root.pdfjsCorePdfManager = {}, root.pdfjsSharedUtil, root.pdfjsCoreStream, root.pdfjsCoreChunkedStream, root.pdfjsCoreDocument); }(this, function (exports, sharedUtil, coreStream, coreChunkedStream, coreDocument) { + var warn = sharedUtil.warn; + var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl; + var shadow = sharedUtil.shadow; var NotImplementedException = sharedUtil.NotImplementedException; var MissingDataException = sharedUtil.MissingDataException; var createPromiseCapability = sharedUtil.createPromiseCapability; @@ -60318,6 +60375,18 @@ get docId() { return this._docId; }, + get docBaseUrl() { + var docBaseUrl = null; + if (this._docBaseUrl) { + var absoluteUrl = createValidAbsoluteUrl(this._docBaseUrl); + if (absoluteUrl) { + docBaseUrl = absoluteUrl.href; + } else { + warn('Invalid absolute docBaseUrl: "' + this._docBaseUrl + '".'); + } + } + return shadow(this, 'docBaseUrl', docBaseUrl); + }, onLoadedStream: function BasePdfManager_onLoadedStream() { throw new NotImplementedException(); }, @@ -60365,8 +60434,9 @@ return BasePdfManager; }(); var LocalPdfManager = function LocalPdfManagerClosure() { - function LocalPdfManager(docId, data, password, evaluatorOptions) { + function LocalPdfManager(docId, data, password, evaluatorOptions, docBaseUrl) { this._docId = docId; + this._docBaseUrl = docBaseUrl; this.evaluatorOptions = evaluatorOptions; var stream = new Stream(data); this.pdfDocument = new PDFDocument(this, stream, password); @@ -60406,8 +60476,9 @@ return LocalPdfManager; }(); var NetworkPdfManager = function NetworkPdfManagerClosure() { - function NetworkPdfManager(docId, pdfNetworkStream, args, evaluatorOptions) { + function NetworkPdfManager(docId, pdfNetworkStream, args, evaluatorOptions, docBaseUrl) { this._docId = docId; + this._docBaseUrl = docBaseUrl; this.msgHandler = args.msgHandler; this.evaluatorOptions = evaluatorOptions; var params = { @@ -60786,6 +60857,7 @@ var cancelXHRs = null; var WorkerTasks = []; var docId = docParams.docId; + var docBaseUrl = docParams.docBaseUrl; var workerHandlerName = docParams.docId + '_worker'; var handler = new MessageHandler(workerHandlerName, docId, port); // Ensure that postMessage transfers are correctly enabled/disabled, @@ -60839,7 +60911,7 @@ var source = data.source; if (source.data) { try { - pdfManager = new LocalPdfManager(docId, source.data, source.password, evaluatorOptions); + pdfManager = new LocalPdfManager(docId, source.data, source.password, evaluatorOptions, docBaseUrl); pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); @@ -60882,7 +60954,7 @@ length: fullRequest.contentLength, disableAutoFetch: disableAutoFetch, rangeChunkSize: source.rangeChunkSize - }, evaluatorOptions); + }, evaluatorOptions, docBaseUrl); pdfManagerCapability.resolve(pdfManager); cancelXHRs = null; }).catch(function (reason) { @@ -60897,7 +60969,7 @@ } // the data is array, instantiating directly from it try { - pdfManager = new LocalPdfManager(docId, pdfFile, source.password, evaluatorOptions); + pdfManager = new LocalPdfManager(docId, pdfFile, source.password, evaluatorOptions, docBaseUrl); pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); @@ -61754,7 +61826,8 @@ exports.UnexpectedResponseException = pdfjsLibs.pdfjsSharedUtil.UnexpectedResponseException; exports.OPS = pdfjsLibs.pdfjsSharedUtil.OPS; exports.UNSUPPORTED_FEATURES = pdfjsLibs.pdfjsSharedUtil.UNSUPPORTED_FEATURES; - exports.isValidUrl = pdfjsLibs.pdfjsSharedUtil.isValidUrl; + exports.isValidUrl = pdfjsLibs.pdfjsDisplayDOMUtils.isValidUrl; + exports.createValidAbsoluteUrl = pdfjsLibs.pdfjsSharedUtil.createValidAbsoluteUrl; exports.createObjectURL = pdfjsLibs.pdfjsSharedUtil.createObjectURL; exports.removeNullCharacters = pdfjsLibs.pdfjsSharedUtil.removeNullCharacters; exports.shadow = pdfjsLibs.pdfjsSharedUtil.shadow; diff --git a/build/pdf.js b/build/pdf.js index 97a9e1c93..511891f18 100644 --- a/build/pdf.js +++ b/build/pdf.js @@ -24,8 +24,8 @@ }(this, function (exports) { // Use strict in our context only - users might not want it 'use strict'; - var pdfjsVersion = '1.6.258'; - var pdfjsBuild = '6906623'; + var pdfjsVersion = '1.6.263'; + var pdfjsBuild = '7e392c0'; var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null; var pdfjsLibs = {}; (function pdfjsWrapper() { @@ -320,29 +320,41 @@ var other = new URL(otherUrl, base); return base.origin === other.origin; } - // Validates if URL is safe and allowed, e.g. to avoid XSS. - function isValidUrl(url, allowRelative) { - if (!url || typeof url !== 'string') { + // Checks if URLs use one of the whitelisted protocols, e.g. to avoid XSS. + function isValidProtocol(url) { + if (!url) { return false; } - // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1) - // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url); - if (!protocol) { - return allowRelative; - } - protocol = protocol[0].toLowerCase(); - switch (protocol) { - case 'http': - case 'https': - case 'ftp': - case 'mailto': - case 'tel': + switch (url.protocol) { + case 'http:': + case 'https:': + case 'ftp:': + case 'mailto:': + case 'tel:': return true; default: return false; } } + /** + * Attempts to create a valid absolute URL (utilizing `isValidProtocol`). + * @param {URL|string} url - An absolute, or relative, URL. + * @param {URL|string} baseUrl - An absolute URL. + * @returns Either a valid {URL}, or `null` otherwise. + */ + function createValidAbsoluteUrl(url, baseUrl) { + if (!url) { + return null; + } + try { + var absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url); + if (isValidProtocol(absoluteUrl)) { + return absoluteUrl; + } + } catch (ex) { + } + return null; + } function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, @@ -2449,7 +2461,7 @@ exports.isString = isString; exports.isSpace = isSpace; exports.isSameOrigin = isSameOrigin; - exports.isValidUrl = isValidUrl; + exports.createValidAbsoluteUrl = createValidAbsoluteUrl; exports.isLittleEndian = isLittleEndian; exports.isEvalSupported = isEvalSupported; exports.loadJpegStream = loadJpegStream; @@ -2472,6 +2484,8 @@ }(this, function (exports, sharedUtil) { var removeNullCharacters = sharedUtil.removeNullCharacters; var warn = sharedUtil.warn; + var deprecated = sharedUtil.deprecated; + var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl; /** * Optimised CSS custom property getter/setter. * @class @@ -2650,9 +2664,15 @@ return true; } } + function isValidUrl(url, allowRelative) { + deprecated('isValidUrl(), please use createValidAbsoluteUrl() instead.'); + var baseUrl = allowRelative ? 'http://example.com' : null; + return createValidAbsoluteUrl(url, baseUrl) !== null; + } exports.CustomStyle = CustomStyle; exports.addLinkAttributes = addLinkAttributes; exports.isExternalLinkTargetSet = isExternalLinkTargetSet; + exports.isValidUrl = isValidUrl; exports.getFilenameFromUrl = getFilenameFromUrl; exports.LinkTarget = LinkTarget; exports.hasCanvasTypedArrays = hasCanvasTypedArrays; @@ -8539,6 +8559,9 @@ * 2^16 = 65536. * @property {PDFWorker} worker - The worker that will be used for the loading * and parsing of the PDF data. + * @property {string} docBaseUrl - (optional) The base URL of the document, + * used when attempting to recover valid absolute URLs for annotations, and + * outline items, that (incorrectly) only specify relative URLs. */ /** * @typedef {Object} PDFDocumentStats @@ -8693,7 +8716,8 @@ cMapPacked: getDefaultSetting('cMapPacked'), disableFontFace: getDefaultSetting('disableFontFace'), disableCreateObjectURL: getDefaultSetting('disableCreateObjectURL'), - postMessageTransfers: getDefaultSetting('postMessageTransfers') && !isPostMessageTransfersDisabled + postMessageTransfers: getDefaultSetting('postMessageTransfers') && !isPostMessageTransfersDisabled, + docBaseUrl: source.docBaseUrl }).then(function (workerId) { if (worker.destroyed) { throw new Error('Worker was destroyed'); @@ -10339,7 +10363,7 @@ PDFJS.VERBOSITY_LEVELS = sharedUtil.VERBOSITY_LEVELS; PDFJS.OPS = sharedUtil.OPS; PDFJS.UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES; - PDFJS.isValidUrl = sharedUtil.isValidUrl; + PDFJS.isValidUrl = displayDOMUtils.isValidUrl; PDFJS.shadow = sharedUtil.shadow; PDFJS.createBlob = sharedUtil.createBlob; PDFJS.createObjectURL = function PDFJS_createObjectURL(data, contentType) { @@ -10545,7 +10569,8 @@ exports.UnexpectedResponseException = pdfjsLibs.pdfjsSharedUtil.UnexpectedResponseException; exports.OPS = pdfjsLibs.pdfjsSharedUtil.OPS; exports.UNSUPPORTED_FEATURES = pdfjsLibs.pdfjsSharedUtil.UNSUPPORTED_FEATURES; - exports.isValidUrl = pdfjsLibs.pdfjsSharedUtil.isValidUrl; + exports.isValidUrl = pdfjsLibs.pdfjsDisplayDOMUtils.isValidUrl; + exports.createValidAbsoluteUrl = pdfjsLibs.pdfjsSharedUtil.createValidAbsoluteUrl; exports.createObjectURL = pdfjsLibs.pdfjsSharedUtil.createObjectURL; exports.removeNullCharacters = pdfjsLibs.pdfjsSharedUtil.removeNullCharacters; exports.shadow = pdfjsLibs.pdfjsSharedUtil.shadow; diff --git a/build/pdf.worker.js b/build/pdf.worker.js index f3db8a12c..e97ddbed7 100644 --- a/build/pdf.worker.js +++ b/build/pdf.worker.js @@ -24,8 +24,8 @@ }(this, function (exports) { // Use strict in our context only - users might not want it 'use strict'; - var pdfjsVersion = '1.6.258'; - var pdfjsBuild = '6906623'; + var pdfjsVersion = '1.6.263'; + var pdfjsBuild = '7e392c0'; var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null; var pdfjsLibs = {}; (function pdfjsWrapper() { @@ -3877,29 +3877,41 @@ var other = new URL(otherUrl, base); return base.origin === other.origin; } - // Validates if URL is safe and allowed, e.g. to avoid XSS. - function isValidUrl(url, allowRelative) { - if (!url || typeof url !== 'string') { + // Checks if URLs use one of the whitelisted protocols, e.g. to avoid XSS. + function isValidProtocol(url) { + if (!url) { return false; } - // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1) - // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url); - if (!protocol) { - return allowRelative; - } - protocol = protocol[0].toLowerCase(); - switch (protocol) { - case 'http': - case 'https': - case 'ftp': - case 'mailto': - case 'tel': + switch (url.protocol) { + case 'http:': + case 'https:': + case 'ftp:': + case 'mailto:': + case 'tel:': return true; default: return false; } } + /** + * Attempts to create a valid absolute URL (utilizing `isValidProtocol`). + * @param {URL|string} url - An absolute, or relative, URL. + * @param {URL|string} baseUrl - An absolute URL. + * @returns Either a valid {URL}, or `null` otherwise. + */ + function createValidAbsoluteUrl(url, baseUrl) { + if (!url) { + return null; + } + try { + var absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url); + if (isValidProtocol(absoluteUrl)) { + return absoluteUrl; + } + } catch (ex) { + } + return null; + } function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, @@ -6006,7 +6018,7 @@ exports.isString = isString; exports.isSpace = isSpace; exports.isSameOrigin = isSameOrigin; - exports.isValidUrl = isValidUrl; + exports.createValidAbsoluteUrl = createValidAbsoluteUrl; exports.isLittleEndian = isLittleEndian; exports.isEvalSupported = isEvalSupported; exports.loadJpegStream = loadJpegStream; @@ -45331,13 +45343,14 @@ var error = sharedUtil.error; var info = sharedUtil.info; var isArray = sharedUtil.isArray; + var isBool = sharedUtil.isBool; var isInt = sharedUtil.isInt; var isString = sharedUtil.isString; var shadow = sharedUtil.shadow; var stringToPDFString = sharedUtil.stringToPDFString; var stringToUTF8String = sharedUtil.stringToUTF8String; var warn = sharedUtil.warn; - var isValidUrl = sharedUtil.isValidUrl; + var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl; var Util = sharedUtil.Util; var Ref = corePrimitives.Ref; var RefSet = corePrimitives.RefSet; @@ -45434,23 +45447,15 @@ continue; } assert(outlineDict.has('Title'), 'Invalid outline item'); - var actionDict = outlineDict.get('A'), dest = null, url = null; - if (actionDict) { - var destEntry = actionDict.get('D'); - if (destEntry) { - dest = destEntry; - } else { - var uriEntry = actionDict.get('URI'); - if (isString(uriEntry) && isValidUrl(uriEntry, false)) { - url = uriEntry; - } - } - } else if (outlineDict.has('Dest')) { - dest = outlineDict.getRaw('Dest'); - if (isName(dest)) { - dest = dest.name; - } - } + var data = { + url: null, + dest: null + }; + Catalog.parseDestDictionary({ + destDict: outlineDict, + resultObj: data, + docBaseUrl: this.pdfManager.docBaseUrl + }); var title = outlineDict.get('Title'); var flags = outlineDict.get('F') || 0; var color = outlineDict.getArray('C'), rgbColor = blackColor; @@ -45459,8 +45464,9 @@ rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0); } var outlineItem = { - dest: dest, - url: url, + dest: data.dest, + url: data.url, + newWindow: data.newWindow, title: stringToPDFString(title), color: rgbColor, count: outlineDict.get('Count'), @@ -45847,6 +45853,124 @@ return next(pageRef); } }; + /** + * Helper function used to parse the contents of destination dictionaries. + * @param {Dict} destDict - The dictionary containing the destination. + * @param {Object} resultObj - The object where the parsed destination + * properties will be placed. + * @param {string} docBaseUrl - (optional) The document base URL that is used + * when attempting to recover valid absolute URLs from relative ones. + */ + Catalog.parseDestDictionary = function Catalog_parseDestDictionary(params) { + // Lets URLs beginning with 'www.' default to using the 'http://' protocol. + function addDefaultProtocolToUrl(url) { + if (url.indexOf('www.') === 0) { + return 'http://' + url; + } + return url; + } + // According to ISO 32000-1:2008, section 12.6.4.7, URIs should be encoded + // in 7-bit ASCII. Some bad PDFs use UTF-8 encoding, see Bugzilla 1122280. + function tryConvertUrlEncoding(url) { + try { + return stringToUTF8String(url); + } catch (e) { + return url; + } + } + var destDict = params.destDict; + if (!isDict(destDict)) { + warn('Catalog_parseDestDictionary: "destDict" must be a dictionary.'); + return; + } + var resultObj = params.resultObj; + if (typeof resultObj !== 'object') { + warn('Catalog_parseDestDictionary: "resultObj" must be an object.'); + return; + } + var docBaseUrl = params.docBaseUrl || null; + var action = destDict.get('A'), url, dest; + if (isDict(action)) { + var linkType = action.get('S').name; + switch (linkType) { + case 'URI': + url = action.get('URI'); + if (isName(url)) { + // Some bad PDFs do not put parentheses around relative URLs. + url = '/' + url.name; + } else if (isString(url)) { + url = addDefaultProtocolToUrl(url); + } + // TODO: pdf spec mentions urls can be relative to a Base + // entry in the dictionary. + break; + case 'GoTo': + dest = action.get('D'); + break; + case 'GoToR': + var urlDict = action.get('F'); + if (isDict(urlDict)) { + // We assume that we found a FileSpec dictionary + // and fetch the URL without checking any further. + url = urlDict.get('F') || null; + } else if (isString(urlDict)) { + url = urlDict; + } + // NOTE: the destination is relative to the *remote* document. + var remoteDest = action.get('D'); + if (remoteDest) { + if (isName(remoteDest)) { + remoteDest = remoteDest.name; + } + if (isString(url)) { + var baseUrl = url.split('#')[0]; + if (isString(remoteDest)) { + // In practice, a named destination may contain only a number. + // If that happens, use the '#nameddest=' form to avoid the link + // redirecting to a page, instead of the correct destination. + url = baseUrl + '#' + (/^\d+$/.test(remoteDest) ? 'nameddest=' : '') + remoteDest; + } else if (isArray(remoteDest)) { + url = baseUrl + '#' + JSON.stringify(remoteDest); + } + } + } + // The 'NewWindow' property, equal to `LinkTarget.BLANK`. + var newWindow = action.get('NewWindow'); + if (isBool(newWindow)) { + resultObj.newWindow = newWindow; + } + break; + case 'Named': + var namedAction = action.get('N'); + if (isName(namedAction)) { + resultObj.action = namedAction.name; + } + break; + default: + warn('Catalog_parseDestDictionary: Unrecognized link type "' + linkType + '".'); + break; + } + } else if (destDict.has('Dest')) { + // Simple destination link. + dest = destDict.get('Dest'); + } + if (isString(url)) { + url = tryConvertUrlEncoding(url); + var absoluteUrl = createValidAbsoluteUrl(url, docBaseUrl); + if (absoluteUrl) { + resultObj.url = absoluteUrl.href; + } + resultObj.unsafeUrl = url; + } + if (dest) { + if (isName(dest)) { + dest = dest.name; + } + if (isString(dest) || isArray(dest)) { + resultObj.dest = dest; + } + } + }; return Catalog; }(); var XRef = function XRefClosure() { @@ -50954,14 +51078,11 @@ var AnnotationType = sharedUtil.AnnotationType; var OPS = sharedUtil.OPS; var Util = sharedUtil.Util; - var isBool = sharedUtil.isBool; var isString = sharedUtil.isString; var isArray = sharedUtil.isArray; var isInt = sharedUtil.isInt; - var isValidUrl = sharedUtil.isValidUrl; var stringToBytes = sharedUtil.stringToBytes; var stringToPDFString = sharedUtil.stringToPDFString; - var stringToUTF8String = sharedUtil.stringToUTF8String; var warn = sharedUtil.warn; var Dict = corePrimitives.Dict; var isDict = corePrimitives.isDict; @@ -50969,6 +51090,7 @@ var isRef = corePrimitives.isRef; var Stream = coreStream.Stream; var ColorSpace = coreColorSpace.ColorSpace; + var Catalog = coreObj.Catalog; var ObjectLoader = coreObj.ObjectLoader; var FileSpec = coreObj.FileSpec; var OperatorList = coreEvaluator.OperatorList; @@ -50983,11 +51105,12 @@ /** * @param {XRef} xref * @param {Object} ref + * @param {PDFManager} pdfManager * @param {string} uniquePrefix * @param {Object} idCounters * @returns {Annotation} */ - create: function AnnotationFactory_create(xref, ref, uniquePrefix, idCounters) { + create: function AnnotationFactory_create(xref, ref, pdfManager, uniquePrefix, idCounters) { var dict = xref.fetchIfRef(ref); if (!isDict(dict)) { return; @@ -51002,7 +51125,8 @@ dict: dict, ref: isRef(ref) ? ref : null, subtype: subtype, - id: id + id: id, + pdfManager: pdfManager }; switch (subtype) { case 'Link': @@ -51669,95 +51793,13 @@ var LinkAnnotation = function LinkAnnotationClosure() { function LinkAnnotation(params) { Annotation.call(this, params); - var dict = params.dict; var data = this.data; data.annotationType = AnnotationType.LINK; - var action = dict.get('A'), url, dest; - if (action && isDict(action)) { - var linkType = action.get('S').name; - switch (linkType) { - case 'URI': - url = action.get('URI'); - if (isName(url)) { - // Some bad PDFs do not put parentheses around relative URLs. - url = '/' + url.name; - } else if (url) { - url = addDefaultProtocolToUrl(url); - } - // TODO: pdf spec mentions urls can be relative to a Base - // entry in the dictionary. - break; - case 'GoTo': - dest = action.get('D'); - break; - case 'GoToR': - var urlDict = action.get('F'); - if (isDict(urlDict)) { - // We assume that we found a FileSpec dictionary - // and fetch the URL without checking any further. - url = urlDict.get('F') || null; - } else if (isString(urlDict)) { - url = urlDict; - } - // NOTE: the destination is relative to the *remote* document. - var remoteDest = action.get('D'); - if (remoteDest) { - if (isName(remoteDest)) { - remoteDest = remoteDest.name; - } - if (isString(url)) { - var baseUrl = url.split('#')[0]; - if (isString(remoteDest)) { - // In practice, a named destination may contain only a number. - // If that happens, use the '#nameddest=' form to avoid the link - // redirecting to a page, instead of the correct destination. - url = baseUrl + '#' + (/^\d+$/.test(remoteDest) ? 'nameddest=' : '') + remoteDest; - } else if (isArray(remoteDest)) { - url = baseUrl + '#' + JSON.stringify(remoteDest); - } - } - } - // The 'NewWindow' property, equal to `LinkTarget.BLANK`. - var newWindow = action.get('NewWindow'); - if (isBool(newWindow)) { - data.newWindow = newWindow; - } - break; - case 'Named': - data.action = action.get('N').name; - break; - default: - warn('unrecognized link type: ' + linkType); - } - } else if (dict.has('Dest')) { - // Simple destination link. - dest = dict.get('Dest'); - } - if (url) { - if (isValidUrl(url, /* allowRelative = */ - false)) { - data.url = tryConvertUrlEncoding(url); - } - } - if (dest) { - data.dest = isName(dest) ? dest.name : dest; - } - } - // Lets URLs beginning with 'www.' default to using the 'http://' protocol. - function addDefaultProtocolToUrl(url) { - if (isString(url) && url.indexOf('www.') === 0) { - return 'http://' + url; - } - return url; - } - function tryConvertUrlEncoding(url) { - // According to ISO 32000-1:2008, section 12.6.4.7, URIs should be encoded - // in 7-bit ASCII. Some bad PDFs use UTF-8 encoding, see Bugzilla 1122280. - try { - return stringToUTF8String(url); - } catch (e) { - return url; - } + Catalog.parseDestDictionary({ + destDict: params.dict, + resultObj: data, + docBaseUrl: params.pdfManager.docBaseUrl + }); } Util.inherit(LinkAnnotation, Annotation, {}); return LinkAnnotation; @@ -52107,7 +52149,7 @@ var annotationFactory = new AnnotationFactory(); for (var i = 0, n = annotationRefs.length; i < n; ++i) { var annotationRef = annotationRefs[i]; - var annotation = annotationFactory.create(this.xref, annotationRef, this.uniquePrefix, this.idCounters); + var annotation = annotationFactory.create(this.xref, annotationRef, this.pdfManager, this.uniquePrefix, this.idCounters); if (annotation) { annotations.push(annotation); } @@ -52378,6 +52420,9 @@ (function (root, factory) { factory(root.pdfjsCorePdfManager = {}, root.pdfjsSharedUtil, root.pdfjsCoreStream, root.pdfjsCoreChunkedStream, root.pdfjsCoreDocument); }(this, function (exports, sharedUtil, coreStream, coreChunkedStream, coreDocument) { + var warn = sharedUtil.warn; + var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl; + var shadow = sharedUtil.shadow; var NotImplementedException = sharedUtil.NotImplementedException; var MissingDataException = sharedUtil.MissingDataException; var createPromiseCapability = sharedUtil.createPromiseCapability; @@ -52393,6 +52438,18 @@ get docId() { return this._docId; }, + get docBaseUrl() { + var docBaseUrl = null; + if (this._docBaseUrl) { + var absoluteUrl = createValidAbsoluteUrl(this._docBaseUrl); + if (absoluteUrl) { + docBaseUrl = absoluteUrl.href; + } else { + warn('Invalid absolute docBaseUrl: "' + this._docBaseUrl + '".'); + } + } + return shadow(this, 'docBaseUrl', docBaseUrl); + }, onLoadedStream: function BasePdfManager_onLoadedStream() { throw new NotImplementedException(); }, @@ -52440,8 +52497,9 @@ return BasePdfManager; }(); var LocalPdfManager = function LocalPdfManagerClosure() { - function LocalPdfManager(docId, data, password, evaluatorOptions) { + function LocalPdfManager(docId, data, password, evaluatorOptions, docBaseUrl) { this._docId = docId; + this._docBaseUrl = docBaseUrl; this.evaluatorOptions = evaluatorOptions; var stream = new Stream(data); this.pdfDocument = new PDFDocument(this, stream, password); @@ -52481,8 +52539,9 @@ return LocalPdfManager; }(); var NetworkPdfManager = function NetworkPdfManagerClosure() { - function NetworkPdfManager(docId, pdfNetworkStream, args, evaluatorOptions) { + function NetworkPdfManager(docId, pdfNetworkStream, args, evaluatorOptions, docBaseUrl) { this._docId = docId; + this._docBaseUrl = docBaseUrl; this.msgHandler = args.msgHandler; this.evaluatorOptions = evaluatorOptions; var params = { @@ -52861,6 +52920,7 @@ var cancelXHRs = null; var WorkerTasks = []; var docId = docParams.docId; + var docBaseUrl = docParams.docBaseUrl; var workerHandlerName = docParams.docId + '_worker'; var handler = new MessageHandler(workerHandlerName, docId, port); // Ensure that postMessage transfers are correctly enabled/disabled, @@ -52914,7 +52974,7 @@ var source = data.source; if (source.data) { try { - pdfManager = new LocalPdfManager(docId, source.data, source.password, evaluatorOptions); + pdfManager = new LocalPdfManager(docId, source.data, source.password, evaluatorOptions, docBaseUrl); pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); @@ -52957,7 +53017,7 @@ length: fullRequest.contentLength, disableAutoFetch: disableAutoFetch, rangeChunkSize: source.rangeChunkSize - }, evaluatorOptions); + }, evaluatorOptions, docBaseUrl); pdfManagerCapability.resolve(pdfManager); cancelXHRs = null; }).catch(function (reason) { @@ -52972,7 +53032,7 @@ } // the data is array, instantiating directly from it try { - pdfManager = new LocalPdfManager(docId, pdfFile, source.password, evaluatorOptions); + pdfManager = new LocalPdfManager(docId, pdfFile, source.password, evaluatorOptions, docBaseUrl); pdfManagerCapability.resolve(pdfManager); } catch (ex) { pdfManagerCapability.reject(ex); diff --git a/package.json b/package.json index d8d58b313..d33a2fff1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pdfjs-dist", - "version": "1.6.258", + "version": "1.6.263", "main": "build/pdf.js", "description": "Generic build of Mozilla's PDF.js library.", "keywords": [ diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 5f0425554..0c0faeab4 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -215,7 +215,7 @@ } DownloadManager.prototype = { downloadUrl: function DownloadManager_downloadUrl(url, filename) { - if (!pdfjsLib.isValidUrl(url, true)) { + if (!pdfjsLib.createValidAbsoluteUrl(url, 'http://example.com')) { return; } // restricted/invalid URL