Bug 1415994 - 6b. Track GeckoDisplay origin changes; r=snorp
Add a `screenOriginChanged` callback to GeckoDisplay.Listener, which
informs Gecko of changes in the origin of the display. The origin
translates to coordinates for web APIs like screenX/screenY and certain
other calculations.
Also, make GeckoDisplay listen to layout changes in the view tree (by
overriding gatherTransparentRegion as an optimization), and call
`screenOriginChanged` accordingly.
MozReview-Commit-ID: C72EHCkbV3T
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -7,16 +7,17 @@
package org.mozilla.gecko;
import org.mozilla.gecko.gfx.GeckoDisplay;
import org.mozilla.gecko.gfx.LayerView;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
@@ -66,16 +67,18 @@ public class GeckoView extends LayerView
public SavedState[] newArray(final int size) {
return new SavedState[size];
}
};
}
private class Display implements GeckoDisplay,
SurfaceHolder.Callback {
+ private final int[] mOrigin = new int[2];
+
private Listener mListener;
private boolean mValid;
@Override // GeckoDisplay
public Listener getListener() {
return mListener;
}
@@ -88,16 +91,17 @@ public class GeckoView extends LayerView
mListener = listener;
if (!mValid || listener == null) {
return;
}
// Tell new listener there is already a surface.
+ onGlobalLayout();
if (GeckoView.this.mSurfaceView != null) {
final SurfaceHolder holder = GeckoView.this.mSurfaceView.getHolder();
final Rect frame = holder.getSurfaceFrame();
listener.surfaceChanged(holder.getSurface(), frame.right, frame.bottom);
}
}
@Override // SurfaceHolder.Callback
@@ -115,16 +119,26 @@ public class GeckoView extends LayerView
@Override // SurfaceHolder.Callback
public void surfaceDestroyed(final SurfaceHolder holder) {
if (mListener != null) {
mListener.surfaceDestroyed();
}
mValid = false;
}
+
+ public void onGlobalLayout() {
+ if (mListener == null) {
+ return;
+ }
+ if (GeckoView.this.mSurfaceView != null) {
+ GeckoView.this.mSurfaceView.getLocationOnScreen(mOrigin);
+ mListener.screenOriginChanged(mOrigin[0], mOrigin[1]);
+ }
+ }
}
public GeckoView(final Context context) {
super(context);
init();
}
public GeckoView(final Context context, final AttributeSet attrs) {
@@ -216,16 +230,27 @@ public class GeckoView extends LayerView
}
if (mSession != null && mSession.isOpen()) {
mSession.closeWindow();
}
}
@Override
+ public boolean gatherTransparentRegion(final Region region) {
+ // For detecting changes in SurfaceView layout, we take a shortcut here and
+ // override gatherTransparentRegion, instead of registering a layout listener,
+ // which is more expensive.
+ if (mSurfaceView != null) {
+ mDisplay.onGlobalLayout();
+ }
+ return super.gatherTransparentRegion(region);
+ }
+
+ @Override
protected Parcelable onSaveInstanceState() {
mStateSaved = true;
return new SavedState(super.onSaveInstanceState(), mSession);
}
@Override
protected void onRestoreInstanceState(final Parcelable state) {
mStateSaved = false;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoDisplay.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoDisplay.java
@@ -33,16 +33,25 @@ public interface GeckoDisplay {
void surfaceChanged(Surface surface, int width, int height);
/**
* The display's Surface has been destroyed. Must be called on the application
* main thread. GeckoSession may block this call to ensure the Surface is valid
* while pausing drawing.
*/
void surfaceDestroyed();
+
+ /**
+ * The display's coordinates on the screen has changed. Must be called on the
+ * application main thread.
+ *
+ * @param left The X coordinate of the display on the screen, in screen pixels.
+ * @param top The Y coordinate of the display on the screen, in screen pixels.
+ */
+ void screenOriginChanged(int left, int top);
}
/**
* Get the current listener attached to this display. Must be called on the
* application main thread.
*
* @return Current listener or null if there is no listener.
*/
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
@@ -12,16 +12,22 @@ import org.mozilla.gecko.util.ThreadUtil
import android.content.Context;
import android.util.Log;
import android.view.Surface;
public class LayerSession {
private static final String LOGTAG = "GeckoLayerSession";
private static final boolean DEBUG = false;
+ // Sent from compositor when the static toolbar image has been updated and
+ // is ready to animate.
+ /* package */ final static int STATIC_TOOLBAR_READY = 1;
+ // Sent from compositor when the static toolbar has been made visible so
+ // the real toolbar should be shown.
+ /* package */ final static int TOOLBAR_SHOW = 4;
// Special message sent from UiCompositorControllerChild once it is open.
/* package */ final static int COMPOSITOR_CONTROLLER_OPEN = 20;
// Special message sent from controller to query if the compositor controller is open.
/* package */ final static int IS_COMPOSITOR_CONTROLLER_OPEN = 21;
protected class Compositor extends JNIObject {
public LayerView layerView;
@@ -48,17 +54,17 @@ public class LayerSession {
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
@Override protected native void disposeNative();
@WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
public native void attachToJava(GeckoLayerClient layerClient,
NativePanZoomController npzc);
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- public native void onSizeChanged(int windowWidth, int windowHeight);
+ public native void onBoundsChanged(int left, int top, int width, int height);
// Gecko thread creates compositor; blocks UI thread.
@WrapForJNI(calledFrom = "ui", dispatchTo = "proxy")
public native void createCompositor(int width, int height, Object surface);
// Gecko thread pauses compositor; blocks UI thread.
@WrapForJNI(calledFrom = "ui", dispatchTo = "current")
public native void syncPauseCompositor();
@@ -91,16 +97,20 @@ public class LayerSession {
LayerSession.this.onCompositorReady();
}
});
}
if (layerView != null) {
layerView.handleToolbarAnimatorMessage(message);
}
+
+ if (message == STATIC_TOOLBAR_READY || message == TOOLBAR_SHOW) {
+ LayerSession.this.onWindowBoundsChanged();
+ }
}
@WrapForJNI(calledFrom = "ui", dispatchTo = "current")
public native void setDefaultClearColor(int color);
@WrapForJNI(calledFrom = "ui", dispatchTo = "current")
public native void requestScreenPixels();
@@ -122,16 +132,18 @@ public class LayerSession {
protected final Compositor mCompositor = new Compositor();
// Following fields are accessed on UI thread.
private GeckoDisplay mDisplay;
private boolean mAttachedCompositor;
private boolean mCalledCreateCompositor;
private boolean mCompositorReady;
private Surface mSurface;
+ private int mLeft;
+ private int mTop;
private int mWidth;
private int mHeight;
/* package */ GeckoDisplay getDisplay() {
if (DEBUG) {
ThreadUtils.assertOnUiThread();
}
return mDisplay;
@@ -175,46 +187,47 @@ public class LayerSession {
if (mSurface != null) {
// If we have a valid surface, resume the
// compositor now that the compositor is ready.
onSurfaceChanged(mSurface, mWidth, mHeight);
mSurface = null;
}
}
- private void onWindowResize() {
+ /* protected */ void onWindowBoundsChanged() {
if (DEBUG) {
ThreadUtils.assertOnUiThread();
}
if (mAttachedCompositor) {
- final int viewportHeight;
+ final int toolbarHeight;
if (mCompositor.layerView != null) {
- viewportHeight = mHeight - mCompositor.layerView.getCurrentToolbarHeight();
+ toolbarHeight = mCompositor.layerView.getCurrentToolbarHeight();
} else {
- viewportHeight = mHeight;
+ toolbarHeight = 0;
}
- mCompositor.onSizeChanged(mWidth, viewportHeight);
+ mCompositor.onBoundsChanged(mLeft, mTop + toolbarHeight,
+ mWidth, mHeight - toolbarHeight);
if (mCompositor.layerView != null) {
mCompositor.layerView.onSizeChanged(mWidth, mHeight);
}
}
}
/* package */ void onSurfaceChanged(final Surface surface, final int width,
final int height) {
ThreadUtils.assertOnUiThread();
mWidth = width;
mHeight = height;
- onWindowResize();
if (mCompositorReady) {
mCompositor.syncResumeResizeCompositor(width, height, surface);
+ onWindowBoundsChanged();
return;
}
if (mAttachedCompositor && !mCalledCreateCompositor) {
mCompositor.createCompositor(width, height, surface);
mCompositor.sendToolbarAnimatorMessage(IS_COMPOSITOR_CONTROLLER_OPEN);
mCalledCreateCompositor = true;
}
@@ -232,16 +245,28 @@ public class LayerSession {
return;
}
// While the surface was valid, we never became attached or the
// compositor never became ready; clear the saved surface.
mSurface = null;
}
+ /* package */ void onScreenOriginChanged(final int left, final int top) {
+ ThreadUtils.assertOnUiThread();
+
+ if (mLeft == left && mTop == top) {
+ return;
+ }
+
+ mLeft = left;
+ mTop = top;
+ onWindowBoundsChanged();
+ }
+
public void addDisplay(final GeckoDisplay display) {
ThreadUtils.assertOnUiThread();
if (display.getListener() != null) {
throw new IllegalArgumentException("Display already attached");
} else if (mDisplay != null) {
throw new IllegalArgumentException("Only one display supported");
}
@@ -253,16 +278,21 @@ public class LayerSession {
final int height) {
onSurfaceChanged(surface, width, height);
}
@Override
public void surfaceDestroyed() {
onSurfaceDestroyed();
}
+
+ @Override
+ public void screenOriginChanged(final int left, final int top) {
+ onScreenOriginChanged(left, top);
+ }
});
}
public void removeDisplay(final GeckoDisplay display) {
ThreadUtils.assertOnUiThread();
if (mDisplay != display) {
throw new IllegalArgumentException("Display not attached");
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -925,28 +925,25 @@ public:
// Set the first-paint flag so that we (re-)link any new Java objects
// to Gecko, co-ordinate viewports, etc.
if (RefPtr<CompositorBridgeChild> bridge = mWindow->GetCompositorBridgeChild()) {
bridge->SendForceIsFirstPaint();
}
}
- void OnSizeChanged(int32_t aWindowWidth, int32_t aWindowHeight)
+ void OnBoundsChanged(int32_t aLeft, int32_t aTop,
+ int32_t aWidth, int32_t aHeight)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return; // Already shut down.
}
- if (aWindowWidth != mWindow->mBounds.width ||
- aWindowHeight != mWindow->mBounds.height) {
-
- mWindow->Resize(aWindowWidth, aWindowHeight, /* repaint */ false);
- }
+ mWindow->Resize(aLeft, aTop, aWidth, aHeight, /* repaint */ false);
}
void CreateCompositor(int32_t aWidth, int32_t aHeight,
jni::Object::Param aSurface)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return; // Already shut down.
@@ -1874,25 +1871,25 @@ nsWindow::GetScreenBounds()
{
return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
}
LayoutDeviceIntPoint
nsWindow::WidgetToScreenOffset()
{
LayoutDeviceIntPoint p(0, 0);
- nsWindow *w = this;
- while (w && !w->IsTopLevel()) {
+ for (nsWindow *w = this; !!w; w = w->mParent) {
p.x += w->mBounds.x;
p.y += w->mBounds.y;
- w = w->mParent;
+ if (w->IsTopLevel()) {
+ break;
+ }
}
-
return p;
}
nsresult
nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
nsEventStatus& aStatus)
{
aStatus = DispatchEvent(aEvent);