[Extensions] Make extension message bubble factory platform-abstract
[chromium-blink-merge.git] / chrome / browser / resources / pdf / pdf.js
blob47baecdd3ed758d40d255f38ea9bca3d326b592a
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
3 // found in the LICENSE file.
5 'use strict';
7 /**
8 * @return {number} Width of a scrollbar in pixels
9 */
10 function getScrollbarWidth() {
11 var div = document.createElement('div');
12 div.style.visibility = 'hidden';
13 div.style.overflow = 'scroll';
14 div.style.width = '50px';
15 div.style.height = '50px';
16 div.style.position = 'absolute';
17 document.body.appendChild(div);
18 var result = div.offsetWidth - div.clientWidth;
19 div.parentNode.removeChild(div);
20 return result;
23 /**
24 * Return the filename component of a URL.
25 * @param {string} url The URL to get the filename from.
26 * @return {string} The filename component.
28 function getFilenameFromURL(url) {
29 var components = url.split(/\/|\\/);
30 return components[components.length - 1];
33 /**
34 * Called when navigation happens in the current tab.
35 * @param {string} url The url to be opened in the current tab.
37 function onNavigateInCurrentTab(url) {
38 window.location.href = url;
41 /**
42 * Called when navigation happens in the new tab.
43 * @param {string} url The url to be opened in the new tab.
45 function onNavigateInNewTab(url) {
46 // Prefer the tabs API because it guarantees we can just open a new tab.
47 // window.open doesn't have this guarantee.
48 if (chrome.tabs)
49 chrome.tabs.create({ url: url});
50 else
51 window.open(url);
54 /**
55 * The minimum number of pixels to offset the toolbar by from the bottom and
56 * right side of the screen.
58 PDFViewer.MIN_TOOLBAR_OFFSET = 15;
60 /**
61 * Creates a new PDFViewer. There should only be one of these objects per
62 * document.
63 * @constructor
64 * @param {Object} streamDetails The stream object which points to the data
65 * contained in the PDF.
67 function PDFViewer(streamDetails) {
68 this.streamDetails_ = streamDetails;
69 this.loaded_ = false;
70 this.parentWindow_ = null;
72 this.delayedScriptingMessages_ = [];
74 this.isPrintPreview_ =
75 this.streamDetails_.originalUrl.indexOf('chrome://print') == 0;
76 this.isMaterial_ = location.pathname.substring(1) === 'index-material.html';
78 // The sizer element is placed behind the plugin element to cause scrollbars
79 // to be displayed in the window. It is sized according to the document size
80 // of the pdf and zoom level.
81 this.sizer_ = $('sizer');
82 this.toolbar_ = $('toolbar');
83 this.pageIndicator_ = $('page-indicator');
84 this.progressBar_ = $('progress-bar');
85 this.passwordScreen_ = $('password-screen');
86 this.passwordScreen_.addEventListener('password-submitted',
87 this.onPasswordSubmitted_.bind(this));
88 this.errorScreen_ = $('error-screen');
90 // Create the viewport.
91 this.viewport_ = new Viewport(window,
92 this.sizer_,
93 this.viewportChanged_.bind(this),
94 this.beforeZoom_.bind(this),
95 this.afterZoom_.bind(this),
96 getScrollbarWidth());
98 // Create the plugin object dynamically so we can set its src. The plugin
99 // element is sized to fill the entire window and is set to be fixed
100 // positioning, acting as a viewport. The plugin renders into this viewport
101 // according to the scroll position of the window.
102 this.plugin_ = document.createElement('embed');
103 // NOTE: The plugin's 'id' field must be set to 'plugin' since
104 // chrome/renderer/printing/print_web_view_helper.cc actually references it.
105 this.plugin_.id = 'plugin';
106 this.plugin_.type = 'application/x-google-chrome-pdf';
107 this.plugin_.addEventListener('message', this.handlePluginMessage_.bind(this),
108 false);
110 // Handle scripting messages from outside the extension that wish to interact
111 // with it. We also send a message indicating that extension has loaded and
112 // is ready to receive messages.
113 window.addEventListener('message', this.handleScriptingMessage.bind(this),
114 false);
116 document.title = getFilenameFromURL(this.streamDetails_.originalUrl);
117 this.plugin_.setAttribute('src', this.streamDetails_.originalUrl);
118 this.plugin_.setAttribute('stream-url', this.streamDetails_.streamUrl);
119 var headers = '';
120 for (var header in this.streamDetails_.responseHeaders) {
121 headers += header + ': ' +
122 this.streamDetails_.responseHeaders[header] + '\n';
124 this.plugin_.setAttribute('headers', headers);
126 if (this.isMaterial_)
127 this.plugin_.setAttribute('is-material', '');
129 if (!this.streamDetails_.embedded)
130 this.plugin_.setAttribute('full-frame', '');
131 document.body.appendChild(this.plugin_);
133 // Setup the button event listeners.
134 if (!this.isMaterial_) {
135 $('fit-to-width-button').addEventListener('click',
136 this.viewport_.fitToWidth.bind(this.viewport_));
137 $('fit-to-page-button').addEventListener('click',
138 this.viewport_.fitToPage.bind(this.viewport_));
139 $('zoom-in-button').addEventListener('click',
140 this.viewport_.zoomIn.bind(this.viewport_));
141 $('zoom-out-button').addEventListener('click',
142 this.viewport_.zoomOut.bind(this.viewport_));
143 $('save-button').addEventListener('click', this.save_.bind(this));
144 $('print-button').addEventListener('click', this.print_.bind(this));
147 if (this.isMaterial_) {
148 this.bookmarksPane_ = $('bookmarks-pane');
150 this.zoomSelector_ = $('zoom-selector');
151 this.zoomSelector_.zoomMin = Viewport.ZOOM_FACTOR_RANGE.min * 100;
152 this.zoomSelector_.zoomMax = Viewport.ZOOM_FACTOR_RANGE.max * 100;
153 this.zoomSelector_.addEventListener('zoom', function(e) {
154 this.viewport_.setZoom(e.detail.zoom);
155 }.bind(this));
156 this.zoomSelector_.addEventListener('fit-to-width',
157 this.viewport_.fitToWidth.bind(this.viewport_));
158 this.zoomSelector_.addEventListener('fit-to-page',
159 this.viewport_.fitToPage.bind(this.viewport_));
161 this.materialToolbar_ = $('material-toolbar');
162 this.materialToolbar_.docTitle = document.title;
163 this.materialToolbar_.addEventListener('save', this.save_.bind(this));
164 this.materialToolbar_.addEventListener('print', this.print_.bind(this));
165 this.materialToolbar_.addEventListener('rotate-right',
166 this.rotateClockwise_.bind(this));
167 this.materialToolbar_.addEventListener('toggle-bookmarks', function() {
168 this.bookmarksPane_.buttonToggle();
169 }.bind(this));
171 document.body.addEventListener('change-page', function(e) {
172 this.viewport_.goToPage(e.detail.page);
173 }.bind(this));
175 this.uiManager_ = new UiManager(window, this.materialToolbar_,
176 [this.bookmarksPane_]);
179 // Set up the zoom API.
180 if (this.shouldManageZoom_()) {
181 chrome.tabs.setZoomSettings(this.streamDetails_.tabId,
182 {mode: 'manual', scope: 'per-tab'}, function() {
183 this.zoomManager_ =
184 new ZoomManager(this.viewport_, this.setZoom_.bind(this));
185 chrome.tabs.onZoomChange.addListener(function(zoomChangeInfo) {
186 if (zoomChangeInfo.tabId != this.streamDetails_.tabId)
187 return;
188 this.zoomManager_.onBrowserZoomChange(zoomChangeInfo.newZoomFactor);
189 }.bind(this));
190 }.bind(this));
193 // Setup the keyboard event listener.
194 document.onkeydown = this.handleKeyEvent_.bind(this);
196 // Parse open pdf parameters.
197 this.paramsParser_ =
198 new OpenPDFParamsParser(this.getNamedDestination_.bind(this));
199 this.navigator_ = new Navigator(this.streamDetails_.originalUrl,
200 this.viewport_, this.paramsParser_,
201 onNavigateInCurrentTab, onNavigateInNewTab);
202 this.viewportScroller_ =
203 new ViewportScroller(this.viewport_, this.plugin_, window);
206 PDFViewer.prototype = {
208 * @private
209 * Handle key events. These may come from the user directly or via the
210 * scripting API.
211 * @param {KeyboardEvent} e the event to handle.
213 handleKeyEvent_: function(e) {
214 var position = this.viewport_.position;
215 // Certain scroll events may be sent from outside of the extension.
216 var fromScriptingAPI = e.fromScriptingAPI;
218 var pageUpHandler = function() {
219 // Go to the previous page if we are fit-to-page.
220 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
221 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
222 // Since we do the movement of the page.
223 e.preventDefault();
224 } else if (fromScriptingAPI) {
225 position.y -= this.viewport.size.height;
226 this.viewport.position = position;
228 }.bind(this);
229 var pageDownHandler = function() {
230 // Go to the next page if we are fit-to-page.
231 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
232 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
233 // Since we do the movement of the page.
234 e.preventDefault();
235 } else if (fromScriptingAPI) {
236 position.y += this.viewport.size.height;
237 this.viewport.position = position;
239 }.bind(this);
241 switch (e.keyCode) {
242 case 32: // Space key.
243 if (e.shiftKey)
244 pageUpHandler();
245 else
246 pageDownHandler();
247 return;
248 case 33: // Page up key.
249 pageUpHandler();
250 return;
251 case 34: // Page down key.
252 pageDownHandler();
253 return;
254 case 37: // Left arrow key.
255 if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
256 // Go to the previous page if there are no horizontal scrollbars.
257 if (!this.viewport_.documentHasScrollbars().horizontal) {
258 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
259 // Since we do the movement of the page.
260 e.preventDefault();
261 } else if (fromScriptingAPI) {
262 position.x -= Viewport.SCROLL_INCREMENT;
263 this.viewport.position = position;
266 return;
267 case 38: // Up arrow key.
268 if (fromScriptingAPI) {
269 position.y -= Viewport.SCROLL_INCREMENT;
270 this.viewport.position = position;
272 return;
273 case 39: // Right arrow key.
274 if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
275 // Go to the next page if there are no horizontal scrollbars.
276 if (!this.viewport_.documentHasScrollbars().horizontal) {
277 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
278 // Since we do the movement of the page.
279 e.preventDefault();
280 } else if (fromScriptingAPI) {
281 position.x += Viewport.SCROLL_INCREMENT;
282 this.viewport.position = position;
285 return;
286 case 40: // Down arrow key.
287 if (fromScriptingAPI) {
288 position.y += Viewport.SCROLL_INCREMENT;
289 this.viewport.position = position;
291 return;
292 case 65: // a key.
293 if (e.ctrlKey || e.metaKey) {
294 this.plugin_.postMessage({
295 type: 'selectAll'
297 // Since we do selection ourselves.
298 e.preventDefault();
300 return;
301 case 71: // g key.
302 if (this.isMaterial_ && (e.ctrlKey || e.metaKey)) {
303 this.materialToolbar_.selectPageNumber();
304 // To prevent the default "find text" behaviour in Chrome.
305 e.preventDefault();
307 return;
308 case 219: // left bracket.
309 if (e.ctrlKey)
310 this.rotateCounterClockwise_();
311 return;
312 case 221: // right bracket.
313 if (e.ctrlKey)
314 this.rotateClockwise_();
315 return;
318 // Give print preview a chance to handle the key event.
319 if (!fromScriptingAPI && this.isPrintPreview_) {
320 this.sendScriptingMessage_({
321 type: 'sendKeyEvent',
322 keyEvent: SerializeKeyEvent(e)
328 * @private
329 * Rotate the plugin clockwise.
331 rotateClockwise_: function() {
332 this.plugin_.postMessage({
333 type: 'rotateClockwise'
338 * @private
339 * Rotate the plugin counter-clockwise.
341 rotateCounterClockwise_: function() {
342 this.plugin_.postMessage({
343 type: 'rotateCounterclockwise'
348 * @private
349 * Notify the plugin to print.
351 print_: function() {
352 this.plugin_.postMessage({
353 type: 'print'
358 * @private
359 * Notify the plugin to save.
361 save_: function() {
362 this.plugin_.postMessage({
363 type: 'save'
368 * Fetches the page number corresponding to the given named destination from
369 * the plugin.
370 * @param {string} name The namedDestination to fetch page number from plugin.
372 getNamedDestination_: function(name) {
373 this.plugin_.postMessage({
374 type: 'getNamedDestination',
375 namedDestination: name
380 * @private
381 * Handle open pdf parameters. This function updates the viewport as per
382 * the parameters mentioned in the url while opening pdf. The order is
383 * important as later actions can override the effects of previous actions.
384 * @param {Object} viewportPosition The initial position of the viewport to be
385 * displayed.
387 handleURLParams_: function(viewportPosition) {
388 if (viewportPosition.page != undefined)
389 this.viewport_.goToPage(viewportPosition.page);
390 if (viewportPosition.position) {
391 // Make sure we don't cancel effect of page parameter.
392 this.viewport_.position = {
393 x: this.viewport_.position.x + viewportPosition.position.x,
394 y: this.viewport_.position.y + viewportPosition.position.y
397 if (viewportPosition.zoom)
398 this.viewport_.setZoom(viewportPosition.zoom);
402 * @private
403 * Update the loading progress of the document in response to a progress
404 * message being received from the plugin.
405 * @param {number} progress the progress as a percentage.
407 updateProgress_: function(progress) {
408 if (this.isMaterial_)
409 this.materialToolbar_.loadProgress = progress;
410 else
411 this.progressBar_.progress = progress;
413 if (progress == -1) {
414 // Document load failed.
415 this.errorScreen_.style.visibility = 'visible';
416 this.sizer_.style.display = 'none';
417 if (!this.isMaterial_)
418 this.toolbar_.style.visibility = 'hidden';
419 if (this.passwordScreen_.active) {
420 this.passwordScreen_.deny();
421 this.passwordScreen_.active = false;
423 } else if (progress == 100) {
424 // Document load complete.
425 if (this.lastViewportPosition_)
426 this.viewport_.position = this.lastViewportPosition_;
427 this.paramsParser_.getViewportFromUrlParams(
428 this.streamDetails_.originalUrl, this.handleURLParams_.bind(this));
429 this.loaded_ = true;
430 this.sendScriptingMessage_({
431 type: 'documentLoaded'
433 while (this.delayedScriptingMessages_.length > 0)
434 this.handleScriptingMessage(this.delayedScriptingMessages_.shift());
436 if (this.isMaterial_)
437 this.uiManager_.hideUiAfterTimeout();
442 * @private
443 * An event handler for handling password-submitted events. These are fired
444 * when an event is entered into the password screen.
445 * @param {Object} event a password-submitted event.
447 onPasswordSubmitted_: function(event) {
448 this.plugin_.postMessage({
449 type: 'getPasswordComplete',
450 password: event.detail.password
455 * @private
456 * An event handler for handling message events received from the plugin.
457 * @param {MessageObject} message a message event.
459 handlePluginMessage_: function(message) {
460 switch (message.data.type.toString()) {
461 case 'documentDimensions':
462 this.documentDimensions_ = message.data;
463 this.viewport_.setDocumentDimensions(this.documentDimensions_);
464 // If we received the document dimensions, the password was good so we
465 // can dismiss the password screen.
466 if (this.passwordScreen_.active)
467 this.passwordScreen_.accept();
469 if (this.isMaterial_) {
470 this.materialToolbar_.docLength =
471 this.documentDimensions_.pageDimensions.length;
472 } else {
473 this.pageIndicator_.initialFadeIn();
474 this.toolbar_.initialFadeIn();
476 break;
477 case 'email':
478 var href = 'mailto:' + message.data.to + '?cc=' + message.data.cc +
479 '&bcc=' + message.data.bcc + '&subject=' + message.data.subject +
480 '&body=' + message.data.body;
481 window.location.href = href;
482 break;
483 case 'getAccessibilityJSONReply':
484 this.sendScriptingMessage_(message.data);
485 break;
486 case 'getPassword':
487 // If the password screen isn't up, put it up. Otherwise we're
488 // responding to an incorrect password so deny it.
489 if (!this.passwordScreen_.active)
490 this.passwordScreen_.active = true;
491 else
492 this.passwordScreen_.deny();
493 break;
494 case 'getSelectedTextReply':
495 this.sendScriptingMessage_(message.data);
496 break;
497 case 'goToPage':
498 this.viewport_.goToPage(message.data.page);
499 break;
500 case 'loadProgress':
501 this.updateProgress_(message.data.progress);
502 break;
503 case 'navigate':
504 // If in print preview, always open a new tab.
505 if (this.isPrintPreview_)
506 this.navigator_.navigate(message.data.url, true);
507 else
508 this.navigator_.navigate(message.data.url, message.data.newTab);
509 break;
510 case 'setScrollPosition':
511 var position = this.viewport_.position;
512 if (message.data.x !== undefined)
513 position.x = message.data.x;
514 if (message.data.y !== undefined)
515 position.y = message.data.y;
516 this.viewport_.position = position;
517 break;
518 case 'setTranslatedStrings':
519 this.passwordScreen_.text = message.data.getPasswordString;
520 if (!this.isMaterial_) {
521 this.progressBar_.text = message.data.loadingString;
522 if (!this.isPrintPreview_)
523 this.progressBar_.style.visibility = 'visible';
525 this.errorScreen_.text = message.data.loadFailedString;
526 break;
527 case 'cancelStreamUrl':
528 chrome.mimeHandlerPrivate.abortStream();
529 break;
530 case 'bookmarks':
531 this.bookmarks_ = message.data.bookmarks;
532 if (this.isMaterial_ && this.bookmarks_.length !== 0) {
533 $('bookmarks-container').bookmarks = this.bookmarks;
534 this.materialToolbar_.hasBookmarks = true;
536 break;
537 case 'setIsSelecting':
538 this.viewportScroller_.setEnableScrolling(message.data.isSelecting);
539 break;
540 case 'getNamedDestinationReply':
541 this.paramsParser_.onNamedDestinationReceived(
542 message.data.pageNumber);
543 break;
548 * @private
549 * A callback that's called before the zoom changes. Notify the plugin to stop
550 * reacting to scroll events while zoom is taking place to avoid flickering.
552 beforeZoom_: function() {
553 this.plugin_.postMessage({
554 type: 'stopScrolling'
559 * @private
560 * A callback that's called after the zoom changes. Notify the plugin of the
561 * zoom change and to continue reacting to scroll events.
563 afterZoom_: function() {
564 var position = this.viewport_.position;
565 var zoom = this.viewport_.zoom;
566 if (this.isMaterial_)
567 this.zoomSelector_.zoomValue = 100 * zoom;
568 this.plugin_.postMessage({
569 type: 'viewport',
570 zoom: zoom,
571 xOffset: position.x,
572 yOffset: position.y
574 if (this.zoomManager_)
575 this.zoomManager_.onPdfZoomChange();
578 setZoom_: function(zoom) {
579 return new Promise(function(resolve, reject) {
580 chrome.tabs.setZoom(this.streamDetails_.tabId, zoom, resolve);
581 }.bind(this));
585 * @private
586 * A callback that's called after the viewport changes.
588 viewportChanged_: function() {
589 if (!this.documentDimensions_)
590 return;
592 // Update the buttons selected.
593 if (!this.isMaterial_) {
594 $('fit-to-page-button').classList.remove('polymer-selected');
595 $('fit-to-width-button').classList.remove('polymer-selected');
596 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
597 $('fit-to-page-button').classList.add('polymer-selected');
598 } else if (this.viewport_.fittingType ==
599 Viewport.FittingType.FIT_TO_WIDTH) {
600 $('fit-to-width-button').classList.add('polymer-selected');
604 // Offset the toolbar position so that it doesn't move if scrollbars appear.
605 var hasScrollbars = this.viewport_.documentHasScrollbars();
606 var scrollbarWidth = this.viewport_.scrollbarWidth;
607 var verticalScrollbarWidth = hasScrollbars.vertical ? scrollbarWidth : 0;
608 var horizontalScrollbarWidth =
609 hasScrollbars.horizontal ? scrollbarWidth : 0;
610 var toolbarRight = Math.max(PDFViewer.MIN_TOOLBAR_OFFSET, scrollbarWidth);
611 var toolbarBottom = Math.max(PDFViewer.MIN_TOOLBAR_OFFSET, scrollbarWidth);
612 toolbarRight -= verticalScrollbarWidth;
613 toolbarBottom -= horizontalScrollbarWidth;
614 if (!this.isMaterial_) {
615 this.toolbar_.style.right = toolbarRight + 'px';
616 this.toolbar_.style.bottom = toolbarBottom + 'px';
617 // Hide the toolbar if it doesn't fit in the viewport.
618 if (this.toolbar_.offsetLeft < 0 || this.toolbar_.offsetTop < 0)
619 this.toolbar_.style.visibility = 'hidden';
620 else
621 this.toolbar_.style.visibility = 'visible';
624 // Update the page indicator.
625 var visiblePage = this.viewport_.getMostVisiblePage();
626 if (this.isMaterial_) {
627 this.materialToolbar_.pageIndex = visiblePage;
628 } else {
629 this.pageIndicator_.index = visiblePage;
630 if (this.documentDimensions_.pageDimensions.length > 1 &&
631 hasScrollbars.vertical) {
632 this.pageIndicator_.style.visibility = 'visible';
633 } else {
634 this.pageIndicator_.style.visibility = 'hidden';
638 var visiblePageDimensions = this.viewport_.getPageScreenRect(visiblePage);
639 var size = this.viewport_.size;
640 this.sendScriptingMessage_({
641 type: 'viewport',
642 pageX: visiblePageDimensions.x,
643 pageY: visiblePageDimensions.y,
644 pageWidth: visiblePageDimensions.width,
645 viewportWidth: size.width,
646 viewportHeight: size.height
651 * Handle a scripting message from outside the extension (typically sent by
652 * PDFScriptingAPI in a page containing the extension) to interact with the
653 * plugin.
654 * @param {MessageObject} message the message to handle.
656 handleScriptingMessage: function(message) {
657 if (this.parentWindow_ != message.source) {
658 this.parentWindow_ = message.source;
659 // Ensure that we notify the embedder if the document is loaded.
660 if (this.loaded_) {
661 this.sendScriptingMessage_({
662 type: 'documentLoaded'
667 if (this.handlePrintPreviewScriptingMessage_(message))
668 return;
670 // Delay scripting messages from users of the scripting API until the
671 // document is loaded. This simplifies use of the APIs.
672 if (!this.loaded_) {
673 this.delayedScriptingMessages_.push(message);
674 return;
677 switch (message.data.type.toString()) {
678 case 'getAccessibilityJSON':
679 case 'getSelectedText':
680 case 'print':
681 case 'selectAll':
682 this.plugin_.postMessage(message.data);
683 break;
688 * @private
689 * Handle scripting messages specific to print preview.
690 * @param {MessageObject} message the message to handle.
691 * @return {boolean} true if the message was handled, false otherwise.
693 handlePrintPreviewScriptingMessage_: function(message) {
694 if (!this.isPrintPreview_)
695 return false;
697 switch (message.data.type.toString()) {
698 case 'loadPreviewPage':
699 this.plugin_.postMessage(message.data);
700 return true;
701 case 'resetPrintPreviewMode':
702 this.loaded_ = false;
703 if (!this.inPrintPreviewMode_) {
704 this.inPrintPreviewMode_ = true;
705 this.viewport_.fitToPage();
708 // Stash the scroll location so that it can be restored when the new
709 // document is loaded.
710 this.lastViewportPosition_ = this.viewport_.position;
712 // TODO(raymes): Disable these properly in the plugin.
713 var printButton = $('print-button');
714 if (printButton)
715 printButton.parentNode.removeChild(printButton);
716 var saveButton = $('save-button');
717 if (saveButton)
718 saveButton.parentNode.removeChild(saveButton);
720 if (!this.isMaterial_)
721 this.pageIndicator_.pageLabels = message.data.pageNumbers;
723 this.plugin_.postMessage({
724 type: 'resetPrintPreviewMode',
725 url: message.data.url,
726 grayscale: message.data.grayscale,
727 // If the PDF isn't modifiable we send 0 as the page count so that no
728 // blank placeholder pages get appended to the PDF.
729 pageCount: (message.data.modifiable ?
730 message.data.pageNumbers.length : 0)
732 return true;
733 case 'sendKeyEvent':
734 this.handleKeyEvent_(DeserializeKeyEvent(message.data.keyEvent));
735 return true;
738 return false;
742 * @private
743 * Send a scripting message outside the extension (typically to
744 * PDFScriptingAPI in a page containing the extension).
745 * @param {Object} message the message to send.
747 sendScriptingMessage_: function(message) {
748 if (this.parentWindow_)
749 this.parentWindow_.postMessage(message, '*');
753 * @private
754 * Return whether this PDFViewer should manage zoom for its containing page.
755 * @return {boolean} Whether this PDFViewer should manage zoom for its
756 * containing page.
758 shouldManageZoom_: function() {
759 return !!(chrome.tabs && !this.streamDetails_.embedded &&
760 this.streamDetails_.tabId != -1);
764 * @type {Viewport} the viewport of the PDF viewer.
766 get viewport() {
767 return this.viewport_;
771 * Each bookmark is an Object containing a:
772 * - title
773 * - page (optional)
774 * - array of children (themselves bookmarks)
775 * @type {Array} the top-level bookmarks of the PDF.
777 get bookmarks() {
778 return this.bookmarks_;