Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / resources / pdf / pdf_scripting_api.js
blob4a08195c83c5777d1b51c17883e9bde18ed3a017
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.
5 /**
6  * Turn a dictionary received from postMessage into a key event.
7  * @param {Object} dict A dictionary representing the key event.
8  * @return {Event} A key event.
9  */
10 function DeserializeKeyEvent(dict) {
11   var e = document.createEvent('Event');
12   e.initEvent('keydown');
13   e.keyCode = dict.keyCode;
14   e.shiftKey = dict.shiftKey;
15   e.ctrlKey = dict.ctrlKey;
16   e.altKey = dict.altKey;
17   e.metaKey = dict.metaKey;
18   e.fromScriptingAPI = true;
19   return e;
22 /**
23  * Turn a key event into a dictionary which can be sent over postMessage.
24  * @param {Event} event A key event.
25  * @return {Object} A dictionary representing the key event.
26  */
27 function SerializeKeyEvent(event) {
28   return {
29     keyCode: event.keyCode,
30     shiftKey: event.shiftKey,
31     ctrlKey: event.ctrlKey,
32     altKey: event.altKey,
33     metaKey: event.metaKey
34   };
37 /**
38  * An enum containing a value specifying whether the PDF is currently loading,
39  * has finished loading or failed to load.
40  */
41 var LoadState = {
42   LOADING: 'loading',
43   SUCCESS: 'success',
44   FAILED: 'failed'
47 /**
48  * Create a new PDFScriptingAPI. This provides a scripting interface to
49  * the PDF viewer so that it can be customized by things like print preview.
50  * @param {Window} window the window of the page containing the pdf viewer.
51  * @param {Object} plugin the plugin element containing the pdf viewer.
52  */
53 function PDFScriptingAPI(window, plugin) {
54   this.loadState_ = LoadState.LOADING;
55   this.pendingScriptingMessages_ = [];
56   this.setPlugin(plugin);
58   window.addEventListener('message', function(event) {
59     if (event.origin != 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai') {
60       console.error('Received message that was not from the extension: ' +
61                     event);
62       return;
63     }
64     switch (event.data.type) {
65       case 'viewport':
66         if (this.viewportChangedCallback_)
67           this.viewportChangedCallback_(event.data.pageX,
68                                         event.data.pageY,
69                                         event.data.pageWidth,
70                                         event.data.viewportWidth,
71                                         event.data.viewportHeight);
72         break;
73       case 'documentLoaded':
74         this.loadState_ = event.data.load_state;
75         if (this.loadCallback_)
76           this.loadCallback_(this.loadState_ == LoadState.SUCCESS);
77         break;
78       case 'getAccessibilityJSONReply':
79         if (this.accessibilityCallback_) {
80           this.accessibilityCallback_(event.data.json);
81           this.accessibilityCallback_ = null;
82         }
83         break;
84       case 'getSelectedTextReply':
85         if (this.selectedTextCallback_) {
86           this.selectedTextCallback_(event.data.selectedText);
87           this.selectedTextCallback_ = null;
88         }
89         break;
90       case 'sendKeyEvent':
91         if (this.keyEventCallback_)
92           this.keyEventCallback_(DeserializeKeyEvent(event.data.keyEvent));
93         break;
94     }
95   }.bind(this), false);
98 PDFScriptingAPI.prototype = {
99   /**
100    * @private
101    * Send a message to the extension. If messages try to get sent before there
102    * is a plugin element set, then we queue them up and send them later (this
103    * can happen in print preview).
104    * @param {Object} message The message to send.
105    */
106   sendMessage_: function(message) {
107     if (this.plugin_)
108       this.plugin_.postMessage(message, '*');
109     else
110       this.pendingScriptingMessages_.push(message);
111   },
113  /**
114   * Sets the plugin element containing the PDF viewer. The element will usually
115   * be passed into the PDFScriptingAPI constructor but may also be set later.
116   * @param {Object} plugin the plugin element containing the PDF viewer.
117   */
118   setPlugin: function(plugin) {
119     this.plugin_ = plugin;
121     if (this.plugin_) {
122       // Send a message to ensure the postMessage channel is initialized which
123       // allows us to receive messages.
124       this.sendMessage_({
125         type: 'initialize'
126       });
127       // Flush pending messages.
128       while (this.pendingScriptingMessages_.length > 0)
129         this.sendMessage_(this.pendingScriptingMessages_.shift());
130     }
131   },
133   /**
134    * Sets the callback which will be run when the PDF viewport changes.
135    * @param {Function} callback the callback to be called.
136    */
137   setViewportChangedCallback: function(callback) {
138     this.viewportChangedCallback_ = callback;
139   },
141   /**
142    * Sets the callback which will be run when the PDF document has finished
143    * loading. If the document is already loaded, it will be run immediately.
144    * @param {Function} callback the callback to be called.
145    */
146   setLoadCallback: function(callback) {
147     this.loadCallback_ = callback;
148     if (this.loadState_ != LoadState.LOADING && this.loadCallback_)
149       this.loadCallback_(this.loadState_ == LoadState.SUCCESS);
150   },
152   /**
153    * Sets a callback that gets run when a key event is fired in the PDF viewer.
154    * @param {Function} callback the callback to be called with a key event.
155    */
156   setKeyEventCallback: function(callback) {
157     this.keyEventCallback_ = callback;
158   },
160   /**
161    * Resets the PDF viewer into print preview mode.
162    * @param {string} url the url of the PDF to load.
163    * @param {boolean} grayscale whether or not to display the PDF in grayscale.
164    * @param {Array<number>} pageNumbers an array of the page numbers.
165    * @param {boolean} modifiable whether or not the document is modifiable.
166    */
167   resetPrintPreviewMode: function(url, grayscale, pageNumbers, modifiable) {
168     this.loadState_ = LoadState.LOADING;
169     this.sendMessage_({
170       type: 'resetPrintPreviewMode',
171       url: url,
172       grayscale: grayscale,
173       pageNumbers: pageNumbers,
174       modifiable: modifiable
175     });
176   },
178   /**
179    * Load a page into the document while in print preview mode.
180    * @param {string} url the url of the pdf page to load.
181    * @param {number} index the index of the page to load.
182    */
183   loadPreviewPage: function(url, index) {
184     this.sendMessage_({
185       type: 'loadPreviewPage',
186       url: url,
187       index: index
188     });
189   },
191   /**
192    * Get accessibility JSON for the document. May only be called after document
193    * load.
194    * @param {Function} callback a callback to be called with the accessibility
195    *     json that has been retrieved.
196    * @param {number} [page] the 0-indexed page number to get accessibility data
197    *     for. If this is not provided, data about the entire document is
198    *     returned.
199    * @return {boolean} true if the function is successful, false if there is an
200    *     outstanding request for accessibility data that has not been answered.
201    */
202   getAccessibilityJSON: function(callback, page) {
203     if (this.accessibilityCallback_)
204       return false;
205     this.accessibilityCallback_ = callback;
206     var message = {
207       type: 'getAccessibilityJSON',
208     };
209     if (page || page == 0)
210       message.page = page;
211     this.sendMessage_(message);
212     return true;
213   },
215   /**
216    * Select all the text in the document. May only be called after document
217    * load.
218    */
219   selectAll: function() {
220     this.sendMessage_({
221       type: 'selectAll'
222     });
223   },
225   /**
226    * Get the selected text in the document. The callback will be called with the
227    * text that is selected. May only be called after document load.
228    * @param {Function} callback a callback to be called with the selected text.
229    * @return {boolean} true if the function is successful, false if there is an
230    *     outstanding request for selected text that has not been answered.
231    */
232   getSelectedText: function(callback) {
233     if (this.selectedTextCallback_)
234       return false;
235     this.selectedTextCallback_ = callback;
236     this.sendMessage_({
237       type: 'getSelectedText'
238     });
239     return true;
240   },
242   /**
243    * Print the document. May only be called after document load.
244    */
245   print: function() {
246     this.sendMessage_({
247       type: 'print'
248     });
249   },
251   /**
252    * Send a key event to the extension.
253    * @param {Event} keyEvent the key event to send to the extension.
254    */
255   sendKeyEvent: function(keyEvent) {
256     this.sendMessage_({
257       type: 'sendKeyEvent',
258       keyEvent: SerializeKeyEvent(keyEvent)
259     });
260   },
264  * Creates a PDF viewer with a scripting interface. This is basically 1) an
265  * iframe which is navigated to the PDF viewer extension and 2) a scripting
266  * interface which provides access to various features of the viewer for use
267  * by print preview and accessibility.
268  * @param {string} src the source URL of the PDF to load initially.
269  * @return {HTMLIFrameElement} the iframe element containing the PDF viewer.
270  */
271 function PDFCreateOutOfProcessPlugin(src) {
272   var iframe = window.document.createElement('iframe');
273   iframe.setAttribute(
274       'src',
275       'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html?' + src);
276   // Prevent the frame from being tab-focusable.
277   iframe.setAttribute('tabindex', '-1');
278   var client = new PDFScriptingAPI(window);
279   iframe.onload = function() {
280     client.setPlugin(iframe.contentWindow);
281   };
282   // Add the functions to the iframe so that they can be called directly.
283   iframe.setViewportChangedCallback =
284       client.setViewportChangedCallback.bind(client);
285   iframe.setLoadCallback = client.setLoadCallback.bind(client);
286   iframe.setKeyEventCallback = client.setKeyEventCallback.bind(client);
287   iframe.resetPrintPreviewMode = client.resetPrintPreviewMode.bind(client);
288   iframe.loadPreviewPage = client.loadPreviewPage.bind(client);
289   iframe.sendKeyEvent = client.sendKeyEvent.bind(client);
290   return iframe;