Bug 941592 - Make double-tap zoom action depend on current zoom level [r=jimm]
authorMatt Brubeck <mbrubeck@mozilla.com>
Tue, 03 Dec 2013 09:47:37 -0800
changeset 158496 7b8cc2b3568ba55b088b3e7930f19e1443d06fe1
parent 158495 ff4388261db5af1791a202ab3af69733171d0fb1
child 158497 e0572cb4eb82aaaaf7e3f9195350f49c20621f41
push id25748
push userryanvm@gmail.com
push dateTue, 03 Dec 2013 21:42:03 +0000
treeherdermozilla-central@03a55dd19083 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs941592
milestone28.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 941592 - Make double-tap zoom action depend on current zoom level [r=jimm]
browser/metro/base/content/apzc.js
browser/metro/base/content/bindings/browser.xml
browser/metro/base/content/contenthandlers/Content.js
--- a/browser/metro/base/content/apzc.js
+++ b/browser/metro/base/content/apzc.js
@@ -27,64 +27,50 @@ var APZCObserver = {
       return;
     }
 
     let os = Services.obs;
     os.addObserver(this, "apzc-transform-begin", false);
 
     // Fired by ContentAreaObserver
     window.addEventListener("SizeChanged", this, true);
-
     Elements.tabList.addEventListener("TabSelect", this, true);
