Improve scrolling performance in OOP PDF
This increases the performance of scrolling in OOP PDF by not sending viewport changes via postMessage every time a scroll event occurs on the page. Instead, scroll events are sent along with pepper DidChangeView messages, which is a much more responsive way of updating the scroll position. Unfortunately this introduces some issues coordinating zoom and scroll events on the page. When a zoom happens in the container page, a scroll message might be sent to the plugin via DidChangeView. Then we send a zoom change message via postMessage. The result is that the zooming and scrolling happen asynchronously and result in a flickering effect. To avoid this, we first notify the plugin that we are about to zoom which causes it to stop reacting to scroll messages. After we have finished zooming we notify the plugin again so that it can continue reacting to scroll messages. BUG=386920 Review URL: https://codereview.chromium.org/347763007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279910 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
chrome
pdf
@ -52,7 +52,9 @@ function PDFViewer() {
|
||||
// Create the viewport.
|
||||
this.viewport_ = new Viewport(window,
|
||||
this.sizer_,
|
||||
this.viewportChangedCallback_.bind(this),
|
||||
this.viewportChanged_.bind(this),
|
||||
this.beforeZoom_.bind(this),
|
||||
this.afterZoom_.bind(this),
|
||||
getScrollbarWidth());
|
||||
|
||||
// Create the plugin object dynamically so we can set its src. The plugin
|
||||
@ -109,6 +111,15 @@ function PDFViewer() {
|
||||
this.plugin_.setAttribute('full-frame', '');
|
||||
document.body.appendChild(this.plugin_);
|
||||
|
||||
// TODO(raymes): Remove this spurious message once crbug.com/388606 is fixed.
|
||||
// This is a hack to initialize pepper sync scripting and avoid re-entrancy.
|
||||
this.plugin_.postMessage({
|
||||
type: 'viewport',
|
||||
zoom: 1,
|
||||
xOffset: 0,
|
||||
yOffset: 0
|
||||
});
|
||||
|
||||
// Setup the button event listeners.
|
||||
$('fit-to-width-button').addEventListener('click',
|
||||
this.viewport_.fitToWidth.bind(this.viewport_));
|
||||
@ -353,9 +364,36 @@ PDFViewer.prototype = {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* A callback that's called when the viewport changes.
|
||||
* A callback that's called before the zoom changes. Notify the plugin to stop
|
||||
* reacting to scroll events while zoom is taking place to avoid flickering.
|
||||
*/
|
||||
viewportChangedCallback_: function() {
|
||||
beforeZoom_: function() {
|
||||
this.plugin_.postMessage({
|
||||
type: 'stopScrolling'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* A callback that's called after the zoom changes. Notify the plugin of the
|
||||
* zoom change and to continue reacting to scroll events.
|
||||
*/
|
||||
afterZoom_: function() {
|
||||
var position = this.viewport_.position;
|
||||
var zoom = this.viewport_.zoom;
|
||||
this.plugin_.postMessage({
|
||||
type: 'viewport',
|
||||
zoom: zoom,
|
||||
xOffset: position.x,
|
||||
yOffset: position.y
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* A callback that's called after the viewport changes.
|
||||
*/
|
||||
viewportChanged_: function() {
|
||||
if (!this.documentDimensions_)
|
||||
return;
|
||||
|
||||
@ -391,16 +429,6 @@ PDFViewer.prototype = {
|
||||
this.pageIndicator_.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
var position = this.viewport_.position;
|
||||
var zoom = this.viewport_.zoom;
|
||||
// Notify the plugin of the viewport change.
|
||||
this.plugin_.postMessage({
|
||||
type: 'viewport',
|
||||
zoom: zoom,
|
||||
xOffset: position.x,
|
||||
yOffset: position.y
|
||||
});
|
||||
|
||||
var visiblePageDimensions = this.viewport_.getPageScreenRect(visiblePage);
|
||||
var size = this.viewport_.size;
|
||||
this.sendScriptingMessage_({
|
||||
|
@ -24,15 +24,22 @@ function getIntersectionArea(rect1, rect2) {
|
||||
* @param {Object} sizer is the element which represents the size of the
|
||||
* document in the viewport
|
||||
* @param {Function} viewportChangedCallback is run when the viewport changes
|
||||
* @param {Function} beforeZoomCallback is run before a change in zoom
|
||||
* @param {Function} afterZoomCallback is run after a change in zoom
|
||||
* @param {number} scrollbarWidth the width of scrollbars on the page
|
||||
*/
|
||||
function Viewport(window,
|
||||
sizer,
|
||||
viewportChangedCallback,
|
||||
beforeZoomCallback,
|
||||
afterZoomCallback,
|
||||
scrollbarWidth) {
|
||||
this.window_ = window;
|
||||
this.sizer_ = sizer;
|
||||
this.viewportChangedCallback_ = viewportChangedCallback;
|
||||
this.beforeZoomCallback_ = beforeZoomCallback;
|
||||
this.afterZoomCallback_ = afterZoomCallback;
|
||||
this.allowedToChangeZoom_ = false;
|
||||
this.zoom_ = 1;
|
||||
this.documentDimensions_ = null;
|
||||
this.pageDimensions_ = [];
|
||||
@ -83,6 +90,12 @@ Viewport.prototype = {
|
||||
* respectively.
|
||||
*/
|
||||
documentNeedsScrollbars_: function(zoom) {
|
||||
if (!this.documentDimensions_) {
|
||||
return {
|
||||
horizontal: false,
|
||||
vertical: false
|
||||
};
|
||||
}
|
||||
var documentWidth = this.documentDimensions_.width * zoom;
|
||||
var documentHeight = this.documentDimensions_.height * zoom;
|
||||
return {
|
||||
@ -173,12 +186,29 @@ Viewport.prototype = {
|
||||
return this.zoom_;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Used to wrap a function that might perform zooming on the viewport. This is
|
||||
* required so that we can notify the plugin that zooming is in progress
|
||||
* so that while zooming is taking place it can stop reacting to scroll events
|
||||
* from the viewport. This is to avoid flickering.
|
||||
*/
|
||||
mightZoom_: function(f) {
|
||||
this.beforeZoomCallback_();
|
||||
this.allowedToChangeZoom_ = true;
|
||||
f();
|
||||
this.allowedToChangeZoom_ = false;
|
||||
this.afterZoomCallback_();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Sets the zoom of the viewport.
|
||||
* @param {number} newZoom the zoom level to zoom to.
|
||||
*/
|
||||
setZoom_: function(newZoom) {
|
||||
if (!this.allowedToChangeZoom_)
|
||||
throw 'Called Viewport.setZoom_ without calling Viewport.mightZoom_.';
|
||||
var oldZoom = this.zoom_;
|
||||
this.zoom_ = newZoom;
|
||||
// Record the scroll position (relative to the middle of the window).
|
||||
@ -193,6 +223,16 @@ Viewport.prototype = {
|
||||
currentScrollPos[1] * newZoom - this.window_.innerHeight / 2);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Sets the zoom for testing purposes.
|
||||
*/
|
||||
setZoomForTest_: function(newZoom) {
|
||||
this.mightZoom_(function() {
|
||||
this.setZoom_(newZoom);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* @type {number} the width of scrollbars in the viewport in pixels.
|
||||
*/
|
||||
@ -333,17 +373,19 @@ Viewport.prototype = {
|
||||
* Zoom the viewport so that the page-width consumes the entire viewport.
|
||||
*/
|
||||
fitToWidth: function() {
|
||||
this.fittingType_ = Viewport.FittingType.FIT_TO_WIDTH;
|
||||
if (!this.documentDimensions_)
|
||||
return;
|
||||
// Track the last y-position so we stay at the same position after zooming.
|
||||
var oldY = this.window_.pageYOffset / this.zoom_;
|
||||
// When computing fit-to-width, the maximum width of a page in the document
|
||||
// is used, which is equal to the size of the document width.
|
||||
this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true));
|
||||
var page = this.getMostVisiblePage();
|
||||
this.window_.scrollTo(0, oldY * this.zoom_);
|
||||
this.updateViewport_();
|
||||
this.mightZoom_(function() {
|
||||
this.fittingType_ = Viewport.FittingType.FIT_TO_WIDTH;
|
||||
if (!this.documentDimensions_)
|
||||
return;
|
||||
// Track the last y-position to stay at the same position after zooming.
|
||||
var oldY = this.window_.pageYOffset / this.zoom_;
|
||||
// When computing fit-to-width, the maximum width of a page in the
|
||||
// document is used, which is equal to the size of the document width.
|
||||
this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true));
|
||||
var page = this.getMostVisiblePage();
|
||||
this.window_.scrollTo(0, oldY * this.zoom_);
|
||||
this.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -351,47 +393,54 @@ Viewport.prototype = {
|
||||
* to the top of the most visible page.
|
||||
*/
|
||||
fitToPage: function() {
|
||||
this.fittingType_ = Viewport.FittingType.FIT_TO_PAGE;
|
||||
if (!this.documentDimensions_)
|
||||
return;
|
||||
var page = this.getMostVisiblePage();
|
||||
this.setZoom_(this.computeFittingZoom_(this.pageDimensions_[page], false));
|
||||
// Center the document in the page by scrolling by the amount of empty
|
||||
// space to the left of the document.
|
||||
var xOffset =
|
||||
(this.documentDimensions_.width - this.pageDimensions_[page].width) *
|
||||
this.zoom_ / 2;
|
||||
this.window_.scrollTo(xOffset,
|
||||
this.pageDimensions_[page].y * this.zoom_);
|
||||
this.updateViewport_();
|
||||
this.mightZoom_(function() {
|
||||
this.fittingType_ = Viewport.FittingType.FIT_TO_PAGE;
|
||||
if (!this.documentDimensions_)
|
||||
return;
|
||||
var page = this.getMostVisiblePage();
|
||||
this.setZoom_(this.computeFittingZoom_(
|
||||
this.pageDimensions_[page], false));
|
||||
// Center the document in the page by scrolling by the amount of empty
|
||||
// space to the left of the document.
|
||||
var xOffset =
|
||||
(this.documentDimensions_.width - this.pageDimensions_[page].width) *
|
||||
this.zoom_ / 2;
|
||||
this.window_.scrollTo(xOffset,
|
||||
this.pageDimensions_[page].y * this.zoom_);
|
||||
this.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Zoom out to the next predefined zoom level.
|
||||
*/
|
||||
zoomOut: function() {
|
||||
this.fittingType_ = Viewport.FittingType.NONE;
|
||||
var nextZoom = Viewport.ZOOM_FACTORS[0];
|
||||
for (var i = 0; i < Viewport.ZOOM_FACTORS.length; i++) {
|
||||
if (Viewport.ZOOM_FACTORS[i] < this.zoom_)
|
||||
nextZoom = Viewport.ZOOM_FACTORS[i];
|
||||
}
|
||||
this.setZoom_(nextZoom);
|
||||
this.updateViewport_();
|
||||
this.mightZoom_(function() {
|
||||
this.fittingType_ = Viewport.FittingType.NONE;
|
||||
var nextZoom = Viewport.ZOOM_FACTORS[0];
|
||||
for (var i = 0; i < Viewport.ZOOM_FACTORS.length; i++) {
|
||||
if (Viewport.ZOOM_FACTORS[i] < this.zoom_)
|
||||
nextZoom = Viewport.ZOOM_FACTORS[i];
|
||||
}
|
||||
this.setZoom_(nextZoom);
|
||||
this.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Zoom in to the next predefined zoom level.
|
||||
*/
|
||||
zoomIn: function() {
|
||||
this.fittingType_ = Viewport.FittingType.NONE;
|
||||
var nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1];
|
||||
for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) {
|
||||
if (Viewport.ZOOM_FACTORS[i] > this.zoom_)
|
||||
nextZoom = Viewport.ZOOM_FACTORS[i];
|
||||
}
|
||||
this.setZoom_(nextZoom);
|
||||
this.updateViewport_();
|
||||
this.mightZoom_(function() {
|
||||
this.fittingType_ = Viewport.FittingType.NONE;
|
||||
var nextZoom = Viewport.ZOOM_FACTORS[Viewport.ZOOM_FACTORS.length - 1];
|
||||
for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) {
|
||||
if (Viewport.ZOOM_FACTORS[i] > this.zoom_)
|
||||
nextZoom = Viewport.ZOOM_FACTORS[i];
|
||||
}
|
||||
this.setZoom_(nextZoom);
|
||||
this.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -399,14 +448,18 @@ Viewport.prototype = {
|
||||
* @param {number} page the index of the page to go to.
|
||||
*/
|
||||
goToPage: function(page) {
|
||||
if (this.pageDimensions_.length == 0)
|
||||
return;
|
||||
if (page < 0)
|
||||
page = 0;
|
||||
if (page >= this.pageDimensions_.length)
|
||||
page = this.pageDimensions_.length - 1;
|
||||
var dimensions = this.pageDimensions_[page];
|
||||
this.window_.scrollTo(dimensions.x * this.zoom_, dimensions.y * this.zoom_);
|
||||
this.mightZoom_(function() {
|
||||
if (this.pageDimensions_.length == 0)
|
||||
return;
|
||||
if (page < 0)
|
||||
page = 0;
|
||||
if (page >= this.pageDimensions_.length)
|
||||
page = this.pageDimensions_.length - 1;
|
||||
var dimensions = this.pageDimensions_[page];
|
||||
this.window_.scrollTo(dimensions.x * this.zoom_,
|
||||
dimensions.y * this.zoom_);
|
||||
this.updateViewport_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -414,17 +467,19 @@ Viewport.prototype = {
|
||||
* @param {Object} documentDimensions the dimensions of the document
|
||||
*/
|
||||
setDocumentDimensions: function(documentDimensions) {
|
||||
var initialDimensions = !this.documentDimensions_;
|
||||
this.documentDimensions_ = documentDimensions;
|
||||
this.pageDimensions_ = this.documentDimensions_.pageDimensions;
|
||||
if (initialDimensions) {
|
||||
this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true));
|
||||
if (this.zoom_ > 1)
|
||||
this.setZoom_(1);
|
||||
this.window_.scrollTo(0, 0);
|
||||
}
|
||||
this.contentSizeChanged_();
|
||||
this.resize_();
|
||||
this.mightZoom_(function() {
|
||||
var initialDimensions = !this.documentDimensions_;
|
||||
this.documentDimensions_ = documentDimensions;
|
||||
this.pageDimensions_ = this.documentDimensions_.pageDimensions;
|
||||
if (initialDimensions) {
|
||||
this.setZoom_(this.computeFittingZoom_(this.documentDimensions_, true));
|
||||
if (this.zoom_ > 1)
|
||||
this.setZoom_(1);
|
||||
this.window_.scrollTo(0, 0);
|
||||
}
|
||||
this.contentSizeChanged_();
|
||||
this.resize_();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -434,6 +489,14 @@ Viewport.prototype = {
|
||||
* @return {Object} a rect representing the page in screen coordinates.
|
||||
*/
|
||||
getPageScreenRect: function(page) {
|
||||
if (!this.documentDimensions_) {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
}
|
||||
if (page >= this.pageDimensions_.length)
|
||||
page = this.pageDimensions_.length - 1;
|
||||
|
||||
|
@ -13,7 +13,7 @@ var tests = [
|
||||
// Verify that the initial zoom is less than or equal to 100%.
|
||||
chrome.test.assertTrue(viewer.viewport.zoom <= 1);
|
||||
|
||||
viewer.viewport.zoom = 1;
|
||||
viewer.viewport.setZoomForTest_(1);
|
||||
var sizer = document.getElementById('sizer');
|
||||
chrome.test.assertEq(826, sizer.offsetWidth);
|
||||
chrome.test.assertEq(1066, sizer.offsetHeight);
|
||||
|
@ -68,7 +68,7 @@ function MockDocumentDimensions(width, height) {
|
||||
var tests = [
|
||||
function testDocumentNeedsScrollbars() {
|
||||
var viewport = new Viewport(new MockWindow(100, 100), new MockSizer(),
|
||||
function() {}, 0);
|
||||
function() {}, function() {}, function() {}, 0);
|
||||
var scrollbars;
|
||||
|
||||
viewport.setDocumentDimensions(new MockDocumentDimensions(90, 90));
|
||||
@ -107,13 +107,13 @@ var tests = [
|
||||
var mockWindow = new MockWindow(100, 100);
|
||||
var mockSizer = new MockSizer();
|
||||
var mockCallback = new MockViewportChangedCallback();
|
||||
var viewport = new Viewport(mockWindow, mockSizer,
|
||||
mockCallback.callback, 0);
|
||||
var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback,
|
||||
function() {}, function() {}, 0);
|
||||
|
||||
// Test setting the zoom without the document dimensions set. The sizer
|
||||
// shouldn't change size.
|
||||
mockCallback.reset();
|
||||
viewport.setZoom_(0.5);
|
||||
viewport.setZoomForTest_(0.5);
|
||||
chrome.test.assertEq(0.5, viewport.zoom);
|
||||
chrome.test.assertTrue(mockCallback.wasCalled);
|
||||
chrome.test.assertEq('0px', mockSizer.style.width);
|
||||
@ -121,12 +121,12 @@ var tests = [
|
||||
chrome.test.assertEq(0, mockWindow.pageXOffset);
|
||||
chrome.test.assertEq(0, mockWindow.pageYOffset);
|
||||
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
viewport.setDocumentDimensions(new MockDocumentDimensions(200, 200));
|
||||
|
||||
// Test zooming out.
|
||||
mockCallback.reset();
|
||||
viewport.setZoom_(0.5);
|
||||
viewport.setZoomForTest_(0.5);
|
||||
chrome.test.assertEq(0.5, viewport.zoom);
|
||||
chrome.test.assertTrue(mockCallback.wasCalled);
|
||||
chrome.test.assertEq('100px', mockSizer.style.width);
|
||||
@ -134,7 +134,7 @@ var tests = [
|
||||
|
||||
// Test zooming in.
|
||||
mockCallback.reset();
|
||||
viewport.setZoom_(2);
|
||||
viewport.setZoomForTest_(2);
|
||||
chrome.test.assertEq(2, viewport.zoom);
|
||||
chrome.test.assertTrue(mockCallback.wasCalled);
|
||||
chrome.test.assertEq('400px', mockSizer.style.width);
|
||||
@ -142,10 +142,10 @@ var tests = [
|
||||
|
||||
// Test that the scroll position scales correctly. It scales relative to the
|
||||
// center of the page.
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
mockWindow.pageXOffset = 50;
|
||||
mockWindow.pageYOffset = 50;
|
||||
viewport.setZoom_(2);
|
||||
viewport.setZoomForTest_(2);
|
||||
chrome.test.assertEq('400px', mockSizer.style.width);
|
||||
chrome.test.assertEq('400px', mockSizer.style.height);
|
||||
chrome.test.assertEq(150, mockWindow.pageXOffset);
|
||||
@ -155,14 +155,15 @@ var tests = [
|
||||
|
||||
function testGetMostVisiblePage() {
|
||||
var mockWindow = new MockWindow(100, 100);
|
||||
var viewport = new Viewport(mockWindow, new MockSizer(), function() {}, 0);
|
||||
var viewport = new Viewport(mockWindow, new MockSizer(), function() {},
|
||||
function() {}, function() {}, 0);
|
||||
|
||||
var documentDimensions = new MockDocumentDimensions(100, 100);
|
||||
documentDimensions.addPage(100, 100);
|
||||
documentDimensions.addPage(150, 100);
|
||||
documentDimensions.addPage(100, 200);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
|
||||
// Scrolled to the start of the first page.
|
||||
mockWindow.scrollTo(0, 0);
|
||||
@ -185,7 +186,7 @@ var tests = [
|
||||
chrome.test.assertEq(2, viewport.getMostVisiblePage());
|
||||
|
||||
// Scrolled just over half way through the first page with 2x zoom.
|
||||
viewport.setZoom_(2);
|
||||
viewport.setZoomForTest_(2);
|
||||
mockWindow.scrollTo(0, 151);
|
||||
chrome.test.assertEq(1, viewport.getMostVisiblePage());
|
||||
chrome.test.succeed();
|
||||
@ -195,14 +196,14 @@ var tests = [
|
||||
var mockWindow = new MockWindow(100, 100);
|
||||
var mockSizer = new MockSizer();
|
||||
var mockCallback = new MockViewportChangedCallback();
|
||||
var viewport = new Viewport(mockWindow, mockSizer,
|
||||
mockCallback.callback, 0);
|
||||
var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback,
|
||||
function() {}, function() {}, 0);
|
||||
var documentDimensions = new MockDocumentDimensions();
|
||||
|
||||
// Test with a document width which matches the window width.
|
||||
documentDimensions.addPage(100, 100);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
viewport.setZoom_(0.1);
|
||||
viewport.setZoomForTest_(0.1);
|
||||
mockCallback.reset();
|
||||
viewport.fitToWidth();
|
||||
chrome.test.assertTrue(mockCallback.wasCalled);
|
||||
@ -234,7 +235,7 @@ var tests = [
|
||||
documentDimensions.reset();
|
||||
documentDimensions.addPage(50, 400);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
mockWindow.scrollTo(0, 100);
|
||||
mockCallback.reset();
|
||||
viewport.fitToWidth();
|
||||
@ -246,8 +247,8 @@ var tests = [
|
||||
// Test fitting works with scrollbars. The page will need to be zoomed to
|
||||
// fit to width, which will cause the page height to span outside of the
|
||||
// viewport, triggering 15px scrollbars to be shown.
|
||||
viewport = new Viewport(mockWindow, mockSizer,
|
||||
mockCallback.callback, 15);
|
||||
viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback,
|
||||
function() {}, function() {}, 15);
|
||||
documentDimensions.reset();
|
||||
documentDimensions.addPage(50, 100);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
@ -263,14 +264,14 @@ var tests = [
|
||||
var mockWindow = new MockWindow(100, 100);
|
||||
var mockSizer = new MockSizer();
|
||||
var mockCallback = new MockViewportChangedCallback();
|
||||
var viewport = new Viewport(mockWindow, mockSizer,
|
||||
mockCallback.callback, 0);
|
||||
var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback,
|
||||
function() {}, function() {}, 0);
|
||||
var documentDimensions = new MockDocumentDimensions();
|
||||
|
||||
// Test with a page size which matches the window size.
|
||||
documentDimensions.addPage(100, 100);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
viewport.setZoom_(0.1);
|
||||
viewport.setZoomForTest_(0.1);
|
||||
mockCallback.reset();
|
||||
viewport.fitToPage();
|
||||
chrome.test.assertTrue(mockCallback.wasCalled);
|
||||
@ -306,7 +307,7 @@ var tests = [
|
||||
documentDimensions.addPage(200, 100);
|
||||
documentDimensions.addPage(100, 400);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
mockWindow.scrollTo(0, 0);
|
||||
mockCallback.reset();
|
||||
viewport.fitToPage();
|
||||
@ -314,7 +315,7 @@ var tests = [
|
||||
chrome.test.assertEq('100px', mockSizer.style.width);
|
||||
chrome.test.assertEq('250px', mockSizer.style.height);
|
||||
chrome.test.assertEq(0.5, viewport.zoom);
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
mockWindow.scrollTo(0, 100);
|
||||
mockCallback.reset();
|
||||
viewport.fitToPage();
|
||||
@ -328,13 +329,13 @@ var tests = [
|
||||
documentDimensions.addPage(200, 200);
|
||||
documentDimensions.addPage(100, 400);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
mockWindow.scrollTo(0, 0);
|
||||
viewport.fitToPage();
|
||||
chrome.test.assertEq(0.5, viewport.zoom);
|
||||
chrome.test.assertEq(0, viewport.position.x);
|
||||
chrome.test.assertEq(0, viewport.position.y);
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
mockWindow.scrollTo(0, 175);
|
||||
viewport.fitToPage();
|
||||
chrome.test.assertEq(0.25, viewport.zoom);
|
||||
@ -348,15 +349,15 @@ var tests = [
|
||||
var mockWindow = new MockWindow(100, 100);
|
||||
var mockSizer = new MockSizer();
|
||||
var mockCallback = new MockViewportChangedCallback();
|
||||
var viewport = new Viewport(mockWindow, mockSizer,
|
||||
mockCallback.callback, 0);
|
||||
var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback,
|
||||
function() {}, function() {}, 0);
|
||||
var documentDimensions = new MockDocumentDimensions();
|
||||
|
||||
documentDimensions.addPage(100, 100);
|
||||
documentDimensions.addPage(200, 200);
|
||||
documentDimensions.addPage(100, 400);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
|
||||
mockCallback.reset();
|
||||
viewport.goToPage(0);
|
||||
@ -376,7 +377,7 @@ var tests = [
|
||||
chrome.test.assertEq(0, viewport.position.x);
|
||||
chrome.test.assertEq(300, viewport.position.y);
|
||||
|
||||
viewport.setZoom_(0.5);
|
||||
viewport.setZoomForTest_(0.5);
|
||||
mockCallback.reset();
|
||||
viewport.goToPage(2);
|
||||
chrome.test.assertTrue(mockCallback.wasCalled);
|
||||
@ -389,13 +390,13 @@ var tests = [
|
||||
var mockWindow = new MockWindow(100, 100);
|
||||
var mockSizer = new MockSizer();
|
||||
var mockCallback = new MockViewportChangedCallback();
|
||||
var viewport = new Viewport(mockWindow, mockSizer,
|
||||
mockCallback.callback, 0);
|
||||
var viewport = new Viewport(mockWindow, mockSizer, mockCallback.callback,
|
||||
function() {}, function() {}, 0);
|
||||
var documentDimensions = new MockDocumentDimensions();
|
||||
documentDimensions.addPage(100, 100);
|
||||
documentDimensions.addPage(200, 200);
|
||||
viewport.setDocumentDimensions(documentDimensions);
|
||||
viewport.setZoom_(1);
|
||||
viewport.setZoomForTest_(1);
|
||||
|
||||
// Test that the rect of the first page is positioned/sized correctly.
|
||||
mockWindow.scrollTo(0, 0);
|
||||
@ -426,6 +427,28 @@ var tests = [
|
||||
chrome.test.assertEq(200 - Viewport.PAGE_SHADOW.bottom -
|
||||
Viewport.PAGE_SHADOW.top, rect1.height);
|
||||
chrome.test.succeed();
|
||||
},
|
||||
|
||||
function testBeforeZoomAfterZoom() {
|
||||
var mockWindow = new MockWindow(100, 100);
|
||||
var mockSizer = new MockSizer();
|
||||
var viewport;
|
||||
var afterZoomCalled = false;
|
||||
var beforeZoomCalled = false;
|
||||
var afterZoom = function() {
|
||||
afterZoomCalled = true;
|
||||
chrome.test.assertTrue(beforeZoomCalled);
|
||||
chrome.test.assertEq(0.5, viewport.zoom);
|
||||
};
|
||||
var beforeZoom = function() {
|
||||
beforeZoomCalled = true;
|
||||
chrome.test.assertFalse(afterZoomCalled);
|
||||
chrome.test.assertEq(1, viewport.zoom);
|
||||
};
|
||||
viewport = new Viewport(mockWindow, mockSizer, function() {},
|
||||
beforeZoom, afterZoom, 0);
|
||||
viewport.setZoomForTest_(0.5);
|
||||
chrome.test.succeed();
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "ppapi/cpp/module.h"
|
||||
#include "ppapi/cpp/point.h"
|
||||
#include "ppapi/cpp/private/pdf.h"
|
||||
#include "ppapi/cpp/private/var_private.h"
|
||||
#include "ppapi/cpp/rect.h"
|
||||
#include "ppapi/cpp/resource.h"
|
||||
#include "ppapi/cpp/url_request_info.h"
|
||||
@ -70,6 +71,8 @@ const char* kJSViewportType = "viewport";
|
||||
const char* kJSXOffset = "xOffset";
|
||||
const char* kJSYOffset = "yOffset";
|
||||
const char* kJSZoom = "zoom";
|
||||
// Stop scrolling message (Page -> Plugin)
|
||||
const char* kJSStopScrollingType = "stopScrolling";
|
||||
// Document dimension arguments (Plugin -> Page).
|
||||
const char* kJSDocumentDimensionsType = "documentDimensions";
|
||||
const char* kJSDocumentWidth = "width";
|
||||
@ -244,7 +247,8 @@ OutOfProcessInstance::OutOfProcessInstance(PP_Instance instance)
|
||||
last_progress_sent_(0),
|
||||
recently_sent_find_update_(false),
|
||||
received_viewport_message_(false),
|
||||
did_call_start_loading_(false) {
|
||||
did_call_start_loading_(false),
|
||||
stop_scrolling_(false) {
|
||||
loader_factory_.Initialize(this);
|
||||
timer_factory_.Initialize(this);
|
||||
form_factory_.Initialize(this);
|
||||
@ -355,20 +359,17 @@ void OutOfProcessInstance::HandleMessage(const pp::Var& message) {
|
||||
dict.Get(pp::Var(kJSYOffset)).is_int() &&
|
||||
dict.Get(pp::Var(kJSZoom)).is_number()) {
|
||||
received_viewport_message_ = true;
|
||||
stop_scrolling_ = false;
|
||||
double zoom = dict.Get(pp::Var(kJSZoom)).AsDouble();
|
||||
int x = dict.Get(pp::Var(kJSXOffset)).AsInt();
|
||||
int y = dict.Get(pp::Var(kJSYOffset)).AsInt();
|
||||
pp::Point scroll_offset(dict.Get(pp::Var(kJSXOffset)).AsInt(),
|
||||
dict.Get(pp::Var(kJSYOffset)).AsInt());
|
||||
|
||||
// Bound the input parameters.
|
||||
zoom = std::max(kMinZoom, zoom);
|
||||
int max_x = document_size_.width() * zoom - plugin_dip_size_.width();
|
||||
x = std::max(std::min(x, max_x), 0);
|
||||
int max_y = document_size_.height() * zoom - plugin_dip_size_.height();
|
||||
y = std::max(std::min(y, max_y), 0);
|
||||
|
||||
SetZoom(zoom);
|
||||
engine_->ScrolledToXPosition(x * device_scale_);
|
||||
engine_->ScrolledToYPosition(y * device_scale_);
|
||||
scroll_offset = BoundScrollOffsetToDocument(scroll_offset);
|
||||
engine_->ScrolledToXPosition(scroll_offset.x() * device_scale_);
|
||||
engine_->ScrolledToYPosition(scroll_offset.y() * device_scale_);
|
||||
} else if (type == kJSGetPasswordCompleteType &&
|
||||
dict.Get(pp::Var(kJSPassword)).is_string()) {
|
||||
if (password_callback_) {
|
||||
@ -425,6 +426,8 @@ void OutOfProcessInstance::HandleMessage(const pp::Var& message) {
|
||||
reply.Set(pp::Var(kJSAccessibilityJSON), pp::Var(json));
|
||||
}
|
||||
PostMessage(reply);
|
||||
} else if (type == kJSStopScrollingType) {
|
||||
stop_scrolling_ = true;
|
||||
} else {
|
||||
NOTREACHED();
|
||||
}
|
||||
@ -507,32 +510,38 @@ void OutOfProcessInstance::DidChangeView(const pp::View& view) {
|
||||
pp::Size view_device_size(view_rect.width() * device_scale,
|
||||
view_rect.height() * device_scale);
|
||||
|
||||
if (view_device_size == plugin_size_ && device_scale == device_scale_)
|
||||
return; // We don't care about the position, only the size.
|
||||
if (view_device_size != plugin_size_ || device_scale != device_scale_) {
|
||||
device_scale_ = device_scale;
|
||||
plugin_dip_size_ = view_rect.size();
|
||||
plugin_size_ = view_device_size;
|
||||
|
||||
device_scale_ = device_scale;
|
||||
plugin_dip_size_ = view_rect.size();
|
||||
plugin_size_ = view_device_size;
|
||||
paint_manager_.SetSize(view_device_size, device_scale_);
|
||||
|
||||
paint_manager_.SetSize(view_device_size, device_scale_);
|
||||
pp::Size new_image_data_size = PaintManager::GetNewContextSize(
|
||||
image_data_.size(),
|
||||
plugin_size_);
|
||||
if (new_image_data_size != image_data_.size()) {
|
||||
image_data_ = pp::ImageData(this,
|
||||
PP_IMAGEDATAFORMAT_BGRA_PREMUL,
|
||||
new_image_data_size,
|
||||
false);
|
||||
first_paint_ = true;
|
||||
}
|
||||
|
||||
pp::Size new_image_data_size = PaintManager::GetNewContextSize(
|
||||
image_data_.size(),
|
||||
plugin_size_);
|
||||
if (new_image_data_size != image_data_.size()) {
|
||||
image_data_ = pp::ImageData(this,
|
||||
PP_IMAGEDATAFORMAT_BGRA_PREMUL,
|
||||
new_image_data_size,
|
||||
false);
|
||||
first_paint_ = true;
|
||||
if (image_data_.is_null()) {
|
||||
DCHECK(plugin_size_.IsEmpty());
|
||||
return;
|
||||
}
|
||||
|
||||
OnGeometryChanged(zoom_, old_device_scale);
|
||||
}
|
||||
|
||||
if (image_data_.is_null()) {
|
||||
DCHECK(plugin_size_.IsEmpty());
|
||||
return;
|
||||
if (!stop_scrolling_) {
|
||||
pp::Point scroll_offset(
|
||||
BoundScrollOffsetToDocument(view.GetScrollOffset()));
|
||||
engine_->ScrolledToXPosition(scroll_offset.x() * device_scale_);
|
||||
engine_->ScrolledToYPosition(scroll_offset.y() * device_scale_);
|
||||
}
|
||||
|
||||
OnGeometryChanged(zoom_, old_device_scale);
|
||||
}
|
||||
|
||||
pp::Var OutOfProcessInstance::GetLinkAtPosition(
|
||||
@ -786,7 +795,8 @@ void OutOfProcessInstance::Invalidate(const pp::Rect& rect) {
|
||||
}
|
||||
|
||||
void OutOfProcessInstance::Scroll(const pp::Point& point) {
|
||||
paint_manager_.ScrollRect(available_area_, point);
|
||||
if (!image_data_.is_null())
|
||||
paint_manager_.ScrollRect(available_area_, point);
|
||||
}
|
||||
|
||||
void OutOfProcessInstance::ScrollToX(int x) {
|
||||
@ -1404,4 +1414,13 @@ void OutOfProcessInstance::UserMetricsRecordAction(
|
||||
pp::PDF::UserMetricsRecordAction(this, pp::Var(action));
|
||||
}
|
||||
|
||||
pp::Point OutOfProcessInstance::BoundScrollOffsetToDocument(
|
||||
const pp::Point& scroll_offset) {
|
||||
int max_x = document_size_.width() * zoom_ - plugin_dip_size_.width();
|
||||
int x = std::max(std::min(scroll_offset.x(), max_x), 0);
|
||||
int max_y = document_size_.height() * zoom_ - plugin_dip_size_.height();
|
||||
int y = std::max(std::min(scroll_offset.y(), max_y), 0);
|
||||
return pp::Point(x, y);
|
||||
}
|
||||
|
||||
} // namespace chrome_pdf
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "ppapi/cpp/instance.h"
|
||||
#include "ppapi/cpp/private/find_private.h"
|
||||
#include "ppapi/cpp/private/uma_private.h"
|
||||
#include "ppapi/cpp/private/var_private.h"
|
||||
#include "ppapi/cpp/url_loader.h"
|
||||
#include "ppapi/utility/completion_callback_factory.h"
|
||||
|
||||
@ -205,6 +204,9 @@ class OutOfProcessInstance : public pp::Instance,
|
||||
// Load the next available preview page into the blank page.
|
||||
void LoadAvailablePreviewPage();
|
||||
|
||||
// Bound the given scroll offset to the document.
|
||||
pp::Point BoundScrollOffsetToDocument(const pp::Point& scroll_offset);
|
||||
|
||||
pp::ImageData image_data_;
|
||||
// Used when the plugin is embedded in a page and we have to create the loader
|
||||
// ourself.
|
||||
@ -284,10 +286,6 @@ class OutOfProcessInstance : public pp::Instance,
|
||||
DocumentLoadState document_load_state_;
|
||||
DocumentLoadState preview_document_load_state_;
|
||||
|
||||
// JavaScript interface to control this instance.
|
||||
// This wraps a PDFScriptableObject in a pp::Var.
|
||||
pp::VarPrivate instance_object_;
|
||||
|
||||
// A UMA resource for histogram reporting.
|
||||
pp::UMAPrivate uma_;
|
||||
|
||||
@ -333,6 +331,11 @@ class OutOfProcessInstance : public pp::Instance,
|
||||
// document finishes loading.
|
||||
bool did_call_start_loading_;
|
||||
|
||||
// If this is true, then don't scroll the plugin in response to DidChangeView
|
||||
// messages. This will be true when the extension page is in the process of
|
||||
// zooming the plugin so that flickering doesn't occur while zooming.
|
||||
bool stop_scrolling_;
|
||||
|
||||
// The callback for receiving the password from the page.
|
||||
scoped_ptr<pp::CompletionCallbackWithOutput<pp::Var> > password_callback_;
|
||||
};
|
||||
|
Reference in New Issue
Block a user