Browse Source

Merge pull request #1701 from brendandahl/fallback-ui

Fallback UI for Extension
Artur Adib 13 years ago
parent
commit
f991af003d
  1. 34
      extensions/firefox/components/PdfStreamConverter.js
  2. 2
      l10n/en-US/chrome.properties
  3. 7
      make.js
  4. 2
      src/api.js
  5. 9
      src/canvas.js
  6. 4
      src/colorspace.js
  7. 2
      src/core.js
  8. 19
      src/evaluator.js
  9. 2
      src/fonts.js
  10. 16
      src/stream.js
  11. 56
      src/util.js
  12. 14
      src/worker.js
  13. 79
      web/viewer.js

34
extensions/firefox/components/PdfStreamConverter.js

@ -19,6 +19,7 @@ const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm'); Cu.import('resource://gre/modules/NetUtil.jsm');
Cu.import('resource://gre/modules/AddonManager.jsm');
let appInfo = Cc['@mozilla.org/xre/app-info;1'] let appInfo = Cc['@mozilla.org/xre/app-info;1']
.getService(Ci.nsIXULAppInfo); .getService(Ci.nsIXULAppInfo);
@ -91,9 +92,15 @@ function getLocalizedStrings(path) {
} }
return map; return map;
} }
function getLocalizedString(strings, id) {
if (id in strings)
return strings[id]['textContent'];
return id;
}
// All the priviledged actions. // All the priviledged actions.
function ChromeActions() { function ChromeActions(domWindow) {
this.domWindow = domWindow;
} }
ChromeActions.prototype = { ChromeActions.prototype = {
@ -161,10 +168,30 @@ ChromeActions.prototype = {
}, },
pdfBugEnabled: function() { pdfBugEnabled: function() {
return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false); return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false);
},
fallback: function(url) {
var self = this;
var domWindow = this.domWindow;
var strings = getLocalizedStrings('chrome.properties');
var message = getLocalizedString(strings, 'unsupported_feature');
var win = Services.wm.getMostRecentWindow('navigator:browser');
var browser = win.gBrowser.getBrowserForDocument(domWindow.top.document);
var notificationBox = win.gBrowser.getNotificationBox(browser);
var buttons = [{
label: getLocalizedString(strings, 'open_with_different_viewer'),
accessKey: null,
callback: function() {
self.download(url);
}
}];
notificationBox.appendNotification(message, 'pdfjs-fallback', null,
notificationBox.PRIORITY_WARNING_LOW,
buttons);
} }
}; };
// Event listener to trigger chrome privedged code. // Event listener to trigger chrome privedged code.
function RequestListener(actions) { function RequestListener(actions) {
this.actions = actions; this.actions = actions;
@ -267,7 +294,8 @@ PdfStreamConverter.prototype = {
var domWindow = getDOMWindow(channel); var domWindow = getDOMWindow(channel);
// Double check the url is still the correct one. // Double check the url is still the correct one.
if (domWindow.document.documentURIObject.equals(aRequest.URI)) { if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
let requestListener = new RequestListener(new ChromeActions); let requestListener = new RequestListener(
new ChromeActions(domWindow));
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) { domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
requestListener.receive(event); requestListener.receive(event);
}, false, true); }, false, true);

2
l10n/en-US/chrome.properties

@ -0,0 +1,2 @@
unsupported_feature=This PDF document might not be displayed correctly.
open_with_different_viewer=Open With Different Viewer

7
make.js

