Bug 1442176 - 2. Add pinned-to-screen flag in GeckoSession; r=snorp r=droeh a=pascalc
☠☠ backed out by be49f2604cfe ☠ ☠
authorJim Chen <nchen@mozilla.com>
Mon, 17 Sep 2018 17:47:21 -0400
changeset 492596 80aaf77b813f333734fc05e60472a2b86f422d97
parent 492595 5bd0f4d6fed870fbe5697035707770522be60327
child 492597 a733114f72eb86e7c9f6a2406f2c832a325a3bf2
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, droeh, pascalc
bugs1442176
milestone63.0
Bug 1442176 - 2. Add pinned-to-screen flag in GeckoSession; r=snorp r=droeh a=pascalc Add a flag for whether the session should be pinned to the screen. The app would check the flag and prevent scrolling of the session when it's pinned. Differential Revision: https://phabricator.services.mozilla.com/D5190
mobile/android/chrome/geckoview/GeckoViewContent.js
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoDisplay.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
--- a/mobile/android/chrome/geckoview/GeckoViewContent.js
+++ b/mobile/android/chrome/geckoview/GeckoViewContent.js
@@ -44,16 +44,17 @@ class GeckoViewContent extends GeckoView
         capture: false,
     };
     addEventListener("DOMFormHasPassword", this, options);
     addEventListener("DOMInputPasswordAdded", this, options);
     addEventListener("pagehide", this, options);
     addEventListener("pageshow", this, options);
     addEventListener("focusin", this, options);
     addEventListener("focusout", this, options);
+    addEventListener("mozcaretstatechanged", this, options);
 
     // Notify WebExtension process script that this tab is ready for extension content to load.
     Services.obs.notifyObservers(this.messageManager, "tab-content-frameloader-created");
   }
 
   onEnable() {
     debug `onEnable`;
 
@@ -228,16 +229,17 @@ class GeckoViewContent extends GeckoView
       case "GeckoView:SetActive":
           if (content && aMsg.data.suspendMedia) {
               content.windowUtils.mediaSuspend = aMsg.data.active ? Ci.nsISuspendedTypes.NONE_SUSPENDED : Ci.nsISuspendedTypes.SUSPENDED_PAUSE;
           }
         break;
     }
   }
 
