Bug 1416319 - 8. Switch to using LayerSession coordinates APIs; r=rbarker

Use the LayerSession coordinates APIs instead of manually calculating
coordinates using viewport metrics and toolbar height, which is prone to
error.

MozReview-Commit-ID: 4ebI3BHEOXR

--HG--
extra : rebase_source : 264f03f3032f101687c2a39d1cf052ac1805f12b
This commit is contained in:
Jim Chen 2017-11-22 14:12:23 -05:00
Родитель 0c82587e52
Коммит 82c5935b9b
11 изменённых файлов: 138 добавлений и 159 удалений

Просмотреть файл

@ -96,7 +96,6 @@ import org.mozilla.gecko.extensions.ExtensionPermissionsHelper;
import org.mozilla.gecko.firstrun.FirstrunAnimationContainer;
import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.home.BrowserSearch;
import org.mozilla.gecko.home.HomeBanner;
import org.mozilla.gecko.home.HomeConfig;
@ -4358,7 +4357,7 @@ public class BrowserApp extends GeckoApp
// If the toolbar is dynamic and not currently showing, just show the real toolbar
// and keep the animated snapshot hidden
if (mDynamicToolbar.isEnabled() && toolbar.getCurrentToolbarHeight() == 0) {
if (mDynamicToolbar.isEnabled() && !isToolbarChromeVisible()) {
toggleToolbarChrome(true);
mShowingToolbarChromeForActionBar = true;
}

Просмотреть файл

@ -6,8 +6,6 @@
package org.mozilla.gecko;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
@ -18,7 +16,9 @@ import org.mozilla.gecko.widget.SwipeDismissListViewTouchListener.OnDismissCallb
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
@ -254,10 +254,6 @@ public class FormAssistPopup extends RelativeLayout implements BundleEventListen
}
private void positionAndShowPopup() {
positionAndShowPopup(mGeckoView.getSession().getViewportMetrics());
}
private void positionAndShowPopup(ImmutableViewportMetrics aMetrics) {
ThreadUtils.assertOnUiThread();
// Don't show the form assist popup when using fullscreen VKB
@ -282,34 +278,30 @@ public class FormAssistPopup extends RelativeLayout implements BundleEventListen
sValidationMessageHeight = (int) (res.getDimension(R.dimen.validation_message_height));
}
float zoom = aMetrics.zoomFactor;
// These values correspond to the input box for which we want to
// display the FormAssistPopup.
int left = (int) (mX * zoom - aMetrics.viewportRectLeft);
int top = (int) (mY * zoom - aMetrics.viewportRectTop + mGeckoView.getTop() +
mGeckoView.getDynamicToolbarAnimator().getCurrentToolbarHeight());
int width = (int) (mW * zoom);
int height = (int) (mH * zoom);
final Matrix matrix = new Matrix();
final RectF input = new RectF((float) mX, (float) mY,
(float) (mX + mW), (float) (mY + mH));
mGeckoView.getSession().getClientToSurfaceMatrix(matrix);
matrix.mapRect(input);
final Rect page = new Rect();
mGeckoView.getSession().getSurfaceBounds(page);
int popupWidth = LayoutParams.MATCH_PARENT;
int popupLeft = left < 0 ? 0 : left;
FloatSize viewport = aMetrics.getSize();
int popupLeft = Math.max((int) input.left, page.left);
// For autocomplete suggestions, if the input is smaller than the screen-width,
// shrink the popup's width. Otherwise, keep it as MATCH_PARENT.
if ((mPopupType == PopupType.AUTOCOMPLETE) && (left + width) < viewport.width) {
popupWidth = left < 0 ? left + width : width;
if ((mPopupType == PopupType.AUTOCOMPLETE) && (int) input.right < page.right) {
// Ensure the popup has a minimum width.
if (popupWidth < sAutoCompleteMinWidth) {
popupWidth = sAutoCompleteMinWidth;
final int visibleWidth = (int) input.right - popupLeft;
popupWidth = Math.max(visibleWidth, sAutoCompleteMinWidth);
// Move the popup to the left if there isn't enough room for it.
if ((popupLeft + popupWidth) > viewport.width) {
popupLeft = (int) (viewport.width - popupWidth);
}
// Move the popup to the left if there isn't enough room for it.
if ((popupLeft + popupWidth) > page.right) {
popupLeft = Math.max(page.right - popupWidth, page.left);
}
}
@ -326,7 +318,7 @@ public class FormAssistPopup extends RelativeLayout implements BundleEventListen
popupHeight = sValidationMessageHeight;
}
int popupTop = top + height;
int popupTop = (int) input.bottom;
if (mPopupType == PopupType.VALIDATIONMESSAGE) {
mValidationMessageText.setLayoutParams(sValidationTextLayoutNormal);
@ -336,20 +328,15 @@ public class FormAssistPopup extends RelativeLayout implements BundleEventListen
// If the popup doesn't fit below the input box, shrink its height, or
// see if we can place it above the input instead.
if ((popupTop + popupHeight) > (mGeckoView.getTop() + viewport.height)) {
if ((popupTop + popupHeight) > page.bottom) {
// Find where the maximum space is, and put the popup there.
if ((viewport.height - popupTop) > top) {
if ((page.bottom - (int) input.bottom) > ((int) input.top - page.top)) {
// Shrink the height to fit it below the input box.
popupHeight = (int) (viewport.height - popupTop);
popupHeight = page.bottom - (int) input.bottom;
} else {
if (popupHeight < top) {
// No shrinking needed to fit on top.
popupTop = (top - popupHeight);
} else {
// Shrink to available space on top.
popupTop = 0;
popupHeight = top;
}
// Shrink to available space on top if needed.
popupTop = Math.max((int) input.top - popupHeight, page.top);
popupHeight = (int) input.top - popupTop;
if (mPopupType == PopupType.VALIDATIONMESSAGE) {
mValidationMessageText.setLayoutParams(sValidationTextLayoutInverted);

Просмотреть файл

@ -12,6 +12,7 @@ import org.mozilla.gecko.util.ThreadUtils;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
@ -174,12 +175,14 @@ public class GeckoAccessibility {
Rect relativeBounds = new Rect(bounds.getInt("left"), bounds.getInt("top"),
bounds.getInt("right"), bounds.getInt("bottom"));
sVirtualCursorNode.setBoundsInParent(relativeBounds);
int[] locationOnScreen = new int[2];
view.getLocationOnScreen(locationOnScreen);
locationOnScreen[1] += view.getDynamicToolbarAnimator().getCurrentToolbarHeight();
Rect screenBounds = new Rect(relativeBounds);
screenBounds.offset(locationOnScreen[0], locationOnScreen[1]);
sVirtualCursorNode.setBoundsInScreen(screenBounds);
final Matrix matrix = new Matrix();
final float[] origin = new float[2];
view.getSession().getClientToScreenMatrix(matrix);
matrix.mapPoints(origin);
relativeBounds.offset((int) origin[0], (int) origin[1]);
sVirtualCursorNode.setBoundsInScreen(relativeBounds);
}
final GeckoBundle braille = message.getBundle("brailleOutput");
@ -192,23 +195,17 @@ public class GeckoAccessibility {
sHoverEnter = message;
}
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
event.setClassName(GeckoAccessibility.class.getName());
if (eventType == AccessibilityEvent.TYPE_ANNOUNCEMENT ||
eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
event.setSource(view, View.NO_ID);
} else {
event.setSource(view, VIRTUAL_CURSOR_POSITION);
}
populateEventFromJSON(event, message);
((ViewParent) view).requestSendAccessibilityEvent(view, event);
}
});
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
event.setClassName(GeckoAccessibility.class.getName());
if (eventType == AccessibilityEvent.TYPE_ANNOUNCEMENT ||
eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
event.setSource(view, View.NO_ID);
} else {
event.setSource(view, VIRTUAL_CURSOR_POSITION);
}
populateEventFromJSON(event, message);
((ViewParent) view).requestSendAccessibilityEvent(view, event);
}
}

Просмотреть файл

@ -1050,11 +1050,11 @@ public abstract class GeckoApp extends GeckoActivity
GeckoAccessibility.setDelegate(mLayerView);
getAppEventDispatcher().registerGeckoThreadListener(this,
"Accessibility:Event",
"Locale:Set",
null);
getAppEventDispatcher().registerUiThreadListener(this,
"Accessibility:Event",
"Contact:Add",
"DevToolsAuth:Scan",
"DOMFullScreen:Start",
@ -2049,11 +2049,11 @@ public abstract class GeckoApp extends GeckoActivity
null);
getAppEventDispatcher().unregisterGeckoThreadListener(this,
"Accessibility:Event",
"Locale:Set",
null);
getAppEventDispatcher().unregisterUiThreadListener(this,
"Accessibility:Event",
"Contact:Add",
"DevToolsAuth:Scan",
"DOMFullScreen:Start",

Просмотреть файл

@ -6,7 +6,9 @@ package org.mozilla.gecko.text;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.Log;
import android.util.TypedValue;
@ -168,20 +170,23 @@ public class FloatingToolbarTextSelection implements TextSelection, BundleEventL
}
private void updateRect(final GeckoBundle message) {
final double x = message.getDouble("x");
final double y = (int) message.getDouble("y");
final double width = (int) message.getDouble("width");
final double height = (int) message.getDouble("height");
final float x = (float) message.getDouble("x");
final float y = (float) message.getDouble("y");
final float width = (float) message.getDouble("width");
final float height = (float) message.getDouble("height");
final float toolbarOffset = geckoView.getDynamicToolbarAnimator().getCurrentToolbarHeight();
final float zoomFactor = geckoView.getSession().getViewportMetrics().zoomFactor;
geckoView.getLocationInWindow(locationInWindow);
final Matrix matrix = new Matrix();
final RectF rect = new RectF(x, y, x + width, y + height);
geckoView.getSession().getClientToScreenMatrix(matrix);
matrix.mapRect(rect);
contentRect = new Rect(
(int) (x * zoomFactor + locationInWindow[0]),
(int) (y * zoomFactor + locationInWindow[1] + toolbarOffset),
(int) ((x + width) * zoomFactor + locationInWindow[0]),
(int) ((y + height) * zoomFactor + locationInWindow[1] +
(height > 0 ? handlesOffset : 0)));
if ((int) height > 0) {
rect.bottom += handlesOffset;
}
if (contentRect == null) {
contentRect = new Rect();
}
rect.round(contentRect);
}
}

Просмотреть файл

@ -352,70 +352,71 @@ class GeckoInputConnection
@TargetApi(21)
@Override
public void updateCompositionRects(final RectF[] aRects) {
public void updateCompositionRects(final RectF[] rects) {
if (!(Build.VERSION.SDK_INT >= 21)) {
return;
}
final GeckoView view = getView();
if (view == null) {
return;
}
final Editable content = getEditable();
if (content == null) {
return;
}
final int composingStart = getComposingSpanStart(content);
final int composingEnd = getComposingSpanEnd(content);
if (composingStart < 0 || composingEnd < 0) {
if (DEBUG) {
Log.d(LOGTAG, "No composition for updates");
}
return;
}
final CharSequence composition = content.subSequence(composingStart, composingEnd);
view.post(new Runnable() {
@Override
public void run() {
updateCompositionRectsOnUi(view, rects, composition);
}
});
}
@TargetApi(21)
/* package */ void updateCompositionRectsOnUi(final GeckoView view,
final RectF[] rects,
final CharSequence composition) {
if (view.getSession() == null) {
return;
}
if (mCursorAnchorInfoBuilder == null) {
mCursorAnchorInfoBuilder = new CursorAnchorInfo.Builder();
}
mCursorAnchorInfoBuilder.reset();
// Calculate Gecko logical coords to screen coords
final GeckoView view = getView();
if (view == null || view.getSession() == null) {
return;
}
// First aRects element is the widget bounds in device units.
final float zoom = view.getSession().getViewportMetrics().zoomFactor;
final Matrix matrix = new Matrix();
matrix.postScale(zoom, zoom);
matrix.postTranslate(aRects[0].left, aRects[0].top);
view.getSession().getClientToScreenMatrix(matrix);
mCursorAnchorInfoBuilder.setMatrix(matrix);
final Editable content = getEditable();
if (content == null) {
return;
}
int composingStart = getComposingSpanStart(content);
int composingEnd = getComposingSpanEnd(content);
if (composingStart < 0 || composingEnd < 0) {
if (DEBUG) {
Log.d(LOGTAG, "No composition for updates");
}
return;
for (int i = 0; i < rects.length; i++) {
mCursorAnchorInfoBuilder.addCharacterBounds(
i, rects[i].left, rects[i].top, rects[i].right, rects[i].bottom,
CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION);
}
// Subsequent aRects elements are character bounds in CSS units.
for (int i = 1; i < aRects.length; i++) {
mCursorAnchorInfoBuilder.addCharacterBounds(i - 1,
aRects[i].left,
aRects[i].top,
aRects[i].right,
aRects[i].bottom,
CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION);
}
mCursorAnchorInfoBuilder.setComposingText(0, content.subSequence(composingStart, composingEnd));
updateCursor();
}
@TargetApi(21)
private void updateCursor() {
if (mCursorAnchorInfoBuilder == null) {
return;
}
mCursorAnchorInfoBuilder.setComposingText(0, composition);
final InputMethodManager imm = getInputMethodManager();
final View v = getView();
if (imm == null || v == null) {
if (imm == null) {
return;
}
imm.updateCursorAnchorInfo(v, mCursorAnchorInfoBuilder.build());
imm.updateCursorAnchorInfo(view, mCursorAnchorInfoBuilder.build());
}
@Override

Просмотреть файл

@ -133,7 +133,7 @@ public class LayerView extends FrameLayout {
// We must have a layer client to get valid viewport metrics
if (mOverscroll != null) {
mOverscroll.draw(canvas, mSession.getViewportMetrics());
mOverscroll.draw(canvas);
}
}

Просмотреть файл

@ -14,7 +14,7 @@ public interface Overscroll {
Y,
};
public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics);
public void draw(final Canvas canvas);
public void setSize(final int width, final int height);
public void setVelocity(final float velocity, final Axis axis);
public void setDistance(final float distance, final Axis axis);

Просмотреть файл

@ -8,9 +8,9 @@ package org.mozilla.gecko.gfx;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Build;
import android.widget.EdgeEffect;
@ -118,32 +118,30 @@ public class OverscrollEdgeEffect implements Overscroll {
}
@Override
public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) {
if (metrics == null || mView.mSession == null) {
public void draw(final Canvas canvas) {
if (mView.mSession == null) {
return;
}
final float width = mView.getWidth();
final float height = mView.getHeight();
final float toolbarEnd = mView.mSession.getDynamicToolbarAnimator()
.getCurrentToolbarHeight();
final Rect pageRect = new Rect();
mView.mSession.getSurfaceBounds(pageRect);
// If we're pulling an edge, or fading it out, draw!
boolean invalidate = false;
if (!mEdges[TOP].isFinished()) {
invalidate |= draw(mEdges[TOP], canvas, 0, toolbarEnd, 0);
invalidate |= draw(mEdges[TOP], canvas, pageRect.left, pageRect.top, 0);
}
if (!mEdges[BOTTOM].isFinished()) {
invalidate |= draw(mEdges[BOTTOM], canvas, width, height, 180);
invalidate |= draw(mEdges[BOTTOM], canvas, pageRect.right, pageRect.bottom, 180);
}
if (!mEdges[LEFT].isFinished()) {
invalidate |= draw(mEdges[LEFT], canvas, 0, height, 270);
invalidate |= draw(mEdges[LEFT], canvas, pageRect.left, pageRect.bottom, 270);
}
if (!mEdges[RIGHT].isFinished()) {
invalidate |= draw(mEdges[RIGHT], canvas, width, 0, 90);
invalidate |= draw(mEdges[RIGHT], canvas, pageRect.right, pageRect.top, 90);
}
// If the edge effect is animating off screen, invalidate.

Просмотреть файл

@ -22,6 +22,9 @@ var FormAssistant = {
// Whether we're in the middle of an autocomplete.
_doingAutocomplete: false,
// Last state received in "PanZoom:StateChange" observer.
_lastPanZoomState: "NOTHING",
init: function() {
Services.obs.addObserver(this, "PanZoom:StateChange");
},
@ -83,6 +86,7 @@ var FormAssistant = {
// temporarily hide the form assist popup while we're panning or zooming the page
this._hideFormAssistPopup(focused);
}
this._lastPanZoomState = aData;
break;
}
},
@ -183,8 +187,8 @@ var FormAssistant = {
case "resize": {
let focused = this.focusedElement;
if (focused && focused.ownerGlobal == aEvent.target) {
// Reposition the popup as if we just stopped pannning.
this.observe(null, "PanZoom:StateChange", "NOTHING");
// Reposition the popup as in the case of pan/zoom.
this.observe(null, "PanZoom:StateChange", this._lastPanZoomState);
// Continue to listen to resizes.
focused.ownerGlobal.addEventListener(
"resize", this, {capture: true, mozSystemGroup: true, once: true});
@ -364,11 +368,7 @@ var FormAssistant = {
document = document.defaultView.frameElement.ownerDocument;
}
let cwu = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let scrollX = {}, scrollY = {};
cwu.getScrollXY(false, scrollX, scrollY);
let scrollX = 0, scrollY = 0;
let r = aElement.getBoundingClientRect();
// step out of iframes and frames, offsetting scroll values
@ -378,13 +378,13 @@ var FormAssistant = {
let rect = frame.frameElement.getBoundingClientRect();
let left = frame.getComputedStyle(frame.frameElement).borderLeftWidth;
let top = frame.getComputedStyle(frame.frameElement).borderTopWidth;
scrollX.value += rect.left + parseInt(left);
scrollY.value += rect.top + parseInt(top);
scrollX += rect.left + parseInt(left);
scrollY += rect.top + parseInt(top);
}
return {
x: r.left + scrollX.value,
y: r.top + scrollY.value,
x: r.left + scrollX,
y: r.top + scrollY,
w: r.width,
h: r.height,
};

Просмотреть файл

@ -536,26 +536,19 @@ ConvertAndroidColor(uint32_t aArgb)
static jni::ObjectArray::LocalRef
ConvertRectArrayToJavaRectFArray(const nsTArray<LayoutDeviceIntRect>& aRects,
const LayoutDeviceIntRect& aWidgetBounds,
const CSSToLayoutDeviceScale aScale)
{
const size_t length = aRects.Length();
auto rects = jni::ObjectArray::New<sdk::RectF>(length + 1);
// First element is the widget bounds in device units.
auto widgetRect = sdk::RectF::New(aWidgetBounds.x, aWidgetBounds.y,
aWidgetBounds.x + aWidgetBounds.width,
aWidgetBounds.y + aWidgetBounds.height);
rects->SetElement(0, widgetRect);
auto rects = jni::ObjectArray::New<sdk::RectF>(length);
for (size_t i = 0; i < length; i++) {
LayoutDeviceIntRect tmp = aRects[i];
const LayoutDeviceIntRect& tmp = aRects[i];
// Subsequent elements are character bounds in CSS units.
// Character bounds in CSS units.
auto rect = sdk::RectF::New(tmp.x / aScale.scale, tmp.y / aScale.scale,
(tmp.x + tmp.width) / aScale.scale,
(tmp.y + tmp.height) / aScale.scale);
rects->SetElement(i + 1, rect);
rects->SetElement(i, rect);
}
return rects;
}
@ -922,7 +915,6 @@ GeckoEditableSupport::UpdateCompositionRects()
auto rects = ConvertRectArrayToJavaRectFArray(
textRects.mReply.mRectArray,
widget->GetScreenBounds(),
widget->GetDefaultScale());
mEditable->UpdateCompositionRects(rects);