Bug 958111 - "Find in Page" on Android should zoom in on the highlighted result. r=margaret

This commit is contained in:
Robin Ricard 2014-02-14 11:02:05 -08:00
parent 92d8e0bdc3
commit 6523e7ec80
6 changed files with 165 additions and 142 deletions

View File

@ -6,6 +6,7 @@
</head>
<body>
<div style='float: left; width: 100%; height: 500px; margin: 0; padding: 0; border: none; background-color: rgb(250,0,0)'> </div>
<div style='float: left; width: 10%; height: 500px; margin: 0; padding: 0; border: none; background-color: rgb(250,0,0)'> </div>
<p>Text taken from Wikipedia.org</p>
<p> <b>Will be searching for this string:</b> Robocop </p>
<p>Mozilla is a free software community best known for producing the Firefox web browser. The Mozilla community uses, develops, spreads and supports Mozilla products and works to advance the goals of the Open Web described in the Mozilla Manifesto.[1] The community is supported institutionally by the Mozilla Foundation and its tax-paying subsidiary, the Mozilla Corporation.[2] </p>

View File

@ -18,8 +18,10 @@ public class testFindInPage extends PixelTest {
blockForGeckoReady();
String url = getAbsoluteUrl("/robocop/robocop_text_page.html");
loadAndPaint(url);
// Select the upper left corner of the screen
height = mDriver.getGeckoHeight()/8;
width = mDriver.getGeckoWidth()/2;
width = mDriver.getGeckoWidth()/8;
// Search that does not find the term and therefor should not pan the page
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();

View File

@ -77,6 +77,7 @@ var FindHelper = {
this._targetTab.sendViewportUpdate();
}
} else {
ZoomHelper.zoomToRect(aData.rect, -1, false, true);
this._viewportChanged = true;
}
}

View File

