Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / resources / pdf / pdf.js
blob4fae2314f21249cabd43d70200848ed67a8c3011
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 // Setup the keyboard event listener.
180 document.onkeydown = this.handleKeyEvent_.bind(this);
182 // Set up the zoom API.
183 if (this.shouldManageZoom_()) {
184 chrome.tabs.setZoomSettings(this.streamDetails_.tabId,
185 {mode: 'manual', scope: 'per-tab'},
186 this.afterZoom_.bind(this));
187 chrome.tabs.onZoomChange.addListener(function(zoomChangeInfo) {
188 if (zoomChangeInfo.tabId != this.streamDetails_.tabId)
189 return;
190 // If the zoom level is close enough to the current zoom level, don't
191 // change it. This avoids us getting into an infinite loop of zoom changes
192 // due to floating point error.
193 var MIN_ZOOM_DELTA = 0.01;
194 var zoomDelta = Math.abs(this.viewport_.zoom -
195 zoomChangeInfo.newZoomFactor);
196 // We should not change zoom level when we are responsible for initiating
197 // the zoom. onZoomChange() is called before setZoomComplete() callback
198 // when we initiate the zoom.
199 if ((zoomDelta > MIN_ZOOM_DELTA) && !this.setZoomInProgress_)
200 this.viewport_.setZoom(zoomChangeInfo.newZoomFactor);
201 }.bind(this));
204 // Parse open pdf parameters.
205 this.paramsParser_ =
206 new OpenPDFParamsParser(this.getNamedDestination_.bind(this));
207 this.navigator_ = new Navigator(this.streamDetails_.originalUrl,
208 this.viewport_, this.paramsParser_,
209 onNavigateInCurrentTab, onNavigateInNewTab);
210 this.viewportScroller_ =
211 new ViewportScroller(this.viewport_, this.plugin_, window);
214 PDFViewer.prototype = {
216 * @private
217 * Handle key events. These may come from the user directly or via the
218 * scripting API.
219 * @param {KeyboardEvent} e the event to handle.
221 handleKeyEvent_: function(e) {
222 var position = this.viewport_.position;
223 // Certain scroll events may be sent from outside of the extension.
224 var fromScriptingAPI = e.fromScriptingAPI;
226 var pageUpHandler = function() {
227 // Go to the previous page if we are fit-to-page.
228 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
229 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
230 // Since we do the movement of the page.
231 e.preventDefault();
232 } else if (fromScriptingAPI) {
233 position.y -= this.viewport.size.height;
234 this.viewport.position = position;
236 }.bind(this);
237 var pageDownHandler = function() {
238 // Go to the next page if we are fit-to-page.
239 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
240 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
241 // Since we do the movement of the page.
242 e.preventDefault();
243 } else if (fromScriptingAPI) {
244 position.y += this.viewport.size.height;
245 this.viewport.position = position;
247 }.bind(this);
249 switch (e.keyCode) {
250 case 32: // Space key.
251 if (e.shiftKey)
252 pageUpHandler();
253 else
254 pageDownHandler();
255 return;
256 case 33: // Page up key.
257 pageUpHandler();
258 return;
259 case 34: // Page down key.
260 pageDownHandler();
261 return;
262 case 37: // Left arrow key.
263 if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
264 // Go to the previous page if there are no horizontal scrollbars.
265 if (!this.viewport_.documentHasScrollbars().horizontal) {
266 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
267 // Since we do the movement of the page.
268 e.preventDefault();
269 } else if (fromScriptingAPI) {
270 position.x -= Viewport.SCROLL_INCREMENT;
271 this.viewport.position = position;
274 return;
275 case 38: // Up arrow key.
276 if (fromScriptingAPI) {
277 position.y -= Viewport.SCROLL_INCREMENT;
278 this.viewport.position = position;
280 return;
281 case 39: // Right arrow key.
282 if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) {
283 // Go to the next page if there are no horizontal scrollbars.
284 if (!this.viewport_.documentHasScrollbars().horizontal) {
285 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
286 // Since we do the movement of the page.
287 e.preventDefault();
288 } else if (fromScriptingAPI) {
289 position.x += Viewport.SCROLL_INCREMENT;
290 this.viewport.position = position;
293 return;
294 case 40: // Down arrow key.
295 if (fromScriptingAPI) {
296 position.y += Viewport.SCROLL_INCREMENT;
297 this.viewport.position = position;
299 return;
300 case 65: // a key.
301 if (e.ctrlKey || e.metaKey) {
302 this.plugin_.postMessage({
303 type: 'selectAll'
305 // Since we do selection ourselves.
306 e.preventDefault();
308 return;
309 case 71: // g key.
310 if (this.isMaterial_ && (e.ctrlKey || e.metaKey)) {
311 this.materialToolbar_.selectPageNumber();
312 // To prevent the default "find text" behaviour in Chrome.
313 e.preventDefault();
315 return;
316 case 219: // left bracket.
317 if (e.ctrlKey)
318 this.rotateCounterClockwise_();
319 return;
320 case 221: // right bracket.
321 if (e.ctrlKey)
322 this.rotateClockwise_();
323 return;
326 // Give print preview a chance to handle the key event.
327 if (!fromScriptingAPI && this.isPrintPreview_) {
328 this.sendScriptingMessage_({
329 type: 'sendKeyEvent',
330 keyEvent: SerializeKeyEvent(e)
336 * @private
337 * Rotate the plugin clockwise.
339 rotateClockwise_: function() {
340 this.plugin_.postMessage({
341 type: 'rotateClockwise'
346 * @private
347 * Rotate the plugin counter-clockwise.
349 rotateCounterClockwise_: function() {
350 this.plugin_.postMessage({
351 type: 'rotateCounterclockwise'
356 * @private
357 * Notify the plugin to print.
359 print_: function() {
360 this.plugin_.postMessage({
361 type: 'print'
366 * @private
367 * Notify the plugin to save.
369 save_: function() {
370 this.plugin_.postMessage({
371 type: 'save'
376 * Fetches the page number corresponding to the given named destination from
377 * the plugin.
378 * @param {string} name The namedDestination to fetch page number from plugin.
380 getNamedDestination_: function(name) {
381 this.plugin_.postMessage({
382 type: 'getNamedDestination',
383 namedDestination: name
388 * @private
389 * Handle open pdf parameters. This function updates the viewport as per
390 * the parameters mentioned in the url while opening pdf. The order is
391 * important as later actions can override the effects of previous actions.
392 * @param {Object} viewportPosition The initial position of the viewport to be
393 * displayed.
395 handleURLParams_: function(viewportPosition) {
396 if (viewportPosition.page != undefined)
397 this.viewport_.goToPage(viewportPosition.page);
398 if (viewportPosition.position) {
399 // Make sure we don't cancel effect of page parameter.
400 this.viewport_.position = {
401 x: this.viewport_.position.x + viewportPosition.position.x,
402 y: this.viewport_.position.y + viewportPosition.position.y
405 if (viewportPosition.zoom)
406 this.viewport_.setZoom(viewportPosition.zoom);
410 * @private
411 * Update the loading progress of the document in response to a progress
412 * message being received from the plugin.
413 * @param {number} progress the progress as a percentage.
415 updateProgress_: function(progress) {
416 if (this.isMaterial_)
417 this.materialToolbar_.loadProgress = progress;
418 else
419 this.progressBar_.progress = progress;
421 if (progress == -1) {
422 // Document load failed.
423 this.errorScreen_.style.visibility = 'visible';
424 this.sizer_.style.display = 'none';
425 if (!this.isMaterial_)
426 this.toolbar_.style.visibility = 'hidden';
427 if (this.passwordScreen_.active) {
428 this.passwordScreen_.deny();
429 this.passwordScreen_.active = false;
431 } else if (progress == 100) {
432 // Document load complete.
433 if (this.lastViewportPosition_)
434 this.viewport_.position = this.lastViewportPosition_;
435 this.paramsParser_.getViewportFromUrlParams(
436 this.streamDetails_.originalUrl, this.handleURLParams_.bind(this));
437 this.loaded_ = true;
438 this.sendScriptingMessage_({
439 type: 'documentLoaded'
441 while (this.delayedScriptingMessages_.length > 0)
442 this.handleScriptingMessage(this.delayedScriptingMessages_.shift());
444 if (this.isMaterial_)
445 this.uiManager_.hideUiAfterTimeout();
450 * @private
451 * An event handler for handling password-submitted events. These are fired
452 * when an event is entered into the password screen.
453 * @param {Object} event a password-submitted event.
455 onPasswordSubmitted_: function(event) {
456 this.plugin_.postMessage({
457 type: 'getPasswordComplete',
458 password: event.detail.password
463 * @private
464 * An event handler for handling message events received from the plugin.
465 * @param {MessageObject} message a message event.
467 handlePluginMessage_: function(message) {
468 switch (message.data.type.toString()) {
469 case 'documentDimensions':
470 this.documentDimensions_ = message.data;
471 this.viewport_.setDocumentDimensions(this.documentDimensions_);
472 // If we received the document dimensions, the password was good so we
473 // can dismiss the password screen.
474 if (this.passwordScreen_.active)
475 this.passwordScreen_.accept();
477 if (this.isMaterial_) {
478 this.materialToolbar_.docLength =
479 this.documentDimensions_.pageDimensions.length;
480 } else {
481 this.pageIndicator_.initialFadeIn();
482 this.toolbar_.initialFadeIn();
485 break;
486 case 'email':
487 var href = 'mailto:' + message.data.to + '?cc=' + message.data.cc +
488 '&bcc=' + message.data.bcc + '&subject=' + message.data.subject +
489 '&body=' + message.data.body;
490 window.location.href = href;
491 break;
492 case 'getAccessibilityJSONReply':
493 this.sendScriptingMessage_(message.data);
494 break;
495 case 'getPassword':
496 // If the password screen isn't up, put it up. Otherwise we're
497 // responding to an incorrect password so deny it.
498 if (!this.passwordScreen_.active)
499 this.passwordScreen_.active = true;
500 else
501 this.passwordScreen_.deny();
502 break;
503 case 'getSelectedTextReply':
504 this.sendScriptingMessage_(message.data);
505 break;
506 case 'goToPage':
507 this.viewport_.goToPage(message.data.page);
508 break;
509 case 'loadProgress':
510 this.updateProgress_(message.data.progress);
511 break;
512 case 'navigate':
513 // If in print preview, always open a new tab.
514 if (this.isPrintPreview_)
515 this.navigator_.navigate(message.data.url, true);
516 else
517 this.navigator_.navigate(message.data.url, message.data.newTab);
518 break;
519 case 'setScrollPosition':
520 var position = this.viewport_.position;
521 if (message.data.x !== undefined)
522 position.x = message.data.x;
523 if (message.data.y !== undefined)
524 position.y = message.data.y;
525 this.viewport_.position = position;
526 break;
527 case 'setTranslatedStrings':
528 this.passwordScreen_.text = message.data.getPasswordString;
529 if (!this.isMaterial_) {
530 this.progressBar_.text = message.data.loadingString;
531 if (!this.isPrintPreview_)
532 this.progressBar_.style.visibility = 'visible';
534 this.errorScreen_.text = message.data.loadFailedString;
535 break;
536 case 'cancelStreamUrl':
537 chrome.mimeHandlerPrivate.abortStream();
538 break;
539 case 'bookmarks':
540 this.bookmarks_ = message.data.bookmarks;
541 if (this.isMaterial_ && this.bookmarks_.length !== 0) {
542 $('bookmarks-container').bookmarks = this.bookmarks;
543 this.materialToolbar_.hasBookmarks = true;
545 break;
546 case 'setIsSelecting':
547 this.viewportScroller_.setEnableScrolling(message.data.isSelecting);
548 break;
549 case 'getNamedDestinationReply':
550 this.paramsParser_.onNamedDestinationReceived(
551 message.data.pageNumber);
552 break;
557 * @private
558 * A callback that's called before the zoom changes. Notify the plugin to stop
559 * reacting to scroll events while zoom is taking place to avoid flickering.
561 beforeZoom_: function() {
562 this.plugin_.postMessage({
563 type: 'stopScrolling'
568 * @private
569 * A callback that's called after the zoom changes. Notify the plugin of the
570 * zoom change and to continue reacting to scroll events.
572 afterZoom_: function() {
573 var position = this.viewport_.position;
574 var zoom = this.viewport_.zoom;
575 if (this.isMaterial_)
576 this.zoomSelector_.zoomValue = 100 * zoom;
577 if (this.shouldManageZoom_() && !this.setZoomInProgress_) {
578 this.setZoomInProgress_ = true;
579 chrome.tabs.setZoom(this.streamDetails_.tabId, zoom,
580 this.setZoomComplete_.bind(this, zoom));
582 this.plugin_.postMessage({
583 type: 'viewport',
584 zoom: zoom,
585 xOffset: position.x,
586 yOffset: position.y
591 * @private
592 * A callback that's called after chrome.tabs.setZoom is complete. This will
593 * call chrome.tabs.setZoom again if the zoom level has changed since it was
594 * last called.
595 * @param {number} lastZoom the zoom level that chrome.tabs.setZoom was called
596 * with.
598 setZoomComplete_: function(lastZoom) {
599 var zoom = this.viewport_.zoom;
600 if (zoom !== lastZoom) {
601 chrome.tabs.setZoom(this.streamDetails_.tabId, zoom,
602 this.setZoomComplete_.bind(this, zoom));
603 } else {
604 this.setZoomInProgress_ = false;
609 * @private
610 * A callback that's called after the viewport changes.
612 viewportChanged_: function() {
613 if (!this.documentDimensions_)
614 return;
616 // Update the buttons selected.
617 if (!this.isMaterial_) {
618 $('fit-to-page-button').classList.remove('polymer-selected');
619 $('fit-to-width-button').classList.remove('polymer-selected');
620 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
621 $('fit-to-page-button').classList.add('polymer-selected');
622 } else if (this.viewport_.fittingType ==
623 Viewport.FittingType.FIT_TO_WIDTH) {
624 $('fit-to-width-button').classList.add('polymer-selected');
628 // Offset the toolbar position so that it doesn't move if scrollbars appear.
629 var hasScrollbars = this.viewport_.documentHasScrollbars();
630 var scrollbarWidth = this.viewport_.scrollbarWidth;
631 var verticalScrollbarWidth = hasScrollbars.vertical ? scrollbarWidth : 0;
632 var horizontalScrollbarWidth =
633 hasScrollbars.horizontal ? scrollbarWidth : 0;
634 var toolbarRight = Math.max(PDFViewer.MIN_TOOLBAR_OFFSET, scrollbarWidth);
635 var toolbarBottom = Math.max(PDFViewer.MIN_TOOLBAR_OFFSET, scrollbarWidth);
636 toolbarRight -= verticalScrollbarWidth;
637 toolbarBottom -= horizontalScrollbarWidth;
638 if (!this.isMaterial_) {
639 this.toolbar_.style.right = toolbarRight + 'px';
640 this.toolbar_.style.bottom = toolbarBottom + 'px';
641 // Hide the toolbar if it doesn't fit in the viewport.
642 if (this.toolbar_.offsetLeft < 0 || this.toolbar_.offsetTop < 0)
643 this.toolbar_.style.visibility = 'hidden';
644 else
645 this.toolbar_.style.visibility = 'visible';
648 // Update the page indicator.
649 var visiblePage = this.viewport_.getMostVisiblePage();
650 if (this.isMaterial_) {
651 this.materialToolbar_.pageIndex = visiblePage;
652 } else {
653 this.pageIndicator_.index = visiblePage;
654 if (this.documentDimensions_.pageDimensions.length > 1 &&
655 hasScrollbars.vertical) {
656 this.pageIndicator_.style.visibility = 'visible';
657 } else {
658 this.pageIndicator_.style.visibility = 'hidden';
662 var visiblePageDimensions = this.viewport_.getPageScreenRect(visiblePage);
663 var size = this.viewport_.size;
664 this.sendScriptingMessage_({
665 type: 'viewport',
666 pageX: visiblePageDimensions.x,
667 pageY: visiblePageDimensions.y,
668 pageWidth: visiblePageDimensions.width,
669 viewportWidth: size.width,
670 viewportHeight: size.height
675 * Handle a scripting message from outside the extension (typically sent by
676 * PDFScriptingAPI in a page containing the extension) to interact with the
677 * plugin.
678 * @param {MessageObject} message the message to handle.
680 handleScriptingMessage: function(message) {
681 if (this.parentWindow_ != message.source) {
682 this.parentWindow_ = message.source;
683 // Ensure that we notify the embedder if the document is loaded.
684 if (this.loaded_) {
685 this.sendScriptingMessage_({
686 type: 'documentLoaded'
691 if (this.handlePrintPreviewScriptingMessage_(message))
692 return;
694 // Delay scripting messages from users of the scripting API until the
695 // document is loaded. This simplifies use of the APIs.
696 if (!this.loaded_) {
697 this.delayedScriptingMessages_.push(message);
698 return;
701 switch (message.data.type.toString()) {
702 case 'getAccessibilityJSON':
703 case 'getSelectedText':
704 case 'print':
705 case 'selectAll':
706 this.plugin_.postMessage(message.data);
707 break;
712 * @private
713 * Handle scripting messages specific to print preview.
714 * @param {MessageObject} message the message to handle.
715 * @return {boolean} true if the message was handled, false otherwise.
717 handlePrintPreviewScriptingMessage_: function(message) {
718 if (!this.isPrintPreview_)
719 return false;
721 switch (message.data.type.toString()) {
722 case 'loadPreviewPage':
723 this.plugin_.postMessage(message.data);
724 return true;
725 case 'resetPrintPreviewMode':
726 this.loaded_ = false;
727 if (!this.inPrintPreviewMode_) {
728 this.inPrintPreviewMode_ = true;
729 this.viewport_.fitToPage();
732 // Stash the scroll location so that it can be restored when the new
733 // document is loaded.
734 this.lastViewportPosition_ = this.viewport_.position;
736 // TODO(raymes): Disable these properly in the plugin.
737 var printButton = $('print-button');
738 if (printButton)
739 printButton.parentNode.removeChild(printButton);
740 var saveButton = $('save-button');
741 if (saveButton)
742 saveButton.parentNode.removeChild(saveButton);
744 if (!this.isMaterial_)
745 this.pageIndicator_.pageLabels = message.data.pageNumbers;
747 this.plugin_.postMessage({
748 type: 'resetPrintPreviewMode',
749 url: message.data.url,
750 grayscale: message.data.grayscale,
751 // If the PDF isn't modifiable we send 0 as the page count so that no
752 // blank placeholder pages get appended to the PDF.
753 pageCount: (message.data.modifiable ?
754 message.data.pageNumbers.length : 0)
756 return true;
757 case 'sendKeyEvent':
758 this.handleKeyEvent_(DeserializeKeyEvent(message.data.keyEvent));
759 return true;
762 return false;
766 * @private
767 * Send a scripting message outside the extension (typically to
768 * PDFScriptingAPI in a page containing the extension).
769 * @param {Object} message the message to send.
771 sendScriptingMessage_: function(message) {
772 if (this.parentWindow_)
773 this.parentWindow_.postMessage(message, '*');
777 * @private
778 * Return whether this PDFViewer should manage zoom for its containing page.
779 * @return {boolean} Whether this PDFViewer should manage zoom for its
780 * containing page.
782 shouldManageZoom_: function() {
783 return !!(chrome.tabs && !this.streamDetails_.embedded &&
784 this.streamDetails_.tabId != -1);
788 * @type {Viewport} the viewport of the PDF viewer.
790 get viewport() {
791 return this.viewport_;
795 * Each bookmark is an Object containing a:
796 * - title
797 * - page (optional)
798 * - array of children (themselves bookmarks)
799 * @type {Array} the top-level bookmarks of the PDF.
801 get bookmarks() {
802 return this.bookmarks_;