@ -110,6 +110,10 @@ target.locale = function() {
cp(path + '/viewer.properties', EXTENSION_LOCALE_OUTPUT + '/' + locale); cp(path + '/viewer.properties', EXTENSION_LOCALE_OUTPUT + '/' + locale);
} }
if (test('-f', path + '/chrome.properties')) {
cp(path + '/chrome.properties', EXTENSION_LOCALE_OUTPUT + '/' + locale);
}
if (test('-f', path + '/metadata.inc')) { if (test('-f', path + '/metadata.inc')) {
var metadata = cat(path + '/metadata.inc'); var metadata = cat(path + '/metadata.inc');
metadataContent += metadata; metadataContent += metadata;
@ -398,7 +402,8 @@ target.mozcentral = function() {
'components', 'components',
'../../LICENSE'], '../../LICENSE'],
DEFAULT_LOCALE_FILES = DEFAULT_LOCALE_FILES =
[LOCALE_SRC_DIR + 'en-US/viewer.properties'], [LOCALE_SRC_DIR + 'en-US/viewer.properties',
LOCALE_SRC_DIR + 'en-US/chrome.properties'],
FIREFOX_MC_EXTENSION_FILES = FIREFOX_MC_EXTENSION_FILES =
['chrome.manifest', ['chrome.manifest',
'components', 'components',

2
src/api.js

@ -452,7 +452,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
messageHandler.send('test', testObj); messageHandler.send('test', testObj);
return; return;
} catch (e) { } catch (e) {
warn('The worker has been disabled.'); info('The worker has been disabled.');
} }
} }
// Either workers are disabled, not supported or have thrown an exception. // Either workers are disabled, not supported or have thrown an exception.

9
src/canvas.js

@ -343,10 +343,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.ctx.webkitLineDashOffset = dashPhase; this.ctx.webkitLineDashOffset = dashPhase;
}, },
setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) { setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
TODO('set rendering intent: ' + intent); // Maybe if we one day fully support color spaces this will be important
// for now we can ignore.
// TODO set rendering intent?
}, },
setFlatness: function CanvasGraphics_setFlatness(flatness) { setFlatness: function CanvasGraphics_setFlatness(flatness) {
TODO('set flatness: ' + flatness); // There's no way to control this with canvas, but we can safely ignore.
// TODO set flatness?
}, },
setGState: function CanvasGraphics_setGState(states) { setGState: function CanvasGraphics_setGState(states) {
for (var i = 0, ii = states.length; i < ii; i++) { for (var i = 0, ii = states.length; i < ii; i++) {
@ -841,7 +844,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
text.length += shownText.length; text.length += shownText.length;
} }
} else { } else {
malformed('TJ array element ' + e + ' is not string or num'); error('TJ array element ' + e + ' is not string or num');
} }
} }

4
src/colorspace.js

@ -451,12 +451,12 @@ var LabCS = (function LabCSClosure() {
error('Invalid WhitePoint components, no fallback available'); error('Invalid WhitePoint components, no fallback available');
if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
warn('Invalid BlackPoint, falling back to default'); info('Invalid BlackPoint, falling back to default');
this.XB = this.YB = this.ZB = 0; this.XB = this.YB = this.ZB = 0;
} }
if (this.amin > this.amax || this.bmin > this.bmax) { if (this.amin > this.amax || this.bmin > this.bmax) {
warn('Invalid Range, falling back to defaults'); info('Invalid Range, falling back to defaults');
this.amin = -100; this.amin = -100;
this.amax = 100; this.amax = 100;
this.bmin = -100; this.bmin = -100;

2
src/core.js

@ -7,7 +7,7 @@ var globalScope = (typeof window === 'undefined') ? this : window;
var isWorker = (typeof window == 'undefined'); var isWorker = (typeof window == 'undefined');
var ERRORS = 0, WARNINGS = 1, TODOS = 5; var ERRORS = 0, WARNINGS = 1, INFOS = 5;
var verbosity = WARNINGS; var verbosity = WARNINGS;
// The global PDFJS object exposes the API // The global PDFJS object exposes the API

19
src/evaluator.js

@ -418,6 +418,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
value[1] value[1]
]); ]);
break; break;
case 'BM':
// We support the default so don't trigger the TODO.
if (!isName(value) || value.name != 'Normal')
TODO('graphic state operator ' + key);
break;
case 'SMask':
// We support the default so don't trigger the TODO.
if (!isName(value) || value.name != 'None')
TODO('graphic state operator ' + key);
break;
// Only generate info log messages for the following since
// they are unlikey to have a big impact on the rendering.
case 'OP': case 'OP':
case 'op': case 'op':
case 'OPM': case 'OPM':
@ -430,14 +442,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
case 'HT': case 'HT':
case 'SM': case 'SM':
case 'SA': case 'SA':
case 'BM':
case 'SMask':
case 'AIS': case 'AIS':
case 'TK': case 'TK':
TODO('graphic state operator ' + key); // TODO implement these operators.
info('graphic state operator ' + key);
break; break;
default: default:
warn('Unknown graphic state operator ' + key); info('Unknown graphic state operator ' + key);
break; break;
} }
} }

