Bug 716403 - Resize viewport dynamically on Android. r=kats,mfinkle

This causes the viewport size to differ, depending on the length of the page.
This has the effect of pages that size themselves to the size of the window
always having the toolbar visible, making sites like Google Maps more usable.
This commit is contained in:
Chris Lord 2013-03-07 10:17:33 +00:00
parent 5894f416fc
commit 65f2a90d9f
4 changed files with 119 additions and 21 deletions

View File

@ -43,6 +43,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.TimerTask;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
public class BrowserToolbar implements ViewSwitcher.ViewFactory,
Tabs.OnTabsChangedListener,
GeckoMenu.ActionItemBarPresenter,
@ -485,6 +487,23 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
}
}
private boolean canToolbarHide() {
// Forbid the toolbar from hiding if hiding the toolbar would cause
// the page to go into overscroll.
ImmutableViewportMetrics metrics = GeckoApp.mAppContext.getLayerView().
getLayerClient().getViewportMetrics();
return (metrics.getPageHeight() >= metrics.getHeight());
}
private void startVisibilityAnimation() {
// Only start the animation if we're showing the toolbar, or it's ok
// to hide it.
if (mVisibility == ToolbarVisibility.VISIBLE ||
canToolbarHide()) {
mVisibilityAnimator.start();
}
}
public void animateVisibility(boolean show, long delay) {
// Do nothing if there's a delayed animation pending that does the
// same thing and this request also has a delay.
@ -503,13 +522,13 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
if (delay > 0) {
mDelayedVisibilityTask = new TimerTask() {
public void run() {
mVisibilityAnimator.start();
startVisibilityAnimation();
mDelayedVisibilityTask = null;
}
};
mLayout.postDelayed(mDelayedVisibilityTask, delay);
} else {
mVisibilityAnimator.start();
startVisibilityAnimation();
}
}

View File

@ -45,6 +45,13 @@ public class BrowserToolbarLayout extends LinearLayout {
super.onSizeChanged(w, h, oldw, oldh);
if (h != oldh) {
// In the current UI, this is the only place we have need of
// viewport margins (to stop the toolbar from obscuring fixed-pos
// content).
GeckoAppShell.sendEventToGecko(
GeckoEvent.createBroadcastEvent("Viewport:FixedMarginsChanged",
"{ \"top\" : " + h + ", \"right\" : 0, \"bottom\" : 0, \"left\" : 0 }"));
refreshMargins();
}
}

View File

@ -909,15 +909,21 @@ class JavaPanZoomController
// Ensure minZoomFactor keeps the page at least as big as the viewport.
if (pageRect.width() > 0) {
float scaleFactor = viewport.width() / pageRect.width();
float pageWidth = pageRect.width() +
viewportMetrics.fixedLayerMarginLeft +
viewportMetrics.fixedLayerMarginRight;
float scaleFactor = viewport.width() / pageWidth;
minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor);
if (viewport.width() > pageRect.width())
if (viewport.width() > pageWidth)
focusX = 0.0f;
}
if (pageRect.height() > 0) {
float scaleFactor = viewport.height() / pageRect.height();
float pageHeight = pageRect.height() +
viewportMetrics.fixedLayerMarginTop +
viewportMetrics.fixedLayerMarginBottom;
float scaleFactor = viewport.height() / pageHeight;
minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor);
if (viewport.height() > pageRect.height())
if (viewport.height() > pageHeight)
focusY = 0.0f;
}

View File