@ -0,0 +1,148 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var ZoomHelper = {
zoomInAndSnapToRange: function(aRange) {
// aRange is always non-null here, since a check happened previously.
let viewport = BrowserApp.selectedTab.getViewport();
let fudge = 15; // Add a bit of fudge.
let boundingElement = aRange.offsetNode;
while (!boundingElement.getBoundingClientRect && boundingElement.parentNode) {
boundingElement = boundingElement.parentNode;
}
let rect = ElementTouchHelper.getBoundingContentRect(boundingElement);
let drRect = aRange.getClientRect();
let scrollTop =
BrowserApp.selectedBrowser.contentDocument.documentElement.scrollTop ||
BrowserApp.selectedBrowser.contentDocument.body.scrollTop;
// We subtract half the height of the viewport so that we can (ideally)
// center the area of interest on the screen.
let topPos = scrollTop + drRect.top - (viewport.cssHeight / 2.0);
// Factor in the border and padding
let boundingStyle = window.getComputedStyle(boundingElement);
let leftAdjustment = parseInt(boundingStyle.paddingLeft) +
parseInt(boundingStyle.borderLeftWidth);
BrowserApp.selectedTab._mReflozPositioned = true;
rect.type = "Browser:ZoomToRect";
rect.x = Math.max(viewport.cssPageLeft, rect.x - fudge + leftAdjustment);
rect.y = Math.max(topPos, viewport.cssPageTop);
rect.w = viewport.cssWidth;
rect.h = viewport.cssHeight;
rect.animate = false;
sendMessageToJava(rect);
BrowserApp.selectedTab._mReflozPoint = null;
},
zoomOut: function() {
BrowserEventHandler.resetMaxLineBoxWidth();
sendMessageToJava({ type: "Browser:ZoomToPageWidth" });
},
isRectZoomedIn: function(aRect, aViewport) {
// This function checks to see if the area of the rect visible in the
// viewport (i.e. the "overlapArea" variable below) is approximately
// the max area of the rect we can show. It also checks that the rect
// is actually on-screen by testing the left and right edges of the rect.
// In effect, this tells us whether or not zooming in to this rect
// will significantly change what the user is seeing.
const minDifference = -20;
const maxDifference = 20;
const maxZoomAllowed = 4; // keep this in sync with mobile/android/base/ui/PanZoomController.MAX_ZOOM
let vRect = new Rect(aViewport.cssX, aViewport.cssY, aViewport.cssWidth, aViewport.cssHeight);
let overlap = vRect.intersect(aRect);
let overlapArea = overlap.width * overlap.height;
let availHeight = Math.min(aRect.width * vRect.height / vRect.width, aRect.height);
let showing = overlapArea / (aRect.width * availHeight);
let dw = (aRect.width - vRect.width);
let dx = (aRect.x - vRect.x);
if (fuzzyEquals(aViewport.zoom, maxZoomAllowed) && overlap.width / aRect.width > 0.9) {
// we're already at the max zoom and the block is not spilling off the side of the screen so that even
// if the block isn't taking up most of the viewport we can't pan/zoom in any more. return true so that we zoom out
return true;
}
return (showing > 0.9 &&
dx > minDifference && dx < maxDifference &&
dw > minDifference && dw < maxDifference);
},
/* Zoom to an element, optionally keeping a particular part of it
* in view if it is really tall.
*/
zoomToElement: function(aElement, aClickY = -1, aCanZoomOut = true, aCanScrollHorizontally = true) {
let rect = ElementTouchHelper.getBoundingContentRect(aElement);
ZoomHelper.zoomToRect(rect, aClickY, aCanZoomOut, aCanScrollHorizontally);
},
zoomToRect: function(aRect, aClickY = -1, aCanZoomOut = true, aCanScrollHorizontally = true) {
const margin = 15;
if(!aRect.h || !aRect.w) {
aRect.h = aRect.height;
aRect.w = aRect.width;
}
let viewport = BrowserApp.selectedTab.getViewport();
let bRect = new Rect(aCanScrollHorizontally ? Math.max(viewport.cssPageLeft, aRect.x - margin) : viewport.cssX,
aRect.y,
aCanScrollHorizontally ? aRect.w + 2 * margin : viewport.cssWidth,
aRect.h);
// constrict the rect to the screen's right edge
bRect.width = Math.min(bRect.width, viewport.cssPageRight - bRect.x);
// if the rect is already taking up most of the visible area and is stretching the
// width of the page, then we want to zoom out instead.
if (BrowserEventHandler.mReflozPref) {
let zoomFactor = BrowserApp.selectedTab.getZoomToMinFontSize(aElement);
bRect.width = zoomFactor <= 1.0 ? bRect.width : gScreenWidth / zoomFactor;
bRect.height = zoomFactor <= 1.0 ? bRect.height : bRect.height / zoomFactor;
if (zoomFactor == 1.0 || ZoomHelper.isRectZoomedIn(bRect, viewport)) {
if (aCanZoomOut) {
ZoomHelper.zoomOut();
}
return;
}
} else if (ZoomHelper.isRectZoomedIn(bRect, viewport)) {
if (aCanZoomOut) {
ZoomHelper.zoomOut();
}
return;
}
let rect = {};
rect.type = "Browser:ZoomToRect";
rect.x = bRect.x;
rect.y = bRect.y;
rect.w = bRect.width;
rect.h = Math.min(bRect.width * viewport.cssHeight / viewport.cssWidth, bRect.height);
if (aClickY >= 0) {
// if the block we're zooming to is really tall, and we want to keep a particular
// part of it in view, then adjust the y-coordinate of the target rect accordingly.
// the 1.2 multiplier is just a little fuzz to compensate for bRect including horizontal
// margins but not vertical ones.
let cssTapY = viewport.cssY + aClickY;
if ((bRect.height > rect.h) && (cssTapY > rect.y + (rect.h * 1.2))) {
rect.y = cssTapY - (rect.h / 2);
}
}
if (rect.w > viewport.cssWidth || rect.h > viewport.cssHeight) {
BrowserEventHandler.resetMaxLineBoxWidth();
}
sendMessageToJava(rect);
},
};

View File