2
src/fonts.js

@ -3648,7 +3648,7 @@ var CFFParser = (function CFFParserClosure() {
++offset; ++offset;
if (offset != 0) { if (offset != 0) {
warn('cff data is shifted'); info('cff data is shifted');
bytes = bytes.subarray(offset); bytes = bytes.subarray(offset);
this.bytes = bytes; this.bytes = bytes;
} }

16
src/stream.js

@ -1687,7 +1687,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (a1 > codingLine[codingPos]) { if (a1 > codingLine[codingPos]) {
if (a1 > this.columns) { if (a1 > this.columns) {
warn('row is wrong length'); info('row is wrong length');
this.err = true; this.err = true;
a1 = this.columns; a1 = this.columns;
} }
@ -1707,7 +1707,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (a1 > codingLine[codingPos]) { if (a1 > codingLine[codingPos]) {
if (a1 > this.columns) { if (a1 > this.columns) {
warn('row is wrong length'); info('row is wrong length');
this.err = true; this.err = true;
a1 = this.columns; a1 = this.columns;
} }
@ -1717,7 +1717,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
codingLine[codingPos] = a1; codingLine[codingPos] = a1;
} else if (a1 < codingLine[codingPos]) { } else if (a1 < codingLine[codingPos]) {
if (a1 < 0) { if (a1 < 0) {
warn('invalid code'); info('invalid code');
this.err = true; this.err = true;
a1 = 0; a1 = 0;
} }
@ -1879,7 +1879,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
this.eof = true; this.eof = true;
break; break;
default: default:
warn('bad 2d code'); info('bad 2d code');
this.addPixels(columns, 0); this.addPixels(columns, 0);
this.err = true; this.err = true;
} }
@ -1942,7 +1942,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
for (var i = 0; i < 4; ++i) { for (var i = 0; i < 4; ++i) {
code1 = this.lookBits(12); code1 = this.lookBits(12);
if (code1 != 1) if (code1 != 1)
warn('bad rtc code: ' + code1); info('bad rtc code: ' + code1);
this.eatBits(12); this.eatBits(12);
if (this.encoding > 0) { if (this.encoding > 0) {
this.lookBits(1); this.lookBits(1);
@ -2065,7 +2065,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (result[0] && result[2]) if (result[0] && result[2])
return result[1]; return result[1];
} }
warn('Bad two dim code'); info('Bad two dim code');
return EOF; return EOF;
}; };
@ -2098,7 +2098,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (result[0]) if (result[0])
return result[1]; return result[1];
} }
warn('bad white code'); info('bad white code');
this.eatBits(1); this.eatBits(1);
return 1; return 1;
}; };
@ -2135,7 +2135,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
if (result[0]) if (result[0])
return result[1]; return result[1];
} }
warn('bad black code'); info('bad black code');
this.eatBits(1); this.eatBits(1);
return 1; return 1;
}; };

56
src/util.js

