Bug 595233 - Fennec FormHelper does not zoom to the correct position on all platforms [r=mfinkle]
authorVivien Nicolas <21@vingtetun.org>
Tue, 26 Oct 2010 21:12:42 +0200
changeset 66918 1aec1d672da97a63ac54bac8cb1e08b69bc2f25d
parent 66917 c461b6a96b0e6de979d6a60ea60c9f6c380385d2
child 66919 a6365689f7bbd759cf2462efcce9cfe3699178a4
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle
bugs595233
Bug 595233 - Fennec FormHelper does not zoom to the correct position on all platforms [r=mfinkle]
mobile/app/mobile.js
mobile/chrome/content/browser-ui.js
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -136,20 +136,16 @@ pref("alerts.height", 50);
 pref("signon.rememberSignons", true);
 pref("signon.expireMasterPassword", false);
 pref("signon.SignonFileName", "signons.txt");
 
 /* form helper */
 pref("formhelper.enabled", true);
 pref("formhelper.autozoom", true);
 pref("formhelper.restore", false);
-pref("formhelper.caretLines.portrait", 4);
-pref("formhelper.caretLines.landscape", 1);
-pref("formhelper.harmonizeValue", 10);
-pref("formhelper.margin", 15);
 
 /* find helper */
 pref("findhelper.autozoom", true);
 
 /* autocomplete */
 pref("browser.formfill.enable", true);
 
 /* microsummaries */
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -1740,16 +1740,30 @@ var FormHelperUI = {
     previous: "cmd_formPrevious",
     close: "cmd_formClose"
   },
 
   //for resize/rotate case
   _currentCaretRect: null,
   _currentElementRect: null,
 
