Bug 795657: Integrate native viewport configuration better into async pan-zoom code. r=jwir3,roc

This is a rollup of the following patches
 - Change the interpretation of FrameMetrics.mZoom to a "resolution-indepedent zoom", instead of a resolution-depedent scale factor. r=roc
 - Remove mention of "meta" from TabChild. r=roc
 - Remove some useless logging. r=roc
 - Tag FrameMetrics with its composition bounds at paint time. r=roc
 - Add a helper to calculate the render resolution for a FrameMetrics. r=roc
 - Add a helper to compute the approximate CSS dimensions a FrameMetrics will cover during composition. r=roc
 - BrowserElementScrolling doesn't actually care about zoom or resolution. r=roc
 - Accept the viewport that content has calculated, when it's received the latest widget geometry update. r=roc
 - Mechanically separate uses of zoom/resolution based on new definitions. r=roc
 - Convert GetViewportInfo()'s resolution-dependent scale into resolution-indepedent zoom. r=roc
 - Reinterpret defaultZoom == 0.0 as "intrinsic scale". r=jwir3,roc
This commit is contained in:
Chris Jones 2012-10-11 22:46:24 -07:00
parent d52cbd4f2e
commit b00b1e25b0
8 changed files with 246 additions and 162 deletions

View File

@ -204,18 +204,9 @@ const ContentPanning = {
_recvViewportChange: function(data) {
let metrics = data.json;
let displayPort = metrics.displayPort;
let compositionWidth = metrics.compositionBounds.width;
let compositionHeight = metrics.compositionBounds.height;
let x = metrics.x;
let y = metrics.y;
this._zoom = metrics.zoom;
this._viewport = new Rect(x, y,
compositionWidth / metrics.zoom,
compositionHeight / metrics.zoom);
this._viewport = new Rect(metrics.x, metrics.y,
metrics.viewport.width,
metrics.viewport.height);
this._cssPageRect = new Rect(metrics.cssPageRect.x,
metrics.cssPageRect.y,
metrics.cssPageRect.width,
@ -232,7 +223,6 @@ const ContentPanning = {
let win = content;
let zoom = this._zoom;
let element = ElementTouchHelper.anyElementFromPoint(win, data.x, data.y);
if (!element) {
this._zoomOut();

View File

@ -183,7 +183,7 @@ TabChild::HandleEvent(nsIDOMEvent* aEvent)
if (eventType.EqualsLiteral("DOMMetaAdded")) {
// This meta data may or may not have been a meta viewport tag. If it was,
// we should handle it immediately.
HandlePossibleMetaViewportChange();
HandlePossibleViewportChange();
}
return NS_OK;
@ -222,21 +222,27 @@ TabChild::Observe(nsISupports *aSubject,
mContentDocumentIsDisplayed = true;
// Reset CSS viewport and zoom to default on new page, then calculate them
// properly using the actual metadata from the page.
// Reset CSS viewport and zoom to default on new page, then
// calculate them properly using the actual metadata from the
// page.
SetCSSViewport(kDefaultViewportSize.width, kDefaultViewportSize.height);
// Calculate a really simple resolution that we probably won't be
// keeping, as well as putting the scroll offset back to the top-left of
// the page.
float resolution = float(mInnerSize.width) / float(kDefaultViewportSize.width);
mLastMetrics.mZoom.width = mLastMetrics.mZoom.height =
mLastMetrics.mResolution.width = mLastMetrics.mResolution.height =
resolution;
// Calculate a really simple resolution that we probably won't
// be keeping, as well as putting the scroll offset back to
// the top-left of the page.
mLastMetrics.mZoom = gfxSize(1.0, 1.0);
mLastMetrics.mViewport =
gfx::Rect(0, 0,
kDefaultViewportSize.width, kDefaultViewportSize.height);
mLastMetrics.mCompositionBounds = nsIntRect(nsIntPoint(0, 0),
mInnerSize);
mLastMetrics.mResolution =
AsyncPanZoomController::CalculateResolution(mLastMetrics);
mLastMetrics.mScrollOffset = gfx::Point(0, 0);
utils->SetResolution(resolution, resolution);
utils->SetResolution(mLastMetrics.mResolution.width,
mLastMetrics.mResolution.height);
HandlePossibleMetaViewportChange();
HandlePossibleViewportChange();
}
}
}
@ -350,7 +356,7 @@ TabChild::SetCSSViewport(float aWidth, float aHeight)
}
void
TabChild::HandlePossibleMetaViewportChange()
TabChild::HandlePossibleViewportChange()
{
if (!IsAsyncPanZoomEnabled()) {
return;
@ -362,16 +368,16 @@ TabChild::HandlePossibleMetaViewportChange()
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
ViewportInfo viewportMetaData =
ViewportInfo viewportInfo =
nsContentUtils::GetViewportInfo(document, mInnerSize.width, mInnerSize.height);
SendUpdateZoomConstraints(viewportMetaData.allowZoom,
viewportMetaData.minZoom,
viewportMetaData.maxZoom);
SendUpdateZoomConstraints(viewportInfo.allowZoom,
viewportInfo.minZoom,
viewportInfo.maxZoom);
float screenW = mInnerSize.width;
float screenH = mInnerSize.height;
float viewportW = viewportMetaData.width;
float viewportH = viewportMetaData.height;
float viewportW = viewportInfo.width;
float viewportH = viewportInfo.height;
// We're not being displayed in any way; don't bother doing anything because
// that will just confuse future adjustments.
@ -385,8 +391,8 @@ TabChild::HandlePossibleMetaViewportChange()
// we have to call SetCSSViewport twice - once to set the width, and the
// second time to figure out the height based on the layout at that width.
float oldBrowserWidth = mOldViewportWidth;
mLastMetrics.mViewport.width = viewportMetaData.width;
mLastMetrics.mViewport.height = viewportMetaData.height;
mLastMetrics.mViewport.width = viewportInfo.width;
mLastMetrics.mViewport.height = viewportInfo.height;
if (!oldBrowserWidth) {
oldBrowserWidth = kDefaultViewportSize.width;
}
@ -425,7 +431,7 @@ TabChild::HandlePossibleMetaViewportChange()
float pageHeight = NS_MAX(htmlHeight, bodyHeight);
minScale = mInnerSize.width / pageWidth;
minScale = clamped((double)minScale, viewportMetaData.minZoom, viewportMetaData.maxZoom);
minScale = clamped((double)minScale, viewportInfo.minZoom, viewportInfo.maxZoom);
viewportH = NS_MAX(viewportH, screenH / minScale);
SetCSSViewport(viewportW, viewportH);
@ -446,23 +452,42 @@ TabChild::HandlePossibleMetaViewportChange()
if (!oldScreenWidth) {
oldScreenWidth = mInnerSize.width;
}
float zoomScale = (screenW * oldBrowserWidth) / (oldScreenWidth * viewportW);
float zoom = clamped(double(mLastMetrics.mZoom.width * zoomScale),
viewportMetaData.minZoom, viewportMetaData.maxZoom);
utils->SetResolution(zoom, zoom);
FrameMetrics metrics(mLastMetrics);
metrics.mViewport = gfx::Rect(0.0f, 0.0f, viewportW, viewportH);
metrics.mScrollableRect = gfx::Rect(0.0f, 0.0f, pageWidth, pageHeight);
metrics.mCompositionBounds = nsIntRect(0, 0, mInnerSize.width, mInnerSize.height);
metrics.mZoom.width = metrics.mZoom.height =
metrics.mResolution.width = metrics.mResolution.height = zoom;
gfxSize intrinsicScale =
AsyncPanZoomController::CalculateIntrinsicScale(metrics);
// FIXME/bug 799585(?): GetViewportInfo() returns a defaultZoom of
// 0.0 to mean "did not calculate a zoom". In that case, we default
// it to the intrinsic scale.
if (viewportInfo.defaultZoom < 0.01f) {
viewportInfo.defaultZoom = intrinsicScale.width;
}
MOZ_ASSERT(viewportInfo.minZoom <= viewportInfo.defaultZoom &&
viewportInfo.defaultZoom <= viewportInfo.maxZoom);
// GetViewportInfo() returns a resolution-dependent scale factor.
// Convert that to a resolution-indepedent zoom.
metrics.mZoom = gfxSize(viewportInfo.defaultZoom / intrinsicScale.width,
viewportInfo.defaultZoom / intrinsicScale.height);
metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort(
// The page must have been refreshed in some way such as a new document or
// new CSS viewport, so we know that there's no velocity, acceleration, and
// we have no idea how long painting will take.
metrics, gfx::Point(0.0f, 0.0f), gfx::Point(0.0f, 0.0f), 0.0);
gfxSize resolution = AsyncPanZoomController::CalculateResolution(metrics);
// XXX is this actually hysteresis? This calculation is not well
// understood. It's taken from the previous JS implementation.
gfxFloat hysteresis/*?*/ =
gfxFloat(oldBrowserWidth) / gfxFloat(oldScreenWidth);
resolution.width *= hysteresis;
resolution.height *= hysteresis;
metrics.mResolution = resolution;
utils->SetResolution(metrics.mResolution.width, metrics.mResolution.height);
// Force a repaint with these metrics. This, among other things, sets the
// displayport, so we start with async painting.
RecvUpdateFrame(metrics);
@ -1077,10 +1102,6 @@ TabChild::RecvShow(const nsIntSize& size)
bool
TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size)
{
#ifdef DEBUG
printf("[TabChild] Update Dimensions to (x,y,w,h)= (%ud, %ud, %ud, %ud) and move to (w,h)= (%ud, %ud)\n", rect.x, rect.y, rect.width, rect.height, size.width, size.height);
#endif
if (!mRemoteFrame) {
return true;
}
@ -1098,7 +1119,7 @@ TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size)
baseWin->SetPositionAndSize(0, 0, size.width, size.height,
true);
HandlePossibleMetaViewportChange();
HandlePossibleViewportChange();
return true;
}
@ -1141,15 +1162,15 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
nsCString data;
data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x));
data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y));
// We don't treat the x and y scales any differently for this
// semi-platform-specific code.
data += nsPrintfCString(", \"zoom\" : %f", aFrameMetrics.mZoom.width);
data += nsPrintfCString(", \"viewport\" : ");
data += nsPrintfCString("{ \"width\" : %f", aFrameMetrics.mViewport.width);
data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mViewport.height);
data += nsPrintfCString(" }");
data += nsPrintfCString(", \"displayPort\" : ");
data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mDisplayPort.x);
data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mDisplayPort.y);
data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mDisplayPort.width);
data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mDisplayPort.height);
data += nsPrintfCString(", \"resolution\" : %f", aFrameMetrics.mResolution.width);
data += nsPrintfCString(" }");
data += nsPrintfCString(", \"compositionBounds\" : ");
data += nsPrintfCString("{ \"x\" : %d", aFrameMetrics.mCompositionBounds.x);
@ -1170,13 +1191,15 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mWebNav);
gfx::Rect cssCompositedRect =
AsyncPanZoomController::CalculateCompositedRectInCssPixels(aFrameMetrics);
utils->SetScrollPositionClampingScrollPortSize(
aFrameMetrics.mCompositionBounds.width / aFrameMetrics.mZoom.width,
aFrameMetrics.mCompositionBounds.height / aFrameMetrics.mZoom.width);
cssCompositedRect.width, cssCompositedRect.height);
window->ScrollTo(aFrameMetrics.mScrollOffset.x,
aFrameMetrics.mScrollOffset.y);
utils->SetResolution(aFrameMetrics.mResolution.width,
aFrameMetrics.mResolution.width);
gfxSize resolution = AsyncPanZoomController::CalculateResolution(
aFrameMetrics);
utils->SetResolution(resolution.width, resolution.height);
nsCOMPtr<nsIDOMDocument> domDoc;
nsCOMPtr<nsIDOMElement> docElement;