+  // eslint-disable-next-line complexity
   handleEvent(aEvent) {
     debug `handleEvent: ${aEvent.type}`;
 
     switch (aEvent.type) {
       case "contextmenu":
         function nearestParentHref(node) {
           while (node && !node.href) {
             node = node.parentNode;
@@ -328,16 +330,24 @@ class GeckoViewContent extends GeckoView
           this._clearAutoFillElements();
         }
         break;
       case "pageshow":
         if (aEvent.target === content.document && aEvent.persisted) {
           this._scanAutoFillDocument(aEvent.target);
         }
         break;
+      case "mozcaretstatechanged":
+        if (aEvent.reason === "presscaret" || aEvent.reason === "releasecaret") {
+          this.eventDispatcher.sendRequest({
+            type: "GeckoView:PinOnScreen",
+            pinned: aEvent.reason === "presscaret",
+          });
+        }
+        break;
     }
   }
 
   // WebProgress event handler.
   onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
     debug `onLocationChange`;
 
     if (this._savedState) {
--- 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
@@ -48,9 +48,22 @@ public class GeckoDisplay {
      * 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.
      */
     public void screenOriginChanged(final int left, final int top) {
         mSession.onScreenOriginChanged(left, top);
     }
+
+    /**
+     * Return whether the display should be pinned on the screen. When pinned, the display
+     * should not be moved on the screen due to animation, scrolling, etc. A common reason
+     * for the display being pinned is when the user is dragging a selection caret inside
+     * the display; normal user interaction would be disrupted in that case if the display
+     * was moved on screen.
+     *
+     * @return True if display should be pinned on the screen.
+     */
+    public boolean shouldPinOnScreen() {
+        return mSession.shouldPinOnScreen();
+    }
 }
--- 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
@@ -478,16 +478,29 @@ public class LayerSession {
         if (mOverscroll == null) {
             return;
         }
 
         mOverscroll.setDistance(x, OverscrollEdgeEffect.AXIS_X);
         mOverscroll.setDistance(y, OverscrollEdgeEffect.AXIS_Y);
     }
 
+    protected boolean mShouldPinOnScreen;
+
+    protected void setShouldPinOnScreen(final boolean pinned) {
+        if (DEBUG) {
+            ThreadUtils.assertOnUiThread();
+        }
+
+        if (mToolbar != null) {
+            mToolbar.setPinned(pinned, DynamicToolbarAnimator.PinReason.CARET_DRAG);
+        }
+        mShouldPinOnScreen = pinned;
+    }
+
     /* package */ void onMetricsChanged(final float scrollX, final float scrollY,
                                         final float zoom) {
         if (DEBUG) {
             ThreadUtils.assertOnUiThread();
         }
 
         mViewportLeft = scrollX;
         mViewportTop = scrollY;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -807,28 +807,31 @@ public class GeckoSession extends LayerS
                       " - chrome startup finished");
             }
         }
     }
 
     private class Listener implements BundleEventListener {
         /* package */ void registerListeners() {
             getEventDispatcher().registerUiThreadListener(this,
+                "GeckoView:PinOnScreen",
                 "GeckoView:Prompt",
                 null);
         }
 
         @Override
         public void handleMessage(final String event, final GeckoBundle message,
                                   final EventCallback callback) {
             if (DEBUG) {
                 Log.d(LOGTAG, "handleMessage: event = " + event);
             }
 
-            if ("GeckoView:Prompt".equals(event)) {
+            if ("GeckoView:PinOnScreen".equals(event)) {
+                GeckoSession.this.setShouldPinOnScreen(message.getBoolean("pinned"));
+            } else if ("GeckoView:Prompt".equals(event)) {
                 handlePromptEvent(GeckoSession.this, message, callback);
             }
         }
     }
 
     protected Window mWindow;
     private GeckoSessionSettings mSettings;
 
@@ -1936,16 +1939,21 @@ public class GeckoSession extends LayerS
             }
             default: {
                 callback.sendError("Invalid type");
                 break;
             }
         }
     }
 
+    /* package */ boolean shouldPinOnScreen() {
+        ThreadUtils.assertOnUiThread();
+        return mShouldPinOnScreen;
+    }
+
     public EventDispatcher getEventDispatcher() {
         return mEventDispatcher;
     }
 
     public interface ProgressDelegate {
         /**
          * Class representing security information for a site.
          */
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
@@ -155,16 +155,20 @@ public class GeckoView extends FrameLayo
             if (mDisplay == null) {
                 return;
             }
             if (GeckoView.this.mSurfaceView != null) {
                 GeckoView.this.mSurfaceView.getLocationOnScreen(mOrigin);
                 mDisplay.screenOriginChanged(mOrigin[0], mOrigin[1]);
             }
         }
+
+        public boolean shouldPinOnScreen() {
+            return mDisplay != null ? mDisplay.shouldPinOnScreen() : false;
+        }
     }
 
     public GeckoView(final Context context) {
         super(context);
         init();
     }
 
     public GeckoView(final Context context, final AttributeSet attrs) {
@@ -208,16 +212,29 @@ public class GeckoView extends FrameLayo
      * @param color Cover color.
      */
     public void coverUntilFirstPaint(final int color) {
         if (mSurfaceView != null) {
             mSurfaceView.setBackgroundColor(color);
         }
     }
 
+    /**
+     * Return whether the view should be pinned on the screen. When pinned, the view
+     * should not be moved on the screen due to animation, scrolling, etc. A common reason
+     * for the view being pinned is when the user is dragging a selection caret inside
+     * the view; normal user interaction would be disrupted in that case if the view
+     * was moved on screen.
+     *
+     * @return True if view should be pinned on the screen.
+     */
+    public boolean shouldPinOnScreen() {
+        return mDisplay.shouldPinOnScreen();
+    }
+
     /* package */ void setActive(final boolean active) {
         if (mSession != null) {
             mSession.setActive(active);
         }
     }
 
     public GeckoSession releaseSession() {
         if (mSession == null) {