+  _visibleScreenArea: null,
+  get visibleScreenArea() {
+    let visibleRect = Rect.fromRect(Browser.selectedBrowser.getBoundingClientRect());
+    let visibleScreenArea = visibleRect;
+    if (this._visibleScreenArea) {
+      visibleScreenArea = this._visibleScreenArea.clone();
+      visibleScreenArea.x = visibleRect.x;
+      visibleScreenArea.y = visibleRect.y;
+      visibleScreenArea.width = visibleRect.width;
+      visibleScreenArea.height = visibleRect.height - this._container.getBoundingClientRect().height;
+    }
+    return visibleScreenArea;
+  },
+
   init: function formHelperInit() {
     this._container = document.getElementById("content-navigator");
     this._autofillContainer = document.getElementById("form-helper-autofill");
     this._cmdPrevious = document.getElementById(this.commands.previous);
     this._cmdNext = document.getElementById(this.commands.next);
     this._visibleScreenArea = new Rect(0, 0, 0, 0);
 
     // Listen for form assistant messages from content
@@ -1786,24 +1800,19 @@ var FormHelperUI = {
       id: aElement.id,
       name: aElement.name,
       value: aElement.value,
       maxLength: aElement.maxLength,
       type: aElement.type,
       isAutocomplete: aElement.isAutocomplete,
       list: aElement.choices
     }
+
     this._updateContainer(lastElement, this._currentElement);
-
-    //hide all sidebars, this will adjust the visible rect.
-    Browser.hideSidebars();
-
-    //save the element Rect and reuse it to avoid jumps in cases the element moves slighty on the website.
-    this._currentElementRect = Rect.fromRect(aElement.rect);
-    this._zoom(this._currentElementRect, Rect.fromRect(aElement.caretRect));
+    this._zoom(Rect.fromRect(aElement.rect), Rect.fromRect(aElement.caretRect));
   },
 
   hide: function formHelperHide() {
     if (!this._open)
       return;
 
     // reset current Element and Caret Rect
     this._currentElementRect = null;
@@ -1835,29 +1844,25 @@ var FormHelperUI = {
         break;
 
       case "FormAssist:AutoComplete":
         this._updateAutocompleteFor(json.current);
         this._container.contentHasChanged();
         break;
 
       case "FormAssist:Resize":
-        // First hide all tool/sidebars they take to much space, this will adjust the visible rect.
-        Browser.hideSidebars();
         SelectHelperUI.resize();
         this._zoom(this._currentElementRect, this._currentCaretRect);
         this._container.contentHasChanged();
         break;
 
        case "FormAssist:Update":
-        // Using currentElementRect here is maybe not 100% perfect since
-        // elements might change there position while typing
-        // out of screen movement is covered by simply following the caret
-        // as long as we see what we type, let the element move
-        this._zoom(this._currentElementRect, Rect.fromRect(json.caretRect));
+        Browser.hideSidebars();
+        Browser.hideTitlebar();
+        this._zoom(null, Rect.fromRect(json.caretRect));
         break;
 
       case "DOMWillOpenModalDialog":
         if (this._open && aMessage.target == Browser.selectedBrowser) {
           this._container.style.display = "none";
           this._container._spacer.hidden = true;
         }
         break;
@@ -1873,17 +1878,18 @@ var FormHelperUI = {
 
   observe: function formHelperObserve(aSubject, aTopic, aData) {
     let rect = Rect.fromRect(JSON.parse(aData));
     rect.height = rect.bottom - rect.top;
     rect.width  = rect.right - rect.left;
 
     this._visibleScreenArea = rect;
     BrowserUI.sizeControls(rect.width, rect.height);
-    this._zoom(this._currentElementRect, this._currentCaretRect);
+    if (this.open)
+      this._zoom(this._currentElementRect, this._currentCaretRect);
   },
 
   goToPrevious: function formHelperGoToPrevious() {
     Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Previous", { });
   },
 
   goToNext: function formHelperGoToNext() {
     Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Next", { });
@@ -1983,109 +1989,44 @@ var FormHelperUI = {
       SelectHelperUI.show(aCurrentElement.list);
     } else if (lastHasChoices && !currentHasChoices) {
       SelectHelperUI.hide();
     }
   },
 
   /** Zoom and move viewport so that element is legible and touchable. */
   _zoom: function _formHelperZoom(aElementRect, aCaretRect) {
+    let zoomRect = this.visibleScreenArea;
     let browser = getBrowser();
-    if (aElementRect && aCaretRect && this._open) {
+
+    // Zoom to a specified Rect
+    if (aElementRect && Browser.selectedTab.allowZoom && Services.prefs.getBoolPref("formhelper.autozoom")) {
+      this._currentElementRect = aElementRect;
+      // Zoom to an element by keeping the caret into view
+      let zoomLevel = this._getZoomLevelForRect(aElementRect);
+      zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax);
+
+      zoomRect = this._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
+      Browser.animatedZoomTo(zoomRect);
+    }
+
+    // Move the view to show the caret if needed
+    if (aCaretRect) {
       this._currentCaretRect = aCaretRect;
-
-      let visibleScreenArea = !this._visibleScreenArea.isEmpty() ? this._visibleScreenArea : new Rect(0, 0, window.innerWidth, window.innerHeight);
-
-      // respect the helper container in setting the correct viewAreaHeight
-      let viewAreaHeight = visibleScreenArea.height - this._container.getBoundingClientRect().height;
-      let viewAreaWidth = visibleScreenArea.width;
-      let caretLines = Services.prefs.getIntPref("formhelper.caretLines.portrait");
-      let harmonizeValue = Services.prefs.getIntPref("formhelper.harmonizeValue");
-
-      if (!Util.isPortrait())
-        caretLines = Services.prefs.getIntPref("formhelper.caretLines.landscape");
-
-      // hide titlebar if the remaining space would be smaller than the height of the titlebar itself
-      // if there is enough space left than adjust the height. Since this adjust the the visible rects
-      // there is no need to adjust the y later
-      let toolbar = document.getElementById("toolbar-main");
-      if (viewAreaHeight - toolbar.boxObject.height <= toolbar.boxObject.height * 2)
-        Browser.hideTitlebar();
-      else
-        viewAreaHeight -= toolbar.boxObject.height;
-
-      // To ensure the correct calculation when the sidebars are visible - get the sidebar size and
-      // use them as margin
-      let [leftvis, rightvis, leftW, rightW] = Browser.computeSidebarVisibility(0, 0);
-      let marginLeft = leftvis ? leftW : 0;
-      let marginRight = rightvis ? rightW : 0;
-
-      // The height and Y of the caret might change during writing - even in cases the
-      // fontsize keeps the same. To avoid unneeded zooming and scrolling
-      let harmonizedCaretHeight = 0;
-      let harmonizedCaretY = 0;
-
-      // Start calculation here, the order is important
-      // All calculations are done in non_zoomed_coordinates => 1:1 to "screen-pixels"
-
-      // for buttons and non input field elements a caretRect with the height of 0 gets reported
-      // cover this case.
-      if (!aCaretRect.isEmpty()) {
-        // the height and y position may vary from letter to letter
-        // adjust position and zooming only if a bigger step was done.
-        harmonizedCaretHeight = aCaretRect.height - aCaretRect.height % harmonizeValue;
-        harmonizedCaretY = aCaretRect.y - aCaretRect.y % harmonizeValue;
-      } else {
-        harmonizedCaretHeight = 30; // fallback height
-
-        // use the element as position
-        harmonizedCaretY = aElementRect.y;
-        aCaretRect.x = aElementRect.x;
-      }
-
-      let oldZoomLevel = zoomLevel = browser.scale;
-      let enableZoom = Browser.selectedTab.allowZoom && Services.prefs.getBoolPref("formhelper.autozoom");
-      if (enableZoom) {
-        zoomLevel = (viewAreaHeight / caretLines) / harmonizedCaretHeight;
-        zoomLevel = Util.clamp(zoomLevel, kBrowserFormZoomLevelMin, kBrowserFormZoomLevelMax);
-        zoomLevel = Browser.selectedTab.clampZoomLevel(zoomLevel);
-      }
-      viewAreaWidth /= zoomLevel;
-
-      const margin = Services.prefs.getIntPref("formhelper.margin");
-
-      // if the viewAreaWidth is smaller than the neutralized position + margins.
-      // [YES] use the x position of the element minus margins as x position for our visible rect.
-      // [NO] use the x position of the caret minus margins as the x position for our visible rect.
-      let x = (marginLeft + marginRight + margin + aCaretRect.x - aElementRect.x) < viewAreaWidth
-               ? aElementRect.x - margin - marginLeft
-               : aCaretRect.x - viewAreaWidth + margin + marginRight;
-      // Use the adjusted Caret Y minus a margin for our visible rect
-      let y = harmonizedCaretY - margin;
-      x *= oldZoomLevel;
-      y *= oldZoomLevel;
-
+      let caretRect = aCaretRect.scale(browser.scale, browser.scale);
       let scroll = browser.getPosition();
-
-      // from here on play with zoomed values
-      // if we want to have it animated, build up zoom rect and animate.
-      if (enableZoom) {
-        // don't use browser functions they are bogus for this case
-        let zoomRatio = zoomLevel / oldZoomLevel;
-
-        let visW = window.innerWidth, visH = window.innerHeight;
-        let newVisW = visW / zoomRatio, newVisH = visH / zoomRatio;
-        let zoomRect = new Rect(x, y, newVisW, newVisH);
-        zoomRect.translateInside(new Rect(0, 0, browser.contentDocumentWidth * oldZoomLevel,
-                                                browser.contentDocumentHeight * oldZoomLevel));
-        Browser.animatedZoomTo(zoomRect);
-      }
-      else { // no zooming at all
-        browser.scrollTo(x, y);
-      }
+      zoomRect.x += scroll.x;
+      zoomRect.y += scroll.y;
+
+      if (zoomRect.contains(caretRect))
+        return;
+
+      let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect);
+      if (deltaX != 0 || deltaY != 0)
+        browser.scrollBy(deltaX, deltaY);
     }
   },
 
   /* Store the current zoom level, and scroll positions to restore them if needed */
   _zoomStart: function _formHelperZoomStart() {
     if (!Services.prefs.getBoolPref("formhelper.restore"))
       return;
 
@@ -2102,16 +2043,40 @@ var FormHelperUI = {
       return;
 
     let restore = this._restore;
     getBrowser().scale = restore.scale;
     Browser.contentScrollboxScroller.scrollTo(restore.contentScrollOffset.x, restore.contentScrollOffset.y);
     Browser.pageScrollboxScroller.scrollTo(restore.pageScrollOffset.x, restore.pageScrollOffset.y);
   },
 
+  _getZoomRectForPoint: function _getZoomRectForPoint(x, y, zoomLevel) {
+    const margin = 30;
+
+    let browser = getBrowser();
+    x = x * browser.scale;
+    y = y * browser.scale;
+
+    let vis = this.visibleScreenArea
+    zoomLevel = Math.min(ZoomManager.MAX, zoomLevel);
+    let oldScale = browser.scale;
+    let zoomRatio = zoomLevel / oldScale;
+    let newVisW = vis.width / zoomRatio, newVisH = vis.height / zoomRatio;
+    let result = new Rect((x - newVisW / 2) - margin / 2, y - newVisH / 2, newVisW + margin, newVisH);
+
+    // Make sure rectangle doesn't poke out of viewport
+    return result.translateInside(new Rect(0, 0, browser.contentDocumentWidth * oldScale,
+                                                 browser.contentDocumentHeight * oldScale));
+  },
+
+  _getZoomLevelForRect: function _getZoomLevelForRect(aRect) {
+    let zoomLevel = this.visibleScreenArea.width / aRect.width;
+    return Util.clamp(zoomLevel, kBrowserFormZoomLevelMin, kBrowserFormZoomLevelMax);
+  },
+
   _getOffsetForCaret: function _formHelperGetOffsetForCaret(aCaretRect, aRect) {
     // Determine if we need to move left or right to bring the caret into view
     let deltaX = 0;
     if (aCaretRect.right > aRect.right)
       deltaX = aCaretRect.right - aRect.right;
     if (aCaretRect.left < aRect.left)
       deltaX = aCaretRect.left - aRect.left;