View File

@ -338,11 +338,12 @@ private:
// variables local to this class before setting it.
void SetCSSViewport(float aX, float aY);
// Recalculates the display state, including the CSS viewport. This should
// be called whenever we believe the meta viewport data on a document may
// have changed. If it didn't change, this function doesn't do anything.
// However, it should not be called all the time as it is fairly expensive.
void HandlePossibleMetaViewportChange();
// Recalculates the display state, including the CSS
// viewport. This should be called whenever we believe the
// viewport data on a document may have changed. If it didn't
// change, this function doesn't do anything. However, it should
// not be called all the time as it is fairly expensive.
void HandlePossibleViewportChange();
// Wraps up a JSON object as a structured clone and sends it to the browser
// chrome script.

View File

@ -38,6 +38,7 @@ public:
, mScrollId(NULL_SCROLL_ID)
, mScrollableRect(0, 0, 0, 0)
, mResolution(1, 1)
, mZoom(1, 1)
, mDevPixelsPerCSSPixel(1)
, mMayHaveTouchListeners(false)
{}
@ -204,17 +205,19 @@ public:
// resolution of parent layers is opaque to this metric.
gfxSize mResolution;
// The amount we are currently zooming the frame. This is distinct from
// |mResolution| as we can paint a frame at a different resolution than we
// zoom it at. This is useful in situations where we want to zoom a frame
// without forcing a repaint. At steady state, this and |mResolution| will be
// equal.
// The resolution-independent "user zoom". For example, if a page
// configures the viewport to a zoom value of 2x, then this member
// will always be 2.0 no matter what the viewport or composition
// bounds.
//
// Every time this frame is composited and the compositor samples its
// transform, this metric is used to create a transform which is
// post-multiplied into the parent's transform. Since this only happens when
// we walk the layer tree, the resulting transform isn't stored here. Thus the
// zoom of parent layers is opaque to this metric.
// In the steady state (no animations), and ignoring DPI, then the
// following is usually true
//
// intrinsicScale = (mCompositionBounds / mViewport)
// mResolution = mZoom * intrinsicScale
//
// When this is not true, we're probably asynchronously sampling a
// zoom animation for content.
gfxSize mZoom;
// The conversion factor between CSS pixels and device pixels for this frame.