-    Elements.tabList.addEventListener("TabOpen", this, true);
-    Elements.tabList.addEventListener("TabClose", this, true);
+    Elements.browsers.addEventListener("pageshow", this, true);
+    messageManager.addMessageListener("Browser:ContentScroll", this);
+    messageManager.addMessageListener("Content:ZoomToRect", this);
   },
 
   shutdown: function shutdown() {
     if (!this._enabled) {
       return;
     }
 
     let os = Services.obs;
     os.removeObserver(this, "apzc-transform-begin");
 
     window.removeEventListener("SizeChanged", this, true);
-
     Elements.tabList.removeEventListener("TabSelect", this, true);
-    Elements.tabList.removeEventListener("TabOpen", this, true);
-    Elements.tabList.removeEventListener("TabClose", this, true);
+    Elements.browsers.removeEventListener("pageshow", this, true);
+    messageManager.removeMessageListener("Browser:ContentScroll", this);
+    messageManager.removeMessageListener("Content:ZoomToRect", this);
   },
 
   handleEvent: function APZC_handleEvent(aEvent) {
     switch (aEvent.type) {
       case "SizeChanged":
       case 'TabSelect':
         this._resetDisplayPort();
         break;
 
       case 'pageshow':
         if (aEvent.target != Browser.selectedBrowser.contentDocument) {
           break;
         }
         this._resetDisplayPort();
         break;
-
-      case 'TabOpen': {
-        let browser = aEvent.originalTarget.linkedBrowser;
-        browser.addEventListener("pageshow", this, true);
-        // Register for notifications from content about scroll actions.
-        browser.messageManager.addMessageListener("Browser:ContentScroll", this);
-        break;
-      }
-      case 'TabClose': {
-        let browser = aEvent.originalTarget.linkedBrowser;
-        browser.removeEventListener("pageshow", this, true);
-        browser.messageManager.removeMessageListener("Browser:ContentScroll", this);
-        break;
-      }
     }
   },
 
   observe: function ao_observe(aSubject, aTopic, aData) {
     if (aTopic == "apzc-transform-begin") {
       // When we're panning, hide the main scrollbars by setting imprecise
       // input (which sets a property on the browser which hides the scrollbar
       // via CSS).  This reduces jittering from left to right. We may be able
@@ -92,30 +78,57 @@ var APZCObserver = {
       if (InputSourceHelper.isPrecise) {
         InputSourceHelper._imprecise();
       }
     }
   },
 
   receiveMessage: function(aMessage) {
     let json = aMessage.json;
+    let browser = aMessage.target;
     switch (aMessage.name) {
        // Content notifies us here (syncronously) if it has scrolled
        // independent of the apz. This can happen in a lot of
        // cases: keyboard shortcuts, scroll wheel, or content script.
        // Let the apz know about this change so that it can update
        // its scroll offset data.
       case "Browser:ContentScroll": {
         let data = json.viewId + " " + json.presShellId + " (" + json.scrollOffset.x + ", " + json.scrollOffset.y + ")";
         Services.obs.notifyObservers(null, "apzc-scroll-offset-changed", data);
         break;
       }
+      case "Content:ZoomToRect": {
+        let { presShellId, viewId } = json;
+        let rect = Rect.fromRect(json.rect);
+        if (this.isRectZoomedIn(rect, browser.contentViewportBounds)) {
+          // If we're already zoomed in, zoom out instead.
+          rect = new Rect(0,0,0,0);
+        }
+        let data = [rect.x, rect.y, rect.width, rect.height, presShellId, viewId].join(",");
+        Services.obs.notifyObservers(null, "apzc-zoom-to-rect", data);
+      }
     }
   },
 
+  /**
+   * Check to see if the area of the rect visible in the viewport is
+   * approximately the max area of the rect we can show.
+   * Based on code from BrowserElementPanning.js
+   */
+  isRectZoomedIn: function (aRect, aViewport) {
+    let overlap = aViewport.intersect(aRect);
+    let overlapArea = overlap.width * overlap.height;
+    let availHeight = Math.min(aRect.width * aViewport.height / aViewport.width, aRect.height);
+    let showing = overlapArea / (aRect.width * availHeight);
+    let ratioW = (aRect.width / aViewport.width);
+    let ratioH = (aRect.height / aViewport.height);
+
+    return (showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9));
+  },
+
   _resetDisplayPort: function () {
     // Start off with something reasonable. The apzc will handle these
     // calculations once scrolling starts.
     let doc = Browser.selectedBrowser.contentDocument.documentElement;
     // While running tests, sometimes this can be null. If we don't have a
     // root document, there's no point in setting a scrollable display port.
     if (!doc) {
       return;
--- a/browser/metro/base/content/bindings/browser.xml
+++ b/browser/metro/base/content/bindings/browser.xml
@@ -169,18 +169,18 @@
             }
 
             let scale = 1;
             if (!ignoreScale) {
               scale = this.scale;
             }
 
             return {
-              x: (aClientX + scrollX - bcr.left) / scale,
-              y: (aClientY + scrollY - bcr.top) / scale
+              x: (aClientX - bcr.left) / scale + scrollX,
+              y: (aClientY - bcr.top) / scale + scrollY
             };
           ]]>
         </body>
       </method>
 
       <method name="rectClientToBrowser">
         <parameter name="aClientRect"/>
         <parameter name="aIgnoreScroll"/>
@@ -199,21 +199,22 @@
             }
 
             let scale = 1;
             if (!ignoreScale) {
               scale = this.scale;
             }
 
             let bcr = this.getBoundingClientRect();
+            let clientRect = Rect.fromRect(aClientRect);
             return new Rect(
-              (aClientRect.x + scrollX - bcr.left) / scale,
-              (aClientRect.y + scrollY - bcr.top) / scale,
-              aClientRect.width / scale,
-              aClientRect.height / scale
+              (clientRect.x - bcr.left) / scale + scrollX,
+              (clientRect.y - bcr.top) / scale + scrollY,
+              clientRect.width / scale,
+              clientRect.height / scale
             );
           ]]>
         </body>
       </method>
 
       <method name="ctobx">
         <parameter name="aX"/>
         <parameter name="aIgnoreScroll"/>
