[mq]: review
authorBenjamin Stover <bstover@mozilla.com>
Wed, 15 Sep 2010 15:05:50 -0700
changeset 66651 098bbfa2b01e8eed3842ce9bddad469baa4c8bab
parent 66650 98bc118caee2b337187f7116eda2a83ab2f30db5
child 66652 3806bd6b1e3d30aa48a4b3da209a50bdcd9f73c9
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
[mq]: review
mobile/app/mobile.js
mobile/chrome/content/AnimatedZoom.js
mobile/chrome/content/InputHandler.js
mobile/chrome/content/bindings/browser.js
mobile/chrome/content/bindings/browser.xml
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.js
mobile/chrome/content/content.js
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -32,18 +32,18 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #filter substitution
 
 // for browser.xml binding
-pref("toolkit.browser.cachePixelX", 400);
-pref("toolkit.browser.cachePixelY", 1000);
+pref("toolkit.browser.cachePixelX", 800);
+pref("toolkit.browser.cachePixelY", 2000);
 pref("toolkit.browser.recacheRatio", 60);
 
 pref("toolkit.defaultChromeURI", "chrome://browser/content/browser.xul");
 pref("general.useragent.compatMode.firefox", true);
 pref("browser.chromeURL", "chrome://browser/content/");
 
 pref("browser.tabs.warnOnClose", true);
 #ifdef MOZ_IPC
