Bug 496644 - when going back or forward, use the last pan x, y offsets [r=vingtetun]
authorBenjamin Stover <bstover@mozilla.com>
Thu, 10 Dec 2009 09:55:27 -0800
changeset 65898 e4f92c53ef4b026a8da45f0ab4907990bc3acde9
parent 65897 ca9809d6d5f2e8b8fe884469827d37343ac0f1da
child 65899 0c7200cfc1dc183e8b10d56de85844dca000a36b
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)
reviewersvingtetun
bugs496644
Bug 496644 - when going back or forward, use the last pan x, y offsets [r=vingtetun]
mobile/chrome/content/BrowserView.js
mobile/chrome/content/browser.js
--- a/mobile/chrome/content/BrowserView.js
+++ b/mobile/chrome/content/BrowserView.js
@@ -522,21 +522,18 @@ BrowserView.prototype = {
 
     tm.dirtyRects(rects, this.isRendering());
   },
 
   /** If browser scrolls, pan content to new scroll area. */
   handlePageScroll: function handlePageScroll(aEvent) {
     if (aEvent.target != this._browser.contentDocument)
       return;
-
-    let { x: scrollX, y: scrollY } = BrowserView.Util.getContentScrollOffset(this._browser);
-    Browser.contentScrollboxScroller.scrollTo(this.browserToViewport(scrollX), 
-                                              this.browserToViewport(scrollY));
-    this.onAfterVisibleMove();
+    // XXX shouldn't really make calls to Browser
+    Browser.scrollContentToBrowser();
   },
 
   handleMozScrolledAreaChanged: function handleMozScrolledAreaChanged(ev) {
     let tab = Browser.getTabForDocument(ev.originalTarget);
     if (!tab)
       return;
 
     let browser = tab.browser;
@@ -548,27 +545,34 @@ BrowserView.prototype = {
     let h = ev.height;
 
     // 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.
     if (x < 0) w += x;
     if (y < 0) h += y;
 
+    let vis = this.getVisibleRect();
     let viewport = bvs.viewportRect;
     let oldRight = viewport.right;
     let oldBottom = viewport.bottom;
     viewport.right  = bvs.zoomLevel * w;
     viewport.bottom = bvs.zoomLevel * h;
 
     if (browser == this._browser) {
       // Page has now loaded enough to allow zooming.
       let sizeChanged = oldRight != viewport.right || oldBottom != viewport.bottom;
       this._viewportChanged(sizeChanged, false);
       this.updateDefaultZoom();
+      if (vis.right > viewport.right || vis.bottom > viewport.bottom) {
+        // Content has shrunk outside of the visible rectangle.
+        // XXX for some reason scroller doesn't know it is outside its bounds
+        Browser.contentScrollboxScroller.scrollBy(0, 0);
+        this.onAfterVisibleMove();
+      }
     }
   },
 
   /** Call when default zoomToPage value may change. */
   updateDefaultZoom: function updateDefaultZoom() {
     let bvs = this._browserViewportState;
     if (!bvs)
       return false;
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -631,16 +631,39 @@ var Browser = {
   },
 
   scrollContentToTop: function scrollContentToTop() {
     this.contentScrollboxScroller.scrollTo(0, 0);
     this.pageScrollboxScroller.scrollTo(0, 0);
     this._browserView.onAfterVisibleMove();
   },
 
+  /** Let current browser's scrollbox know about where content has been panned. */
+  scrollBrowserToContent: function scrollBrowserToContent() {
+    let browser = this.selectedBrowser;
+    if (browser) {
+      let scroll = Browser.getScrollboxPosition(Browser.contentScrollboxScroller);
+      let windowUtils = BrowserView.Util.getBrowserDOMWindowUtils(browser);
+      browser.contentWindow.scrollTo(scroll.x, scroll.y);
+    }
+  },
+
+  /** Update viewport to location of browser's scrollbars. */
+  scrollContentToBrowser: function scrollContentToBrowser() {
+    let bv = this._browserView;
+    let pos = BrowserView.Util.getContentScrollOffset(this.selectedBrowser);
+    pos.map(bv.browserToViewport);
+    if (pos.y != 0)
+      Browser.hideTitlebar();
+    else
+      Browser.pageScrollboxScroller.scrollTo(0, 0);
+    Browser.contentScrollboxScroller.scrollTo(pos.x, pos.y);
+    bv.onAfterVisibleMove();
+  },
+
   hideSidebars: function scrollSidebarsOffscreen() {
     let container = this.contentScrollbox;
     let rect = container.getBoundingClientRect();
     this.controlsScrollboxScroller.scrollBy(Math.round(rect.left), 0);
     this._browserView.onAfterVisibleMove();
   },
 
   hideTitlebar: function hideTitlebar() {
@@ -2433,20 +2456,16 @@ ProgressController.prototype = {
     this._hostChanged = true;
     
     if (location != this.browser.lastSpec) {
       this.browser.lastSpec = this.browser.currentURI.spec;
       Browser.removeTransientNotificationsForTab(this._tab);
 
       if (this._tab == Browser.selectedTab) {
         BrowserUI.updateURI();
-
-        // We're about to have new page content, to scroll the content area
-        // to the top so the new paints will draw correctly.
-        Browser.scrollContentToTop();
       }
     }
   },
 
   /**
    * This method is called to indicate a status changes for the currently
    * loading page.  The message is already formatted for display.
    */
@@ -2605,16 +2624,17 @@ var OfflineApps = {
 function Tab() {
   this._id = null;
   this._browser = null;
   this._browserViewportState = null;
   this._state = null;
   this._listener = null;
   this._loading = false;
   this._chromeTab = null;
+  this._resizeAndPaint = Util.bind(this._resizeAndPaint, this);
 
   // Set to 0 since new tabs that have not been viewed yet are good tabs to
   // toss if app needs more memory.
   this.lastSelected = 0;
 
   this.create();
 }
 
@@ -2633,46 +2653,68 @@ Tab.prototype = {
 
   /**
    * Throttles redraws to once every 2 seconds while loading the page, zooming to fit page if
    * user hasn't started zooming.
    */
   _resizeAndPaint: function() {
     let bv = Browser._browserView;
     bv.commitBatchOperation();
+
+    if (this._loadingPaintCount == 0)
+      Browser.scrollContentToTop();
+
     if (this._loading) {
       // kick ourselves off 2s later while we're still loading
       bv.beginBatchOperation();
-      this._loadingTimeout = setTimeout(Util.bind(this._resizeAndPaint, this), 2000);
+      this._loadingTimeout = setTimeout(this._resizeAndPaint, 2000);
     } else {
       delete this._loadingTimeout;
     }
+    this._loadingPaintCount++;
+  },
+
+  _startResizeAndPaint: function() {
+    this._loadingTimeout = setTimeout(this._resizeAndPaint, 2000);
+    this._loadingPaintCount = 0;
+  },
+
+  _stopResizeAndPaint: function() {
+    if (this._loadingTimeout) {
+      Browser._browserView.commitBatchOperation();
+      clearTimeout(this._loadingTimeout);
+      delete this._loadingTimeout;
+    }
   },
 
   /** Returns tab's identity state for updating security UI. */
   getIdentityState: function() {
     return this._listener.state;
   },
 
   startLoading: function() {
     this._loading = true;
     let bvs = this._browserViewportState;
     bvs.defaultZoomLevel = bvs.zoomLevel; // ensures zoom level is reset on new pages
 
     if (!this._loadingTimeout) {
       Browser._browserView.beginBatchOperation();
       Browser._browserView.invalidateEntireView();
-      this._loadingTimeout = setTimeout(Util.bind(this._resizeAndPaint, this), 2000);
+      // Sync up browser so previous and forward scroll positions are set. This is a good time to do
+      // this because the resulting invalidation is irrelevant.
+      Browser.scrollBrowserToContent();
+      this._startResizeAndPaint();
     }
   },
 
   endLoading: function() {
     // Determine at what resolution the browser is rendered based on meta tag
     let browser = this._browser;
     let metaData = Util.contentIsHandheld(browser);
+    let bv = Browser._browserView;
 
     if (metaData.reason == "handheld" || metaData.reason == "doctype") {
       browser.className = "browser-handheld";
     } else if (metaData.reason == "viewport") {
       let screenW = window.innerWidth;
       let screenH = window.innerHeight;
       let viewportW = metaData.width; 
       let viewportH = metaData.height;
@@ -2690,23 +2732,28 @@ Tab.prototype = {
       browser.className = "browser-viewport";
       browser.style.width = viewportW + "px";
       browser.style.height = viewportH + "px";
     } else {
       browser.className = "browser";
     }
 
     this.setIcon(browser.mIconURL);
-
     this._loading = false;
-    clearTimeout(this._loadingTimeout);
-
-    // in order to ensure we commit our current batch,
-    // we need to run this function here
-    this._resizeAndPaint();
+
+    // Don't render until pane has been scrolled to the correct position.
+    bv.pauseRendering();
+    this._stopResizeAndPaint();
+    // XXX Sometimes MozScrollSizeChange has not occurred, so the scroll pane will not
+    // be resized yet. We are assuming this event is on the queue, so scroll the pane
+    // "soon."
+    Util.executeSoon(function() {
+      Browser.scrollContentToBrowser();
+      bv.resumeRendering();
+    });
 
     // if this tab was sacrificed previously, restore its state
     this.restoreState();
   },
 
   isLoading: function() {
     return this._loading;
   },
@@ -2758,23 +2805,18 @@ Tab.prototype = {
     this._listener = new ProgressController(this);
     browser.addProgressListener(this._listener);
   },
 
   _destroyBrowser: function() {
     if (this._browser) {
       document.getElementById("browsers").removeChild(this._browser);
       this._browser = null;
-
-      if (this._loading) {
-        this._loading = false;
-        Browser._browserView.commitBatchOperation();
-        clearTimeout(this._loadingTimeout);
-        delete this._loadingTimeout;
-      }
+      this._loading = false;
+      this._stopResizeAndPaint();
     }
   },
 
   /** Serializes as much state as possible of the current content.  */
   saveState: function() {
     let state = { };
 
     var browser = this._browser;