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

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. These are in
7 * ascending order.
8 */
9 var ZOOM_FACTORS = [0.25, 0.333, 0.5, 0.666, 0.75, 0.9, 1.0,
10 1.1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, 5.0];
arv (Not doing code reviews) 2014/02/18 17:37:29 x.0 -> x JS only has one number type
raymes 2014/02/18 23:11:45 Done.
11
12 /**
13 * Returns the area of the intersection of two rectangles.
14 * @param {Object} rect1 the first rect
15 * @param {Object} rect2 the second rect
16 * @return {number} the area of the intersection of the rects
17 */
18 function getIntersectionArea(rect1, rect2) {
19 var xOverlap = Math.max(0,
20 Math.min(rect1.x + rect1.width, rect2.x + rect2.width) -
21 Math.max(rect1.x, rect2.x));
22 var yOverlap = Math.max(0,
23 Math.min(rect1.y + rect1.height, rect2.y + rect2.height) -
24 Math.max(rect1.y, rect2.y));
25 return xOverlap * yOverlap;
26 }
27
28 /**
29 * @return {number} width of a scrollbar in pixels
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;
arv (Not doing code reviews) 2014/02/18 17:37:29 Isn't this always 500? It seems like you could sk
raymes 2014/02/18 23:11:45 You're right, done!
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 {Window} window the 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
arv (Not doing code reviews) 2014/02/18 17:37:29 JSDoc should be indented too
raymes 2014/02/18 23:11:45 Done.
53 * @param {Function} viewportChangedCallback is run when the viewport changes
54 */
55 Viewport = function(window,
arv (Not doing code reviews) 2014/02/18 17:37:29 Prefer FunctionDeclaration over FunctionExpression
raymes 2014/02/18 23:11:45 Done.
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 window.addEventListener('scroll', this.updateViewport_.bind(this));
69 };
70
71 Viewport.prototype = {
72 /**
73 * @private
74 * Returns true if the document needs scrollbars at the given zoom level.
75 * @param {number} zoom compute whether scrollbars are needed at this zoom
76 * @return {Object} with 'x' and 'y' keys which map to bool values
77 * indicating if the horizontal and vertical scrollbars are needed
78 * respectively.
79 */
80 documentNeedsScrollbars_: function(zoom) {
81 return {
82 x: this.documentDimensions_.width * zoom > this.window_.innerWidth,
83 y: this.documentDimensions_.height * zoom > this.window_.innerHeight
84 };
85 },
86
87 /**
88 * Returns true if the document needs scrollbars at the current zoom level.
89 * @return {Object} with 'x' and 'y' keys which map to bool values
90 * indicating if the horizontal and vertical scrollbars are needed
91 * respectively.
92 */
93 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 contentSizeChanged_: function() {
102 this.sizer_.style.width =
103 this.documentDimensions_.width * this.zoom_ + 'px';
104 this.sizer_.style.height =
105 this.documentDimensions_.height * this.zoom_ + 'px';
106 },
107
108 /**
109 * Sets the zoom of the viewport.
110 * @param {number} newZoom the zoom level to zoom to
111 */
112 setZoom: function(newZoom) {
113 var oldZoom = this.zoom_;
114 this.zoom_ = newZoom;
115 // Record the scroll position (relative to the middle of the window).
116 var currentScrollPos = [
117 (this.window_.scrollX + this.window_.innerWidth / 2) / oldZoom,
118 (this.window_.scrollY + this.window_.innerHeight / 2) / oldZoom
119 ];
120 this.contentSizeChanged_();
121 // Scroll to the scaled scroll position.
122 this.window_.scrollTo(
123 currentScrollPos[0] * newZoom - this.window_.innerWidth / 2,
124 currentScrollPos[1] * newZoom - this.window_.innerHeight / 2);
125 },
126
127 /**
128 * @private
129 * Called when the viewport should be updated.
130 */
131 updateViewport_: function() {
132 // Shift the toolbar so that it doesn't move when the scrollbars display
133 var needsScrollbars = this.documentHasScrollbars();
134 this.viewportChangedCallback_(this.zoom_,
135 this.window_.pageXOffset,
136 this.window_.pageYOffset,
137 this.scrollbarWidth_,
138 needsScrollbars);
139 },
140
141 /**
142 * @private
143 * Returns a rect representing the current viewport.
144 * @return {Object} a rect representing the current viewport.
145 */
146 getCurrentViewportRect_: function() {
147 return {
148 x: this.window_.pageXOffset / this.zoom_,
149 y: this.window_.pageYOffset / this.zoom_,
150 width: this.window_.innerWidth / this.zoom_,
151 height: this.window_.innerHeight / this.zoom_,
152 };
153 },
154
155 /**
156 * Returns the page with the most pixels in the current viewport.
157 * @return {int} the index of the most visible page.
158 */
159 getMostVisiblePage: function() {
160 // TODO(raymes): Do a binary search here.
161 var mostVisiblePage = {'number': 0, 'area': 0};
162 for (var i = 0; i < this.pageDimensions_.length; i++) {
163 var area = getIntersectionArea(this.pageDimensions_[i],
164 this.getCurrentViewportRect_());
165 if (area > mostVisiblePage.area) {
166 mostVisiblePage.area = area;
167 mostVisiblePage.number = i;
168 }
169 }
170 return mostVisiblePage.number;
171 },
172
173 /**
174 * @private
175 * Compute the zoom level for fit-to-page or fit-to-width. |pageDimensions| is
176 * the dimensions for a given page and if |widthOnly| is true, it indicates
177 * that fit-to-page zoom should be computed rather than fit-to-page.
178 * @param {Object} pageDimensions the dimensions of a given page
179 * @param {boolean} widthOnly a bool indicating whether fit-to-page or
180 * fit-to-width should be computed.
181 * @return {number} the zoom to use
182 */
183 computeFittingZoom_: function(pageDimensions, widthOnly) {
184 // First compute the zoom without scrollbars.
185 var zoomWidth = this.window_.innerWidth / pageDimensions.width;
186 var zoom;
187 if (widthOnly) {
188 zoom = zoomWidth;
189 } else {
190 var zoomHeight = this.window_.innerHeight / pageDimensions.height;
191 zoom = Math.min(zoomWidth, zoomHeight);
192 }
193 // Check if there needs to be any scrollbars.
194 var needsScrollbars = this.documentNeedsScrollbars_(zoom);
195
196 // If the document fits, just return the zoom.
197 if (!needsScrollbars.x && !needsScrollbars.y)
198 return zoom;
199
200 var zoomedDimensions = {
201 width: this.documentDimensions_.width * zoom,
202 height: this.documentDimensions_.height * zoom
203 };
204
205 // Check if adding a scrollbar will result in needing the other scrollbar.
206 var scrollbarWidth = this.scrollbarWidth_;
207 if (needsScrollbars.x &&
208 zoomedDimensions.height > this.window_.innerHeight - scrollbarWidth) {
209 needsScrollbars.y = true;
210 }
211 if (needsScrollbars.y &&
212 zoomedDimensions.width > this.window_.innerWidth - scrollbarWidth) {
213 needsScrollbars.x = true;
214 }
215
216 // Compute available window space.
217 var windowWithScrollbars = {
218 width: this.window_.innerWidth,
219 height: this.window_.innerHeight
220 };
221 if (needsScrollbars.x)
222 windowWithScrollbars.height -= scrollbarWidth;
223 if (needsScrollbars.y)
224 windowWithScrollbars.width -= scrollbarWidth;
225
226 // Recompute the zoom.
227 zoomWidth = windowWithScrollbars.width / pageDimensions.width;
228 if (widthOnly) {
229 zoom = zoomWidth;
230 } else {
231 var zoomHeight = windowWithScrollbars.height / pageDimensions.height;
232 zoom = Math.min(zoomWidth, zoomHeight);
233 }
234 return zoom;
235 },
236
237 /**
238 * Zoom the viewport so that the page-width consumes the entire viewport.
239 */
240 fitToWidth: function() {
241 var page = this.getMostVisiblePage();
242 this.setZoom(this.computeFittingZoom_(this.pageDimensions_[page], true));
243 this.window_.scrollTo(this.pageDimensions_[page].x * this.zoom_,
244 this.window_.scrollY);
245 this.updateViewport_();
246 },
247
248 /**
249 * Zoom the viewport so that a page consumes the entire viewport. Also scrolls
250 * to the top of the most visible page.
251 */
252 fitToPage: function() {
253 var page = this.getMostVisiblePage();
254 this.setZoom(this.computeFittingZoom_(this.pageDimensions_[page], false));
255 this.window_.scrollTo(this.pageDimensions_[page].x * this.zoom_,
256 this.pageDimensions_[page].y * this.zoom_);
257 this.updateViewport_();
258 },
259
260 /**
261 * Zoom out to the next predefined zoom level.
262 */
263 zoomOut: function() {
264 var nextZoom = ZOOM_FACTORS[0];
265 for (var i = 0; i < ZOOM_FACTORS.length; i++) {
266 if (ZOOM_FACTORS[i] < this.zoom_)
267 nextZoom = ZOOM_FACTORS[i];
268 }
269 this.setZoom(nextZoom);
270 this.updateViewport_();
271 },
272
273 /**
274 * Zoom in to the next predefined zoom level.
275 */
276 zoomIn: function() {
277 var nextZoom = ZOOM_FACTORS[ZOOM_FACTORS.length - 1];
278 for (var i = ZOOM_FACTORS.length - 1; i >= 0; i--) {
279 if (ZOOM_FACTORS[i] > this.zoom_)
280 nextZoom = ZOOM_FACTORS[i];
281 }
282 this.setZoom(nextZoom);
283 this.updateViewport_();
284 },
285
286 /**
287 * Go to the given page index.
288 * @param {number} page the index of the page to go to
289 */
290 goToPage: function(page) {
291 if (page < 0)
292 page = 0;
293 var dimensions = this.pageDimensions_[page];
294 this.window_.scrollTo(dimensions.x * this.zoom_, dimensions.y * this.zoom_);
295 },
296
297 /**
298 * Set the dimensions of the document.
299 * @param {Object} documentDimensions the dimensions of the document
300 */
301 setDocumentDimensions: function(documentDimensions) {
302 this.documentDimensions_ = documentDimensions;
303 this.pageDimensions_ = this.documentDimensions_.pageDimensions;
304 this.contentSizeChanged_();
305 this.updateViewport_();
306 }
307 };
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