@@ -368,17 +368,17 @@ pref("browser.console.showInPanel", fals
 // kinetic tweakables
 pref("browser.ui.kinetic.updateInterval", 30);
 pref("browser.ui.kinetic.decelerationRate", 20);
 pref("browser.ui.kinetic.speedSensitivity", 80);
 pref("browser.ui.kinetic.swipeLength", 160);
 
 // zooming
 pref("browser.ui.zoom.pageFitGranularity", 9); // don't zoom to fit by less than 1/9
-pref("browser.ui.zoom.animationDuration", 350); // ms duration of double-tap zoom animation
+pref("browser.ui.zoom.animationDuration", 200); // ms duration of double-tap zoom animation
 
 // pinch gesture
 pref("browser.ui.pinch.maxGrowth", 150);     // max pinch distance growth
 pref("browser.ui.pinch.maxShrink", 200);     // max pinch distance shrinkage
 pref("browser.ui.pinch.scalingFactor", 500); // scaling factor for above pinch limits
 
 // Touch radius (area around the touch location to look for target elements):
 pref("browser.ui.touch.left", 8);
--- a/mobile/chrome/content/AnimatedZoom.js
+++ b/mobile/chrome/content/AnimatedZoom.js
@@ -41,42 +41,40 @@
 
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 /**
  * Responsible for zooming in to a given view rectangle
  */
-const animatedZoom = {
+const AnimatedZoom = {
   /** Starts an animated zoom to zoomRect. */
   animateTo: function(aZoomRect) {
     if (!aZoomRect)
       return;
 
     this.zoomTo = aZoomRect.clone();
 
     if (this.animationDuration === undefined)
       this.animationDuration = Services.prefs.getIntPref("browser.ui.zoom.animationDuration");
 
     Browser.hideSidebars();
     Browser.hideTitlebar();
     Browser.forceChromeReflow();
 
-    this.beginTime = Date.now();
+    this.beginTime = mozAnimationStartTime;
 
     // Check if zooming animations were occuring before.
     if (this.zoomRect) {
       this.zoomFrom = this.zoomRect;
-    }
-    else {
+    } else {
       let browserRect = Rect.fromRect(getBrowser().getBoundingClientRect());
-      let scrollX = {}, scrollY = {};
-      getBrowser().getPosition(scrollX, scrollY);
-      this.zoomFrom = browserRect.translate(scrollX.value, scrollY.value);
+      let scroll = getBrowser().getPosition();
+      this.zoomFrom = browserRect.translate(scroll.x, scroll.y);
       this.updateTo(this.zoomFrom);
 
       window.addEventListener("MozBeforePaint", this, false);
       mozRequestAnimationFrame();
     }
   },
 
   /** Updates the zoom to new rect. */
@@ -104,23 +102,21 @@ const animatedZoom = {
   },
 
   handleEvent: function(aEvent) {
     try {
       let tdiff = aEvent.timeStamp - this.beginTime;
       let counter = tdiff / this.animationDuration;
       if (counter < 1) {
         // update browser to interpolated rectangle
-        let rect = this.zoomFrom.blend(this.zoomTo, Math.min(counter, 1));
+        let rect = this.zoomFrom.blend(this.zoomTo, counter);
         this.updateTo(rect);
         mozRequestAnimationFrame();
-      }
-      else {
+      } else {
         // last cycle already rendered final scaled image, now clean up
         this.finish();
       }
-    }
-    catch(e) {
+    } catch(e) {
       this.finish();
       throw e;
     }
   }
 };
--- a/mobile/chrome/content/InputHandler.js
+++ b/mobile/chrome/content/InputHandler.js
@@ -427,30 +427,30 @@ MouseModule.prototype = {
     dragData.reset();
 
     // walk up the DOM tree in search of nearest scrollable ancestor.  nulls are
     // returned if none found.
     let [targetScrollbox, targetScrollInterface, dragger]
       = this.getScrollboxFromElement(aEvent.target);
 
     // stop kinetic panning if targetScrollbox has changed
-    let oldDragger = this._dragger;
     if (this._kinetic.isActive() && this._dragger != dragger)
       this._kinetic.end();
 
     let targetClicker = this.getClickerFromElement(aEvent.target);
 
     this._targetScrollInterface = targetScrollInterface;
     this._dragger = dragger;
     this._clicker = (targetClicker) ? targetClicker.customClicker : null;
 
     if (this._clicker)
       this._clicker.mouseDown(aEvent.clientX, aEvent.clientY);
 
-    if (this._dragger && this._dragger.isDraggable(targetScrollbox, targetScrollInterface))
+    let draggable = this._dragger.isDraggable(targetScrollbox, targetScrollInterface);
+    if (this._dragger && (draggable.x || draggable.y))
       this._doDragStart(aEvent);
 
     if (this._targetIsContent(aEvent)) {
       this._recordEvent(aEvent);
     }
     else {
       if (this._clickTimeout) {
         // cancel all pending content clicks
@@ -1300,35 +1300,34 @@ GestureModule.prototype = {
     // grab events during pinch
     this._owner.grab(this);
 
     // hide element highlight
     // XXX ugh, this is awful. None of this code should be in InputHandler.
     document.getElementById("inputhandler-overlay").customClicker.panBegin();
 
     // create the AnimatedZoom object for fast arbitrary zooming
-    this._pinchZoom = animatedZoom;
+    this._pinchZoom = AnimatedZoom;
 
     // start from current zoom level
     this._pinchZoomLevel = getBrowser().scale;
     this._ignoreNextUpdate = true; // first update gives useless, huge delta
 
     // cache gesture limit values
     this._maxGrowth = Services.prefs.getIntPref("browser.ui.pinch.maxGrowth");
     this._maxShrink = Services.prefs.getIntPref("browser.ui.pinch.maxShrink");
     this._scalingFactor = Services.prefs.getIntPref("browser.ui.pinch.scalingFactor");
 
     // save the initial gesture start point as reference
     [this._pinchStartX, this._pinchStartY] =
         Browser.transformClientToBrowser(aEvent.clientX, aEvent.clientY);
 
-    let scrollX = {}, scrollY = {};
-    getBrowser().getPosition(scrollX, scrollY);
-    this._pinchScrollX = scrollX.value;
-    this._pinchScrollY = scrollY.value;
+    let scroll = getBrowser().getPosition();
+    this._pinchScrollX = scroll.x;
+    this._pinchScrollY = scroll.y
 
     let [centerX, centerY] = Browser.transformClientToBrowser(window.innerWidth / 2,
                                                               window.innerHeight / 2);
     this._centerX = centerX;
     this._centerY = centerY;
   },
 
   _pinchUpdate: function _pinchUpdate(aEvent) {
@@ -1340,20 +1339,19 @@ GestureModule.prototype = {
     this._pinchZoomLevel *= (1 + delta / this._scalingFactor);
     this._pinchZoomLevel = Browser.selectedTab.clampZoomLevel(this._pinchZoomLevel);
 
     // get current pinch position to calculate opposite vector for zoom point
     let [pX, pY] =
         Browser.transformClientToBrowser(aEvent.clientX, aEvent.clientY);
 
     let scale = getBrowser().scale;
-    let scrollX = {}, scrollY = {};
-    getBrowser().getPosition(scrollX, scrollY);
-    pX += (this._pinchScrollX - scrollX.value) / scale;
-    pY += (this._pinchScrollY - scrollY.value) / scale;
+    let scroll = getBrowser().getPosition();
+    pX += (this._pinchScrollX - scroll.x) / scale;
+    pY += (this._pinchScrollY - scroll.y) / scale;
 
     // redraw zoom canvas according to new zoom rect
     let rect = Browser._getZoomRectForPoint(this._centerX + this._pinchStartX - pX,
                                             this._centerY + this._pinchStartY - pY,
                                             this._pinchZoomLevel);
     this._pinchZoom.updateTo(rect);
   },
 
--- a/mobile/chrome/content/bindings/browser.js
+++ b/mobile/chrome/content/bindings/browser.js
@@ -362,17 +362,17 @@ let ContentScroll =  {
         break;
 
       case "Content:SetCacheViewport": {
         let displayport = new Rect(json.x, json.y, json.w, json.h);
         if (displayport.isEmpty())
           break;
 
         let cwu = Util.getWindowUtils(content);
-        cwu.setResolution(json.zoomLevel, json.zoomLevel);
+        cwu.setResolution(json.scale, json.scale);
         cwu.setDisplayPort(displayport.x, displayport.y, displayport.width, displayport.height);
         break;
       }
 
       case "Content:SetCssViewportSize": {
         let cwu = Util.getWindowUtils(content);
         cwu.setCSSViewport(json.width, json.height);
         break;
@@ -383,21 +383,21 @@ let ContentScroll =  {
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "scroll":
         Util.dumpLn("XXX stub");
         break;
 
       case "MozScrolledAreaChanged": {
         let doc = aEvent.originalTarget;
-        let win = doc.defaultView;
+        if (content != doc.defaultView) // We are only interested in root scroll pane changes
+          return;
+
         // XXX need to make some things in Util as its own module!
-        let scrollOffset = Util.getScrollOffset(win);
-        if (win.parent != win) // We are only interested in root scroll pane changes
-          return;
+        let scrollOffset = Util.getScrollOffset(content);
 
         // Adjust width and height from the incoming event properties so that we
         // ignore changes to width and height contributed by growth in page
         // quadrants other than x > 0 && y > 0.
         let x = aEvent.x + scrollOffset.x;
         let y = aEvent.y + scrollOffset.y;
         let width = aEvent.width + (x < 0 ? x : 0);
         let height = aEvent.height + (y < 0 ? y : 0);
--- a/mobile/chrome/content/bindings/browser.xml
+++ b/mobile/chrome/content/bindings/browser.xml
@@ -142,18 +142,18 @@
                   break;
                 case "search":
                   this._searchEngines.push({ title: link.title, href: link.href });
                   break;
               }
               break;
 
             case "MozScrolledAreaChanged":
-              this._widthInCSSPx = aMessage.json.width;
-              this._heightInCSSPx = aMessage.json.height;
+              this._contentDocumentWidth = aMessage.json.width;
+              this._contentDocumentHeight = aMessage.json.height;
               this._updateCacheViewport();
               break;
          }
         ]]></body>
       </method>
 
       <method name="_getLinkType">
         <parameter name="aLink" />
@@ -258,17 +258,17 @@
                 ];
 
                 if (this._browser.contentWindowId != json.windowId) {
                   this._browser.contentWindowId = json.windowId;
                   this._browser._documentURI = json.documentURI;
                   this._browser._searchEngines = [];
                 }
 
-                this._browser._zoomLevel = 1;
+                this._browser.scale = 1;
                 this._browser._updateCacheViewport();
 
                 this._notify(Components.interfaces.nsIWebProgress.NOTIFY_LOCATION,
                              "onLocationChange",
                              args);
 
                 break;
 
@@ -380,115 +380,115 @@
             this.updatePageReport();
           ]]>
         </body>
       </method>
 
       <field name="_frameLoader">null</field>
 
       <!-- Dimensions of content window -->
-      <property name="viewportWidthInCSSPx"
-                onget="return this._viewportWidthInCSSPx;"
+      <property name="contentWindowWidth"
+                onget="return this._contentWindowWidth;"
                 readonly="true"/>
-      <property name="viewportHeightInCSSPx"
-                onget="return this._viewportHeightInCSSPx;"
+      <property name="contentWindowHeight"
+                onget="return this._contentWindowHeight;"
                 readonly="true"/>
 
       <!-- Dimensions of content document -->
-      <field name="_widthInCSSPx">0</field>
-      <field name="_heightInCSSPx">0</field>
-      <property name="_widthInDevicePx"
-                onget="return this._widthInCSSPx * this._zoomLevel;"
+      <field name="_contentDocumentWidth">0</field>
+      <field name="_contentDocumentHeight">0</field>
+      <property name="contentDocumentWidth"
+                onget="return this._contentDocumentWidth;"
                 readonly="true"/>
-      <property name="_heightInDevicePx"
-                onget="return this._heightInCSSPx * this._zoomLevel;"
+      <property name="contentDocumentHeight"
+                onget="return this._contentDocumentHeight;"
                 readonly="true"/>
 
       <!-- Zoom level is the ratio of device pixels to CSS pixels -->
-      <field name="_zoomLevel">1</field>
+      <field name="_scale">1</field>
       <property name="scale"
-                onget="return this._zoomLevel;"
-                readonly="true"/>
+                onget="return this._scale;"
+                onset="return this._setScale(val);"/>
 
       <!-- These counters are used to update the cached viewport after they reach a certain
            threshold when scrolling -->
       <field name="_pendingPixelsX">0</field>
       <field name="_pendingPixelsY">0</field>
       <field name="_pendingThresholdX">0</field>
       <field name="_pendingThresholdY">0</field>
       <!-- This determines what percentage of cached pixels are not yet visible before the cache
-           is refreshed. For instance, if we recached at 50% and there are originally a total of
-           400px offscreen, we'd refresh once 200 of those pixels have been scrolled into
+           is refreshed. For instance, if we recached at 25% and there are originally a total of
+           400px offscreen, we'd refresh once 100 of those pixels have been scrolled into
            view. -->
-      <field name="_recacheRatio">0</field>
+      <field name="_recacheRatio">1</field>
 
       <!-- The cache viewport is what parts of content is cached in the parent process for
            fast scrolling. This syncs that up with the current projection viewport. -->
       <method name="_updateCacheViewport">
         <body>
           <![CDATA[
             let cacheX = this._pendingThresholdX / this._recacheRatio;
             let cacheY = this._pendingThresholdY / this._recacheRatio;
             let bcr = this.getBoundingClientRect();
 
             let frameLoader = this._frameLoader;
             this.messageManager.sendAsyncMessage("Content:SetCacheViewport", {
-              x: (frameLoader.viewportScrollX + bcr.width  / 2 - cacheX) / this._zoomLevel,
-              y: (frameLoader.viewportScrollY + bcr.height / 2 - cacheY) / this._zoomLevel,
-              w: (cacheX * 2) / this._zoomLevel,
-              h: (cacheY * 2) / this._zoomLevel,
-              zoomLevel: this._zoomLevel
+              x: (frameLoader.viewportScrollX + bcr.width  / 2 - cacheX) / this._scale,
+              y: (frameLoader.viewportScrollY + bcr.height / 2 - cacheY) / this._scale,
+              w: (cacheX * 2) / this._scale,
+              h: (cacheY * 2) / this._scale,
+              scale: this._scale
             });
 
             this._pendingPixelsX = 0;
             this._pendingPixelsY = 0;
           ]]>
         </body>
       </method>
 
       <!-- Synchronize the CSS viewport with the projection viewport. -->
       <method name="_updateCSSViewport">
         <body>
           <![CDATA[
             let frameLoader = this._frameLoader;
             this.messageManager.sendAsyncMessage("Content:ScrollTo", {
-              x: frameLoader.viewportScrollX / this._zoomLevel,
-              y: frameLoader.viewportScrollY / this._zoomLevel
+              x: frameLoader.viewportScrollX / this._scale,
+              y: frameLoader.viewportScrollY / this._scale
             });
           ]]>
         </body>
       </method>
 
       <!-- Sets the scale of CSS pixels to device pixels. Does not affect page layout. -->
-      <method name="setScale">
-        <parameter name="zl"/>
+      <method name="_setScale">
+        <parameter name="scale"/>
         <body>
           <![CDATA[
-            if (zl <= 0) throw "Bad zoom level given.";
+            if (scale <= 0) throw "Bad scale given.";
 
-            this._zoomLevel = zl;
-            this._frameLoader.setViewportScale(zl, zl);
+            this._scale = scale;
+            this._frameLoader.setViewportScale(scale, scale);
             this._updateCacheViewport();
 
             let event = document.createEvent("Events");
             event.initEvent("ZoomChanged", true, false);
             this.dispatchEvent(event);
           ]]>
         </body>
       </method>
 
       <!-- Sets size of CSS viewport, which affects how page is layout. -->
-      <method name="setCssViewportSize">
+      <method name="setWindowSize">
         <parameter name="width"/>
         <parameter name="height"/>
         <body>
           <![CDATA[
-            this._viewportWidthInCSSPx = width;
-            this._viewportHeightInCSSPx = height;
-            this.messageManager.sendAsyncMessage("Content:SetCssViewportSize", {
+            this._contentWindowWidth = width;
+            this._contentWindowHeight = height;
+            this.messageManager.sendAsyncMessage("Content:SetWindowSize", {
               width: width,
               height: height
             });
           ]]>
         </body>
       </method>
 
       <!-- Scroll viewport by (x, y) device pixels. -->
@@ -514,19 +514,21 @@
       </method>
 
       <!-- Get position of viewport in device pixels. -->
       <method name="getPosition">
         <parameter name="scrollX"/>
         <parameter name="scrollY"/>
         <body>
           <![CDATA[
+            let scrollX = {}, scrollY = {};
             let cwu = this.contentWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
                       getInterface(Components.interfaces.nsIDOMWindowUtils);
             cwu.getScrollXY(false, scrollX, scrollY);
+            return { x: scrollX.value, y: scrollY.value };
           ]]>
         </body>
       </method>
 
       <constructor>
         <![CDATA[
           this._frameLoader = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
 
@@ -736,17 +738,17 @@
                 ];
 
                 if (this._browser.contentWindowId != json.windowId) {
                   this._browser.contentWindowId = json.windowId;
                   this._browser._documentURI = json.documentURI;
                   this._browser._searchEngines = [];
                 }
 
-                this._browser._zoomLevel = 1;
+                this._browser._scale = 1;
                 this._browser._updateCacheViewport();
 
                 this._notify(Components.interfaces.nsIWebProgress.NOTIFY_LOCATION,
                              "onLocationChange",
                              args);
 
                 break;
 
@@ -809,29 +811,34 @@
       <property name="contentViewerFile"
                 onget="throw 'contentViewerFile: Not Remoteable'"
                 readonly="true"/>
 
       <property name="documentCharsetInfo"
                 onget="throw 'documentCharsetInfo: Not Remoteable'"
                 readonly="true"/>
 
+      <!-- Scroll by (x, y) device pixels -->
       <method name="scrollBy">
         <parameter name="x"/>
         <parameter name="y"/>
         <body>
           <![CDATA[
             let frameLoader = this._frameLoader;
 
+            // Bounding content rectangle is in device pixels
             let bcr = this.getBoundingClientRect();
             let viewportWidth = bcr.width;
             let viewportHeight = bcr.height;
+            // Calculate document dimensions in device pixels
+            let docWidth = this.contentDocumentWidth * this.scale;
+            let docHeight = this.contentDocumentHeight * this.scale;
 
-            x = Math.floor(Math.max(0, Math.min(this._widthInDevicePx - viewportWidth, frameLoader.viewportScrollX + x)) - frameLoader.viewportScrollX);
-            y = Math.floor(Math.max(0, Math.min(this._heightInDevicePx - viewportHeight, frameLoader.viewportScrollY + y)) - frameLoader.viewportScrollY);
+            x = Math.floor(Math.max(0, Math.min(docWidth - viewportWidth, frameLoader.viewportScrollX + x)) - frameLoader.viewportScrollX);
+            y = Math.floor(Math.max(0, Math.min(docHeight - viewportHeight, frameLoader.viewportScrollY + y)) - frameLoader.viewportScrollY);
 
             if (x == 0 && y == 0)
               return;
 
             frameLoader.scrollViewportBy(x, y);
 
             // Add this to the amount of pixels we have "used" from our cache. When this hits the
             // threshold, we will refresh.
@@ -840,35 +847,34 @@
 
             if (Math.abs(this._pendingPixelsX) > Math.max(0, this._pendingThresholdX - bcr.width / 2) ||
                 Math.abs(this._pendingPixelsY) > Math.max(0, this._pendingThresholdY - bcr.height / 2))
               this._updateCacheViewport();
           ]]>
         </body>
       </method>
 
+      <!-- Scroll to position (x, y) in device pixels -->
       <method name="scrollTo">
         <parameter name="x"/>
         <parameter name="y"/>
         <body>
           <![CDATA[
             let frameLoader = this._frameLoader;
             this.scrollBy(x - frameLoader.viewportScrollX, y - frameLoader.viewportScrollY);
           ]]>
         </body>
       </method>
 
+      <!-- Get position of window in device pixels -->
       <method name="getPosition">
-        <parameter name="scrollX"/>
-        <parameter name="scrollY"/>
         <body>
           <![CDATA[
             let frameLoader = this._frameLoader;
-            scrollX.value = frameLoader.viewportScrollX;
-            scrollY.value = frameLoader.viewportScrollY;
+            return { x: frameLoader.viewportScrollX, y: frameLoader.viewportScrollY };
           ]]>
         </body>
       </method>
     </implementation>
 
   </binding>
 
 </bindings>
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -1052,44 +1052,44 @@ var BrowserUI = {
 
 var TapHighlightHelper = {
   get _overlay() {
     delete this._overlay;
     return this._overlay = document.getElementById("content-overlay");
   },
 
   show: function show(aRects) {
-    let scrollX = {}, scrollY = {};
-    getBrowser().getPosition(scrollX, scrollY);
+    let browser = getBrowser();
+    let scroll = browser.getPosition();
 
     let union = aRects.reduce(function(a, b) {
       return a.expandToContain(b);
-    }, new Rect(0, 0, 0, 0)).map(function(val) { return val * getBrowser().scale; })
-                            .translate(-scrollX.value, -scrollY.value);
-
-    let vis = Rect.fromRect(getBrowser().getBoundingClientRect());
+    }, new Rect(0, 0, 0, 0)).map(function(val) val * browser.scale)
+                            .translate(-scroll.x, -scroll.y);
+
+    let vis = Rect.fromRect(browser.getBoundingClientRect());
     let canvasArea = vis.intersect(union);
 
     let overlay = this._overlay;
     overlay.width = canvasArea.width;
     overlay.style.width = canvasArea.width + "px";
     overlay.height = canvasArea.height;
     overlay.style.height = canvasArea.height + "px";
 
     let ctx = overlay.getContext("2d");
     ctx.save();
     ctx.translate(-canvasArea.left, -canvasArea.top);
-    ctx.scale(getBrowser().scale, getBrowser().scale);
+    ctx.scale(browser.scale, browser.scale);
 
     overlay.style.left = canvasArea.left + "px";
     overlay.style.top = canvasArea.top + "px";
     ctx.fillStyle = "rgba(0, 145, 255, .5)";
     for (let i = aRects.length - 1; i >= 0; i--) {
       let rect = aRects[i];
-      ctx.fillRect(rect.left - scrollX.value / getBrowser().scale, rect.top - scrollY.value / getBrowser().scale, rect.width, rect.height);
+      ctx.fillRect(rect.left - scroll.x / browser.scale, rect.top - scroll.y / browser.scale, rect.width, rect.height);
     }
     ctx.restore();
     overlay.style.display = "block";
   },
 
   hide: function hide() {
     this._overlay.style.display = "none";
   }
@@ -1872,16 +1872,17 @@ 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 browser = getBrowser();
     if (aElementRect && aCaretRect && this._open) {
       this._currentCaretRect = aCaretRect;
 
       let 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;
@@ -1924,17 +1925,17 @@ var FormHelperUI = {
       } else {
         harmonizedCaretHeight = 30; // fallback height
 
         // use the element as position
         harmonizedCaretY = aElementRect.y;
         aCaretRect.x = aElementRect.x;
       }
 
-      let zoomLevel = getBrowser().scale;
+      let zoomLevel = browser.scale;
       let enableZoom = Browser.selectedTab.allowZoom && Services.prefs.getBoolPref("formhelper.autozoom");
       if (enableZoom) {
         zoomLevel = (viewAreaHeight / caretLines) / harmonizedCaretHeight;
         zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax);
       }
       viewAreaWidth /= zoomLevel;
 
       const margin = Services.prefs.getIntPref("formhelper.margin");
@@ -1942,35 +1943,34 @@ var FormHelperUI = {
       // 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 adjustet Caret Y minus a margin four our visible rect
       let y = harmonizedCaretY - margin;
-
-      let scrollX = {}, scrollY = {};
-      getBrowser().getPosition(scrollX, scrollY);
-      let vis = new Rect(scrollX.value, scrollY.value, window.innerWidth, window.innerHeight);
-      x *= getBrowser().scale;
-      y *= getBrowser().scale;
+      x *= browser.scale;
+      y *= browser.scale;
+
+      let scroll = browser.getPosition(scrollX, scrollY);
+      let vis = new Rect(scroll.x, scroll.y, window.innerWidth, window.innerHeight);
 
       // from here on play with zoomed values
       // if we want to have it animated, build up zoom rect and animate.
-      if (enableZoom && getBrowser().scale != zoomLevel) {
+      if (enableZoom && browser.scale != zoomLevel) {
         // don't use browser functions they are bogus for this case
-        let zoomRatio = zoomLevel / getBrowser().scale;
+        let zoomRatio = zoomLevel / browser.scale;
         let newVisW = vis.width / zoomRatio, newVisH = vis.height / zoomRatio;
         let zoomRect = new Rect(x, y, newVisW, newVisH);
 
         Browser.animatedZoomTo(zoomRect);
       }
       else { // no zooming at all
-        getBrowser().scrollBy(x - vis.x, y - vis.y);
+        browser.scrollBy(x - vis.x, y - vis.y);
       }
     }
   },
 
   /* Store the current zoom level, and scroll positions to restore them if needed */
   _zoomStart: function _formHelperZoomStart() {
     if (!Services.prefs.getBoolPref("formhelper.restore"))
       return;
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -186,17 +186,19 @@ var Browser = {
         getBrowser().scrollBy(x, y);
       },
 
       scrollTo: function(x, y) {
         getBrowser().scrollTo(x, y);
       },
 
       getPosition: function(scrollX, scrollY) {
-        getBrowser().getPosition(scrollX, scrollY);
+        let { x: x, y: y } = getBrowser().getPosition();
+        scrollX.value = x;
+        scrollY.value = y;
       }
     };
 
     /* horizontally scrolling box that holds the sidebars as well as the contentScrollbox */
     let controlsScrollbox = this.controlsScrollbox = document.getElementById("controls-scrollbox");
     this.controlsScrollboxScroller = controlsScrollbox.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
     controlsScrollbox.customDragger = {
       isDraggable: function isDraggable(target, content) { return {}; },
@@ -836,19 +838,16 @@ var Browser = {
 
     let zoomValues = ZoomManager.zoomValues;
     let i = zoomValues.indexOf(ZoomManager.snap(zoomLevel)) + (aDirection < 0 ? 1 : -1);
     if (i >= 0 && i < zoomValues.length)
       zoomLevel = zoomValues[i];
 
     zoomLevel = tab.clampZoomLevel(zoomLevel);
 
-    let scrollX = {}, scrollY = {};
-    tab.browser.getPosition(scrollX, scrollY);
-
     let [centerX, centerY] = this.transformClientToBrowser(window.innerWidth / 2,
                                                            window.innerHeight / 2);
 
     let rect = this._getZoomRectForPoint(centerX, centerY, zoomLevel);
     this.animatedZoomTo(rect);
   },
 
   /** Rect should be in browser coordinates. */
@@ -885,34 +884,35 @@ var Browser = {
     y = y * getBrowser().scale;
 
     zoomLevel = Math.min(ZoomManager.MAX, zoomLevel);
     let zoomRatio = zoomLevel / getBrowser().scale;
     let newVisW = window.innerWidth / zoomRatio, newVisH = window.innerHeight / zoomRatio;
     let result = new Rect(x - newVisW / 2, y - newVisH / 2, newVisW, newVisH);
 
     // Make sure rectangle doesn't poke out of viewport
-    return result.translateInside(new Rect(0, 0, getBrowser()._widthInDevicePx, getBrowser()._heightInDevicePx));
+    return result.translateInside(new Rect(0, 0, getBrowser().contentDocumentWidth, getBrowser().contentDocumentHeight));
   },
 
   animatedZoomTo: function animatedZoomTo(rect) {
-    animatedZoom.animateTo(rect);
+    AnimatedZoom.animateTo(rect);
   },
 
   setVisibleRect: function setVisibleRect(rect) {
+    let browser = getBrowser();
     let zoomRatio = window.innerWidth / rect.width;
-    let zoomLevel = getBrowser().scale * zoomRatio;
+    let zoomLevel = browser.scale * zoomRatio;
     let scrollX = rect.left * zoomRatio;
     let scrollY = rect.top * zoomRatio;
 
     this.hideSidebars();
     this.hideTitlebar();
 
-    getBrowser().setScale(this.selectedTab.clampZoomLevel(zoomLevel));
-    getBrowser().scrollTo(scrollX, scrollY);
+    browser.scale = this.selectedTab.clampZoomLevel(zoomLevel);
+    browser.scrollTo(scrollX, scrollY);
   },
 
   zoomToPoint: function zoomToPoint(cX, cY, aRect) {
     let tab = this.selectedTab;
     if (!tab.allowZoom)
       return null;
 
     let zoomRect = null;
@@ -944,19 +944,18 @@ var Browser = {
     let container = document.getElementById("browsers");
     let containerBCR = container.getBoundingClientRect();
 
     let x0 = Math.round(containerBCR.left);
     let y0;
     if (arguments.length > 1)
       y0 = Math.round(containerBCR.top);
 
-    let scrollX = {}, scrollY = {};
-    getBrowser().getPosition(scrollX, scrollY);
-    return (arguments.length > 1) ? [x - x0 + scrollX.value, y - y0 + scrollY.value] : (x - x0 + scrollX.value);
+    let scroll = getBrowser().getPosition();
+    return (arguments.length > 1) ? [x - x0 + scroll.x, y - y0 + scroll.y] : (x - x0 + scroll.x);
   },
 
   browserViewToClient: function browserViewToClient(x, y) {
     let container = document.getElementById("browsers");
     let containerBCR = container.getBoundingClientRect();
 
     let x0 = Math.round(-containerBCR.left);
     let y0;
@@ -1074,17 +1073,16 @@ Browser.MainDragger.prototype = {
 
     rect = Rect.fromRect(Browser.pageScrollbox.getBoundingClientRect()).map(Math.round);
     if (doffset.x < 0 && rect.right < window.innerWidth)
       x = Math.max(doffset.x, rect.right - window.innerWidth);
     if (doffset.x > 0 && rect.left > 0)
       x = Math.min(doffset.x, rect.left);
 
     let height = document.getElementById("tile-stack").getBoundingClientRect().height;
-    // XXX change
     rect = Rect.fromRect(Browser.contentScrollbox.getBoundingClientRect()).map(Math.round);
     if (doffset.y < 0 && rect.bottom < height)
       y = Math.max(doffset.y, rect.bottom - height);
     if (doffset.y > 0 && rect.top > 0)
       y = Math.min(doffset.y, rect.top);
 
     doffset.subtract(x, y);
     return new Point(x, y);
@@ -2237,38 +2235,30 @@ Tab.prototype = {
         viewportH = viewportW * (screenH / screenW);
       } else if (!validW && validH) {
         viewportW = viewportH * (screenW / screenH);
       } else {
         viewportW = kDefaultBrowserWidth;
         viewportH = kDefaultBrowserWidth * (screenH / screenW);
       }
 
-      browser.setCssViewportSize(viewportW, viewportH);
+      browser.setWindowSize(viewportW, viewportH);
     }
     else {
       let browserBCR = browser.getBoundingClientRect();
       let w = browserBCR.width;
       let h = browserBCR.height;
       if (metaData.defaultZoom != 1.0) {
         let dpiScale = Services.prefs.getIntPref("zoom.dpiScale") / 100;
         w /= dpiScale;
         h /= dpiScale;
       }
 
-      browser.setCssViewportSize(w, h);
+      browser.setWindowSize(w, h);
     }
-
-    // Local XUL documents are not firing MozScrolledAreaChanged
-    /*let contentDocument = browser.contentDocument;
-    if (contentDocument && contentDocument instanceof XULDocument) {
-      let width = contentDocument.documentElement.scrollWidth;
-      let height = contentDocument.documentElement.scrollHeight;
-      BrowserView.Util.ensureMozScrolledAreaEvent(browser, width, height);
-    } */
   },
 
   startLoading: function startLoading() {
     if (this._loading) throw "Already Loading!";
 
     this._loading = true;
   },
 
@@ -2359,37 +2349,35 @@ Tab.prototype = {
       bounded = Math.min(bounded, md.maxZoom);
 
     bounded = Math.max(bounded, this.getPageZoomLevel());
 
     let rounded = Math.round(bounded * kBrowserViewZoomLevelPrecision) / kBrowserViewZoomLevelPrecision;
     return rounded || 1.0;
   },
 
-  /**
-   * XXX document me
-   */
+  /** Record the initial zoom level when a page first loads. */
   resetZoomLevel: function resetZoomLevel() {
-    let browser = this._browser;
-    this._defaultZoomLevel = browser.scale;
+    this._defaultZoomLevel = this._browser.scale;
   },
 
   /**
-   * XXX document me
+   * Recalculate default zoom level when page size changes, and update zoom
+   * level if we are at default.
    */
   updateDefaultZoomLevel: function updateDefaultZoomLevel() {
     let browser = this._browser;
     let isDefault = (browser.scale == this._defaultZoomLevel);
     this._defaultZoomLevel = this.getDefaultZoomLevel();
     if (isDefault)
-      browser.setScale(this._defaultZoomLevel);
+      browser.scale = this._defaultZoomLevel;
   },
 
   isDefaultZoomLevel: function isDefaultZoomLevel() {
-    return getBrowser().scale == this._defaultZoomLevel;
+    return this._browser.scale == this._defaultZoomLevel;
   },
 
   getDefaultZoomLevel: function getDefaultZoomLevel() {
     let md = this.metaData;
     if (md && md.defaultZoom)
       return this.clampZoomLevel(md.defaultZoom);
 
     let pageZoom = this.getPageZoomLevel();
@@ -2399,17 +2387,17 @@ Tab.prototype = {
     let threshold = 1 - 1 / granularity;
     if (threshold < pageZoom && pageZoom < 1)
       pageZoom = 1;
 
     return this.clampZoomLevel(pageZoom);
   },
 
   getPageZoomLevel: function getPageZoomLevel() {
-    let browserW = this._browser._widthInCSSPx;
+    let browserW = this._browser.contentDocumentWidth;
     return this._browser.getBoundingClientRect().width / browserW;
   },
 
   get allowZoom() {
     return this.metaData.allowZoom;
   },
 
   updateThumbnail: function updateThumbnail() {
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -344,23 +344,21 @@ Content.prototype = {
   receiveMessage: function receiveMessage(aMessage) {
     let json = aMessage.json;
     let x = json.x;
     let y = json.y;
     let modifiers = json.modifiers;
 
     switch (aMessage.name) {
       case "Browser:Blur":
-        docShell.isOffScreenBrowser = false;
         docShell.isActive = false;
         this._selected = false;
         break;
 
       case "Browser:Focus":
-        docShell.isOffScreenBrowser = true;
         docShell.isActive = true;
         this._selected = true;
         break;
 
       case "Browser:KeyEvent":
         let utils = Util.getWindowUtils(content);
         let defaultAction = utils.sendKeyEvent(json.type, json.keyCode, json.charCode, modifiers);
         if (defaultAction && json.type == "keypress") {