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.
8 * @return {number} Width of a scrollbar in pixels
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
);
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];
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
;
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.
49 chrome
.tabs
.create({ url
: url
});
55 * Whether keydown events should currently be ignored. Events are ignored when
56 * an editable element has focus, to allow for proper editing controls.
57 * @param {HTMLElement} activeElement The currently selected DOM node.
58 * @return {boolean} True if keydown events should be ignored.
60 function shouldIgnoreKeyEvents(activeElement
) {
61 while (activeElement
.shadowRoot
!= null &&
62 activeElement
.shadowRoot
.activeElement
!= null) {
63 activeElement
= activeElement
.shadowRoot
.activeElement
;
66 return (activeElement
.isContentEditable
||
67 activeElement
.tagName
== 'INPUT' ||
68 activeElement
.tagName
== 'TEXTAREA');
72 * The minimum number of pixels to offset the toolbar by from the bottom and
73 * right side of the screen.
75 PDFViewer
.MIN_TOOLBAR_OFFSET
= 15;
78 * The height of the toolbar along the top of the page. The document will be
79 * shifted down by this much in the viewport.
81 PDFViewer
.MATERIAL_TOOLBAR_HEIGHT
= 64;
84 * Creates a new PDFViewer. There should only be one of these objects per
87 * @param {!BrowserApi} browserApi An object providing an API to the browser.
89 function PDFViewer(browserApi
) {
90 this.browserApi_
= browserApi
;
91 this.loadState_
= LoadState
.LOADING
;
92 this.parentWindow_
= null;
94 this.delayedScriptingMessages_
= [];
96 this.isPrintPreview_
= this.browserApi_
.getStreamInfo().originalUrl
.indexOf(
97 'chrome://print') == 0;
98 this.isMaterial_
= location
.pathname
.substring(1) === 'index-material.html';
100 // The sizer element is placed behind the plugin element to cause scrollbars
101 // to be displayed in the window. It is sized according to the document size
102 // of the pdf and zoom level.
103 this.sizer_
= $('sizer');
104 this.toolbar_
= $('toolbar');
105 this.pageIndicator_
= $('page-indicator');
106 this.progressBar_
= $('progress-bar');
107 this.passwordScreen_
= $('password-screen');
108 this.passwordScreen_
.addEventListener('password-submitted',
109 this.onPasswordSubmitted_
.bind(this));
110 this.errorScreen_
= $('error-screen');
112 // Create the viewport.
113 var topToolbarHeight
=
114 this.isMaterial_
? PDFViewer
.MATERIAL_TOOLBAR_HEIGHT
: 0;
115 this.viewport_
= new Viewport(window
,
117 this.viewportChanged_
.bind(this),
118 this.beforeZoom_
.bind(this),
119 this.afterZoom_
.bind(this),
121 this.browserApi_
.getDefaultZoom(),
124 // Create the plugin object dynamically so we can set its src. The plugin
125 // element is sized to fill the entire window and is set to be fixed
126 // positioning, acting as a viewport. The plugin renders into this viewport
127 // according to the scroll position of the window.
128 this.plugin_
= document
.createElement('embed');
129 // NOTE: The plugin's 'id' field must be set to 'plugin' since
130 // chrome/renderer/printing/print_web_view_helper.cc actually references it.
131 this.plugin_
.id
= 'plugin';
132 this.plugin_
.type
= 'application/x-google-chrome-pdf';
133 this.plugin_
.addEventListener('message', this.handlePluginMessage_
.bind(this),
136 // Handle scripting messages from outside the extension that wish to interact
137 // with it. We also send a message indicating that extension has loaded and
138 // is ready to receive messages.
139 window
.addEventListener('message', this.handleScriptingMessage
.bind(this),
142 document
.title
= decodeURIComponent(
143 getFilenameFromURL(this.browserApi_
.getStreamInfo().originalUrl
));
144 this.plugin_
.setAttribute('src',
145 this.browserApi_
.getStreamInfo().originalUrl
);
146 this.plugin_
.setAttribute('stream-url',
147 this.browserApi_
.getStreamInfo().streamUrl
);
149 for (var header
in this.browserApi_
.getStreamInfo().responseHeaders
) {
150 headers
+= header
+ ': ' +
151 this.browserApi_
.getStreamInfo().responseHeaders
[header
] + '\n';
153 this.plugin_
.setAttribute('headers', headers
);
155 if (this.isMaterial_
) {
156 this.plugin_
.setAttribute('is-material', '');
157 this.plugin_
.setAttribute('top-toolbar-height',
158 PDFViewer
.MATERIAL_TOOLBAR_HEIGHT
);
161 if (!this.browserApi_
.getStreamInfo().embedded
)
162 this.plugin_
.setAttribute('full-frame', '');
163 document
.body
.appendChild(this.plugin_
);
165 // Setup the button event listeners.
166 if (!this.isMaterial_
) {
167 $('fit-to-width-button').addEventListener('click',
168 this.viewport_
.fitToWidth
.bind(this.viewport_
));
169 $('fit-to-page-button').addEventListener('click',
170 this.viewport_
.fitToPage
.bind(this.viewport_
));
171 $('zoom-in-button').addEventListener('click',
172 this.viewport_
.zoomIn
.bind(this.viewport_
));
173 $('zoom-out-button').addEventListener('click',
174 this.viewport_
.zoomOut
.bind(this.viewport_
));
175 $('save-button').addEventListener('click', this.save_
.bind(this));
176 $('print-button').addEventListener('click', this.print_
.bind(this));
179 if (this.isMaterial_
) {
180 this.zoomToolbar_
= $('zoom-toolbar');
181 this.zoomToolbar_
.addEventListener('fit-to-width',
182 this.viewport_
.fitToWidth
.bind(this.viewport_
));
183 this.zoomToolbar_
.addEventListener('fit-to-page',
184 this.fitToPage_
.bind(this));
185 this.zoomToolbar_
.addEventListener('zoom-in',
186 this.viewport_
.zoomIn
.bind(this.viewport_
));
187 this.zoomToolbar_
.addEventListener('zoom-out',
188 this.viewport_
.zoomOut
.bind(this.viewport_
));
190 this.materialToolbar_
= $('material-toolbar');
191 this.materialToolbar_
.docTitle
= document
.title
;
192 this.materialToolbar_
.addEventListener('save', this.save_
.bind(this));
193 this.materialToolbar_
.addEventListener('print', this.print_
.bind(this));
194 this.materialToolbar_
.addEventListener('rotate-right',
195 this.rotateClockwise_
.bind(this));
196 this.materialToolbar_
.addEventListener('rotate-left',
197 this.rotateCounterClockwise_
.bind(this));
199 document
.body
.addEventListener('change-page', function(e
) {
200 this.viewport_
.goToPage(e
.detail
.page
);
203 this.toolbarManager_
=
204 new ToolbarManager(window
, this.materialToolbar_
, this.zoomToolbar_
);
206 // Must attach to mouseup on the plugin element, since it eats mousedown and
208 this.plugin_
.addEventListener(
210 this.materialToolbar_
.hideDropdowns
.bind(this.materialToolbar_
));
213 // Set up the ZoomManager.
214 this.zoomManager_
= new ZoomManager(
215 this.viewport_
, this.browserApi_
.setZoom
.bind(this.browserApi_
),
216 this.browserApi_
.getDefaultZoom());
217 this.browserApi_
.addZoomEventListener(
218 this.zoomManager_
.onBrowserZoomChange
.bind(this.zoomManager_
));
220 // Setup the keyboard event listener.
221 document
.addEventListener('keydown', this.handleKeyEvent_
.bind(this));
222 document
.addEventListener('mousemove', this.handleMouseEvent_
.bind(this));
224 // Parse open pdf parameters.
226 new OpenPDFParamsParser(this.getNamedDestination_
.bind(this));
227 this.navigator_
= new Navigator(this.browserApi_
.getStreamInfo().originalUrl
,
228 this.viewport_
, this.paramsParser_
,
229 onNavigateInCurrentTab
, onNavigateInNewTab
);
230 this.viewportScroller_
=
231 new ViewportScroller(this.viewport_
, this.plugin_
, window
);
234 PDFViewer
.prototype = {
237 * Handle key events. These may come from the user directly or via the
239 * @param {KeyboardEvent} e the event to handle.
241 handleKeyEvent_: function(e
) {
242 var position
= this.viewport_
.position
;
243 // Certain scroll events may be sent from outside of the extension.
244 var fromScriptingAPI
= e
.fromScriptingAPI
;
246 if (shouldIgnoreKeyEvents(document
.activeElement
) || e
.defaultPrevented
)
249 if (this.isMaterial_
)
250 this.toolbarManager_
.hideToolbarsAfterTimeout(e
);
252 var pageUpHandler = function() {
253 // Go to the previous page if we are fit-to-page.
254 if (this.viewport_
.fittingType
== Viewport
.FittingType
.FIT_TO_PAGE
) {
255 this.viewport_
.goToPage(this.viewport_
.getMostVisiblePage() - 1);
256 // Since we do the movement of the page.
258 } else if (fromScriptingAPI
) {
259 position
.y
-= this.viewport
.size
.height
;
260 this.viewport
.position
= position
;
263 var pageDownHandler = function() {
264 // Go to the next page if we are fit-to-page.
265 if (this.viewport_
.fittingType
== Viewport
.FittingType
.FIT_TO_PAGE
) {
266 this.viewport_
.goToPage(this.viewport_
.getMostVisiblePage() + 1);
267 // Since we do the movement of the page.
269 } else if (fromScriptingAPI
) {
270 position
.y
+= this.viewport
.size
.height
;
271 this.viewport
.position
= position
;
276 case 27: // Escape key.
277 if (this.isMaterial_
)
278 this.toolbarManager_
.hideSingleToolbarLayer();
280 case 32: // Space key.
286 case 33: // Page up key.
289 case 34: // Page down key.
292 case 37: // Left arrow key.
293 if (!(e
.altKey
|| e
.ctrlKey
|| e
.metaKey
|| e
.shiftKey
)) {
294 // Go to the previous page if there are no horizontal scrollbars.
295 if (!this.viewport_
.documentHasScrollbars().horizontal
) {
296 this.viewport_
.goToPage(this.viewport_
.getMostVisiblePage() - 1);
297 // Since we do the movement of the page.
299 } else if (fromScriptingAPI
) {
300 position
.x
-= Viewport
.SCROLL_INCREMENT
;
301 this.viewport
.position
= position
;
305 case 38: // Up arrow key.
306 if (fromScriptingAPI
) {
307 position
.y
-= Viewport
.SCROLL_INCREMENT
;
308 this.viewport
.position
= position
;
311 case 39: // Right arrow key.
312 if (!(e
.altKey
|| e
.ctrlKey
|| e
.metaKey
|| e
.shiftKey
)) {
313 // Go to the next page if there are no horizontal scrollbars.
314 if (!this.viewport_
.documentHasScrollbars().horizontal
) {
315 this.viewport_
.goToPage(this.viewport_
.getMostVisiblePage() + 1);
316 // Since we do the movement of the page.
318 } else if (fromScriptingAPI
) {
319 position
.x
+= Viewport
.SCROLL_INCREMENT
;
320 this.viewport
.position
= position
;
324 case 40: // Down arrow key.
325 if (fromScriptingAPI
) {
326 position
.y
+= Viewport
.SCROLL_INCREMENT
;
327 this.viewport
.position
= position
;
331 if (e
.ctrlKey
|| e
.metaKey
) {
332 this.plugin_
.postMessage({
335 // Since we do selection ourselves.
340 if (this.isMaterial_
&& (e
.ctrlKey
|| e
.metaKey
)) {
341 this.toolbarManager_
.showToolbars();
342 this.materialToolbar_
.selectPageNumber();
343 // To prevent the default "find text" behaviour in Chrome.
347 case 219: // left bracket.
349 this.rotateCounterClockwise_();
351 case 221: // right bracket.
353 this.rotateClockwise_();
357 // Give print preview a chance to handle the key event.
358 if (!fromScriptingAPI
&& this.isPrintPreview_
) {
359 this.sendScriptingMessage_({
360 type
: 'sendKeyEvent',
361 keyEvent
: SerializeKeyEvent(e
)
363 } else if (this.isMaterial_
) {
364 // Show toolbars as a fallback.
365 if (!(e
.shiftKey
|| e
.ctrlKey
|| e
.altKey
))
366 this.toolbarManager_
.showToolbars();
370 handleMouseEvent_: function(e
) {
371 if (this.isMaterial_
)
372 this.toolbarManager_
.showToolbarsForMouseMove(e
);
377 * Rotate the plugin clockwise.
379 rotateClockwise_: function() {
380 this.plugin_
.postMessage({
381 type
: 'rotateClockwise'
387 * Rotate the plugin counter-clockwise.
389 rotateCounterClockwise_: function() {
390 this.plugin_
.postMessage({
391 type
: 'rotateCounterclockwise'
395 fitToPage_: function() {
396 this.viewport_
.fitToPage();
397 this.toolbarManager_
.forceHideTopToolbar();
402 * Notify the plugin to print.
405 this.plugin_
.postMessage({
412 * Notify the plugin to save.
415 this.plugin_
.postMessage({
421 * Fetches the page number corresponding to the given named destination from
423 * @param {string} name The namedDestination to fetch page number from plugin.
425 getNamedDestination_: function(name
) {
426 this.plugin_
.postMessage({
427 type
: 'getNamedDestination',
428 namedDestination
: name
434 * Sends a 'documentLoaded' message to the PDFScriptingAPI if the document has
437 sendDocumentLoadedMessage_: function() {
438 if (this.loadState_
== LoadState
.LOADING
)
440 this.sendScriptingMessage_({
441 type
: 'documentLoaded',
442 load_state
: this.loadState_
448 * Handle open pdf parameters. This function updates the viewport as per
449 * the parameters mentioned in the url while opening pdf. The order is
450 * important as later actions can override the effects of previous actions.
451 * @param {Object} viewportPosition The initial position of the viewport to be
454 handleURLParams_: function(viewportPosition
) {
455 if (viewportPosition
.page
!= undefined)
456 this.viewport_
.goToPage(viewportPosition
.page
);
457 if (viewportPosition
.position
) {
458 // Make sure we don't cancel effect of page parameter.
459 this.viewport_
.position
= {
460 x
: this.viewport_
.position
.x
+ viewportPosition
.position
.x
,
461 y
: this.viewport_
.position
.y
+ viewportPosition
.position
.y
464 if (viewportPosition
.zoom
)
465 this.viewport_
.setZoom(viewportPosition
.zoom
);
470 * Update the loading progress of the document in response to a progress
471 * message being received from the plugin.
472 * @param {number} progress the progress as a percentage.
474 updateProgress_: function(progress
) {
475 if (this.isMaterial_
)
476 this.materialToolbar_
.loadProgress
= progress
;
478 this.progressBar_
.progress
= progress
;
480 if (progress
== -1) {
481 // Document load failed.
482 this.errorScreen_
.style
.visibility
= 'visible';
483 this.sizer_
.style
.display
= 'none';
484 if (!this.isMaterial_
)
485 this.toolbar_
.style
.visibility
= 'hidden';
486 if (this.passwordScreen_
.active
) {
487 this.passwordScreen_
.deny();
488 this.passwordScreen_
.active
= false;
490 this.loadState_
= LoadState
.FAILED
;
491 this.sendDocumentLoadedMessage_();
492 } else if (progress
== 100) {
493 // Document load complete.
494 if (this.lastViewportPosition_
)
495 this.viewport_
.position
= this.lastViewportPosition_
;
496 this.paramsParser_
.getViewportFromUrlParams(
497 this.browserApi_
.getStreamInfo().originalUrl
,
498 this.handleURLParams_
.bind(this));
499 this.loadState_
= LoadState
.SUCCESS
;
500 this.sendDocumentLoadedMessage_();
501 while (this.delayedScriptingMessages_
.length
> 0)
502 this.handleScriptingMessage(this.delayedScriptingMessages_
.shift());
504 if (this.isMaterial_
)
505 this.toolbarManager_
.hideToolbarsAfterTimeout();
511 * An event handler for handling password-submitted events. These are fired
512 * when an event is entered into the password screen.
513 * @param {Object} event a password-submitted event.
515 onPasswordSubmitted_: function(event
) {
516 this.plugin_
.postMessage({
517 type
: 'getPasswordComplete',
518 password
: event
.detail
.password
524 * An event handler for handling message events received from the plugin.
525 * @param {MessageObject} message a message event.
527 handlePluginMessage_: function(message
) {
528 switch (message
.data
.type
.toString()) {
529 case 'documentDimensions':
530 this.documentDimensions_
= message
.data
;
531 this.viewport_
.setDocumentDimensions(this.documentDimensions_
);
532 // If we received the document dimensions, the password was good so we
533 // can dismiss the password screen.
534 if (this.passwordScreen_
.active
)
535 this.passwordScreen_
.accept();
537 if (this.isMaterial_
) {
538 this.materialToolbar_
.docLength
=
539 this.documentDimensions_
.pageDimensions
.length
;
541 this.pageIndicator_
.initialFadeIn();
542 this.toolbar_
.initialFadeIn();
546 var href
= 'mailto:' + message
.data
.to
+ '?cc=' + message
.data
.cc
+
547 '&bcc=' + message
.data
.bcc
+ '&subject=' + message
.data
.subject
+
548 '&body=' + message
.data
.body
;
549 window
.location
.href
= href
;
551 case 'getAccessibilityJSONReply':
552 this.sendScriptingMessage_(message
.data
);
555 // If the password screen isn't up, put it up. Otherwise we're
556 // responding to an incorrect password so deny it.
557 if (!this.passwordScreen_
.active
)
558 this.passwordScreen_
.active
= true;
560 this.passwordScreen_
.deny();
562 case 'getSelectedTextReply':
563 this.sendScriptingMessage_(message
.data
);
566 this.viewport_
.goToPage(message
.data
.page
);
569 this.updateProgress_(message
.data
.progress
);
572 // If in print preview, always open a new tab.
573 if (this.isPrintPreview_
)
574 this.navigator_
.navigate(message
.data
.url
, true);
576 this.navigator_
.navigate(message
.data
.url
, message
.data
.newTab
);
578 case 'setScrollPosition':
579 var position
= this.viewport_
.position
;
580 if (message
.data
.x
!== undefined)
581 position
.x
= message
.data
.x
;
582 if (message
.data
.y
!== undefined)
583 position
.y
= message
.data
.y
;
584 this.viewport_
.position
= position
;
586 case 'setTranslatedStrings':
587 this.passwordScreen_
.text
= message
.data
.getPasswordString
;
588 if (!this.isMaterial_
) {
589 this.progressBar_
.text
= message
.data
.loadingString
;
590 if (!this.isPrintPreview_
)
591 this.progressBar_
.style
.visibility
= 'visible';
593 this.errorScreen_
.text
= message
.data
.loadFailedString
;
595 case 'cancelStreamUrl':
596 chrome
.mimeHandlerPrivate
.abortStream();
599 this.bookmarks_
= message
.data
.bookmarks
;
600 if (this.isMaterial_
&& this.bookmarks_
.length
!== 0)
601 this.materialToolbar_
.bookmarks
= this.bookmarks
;
603 case 'setIsSelecting':
604 this.viewportScroller_
.setEnableScrolling(message
.data
.isSelecting
);
606 case 'getNamedDestinationReply':
607 this.paramsParser_
.onNamedDestinationReceived(
608 message
.data
.pageNumber
);
615 * A callback that's called before the zoom changes. Notify the plugin to stop
616 * reacting to scroll events while zoom is taking place to avoid flickering.
618 beforeZoom_: function() {
619 this.plugin_
.postMessage({
620 type
: 'stopScrolling'
626 * A callback that's called after the zoom changes. Notify the plugin of the
627 * zoom change and to continue reacting to scroll events.
629 afterZoom_: function() {
630 var position
= this.viewport_
.position
;
631 var zoom
= this.viewport_
.zoom
;
632 if (this.isMaterial_
)
633 this.zoomToolbar_
.zoomValue
= 100 * zoom
;
634 this.plugin_
.postMessage({
640 this.zoomManager_
.onPdfZoomChange();
645 * A callback that's called after the viewport changes.
647 viewportChanged_: function() {
648 if (!this.documentDimensions_
)
651 // Update the buttons selected.
652 if (!this.isMaterial_
) {
653 $('fit-to-page-button').classList
.remove('polymer-selected');
654 $('fit-to-width-button').classList
.remove('polymer-selected');
655 if (this.viewport_
.fittingType
== Viewport
.FittingType
.FIT_TO_PAGE
) {
656 $('fit-to-page-button').classList
.add('polymer-selected');
657 } else if (this.viewport_
.fittingType
==
658 Viewport
.FittingType
.FIT_TO_WIDTH
) {
659 $('fit-to-width-button').classList
.add('polymer-selected');
663 // Offset the toolbar position so that it doesn't move if scrollbars appear.
664 var hasScrollbars
= this.viewport_
.documentHasScrollbars();
665 var scrollbarWidth
= this.viewport_
.scrollbarWidth
;
666 var verticalScrollbarWidth
= hasScrollbars
.vertical
? scrollbarWidth
: 0;
667 var horizontalScrollbarWidth
=
668 hasScrollbars
.horizontal
? scrollbarWidth
: 0;
669 var toolbarRight
= Math
.max(PDFViewer
.MIN_TOOLBAR_OFFSET
, scrollbarWidth
);
670 var toolbarBottom
= Math
.max(PDFViewer
.MIN_TOOLBAR_OFFSET
, scrollbarWidth
);
671 toolbarRight
-= verticalScrollbarWidth
;
672 toolbarBottom
-= horizontalScrollbarWidth
;
673 if (!this.isMaterial_
) {
674 this.toolbar_
.style
.right
= toolbarRight
+ 'px';
675 this.toolbar_
.style
.bottom
= toolbarBottom
+ 'px';
676 // Hide the toolbar if it doesn't fit in the viewport.
677 if (this.toolbar_
.offsetLeft
< 0 || this.toolbar_
.offsetTop
< 0)
678 this.toolbar_
.style
.visibility
= 'hidden';
680 this.toolbar_
.style
.visibility
= 'visible';
683 // Update the page indicator.
684 var visiblePage
= this.viewport_
.getMostVisiblePage();
685 if (this.isMaterial_
) {
686 this.materialToolbar_
.pageNo
= visiblePage
+ 1;
688 this.pageIndicator_
.index
= visiblePage
;
689 if (this.documentDimensions_
.pageDimensions
.length
> 1 &&
690 hasScrollbars
.vertical
) {
691 this.pageIndicator_
.style
.visibility
= 'visible';
693 this.pageIndicator_
.style
.visibility
= 'hidden';
697 var visiblePageDimensions
= this.viewport_
.getPageScreenRect(visiblePage
);
698 var size
= this.viewport_
.size
;
699 this.sendScriptingMessage_({
701 pageX
: visiblePageDimensions
.x
,
702 pageY
: visiblePageDimensions
.y
,
703 pageWidth
: visiblePageDimensions
.width
,
704 viewportWidth
: size
.width
,
705 viewportHeight
: size
.height
710 * Handle a scripting message from outside the extension (typically sent by
711 * PDFScriptingAPI in a page containing the extension) to interact with the
713 * @param {MessageObject} message the message to handle.
715 handleScriptingMessage: function(message
) {
716 if (this.parentWindow_
!= message
.source
) {
717 this.parentWindow_
= message
.source
;
718 // Ensure that we notify the embedder if the document is loaded.
719 if (this.loadState_
!= LoadState
.LOADING
)
720 this.sendDocumentLoadedMessage_();
723 if (this.handlePrintPreviewScriptingMessage_(message
))
726 // Delay scripting messages from users of the scripting API until the
727 // document is loaded. This simplifies use of the APIs.
728 if (this.loadState_
!= LoadState
.SUCCESS
) {
729 this.delayedScriptingMessages_
.push(message
);
733 switch (message
.data
.type
.toString()) {
734 case 'getAccessibilityJSON':
735 case 'getSelectedText':
738 this.plugin_
.postMessage(message
.data
);
745 * Handle scripting messages specific to print preview.
746 * @param {MessageObject} message the message to handle.
747 * @return {boolean} true if the message was handled, false otherwise.
749 handlePrintPreviewScriptingMessage_: function(message
) {
750 if (!this.isPrintPreview_
)
753 switch (message
.data
.type
.toString()) {
754 case 'loadPreviewPage':
755 this.plugin_
.postMessage(message
.data
);
757 case 'resetPrintPreviewMode':
758 this.loadState_
= LoadState
.LOADING
;
759 if (!this.inPrintPreviewMode_
) {
760 this.inPrintPreviewMode_
= true;
761 this.viewport_
.fitToPage();
764 // Stash the scroll location so that it can be restored when the new
765 // document is loaded.
766 this.lastViewportPosition_
= this.viewport_
.position
;
768 // TODO(raymes): Disable these properly in the plugin.
769 var printButton
= $('print-button');
771 printButton
.parentNode
.removeChild(printButton
);
772 var saveButton
= $('save-button');
774 saveButton
.parentNode
.removeChild(saveButton
);
776 if (!this.isMaterial_
)
777 this.pageIndicator_
.pageLabels
= message
.data
.pageNumbers
;
779 this.plugin_
.postMessage({
780 type
: 'resetPrintPreviewMode',
781 url
: message
.data
.url
,
782 grayscale
: message
.data
.grayscale
,
783 // If the PDF isn't modifiable we send 0 as the page count so that no
784 // blank placeholder pages get appended to the PDF.
785 pageCount
: (message
.data
.modifiable
?
786 message
.data
.pageNumbers
.length
: 0)
790 this.handleKeyEvent_(DeserializeKeyEvent(message
.data
.keyEvent
));
799 * Send a scripting message outside the extension (typically to
800 * PDFScriptingAPI in a page containing the extension).
801 * @param {Object} message the message to send.
803 sendScriptingMessage_: function(message
) {
804 if (this.parentWindow_
)
805 this.parentWindow_
.postMessage(message
, '*');
810 * @type {Viewport} the viewport of the PDF viewer.
813 return this.viewport_
;
817 * Each bookmark is an Object containing a:
820 * - array of children (themselves bookmarks)
821 * @type {Array} the top-level bookmarks of the PDF.
824 return this.bookmarks_
;