diff --git a/external/jpgjs/LICENSE b/external/jpgjs/LICENSE index f28619c6a..1e29c87c8 100644 --- a/external/jpgjs/LICENSE +++ b/external/jpgjs/LICENSE @@ -1,24 +1,17 @@ -Copyright (C) 2011 by notmasteryet + Copyright (C) 2011 notmasteryet -Contributors: Yury Delendik - Brendan Dahl + Contributors: Yury Delendik + Brendan Dahl -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: + 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 -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. + http://www.apache.org/licenses/LICENSE-2.0 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + 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. diff --git a/external/jpgjs/jpg.js b/external/jpgjs/jpg.js index 103153d9d..941d01a87 100644 --- a/external/jpgjs/jpg.js +++ b/external/jpgjs/jpg.js @@ -1,5 +1,20 @@ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* + Copyright 2011 notmasteryet + + 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. +*/ // - The JPEG specification can be found in the ITU CCITT Recommendation T.81 // (www.w3.org/Graphics/JPEG/itu-t81.pdf) @@ -627,8 +642,9 @@ var JpegImage = (function jpegImage() { break; case 0xFFDB: // DQT (Define Quantization Tables) - var quantizationTableCount = Math.floor((readUint16() - 2) / 65); - for (i = 0; i < quantizationTableCount; i++) { + var quantizationTablesLength = readUint16(); + var quantizationTablesEnd = quantizationTablesLength + offset - 2; + while (offset < quantizationTablesEnd) { var quantizationTableSpec = data[offset++]; var tableData = new Int32Array(64); if ((quantizationTableSpec >> 4) === 0) { // 8 bit values @@ -721,6 +737,13 @@ var JpegImage = (function jpegImage() { offset += processed; break; default: + if (data[offset - 3] == 0xFF && + data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) { + // could be incorrect encoding -- last 0xFF byte of the previous + // block was eaten by the encoder + offset -= 3; + break; + } throw "unknown JPEG marker " + fileMarker.toString(16); } fileMarker = readUint16(); diff --git a/make.js b/make.js index a42ae7fd2..7299bbf3c 100755 --- a/make.js +++ b/make.js @@ -6,6 +6,7 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root BUILD_DIR = 'build/', BUILD_TARGET = BUILD_DIR + 'pdf.js', FIREFOX_BUILD_DIR = BUILD_DIR + '/firefox/', + CHROME_BUILD_DIR = BUILD_DIR + '/chrome/', EXTENSION_SRC_DIR = 'extensions/', LOCALE_SRC_DIR = 'l10n/', GH_PAGES_DIR = BUILD_DIR + 'gh-pages/', @@ -107,6 +108,8 @@ target.web = function() { cp('-R', GENERIC_DIR + '/*', GH_PAGES_DIR); cp(FIREFOX_BUILD_DIR + '/*.xpi', FIREFOX_BUILD_DIR + '/*.rdf', GH_PAGES_DIR + EXTENSION_SRC_DIR + 'firefox/'); + cp(CHROME_BUILD_DIR + '/*.crx', FIREFOX_BUILD_DIR + '/*.rdf', + GH_PAGES_DIR + EXTENSION_SRC_DIR + 'chrome/'); cp('web/index.html.template', GH_PAGES_DIR + '/index.html'); cd(GH_PAGES_DIR); @@ -258,6 +261,7 @@ target.pagesrepo = function() { mkdir('-p', GH_PAGES_DIR + '/web/images'); mkdir('-p', GH_PAGES_DIR + BUILD_DIR); mkdir('-p', GH_PAGES_DIR + EXTENSION_SRC_DIR + '/firefox'); + mkdir('-p', GH_PAGES_DIR + EXTENSION_SRC_DIR + '/chrome'); }; @@ -574,6 +578,70 @@ target.chrome = function() { ] }; builder.build(setup); + + // Bundle the files to a Chrome extension file .crx if path to key is set + var pem = env['PDFJS_CHROME_KEY']; + if (!pem) { + return; + } + + echo(); + echo('### Bundling .crx extension into ' + CHROME_BUILD_DIR); + + if (!test('-f', pem)) { + echo('Incorrect PDFJS_CHROME_KEY path'); + exit(1); + } + + var browserManifest = env['PDF_BROWSERS'] || + 'test/resources/browser_manifests/browser_manifest.json'; + + if (!test('-f', browserManifest)) { + echo('Browser manifest file ' + browserManifest + ' does not exist.'); + echo('Try copying one of the examples in test/resources/browser_manifests'); + exit(1); + } + + try { + var manifest = JSON.parse(cat(browserManifest)); + } catch (e) { + echo('Malformed browser manifest file'); + echo(e.message); + exit(1); + } + + var executable; + manifest.forEach(function(browser) { + if (browser.name === 'chrome') { + executable = browser.path; + } + }); + + // If there was no chrome entry in the browser manifest, exit + if (!executable) { + echo('There was no \'chrome\' entry in the browser manifest'); + exit(1); + } + + // If we're on a Darwin (Mac) OS, then let's check for an .app path + if (process.platform === 'darwin' && executable.indexOf('.app') !== -1) { + executable = executable + '/Contents/MacOS/Google Chrome'; + } + + // If the chrome executable doesn't exist + if (!test('-f', executable)) { + echo('Incorrect executable path to chrome'); + exit(1); + } + + // Let chrome pack the extension for us + exec('"' + executable + '"' + + ' --no-message-box' + + ' "--pack-extension=' + ROOT_DIR + CHROME_BUILD_DIR + '"' + + ' "--pack-extension-key=' + pem + '"'); + + // Rename to pdf.js.crx + mv(BUILD_DIR + 'chrome.crx', CHROME_BUILD_DIR + 'pdf.js.crx'); }; diff --git a/src/api.js b/src/api.js index 4e7ce4e76..6c862a91b 100644 --- a/src/api.js +++ b/src/api.js @@ -311,13 +311,19 @@ var PDFPageProxy = (function PDFPageProxyClosure() { ensureFonts: function PDFPageProxy_ensureFonts(fonts, callback) { this.stats.time('Font Loading'); // Convert the font names to the corresponding font obj. + var fontObjs = []; for (var i = 0, ii = fonts.length; i < ii; i++) { - fonts[i] = this.objs.objs[fonts[i]].data; + var obj = this.objs.objs[fonts[i]].data; + if (obj.error) { + warn('Error during font loading: ' + obj.error); + continue; + } + fontObjs.push(obj); } // Load all the fonts FontLoader.bind( - fonts, + fontObjs, function pageEnsureFontsFontObjs(fontObjs) { this.stats.timeEnd('Font Loading'); @@ -565,7 +571,12 @@ var WorkerTransport = (function WorkerTransportClosure() { // At this point, only the font object is created but the font is // not yet attached to the DOM. This is done in `FontLoader.bind`. - var font = new Font(name, file, properties); + var font; + try { + font = new Font(name, file, properties); + } catch (e) { + font = new ErrorFont(e); + } this.objs.resolve(id, font); break; default: diff --git a/src/fonts.js b/src/fonts.js index eda67df33..4af42265d 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -404,28 +404,15 @@ function mapPrivateUseChars(code) { } var FontLoader = { - listeningForFontLoad: false, + loadingContext: { + requests: [], + nextRequestId: 0 + }, bind: function fontLoaderBind(fonts, callback) { - function checkFontsLoaded() { - for (var i = 0, ii = fonts.length; i < ii; i++) { - var fontObj = fonts[i]; - if (fontObj.loading) { - return false; - } - } - - document.documentElement.removeEventListener( - 'pdfjsFontLoad', checkFontsLoaded, false); - - // Use timeout to fix chrome intermittent failures on font loading. - setTimeout(callback, 0); - return true; - } - - var rules = [], names = [], fontsToLoad = []; - var fontCreateTimer = 0; + assert(!isWorker, 'bind() shall be called from main thread'); + var rules = [], fontsToLoad = []; for (var i = 0, ii = fonts.length; i < ii; i++) { var font = fonts[i]; @@ -436,8 +423,6 @@ var FontLoader = { } font.attached = true; - fontsToLoad.push(font); - var str = ''; var data = font.data; if (data) { @@ -448,28 +433,51 @@ var FontLoader = { var rule = font.bindDOM(str); if (rule) { rules.push(rule); - names.push(font.loadedName); + fontsToLoad.push(font); } } } - this.listeningForFontLoad = false; - if (!isWorker && rules.length) { - FontLoader.prepareFontLoadEvent(rules, names, fontsToLoad); - } - - if (!checkFontsLoaded()) { - document.documentElement.addEventListener( - 'pdfjsFontLoad', checkFontsLoaded, false); + var request = FontLoader.queueLoadingCallback(callback); + if (rules.length > 0) { + FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request); + } else { + request.complete(); } }, + + queueLoadingCallback: function FontLoader_queueLoadingCallback(callback) { + function LoadLoader_completeRequest() { + assert(!request.end, 'completeRequest() cannot be called twice'); + request.end = Date.now(); + + // sending all completed requests in order how they were queued + while (context.requests.length > 0 && context.requests[0].end) { + var otherRequest = context.requests.shift(); + setTimeout(otherRequest.callback, 0); + } + } + + var context = FontLoader.loadingContext; + var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++); + var request = { + id: requestId, + complete: LoadLoader_completeRequest, + callback: callback, + started: Date.now() + }; + context.requests.push(request); + return request; + }, + // Set things up so that at least one pdfjsFontLoad event is - // dispatched when all the @font-face |rules| for |names| have been + // dispatched when all the @font-face |rules| for |fonts| have been // loaded in a subdocument. It's expected that the load of |rules| // has already started in this (outer) document, so that they should // be ordered before the load in the subdocument. - prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules, names, - fonts) { + prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules, + fonts, + request) { /** Hack begin */ // There's no event when a font has finished downloading so the // following code is a dirty hack to 'guess' when a font is @@ -493,6 +501,20 @@ var FontLoader = { // The postMessage() hackery was added to work around chrome bug // 82402. + var requestId = request.id; + // Validate the requestId parameter -- the value used to construct HTML. + if (!/^[\w\-]+$/.test(requestId)) { + error('Invalid request id: ' + requestId); + + // Normally the error-function throws. But if a malicious code + // intercepts the function call then the return is needed. + return; + } + + var names = []; + for (var i = 0, ii = fonts.length; i < ii; i++) + names.push(fonts[i].loadedName); + // Validate the names parameter -- the values can used to construct HTML. if (!/^\w+$/.test(names.join(''))) { error('Invalid font name(s): ' + names.join()); @@ -514,22 +536,21 @@ var FontLoader = { div.innerHTML = html; document.body.appendChild(div); - if (!this.listeningForFontLoad) { - window.addEventListener( - 'message', - function fontLoaderMessage(e) { - var fontNames = JSON.parse(e.data); - for (var i = 0, ii = fonts.length; i < ii; ++i) { - var font = fonts[i]; - font.loading = false; - } - var evt = document.createEvent('Events'); - evt.initEvent('pdfjsFontLoad', true, false); - document.documentElement.dispatchEvent(evt); - }, - false); - this.listeningForFontLoad = true; - } + window.addEventListener( + 'message', + function fontLoaderMessage(e) { + if (e.data !== requestId) + return; + for (var i = 0, ii = fonts.length; i < ii; ++i) { + var font = fonts[i]; + font.loading = false; + } + request.complete(); + // cleanup + document.body.removeChild(frame); + window.removeEventListener('message', fontLoaderMessage, false); + }, + false); // XXX we should have a time-out here too, and maybe fire // pdfjsFontLoadFailed? @@ -540,13 +561,8 @@ var FontLoader = { } src += ''; src += '