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

Unified 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, 11 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 side-by-side diff with in-line comments
Download patch
« chrome/browser/resources/pdf/pdf.js ('K') | « chrome/browser/resources/pdf/pdf.js ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/pdf/viewport.js
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
new file mode 100644
index 0000000000000000000000000000000000000000..c4027befa6ef2eef70f6d00adfec5c5cbedef325
--- /dev/null
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -0,0 +1,303 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Predefined zoom factors to be used when zooming in/out.
+ */
+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.
+ 1.1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, 5.0];
+
+/**
+ * Returns the area of the intersection of two rectangles.
+ * @param {dictionary} rect1 the first rect
+ * @param {dictionary} rect2 the second rect
+ * @return {number} the area of the intersection of the rects
+ */
+function getIntersectionArea(rect1, rect2) {
+ var xOverlap = Math.max(0,
+ Math.min(rect1.x + rect1.width, rect2.x + rect2.width) -
+ Math.max(rect1.x, rect2.x));
+ var yOverlap = Math.max(0,
+ Math.min(rect1.y + rect1.height, rect2.y + rect2.height) -
+ Math.max(rect1.y, rect2.y));
+ return xOverlap * yOverlap;
+}
+
+/**
+ * Return the width of a scrollbar in pixels.
+ * @return {number} width of a scrollbar
+ */
+function getScrollbarWidth() {
+ var parentDiv = document.createElement('div');
+ parentDiv.style.visibility = 'hidden';
+ parentDiv.style.width = '500px';
+ document.body.appendChild(parentDiv);
+ var parentDivWidth = parentDiv.offsetWidth;
+ parentDiv.style.overflow = 'scroll';
+ var childDiv = document.createElement('div');
+ childDiv.style.width = '100%';
+ parentDiv.appendChild(childDiv);
+ var childDivWidth = childDiv.offsetWidth;
+ parentDiv.parentNode.removeChild(parentDiv);
+ return parentDivWidth - childDivWidth;
+}
+
+/**
+ * Create a new viewport.
+ * @param {object} window is the page window
+ * @param {object} sizer is the element which represents the size of the
+ * document in the viewport
+ * @param {function} fitToPageEnabledFunction returns true if fit-to-page is
+ * enabled
+ * @param {function} viewportChangedCallback is run when the viewport changes
+ */
+Viewport = function(window,
+ sizer,
+ fitToPageEnabledFunction,
+ viewportChangedCallback) {
+ this.window_ = window;
+ this.sizer_ = sizer;
+ this.fitToPageEnabledFunction_ = fitToPageEnabledFunction;
+ this.viewportChangedCallback_ = viewportChangedCallback;
+ this.zoom_ = 1;
+ this.documentDimensions_ = {};
+ this.pageDimensions_ = [];
+ this.scrollbarWidth_ = getScrollbarWidth();
+
+ var scrollCallback = function() {
+ this.updateViewport_();
+ }.bind(this);
+ 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.
+};
+
+/**
+ * @private
+ * Returns true if the document needs scrollbars at the given zoom level.
+ * @param {number} zoom compute whether scrollbars are needed at this zoom
+ * @return {dictionary} with 'x' and 'y' keys which map to bool values
+ * indicating if the horizontal and vertical scrollbars are needed respectively.
+ */
+Viewport.prototype.documentNeedsScrollbars_ = function(zoom) {
+ return {
+ x: this.documentDimensions_.width * zoom > this.window_.innerWidth,
+ y: this.documentDimensions_.height * zoom > this.window_.innerHeight
+ };
+};
+
+/**
+ * Returns true if the document needs scrollbars at the current zoom level.
+ * @return {dictionary} with 'x' and 'y' keys which map to bool values
+ * indicating if the horizontal and vertical scrollbars are needed respectively.
+ */
+Viewport.prototype.documentHasScrollbars = function() {
+ return this.documentNeedsScrollbars_(this.zoom_);
+};
+
+/**
+ * @private
+ * Helper function called when the zoomed document size changes.
+ */
+Viewport.prototype.contentSizeChanged_ = function() {
+ this.sizer_.style.width = this.documentDimensions_.width * this.zoom_ + 'px';
+ this.sizer_.style.height =
+ this.documentDimensions_.height * this.zoom_ + 'px';
+};
+
+/**
+ * Sets the zoom of the viewport.
+ * @param {number} newZoom the zoom level to zoom to
+ */
+Viewport.prototype.setZoom = function(newZoom) {
+ var oldZoom = this.zoom_;
+ this.zoom_ = newZoom;
+ // Record the scroll position (relative to the middle of the window).
+ var currentScrollPos =
+ [(this.window_.scrollX + this.window_.innerWidth / 2.0) / oldZoom,
+ (this.window_.scrollY + this.window_.innerHeight / 2.0) / oldZoom];
+ this.contentSizeChanged_();
+ // Scroll to the scaled scroll position.
+ this.window_.scrollTo(
+ (currentScrollPos[0] * newZoom) - this.window_.innerWidth / 2.0,
+ (currentScrollPos[1] * newZoom) - this.window_.innerHeight / 2.0);
+};
+
+/**
+ * @private
+ * Called when the viewport should be updated.
+ */
+Viewport.prototype.updateViewport_ = function() {
+ // Shift the toolbar so that it doesn't move when the scrollbars display
+ var needsScrollbars = this.documentHasScrollbars();
+ 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.
+ this.viewportChangedCallback_(this.zoom_,
+ this.window_.pageXOffset,
+ this.window_.pageYOffset,
+ scrollbarWidth,
+ needsScrollbars);
+};
+
+/**
+ * @private
+ * Returns a rect representing the current viewport.
+ * @return {dictionary} a rect representing the current viewport.
+ */
+Viewport.prototype.getCurrentViewportRect_ = function() {
+ return {
+ 'x': this.window_.pageXOffset / this.zoom_,
+ 'y': this.window_.pageYOffset / this.zoom_,
+ 'width': this.window_.innerWidth / this.zoom_,
+ 'height': this.window_.innerHeight / this.zoom_,
+ };
+};
+
+/**
+ * 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.
+ * @return {int} the index of the most visible page.
+ */
+Viewport.prototype.getMostVisiblePage = function() {
+ // TODO(raymes): Do a binary search here.
+ var mostVisiblePage = {'number': 0, 'area': 0};
+ for (var i = 0; i < this.pageDimensions_.length; i++) {
+ var area = getIntersectionArea(this.pageDimensions_[i],
+ this.getCurrentViewportRect_());
+ if (area > mostVisiblePage.area) {
+ mostVisiblePage.area = area;
+ mostVisiblePage.number = i;
+ }
+ }
+ return mostVisiblePage.number;
+};
+
+/**
+ * @private
+ * Compute the zoom level for fit-to-page or fit-to-width. |pageDimensions| is
+ * the dimensions for a given page and if |widthOnly| is true, it indicates that
+ * fit-to-page zoom should be computed rather than fit-to-page.
+ * @param {dictionary} pageDimensions the dimensions of a given page
+ * @param {boolean} widthOnly a bool indicating whether fit-to-page or
+ * fit-to-width should be computed.
+ * @return {number} the zoom to use
+ */
+Viewport.prototype.computeFittingZoom_ = function(pageDimensions, widthOnly) {
+ // First compute the zoom without scrollbars.
+ var zoomWidth = this.window_.innerWidth / pageDimensions.width;
+ var zoom;
+ if (widthOnly) {
+ zoom = zoomWidth;
+ } else {
+ var zoomHeight = this.window_.innerHeight / pageDimensions.height;
+ zoom = Math.min(zoomWidth, zoomHeight);
+ }
+ // Check if there needs to be any scrollbars.
+ var needsScrollbars = this.documentNeedsScrollbars_(zoom);
+
+ // If the document fits, just return the zoom.
+ if (!needsScrollbars.x && !needsScrollbars.y)
+ return zoom;
+
+ var zoomedDimensions = {
+ width: this.documentDimensions_.width * zoom,
+ height: this.documentDimensions_.height * zoom
+ };
+
+ // Check if adding a scrollbar will result in needing the other scrollbar.
+ var scrollbarWidth = getScrollbarWidth();
+ if (needsScrollbars.x &&
+ zoomedDimensions.height > this.window_.innerHeight - scrollbarWidth) {
+ needsScrollbars.y = true;
+ }
+ if (needsScrollbars.y &&
+ zoomedDimensions.width > this.window_.innerWidth - scrollbarWidth) {
+ needsScrollbars.x = true;
+ }
+
+ // Compute available window space.
+ var windowWithScrollbars = {
+ width: this.window_.innerWidth,
+ height: this.window_.innerHeight
+ };
+ if (needsScrollbars.x)
+ windowWithScrollbars.height -= scrollbarWidth;
+ if (needsScrollbars.y)
+ windowWithScrollbars.width -= scrollbarWidth;
+
+ // Recompute the zoom.
+ zoomWidth = windowWithScrollbars.width / pageDimensions.width;
+ if (widthOnly) {
+ zoom = zoomWidth;
+ } else {
+ var zoomHeight = windowWithScrollbars.height / pageDimensions.height;
+ zoom = Math.min(zoomWidth, zoomHeight);
+ }
+ return zoom;
+};
+
+/**
+ * Zoom the viewport so that the page-width consumes the entire viewport.
+ */
+Viewport.prototype.fitToWidth = function() {
+ var page = this.getMostVisiblePage();
+ this.setZoom(this.computeFittingZoom_(this.pageDimensions_[page], true));
+ this.updateViewport_();
+};
+
+/**
+ * Zoom the viewport so that a page consumes the entire viewport. Also scrolls
+ * to the top of the most visible page.
+ */
+Viewport.prototype.fitToPage = function() {
+ var page = this.getMostVisiblePage();
+ this.setZoom(this.computeFittingZoom_(this.pageDimensions_[page], false));
+ this.window_.scrollTo(this.pageDimensions_[page].x * this.zoom_,
+ this.pageDimensions_[page].y * this.zoom_);
+ this.updateViewport_();
+};
+
+/**
+ * Zoom in to the next predefined zoom level.
+ */
+Viewport.prototype.zoomIn = function() {
+ var nextZoom = ZOOM_FACTORS[0];
+ for (var i = 0; i < ZOOM_FACTORS.length; i++) {
+ 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
+ nextZoom = ZOOM_FACTORS[i];
+ }
+ this.setZoom(nextZoom);
+ this.updateViewport_();
+};
+
+/**
+ * Zoom out to the next predefined zoom level.
+ */
+Viewport.prototype.zoomOut = function() {
+ var nextZoom = ZOOM_FACTORS[ZOOM_FACTORS.length - 1];
+ for (var i = ZOOM_FACTORS.length - 1; i >= 0; i--) {
+ if (ZOOM_FACTORS[i] > this.zoom_)
+ nextZoom = ZOOM_FACTORS[i];
+ }
+ this.setZoom(nextZoom);
+ this.updateViewport_();
+};
+
+/**
+ * Go to the given page index.
+ * @param {number} page the index of the page to go to
+ */
+Viewport.prototype.goToPage = function(page) {
+ if (page < 0)
+ page = 0;
+ var dimensions = this.pageDimensions_[page];
+ this.window_.scrollTo(dimensions.x * this.zoom_, dimensions.y * this.zoom_);
+};
+
+/**
+ * Set the dimensions of the document.
+ * @param {dictionary} documentDimensions the dimensions of the document
+ */
+Viewport.prototype.setDocumentDimensions = function(documentDimensions) {
+ this.documentDimensions_ = documentDimensions;
+ this.pageDimensions_ = this.documentDimensions_.pageDimensions;
+ this.contentSizeChanged_();
+ this.updateViewport_();
+};
« 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