Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1050)

Side by Side Diff: chrome/browser/resources/pdf/viewport.js

Issue 137663008: Implement viewporting for the out of process PDF plugin. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 /**
6 * Predefined zoom factors to be used when zooming in/out.
7 */
8 ZOOM_FACTORS = [0.25, 0.333, 0.5, 0.666, 0.75, 0.9, 1.0,
koz (OOO until 15th September) 2014/02/03 03:40:43 add a 'var' here?
raymes 2014/02/05 06:05:17 Done.
9 1.1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, 5.0];
10
11 /**
12 * Returns the area of the intersection of two rectangles.
13 * @param {dictionary} rect1 the first rect
14 * @param {dictionary} rect2 the second rect
15 * @return {number} the area of the intersection of the rects
16 */
17 function getIntersectionArea(rect1, rect2) {
18 var xOverlap = Math.max(0,
19 Math.min(rect1.x + rect1.width, rect2.x + rect2.width) -
20 Math.max(rect1.x, rect2.x));
21 var yOverlap = Math.max(0,
22 Math.min(rect1.y + rect1.height, rect2.y + rect2.height) -
23 Math.max(rect1.y, rect2.y));
24 return xOverlap * yOverlap;
25 }
26
27 /**
28 * Return the width of a scrollbar in pixels.
29 * @return {number} width of a scrollbar
30 */
31 function getScrollbarWidth() {
32 var parentDiv = document.createElement('div');
33 parentDiv.style.visibility = 'hidden';
34 parentDiv.style.width = '500px';
35 document.body.appendChild(parentDiv);
36 var parentDivWidth = parentDiv.offsetWidth;
37 parentDiv.style.overflow = 'scroll';
38 var childDiv = document.createElement('div');
39 childDiv.style.width = '100%';
40 parentDiv.appendChild(childDiv);
41 var childDivWidth = childDiv.offsetWidth;
42 parentDiv.parentNode.removeChild(parentDiv);
43 return parentDivWidth - childDivWidth;
44 }
45
46 /**
47 * Create a new viewport.
48 * @param {object} window is the page window
49 * @param {object} sizer is the element which represents the size of the
50 * document in the viewport
51 * @param {function} fitToPageEnabledFunction returns true if fit-to-page is
52 * enabled
53 * @param {function} viewportChangedCallback is run when the viewport changes
54 */
55 Viewport = function(window,
56 sizer,
57 fitToPageEnabledFunction,
58 viewportChangedCallback) {
59 this.window_ = window;
60 this.sizer_ = sizer;
61 this.fitToPageEnabledFunction_ = fitToPageEnabledFunction;
62 this.viewportChangedCallback_ = viewportChangedCallback;
63 this.zoom_ = 1;
64 this.documentDimensions_ = {};
65 this.pageDimensions_ = [];
66 this.scrollbarWidth_ = getScrollbarWidth();
67
68 var scrollCallback = function() {
69 this.updateViewport_();
70 }.bind(this);
71 window.addEventListener('scroll', scrollCallback);
koz (OOO until 15th September) 2014/02/03 03:40:43 This could be written as window.addEventListener(
raymes 2014/02/05 06:05:17 Done.
72 };
73
74 /**
75 * @private
76 * Returns true if the document needs scrollbars at the given zoom level.
77 * @param {number} zoom compute whether scrollbars are needed at this zoom
78 * @return {dictionary} with 'x' and 'y' keys which map to bool values
79 * indicating if the horizontal and vertical scrollbars are needed respectively.
80 */
81 Viewport.prototype.documentNeedsScrollbars_ = function(zoom) {
82 return {
83 x: this.documentDimensions_.width * zoom > this.window_.innerWidth,
84 y: this.documentDimensions_.height * zoom > this.window_.innerHeight
85 };
86 };
87
88 /**
89 * Returns true if the document needs scrollbars at the current zoom level.
90 * @return {dictionary} with 'x' and 'y' keys which map to bool values
91 * indicating if the horizontal and vertical scrollbars are needed respectively.
92 */
93 Viewport.prototype.documentHasScrollbars = function() {
94 return this.documentNeedsScrollbars_(this.zoom_);
95 };
96
97 /**
98 * @private
99 * Helper function called when the zoomed document size changes.
100 */
101 Viewport.prototype.contentSizeChanged_ = function() {
102 this.sizer_.style.width = this.documentDimensions_.width * this.zoom_ + 'px';
103 this.sizer_.style.height =
104 this.documentDimensions_.height * this.zoom_ + 'px';
105 };
106
107 /**
108 * Sets the zoom of the viewport.
109 * @param {number} newZoom the zoom level to zoom to
110 */
111 Viewport.prototype.setZoom = function(newZoom) {
112 var oldZoom = this.zoom_;
113 this.zoom_ = newZoom;
114 // Record the scroll position (relative to the middle of the window).
115 var currentScrollPos =
116 [(this.window_.scrollX + this.window_.innerWidth / 2.0) / oldZoom,
117 (this.window_.scrollY + this.window_.innerHeight / 2.0) / oldZoom];
118 this.contentSizeChanged_();
119 // Scroll to the scaled scroll position.
120 this.window_.scrollTo(
121 (currentScrollPos[0] * newZoom) - this.window_.innerWidth / 2.0,
122 (currentScrollPos[1] * newZoom) - this.window_.innerHeight / 2.0);
123 };
124
125 /**
126 * @private
127 * Called when the viewport should be updated.
128 */
129 Viewport.prototype.updateViewport_ = function() {
130 // Shift the toolbar so that it doesn't move when the scrollbars display
131 var needsScrollbars = this.documentHasScrollbars();
132 var scrollbarWidth = this.scrollbarWidth_;
koz (OOO until 15th September) 2014/02/03 03:40:43 nit: this can be inlined
raymes 2014/02/05 06:05:17 Done.
133 this.viewportChangedCallback_(this.zoom_,
134 this.window_.pageXOffset,
135 this.window_.pageYOffset,
136 scrollbarWidth,
137 needsScrollbars);
138 };
139
140 /**
141 * @private
142 * Returns a rect representing the current viewport.
143 * @return {dictionary} a rect representing the current viewport.
144 */
145 Viewport.prototype.getCurrentViewportRect_ = function() {
146 return {
147 'x': this.window_.pageXOffset / this.zoom_,
148 'y': this.window_.pageYOffset / this.zoom_,
149 'width': this.window_.innerWidth / this.zoom_,
150 'height': this.window_.innerHeight / this.zoom_,
151 };
152 };
153
154 /**
155 * Returns the most visible page on the screen.
koz (OOO until 15th September) 2014/02/03 03:40:43 Maybe rephase as "Returns the page with the most p
raymes 2014/02/05 06:05:17 Done.
156 * @return {int} the index of the most visible page.
157 */
158 Viewport.prototype.getMostVisiblePage = function() {
159 // TODO(raymes): Do a binary search here.
160 var mostVisiblePage = {'number': 0, 'area': 0};
161 for (var i = 0; i < this.pageDimensions_.length; i++) {
162 var area = getIntersectionArea(this.pageDimensions_[i],
163 this.getCurrentViewportRect_());
164 if (area > mostVisiblePage.area) {
165 mostVisiblePage.area = area;
166 mostVisiblePage.number = i;
167 }
168 }
169 return mostVisiblePage.number;
170 };
171
172 /**
173 * @private
174 * Compute the zoom level for fit-to-page or fit-to-width. |pageDimensions| is
175 * the dimensions for a given page and if |widthOnly| is true, it indicates that
176 * fit-to-page zoom should be computed rather than fit-to-page.
177 * @param {dictionary} pageDimensions the dimensions of a given page
178 * @param {boolean} widthOnly a bool indicating whether fit-to-page or
179 * fit-to-width should be computed.
180 * @return {number} the zoom to use
181 */
182 Viewport.prototype.computeFittingZoom_ = function(pageDimensions, widthOnly) {
183 // First compute the zoom without scrollbars.
184 var zoomWidth = this.window_.innerWidth / pageDimensions.width;
185 var zoom;
186 if (widthOnly) {
187 zoom = zoomWidth;
188 } else {
189 var zoomHeight = this.window_.innerHeight / pageDimensions.height;
190 zoom = Math.min(zoomWidth, zoomHeight);
191 }
192 // Check if there needs to be any scrollbars.
193 var needsScrollbars = this.documentNeedsScrollbars_(zoom);
194
195 // If the document fits, just return the zoom.
196 if (!needsScrollbars.x && !needsScrollbars.y)
197 return zoom;
198
199 var zoomedDimensions = {
200 width: this.documentDimensions_.width * zoom,
201 height: this.documentDimensions_.height * zoom
202 };
203
204 // Check if adding a scrollbar will result in needing the other scrollbar.
205 var scrollbarWidth = getScrollbarWidth();
206 if (needsScrollbars.x &&
207 zoomedDimensions.height > this.window_.innerHeight - scrollbarWidth) {
208 needsScrollbars.y = true;
209 }
210 if (needsScrollbars.y &&
211 zoomedDimensions.width > this.window_.innerWidth - scrollbarWidth) {
212 needsScrollbars.x = true;
213 }
214
215 // Compute available window space.
216 var windowWithScrollbars = {
217 width: this.window_.innerWidth,
218 height: this.window_.innerHeight
219 };
220 if (needsScrollbars.x)
221 windowWithScrollbars.height -= scrollbarWidth;
222 if (needsScrollbars.y)
223 windowWithScrollbars.width -= scrollbarWidth;
224
225 // Recompute the zoom.
226 zoomWidth = windowWithScrollbars.width / pageDimensions.width;
227 if (widthOnly) {
228 zoom = zoomWidth;
229 } else {
230 var zoomHeight = windowWithScrollbars.height / pageDimensions.height;
231 zoom = Math.min(zoomWidth, zoomHeight);
232 }
233 return zoom;
234 };
235
236 /**
237 * Zoom the viewport so that the page-width consumes the entire viewport.
238 */
239 Viewport.prototype.fitToWidth = function() {
240 var page = this.getMostVisiblePage();
241 this.setZoom(this.computeFittingZoom_(this.pageDimensions_[page], true));
242 this.updateViewport_();
243 };
244
245 /**
246 * Zoom the viewport so that a page consumes the entire viewport. Also scrolls
247 * to the top of the most visible page.
248 */
249 Viewport.prototype.fitToPage = function() {
250 var page = this.getMostVisiblePage();
251 this.setZoom(this.computeFittingZoom_(this.pageDimensions_[page], false));
252 this.window_.scrollTo(this.pageDimensions_[page].x * this.zoom_,
253 this.pageDimensions_[page].y * this.zoom_);
254 this.updateViewport_();
255 };
256
257 /**
258 * Zoom in to the next predefined zoom level.
259 */
260 Viewport.prototype.zoomIn = function() {
261 var nextZoom = ZOOM_FACTORS[0];
262 for (var i = 0; i < ZOOM_FACTORS.length; i++) {
263 if (ZOOM_FACTORS[i] < this.zoom_)
koz (OOO until 15th September) 2014/02/03 03:40:43 Won't this get the next smallest zoom factor, whic
raymes 2014/02/05 06:05:17 You're right, good catch! - the ids were backward
264 nextZoom = ZOOM_FACTORS[i];
265 }
266 this.setZoom(nextZoom);
267 this.updateViewport_();
268 };
269
270 /**
271 * Zoom out to the next predefined zoom level.
272 */
273 Viewport.prototype.zoomOut = function() {
274 var nextZoom = ZOOM_FACTORS[ZOOM_FACTORS.length - 1];
275 for (var i = ZOOM_FACTORS.length - 1; i >= 0; i--) {
276 if (ZOOM_FACTORS[i] > this.zoom_)
277 nextZoom = ZOOM_FACTORS[i];
278 }
279 this.setZoom(nextZoom);
280 this.updateViewport_();
281 };
282
283 /**
284 * Go to the given page index.
285 * @param {number} page the index of the page to go to
286 */
287 Viewport.prototype.goToPage = function(page) {
288 if (page < 0)
289 page = 0;
290 var dimensions = this.pageDimensions_[page];
291 this.window_.scrollTo(dimensions.x * this.zoom_, dimensions.y * this.zoom_);
292 };
293
294 /**
295 * Set the dimensions of the document.
296 * @param {dictionary} documentDimensions the dimensions of the document
297 */
298 Viewport.prototype.setDocumentDimensions = function(documentDimensions) {
299 this.documentDimensions_ = documentDimensions;
300 this.pageDimensions_ = this.documentDimensions_.pageDimensions;
301 this.contentSizeChanged_();
302 this.updateViewport_();
303 };
OLDNEW
« chrome/browser/resources/pdf/pdf.js ('K') | « chrome/browser/resources/pdf/pdf.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698