1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 /** 6 * Create a new PDFScriptingAPI. This provides a scripting interface to 7 * the PDF viewer so that it can be customized by things like print preview. 8 * @param {Window} window the window of the page containing the pdf viewer. 9 * @param {string} extensionUrl the url of the PDF extension. 10 */ 11 function PDFScriptingAPI(window, extensionUrl) { 12 this.extensionUrl_ = extensionUrl; 13 this.messageQueue_ = []; 14 window.addEventListener('message', function(event) { 15 if (event.origin != this.extensionUrl_) { 16 console.error('Received message that was not from the extension: ' + 17 event); 18 return; 19 } 20 switch (event.data.type) { 21 case 'readyToReceive': 22 this.setDestinationWindow(event.source); 23 break; 24 case 'viewport': 25 if (this.viewportChangedCallback_) 26 this.viewportChangedCallback_(event.data.pageX, 27 event.data.pageY, 28 event.data.pageWidth, 29 event.data.viewportWidth, 30 event.data.viewportHeight); 31 break; 32 case 'documentLoaded': 33 if (this.loadCallback_) 34 this.loadCallback_(); 35 break; 36 case 'getAccessibilityJSONReply': 37 if (this.accessibilityCallback_) { 38 this.accessibilityCallback_(event.data.json); 39 this.accessibilityCallback_ = null; 40 } 41 break; 42 } 43 }.bind(this), false); 44 } 45 46 PDFScriptingAPI.prototype = { 47 /** 48 * @private 49 * Send a message to the extension. If we try to send messages prior to the 50 * extension being ready to receive messages (i.e. before it has finished 51 * loading) we queue up the messages and flush them later. 52 * @param {Object} message The message to send. 53 */ 54 sendMessage_: function(message) { 55 if (!this.pdfWindow_) { 56 this.messageQueue_.push(message); 57 return; 58 } 59 60 this.pdfWindow_.postMessage(message, this.extensionUrl_); 61 }, 62 63 /** 64 * Sets the destination window containing the PDF viewer. This will be called 65 * when a 'readyToReceive' message is received from the PDF viewer or it can 66 * be called during tests. It then flushes any pending messages to the window. 67 * @param {Window} pdfWindow the window containing the PDF viewer. 68 */ 69 setDestinationWindow: function(pdfWindow) { 70 this.pdfWindow_ = pdfWindow; 71 while (this.messageQueue_.length != 0) { 72 this.pdfWindow_.postMessage(this.messageQueue_.shift(), 73 this.extensionUrl_); 74 } 75 }, 76 77 /** 78 * Sets the callback which will be run when the PDF viewport changes. 79 * @param {Function} callback the callback to be called. 80 */ 81 setViewportChangedCallback: function(callback) { 82 this.viewportChangedCallback_ = callback; 83 }, 84 85 /** 86 * Sets the callback which will be run when the PDF document has finished 87 * loading. 88 * @param {Function} callback the callback to be called. 89 */ 90 setLoadCallback: function(callback) { 91 this.loadCallback_ = callback; 92 }, 93 94 /** 95 * Resets the PDF viewer into print preview mode. 96 * @param {string} url the url of the PDF to load. 97 * @param {boolean} grayscale whether or not to display the PDF in grayscale. 98 * @param {Array.<number>} pageNumbers an array of the page numbers. 99 * @param {boolean} modifiable whether or not the document is modifiable. 100 */ 101 resetPrintPreviewMode: function(url, grayscale, pageNumbers, modifiable) { 102 this.sendMessage_({ 103 type: 'resetPrintPreviewMode', 104 url: url, 105 grayscale: grayscale, 106 pageNumbers: pageNumbers, 107 modifiable: modifiable 108 }); 109 }, 110 111 /** 112 * Load a page into the document while in print preview mode. 113 * @param {string} url the url of the pdf page to load. 114 * @param {number} index the index of the page to load. 115 */ 116 loadPreviewPage: function(url, index) { 117 this.sendMessage_({ 118 type: 'loadPreviewPage', 119 url: url, 120 index: index 121 }); 122 }, 123 124 /** 125 * Get accessibility JSON for the document. 126 * @param {Function} callback a callback to be called with the accessibility 127 * json that has been retrieved. 128 * @param {number} [page] the 0-indexed page number to get accessibility data 129 * for. If this is not provided, data about the entire document is 130 * returned. 131 * @return {boolean} true if the function is successful, false if there is an 132 * outstanding request for accessibility data that has not been answered. 133 */ 134 getAccessibilityJSON: function(callback, page) { 135 if (this.accessibilityCallback_) 136 return false; 137 this.accessibilityCallback_ = callback; 138 var message = { 139 type: 'getAccessibilityJSON', 140 }; 141 if (page || page == 0) 142 message.page = page; 143 this.sendMessage_(message); 144 return true; 145 }, 146 147 /** 148 * Send a key event to the extension. 149 * @param {number} keyCode the key code to send to the extension. 150 */ 151 sendKeyEvent: function(keyCode) { 152 this.sendMessage_({ 153 type: 'sendKeyEvent', 154 keyCode: keyCode 155 }); 156 }, 157 }; 158 159 /** 160 * Creates a PDF viewer with a scripting interface. This is basically 1) an 161 * iframe which is navigated to the PDF viewer extension and 2) a scripting 162 * interface which provides access to various features of the viewer for use 163 * by print preview and accessbility. 164 * @param {string} src the source URL of the PDF to load initially. 165 * @return {HTMLIFrameElement} the iframe element containing the PDF viewer. 166 */ 167 function PDFCreateOutOfProcessPlugin(src) { 168 var EXTENSION_URL = 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai'; 169 var iframe = window.document.createElement('iframe'); 170 iframe.setAttribute('src', EXTENSION_URL + '/index.html?' + src); 171 var client = new PDFScriptingAPI(window, EXTENSION_URL); 172 173 // Add the functions to the iframe so that they can be called directly. 174 iframe.setViewportChangedCallback = 175 client.setViewportChangedCallback.bind(client); 176 iframe.setLoadCallback = client.setLoadCallback.bind(client); 177 iframe.resetPrintPreviewMode = client.resetPrintPreviewMode.bind(client); 178 iframe.loadPreviewPage = client.loadPreviewPage.bind(client); 179 iframe.sendKeyEvent = client.sendKeyEvent.bind(client); 180 return iframe; 181 } 182