@ -205,6 +205,7 @@ var BrowserApp = {
Services.obs.addObserver(this, "FullScreen:Exit", false);
Services.obs.addObserver(this, "Viewport:Change", false);
Services.obs.addObserver(this, "Viewport:Flush", false);
Services.obs.addObserver(this, "Viewport:FixedMarginsChanged", false);
Services.obs.addObserver(this, "Passwords:Init", false);
Services.obs.addObserver(this, "FormHistory:Init", false);
Services.obs.addObserver(this, "ToggleProfiling", false);
@ -1252,6 +1253,10 @@ var BrowserApp = {
sendMessageToJava({ type: "Telemetry:Gather" });
break;
case "Viewport:FixedMarginsChanged":
gViewportMargins = JSON.parse(aData);
break;
default:
dump('BrowserApp.observe: unexpected topic "' + aTopic + '"\n');
break;
@ -2785,6 +2790,12 @@ nsBrowserAccess.prototype = {
let gScreenWidth = 1;
let gScreenHeight = 1;
// The margins that should be applied to the viewport for fixed position
// children. This is used to avoid browser chrome permanently obscuring
// fixed position content, and also to make sure window-sized pages take
// into account said browser chrome.
let gViewportMargins = { top: 0, right: 0, bottom: 0, left: 0};
function Tab(aURL, aParams) {
this.browser = null;
this.id = 0;
@ -2793,6 +2804,9 @@ function Tab(aURL, aParams) {
this._zoom = 1.0;
this._drawZoom = 1.0;
this.userScrollPos = { x: 0, y: 0 };
this.viewportExcludesHorizontalMargins = true;
this.viewportExcludesVerticalMargins = true;
this.updatingViewportForPageSizeChange = false;
this.contentDocumentIsDisplayed = true;
this.pluginDoorhangerTimeout = null;
this.shouldShowPluginDoorhanger = true;
@ -3261,15 +3275,34 @@ Tab.prototype = {
setScrollClampingSize: function(zoom) {
let viewportWidth = gScreenWidth / zoom;
let viewportHeight = gScreenHeight / zoom;
let screenWidth = gScreenWidth;
let screenHeight = gScreenHeight;
let [pageWidth, pageHeight] = this.getPageSize(this.browser.contentDocument,
viewportWidth, viewportHeight);
// Check if the page would fit into either of the viewport dimensions minus
// the margins and shrink the screen size accordingly so that the aspect
// ratio calculation below works correctly in these situations.
// We take away the margin size over two to account for rounding errors,
// as the browser size set in updateViewportSize doesn't allow for any
// size between these two values (and thus anything between them is
// attributable to rounding error).
if ((pageHeight * zoom) < gScreenHeight - (gViewportMargins.top + gViewportMargins.bottom) / 2) {
screenHeight = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom;
viewportHeight = screenHeight / zoom;
}
if ((pageWidth * zoom) < gScreenWidth - (gViewportMargins.left + gViewportMargins.right) / 2) {
screenWidth = gScreenWidth - gViewportMargins.left - gViewportMargins.right;
viewportWidth = screenWidth / zoom;
}
// Make sure the aspect ratio of the screen is maintained when setting
// the clamping scroll-port size.
let factor = Math.min(viewportWidth / gScreenWidth, pageWidth / gScreenWidth,
viewportHeight / gScreenHeight, pageHeight / gScreenHeight);
let scrollPortWidth = gScreenWidth * factor;
let scrollPortHeight = gScreenHeight * factor;
let factor = Math.min(viewportWidth / screenWidth, pageWidth / screenWidth,
viewportHeight / screenHeight, pageHeight / screenHeight);
let scrollPortWidth = Math.min(screenWidth * factor, pageWidth * zoom);
let scrollPortHeight = Math.min(screenHeight * factor, pageHeight * zoom);
let win = this.browser.contentWindow;
win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).
@ -3329,20 +3362,23 @@ Tab.prototype = {
},
getViewport: function() {
let screenW = gScreenWidth - gViewportMargins.left - gViewportMargins.right;
let screenH = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom;
let viewport = {
width: gScreenWidth,
height: gScreenHeight,
cssWidth: gScreenWidth / this._zoom,
cssHeight: gScreenHeight / this._zoom,
width: screenW,
height: screenH,
cssWidth: screenW / this._zoom,
cssHeight: screenH / this._zoom,
pageLeft: 0,
pageTop: 0,
pageRight: gScreenWidth,
pageBottom: gScreenHeight,
pageRight: screenW,
pageBottom: screenH,
// We make up matching css page dimensions
cssPageLeft: 0,
cssPageTop: 0,
cssPageRight: gScreenWidth / this._zoom,
cssPageBottom: gScreenHeight / this._zoom,
cssPageRight: screenW / this._zoom,
cssPageBottom: screenH / this._zoom,
zoom: this._zoom,
};
@ -3392,6 +3428,20 @@ Tab.prototype = {
let displayPort = getBridge().getDisplayPort(aPageSizeUpdate, BrowserApp.isBrowserContentDocumentDisplayed(), this.id, viewport);
if (displayPort != null)
this.setDisplayPort(displayPort);
// If the page size has changed so that it might or might not fit on the
// screen with the margins included, run updateViewportSize to resize the
// browser accordingly. The -1 is to account for rounding errors.
if (!this.updatingViewportForPageSizeChange) {
this.updatingViewportForPageSizeChange = true;
if (((viewport.pageBottom - viewport.pageTop <= gScreenHeight - 1) !=
this.viewportExcludesVerticalMargins) ||
((viewport.pageRight - viewport.pageLeft <= gScreenWidth - 1) !=
this.viewportExcludesHorizontalMargins)) {
this.updateViewportSize(gScreenWidth);
}
this.updatingViewportForPageSizeChange = false;
}
},
handleEvent: function(aEvent) {
@ -3818,9 +3868,11 @@ Tab.prototype = {
if (!browser)
return;
let screenW = gScreenWidth;
let screenH = gScreenHeight;
let screenW = gScreenWidth - gViewportMargins.left - gViewportMargins.right;
let screenH = gScreenHeight - gViewportMargins.top - gViewportMargins.bottom;
let viewportW, viewportH;
this.viewportExcludesHorizontalMargins = true;
this.viewportExcludesVerticalMargins = true;
let metadata = this.metadata;
if (metadata.autoSize) {
@ -3875,7 +3927,21 @@ Tab.prototype = {
// this may get run during a Viewport:Change message while the document
// has not yet loaded, so need to guard against a null document.
let [pageWidth, pageHeight] = this.getPageSize(this.browser.contentDocument, viewportW, viewportH);
minScale = gScreenWidth / pageWidth;
minScale = screenW / pageWidth;
// In the situation the page size exceeds the screen size minus the
// viewport margins on either axis, lengthen the viewport on the
// corresponding axis to include the margins.
// The +1 is to account for rounding errors.
if (pageWidth * this._zoom >= screenW + 1) {
screenW = gScreenWidth;
this.viewportExcludesHorizontalMargins = false;
}
if (pageHeight * this._zoom >= screenH + 1) {
screenH = gScreenHeight;
this.viewportExcludesVerticalMargins = false;
}
}
minScale = this.clampZoom(minScale);
viewportH = Math.max(viewportH, screenH / minScale);