@ -91,6 +91,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebappManager",
["PluginHelper", "chrome://browser/content/PluginHelper.js"],
["OfflineApps", "chrome://browser/content/OfflineApps.js"],
["Linkifier", "chrome://browser/content/Linkify.js"],
["ZoomHelper", "chrome://browser/content/ZoomHelper.js"],
["CastingApps", "chrome://browser/content/CastingApps.js"],
].forEach(function (aScript) {
let [name, script] = aScript;
@ -188,7 +189,7 @@ function doChangeMaxLineBoxWidth(aWidth) {
docViewer.changeMaxLineBoxWidth(aWidth);
if (range) {
BrowserEventHandler._zoomInAndSnapToRange(range);
ZoomHelper.zoomInAndSnapToRange(range);
} else {
// In this case, we actually didn't zoom into a specific range. It
// probably happened from a page load reflow-on-zoom event, so we
@ -1376,8 +1377,8 @@ var BrowserApp = {
let shouldZoom = Services.prefs.getBoolPref("formhelper.autozoom");
if (formHelperMode == kFormHelperModeDynamic && this.isTablet)
shouldZoom = false;
// _zoomToElement will handle not sending any message if this input is already mostly filling the screen
BrowserEventHandler._zoomToElement(focused, -1, false,
// ZoomHelper.zoomToElement will handle not sending any message if this input is already mostly filling the screen
ZoomHelper.zoomToElement(focused, -1, false,
aAllowZoom && shouldZoom && !ViewportHandler.getViewportMetadata(aBrowser.contentWindow).isSpecified);
}
},
@ -2929,16 +2930,16 @@ Tab.prototype = {
* 2a. PerformReflowOnZoom() and resetMaxLineBoxWidth() schedule a call to
* doChangeMaxLineBoxWidth, based on a timeout specified in preferences.
* 3. doChangeMaxLineBoxWidth changes the line box width (which also
* schedules a reflow event), and then calls _zoomInAndSnapToRange.
* 4. _zoomInAndSnapToRange performs the positioning of reflow-on-zoom and
* then re-enables painting.
* schedules a reflow event), and then calls ZoomHelper.zoomInAndSnapToRange.
* 4. ZoomHelper.zoomInAndSnapToRange performs the positioning of reflow-on-zoom
* and then re-enables painting.
*
* Some of the events happen synchronously, while others happen asynchronously.
* The following is a rough sketch of the progression of events:
*
* double tap event seen -> onDoubleTap() -> ... asynchronous ...
* -> setViewport() -> performReflowOnZoom() -> ... asynchronous ...
* -> doChangeMaxLineBoxWidth() -> _zoomInAndSnapToRange()
* -> doChangeMaxLineBoxWidth() -> ZoomHelper.zoomInAndSnapToRange()
* -> ... asynchronous ... -> setViewport() -> Observe('after-viewport-change')
* -> resumePainting()
*/
@ -4578,41 +4579,6 @@ var BrowserEventHandler = {
}
},
_zoomOut: function() {
BrowserEventHandler.resetMaxLineBoxWidth();
sendMessageToJava({ type: "Browser:ZoomToPageWidth" });
},
_isRectZoomedIn: function(aRect, aViewport) {
// This function checks to see if the area of the rect visible in the
// viewport (i.e. the "overlapArea" variable below) is approximately
// the max area of the rect we can show. It also checks that the rect
// is actually on-screen by testing the left and right edges of the rect.
// In effect, this tells us whether or not zooming in to this rect
// will significantly change what the user is seeing.
const minDifference = -20;
const maxDifference = 20;
const maxZoomAllowed = 4; // keep this in sync with mobile/android/base/ui/PanZoomController.MAX_ZOOM
let vRect = new Rect(aViewport.cssX, aViewport.cssY, aViewport.cssWidth, aViewport.cssHeight);
let overlap = vRect.intersect(aRect);
let overlapArea = overlap.width * overlap.height;
let availHeight = Math.min(aRect.width * vRect.height / vRect.width, aRect.height);
let showing = overlapArea / (aRect.width * availHeight);
let dw = (aRect.width - vRect.width);
let dx = (aRect.x - vRect.x);
if (fuzzyEquals(aViewport.zoom, maxZoomAllowed) && overlap.width / aRect.width > 0.9) {
// we're already at the max zoom and the block is not spilling off the side of the screen so that even
// if the block isn't taking up most of the viewport we can't pan/zoom in any more. return true so that we zoom out
return true;
}
return (showing > 0.9 &&
dx > minDifference && dx < maxDifference &&
dw > minDifference && dw < maxDifference);
},
onDoubleTap: function(aData) {
let data = JSON.parse(aData);
let element = ElementTouchHelper.anyElementFromPoint(data.x, data.y);
@ -4643,7 +4609,7 @@ var BrowserEventHandler = {
}
if (!element) {
this._zoomOut();
ZoomHelper.zoomOut();
return;
}
@ -4651,9 +4617,9 @@ var BrowserEventHandler = {
element = element.parentNode;
if (!element) {
this._zoomOut();
ZoomHelper.zoomOut();
} else {
this._zoomToElement(element, data.y);
ZoomHelper.zoomToElement(element, data.y);
}
},
@ -4679,102 +4645,6 @@ var BrowserEventHandler = {
return false;
},
/* Zoom to an element, optionally keeping a particular part of it
* in view if it is really tall.
*/
_zoomToElement: function(aElement, aClickY = -1, aCanZoomOut = true, aCanScrollHorizontally = true) {
const margin = 15;
let rect = ElementTouchHelper.getBoundingContentRect(aElement);
let viewport = BrowserApp.selectedTab.getViewport();
let bRect = new Rect(aCanScrollHorizontally ? Math.max(viewport.cssPageLeft, rect.x - margin) : viewport.cssX,
rect.y,
aCanScrollHorizontally ? rect.w + 2 * margin : viewport.cssWidth,
rect.h);
// constrict the rect to the screen's right edge
bRect.width = Math.min(bRect.width, viewport.cssPageRight - bRect.x);
// if the rect is already taking up most of the visible area and is stretching the
// width of the page, then we want to zoom out instead.
if (BrowserEventHandler.mReflozPref) {
let zoomFactor = BrowserApp.selectedTab.getZoomToMinFontSize(aElement);
bRect.width = zoomFactor <= 1.0 ? bRect.width : gScreenWidth / zoomFactor;
bRect.height = zoomFactor <= 1.0 ? bRect.height : bRect.height / zoomFactor;
if (zoomFactor == 1.0 || this._isRectZoomedIn(bRect, viewport)) {
if (aCanZoomOut) {
this._zoomOut();
}
return;
}
} else if (this._isRectZoomedIn(bRect, viewport)) {
if (aCanZoomOut) {
this._zoomOut();
}
return;
}
rect.type = "Browser:ZoomToRect";
rect.x = bRect.x;
rect.y = bRect.y;
rect.w = bRect.width;
rect.h = Math.min(bRect.width * viewport.cssHeight / viewport.cssWidth, bRect.height);
if (aClickY >= 0) {
// if the block we're zooming to is really tall, and we want to keep a particular
// part of it in view, then adjust the y-coordinate of the target rect accordingly.
// the 1.2 multiplier is just a little fuzz to compensate for bRect including horizontal
// margins but not vertical ones.
let cssTapY = viewport.cssY + aClickY;
if ((bRect.height > rect.h) && (cssTapY > rect.y + (rect.h * 1.2))) {
rect.y = cssTapY - (rect.h / 2);
}
}
if (rect.w > viewport.cssWidth || rect.h > viewport.cssHeight) {
BrowserEventHandler.resetMaxLineBoxWidth();
}
sendMessageToJava(rect);
},
_zoomInAndSnapToRange: function(aRange) {
// aRange is always non-null here, since a check happened previously.
let viewport = BrowserApp.selectedTab.getViewport();
let fudge = 15; // Add a bit of fudge.
let boundingElement = aRange.offsetNode;
while (!boundingElement.getBoundingClientRect && boundingElement.parentNode) {
boundingElement = boundingElement.parentNode;
}
let rect = ElementTouchHelper.getBoundingContentRect(boundingElement);
let drRect = aRange.getClientRect();
let scrollTop =
BrowserApp.selectedBrowser.contentDocument.documentElement.scrollTop ||
BrowserApp.selectedBrowser.contentDocument.body.scrollTop;
// We subtract half the height of the viewport so that we can (ideally)
// center the area of interest on the screen.
let topPos = scrollTop + drRect.top - (viewport.cssHeight / 2.0);
// Factor in the border and padding
let boundingStyle = window.getComputedStyle(boundingElement);
let leftAdjustment = parseInt(boundingStyle.paddingLeft) +
parseInt(boundingStyle.borderLeftWidth);
BrowserApp.selectedTab._mReflozPositioned = true;
rect.type = "Browser:ZoomToRect";
rect.x = Math.max(viewport.cssPageLeft, rect.x - fudge + leftAdjustment);
rect.y = Math.max(topPos, viewport.cssPageTop);
rect.w = viewport.cssWidth;
rect.h = viewport.cssHeight;
rect.animate = false;
sendMessageToJava(rect);
BrowserApp.selectedTab._mReflozPoint = null;
},
onPinchFinish: function(aData) {
let data = {};
try {

View File

@ -52,6 +52,7 @@ chrome.jar:
content/FeedHandler.js (content/FeedHandler.js)
content/Feedback.js (content/Feedback.js)
content/Linkify.js (content/Linkify.js)
content/ZoomHelper.js (content/ZoomHelper.js)
content/CastingApps.js (content/CastingApps.js)
#ifdef MOZ_SERVICES_HEALTHREPORT
content/aboutHealthReport.xhtml (content/aboutHealthReport.xhtml)