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 /** | 7 /** |
8 * @return {number} Width of a scrollbar in pixels | 8 * @return {number} Width of a scrollbar in pixels |
9 */ | 9 */ |
10 function getScrollbarWidth() { | 10 function getScrollbarWidth() { |
(...skipping 25 matching lines...) Expand all Loading... |
36 */ | 36 */ |
37 PDFViewer.MIN_TOOLBAR_OFFSET = 15; | 37 PDFViewer.MIN_TOOLBAR_OFFSET = 15; |
38 | 38 |
39 /** | 39 /** |
40 * Creates a new PDFViewer. There should only be one of these objects per | 40 * Creates a new PDFViewer. There should only be one of these objects per |
41 * document. | 41 * document. |
42 * @param {Object} streamDetails The stream object which points to the data | 42 * @param {Object} streamDetails The stream object which points to the data |
43 * contained in the PDF. | 43 * contained in the PDF. |
44 */ | 44 */ |
45 function PDFViewer(streamDetails) { | 45 function PDFViewer(streamDetails) { |
46 this.streamDetails = streamDetails; | 46 this.streamDetails_ = streamDetails; |
47 this.loaded = false; | 47 this.loaded_ = false; |
| 48 this.parentWindow_ = null; |
48 | 49 |
49 // The sizer element is placed behind the plugin element to cause scrollbars | 50 // The sizer element is placed behind the plugin element to cause scrollbars |
50 // to be displayed in the window. It is sized according to the document size | 51 // to be displayed in the window. It is sized according to the document size |
51 // of the pdf and zoom level. | 52 // of the pdf and zoom level. |
52 this.sizer_ = $('sizer'); | 53 this.sizer_ = $('sizer'); |
53 this.toolbar_ = $('toolbar'); | 54 this.toolbar_ = $('toolbar'); |
54 this.pageIndicator_ = $('page-indicator'); | 55 this.pageIndicator_ = $('page-indicator'); |
55 this.progressBar_ = $('progress-bar'); | 56 this.progressBar_ = $('progress-bar'); |
56 this.passwordScreen_ = $('password-screen'); | 57 this.passwordScreen_ = $('password-screen'); |
57 this.passwordScreen_.addEventListener('password-submitted', | 58 this.passwordScreen_.addEventListener('password-submitted', |
(...skipping 15 matching lines...) Expand all Loading... |
73 // NOTE: The plugin's 'id' field must be set to 'plugin' since | 74 // NOTE: The plugin's 'id' field must be set to 'plugin' since |
74 // chrome/renderer/printing/print_web_view_helper.cc actually references it. | 75 // chrome/renderer/printing/print_web_view_helper.cc actually references it. |
75 this.plugin_.id = 'plugin'; | 76 this.plugin_.id = 'plugin'; |
76 this.plugin_.type = 'application/x-google-chrome-pdf'; | 77 this.plugin_.type = 'application/x-google-chrome-pdf'; |
77 this.plugin_.addEventListener('message', this.handlePluginMessage_.bind(this), | 78 this.plugin_.addEventListener('message', this.handlePluginMessage_.bind(this), |
78 false); | 79 false); |
79 | 80 |
80 // Handle scripting messages from outside the extension that wish to interact | 81 // Handle scripting messages from outside the extension that wish to interact |
81 // with it. We also send a message indicating that extension has loaded and | 82 // with it. We also send a message indicating that extension has loaded and |
82 // is ready to receive messages. | 83 // is ready to receive messages. |
83 window.addEventListener('message', this.handleScriptingMessage_.bind(this), | 84 window.addEventListener('message', this.handleScriptingMessage.bind(this), |
84 false); | 85 false); |
85 this.sendScriptingMessage_({type: 'readyToReceive'}); | |
86 | 86 |
87 document.title = getFilenameFromURL(this.streamDetails.originalUrl); | 87 document.title = getFilenameFromURL(this.streamDetails_.originalUrl); |
88 this.plugin_.setAttribute('src', this.streamDetails.originalUrl); | 88 this.plugin_.setAttribute('src', this.streamDetails_.originalUrl); |
89 this.plugin_.setAttribute('stream-url', this.streamDetails.streamUrl); | 89 this.plugin_.setAttribute('stream-url', this.streamDetails_.streamUrl); |
90 var headers = ''; | 90 var headers = ''; |
91 for (var header in this.streamDetails.responseHeaders) { | 91 for (var header in this.streamDetails_.responseHeaders) { |
92 headers += header + ': ' + | 92 headers += header + ': ' + |
93 this.streamDetails.responseHeaders[header] + '\n'; | 93 this.streamDetails_.responseHeaders[header] + '\n'; |
94 } | 94 } |
95 this.plugin_.setAttribute('headers', headers); | 95 this.plugin_.setAttribute('headers', headers); |
96 | 96 |
97 if (!this.streamDetails.embedded) | 97 if (!this.streamDetails_.embedded) |
98 this.plugin_.setAttribute('full-frame', ''); | 98 this.plugin_.setAttribute('full-frame', ''); |
99 document.body.appendChild(this.plugin_); | 99 document.body.appendChild(this.plugin_); |
100 | 100 |
101 // TODO(raymes): Remove this spurious message once crbug.com/388606 is fixed. | |
102 // This is a hack to initialize pepper sync scripting and avoid re-entrancy. | |
103 this.plugin_.postMessage({ | |
104 type: 'viewport', | |
105 zoom: 1, | |
106 xOffset: 0, | |
107 yOffset: 0 | |
108 }); | |
109 | |
110 // Setup the button event listeners. | 101 // Setup the button event listeners. |
111 $('fit-to-width-button').addEventListener('click', | 102 $('fit-to-width-button').addEventListener('click', |
112 this.viewport_.fitToWidth.bind(this.viewport_)); | 103 this.viewport_.fitToWidth.bind(this.viewport_)); |
113 $('fit-to-page-button').addEventListener('click', | 104 $('fit-to-page-button').addEventListener('click', |
114 this.viewport_.fitToPage.bind(this.viewport_)); | 105 this.viewport_.fitToPage.bind(this.viewport_)); |
115 $('zoom-in-button').addEventListener('click', | 106 $('zoom-in-button').addEventListener('click', |
116 this.viewport_.zoomIn.bind(this.viewport_)); | 107 this.viewport_.zoomIn.bind(this.viewport_)); |
117 $('zoom-out-button').addEventListener('click', | 108 $('zoom-out-button').addEventListener('click', |
118 this.viewport_.zoomOut.bind(this.viewport_)); | 109 this.viewport_.zoomOut.bind(this.viewport_)); |
119 $('save-button').addEventListener('click', this.save_.bind(this)); | 110 $('save-button').addEventListener('click', this.save_.bind(this)); |
120 $('print-button').addEventListener('click', this.print_.bind(this)); | 111 $('print-button').addEventListener('click', this.print_.bind(this)); |
121 | 112 |
122 // Setup the keyboard event listener. | 113 // Setup the keyboard event listener. |
123 document.onkeydown = this.handleKeyEvent_.bind(this); | 114 document.onkeydown = this.handleKeyEvent_.bind(this); |
124 | 115 |
125 // Set up the zoom API. | 116 // Set up the zoom API. |
126 if (this.shouldManageZoom_()) { | 117 if (this.shouldManageZoom_()) { |
127 chrome.tabs.setZoomSettings(this.streamDetails.tabId, | 118 chrome.tabs.setZoomSettings(this.streamDetails_.tabId, |
128 {mode: 'manual', scope: 'per-tab'}, | 119 {mode: 'manual', scope: 'per-tab'}, |
129 this.afterZoom_.bind(this)); | 120 this.afterZoom_.bind(this)); |
130 chrome.tabs.onZoomChange.addListener(function(zoomChangeInfo) { | 121 chrome.tabs.onZoomChange.addListener(function(zoomChangeInfo) { |
131 if (zoomChangeInfo.tabId != this.streamDetails.tabId) | 122 if (zoomChangeInfo.tabId != this.streamDetails_.tabId) |
132 return; | 123 return; |
133 // If the zoom level is close enough to the current zoom level, don't | 124 // If the zoom level is close enough to the current zoom level, don't |
134 // change it. This avoids us getting into an infinite loop of zoom changes | 125 // change it. This avoids us getting into an infinite loop of zoom changes |
135 // due to floating point error. | 126 // due to floating point error. |
136 var MIN_ZOOM_DELTA = 0.01; | 127 var MIN_ZOOM_DELTA = 0.01; |
137 var zoomDelta = Math.abs(this.viewport_.zoom - | 128 var zoomDelta = Math.abs(this.viewport_.zoom - |
138 zoomChangeInfo.newZoomFactor); | 129 zoomChangeInfo.newZoomFactor); |
139 // We should not change zoom level when we are responsible for initiating | 130 // We should not change zoom level when we are responsible for initiating |
140 // the zoom. onZoomChange() is called before setZoomComplete() callback | 131 // the zoom. onZoomChange() is called before setZoomComplete() callback |
141 // when we initiate the zoom. | 132 // when we initiate the zoom. |
142 if ((zoomDelta > MIN_ZOOM_DELTA) && !this.setZoomInProgress_) | 133 if ((zoomDelta > MIN_ZOOM_DELTA) && !this.setZoomInProgress_) |
143 this.viewport_.setZoom(zoomChangeInfo.newZoomFactor); | 134 this.viewport_.setZoom(zoomChangeInfo.newZoomFactor); |
144 }.bind(this)); | 135 }.bind(this)); |
145 } | 136 } |
146 | 137 |
147 // Parse open pdf parameters. | 138 // Parse open pdf parameters. |
148 var paramsParser = new OpenPDFParamsParser(this.streamDetails.originalUrl); | 139 var paramsParser = new OpenPDFParamsParser(this.streamDetails_.originalUrl); |
149 this.urlParams_ = paramsParser.urlParams; | 140 this.urlParams_ = paramsParser.urlParams; |
150 } | 141 } |
151 | 142 |
152 PDFViewer.prototype = { | 143 PDFViewer.prototype = { |
153 /** | 144 /** |
154 * @private | 145 * @private |
155 * Handle key events. These may come from the user directly or via the | 146 * Handle key events. These may come from the user directly or via the |
156 * scripting API. | 147 * scripting API. |
157 * @param {KeyboardEvent} e the event to handle. | 148 * @param {KeyboardEvent} e the event to handle. |
158 */ | 149 */ |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 this.toolbar_.style.visibility = 'hidden'; | 312 this.toolbar_.style.visibility = 'hidden'; |
322 if (this.passwordScreen_.active) { | 313 if (this.passwordScreen_.active) { |
323 this.passwordScreen_.deny(); | 314 this.passwordScreen_.deny(); |
324 this.passwordScreen_.active = false; | 315 this.passwordScreen_.active = false; |
325 } | 316 } |
326 } else if (progress == 100) { | 317 } else if (progress == 100) { |
327 // Document load complete. | 318 // Document load complete. |
328 if (this.lastViewportPosition_) | 319 if (this.lastViewportPosition_) |
329 this.viewport_.position = this.lastViewportPosition_; | 320 this.viewport_.position = this.lastViewportPosition_; |
330 this.handleURLParams_(); | 321 this.handleURLParams_(); |
331 this.loaded = true; | 322 this.loaded_ = true; |
332 var loadEvent = new Event('pdfload'); | |
333 window.dispatchEvent(loadEvent); | |
334 this.sendScriptingMessage_({ | 323 this.sendScriptingMessage_({ |
335 type: 'documentLoaded' | 324 type: 'documentLoaded' |
336 }); | 325 }); |
337 } | 326 } |
338 }, | 327 }, |
339 | 328 |
340 /** | 329 /** |
341 * @private | 330 * @private |
342 * An event handler for handling password-submitted events. These are fired | 331 * An event handler for handling password-submitted events. These are fired |
343 * when an event is entered into the password screen. | 332 * when an event is entered into the password screen. |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
409 position.y = message.data.y; | 398 position.y = message.data.y; |
410 this.viewport_.position = position; | 399 this.viewport_.position = position; |
411 break; | 400 break; |
412 case 'setTranslatedStrings': | 401 case 'setTranslatedStrings': |
413 this.passwordScreen_.text = message.data.getPasswordString; | 402 this.passwordScreen_.text = message.data.getPasswordString; |
414 this.progressBar_.text = message.data.loadingString; | 403 this.progressBar_.text = message.data.loadingString; |
415 this.progressBar_.style.visibility = 'visible'; | 404 this.progressBar_.style.visibility = 'visible'; |
416 this.errorScreen_.text = message.data.loadFailedString; | 405 this.errorScreen_.text = message.data.loadFailedString; |
417 break; | 406 break; |
418 case 'cancelStreamUrl': | 407 case 'cancelStreamUrl': |
419 chrome.streamsPrivate.abort(this.streamDetails.streamUrl); | 408 chrome.streamsPrivate.abort(this.streamDetails_.streamUrl); |
420 break; | 409 break; |
421 } | 410 } |
422 }, | 411 }, |
423 | 412 |
424 /** | 413 /** |
425 * @private | 414 * @private |
426 * A callback that's called before the zoom changes. Notify the plugin to stop | 415 * A callback that's called before the zoom changes. Notify the plugin to stop |
427 * reacting to scroll events while zoom is taking place to avoid flickering. | 416 * reacting to scroll events while zoom is taking place to avoid flickering. |
428 */ | 417 */ |
429 beforeZoom_: function() { | 418 beforeZoom_: function() { |
430 this.plugin_.postMessage({ | 419 this.plugin_.postMessage({ |
431 type: 'stopScrolling' | 420 type: 'stopScrolling' |
432 }); | 421 }); |
433 }, | 422 }, |
434 | 423 |
435 /** | 424 /** |
436 * @private | 425 * @private |
437 * A callback that's called after the zoom changes. Notify the plugin of the | 426 * A callback that's called after the zoom changes. Notify the plugin of the |
438 * zoom change and to continue reacting to scroll events. | 427 * zoom change and to continue reacting to scroll events. |
439 */ | 428 */ |
440 afterZoom_: function() { | 429 afterZoom_: function() { |
441 var position = this.viewport_.position; | 430 var position = this.viewport_.position; |
442 var zoom = this.viewport_.zoom; | 431 var zoom = this.viewport_.zoom; |
443 if (this.shouldManageZoom_() && !this.setZoomInProgress_) { | 432 if (this.shouldManageZoom_() && !this.setZoomInProgress_) { |
444 this.setZoomInProgress_ = true; | 433 this.setZoomInProgress_ = true; |
445 chrome.tabs.setZoom(this.streamDetails.tabId, zoom, | 434 chrome.tabs.setZoom(this.streamDetails_.tabId, zoom, |
446 this.setZoomComplete_.bind(this, zoom)); | 435 this.setZoomComplete_.bind(this, zoom)); |
447 } | 436 } |
448 this.plugin_.postMessage({ | 437 this.plugin_.postMessage({ |
449 type: 'viewport', | 438 type: 'viewport', |
450 zoom: zoom, | 439 zoom: zoom, |
451 xOffset: position.x, | 440 xOffset: position.x, |
452 yOffset: position.y | 441 yOffset: position.y |
453 }); | 442 }); |
454 }, | 443 }, |
455 | 444 |
456 /** | 445 /** |
457 * @private | 446 * @private |
458 * A callback that's called after chrome.tabs.setZoom is complete. This will | 447 * A callback that's called after chrome.tabs.setZoom is complete. This will |
459 * call chrome.tabs.setZoom again if the zoom level has changed since it was | 448 * call chrome.tabs.setZoom again if the zoom level has changed since it was |
460 * last called. | 449 * last called. |
461 * @param {number} lastZoom the zoom level that chrome.tabs.setZoom was called | 450 * @param {number} lastZoom the zoom level that chrome.tabs.setZoom was called |
462 * with. | 451 * with. |
463 */ | 452 */ |
464 setZoomComplete_: function(lastZoom) { | 453 setZoomComplete_: function(lastZoom) { |
465 var zoom = this.viewport_.zoom; | 454 var zoom = this.viewport_.zoom; |
466 if (zoom != lastZoom) { | 455 if (zoom != lastZoom) { |
467 chrome.tabs.setZoom(this.streamDetails.tabId, zoom, | 456 chrome.tabs.setZoom(this.streamDetails_.tabId, zoom, |
468 this.setZoomComplete_.bind(this, zoom)); | 457 this.setZoomComplete_.bind(this, zoom)); |
469 } else { | 458 } else { |
470 this.setZoomInProgress_ = false; | 459 this.setZoomInProgress_ = false; |
471 } | 460 } |
472 }, | 461 }, |
473 | 462 |
474 /** | 463 /** |
475 * @private | 464 * @private |
476 * A callback that's called after the viewport changes. | 465 * A callback that's called after the viewport changes. |
477 */ | 466 */ |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
528 }); | 517 }); |
529 }, | 518 }, |
530 | 519 |
531 /** | 520 /** |
532 * @private | 521 * @private |
533 * Handle a scripting message from outside the extension (typically sent by | 522 * Handle a scripting message from outside the extension (typically sent by |
534 * PDFScriptingAPI in a page containing the extension) to interact with the | 523 * PDFScriptingAPI in a page containing the extension) to interact with the |
535 * plugin. | 524 * plugin. |
536 * @param {MessageObject} message the message to handle. | 525 * @param {MessageObject} message the message to handle. |
537 */ | 526 */ |
538 handleScriptingMessage_: function(message) { | 527 handleScriptingMessage: function(message) { |
539 switch (message.data.type.toString()) { | 528 switch (message.data.type.toString()) { |
540 case 'getAccessibilityJSON': | 529 case 'getAccessibilityJSON': |
541 case 'loadPreviewPage': | 530 case 'loadPreviewPage': |
542 this.plugin_.postMessage(message.data); | 531 this.plugin_.postMessage(message.data); |
543 break; | 532 break; |
544 case 'resetPrintPreviewMode': | 533 case 'resetPrintPreviewMode': |
545 if (!this.inPrintPreviewMode_) { | 534 if (!this.inPrintPreviewMode_) { |
546 this.inPrintPreviewMode_ = true; | 535 this.inPrintPreviewMode_ = true; |
547 this.viewport_.fitToPage(); | 536 this.viewport_.fitToPage(); |
548 } | 537 } |
(...skipping 21 matching lines...) Expand all Loading... |
570 pageCount: (message.data.modifiable ? | 559 pageCount: (message.data.modifiable ? |
571 message.data.pageNumbers.length : 0) | 560 message.data.pageNumbers.length : 0) |
572 }); | 561 }); |
573 break; | 562 break; |
574 case 'sendKeyEvent': | 563 case 'sendKeyEvent': |
575 var e = document.createEvent('Event'); | 564 var e = document.createEvent('Event'); |
576 e.initEvent('scriptingKeypress'); | 565 e.initEvent('scriptingKeypress'); |
577 e.keyCode = message.data.keyCode; | 566 e.keyCode = message.data.keyCode; |
578 this.handleKeyEvent_(e); | 567 this.handleKeyEvent_(e); |
579 break; | 568 break; |
| 569 case 'setParentWindow': |
| 570 if (this.parentWindow_ != message.source) { |
| 571 this.parentWindow_ = message.source; |
| 572 // If the document has already loaded, we always send a message that |
| 573 // indicates that so that the embedder is aware. |
| 574 if (this.loaded_) { |
| 575 this.sendScriptingMessage_({ |
| 576 type: 'documentLoaded' |
| 577 }); |
| 578 } |
| 579 } |
| 580 break; |
580 } | 581 } |
581 | |
582 }, | 582 }, |
583 | 583 |
584 /** | 584 /** |
585 * @private | 585 * @private |
586 * Send a scripting message outside the extension (typically to | 586 * Send a scripting message outside the extension (typically to |
587 * PDFScriptingAPI in a page containing the extension). | 587 * PDFScriptingAPI in a page containing the extension). |
588 * @param {Object} message the message to send. | 588 * @param {Object} message the message to send. |
589 */ | 589 */ |
590 sendScriptingMessage_: function(message) { | 590 sendScriptingMessage_: function(message) { |
591 window.parent.postMessage(message, '*'); | 591 if (this.parentWindow_) |
| 592 this.parentWindow_.postMessage(message, '*'); |
592 }, | 593 }, |
593 | 594 |
594 /** | 595 /** |
595 * @private | 596 * @private |
596 * Return whether this PDFViewer should manage zoom for its containing page. | 597 * Return whether this PDFViewer should manage zoom for its containing page. |
597 * @return {boolean} Whether this PDFViewer should manage zoom for its | 598 * @return {boolean} Whether this PDFViewer should manage zoom for its |
598 * containing page. | 599 * containing page. |
599 */ | 600 */ |
600 shouldManageZoom_: function() { | 601 shouldManageZoom_: function() { |
601 return !!(chrome.tabs && !this.streamDetails.embedded && | 602 return !!(chrome.tabs && !this.streamDetails_.embedded && |
602 this.streamDetails.tabId != -1); | 603 this.streamDetails_.tabId != -1); |
603 }, | 604 }, |
604 | 605 |
605 /** | 606 /** |
606 * @type {Viewport} the viewport of the PDF viewer. | 607 * @type {Viewport} the viewport of the PDF viewer. |
607 */ | 608 */ |
608 get viewport() { | 609 get viewport() { |
609 return this.viewport_; | 610 return this.viewport_; |
610 } | 611 } |
611 }; | 612 }; |
OLD | NEW |