From 492fa6edb40fb905059c01b20270535e633247ad Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 1 Aug 2012 11:29:13 -0700 Subject: [PATCH] Add the new preprocessor. --- make.js | 430 +++++++++++++--------- src/api.js | 25 +- src/pdf.js | 10 +- web/viewer-snippet-firefox-extension.html | 5 +- web/viewer.html | 81 ++-- web/viewer.js | 195 +++++----- 6 files changed, 431 insertions(+), 315 deletions(-) diff --git a/make.js b/make.js index cc6274fac..bb9896391 100755 --- a/make.js +++ b/make.js @@ -1,6 +1,7 @@ #!/usr/bin/env node require('./external/shelljs/make'); var fs = require('fs'); +var path = require('path'); var vm = require('vm'); var ROOT_DIR = __dirname + '/', // absolute path to project's root @@ -10,6 +11,7 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root EXTENSION_SRC_DIR = 'extensions/', LOCALE_SRC_DIR = 'l10n/', GH_PAGES_DIR = BUILD_DIR + 'gh-pages/', + GENERIC_DIR = BUILD_DIR + 'generic/', REPO = 'git@github.com:mozilla/pdf.js.git', PYTHON_BIN = 'python2.7', MOZCENTRAL_PREF_PREFIX = 'pdfjs', @@ -17,48 +19,147 @@ var ROOT_DIR = __dirname + '/', // absolute path to project's root MOZCENTRAL_STREAM_CONVERTER_ID = 'd0c5195d-e798-49d4-b1d3-9324328b2291', FIREFOX_STREAM_CONVERTER_ID = '6457a96b-2d68-439a-bcfa-44465fbcdbb1'; -function preprocess(inFilename, outFilename, flags) { - // TODO make this really read line by line. - var lines = fs.readFileSync(inFilename).toString().split("\n"); - var totalLines = lines.length; - var out = ''; - var i = 0; - function readLine() { - if (i < totalLines) { - return lines[i++]; - } - return null; - } - function writeLine(line) { - out += line + '\n'; - } +var DEFINES = { + PRODUCTION: true, + // The main build targets: + GENERIC: false, + FIREFOX: false, + MOZCENTRAL: false, + B2G: false, + CHROME: false +}; - var s, state = 0, stack = []; - while ((s = readLine()) !== null) { - var m = new RegExp(/^\/\/\s*#(if|else|endif)\b(?:\s+(.*))?/).exec(s); - if (m) { - if (m[1] === "if") { - stack.push(state); - state = vm.runInNewContext(m[2], flags) ? 3 : 1; - } else if (m[1] === "else") { - state = state === 1 ? 3 : 2; +function extendDefines(obj) { + var ret = {}; + for (var key in DEFINES) + ret[key] = DEFINES[key]; + for (key in obj) + ret[key] = obj[key]; + return ret; +} + +var BuildHelper = { + /** + * A simple preprocessor that is based on the firefox preprocessor + * see (https://developer.mozilla.org/en/Build/Text_Preprocessor). The main + * difference is that this supports a subset of the commands and it supports + * preproccesor commands in html style comments. + * Currently Supported commands: + * - if + * - else + * - endif + * - include + * - expand + */ + preprocess: function preprocess(inFilename, outFilename, defines) { + // TODO make this really read line by line. + var lines = fs.readFileSync(inFilename).toString().split('\n'); + var totalLines = lines.length; + var out = ''; + var i = 0; + function readLine() { + if (i < totalLines) { + return lines[i++]; + } + return null; + } + var writeLine = typeof outFilename === 'function' ? outFilename : function(line) { + out += line + '\n'; + } + function include(file) { + var realPath = fs.realpathSync(inFilename); + var dir = path.dirname(realPath); + preprocess(path.join(dir, file), writeLine, defines); + } + function expand(line) { + line = line.replace(/__[\w]+__/g, function(variable) { + variable = variable.substring(2, variable.length - 2); + if (variable in defines) { + return defines[variable]; + } + return ''; + }); + writeLine(line); + } + + var s, state = 0, stack = []; + var control = /^(?:\/\/|)?$)?/; + var lineNumber = 0; + while ((s = readLine()) !== null) { + ++lineNumber; + var m = control.exec(s); + if (m) { + switch (m[1]) { + case 'if': + stack.push(state); + try { + state = vm.runInNewContext(m[2], defines) ? 3 : 1; + } catch (e) { + console.error('Could not evalute line \'' + m[2] + '\' at ' + + fs.realpathSync(inFilename) + ':' + lineNumber); + throw e; + } + break; + case 'else': + state = state === 1 ? 3 : 2; + break; + case 'endif': + state = stack.pop(); + break; + case 'expand': + if (state === 0 || state === 3) + expand(m[2]); + break; + case 'include': + if (state === 0 || state === 3) + include(m[2]); + break; + } } else { - state = stack.pop(); - } - } else { - if (state === 0) { - writeLine(s); - } else if(state === 3) { - writeLine(s.replace(/^\/\//g, " ")); + if (state === 0) { + writeLine(s); + } else if(state === 3) { + writeLine(s.replace(/^\/\/|^/g, " ")); + } } } - } - fs.writeFileSync(outFilename, out); -} + if (state !== 0 || stack.length !== 0) + throw new Error('Missing endif in preprocessor.'); + if (typeof outFilename !== 'function') + fs.writeFileSync(outFilename, out); + }, + /** + * Simplifies common build steps. + * @param setup + * .defines defines for preprocessors + * .copy array of arrays of source and destination pairs of files to copy + * .preprocess array of arrays of source and destination pairs of files + * run through preprocessor + */ + build: function build(setup) { + var defines = setup.defines; -target.pre = function() { - preprocess('in.txt', 'out.txt', {B2G: true}); -} + setup.copy.forEach(function(option) { + var source = option[0]; + var destination = option[1]; + cp('-R', source, destination); + }); + + setup.preprocess.forEach(function(option) { + var sources = option[0]; + var destination = option[1]; + + sources = ls('-R', sources); + sources.forEach(function(source) { + // ??? Warn if the source is wildcard and dest is file? + var destWithFolder = destination; + if (test('-d', destination)) + destWithFolder += '/' + path.basename(source); + BuildHelper.preprocess(source, destWithFolder, defines); + }); + }); + } +}; // // make all @@ -76,14 +177,59 @@ target.all = function() { // Production stuff // +// Files that need to be included in every build. +var COMMON_WEB_FILES = + ['web/viewer.css', + 'web/images', + 'web/debugger.js'], + COMMON_WEB_FILES_PREPROCESS = + ['web/viewer.js', + 'web/viewer.html']; + +// +// make generic +// Builds the generic production viewer that should be compatible with most +// modern HTML5 browsers. +// +target.generic = function() { + target.bundle(); + target.locale(); + + cd(ROOT_DIR); + echo(); + echo('### Creating generic viewer'); + + rm('-rf', GENERIC_DIR); + mkdir('-p', GENERIC_DIR); + mkdir('-p', GENERIC_DIR + BUILD_DIR); + mkdir('-p', GENERIC_DIR + '/web'); + + var defines = extendDefines({GENERIC: true}); + + var setup = { + defines: defines, + copy: [ + [COMMON_WEB_FILES, GENERIC_DIR + '/web'], + ['external/webL10n/l10n.js', GENERIC_DIR + '/web'], + ['web/compatibility.js', GENERIC_DIR + '/web'], + ['web/compressed.tracemonkey-pldi-09.pdf', GENERIC_DIR + '/web'], + ['web/locale.properties', GENERIC_DIR + '/web'] + ], + preprocess: [ + [BUILD_TARGET, GENERIC_DIR + BUILD_TARGET], + [COMMON_WEB_FILES_PREPROCESS, GENERIC_DIR + '/web'] + ] + }; + BuildHelper.build(setup); +}; + // // make web // Generates the website for the project, by checking out the gh-pages branch underneath // the build directory, and then moving the various viewer files into place. // target.web = function() { - target.production(); - target.locale(); + target.generic(); target.extension(); target.pagesrepo(); @@ -91,25 +237,18 @@ target.web = function() { echo(); echo('### Creating web site'); - var GH_PAGES_SRC_FILES = [ - 'web/*', - 'external/webL10n/l10n.js' - ]; - - cp(BUILD_TARGET, GH_PAGES_DIR + BUILD_TARGET); - cp('-R', GH_PAGES_SRC_FILES, GH_PAGES_DIR + '/web'); + cp('-R', GENERIC_DIR + '/*', GH_PAGES_DIR); cp(FIREFOX_BUILD_DIR + '/*.xpi', FIREFOX_BUILD_DIR + '/*.rdf', - GH_PAGES_DIR + EXTENSION_SRC_DIR + 'firefox/'); - cp(GH_PAGES_DIR + '/web/index.html.template', GH_PAGES_DIR + '/index.html'); - mv('-f', GH_PAGES_DIR + '/web/viewer-production.html', - GH_PAGES_DIR + '/web/viewer.html'); + GH_PAGES_DIR + EXTENSION_SRC_DIR + 'firefox/'); + cp('web/index.html.template', GH_PAGES_DIR + '/index.html'); + cd(GH_PAGES_DIR); exec('git add -A'); echo(); echo("Website built in " + GH_PAGES_DIR); echo("Don't forget to cd into " + GH_PAGES_DIR + - " and issue 'git commit' to push changes."); + " and issue 'git commit' to push changes."); }; // @@ -169,20 +308,10 @@ target.locale = function() { chromeManifestContent.to(CHROME_MANIFEST_OUTPUT); }; -// -// make production -// Creates production output (pdf.js, and corresponding changes to web/ files) -// -target.production = function() { - target.bundle(); - target.viewer(); -}; - // // make bundle // Bundles all source files into one wrapper 'pdf.js' file, in the given order. // - target.bundle = function() { cd(ROOT_DIR); echo(); @@ -223,29 +352,12 @@ target.bundle = function() { bundleVersion = exec('git log --format="%h" -n 1', {silent: true}).output.replace('\n', ''); - sed(/.*PDFJSSCRIPT_INCLUDE_ALL.*\n/, bundle, 'pdf.js') - .to(ROOT_DIR + BUILD_TARGET); - sed('-i', 'PDFJSSCRIPT_BUNDLE_VER', bundleVersion, ROOT_DIR + BUILD_TARGET); + // This just preprocesses the empty pdf.js file, we don't actually want to + // preprocess everything yet since other build targets use this file. + BuildHelper.preprocess('pdf.js', ROOT_DIR + BUILD_TARGET, + {BUNDLE: bundle, BUNDLE_VERSION: bundleVersion}); }; -// -// make viewer -// Changes development - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/viewer.js b/web/viewer.js index f6874f9a9..e2b2e1260 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -90,6 +90,7 @@ var ProgressBar = (function ProgressBarClosure() { return ProgressBar; })(); +//#if FIREFOX || MOZCENTRAL var FirefoxCom = (function FirefoxComClosure() { return { /** @@ -150,6 +151,7 @@ var FirefoxCom = (function FirefoxComClosure() { } }; })(); +//#endif // Settings Manager - This is a utility for saving settings // First we see if localStorage is available @@ -167,17 +169,17 @@ var Settings = (function SettingsClosure() { } })(); - var isFirefoxExtension = PDFJS.isFirefoxExtension; - function Settings(fingerprint) { var database = null; var index; - if (isFirefoxExtension) - database = FirefoxCom.requestSync('getDatabase', null) || '{}'; - else if (isLocalStorageEnabled) +//#if !(FIREFOX || MOZCENTRAL) + if (isLocalStorageEnabled) database = localStorage.getItem('database') || '{}'; else return false; +//#else +// database = FirefoxCom.requestSync('getDatabase', null) || '{}'; +//#endif database = JSON.parse(database); if (!('files' in database)) @@ -205,10 +207,12 @@ var Settings = (function SettingsClosure() { var file = this.file; file[name] = val; var database = JSON.stringify(this.database); - if (isFirefoxExtension) - FirefoxCom.requestSync('setDatabase', database); - else if (isLocalStorageEnabled) +//#if !(FIREFOX || MOZCENTRAL) + if (isLocalStorageEnabled) localStorage.setItem('database', database); +//#else +// FirefoxCom.requestSync('setDatabase', database); +//#endif }, get: function settingsGet(name, defaultValue) { @@ -460,52 +464,54 @@ var PDFView = { } var url = this.url.split('#')[0]; - if (PDFJS.isFirefoxExtension) { - // Document isn't ready just try to download with the url. - if (!this.pdfDocument) { - noData(); - return; - } - this.pdfDocument.getData().then( - function getDataSuccess(data) { - var bb = new MozBlobBuilder(); - bb.append(data.buffer); - var blobUrl = window.URL.createObjectURL( - bb.getBlob('application/pdf')); - - FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url }, - function response(err) { - if (err) { - // This error won't really be helpful because it's likely the - // fallback won't work either (or is already open). - PDFView.error('PDF failed to download.'); - } - window.URL.revokeObjectURL(blobUrl); - } - ); - }, - noData // Error ocurred try downloading with just the url. - ); - } else { - url += '#pdfjs.action=download', '_parent'; - window.open(url, '_parent'); - } +//#if !(FIREFOX || MOZCENTRAL) + url += '#pdfjs.action=download', '_parent'; + window.open(url, '_parent'); +//#else +// // Document isn't ready just try to download with the url. +// if (!this.pdfDocument) { +// noData(); +// return; +// } +// this.pdfDocument.getData().then( +// function getDataSuccess(data) { +// var bb = new MozBlobBuilder(); +// bb.append(data.buffer); +// var blobUrl = window.URL.createObjectURL( +// bb.getBlob('application/pdf')); +// +// FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url }, +// function response(err) { +// if (err) { +// // This error won't really be helpful because it's likely the +// // fallback won't work either (or is already open). +// PDFView.error('PDF failed to download.'); +// } +// window.URL.revokeObjectURL(blobUrl); +// } +// ); +// }, +// noData // Error ocurred try downloading with just the url. +// ); +//#endif }, fallback: function pdfViewFallback() { - if (!PDFJS.isFirefoxExtension) +//#if !(FIREFOX || MOZCENTRAL) return; - // Only trigger the fallback once so we don't spam the user with messages - // for one PDF. - if (this.fellback) - return; - this.fellback = true; - var url = this.url.split('#')[0]; - FirefoxCom.request('fallback', url, function response(download) { - if (!download) - return; - PDFView.download(); - }); +//#else +// // Only trigger the fallback once so we don't spam the user with messages +// // for one PDF. +// if (this.fellback) +// return; +// this.fellback = true; +// var url = this.url.split('#')[0]; +// FirefoxCom.request('fallback', url, function response(download) { +// if (!download) +// return; +// PDFView.download(); +// }); +//#endif }, navigateTo: function pdfViewNavigateTo(dest) { @@ -557,9 +563,11 @@ var PDFView = { * @param {String} anchor The anchor hash include the #. */ getAnchorUrl: function getAnchorUrl(anchor) { - if (PDFJS.isFirefoxExtension) - return this.url.split('#')[0] + anchor; +//#if !(FIREFOX || MOZCENTRAL) return anchor; +//#else +// return this.url.split('#')[0] + anchor; +//#endif }, /** @@ -593,11 +601,7 @@ var PDFView = { } } } - if (PDFJS.isFirefoxExtension) { - console.error(message + '\n' + moreInfoText); - this.fallback(); - return; - } +//#if !(FIREFOX || MOZCENTRAL) var errorWrapper = document.getElementById('errorWrapper'); errorWrapper.removeAttribute('hidden'); @@ -627,6 +631,10 @@ var PDFView = { errorMoreInfo.value = moreInfoText; errorMoreInfo.rows = moreInfoText.split('\n').length - 1; +//#else +// console.error(message + '\n' + moreInfoText); +// this.fallback(); +//#endif }, progress: function pdfViewProgress(level) { @@ -1804,15 +1812,21 @@ window.addEventListener('load', function webViewerLoad(evt) { PDFView.initialize(); var params = PDFView.parseQueryString(document.location.search.substring(1)); - var file = PDFJS.isFirefoxExtension ? - window.location.toString() : params.file || kDefaultURL; +//#if !(FIREFOX || MOZCENTRAL) + var file = params.file || kDefaultURL; +//#else +//var file = window.location.toString() +//#endif - if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader || - !window.FileList || !window.Blob) { +//#if !(FIREFOX || MOZCENTRAL) + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { document.getElementById('openFile').setAttribute('hidden', 'true'); } else { document.getElementById('fileInput').value = null; } +//#else +//document.getElementById('openFile').setAttribute('hidden', 'true'); +//#endif // Special debugging flags in the hash section of the URL. var hash = document.location.hash.substring(1); @@ -1821,18 +1835,21 @@ window.addEventListener('load', function webViewerLoad(evt) { if ('disableWorker' in hashParams) PDFJS.disableWorker = (hashParams['disableWorker'] === 'true'); - if (!PDFJS.isFirefoxExtension) { - var locale = navigator.language; - if ('locale' in hashParams) - locale = hashParams['locale']; - mozL10n.language.code = locale; - } +//#if !(FIREFOX || MOZCENTRAL) + var locale = navigator.language; + if ('locale' in hashParams) + locale = hashParams['locale']; + mozL10n.language.code = locale; +//#endif if ('disableTextLayer' in hashParams) PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true'); - if ('pdfBug' in hashParams && - (!PDFJS.isFirefoxExtension || FirefoxCom.requestSync('pdfBugEnabled'))) { +//#if !(FIREFOX || MOZCENTRAL) + if ('pdfBug' in hashParams) { +//#else +//if ('pdfBug' in hashParams && FirefoxCom.requestSync('pdfBugEnabled')) { +//#endif PDFJS.pdfBug = true; var pdfBug = hashParams['pdfBug']; var enabled = pdfBug.split(','); @@ -1840,10 +1857,12 @@ window.addEventListener('load', function webViewerLoad(evt) { PDFBug.init(); } - if (!PDFJS.isFirefoxExtension || - (PDFJS.isFirefoxExtension && FirefoxCom.requestSync('searchEnabled'))) { - document.querySelector('#viewSearch').classList.remove('hidden'); - } +//#if !(FIREFOX || MOZCENTRAL) +//#else +//if (FirefoxCom.requestSync('searchEnabled')) { +// document.querySelector('#viewSearch').classList.remove('hidden'); +//} +//#endif if (!PDFView.supportsPrinting) { document.getElementById('print').classList.add('hidden'); @@ -2143,19 +2162,19 @@ window.addEventListener('afterprint', function afterPrint(evt) { })(); //#if B2G -// window.navigator.mozSetMessageHandler('activity', function(activity) { -// var url = activity.source.data.url; -// // Temporarily get the data here since the cross domain xhr is broken in -// // the worker currently, see bug 761227. -// var params = { -// url: url, -// error: function(e) { -// PDFView.error(mozL10n.get('loading_error', null, -// 'An error occurred while loading the PDF.'), e); -// } -// }; -// PDFJS.getPdf(params, function successCallback(data) { -// PDFView.open(data, 0); -// }); -// }); +//window.navigator.mozSetMessageHandler('activity', function(activity) { +// var url = activity.source.data.url; +// // Temporarily get the data here since the cross domain xhr is broken in +// // the worker currently, see bug 761227. +// var params = { +// url: url, +// error: function(e) { +// PDFView.error(mozL10n.get('loading_error', null, +// 'An error occurred while loading the PDF.'), e); +// } +// }; +// PDFJS.getPdf(params, function successCallback(data) { +// PDFView.open(data, 0); +// }); +//}); //#endif