View File

@ -142,11 +142,11 @@ nsEventStatus
AsyncPanZoomController::ReceiveInputEvent(const nsInputEvent& aEvent,
nsInputEvent* aOutEvent)
{
float currentZoom;
gfxFloat currentResolution;
gfx::Point currentScrollOffset, lastScrollOffset;
{
MonitorAutoLock monitor(mMonitor);
currentZoom = mFrameMetrics.mZoom.width;
currentResolution = CalculateResolution(mFrameMetrics).width;
currentScrollOffset = gfx::Point(mFrameMetrics.mScrollOffset.x,
mFrameMetrics.mScrollOffset.y);
lastScrollOffset = gfx::Point(mLastContentPaintMetrics.mScrollOffset.x,
@ -179,7 +179,7 @@ AsyncPanZoomController::ReceiveInputEvent(const nsInputEvent& aEvent,
if (touch) {
gfx::Point refPoint = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(touch->mRefPoint.x, touch->mRefPoint.y),
currentZoom);
currentResolution);
touch->mRefPoint = nsIntPoint(refPoint.x, refPoint.y);
}
}
@ -188,7 +188,7 @@ AsyncPanZoomController::ReceiveInputEvent(const nsInputEvent& aEvent,
default: {
gfx::Point refPoint = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aOutEvent->refPoint.x, aOutEvent->refPoint.y),
currentZoom);
currentResolution);
aOutEvent->refPoint = nsIntPoint(refPoint.x, refPoint.y);
break;
}
@ -444,10 +444,11 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
{
MonitorAutoLock monitor(mMonitor);
float scale = mFrameMetrics.mZoom.width;
gfxFloat resolution = CalculateResolution(mFrameMetrics).width;
gfxFloat userZoom = mFrameMetrics.mZoom.width;
nsIntPoint focusPoint = aEvent.mFocusPoint;
float xFocusChange = (mLastZoomFocus.x - focusPoint.x) / scale, yFocusChange = (mLastZoomFocus.y - focusPoint.y) / scale;
gfxFloat xFocusChange = (mLastZoomFocus.x - focusPoint.x) / resolution;
gfxFloat yFocusChange = (mLastZoomFocus.y - focusPoint.y) / resolution;
// If displacing by the change in focus point will take us off page bounds,
// then reduce the displacement such that it doesn't.
if (mX.DisplacementWillOverscroll(xFocusChange) != Axis::OVERSCROLL_NONE) {
@ -461,17 +462,18 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
// When we zoom in with focus, we can zoom too much towards the boundaries
// that we actually go over them. These are the needed displacements along
// either axis such that we don't overscroll the boundaries when zooming.
float neededDisplacementX = 0, neededDisplacementY = 0;
gfxFloat neededDisplacementX = 0, neededDisplacementY = 0;
// Only do the scaling if we won't go over 8x zoom in or out.
bool doScale = (scale < mMaxZoom && spanRatio > 1.0f) || (scale > mMinZoom && spanRatio < 1.0f);
bool doScale = (spanRatio > 1.0 && userZoom < mMaxZoom) ||
(spanRatio < 1.0 && userZoom > mMinZoom);
// If this zoom will take it over 8x zoom in either direction, but it's not
// already there, then normalize it.
if (scale * spanRatio > mMaxZoom) {
spanRatio = scale / mMaxZoom;
} else if (scale * spanRatio < mMinZoom) {
spanRatio = scale / mMinZoom;
if (userZoom * spanRatio > mMaxZoom) {
spanRatio = userZoom / mMaxZoom;
} else if (userZoom * spanRatio < mMinZoom) {
spanRatio = userZoom / mMinZoom;
}
if (doScale) {
@ -509,8 +511,7 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
}
if (doScale) {
ScaleWithFocus(scale * spanRatio,
focusPoint);
ScaleWithFocus(userZoom * spanRatio, focusPoint);
if (neededDisplacementX != 0 || neededDisplacementY != 0) {
ScrollBy(gfx::Point(neededDisplacementX, neededDisplacementY));
@ -549,10 +550,12 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEven
if (mGeckoContentController) {
MonitorAutoLock monitor(mMonitor);
gfxFloat resolution = CalculateResolution(mFrameMetrics).width;
gfx::Point point = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y),
mFrameMetrics.mZoom.width);
mGeckoContentController->HandleSingleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y)));
resolution);
mGeckoContentController->HandleSingleTap(nsIntPoint(NS_lround(point.x),
NS_lround(point.y)));
return nsEventStatus_eConsumeNoDefault;
}
return nsEventStatus_eIgnore;
@ -568,10 +571,12 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
MonitorAutoLock monitor(mMonitor);
if (mAllowZoom) {
gfxFloat resolution = CalculateResolution(mFrameMetrics).width;
gfx::Point point = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y),
mFrameMetrics.mZoom.width);
mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y)));
resolution);
mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x),
NS_lround(point.y)));
}
return nsEventStatus_eConsumeNoDefault;
@ -643,10 +648,12 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.
float inverseScale = 1 / mFrameMetrics.mZoom.width;
gfxFloat inverseResolution = 1 / CalculateResolution(mFrameMetrics).width;
int32_t xDisplacement = mX.GetDisplacementForDuration(inverseScale, timeDelta);
int32_t yDisplacement = mY.GetDisplacementForDuration(inverseScale, timeDelta);
int32_t xDisplacement = mX.GetDisplacementForDuration(inverseResolution,
timeDelta);
int32_t yDisplacement = mY.GetDisplacementForDuration(inverseResolution,
timeDelta);
if (!xDisplacement && !yDisplacement) {
return;
}
@ -681,11 +688,11 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.
float inverseScale = 1 / mFrameMetrics.mZoom.width;
gfxFloat inverseResolution = 1 / CalculateResolution(mFrameMetrics).width;
ScrollBy(gfx::Point(
mX.GetDisplacementForDuration(inverseScale, aDelta),
mY.GetDisplacementForDuration(inverseScale, aDelta)
mX.GetDisplacementForDuration(inverseResolution, aDelta),
mY.GetDisplacementForDuration(inverseResolution, aDelta)
));
RequestContentRepaint();
@ -711,23 +718,26 @@ void AsyncPanZoomController::ScrollBy(const gfx::Point& aOffset) {
void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) {
FrameMetrics metrics = mFrameMetrics;
gfx::Rect pageSize = aCSSPageRect;
float scale = mFrameMetrics.mZoom.width;
gfxFloat resolution = CalculateResolution(mFrameMetrics).width;
// The page rect is the css page rect scaled by the current zoom.
pageSize.ScaleInverseRoundOut(scale);
pageSize.ScaleInverseRoundOut(resolution);
// Round the page rect so we don't get any truncation, then get the nsIntRect
// from this.
metrics.mContentRect = nsIntRect(pageSize.x, pageSize.y, pageSize.width, pageSize.height);
metrics.mContentRect = nsIntRect(pageSize.x, pageSize.y,
pageSize.width, pageSize.height);
metrics.mScrollableRect = aCSSPageRect;
mFrameMetrics = metrics;
}
void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFocus) {
float scaleFactor = aScale / mFrameMetrics.mZoom.width;
void AsyncPanZoomController::ScaleWithFocus(float aZoom,
const nsIntPoint& aFocus) {
float zoomFactor = aZoom / mFrameMetrics.mZoom.width;
gfxFloat resolution = CalculateResolution(mFrameMetrics).width;
SetZoomAndResolution(aScale);
SetZoomAndResolution(aZoom);
// Force a recalculation of the page rect based on the new zoom and the
// current CSS page rect (which is unchanged since it's not affected by zoom).
@ -735,9 +745,11 @@ void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFoc
// If the new scale is very small, we risk multiplying in huge rounding
// errors, so don't bother adjusting the scroll offset.
if (aScale >= 0.01f) {
mFrameMetrics.mScrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / aScale;
mFrameMetrics.mScrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / aScale;
if (resolution >= 0.01f) {
mFrameMetrics.mScrollOffset.x +=
gfxFloat(aFocus.x) * (zoomFactor - 1.0) / resolution;
mFrameMetrics.mScrollOffset.y +=
gfxFloat(aFocus.y) * (zoomFactor - 1.0) / resolution;
}
}
@ -804,9 +816,9 @@ const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort(
double estimatedPaintDuration =
aEstimatedPaintDuration > EPSILON ? aEstimatedPaintDuration : 1.0;
float scale = aFrameMetrics.mZoom.width;
gfxFloat resolution = CalculateResolution(aFrameMetrics).width;
nsIntRect compositionBounds = aFrameMetrics.mCompositionBounds;
compositionBounds.ScaleInverseRoundIn(scale);
compositionBounds.ScaleInverseRoundIn(resolution);
const gfx::Rect& scrollableRect = aFrameMetrics.mScrollableRect;
gfx::Point scrollOffset = aFrameMetrics.mScrollOffset;
@ -864,6 +876,35 @@ const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort(
return displayPort;
}
/*static*/ gfxSize
AsyncPanZoomController::CalculateIntrinsicScale(const FrameMetrics& aMetrics)
{
gfxFloat intrinsicScale = (gfxFloat(aMetrics.mCompositionBounds.width) /
gfxFloat(aMetrics.mViewport.width));
return gfxSize(intrinsicScale, intrinsicScale);
}
/*static*/ gfxSize
AsyncPanZoomController::CalculateResolution(const FrameMetrics& aMetrics)
{
gfxSize intrinsicScale = CalculateIntrinsicScale(aMetrics);
gfxSize userZoom = aMetrics.mZoom;
return gfxSize(intrinsicScale.width * userZoom.width,
intrinsicScale.height * userZoom.height);
}
/*static*/ gfx::Rect
AsyncPanZoomController::CalculateCompositedRectInCssPixels(const FrameMetrics& aMetrics)
{
gfxSize resolution = CalculateResolution(aMetrics);
gfx::Rect rect(aMetrics.mCompositionBounds.x,
aMetrics.mCompositionBounds.y,
aMetrics.mCompositionBounds.width,
aMetrics.mCompositionBounds.height);
rect.ScaleInverseRoundIn(resolution.width, resolution.height);
return rect;
}
void AsyncPanZoomController::SetDPI(int aDPI) {
mDPI = aDPI;
}
@ -916,27 +957,26 @@ void AsyncPanZoomController::RequestContentRepaint() {
return;
}
// Cache the resolution since we're temporarily changing it to accomodate
// mixed resolution/zoom (normally we make them the same thing).
float actualResolution = mFrameMetrics.mResolution.width;
// Cache the zoom since we're temporarily changing it for
// acceleration-scaled painting.
gfxFloat actualZoom = mFrameMetrics.mZoom.width;
// Calculate the factor of acceleration based on the faster of the two axes.
float accelerationFactor =
clamped(NS_MAX(mX.GetAccelerationFactor(), mY.GetAccelerationFactor()),
float(MIN_ZOOM) / 2.0f, float(MAX_ZOOM));
// Scale down the resolution a bit based on acceleration.
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
actualResolution / accelerationFactor;
mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height =
actualZoom / accelerationFactor;
// This message is compressed, so fire whether or not we already have a paint
// queued up. We need to know whether or not a paint was requested anyways,
// ofr the purposes of content calling window.scrollTo().
// for the purposes of content calling window.scrollTo().
mGeckoContentController->RequestContentRepaint(mFrameMetrics);
mLastPaintRequestMetrics = mFrameMetrics;
mWaitingForContentToPaint = true;
// Set the resolution back to what it was for the purpose of logic control.
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
actualResolution;
// Set the zoom back to what it was for the purpose of logic control.
mFrameMetrics.mZoom = gfxSize(actualZoom, actualZoom);
}
bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
@ -962,11 +1002,10 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
{
MonitorAutoLock mon(mMonitor);
switch (mState)
{
switch (mState) {
case FLING:
// If a fling is currently happening, apply it now. We can pull the updated
// metrics afterwards.
// If a fling is currently happening, apply it now. We can pull
// the updated metrics afterwards.
requestAnimationFrame |= DoFling(aSampleTime - mLastSampleTime);
break;
case ANIMATING_ZOOM: {
@ -974,11 +1013,15 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
if (animPosition > 1.0) {
animPosition = 1.0;
}
// Sample the zoom at the current time point. The sampled zoom
// will affect the final computed resolution.
double sampledPosition = gComputedTimingFunction->GetValue(animPosition);
mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height =
mEndZoomToMetrics.mZoom.width * sampledPosition +
mStartZoomToMetrics.mZoom.width * (1 - sampledPosition);
gfxFloat startZoom = mStartZoomToMetrics.mZoom.width;
gfxFloat endZoom = mEndZoomToMetrics.mZoom.width;
gfxFloat sampledZoom = (endZoom * sampledPosition +
startZoom * (1 - sampledPosition));
mFrameMetrics.mZoom = gfxSize(sampledZoom, sampledZoom);
mFrameMetrics.mScrollOffset = gfx::Point(
mEndZoomToMetrics.mScrollOffset.x * sampledPosition +
@ -1002,12 +1045,13 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
break;
}
// Current local transform; this is not what's painted but rather what PZC has
// transformed due to touches like panning or pinching. Eventually, the root
// layer transform will become this during runtime, but we must wait for Gecko
// to repaint.
localScaleX = mFrameMetrics.mZoom.width;
localScaleY = mFrameMetrics.mZoom.height;
// Current local transform; this is not what's painted but rather
// what PZC has transformed due to touches like panning or
// pinching. Eventually, the root layer transform will become this
// during runtime, but we must wait for Gecko to repaint.
gfxSize localScale = CalculateResolution(mFrameMetrics);
localScaleX = localScale.width;
localScaleY = localScale.height;
if (frame.IsScrollable()) {
metricsScrollOffset = frame.GetScrollOffsetInLayerPixels();
@ -1073,6 +1117,16 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
}
mWaitingForContentToPaint = false;
bool needContentRepaint = false;
if (aViewportFrame.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width &&
aViewportFrame.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) {
// Remote content has sync'd up to the composition geometry
// change, so we can accept the viewport it's calculated.
gfxSize previousResolution = CalculateResolution(mFrameMetrics);
mFrameMetrics.mViewport = aViewportFrame.mViewport;
gfxSize newResolution = CalculateResolution(mFrameMetrics);
needContentRepaint |= (previousResolution != newResolution);
}
if (aIsFirstPaint || mFrameMetrics.IsDefault()) {
mPreviousPaintDurations.Clear();
@ -1080,14 +1134,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
mX.CancelTouch();
mY.CancelTouch();
// The composition bounds are not stored within the layers code, so we have
// to reset them back to what they were every time we overwrite them.
nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
mFrameMetrics = aViewportFrame;
mFrameMetrics.mCompositionBounds = compositionBounds;
// On first paint, we want to bring zoom back in sync with resolution.
mFrameMetrics.mZoom = mFrameMetrics.mResolution;
SetPageRect(mFrameMetrics.mScrollableRect);
mState = NOTHING;
@ -1095,6 +1143,10 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
mFrameMetrics.mScrollableRect = aViewportFrame.mScrollableRect;
SetPageRect(mFrameMetrics.mScrollableRect);
}
if (needContentRepaint) {
RequestContentRepaint();
}
}
const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
@ -1114,11 +1166,7 @@ void AsyncPanZoomController::UpdateCompositionBounds(const nsIntRect& aCompositi
// has gone out of view, the buffer will be cleared elsewhere anyways.
if (aCompositionBounds.width && aCompositionBounds.height &&
oldCompositionBounds.width && oldCompositionBounds.height) {
// Alter the zoom such that we can see the same width of the page as we used
// to be able to.
SetZoomAndResolution(mFrameMetrics.mResolution.width *
aCompositionBounds.width /
oldCompositionBounds.width);
SetZoomAndResolution(mFrameMetrics.mZoom.width);
// Repaint on a rotation so that our new resolution gets properly updated.
RequestContentRepaint();
@ -1143,13 +1191,15 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
gfx::Rect cssPageRect = mFrameMetrics.mScrollableRect;
gfx::Point scrollOffset = mFrameMetrics.mScrollOffset;
gfxSize resolution = CalculateResolution(mFrameMetrics);
// If the rect is empty, treat it as a request to zoom out to the full page
// size.
if (zoomToRect.IsEmpty()) {
// composition bounds in CSS coordinates
nsIntRect cssCompositionBounds = compositionBounds;
cssCompositionBounds.ScaleInverseRoundIn(mFrameMetrics.mZoom.width);
cssCompositionBounds.ScaleInverseRoundIn(resolution.width,
resolution.height);
cssCompositionBounds.MoveBy(scrollOffset.x, scrollOffset.y);
float y = mFrameMetrics.mScrollOffset.y;
@ -1182,24 +1232,25 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
zoomToRect = zoomToRect.Intersect(cssPageRect);
}
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
NS_MIN(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height);
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
clamped(float(mEndZoomToMetrics.mZoom.width),
mMinZoom,
mMaxZoom);
gfxFloat targetResolution =
NS_MIN(compositionBounds.width / zoomToRect.width,
compositionBounds.height / zoomToRect.height);
gfxFloat targetZoom = clamped(float(targetResolution / resolution.width),
mMinZoom, mMaxZoom);
mEndZoomToMetrics.mZoom = gfxSize(targetZoom, targetZoom);
// Recalculate the zoom to rect using the new dimensions.
zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mZoom.width;
zoomToRect.height = compositionBounds.height / mEndZoomToMetrics.mZoom.height;
zoomToRect.width = compositionBounds.width / targetResolution;
zoomToRect.height = compositionBounds.height / targetResolution;
// Clamp the zoom to rect to the CSS rect to make sure it fits.
zoomToRect = zoomToRect.Intersect(cssPageRect);
// Do one final recalculation to get the resolution.
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
NS_MAX(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height);
targetResolution = NS_MAX(compositionBounds.width / zoomToRect.width,
compositionBounds.height / zoomToRect.height);
targetZoom = targetResolution / resolution.width;
mEndZoomToMetrics.mZoom = gfxSize(targetZoom, targetZoom);
mStartZoomToMetrics = mFrameMetrics;
mEndZoomToMetrics.mScrollOffset =
@ -1256,10 +1307,10 @@ void AsyncPanZoomController::TimeoutTouchListeners() {
ContentReceivedTouch(false);
}
void AsyncPanZoomController::SetZoomAndResolution(float aScale) {
void AsyncPanZoomController::SetZoomAndResolution(float aZoom) {
mMonitor.AssertCurrentThreadOwns();
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height = aScale;
mFrameMetrics.mZoom = gfxSize(aZoom, aZoom);
mFrameMetrics.mResolution = CalculateResolution(mFrameMetrics);
}
void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom,

View File

@ -209,6 +209,22 @@ public:
const gfx::Point& aAcceleration,
double aEstimatedPaintDuration);
/**
* Return the scale factor needed to fit the viewport in |aMetrics|
* into its compositiong bounds.
*/
static gfxSize CalculateIntrinsicScale(const FrameMetrics& aMetrics);
/**
* Return the resolution that content should be rendered at given
* the configuration in aFrameMetrics: viewport dimensions, zoom
* factor, etc. (The mResolution member of aFrameMetrics is
* ignored.)
*/
static gfxSize CalculateResolution(const FrameMetrics& aMetrics);
static gfx::Rect CalculateCompositedRectInCssPixels(const FrameMetrics& aMetrics);
protected:
/**
* Internal handler for ReceiveInputEvent(). Does all the actual work.

View File

@ -253,14 +253,10 @@ float Axis::GetOrigin() {
}
float Axis::GetCompositionLength() {
nsIntRect compositionBounds =
mAsyncPanZoomController->GetFrameMetrics().mCompositionBounds;
gfx::Rect scaledCompositionBounds =
gfx::Rect(compositionBounds.x, compositionBounds.y,
compositionBounds.width, compositionBounds.height);
scaledCompositionBounds.ScaleInverseRoundIn(
mAsyncPanZoomController->GetFrameMetrics().mZoom.width);
return GetRectLength(scaledCompositionBounds);
const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
gfx::Rect cssCompositedRect =
AsyncPanZoomController::CalculateCompositedRectInCssPixels(metrics);
return GetRectLength(cssCompositedRect);
}
float Axis::GetPageStart() {

View File

@ -637,6 +637,10 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
metrics.mMayHaveTouchListeners = aMayHaveTouchListeners;
if (nsIWidget* widget = aForFrame->GetNearestWidget()) {
widget->GetBounds(metrics.mCompositionBounds);
}
aRoot->SetFrameMetrics(metrics);
}