262 lines
8.4 KiB
JavaScript
262 lines
8.4 KiB
JavaScript
// Copyright 2014 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
/**
|
|
* Turn a dictionary received from postMessage into a key event.
|
|
* @param {Object} dict A dictionary representing the key event.
|
|
* @return {Event} A key event.
|
|
*/
|
|
function DeserializeKeyEvent(dict) {
|
|
var e = document.createEvent('Event');
|
|
e.initEvent('keydown');
|
|
e.keyCode = dict.keyCode;
|
|
e.shiftKey = dict.shiftKey;
|
|
e.ctrlKey = dict.ctrlKey;
|
|
e.altKey = dict.altKey;
|
|
e.metaKey = dict.metaKey;
|
|
e.fromScriptingAPI = true;
|
|
return e;
|
|
}
|
|
|
|
/**
|
|
* Turn a key event into a dictionary which can be sent over postMessage.
|
|
* @param {Event} event A key event.
|
|
* @return {Object} A dictionary representing the key event.
|
|
*/
|
|
function SerializeKeyEvent(event) {
|
|
return {
|
|
keyCode: event.keyCode,
|
|
shiftKey: event.shiftKey,
|
|
ctrlKey: event.ctrlKey,
|
|
altKey: event.altKey,
|
|
metaKey: event.metaKey
|
|
};
|
|
}
|
|
|
|
/**
|
|
* An enum containing a value specifying whether the PDF is currently loading,
|
|
* has finished loading or failed to load.
|
|
*/
|
|
var LoadState = {
|
|
LOADING: 'loading',
|
|
SUCCESS: 'success',
|
|
FAILED: 'failed'
|
|
};
|
|
|
|
/**
|
|
* Create a new PDFScriptingAPI. This provides a scripting interface to
|
|
* the PDF viewer so that it can be customized by things like print preview.
|
|
* @param {Window} window the window of the page containing the pdf viewer.
|
|
* @param {Object} plugin the plugin element containing the pdf viewer.
|
|
*/
|
|
function PDFScriptingAPI(window, plugin) {
|
|
this.loadState_ = LoadState.LOADING;
|
|
this.pendingScriptingMessages_ = [];
|
|
this.setPlugin(plugin);
|
|
|
|
window.addEventListener('message', function(event) {
|
|
if (event.origin != 'chrome://pdf-viewer' &&
|
|
event.origin != 'chrome://print') {
|
|
console.error('Received message that was not from the extension: ' +
|
|
event);
|
|
return;
|
|
}
|
|
switch (event.data.type) {
|
|
case 'viewport':
|
|
if (this.viewportChangedCallback_)
|
|
this.viewportChangedCallback_(event.data.pageX,
|
|
event.data.pageY,
|
|
event.data.pageWidth,
|
|
event.data.viewportWidth,
|
|
event.data.viewportHeight);
|
|
break;
|
|
case 'documentLoaded':
|
|
this.loadState_ = event.data.load_state;
|
|
if (this.loadCallback_)
|
|
this.loadCallback_(this.loadState_ == LoadState.SUCCESS);
|
|
break;
|
|
case 'getSelectedTextReply':
|
|
if (this.selectedTextCallback_) {
|
|
this.selectedTextCallback_(event.data.selectedText);
|
|
this.selectedTextCallback_ = null;
|
|
}
|
|
break;
|
|
case 'sendKeyEvent':
|
|
if (this.keyEventCallback_)
|
|
this.keyEventCallback_(DeserializeKeyEvent(event.data.keyEvent));
|
|
break;
|
|
}
|
|
}.bind(this), false);
|
|
}
|
|
|
|
PDFScriptingAPI.prototype = {
|
|
/**
|
|
* @private
|
|
* Send a message to the extension. If messages try to get sent before there
|
|
* is a plugin element set, then we queue them up and send them later (this
|
|
* can happen in print preview).
|
|
* @param {Object} message The message to send.
|
|
*/
|
|
sendMessage_: function(message) {
|
|
if (this.plugin_)
|
|
this.plugin_.postMessage(message, '*');
|
|
else
|
|
this.pendingScriptingMessages_.push(message);
|
|
},
|
|
|
|
/**
|
|
* Sets the plugin element containing the PDF viewer. The element will usually
|
|
* be passed into the PDFScriptingAPI constructor but may also be set later.
|
|
* @param {Object} plugin the plugin element containing the PDF viewer.
|
|
*/
|
|
setPlugin: function(plugin) {
|
|
this.plugin_ = plugin;
|
|
|
|
if (this.plugin_) {
|
|
// Send a message to ensure the postMessage channel is initialized which
|
|
// allows us to receive messages.
|
|
this.sendMessage_({
|
|
type: 'initialize'
|
|
});
|
|
// Flush pending messages.
|
|
while (this.pendingScriptingMessages_.length > 0)
|
|
this.sendMessage_(this.pendingScriptingMessages_.shift());
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets the callback which will be run when the PDF viewport changes.
|
|
* @param {Function} callback the callback to be called.
|
|
*/
|
|
setViewportChangedCallback: function(callback) {
|
|
this.viewportChangedCallback_ = callback;
|
|
},
|
|
|
|
/**
|
|
* Sets the callback which will be run when the PDF document has finished
|
|
* loading. If the document is already loaded, it will be run immediately.
|
|
* @param {Function} callback the callback to be called.
|
|
*/
|
|
setLoadCallback: function(callback) {
|
|
this.loadCallback_ = callback;
|
|
if (this.loadState_ != LoadState.LOADING && this.loadCallback_)
|
|
this.loadCallback_(this.loadState_ == LoadState.SUCCESS);
|
|
},
|
|
|
|
/**
|
|
* Sets a callback that gets run when a key event is fired in the PDF viewer.
|
|
* @param {Function} callback the callback to be called with a key event.
|
|
*/
|
|
setKeyEventCallback: function(callback) {
|
|
this.keyEventCallback_ = callback;
|
|
},
|
|
|
|
/**
|
|
* Resets the PDF viewer into print preview mode.
|
|
* @param {string} url the url of the PDF to load.
|
|
* @param {boolean} grayscale whether or not to display the PDF in grayscale.
|
|
* @param {Array<number>} pageNumbers an array of the page numbers.
|
|
* @param {boolean} modifiable whether or not the document is modifiable.
|
|
*/
|
|
resetPrintPreviewMode: function(url, grayscale, pageNumbers, modifiable) {
|
|
this.loadState_ = LoadState.LOADING;
|
|
this.sendMessage_({
|
|
type: 'resetPrintPreviewMode',
|
|
url: url,
|
|
grayscale: grayscale,
|
|
pageNumbers: pageNumbers,
|
|
modifiable: modifiable
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Load a page into the document while in print preview mode.
|
|
* @param {string} url the url of the pdf page to load.
|
|
* @param {number} index the index of the page to load.
|
|
*/
|
|
loadPreviewPage: function(url, index) {
|
|
this.sendMessage_({
|
|
type: 'loadPreviewPage',
|
|
url: url,
|
|
index: index
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Select all the text in the document. May only be called after document
|
|
* load.
|
|
*/
|
|
selectAll: function() {
|
|
this.sendMessage_({
|
|
type: 'selectAll'
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get the selected text in the document. The callback will be called with the
|
|
* text that is selected. May only be called after document load.
|
|
* @param {Function} callback a callback to be called with the selected text.
|
|
* @return {boolean} true if the function is successful, false if there is an
|
|
* outstanding request for selected text that has not been answered.
|
|
*/
|
|
getSelectedText: function(callback) {
|
|
if (this.selectedTextCallback_)
|
|
return false;
|
|
this.selectedTextCallback_ = callback;
|
|
this.sendMessage_({
|
|
type: 'getSelectedText'
|
|
});
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Print the document. May only be called after document load.
|
|
*/
|
|
print: function() {
|
|
this.sendMessage_({
|
|
type: 'print'
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Send a key event to the extension.
|
|
* @param {Event} keyEvent the key event to send to the extension.
|
|
*/
|
|
sendKeyEvent: function(keyEvent) {
|
|
this.sendMessage_({
|
|
type: 'sendKeyEvent',
|
|
keyEvent: SerializeKeyEvent(keyEvent)
|
|
});
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Creates a PDF viewer with a scripting interface. This is basically 1) an
|
|
* iframe which is navigated to the PDF viewer extension and 2) a scripting
|
|
* interface which provides access to various features of the viewer for use
|
|
* by print preview and accessibility.
|
|
* @param {string} src the source URL of the PDF to load initially.
|
|
* @return {HTMLIFrameElement} the iframe element containing the PDF viewer.
|
|
*/
|
|
function PDFCreateOutOfProcessPlugin(src) {
|
|
var client = new PDFScriptingAPI(window);
|
|
var iframe = window.document.createElement('iframe');
|
|
iframe.setAttribute('src', 'pdf_preview.html?' + src);
|
|
// Prevent the frame from being tab-focusable.
|
|
iframe.setAttribute('tabindex', '-1');
|
|
|
|
iframe.onload = function() {
|
|
client.setPlugin(iframe.contentWindow);
|
|
};
|
|
|
|
// Add the functions to the iframe so that they can be called directly.
|
|
iframe.setViewportChangedCallback =
|
|
client.setViewportChangedCallback.bind(client);
|
|
iframe.setLoadCallback = client.setLoadCallback.bind(client);
|
|
iframe.setKeyEventCallback = client.setKeyEventCallback.bind(client);
|
|
iframe.resetPrintPreviewMode = client.resetPrintPreviewMode.bind(client);
|
|
iframe.loadPreviewPage = client.loadPreviewPage.bind(client);
|
|
iframe.sendKeyEvent = client.sendKeyEvent.bind(client);
|
|
return iframe;
|
|
}
|