Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / resources / pdf / pdf.js
blob4af1116f6440a5d70ea8ca0a81b24dc5a99eb454
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 <include src="../../../../ui/webui/resources/js/util.js">
8 <include src="viewport.js">
9 <include src="pdf_scripting_api.js">
11 /**
12 * @return {number} Width of a scrollbar in pixels
14 function getScrollbarWidth() {
15 var div = document.createElement('div');
16 div.style.visibility = 'hidden';
17 div.style.overflow = 'scroll';
18 div.style.width = '50px';
19 div.style.height = '50px';
20 div.style.position = 'absolute';
21 document.body.appendChild(div);
22 var result = div.offsetWidth - div.clientWidth;
23 div.parentNode.removeChild(div);
24 return result;
27 /**
28 * Creates a new PDFViewer. There should only be one of these objects per
29 * document.
31 function PDFViewer() {
32 // The sizer element is placed behind the plugin element to cause scrollbars
33 // to be displayed in the window. It is sized according to the document size
34 // of the pdf and zoom level.
35 this.sizer_ = $('sizer');
36 this.toolbar_ = $('toolbar');
37 this.pageIndicator_ = $('page-indicator');
38 this.progressBar_ = $('progress-bar');
39 this.passwordScreen_ = $('password-screen');
40 this.passwordScreen_.addEventListener('password-submitted',
41 this.onPasswordSubmitted_.bind(this));
42 this.errorScreen_ = $('error-screen');
44 // Create the viewport.
45 this.viewport_ = new Viewport(window,
46 this.sizer_,
47 this.viewportChangedCallback_.bind(this),
48 getScrollbarWidth());
50 // Create the plugin object dynamically so we can set its src. The plugin
51 // element is sized to fill the entire window and is set to be fixed
52 // positioning, acting as a viewport. The plugin renders into this viewport
53 // according to the scroll position of the window.
54 this.plugin_ = document.createElement('object');
55 this.plugin_.id = 'plugin';
56 this.plugin_.type = 'application/x-google-chrome-pdf';
57 this.plugin_.addEventListener('message', this.handleMessage_.bind(this),
58 false);
60 // If the viewer is started from a MIME type request, there will be a
61 // background page and stream details object with the details of the request.
62 // Otherwise, we take the query string of the URL to indicate the URL of the
63 // PDF to load. This is used for print preview in particular.
64 var streamDetails;
65 if (chrome.extension.getBackgroundPage &&
66 chrome.extension.getBackgroundPage()) {
67 streamDetails = chrome.extension.getBackgroundPage().popStreamDetails();
70 if (!streamDetails) {
71 // The URL of this page will be of the form
72 // "chrome-extension://<extension id>?<pdf url>". We pull out the <pdf url>
73 // part here.
74 var url = window.location.search.substring(1);
75 streamDetails = {
76 streamUrl: url,
77 originalUrl: url
81 this.plugin_.setAttribute('src', streamDetails.streamUrl);
82 if (window.top == window)
83 this.plugin_.setAttribute('full-frame', '');
84 document.body.appendChild(this.plugin_);
86 this.messagingHost_ = new PDFMessagingHost(window, this);
88 this.setupEventListeners_(streamDetails);
91 PDFViewer.prototype = {
92 /**
93 * @private
94 * Sets up event listeners for key shortcuts and also the UI buttons.
95 * @param {Object} streamDetails the details of the original HTTP request for
96 * the PDF.
98 setupEventListeners_: function(streamDetails) {
99 // Setup the button event listeners.
100 $('fit-to-width-button').addEventListener('click',
101 this.viewport_.fitToWidth.bind(this.viewport_));
102 $('fit-to-page-button').addEventListener('click',
103 this.viewport_.fitToPage.bind(this.viewport_));
104 $('zoom-in-button').addEventListener('click',
105 this.viewport_.zoomIn.bind(this.viewport_));
106 $('zoom-out-button').addEventListener('click',
107 this.viewport_.zoomOut.bind(this.viewport_));
108 $('save-button-link').href = streamDetails.originalUrl;
109 $('print-button').addEventListener('click', this.print_.bind(this));
111 // Setup keyboard event listeners.
112 document.onkeydown = function(e) {
113 switch (e.keyCode) {
114 case 37: // Left arrow key.
115 // Go to the previous page if there are no horizontal scrollbars.
116 if (!this.viewport_.documentHasScrollbars().x) {
117 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
118 // Since we do the movement of the page.
119 e.preventDefault();
121 return;
122 case 33: // Page up key.
123 // Go to the previous page if we are fit-to-page.
124 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
125 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() - 1);
126 // Since we do the movement of the page.
127 e.preventDefault();
129 return;
130 case 39: // Right arrow key.
131 // Go to the next page if there are no horizontal scrollbars.
132 if (!this.viewport_.documentHasScrollbars().x) {
133 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
134 // Since we do the movement of the page.
135 e.preventDefault();
137 return;
138 case 34: // Page down key.
139 // Go to the next page if we are fit-to-page.
140 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
141 this.viewport_.goToPage(this.viewport_.getMostVisiblePage() + 1);
142 // Since we do the movement of the page.
143 e.preventDefault();
145 return;
146 case 187: // +/= key.
147 case 107: // Numpad + key.
148 if (e.ctrlKey || e.metaKey) {
149 this.viewport_.zoomIn();
150 // Since we do the zooming of the page.
151 e.preventDefault();
153 return;
154 case 189: // -/_ key.
155 case 109: // Numpad - key.
156 if (e.ctrlKey || e.metaKey) {
157 this.viewport_.zoomOut();
158 // Since we do the zooming of the page.
159 e.preventDefault();
161 return;
162 case 83: // s key.
163 if (e.ctrlKey || e.metaKey) {
164 // Simulate a click on the button so that the <a download ...>
165 // attribute is used.
166 $('save-button-link').click();
167 // Since we do the saving of the page.
168 e.preventDefault();
170 return;
171 case 80: // p key.
172 if (e.ctrlKey || e.metaKey) {
173 this.print_();
174 // Since we do the printing of the page.
175 e.preventDefault();
177 return;
179 }.bind(this);
183 * @private
184 * Notify the plugin to print.
186 print_: function() {
187 this.plugin_.postMessage({
188 type: 'print',
193 * @private
194 * Update the loading progress of the document in response to a progress
195 * message being received from the plugin.
196 * @param {number} progress the progress as a percentage.
198 updateProgress_: function(progress) {
199 this.progressBar_.progress = progress;
200 if (progress == -1) {
201 // Document load failed.
202 this.errorScreen_.style.visibility = 'visible';
203 this.sizer_.style.display = 'none';
204 this.toolbar_.style.visibility = 'hidden';
205 if (this.passwordScreen_.active) {
206 this.passwordScreen_.deny();
207 this.passwordScreen_.active = false;
209 } else if (progress == 100) {
210 // Document load complete.
211 var loadEvent = new Event('pdfload');
212 window.dispatchEvent(loadEvent);
213 // TODO(raymes): Replace this and other callbacks with events.
214 this.messagingHost_.documentLoaded();
215 if (this.lastViewportPosition_)
216 this.viewport_.position = this.lastViewportPosition_;
221 * @private
222 * An event handler for handling password-submitted events. These are fired
223 * when an event is entered into the password screen.
224 * @param {Object} event a password-submitted event.
226 onPasswordSubmitted_: function(event) {
227 this.plugin_.postMessage({
228 type: 'getPasswordComplete',
229 password: event.detail.password
234 * @private
235 * An event handler for handling message events received from the plugin.
236 * @param {MessageObject} message a message event.
238 handleMessage_: function(message) {
239 switch (message.data.type.toString()) {
240 case 'documentDimensions':
241 this.documentDimensions_ = message.data;
242 this.viewport_.setDocumentDimensions(this.documentDimensions_);
243 this.toolbar_.style.visibility = 'visible';
244 // If we received the document dimensions, the password was good so we
245 // can dismiss the password screen.
246 if (this.passwordScreen_.active)
247 this.passwordScreen_.accept();
249 this.pageIndicator_.initialFadeIn();
250 this.toolbar_.initialFadeIn();
251 break;
252 case 'loadProgress':
253 this.updateProgress_(message.data.progress);
254 break;
255 case 'goToPage':
256 this.viewport_.goToPage(message.data.page);
257 break;
258 case 'setScrollPosition':
259 var position = this.viewport_.position;
260 if (message.data.x != undefined)
261 position.x = message.data.x;
262 if (message.data.y != undefined)
263 position.y = message.data.y;
264 this.viewport_.position = position;
265 break;
266 case 'getPassword':
267 // If the password screen isn't up, put it up. Otherwise we're
268 // responding to an incorrect password so deny it.
269 if (!this.passwordScreen_.active)
270 this.passwordScreen_.active = true;
271 else
272 this.passwordScreen_.deny();
273 break;
274 case 'setTranslatedStrings':
275 this.passwordScreen_.text = message.data.getPasswordString;
276 this.progressBar_.text = message.data.loadingString;
277 this.errorScreen_.text = message.data.loadFailedString;
278 break;
283 * @private
284 * A callback that's called when the viewport changes.
286 viewportChangedCallback_: function() {
287 if (!this.documentDimensions_)
288 return;
290 // Update the buttons selected.
291 $('fit-to-page-button').classList.remove('polymer-selected');
292 $('fit-to-width-button').classList.remove('polymer-selected');
293 if (this.viewport_.fittingType == Viewport.FittingType.FIT_TO_PAGE) {
294 $('fit-to-page-button').classList.add('polymer-selected');
295 } else if (this.viewport_.fittingType ==
296 Viewport.FittingType.FIT_TO_WIDTH) {
297 $('fit-to-width-button').classList.add('polymer-selected');
300 var hasScrollbars = this.viewport_.documentHasScrollbars();
301 var scrollbarWidth = this.viewport_.scrollbarWidth;
302 // Offset the toolbar position so that it doesn't move if scrollbars appear.
303 var toolbarRight = hasScrollbars.vertical ? 0 : scrollbarWidth;
304 var toolbarBottom = hasScrollbars.horizontal ? 0 : scrollbarWidth;
305 this.toolbar_.style.right = toolbarRight + 'px';
306 this.toolbar_.style.bottom = toolbarBottom + 'px';
308 // Update the page indicator.
309 this.pageIndicator_.index = this.viewport_.getMostVisiblePage();
310 if (this.documentDimensions_.pageDimensions.length > 1 &&
311 hasScrollbars.vertical) {
312 this.pageIndicator_.style.visibility = 'visible';
313 } else {
314 this.pageIndicator_.style.visibility = 'hidden';
317 this.messagingHost_.viewportChanged();
319 var position = this.viewport_.position;
320 var zoom = this.viewport_.zoom;
321 // Notify the plugin of the viewport change.
322 this.plugin_.postMessage({
323 type: 'viewport',
324 zoom: zoom,
325 xOffset: position.x,
326 yOffset: position.y
331 * Resets the viewer into print preview mode, which is used for Chrome print
332 * preview.
333 * @param {string} url the url of the pdf to load.
334 * @param {boolean} grayscale true if the pdf should be displayed in
335 * grayscale, false otherwise.
336 * @param {Array.<number>} pageNumbers an array of the number to label each
337 * page in the document.
338 * @param {boolean} modifiable whether the PDF is modifiable or not.
340 resetPrintPreviewMode: function(url,
341 grayscale,
342 pageNumbers,
343 modifiable) {
344 if (!this.inPrintPreviewMode_) {
345 this.inPrintPreviewMode_ = true;
346 this.viewport_.fitToPage();
349 // Stash the scroll location so that it can be restored when the new
350 // document is loaded.
351 this.lastViewportPosition_ = this.viewport_.position;
353 // TODO(raymes): Disable these properly in the plugin.
354 var printButton = $('print-button');
355 if (printButton)
356 printButton.parentNode.removeChild(printButton);
357 var saveButton = $('save-button');
358 if (saveButton)
359 saveButton.parentNode.removeChild(saveButton);
361 this.pageIndicator_.pageLabels = pageNumbers;
363 this.plugin_.postMessage({
364 type: 'resetPrintPreviewMode',
365 url: url,
366 grayscale: grayscale,
367 // If the PDF isn't modifiable we send 0 as the page count so that no
368 // blank placeholder pages get appended to the PDF.
369 pageCount: (modifiable ? pageNumbers.length : 0)
374 * Load a page into the document while in print preview mode.
375 * @param {string} url the url of the pdf page to load.
376 * @param {number} index the index of the page to load.
378 loadPreviewPage: function(url, index) {
379 this.plugin_.postMessage({
380 type: 'loadPreviewPage',
381 url: url,
382 index: index
387 * @type {Viewport} the viewport of the PDF viewer.
389 get viewport() {
390 return this.viewport_;
394 var viewer = new PDFViewer();