Bug 696319 - Scroll window as needed when soft keyboard is up [r=mfinkle]
authorKartikaya Gupta <kgupta@mozilla.com>
Sat, 03 Dec 2011 22:59:27 -0500
changeset 83684 f090c3ed08e0300f0a9d172cb589af0223a1d85a
parent 83683 f78079acfa12ee43f8cdc57efb8efcad22064138
child 83685 d29a6f00f79b6ff661f7aec67e38facfe5bc8ae7
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle
bugs696319
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 696319 - Scroll window as needed when soft keyboard is up [r=mfinkle] Send an event to scroll to the focused input field when the soft keyboard comes up. Ensure that this happens *after* the viewport change event is sent to Gecko, so that Gecko actually knows that browser viewport is smaller and doesn't just no-op the scroll request.
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoInputConnection.java
mobile/android/base/gfx/GeckoSoftwareLayerClient.java
mobile/android/base/gfx/LayerClient.java
mobile/android/base/gfx/LayerController.java
mobile/android/base/gfx/PlaceholderLayerClient.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -1567,9 +1567,15 @@ public class GeckoAppShell
         event.setEnabled(enabled);
         event.setChecked(checked);
         event.setPassword(password);
         event.setContentDescription(description);
         event.getText().add(text);
 
         accessibilityManager.sendAccessibilityEvent(event);
     }
+
+    public static void viewSizeChanged() {
+        if (mInputConnection != null && mInputConnection.isIMEEnabled()) {
+            sendEventToGecko(new GeckoEvent("ScrollTo:FocusedInput", ""));
+        }
+    }
 }
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -906,16 +906,20 @@ public class GeckoInputConnection
                                               imm.SHOW_FORCED, 0);
                 return true;
             default:
                 break;
         }
         return false;
     }
 
+    public boolean isIMEEnabled() {
+        // make sure this picks up PASSWORD and PLUGIN states as well
+        return mIMEState != IME_STATE_DISABLED;
+    }
 
     public void notifyIME(int type, int state) {
 
         View v = GeckoApp.mAppContext.getLayerController().getView();
 
         Log.d(LOGTAG, "notifyIME");
 
         if (v == null)
--- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
+++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
@@ -80,16 +80,17 @@ public class GeckoSoftwareLayerClient ex
     /* The viewport rect that Gecko is currently displaying. */
     private ViewportMetrics mGeckoViewport;
 
     private CairoImage mCairoImage;
 
     private static final long MIN_VIEWPORT_CHANGE_DELAY = 350L;
     private long mLastViewportChangeTime;
     private boolean mPendingViewportAdjust;
+    private boolean mViewportSizeChanged;
 
     public GeckoSoftwareLayerClient(Context context) {
         mContext = context;
 
         mWidth = LayerController.TILE_WIDTH;
         mHeight = LayerController.TILE_HEIGHT;
         mFormat = CairoImage.FORMAT_RGB16_565;
 
@@ -223,16 +224,21 @@ public class GeckoSoftwareLayerClient ex
                                               metrics.widthPixels, metrics.heightPixels);
             GeckoAppShell.sendEventToGecko(event);
         }
 
         render();
     }
 
     @Override
+    public void viewportSizeChanged() {
+        mViewportSizeChanged = true;
+    }
+
+    @Override
     public void render() {
         adjustViewportWithThrottling();
     }
 
     private void adjustViewportWithThrottling() {
         if (!getLayerController().getRedrawHint())
             return;
 
@@ -261,16 +267,20 @@ public class GeckoSoftwareLayerClient ex
             new ViewportMetrics(getLayerController().getViewportMetrics());
 
         PointF viewportOffset = viewportMetrics.getOptimumViewportOffset();
         viewportMetrics.setViewportOffset(viewportOffset);
         viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
 
         GeckoEvent event = new GeckoEvent("Viewport:Change", viewportMetrics.toJSON());
         GeckoAppShell.sendEventToGecko(event);
+        if (mViewportSizeChanged) {
+            mViewportSizeChanged = false;
+            GeckoAppShell.viewSizeChanged();
+        }
 
         mLastViewportChangeTime = System.currentTimeMillis();
     }
 
     public void handleMessage(String event, JSONObject message) {
         if ("Viewport:Update".equals(event)) {
             beginTransaction(mTileLayer);
             try {
--- a/mobile/android/base/gfx/LayerClient.java
+++ b/mobile/android/base/gfx/LayerClient.java
@@ -39,16 +39,17 @@ package org.mozilla.gecko.gfx;
 
 /**
  * A layer client provides tiles and manages other information used by the layer controller.
  */
 public abstract class LayerClient {
     private LayerController mLayerController;
 
     public abstract void geometryChanged();
+    public abstract void viewportSizeChanged();
     protected abstract void render();
 
     public LayerController getLayerController() { return mLayerController; }
     public void setLayerController(LayerController layerController) {
         mLayerController = layerController;
     }
 
     /**
--- a/mobile/android/base/gfx/LayerController.java
+++ b/mobile/android/base/gfx/LayerController.java
@@ -157,16 +157,19 @@ public class LayerController {
      * TODO: Refactor this to use an interface. Expose that interface only to the view and not
      * to the layer client. That way, the layer client won't be tempted to call this, which might
      * result in an infinite loop.
      */
     public void setViewportSize(FloatSize size) {
         mViewportMetrics.setSize(size);
         setForceRedraw();
 
+        if (mLayerClient != null)
+            mLayerClient.viewportSizeChanged();
+
         notifyLayerClientOfGeometryChange();
         mPanZoomController.geometryChanged(false);
         mView.requestRender();
     }
 
     public void scrollTo(PointF point) {
         mViewportMetrics.setOrigin(point);
         notifyLayerClientOfGeometryChange();
--- a/mobile/android/base/gfx/PlaceholderLayerClient.java
+++ b/mobile/android/base/gfx/PlaceholderLayerClient.java
@@ -137,16 +137,18 @@ public class PlaceholderLayerClient exte
         endTransaction(tileLayer);
 
         getLayerController().setRoot(tileLayer);
     }
 
     @Override
     public void geometryChanged() { /* no-op */ }
     @Override
+    public void viewportSizeChanged() { /* no-op */ }
+    @Override
     public void render() { /* no-op */ }
 
     @Override
     public void setLayerController(LayerController layerController) {
         super.setLayerController(layerController);
 
         if (mViewportUnknown)
             mViewport.setViewport(layerController.getViewport());
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -513,18 +513,20 @@ var BrowserApp = {
     }
   },
 
   scrollToFocusedInput: function(aBrowser) {
     let doc = aBrowser.contentDocument;
     if (!doc)
       return;
     let focused = doc.activeElement;
-    if ((focused instanceof HTMLInputElement && focused.mozIsTextField(false)) || (focused instanceof HTMLTextAreaElement))
+    if ((focused instanceof HTMLInputElement && focused.mozIsTextField(false)) || (focused instanceof HTMLTextAreaElement)) {
       focused.scrollIntoView(false);
+      BrowserApp.getTabForBrowser(aBrowser).sendViewportUpdate();
+    }
   },
 
   getDrawMetadata: function getDrawMetadata() {
     return JSON.stringify(this.selectedTab.viewport);
   },
 
   observe: function(aSubject, aTopic, aData) {
     let browser = this.selectedBrowser;