OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 'use strict'; | 5 'use strict'; |
6 | 6 |
7 <include src="../../../../ui/webui/resources/js/util.js"> | 7 <include src="../../../../ui/webui/resources/js/util.js"> |
| 8 <include src="pdf_scripting_api.js"> |
8 <include src="viewport.js"> | 9 <include src="viewport.js"> |
9 <include src="pdf_scripting_api.js"> | |
10 | 10 |
11 /** | 11 /** |
12 * @return {number} Width of a scrollbar in pixels | 12 * @return {number} Width of a scrollbar in pixels |
13 */ | 13 */ |
14 function getScrollbarWidth() { | 14 function getScrollbarWidth() { |
15 var div = document.createElement('div'); | 15 var div = document.createElement('div'); |
16 div.style.visibility = 'hidden'; | 16 div.style.visibility = 'hidden'; |
17 div.style.overflow = 'scroll'; | 17 div.style.overflow = 'scroll'; |
18 div.style.width = '50px'; | 18 div.style.width = '50px'; |
19 div.style.height = '50px'; | 19 div.style.height = '50px'; |
(...skipping 29 matching lines...) Expand all Loading... |
49 | 49 |
50 // Create the plugin object dynamically so we can set its src. The plugin | 50 // Create the plugin object dynamically so we can set its src. The plugin |
51 // element is sized to fill the entire window and is set to be fixed | 51 // element is sized to fill the entire window and is set to be fixed |
52 // positioning, acting as a viewport. The plugin renders into this viewport | 52 // positioning, acting as a viewport. The plugin renders into this viewport |
53 // according to the scroll position of the window. | 53 // according to the scroll position of the window. |
54 this.plugin_ = document.createElement('object'); | 54 this.plugin_ = document.createElement('object'); |
55 // NOTE: The plugin's 'id' field must be set to 'plugin' since | 55 // NOTE: The plugin's 'id' field must be set to 'plugin' since |
56 // chrome/renderer/printing/print_web_view_helper.cc actually references it. | 56 // chrome/renderer/printing/print_web_view_helper.cc actually references it. |
57 this.plugin_.id = 'plugin'; | 57 this.plugin_.id = 'plugin'; |
58 this.plugin_.type = 'application/x-google-chrome-pdf'; | 58 this.plugin_.type = 'application/x-google-chrome-pdf'; |
59 this.plugin_.addEventListener('message', this.handleMessage_.bind(this), | 59 this.plugin_.addEventListener('message', this.handlePluginMessage_.bind(this), |
60 false); | 60 false); |
61 | 61 |
| 62 // Handle scripting messages from outside the extension that wish to interact |
| 63 // with it. We also send a message indicating that extension has loaded and |
| 64 // is ready to receive messages. |
| 65 window.addEventListener('message', this.handleScriptingMessage_.bind(this), |
| 66 false); |
| 67 this.sendScriptingMessage_({type: 'readyToReceive'}); |
| 68 |
62 // If the viewer is started from a MIME type request, there will be a | 69 // If the viewer is started from a MIME type request, there will be a |
63 // background page and stream details object with the details of the request. | 70 // background page and stream details object with the details of the request. |
64 // Otherwise, we take the query string of the URL to indicate the URL of the | 71 // Otherwise, we take the query string of the URL to indicate the URL of the |
65 // PDF to load. This is used for print preview in particular. | 72 // PDF to load. This is used for print preview in particular. |
66 var streamDetails; | 73 var streamDetails; |
67 if (chrome.extension.getBackgroundPage && | 74 if (chrome.extension.getBackgroundPage && |
68 chrome.extension.getBackgroundPage()) { | 75 chrome.extension.getBackgroundPage()) { |
69 streamDetails = chrome.extension.getBackgroundPage().popStreamDetails(); | 76 streamDetails = chrome.extension.getBackgroundPage().popStreamDetails(); |
70 } | 77 } |
71 | 78 |
72 if (!streamDetails) { | 79 if (!streamDetails) { |
73 // The URL of this page will be of the form | 80 // The URL of this page will be of the form |
74 // "chrome-extension://<extension id>?<pdf url>". We pull out the <pdf url> | 81 // "chrome-extension://<extension id>?<pdf url>". We pull out the <pdf url> |
75 // part here. | 82 // part here. |
76 var url = window.location.search.substring(1); | 83 var url = window.location.search.substring(1); |
77 streamDetails = { | 84 streamDetails = { |
78 streamUrl: url, | 85 streamUrl: url, |
79 originalUrl: url | 86 originalUrl: url |
80 }; | 87 }; |
81 } | 88 } |
82 | 89 |
83 this.plugin_.setAttribute('src', streamDetails.streamUrl); | 90 this.plugin_.setAttribute('src', streamDetails.streamUrl); |
84 if (window.top == window) | 91 if (window.top == window) |
85 this.plugin_.setAttribute('full-frame', ''); | 92 this.plugin_.setAttribute('full-frame', ''); |
86 document.body.appendChild(this.plugin_); | 93 document.body.appendChild(this.plugin_); |
87 | 94 |
88 this.messagingHost_ = new PDFMessagingHost(window, this); | |
89 | |
90 this.setupEventListeners_(streamDetails); | 95 this.setupEventListeners_(streamDetails); |
91 } | 96 } |
92 | 97 |
93 PDFViewer.prototype = { | 98 PDFViewer.prototype = { |
94 /** | 99 /** |
95 * @private | 100 * @private |
96 * Sets up event listeners for key shortcuts and also the UI buttons. | 101 * Sets up event listeners for key shortcuts and also the UI buttons. |
97 * @param {Object} streamDetails the details of the original HTTP request for | 102 * @param {Object} streamDetails the details of the original HTTP request for |
98 * the PDF. | 103 * the PDF. |
99 */ | 104 */ |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 this.sizer_.style.display = 'none'; | 210 this.sizer_.style.display = 'none'; |
206 this.toolbar_.style.visibility = 'hidden'; | 211 this.toolbar_.style.visibility = 'hidden'; |
207 if (this.passwordScreen_.active) { | 212 if (this.passwordScreen_.active) { |
208 this.passwordScreen_.deny(); | 213 this.passwordScreen_.deny(); |
209 this.passwordScreen_.active = false; | 214 this.passwordScreen_.active = false; |
210 } | 215 } |
211 } else if (progress == 100) { | 216 } else if (progress == 100) { |
212 // Document load complete. | 217 // Document load complete. |
213 var loadEvent = new Event('pdfload'); | 218 var loadEvent = new Event('pdfload'); |
214 window.dispatchEvent(loadEvent); | 219 window.dispatchEvent(loadEvent); |
215 // TODO(raymes): Replace this and other callbacks with events. | 220 this.sendScriptingMessage_({ |
216 this.messagingHost_.documentLoaded(); | 221 type: 'documentLoaded' |
| 222 }); |
217 if (this.lastViewportPosition_) | 223 if (this.lastViewportPosition_) |
218 this.viewport_.position = this.lastViewportPosition_; | 224 this.viewport_.position = this.lastViewportPosition_; |
219 } | 225 } |
220 }, | 226 }, |
221 | 227 |
222 /** | 228 /** |
223 * @private | 229 * @private |
224 * An event handler for handling password-submitted events. These are fired | 230 * An event handler for handling password-submitted events. These are fired |
225 * when an event is entered into the password screen. | 231 * when an event is entered into the password screen. |
226 * @param {Object} event a password-submitted event. | 232 * @param {Object} event a password-submitted event. |
227 */ | 233 */ |
228 onPasswordSubmitted_: function(event) { | 234 onPasswordSubmitted_: function(event) { |
229 this.plugin_.postMessage({ | 235 this.plugin_.postMessage({ |
230 type: 'getPasswordComplete', | 236 type: 'getPasswordComplete', |
231 password: event.detail.password | 237 password: event.detail.password |
232 }); | 238 }); |
233 }, | 239 }, |
234 | 240 |
235 /** | 241 /** |
236 * @private | 242 * @private |
237 * An event handler for handling message events received from the plugin. | 243 * An event handler for handling message events received from the plugin. |
238 * @param {MessageObject} message a message event. | 244 * @param {MessageObject} message a message event. |
239 */ | 245 */ |
240 handleMessage_: function(message) { | 246 handlePluginMessage_: function(message) { |
241 switch (message.data.type.toString()) { | 247 switch (message.data.type.toString()) { |
242 case 'documentDimensions': | 248 case 'documentDimensions': |
243 this.documentDimensions_ = message.data; | 249 this.documentDimensions_ = message.data; |
244 this.viewport_.setDocumentDimensions(this.documentDimensions_); | 250 this.viewport_.setDocumentDimensions(this.documentDimensions_); |
245 this.toolbar_.style.visibility = 'visible'; | 251 this.toolbar_.style.visibility = 'visible'; |
246 // If we received the document dimensions, the password was good so we | 252 // If we received the document dimensions, the password was good so we |
247 // can dismiss the password screen. | 253 // can dismiss the password screen. |
248 if (this.passwordScreen_.active) | 254 if (this.passwordScreen_.active) |
249 this.passwordScreen_.accept(); | 255 this.passwordScreen_.accept(); |
250 | 256 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 | 307 |
302 var hasScrollbars = this.viewport_.documentHasScrollbars(); | 308 var hasScrollbars = this.viewport_.documentHasScrollbars(); |
303 var scrollbarWidth = this.viewport_.scrollbarWidth; | 309 var scrollbarWidth = this.viewport_.scrollbarWidth; |
304 // Offset the toolbar position so that it doesn't move if scrollbars appear. | 310 // Offset the toolbar position so that it doesn't move if scrollbars appear. |
305 var toolbarRight = hasScrollbars.vertical ? 0 : scrollbarWidth; | 311 var toolbarRight = hasScrollbars.vertical ? 0 : scrollbarWidth; |
306 var toolbarBottom = hasScrollbars.horizontal ? 0 : scrollbarWidth; | 312 var toolbarBottom = hasScrollbars.horizontal ? 0 : scrollbarWidth; |
307 this.toolbar_.style.right = toolbarRight + 'px'; | 313 this.toolbar_.style.right = toolbarRight + 'px'; |
308 this.toolbar_.style.bottom = toolbarBottom + 'px'; | 314 this.toolbar_.style.bottom = toolbarBottom + 'px'; |
309 | 315 |
310 // Update the page indicator. | 316 // Update the page indicator. |
311 this.pageIndicator_.index = this.viewport_.getMostVisiblePage(); | 317 var visiblePage = this.viewport_.getMostVisiblePage(); |
| 318 this.pageIndicator_.index = visiblePage; |
312 if (this.documentDimensions_.pageDimensions.length > 1 && | 319 if (this.documentDimensions_.pageDimensions.length > 1 && |
313 hasScrollbars.vertical) { | 320 hasScrollbars.vertical) { |
314 this.pageIndicator_.style.visibility = 'visible'; | 321 this.pageIndicator_.style.visibility = 'visible'; |
315 } else { | 322 } else { |
316 this.pageIndicator_.style.visibility = 'hidden'; | 323 this.pageIndicator_.style.visibility = 'hidden'; |
317 } | 324 } |
318 | 325 |
319 this.messagingHost_.viewportChanged(); | |
320 | |
321 var position = this.viewport_.position; | 326 var position = this.viewport_.position; |
322 var zoom = this.viewport_.zoom; | 327 var zoom = this.viewport_.zoom; |
323 // Notify the plugin of the viewport change. | 328 // Notify the plugin of the viewport change. |
324 this.plugin_.postMessage({ | 329 this.plugin_.postMessage({ |
325 type: 'viewport', | 330 type: 'viewport', |
326 zoom: zoom, | 331 zoom: zoom, |
327 xOffset: position.x, | 332 xOffset: position.x, |
328 yOffset: position.y | 333 yOffset: position.y |
329 }); | 334 }); |
| 335 |
| 336 var visiblePageDimensions = this.viewport_.getPageScreenRect(visiblePage); |
| 337 var size = this.viewport_.size; |
| 338 this.sendScriptingMessage_({ |
| 339 type: 'viewport', |
| 340 pageX: visiblePageDimensions.x, |
| 341 pageY: visiblePageDimensions.y, |
| 342 pageWidth: visiblePageDimensions.width, |
| 343 viewportWidth: size.width, |
| 344 viewportHeight: size.height, |
| 345 }); |
330 }, | 346 }, |
331 | 347 |
332 /** | 348 /** |
333 * Resets the viewer into print preview mode, which is used for Chrome print | 349 * @private |
334 * preview. | 350 * Handle a scripting message from outside the extension (typically sent by |
335 * @param {string} url the url of the pdf to load. | 351 * PDFScriptingAPI in a page containing the extension) to interact with the |
336 * @param {boolean} grayscale true if the pdf should be displayed in | 352 * plugin. |
337 * grayscale, false otherwise. | 353 * @param {MessageObject} message the message to handle. |
338 * @param {Array.<number>} pageNumbers an array of the number to label each | |
339 * page in the document. | |
340 * @param {boolean} modifiable whether the PDF is modifiable or not. | |
341 */ | 354 */ |
342 resetPrintPreviewMode: function(url, | 355 handleScriptingMessage_: function(message) { |
343 grayscale, | 356 switch (message.data.type.toString()) { |
344 pageNumbers, | 357 case 'resetPrintPreviewMode': |
345 modifiable) { | 358 if (!this.inPrintPreviewMode_) { |
346 if (!this.inPrintPreviewMode_) { | 359 this.inPrintPreviewMode_ = true; |
347 this.inPrintPreviewMode_ = true; | 360 this.viewport_.fitToPage(); |
348 this.viewport_.fitToPage(); | 361 } |
| 362 |
| 363 // Stash the scroll location so that it can be restored when the new |
| 364 // document is loaded. |
| 365 this.lastViewportPosition_ = this.viewport_.position; |
| 366 |
| 367 // TODO(raymes): Disable these properly in the plugin. |
| 368 var printButton = $('print-button'); |
| 369 if (printButton) |
| 370 printButton.parentNode.removeChild(printButton); |
| 371 var saveButton = $('save-button'); |
| 372 if (saveButton) |
| 373 saveButton.parentNode.removeChild(saveButton); |
| 374 |
| 375 this.pageIndicator_.pageLabels = message.data.pageNumbers; |
| 376 |
| 377 this.plugin_.postMessage({ |
| 378 type: 'resetPrintPreviewMode', |
| 379 url: message.data.url, |
| 380 grayscale: message.data.grayscale, |
| 381 // If the PDF isn't modifiable we send 0 as the page count so that no |
| 382 // blank placeholder pages get appended to the PDF. |
| 383 pageCount: (message.data.modifiable ? |
| 384 message.data.pageNumbers.length : 0) |
| 385 }); |
| 386 break; |
| 387 case 'loadPreviewPage': |
| 388 this.plugin_.postMessage(message.data); |
| 389 break; |
349 } | 390 } |
350 | 391 |
351 // Stash the scroll location so that it can be restored when the new | |
352 // document is loaded. | |
353 this.lastViewportPosition_ = this.viewport_.position; | |
354 | |
355 // TODO(raymes): Disable these properly in the plugin. | |
356 var printButton = $('print-button'); | |
357 if (printButton) | |
358 printButton.parentNode.removeChild(printButton); | |
359 var saveButton = $('save-button'); | |
360 if (saveButton) | |
361 saveButton.parentNode.removeChild(saveButton); | |
362 | |
363 this.pageIndicator_.pageLabels = pageNumbers; | |
364 | |
365 this.plugin_.postMessage({ | |
366 type: 'resetPrintPreviewMode', | |
367 url: url, | |
368 grayscale: grayscale, | |
369 // If the PDF isn't modifiable we send 0 as the page count so that no | |
370 // blank placeholder pages get appended to the PDF. | |
371 pageCount: (modifiable ? pageNumbers.length : 0) | |
372 }); | |
373 }, | 392 }, |
374 | 393 |
375 /** | 394 /** |
376 * Load a page into the document while in print preview mode. | 395 * @private |
377 * @param {string} url the url of the pdf page to load. | 396 * Send a scripting message outside the extension (typically to |
378 * @param {number} index the index of the page to load. | 397 * PDFScriptingAPI in a page containing the extension). |
| 398 * @param {Object} message the message to send. |
379 */ | 399 */ |
380 loadPreviewPage: function(url, index) { | 400 sendScriptingMessage_: function(message) { |
381 this.plugin_.postMessage({ | 401 window.parent.postMessage(message, '*'); |
382 type: 'loadPreviewPage', | |
383 url: url, | |
384 index: index | |
385 }); | |
386 }, | 402 }, |
387 | 403 |
388 /** | 404 /** |
389 * @type {Viewport} the viewport of the PDF viewer. | 405 * @type {Viewport} the viewport of the PDF viewer. |
390 */ | 406 */ |
391 get viewport() { | 407 get viewport() { |
392 return this.viewport_; | 408 return this.viewport_; |
393 } | 409 } |
394 }; | 410 }; |
395 | 411 |
396 var viewer = new PDFViewer(); | 412 var viewer = new PDFViewer(); |
OLD | NEW |