@ -3,6 +3,8 @@
'use strict'; 'use strict';
// Use only for debugging purposes. This should not be used in any code that is
// in mozilla master.
function log(msg) { function log(msg) {
if (console && console.log) if (console && console.log)
console.log(msg); console.log(msg);
@ -10,32 +12,44 @@ function log(msg) {
print(msg); print(msg);
} }
function warn(msg) { // A notice for devs that will not trigger the fallback UI. These are good
if (verbosity >= WARNINGS) // for things that are helpful to devs, such as warning that Workers were
log('Warning: ' + msg); // disabled, which is important to devs but not end users.
function info(msg) {
if (verbosity >= INFOS) {
log('Info: ' + msg);
PDFJS.LogManager.notify('info', msg);
}
} }
function backtrace() { // Non-fatal warnings that should trigger the fallback UI.
try { function warn(msg) {
throw new Error(); if (verbosity >= WARNINGS) {
} catch (e) { log('Warning: ' + msg);
return e.stack ? e.stack.split('\n').slice(2).join('\n') : ''; PDFJS.LogManager.notify('warn', msg);
} }
} }
// Fatal errors that should trigger the fallback UI and halt execution by
// throwing an exception.
function error(msg) { function error(msg) {
log('Error: ' + msg); log('Error: ' + msg);
log(backtrace()); log(backtrace());
PDFJS.LogManager.notify('error', msg);
throw new Error(msg); throw new Error(msg);
} }
// Missing features that should trigger the fallback UI.
function TODO(what) { function TODO(what) {
if (verbosity >= TODOS) warn('TODO: ' + what);
log('TODO: ' + what);
} }
function malformed(msg) { function backtrace() {
error('Malformed PDF: ' + msg); try {
throw new Error();
} catch (e) {
return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
}
} }
function assert(cond, msg) { function assert(cond, msg) {
@ -47,9 +61,25 @@ function assert(cond, msg) {
// behavior is undefined. // behavior is undefined.
function assertWellFormed(cond, msg) { function assertWellFormed(cond, msg) {
if (!cond) if (!cond)
malformed(msg); error(msg);
} }
var LogManager = PDFJS.LogManager = (function LogManagerClosure() {
var loggers = [];
return {
addLogger: function logManager_addLogger(logger) {
loggers.push(logger);
},
notify: function(type, message) {
for (var i = 0, ii = loggers.length; i < ii; i++) {
var logger = loggers[i];
if (logger[type])
logger[type](message);
}
}
};
})();
function shadow(obj, prop, value) { function shadow(obj, prop, value) {
Object.defineProperty(obj, prop, { value: value, Object.defineProperty(obj, prop, { value: value,
enumerable: true, enumerable: true,

14
src/worker.js

@ -16,6 +16,9 @@ function MessageHandler(name, comObj) {
ah['console_error'] = [function ahConsoleError(data) { ah['console_error'] = [function ahConsoleError(data) {
console.error.apply(console, data); console.error.apply(console, data);
}]; }];
ah['_warn'] = [function ah_Warn(data) {
warn(data);
}];
comObj.onmessage = function messageHandlerComObjOnMessage(event) { comObj.onmessage = function messageHandlerComObjOnMessage(event) {
var data = event.data; var data = event.data;
@ -244,6 +247,17 @@ var workerConsole = {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
globalScope.console = workerConsole; globalScope.console = workerConsole;
// Add a logger so we can pass warnings on to the main thread, errors will
// throw an exception which will be forwarded on automatically.
PDFJS.LogManager.addLogger({
warn: function(msg) {
postMessage({
action: '_warn',
data: msg
});
}
});
var handler = new MessageHandler('worker_processor', this); var handler = new MessageHandler('worker_processor', this);
WorkerMessageHandler.setup(handler); WorkerMessageHandler.setup(handler);
} }

79
web/viewer.js

@ -221,6 +221,7 @@ var PDFView = {
initialBookmark: document.location.hash.substring(1), initialBookmark: document.location.hash.substring(1),
container: null, container: null,
initialized: false, initialized: false,
fellback: false,
// called once when the document is loaded // called once when the document is loaded
initialize: function pdfViewInitialize() { initialize: function pdfViewInitialize() {
this.container = document.getElementById('viewerContainer'); this.container = document.getElementById('viewerContainer');
@ -390,6 +391,18 @@ var PDFView = {
} }
}, },
fallback: function pdfViewFallback() {
if (!PDFJS.isFirefoxExtension)
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);
},
navigateTo: function pdfViewNavigateTo(dest) { navigateTo: function pdfViewNavigateTo(dest) {
if (typeof dest === 'string') if (typeof dest === 'string')
dest = this.destinations[dest]; dest = this.destinations[dest];
@ -452,6 +465,34 @@ var PDFView = {
* and optionally a 'stack' property. * and optionally a 'stack' property.
*/ */
error: function pdfViewError(message, moreInfo) { error: function pdfViewError(message, moreInfo) {
var moreInfoText = mozL10n.get('error_build', {build: PDFJS.build},
'PDF.JS Build: {{build}}') + '\n';
if (moreInfo) {
moreInfoText +=
mozL10n.get('error_message', {message: moreInfo.message},
'Message: {{message}}');
if (moreInfo.stack) {
moreInfoText += '\n' +
mozL10n.get('error_stack', {stack: moreInfo.stack},
'Stack: {{stack}}');
} else {
if (moreInfo.filename) {
moreInfoText += '\n' +
mozL10n.get('error_file', {file: moreInfo.filename},
'File: {{file}}');
}
if (moreInfo.lineNumber) {
moreInfoText += '\n' +
mozL10n.get('error_line', {line: moreInfo.lineNumber},
'Line: {{line}}');
}
}
}
if (PDFJS.isFirefoxExtension) {
console.error(message + '\n' + moreInfoText);
this.fallback();
return;
}
var errorWrapper = document.getElementById('errorWrapper'); var errorWrapper = document.getElementById('errorWrapper');
errorWrapper.removeAttribute('hidden'); errorWrapper.removeAttribute('hidden');
@ -478,32 +519,9 @@ var PDFView = {
}; };
moreInfoButton.removeAttribute('hidden'); moreInfoButton.removeAttribute('hidden');
lessInfoButton.setAttribute('hidden', 'true'); lessInfoButton.setAttribute('hidden', 'true');
errorMoreInfo.value = errorMoreInfo.value = moreInfoText;
mozL10n.get('error_build', {build: PDFJS.build},
'PDF.JS Build: {{build}}') + '\n';
if (moreInfo) { errorMoreInfo.rows = moreInfoText.split('\n').length - 1;
errorMoreInfo.value +=
mozL10n.get('error_message', {message: moreInfo.message},
'Message: {{message}}');
if (moreInfo.stack) {
errorMoreInfo.value += '\n' +
mozL10n.get('error_stack', {stack: moreInfo.stack},
'Stack: {{stack}}');
} else {
if (moreInfo.filename) {
errorMoreInfo.value += '\n' +
mozL10n.get('error_file', {file: moreInfo.filename},
'File: {{file}}');
}
if (moreInfo.lineNumber) {
errorMoreInfo.value += '\n' +
mozL10n.get('error_line', {line: moreInfo.lineNumber},
'Line: {{line}}');
}
}
}
errorMoreInfo.rows = errorMoreInfo.value.split('\n').length - 1;
}, },
progress: function pdfViewProgress(level) { progress: function pdfViewProgress(level) {
@ -916,6 +934,9 @@ var PageView = function pageView(container, pdfPage, id, scale,
if (comment) if (comment)
div.appendChild(comment); div.appendChild(comment);
break; break;
case 'Widget':
TODO('support forms');
break;
} }
} }
}); });
@ -1397,6 +1418,14 @@ window.addEventListener('load', function webViewerLoad(evt) {
PDFBug.init(); PDFBug.init();
} }
// Listen for warnings to trigger the fallback UI. Errors should be caught
// and call PDFView.error() so we don't need to listen for those.
PDFJS.LogManager.addLogger({
warn: function() {
PDFView.fallback();
}
});
var thumbsView = document.getElementById('thumbnailView'); var thumbsView = document.getElementById('thumbnailView');
thumbsView.addEventListener('scroll', updateThumbViewArea, true); thumbsView.addEventListener('scroll', updateThumbViewArea, true);

Loading…
Cancel
Save