--- a/browser/metro/base/content/contenthandlers/Content.js
+++ b/browser/metro/base/content/contenthandlers/Content.js
@@ -111,17 +111,16 @@ function getOverflowContentBoundingRect(
 
 /*
  * Content
  *
  * Browser event receiver for content.
  */
 let Content = {
   _debugEvents: false,
-  _isZoomedIn: false,
 
   get formAssistant() {
     delete this.formAssistant;
     return this.formAssistant = new FormAssistant();
   },
 
   init: function init() {
     // Asyncronous messages sent from the browser
@@ -140,17 +139,16 @@ let Content = {
     addEventListener("keyup", this);
 
     // Synchronous events caught during the bubbling phase
     addEventListener("MozApplicationManifest", this, false);
     addEventListener("DOMContentLoaded", this, false);
     addEventListener("DOMAutoComplete", this, false);
     addEventListener("DOMFormHasPassword", this, false);
     addEventListener("blur", this, false);
-    addEventListener("pagehide", this, false);
     // Attach a listener to watch for "click" events bubbling up from error
     // pages and other similar page. This lets us fix bugs like 401575 which
     // require error page UI to do privileged things, without letting error
     // pages have any privilege themselves.
     addEventListener("click", this, false);
 
     docShell.useGlobalHistory = true;
   },
@@ -205,20 +203,16 @@ let Content = {
         this._maybeNotifyErrorPage();
         break;
 
       case "DOMAutoComplete":
       case "blur":
         LoginManagerContent.onUsernameInput(aEvent);
         break;
 
-      case "pagehide":
-        this._isZoomedIn = false;
-        break;
-
       case "touchstart":
         this._onTouchStart(aEvent);
         break;
     }
   },
 
   receiveMessage: function receiveMessage(aMessage) {
     if (this._debugEvents) Util.dumpLn("Content:", aMessage.name);
@@ -376,21 +370,16 @@ let Content = {
     let utils = Util.getWindowUtils(content);
     for (let type of ["mousemove", "mousedown", "mouseup"]) {
       utils.sendMouseEventToWindow(type, aX, aY, 0, 1, aModifiers, true, 1.0,
           Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
     }
   },
 
   _onDoubleTap: function (aX, aY) {
-    if (this._isZoomedIn) {
-      this._zoomOut();
-      return;
-    }
-
     let { element } = Content.getCurrentWindowAndOffset(aX, aY);
     while (element && !this._shouldZoomToElement(element)) {
       element = element.parentNode;
     }
 
     if (!element) {
       this._zoomOut();
     } else {
@@ -399,45 +388,41 @@ let Content = {
   },
 
   /******************************************************
    * Zoom utilities
    */
   _zoomOut: function() {
     let rect = new Rect(0,0,0,0);
     this._zoomToRect(rect);
-    this._isZoomedIn = false;
   },
 
   _zoomToElement: function(aElement) {
     let rect = getBoundingContentRect(aElement);
     this._inflateRect(rect, kZoomToElementMargin);
     this._zoomToRect(rect);
-    this._isZoomedIn = true;
   },
 
   _inflateRect: function(aRect, aMargin) {
     aRect.left -= aMargin;
     aRect.top -= aMargin;
     aRect.bottom += aMargin;
     aRect.right += aMargin;
   },
 
   _zoomToRect: function (aRect) {
     let utils = Util.getWindowUtils(content);
     let viewId = utils.getViewId(content.document.documentElement);
     let presShellId = {};
     utils.getPresShellId(presShellId);
-    let zoomData = [aRect.x,
-                    aRect.y,
-                    aRect.width,
-                    aRect.height,
-                    presShellId.value,
-                    viewId].join(",");
-    Services.obs.notifyObservers(null, "apzc-zoom-to-rect", zoomData);
+    sendAsyncMessage("Content:ZoomToRect", {
+      rect: aRect,
+      presShellId: presShellId.value,
+      viewId: viewId,
+    });
   },
 
   _shouldZoomToElement: function(aElement) {
     let win = aElement.ownerDocument.defaultView;
     if (win.getComputedStyle(aElement, null).display == "inline") {
       return false;
     }
     else if (aElement instanceof Ci.nsIDOMHTMLLIElement) {