pdf_scripting_api.js revision a02191e04bc25c4935f804f2c080ae28663d096d
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 PDFMessagingClient. This provides a scripting interface to 7 * the PDF viewer so that it can be customized by things like print preview. 8 * @param {HTMLIFrameElement} iframe an iframe containing the PDF viewer. 9 * @param {Window} window the window of the page containing the iframe. 10 * @param {string} extensionUrl the url of the PDF extension. 11 */ 12function PDFMessagingClient(iframe, window, extensionUrl) { 13 this.iframe_ = iframe; 14 this.extensionUrl_ = extensionUrl; 15 this.readyToReceive_ = false; 16 this.messageQueue_ = []; 17 window.addEventListener('message', function(event) { 18 switch (event.data.type) { 19 case 'readyToReceive': 20 this.flushPendingMessages_(); 21 break; 22 case 'viewportChanged': 23 this.viewportChangedCallback_(event.data.pageX, 24 event.data.pageY, 25 event.data.pageWidth, 26 event.data.viewportWidth, 27 event.data.viewportHeight); 28 break; 29 case 'documentLoaded': 30 this.loadCallback_(); 31 break; 32 } 33 }.bind(this), false); 34} 35 36PDFMessagingClient.prototype = { 37 /** 38 * @private 39 * Send a message to the extension. If we try to send messages prior to the 40 * extension being ready to receive messages (i.e. before it has finished 41 * loading) we queue up the messages and flush them later. 42 * @param {MessageObject} the message to send. 43 */ 44 sendMessage_: function(message) { 45 if (!this.readyToReceive_) { 46 this.messageQueue_.push(message); 47 return; 48 } 49 50 this.iframe_.contentWindow.postMessage(message, this.extensionUrl_); 51 }, 52 53 /** 54 * @private 55 * Flushes all pending messages to the extension. 56 */ 57 flushPendingMessages_: function() { 58 this.readyToReceive_ = true; 59 while (this.messageQueue_.length != 0) { 60 this.iframe_.contentWindow.postMessage(this.messageQueue_.shift(), 61 this.extensionUrl_); 62 } 63 }, 64 65 /** 66 * Sets the callback which will be run when the PDF viewport changes. 67 * @param {Function} callback the callback to be called. 68 */ 69 setViewportChangedCallback: function(callback) { 70 this.viewportChangedCallback_ = callback; 71 }, 72 73 /** 74 * Sets the callback which will be run when the PDF document has finished 75 * loading. 76 * @param {Function} callback the callback to be called. 77 */ 78 setLoadCallback: function(callback) { 79 this.loadCallback_ = callback; 80 }, 81 82 /** 83 * Resets the PDF viewer into print preview mode. 84 * @param {string} url the url of the PDF to load. 85 * @param {boolean} grayscale whether or not to display the PDF in grayscale. 86 * @param {Array.<number>} pageNumbers an array of the page numbers. 87 * @param {boolean} modifiable whether or not the document is modifiable. 88 */ 89 resetPrintPreviewMode: function(url, grayscale, pageNumbers, modifiable) { 90 this.sendMessage_({ 91 type: 'resetPrintPreviewMode', 92 url: url, 93 grayscale: grayscale, 94 pageNumbers: pageNumbers, 95 modifiable: modifiable 96 }); 97 }, 98 99 /** 100 * Load a page into the document while in print preview mode. 101 * @param {string} url the url of the pdf page to load. 102 * @param {number} index the index of the page to load. 103 */ 104 loadPreviewPage: function(url, index) { 105 this.sendMessage_({ 106 type: 'loadPreviewPage', 107 url: url, 108 index: index 109 }); 110 }, 111 112 /** 113 * Send a key event to the extension. 114 * @param {number} keyCode the key code to send to the extension. 115 */ 116 sendKeyEvent: function(keyCode) { 117 // TODO(raymes): Figure out a way to do this. It's only used to do scrolling 118 // the viewport, so probably just expose viewport controls instead. 119 }, 120}; 121 122/** 123 * Creates a PDF viewer with a scripting interface. This is basically 1) an 124 * iframe which is navigated to the PDF viewer extension and 2) a scripting 125 * interface which provides access to various features of the viewer for use 126 * by print preview and accessbility. 127 * @param {string} src the source URL of the PDF to load initially. 128 * @return {HTMLIFrameElement} the iframe element containing the PDF viewer. 129 */ 130function PDFCreateOutOfProcessPlugin(src) { 131 var EXTENSION_URL = 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai'; 132 var iframe = window.document.createElement('iframe'); 133 iframe.setAttribute('src', EXTENSION_URL + '/index.html?' + src); 134 var client = new PDFMessagingClient(iframe, window, EXTENSION_URL); 135 136 // Add the functions to the iframe so that they can be called directly. 137 iframe.setViewportChangedCallback = 138 client.setViewportChangedCallback.bind(client); 139 iframe.setLoadCallback = client.setLoadCallback.bind(client); 140 iframe.resetPrintPreviewMode = client.resetPrintPreviewMode.bind(client); 141 iframe.loadPreviewPage = client.loadPreviewPage.bind(client); 142 iframe.sendKeyEvent = client.sendKeyEvent.bind(client); 143 return iframe; 144} 145 146/** 147 * Create a new PDFMessagingHost. This is the extension-side of the scripting 148 * interface to the PDF viewer. It handles requests from a page which contains 149 * a PDF viewer extension and translates them into actions on the viewer. 150 * @param {Window} window the window containing the PDF extension. 151 * @param {PDFViewer} pdfViewer the object which provides access to the viewer. 152 */ 153function PDFMessagingHost(window, pdfViewer) { 154 this.window_ = window; 155 this.pdfViewer_ = pdfViewer; 156 this.viewport_ = pdfViewer.viewport; 157 158 window.addEventListener('message', function(event) { 159 switch (event.data.type) { 160 case 'resetPrintPreviewMode': 161 this.pdfViewer_.resetPrintPreviewMode( 162 event.data.url, 163 event.data.grayscale, 164 event.data.pageNumbers, 165 event.data.modifiable); 166 167 break; 168 case 'loadPreviewPage': 169 this.pdfViewer_.loadPreviewPage(event.data.url, event.data.index); 170 break; 171 } 172 }.bind(this), false); 173 174 if (this.window_.parent != this.window_) 175 this.sendMessage_({type: 'readyToReceive'}); 176} 177 178PDFMessagingHost.prototype = { 179 sendMessage_: function(message) { 180 if (this.window_.parent == this.window_) 181 return; 182 this.window_.parent.postMessage(message, '*'); 183 }, 184 185 viewportChanged: function() { 186 var visiblePage = this.viewport_.getMostVisiblePage(); 187 var pageDimensions = this.viewport_.getPageScreenRect(visiblePage); 188 var size = this.viewport_.size; 189 190 this.sendMessage_({ 191 type: 'viewportChanged', 192 pageX: pageDimensions.x, 193 pageY: pageDimensions.y, 194 pageWidth: pageDimensions.width, 195 viewportWidth: size.width, 196 viewportHeight: size.height, 197 }); 198 }, 199 200 documentLoaded: function() { 201 if (this.window_.parent == this.window_) 202 return; 203 this.sendMessage_({ type: 'documentLoaded' }); 204 } 205}; 206