b=452069, r=gavin. Off-screen UI should slide into view when apnned
authorMark Finkle <mfinkle@mozilla.com>
Wed, 10 Sep 2008 19:38:02 -0500
changeset 64821 6b4f78acd87051c8a513b346293073b0322f311b
parent 64820 60eb18a52ffd0d568cfe8d5dae41be5b95c526be
child 64822 72c6a455a27abd1999a93c33b1d80c43ca0f833d
push idunknown
push userunknown
push dateunknown
reviewersgavin
bugs452069
b=452069, r=gavin. Off-screen UI should slide into view when apnned
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.js
mobile/chrome/content/browser.xul
mobile/chrome/content/deckbrowser.xml
mobile/chrome/skin/browser.css
mobile/chrome/skin/extensions.css
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -34,25 +34,22 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 const TOOLBARSTATE_LOADING        = 1;
 const TOOLBARSTATE_LOADED         = 2;
 const TOOLBARSTATE_INDETERMINATE  = 3;
 
-const PANELMODE_NONE              = 0;
-const PANELMODE_URLVIEW           = 1;
-const PANELMODE_URLEDIT           = 2;
-const PANELMODE_BOOKMARK          = 3;
-const PANELMODE_BOOKMARKLIST      = 4;
-const PANELMODE_ADDONS            = 5;
-const PANELMODE_SIDEBAR           = 6;
-const PANELMODE_TABLIST           = 7;
-const PANELMODE_FULL              = 8;
+const UIMODE_NONE              = 0;
+const UIMODE_URLVIEW           = 1;
+const UIMODE_URLEDIT           = 2;
+const UIMODE_BOOKMARK          = 3;
+const UIMODE_BOOKMARKLIST      = 4;
+const UIMODE_PANEL             = 5;
 
 const kDefaultFavIconURL = "chrome://browser/skin/images/default-favicon.png";
 
 var BrowserUI = {
   _panel : null,
   _caption : null,
   _edit : null,
   _throbber : null,
@@ -93,25 +90,27 @@ var BrowserUI = {
     this._titleChanged(browser.contentDocument);
     this._favicon.setAttribute("src", browser.mIconURL || kDefaultFavIconURL);
 
     let toolbar = document.getElementById("toolbar-main");
     let browserBox = document.getElementById("browser");
     if (Browser.content.currentTab.chromeTop) {
       // Browser box was panned, so let's reset it
       browserBox.top = Browser.content.currentTab.chromeTop;
+      browserBox.left = 0;
       toolbar.top = browserBox.top - toolbar.boxObject.height;
     }
     else {
       // Must be initial conditions
       toolbar.top = 0;
       browserBox.top = toolbar.boxObject.height;
+      browserBox.left = 0;
     }
 
-    this.show(PANELMODE_NONE);
+    this.show(UIMODE_NONE);
   },
 
   _setIcon : function(aURI) {
     var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
     var faviconURI = ios.newURI(aURI, null, null);
 
     var fis = Cc["@mozilla.org/browser/favicon-service;1"].getService(Ci.nsIFaviconService);
     if (faviconURI.schemeIs("javascript") ||
@@ -165,42 +164,57 @@ var BrowserUI = {
       var node = rootNode.getChild(i);
       items.push(node);
     }
     rootNode.containerOpen = false;
 
     return items;
   },
 
-  _dragData :  { dragging : false, sY : 0, sTop : 0 },
+  _dragData :  {
+    dragging : false,
+    startX : 0,
+    startY : 0,
+    dragX : 0,
+    dragY : 0,
+    lastX : 0,
+    lastY : 0,
+    sTop : 0,
+    sLeft : 0
+  },
 
   _scrollToolbar : function bui_scrollToolbar(aEvent) {
+    var [scrollWidth, ] = Browser.content._contentAreaDimensions;
+    var [canvasW, ] = Browser.content._effectiveCanvasDimensions;
+
+    var pannedUI = false;
+
     if (this._dragData.dragging && Browser.content.scrollY == 0) {
       let toolbar = document.getElementById("toolbar-main");
       let browser = document.getElementById("browser");
-      let dy = this._dragData.sY - aEvent.screenY;
+      let dy = this._dragData.lastY - aEvent.screenY;
+      this._dragData.dragY += dy;
+
+      // NOTE: We should only be scrolling the toolbar if the sidebars are not
+      // visible (browser.left == 0)
 
       let newTop = null;
-      if (dy > 0 && toolbar.top > -toolbar.boxObject.height) {
-        // Revert the chrome to a "safe" mode
-        if (this.mode != PANELMODE_URLVIEW)
-          this.show(PANELMODE_URLVIEW);
-
+      if (dy > 0 && (toolbar.top > -toolbar.boxObject.height && browser.left == 0)) {
         // Scroll the toolbar up unless it is already scrolled up
         newTop = this._dragData.sTop - dy;
 
         // Clip the adjustment to just enough to hide the toolbar
         if (newTop < -toolbar.boxObject.height)
           newTop = -toolbar.boxObject.height;
 
         // Reset the browser start point
         Browser.content.dragData.sX = aEvent.screenX;
         Browser.content.dragData.sY = aEvent.screenY;
       }
-      else if (dy < 0 && toolbar.top < 0) {
+      else if (dy < 0 && (toolbar.top < 0 && browser.left == 0)) {
         // Scroll the toolbar down unless it is already down
         newTop = this._dragData.sTop - dy;
 
         // Clip the adjustment to just enough to fully show the toolbar
         if (newTop > 0)
           newTop = 0;
       }
 
@@ -208,68 +222,159 @@ var BrowserUI = {
       // getting to the deckbrowser.
       if (newTop != null) {
         toolbar.top = newTop;
         browser.top = newTop + toolbar.boxObject.height;
 
         // Cache the current top so we can use it when switching tabs
         Browser.content.currentTab.chromeTop = browser.top;
 
-        aEvent.stopPropagation();
+        pannedUI = true;
       }
     }
+
+    if (this._dragData.dragging && (Browser.content.scrollX == 0 || (Browser.content.scrollX + canvasW) == scrollWidth)) {
+      let tabbar = document.getElementById("tab-list-container");
+      let sidebar = document.getElementById("browser-controls");
+      let panelUI = document.getElementById("panel-container");
+      let toolbar = document.getElementById("toolbar-main");
+      let browser = document.getElementById("browser");
+      let dx = this._dragData.lastX - aEvent.screenX;
+      this._dragData.dragX += dx;
+
+      if (Math.abs(this._dragData.screenX - aEvent.screenX) > 30) {
+        let newLeft = this._dragData.sLeft - dx;
+        let oldLeft = tabbar.left;
+
+        let tabbarW = tabbar.boxObject.width;
+        let sidebarW = sidebar.boxObject.width;
+        let browserW = browser.boxObject.width;
+
+        // Limit the panning
+        if (newLeft > 0)
+          newLeft = 0;
+        if (newLeft < -(tabbarW + sidebarW))
+          newLeft = -(tabbarW + sidebarW);
+
+        // Add a "snap" for the tabbar
+        if (Math.abs(newLeft + tabbarW) < 30)
+          newLeft = -tabbarW;
+        tabbar.left = newLeft;
+
+        // Never let the toolbar pan off the screen
+        let newToolbarLeft = newLeft;
+        if (newToolbarLeft < 0)
+          newToolbarLeft = 0;
+        toolbar.left = newToolbarLeft;
+
+        // Make the toolbar appear/disappear depending on the state of the sidebars
+        if (newLeft + tabbarW != 0)
+          toolbar.top = 0;
+        else
+          toolbar.top = browser.top - toolbar.boxObject.height;
+
+        browser.left = newLeft + tabbarW;
+        sidebar.left = newLeft + tabbarW + browserW;
+        panelUI.left = newLeft + tabbarW + browserW + sidebarW;
+
+        pannedUI = true;
+      }
+    }
+
+    if (pannedUI) {
+      aEvent.stopPropagation();
+
+      // Force a sync redraw
+      window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+            .getInterface(Components.interfaces.nsIDOMWindowUtils)
+            .processUpdates();
+    }
     else {
       // Reset our start point while the browser is doing its panning
-      this._dragData.sY = aEvent.screenY;
+      this._dragData.lastX = aEvent.screenX;
+      this._dragData.lastY = aEvent.screenY;
     }
   },
 
-  // This function will always show the toolbar
-  _showToolbar : function() {
+  _showToolbar : function(aShow) {
     var toolbar = document.getElementById("toolbar-main");
     var browser = document.getElementById("browser");
 
-    if (toolbar.top == -toolbar.boxObject.height) {
-      // Float the toolbar over content
-      toolbar.top = 0;
+    if (aShow) {
+      // Always show the toolbar, either by floating or panning
+      if (toolbar.top == -toolbar.boxObject.height) {
+        // Float the toolbar over content
+        toolbar.top = 0;
+      }
+      else if (toolbar.top < 0) {
+        // Partially showing, so show it completely
+        toolbar.top = 0;
+        browser.top = toolbar.boxObject.height;
+      }
     }
-    else if (toolbar.top < 0) {
-      // Partially showing, so show it completely
-      toolbar.top = 0;
-      browser.top = toolbar.boxObject.height;
+    else {
+      // If we are floating the toolbar, then hide it again
+      if (browser.top == 0) {
+        toolbar.top = -toolbar.boxObject.height;
+      }
     }
   },
 
-  // This function will only hide the toolbar if it was floated
-  _hideToolbar : function() {
-    var toolbar = document.getElementById("toolbar-main");
-    var browser = document.getElementById("browser");
+  _showPanel : function(aShow) {
+      let tabbar = document.getElementById("tab-list-container");
+      let sidebar = document.getElementById("browser-controls");
+      let panelUI = document.getElementById("panel-container");
+      let toolbar = document.getElementById("toolbar-main");
+      let browser = document.getElementById("browser");
+
+      let tabbarW = tabbar.boxObject.width;
+      let sidebarW = sidebar.boxObject.width;
+      let browserW = browser.boxObject.width;
 
-    // If we are floating the toolbar, then hide it again
-    if (browser.top == 0) {
-      toolbar.top = -toolbar.boxObject.height;
-    }
+      let newLeft = (aShow ? -browserW : -(tabbarW + sidebarW));
+      tabbar.left = newLeft;
+
+      let newToolbarLeft = newLeft + tabbarW;
+      if (newToolbarLeft < -sidebarW)
+        newToolbarLeft += sidebarW;
+      else if (newToolbarLeft < 0)
+        newToolbarLeft = 0;
+      toolbar.left = newToolbarLeft;
+
+      browser.left = newLeft + tabbarW;
+      sidebar.left = newLeft + tabbarW + browserW;
+      panelUI.left = newLeft + tabbarW + browserW + sidebarW;
+      panelUI.width = browserW;
   },
 
-  _sizeControls : function (aEvent) {
+  _layoutControls : true,
+  _sizeControls : function(aEvent) {
     var rect = document.getElementById("browser-container").getBoundingClientRect();
     var containerW = rect.right - rect.left;
     var containerH = rect.bottom - rect.top;
 
     var browser = document.getElementById("browser");
     browser.width = containerW;
     browser.height = containerH;
 
+    var sidebar = document.getElementById("browser-controls");
+    var panelUI = document.getElementById("panel-container");
+    var tabbar = document.getElementById("tab-list-container");
+    if (this._layoutControls) {
+      tabbar.left = -tabbar.boxObject.width;
+      panelUI.left = containerW + sidebar.boxObject.width;
+      sidebar.left = containerW;
+      sidebar.height = panelUI.height = tabbar.height = containerH;
+      this._layoutControls = false;
+    }
+    panelUI.width = containerW - sidebar.boxObject.width - tabbar.boxObject.width;
+
     var toolbar = document.getElementById("toolbar-main");
-    var sidebar = document.getElementById("browser-controls");
-    var tablist = document.getElementById("tab-list-container");
-    sidebar.left = toolbar.width = containerW;
-    sidebar.height = tablist.height = containerH;
-
     var popup = document.getElementById("popup_autocomplete");
+    toolbar.width = containerW;
     popup.height = containerH - toolbar.boxObject.height;
   },
 
   init : function() {
     this._caption = document.getElementById("urlbar-caption");
     this._caption.addEventListener("click", this, false);
     this._edit = document.getElementById("urlbar-edit");
     this._edit.addEventListener("blur", this, false);
@@ -277,18 +382,16 @@ var BrowserUI = {
     this._edit.addEventListener("input", this, false);
     this._throbber = document.getElementById("urlbar-throbber");
     this._favicon = document.getElementById("urlbar-favicon");
     this._favicon.addEventListener("error", this, false);
     this._autocompleteNavbuttons = document.getElementById("autocomplete_navbuttons");
 
     Browser.content.addEventListener("DOMTitleChanged", this, true);
     Browser.content.addEventListener("DOMLinkAdded", this, true);
-    Browser.content.addEventListener("overpan", this, false);
-    Browser.content.addEventListener("pan", this, true);
 
     document.getElementById("tab-list").addEventListener("TabSelect", this, true);
 
     Browser.content.addEventListener("mousedown", this, true);
     Browser.content.addEventListener("mouseup", this, true);
     Browser.content.addEventListener("mousemove", this, true);
 
     window.addEventListener("resize", this, false);
@@ -298,17 +401,17 @@ var BrowserUI = {
     if (aState == TOOLBARSTATE_INDETERMINATE) {
       this._faviconAdded = false;
       aState = TOOLBARSTATE_LOADED;
       this.setURI();
     }
 
     var toolbar = document.getElementById("toolbar-main");
     if (aState == TOOLBARSTATE_LOADING) {
-      this.show(PANELMODE_URLVIEW);
+      this.show(UIMODE_URLVIEW);
       Browser.content.setLoading(aBrowser);
 
       toolbar.top = 0;
       toolbar.setAttribute("mode", "loading");
       this._throbber.setAttribute("src", "chrome://browser/skin/images/throbber.gif");
       this._favicon.setAttribute("src", "");
       this._faviconAdded = false;
     }
@@ -357,39 +460,39 @@ var BrowserUI = {
 
     try {
       uri = this._URIFixup.createExposableURI(uri);
     } catch (ex) {}
 
     var urlString = uri.spec;
     if (urlString == "about:blank") {
       urlString = "";
-      this.show(PANELMODE_URLEDIT);
+      this.show(UIMODE_URLEDIT);
     }
 
     this._caption.value = urlString;
     this._edit.value = urlString;
   },
 
   goToURI : function(aURI) {
     this._edit.reallyClosePopup();
 
     if (!aURI)
       aURI = this._edit.value;
 
     var flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
     getBrowser().loadURIWithFlags(aURI, flags, null, null);
-    this.show(PANELMODE_URLVIEW);
+    this.show(UIMODE_URLVIEW);
   },
 
   search : function() {
     var queryURI = "http://www.google.com/search?q=" + this._edit.value + "&hl=en&lr=&btnG=Search";
     getBrowser().loadURI(queryURI, null, null, false);
 
-    this.show(PANELMODE_URLVIEW);
+    this.show(UIMODE_URLVIEW);
   },
 
   openDefaultHistory : function () {
     if (!this._edit.value) {
       this._autocompleteNavbuttons.hidden = true;
       this._edit.showHistoryPopup();
     }
   },
@@ -434,129 +537,97 @@ var BrowserUI = {
       button.setAttribute("label", engine.name);
       if (engine.iconURI)
         button.setAttribute("image", engine.iconURI.spec);
       container.insertBefore(button, container.firstChild);
       button.engine = engine;
     }
   },
 
-  mode : PANELMODE_NONE,
+  mode : UIMODE_NONE,
   show : function(aMode) {
     if (this.mode == aMode)
       return;
 
-    if (this.mode == PANELMODE_BOOKMARKLIST && aMode != PANELMODE_BOOKMARKLIST)
+    if (this.mode == UIMODE_BOOKMARKLIST && aMode != UIMODE_BOOKMARKLIST)
       window.removeEventListener("keypress", BrowserUI.closePopup, false);
 
     this.mode = aMode;
 
     var toolbar = document.getElementById("toolbar-main");
     var bookmark = document.getElementById("bookmark-container");
     var urllist = document.getElementById("urllist-container");
-    var sidebar = document.getElementById("browser-controls");
-    var tablist = document.getElementById("tab-list-container");
-    var addons = document.getElementById("addons-container");
     var container = document.getElementById("browser-container");
 
-    if (aMode == PANELMODE_URLVIEW || aMode == PANELMODE_SIDEBAR ||
-        aMode == PANELMODE_TABLIST || aMode == PANELMODE_FULL)
+    if (aMode == UIMODE_URLVIEW)
     {
-      this._showToolbar();
+      this._showToolbar(true);
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._edit.reallyClosePopup();
       this._caption.hidden = false;
       bookmark.hidden = true;
       urllist.hidden = true;
-      addons.hidden = true;
-
-      let sidebarTo = toolbar.boxObject.width;
-      let tablistTo = -tablist.boxObject.width;
-      if (aMode == PANELMODE_SIDEBAR || aMode == PANELMODE_FULL)
-        sidebarTo -= sidebar.boxObject.width;
-      if (aMode == PANELMODE_TABLIST || aMode == PANELMODE_FULL)
-        tablistTo = 0;
-      sidebar.left = sidebarTo;
-      tablist.left = tablistTo;
     }
-    else if (aMode == PANELMODE_URLEDIT) {
-      this._showToolbar();
+    else if (aMode == UIMODE_URLEDIT) {
+      this._showToolbar(true);
       toolbar.setAttribute("mode", "edit");
       this._caption.hidden = true;
       this._edit.hidden = false;
       this._edit.focus();
 
       bookmark.hidden = true;
       urllist.hidden = true;
-      addons.hidden = true;
-      sidebar.left = toolbar.boxObject.width;
-      tablist.left = -tablist.boxObject.width;
     }
-    else if (aMode == PANELMODE_BOOKMARK) {
-      this._showToolbar();
+    else if (aMode == UIMODE_BOOKMARK) {
+      this._showToolbar(true);
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._edit.reallyClosePopup();
       this._caption.hidden = false;
 
       urllist.hidden = true;
-      sidebar.left = toolbar.boxObject.width;
-      tablist.left = -tablist.boxObject.width;
-
       bookmark.hidden = false;
-      addons.hidden = true;
       bookmark.width = container.boxObject.width;
     }
-    else if (aMode == PANELMODE_BOOKMARKLIST) {
-      this._hideToolbar();
+    else if (aMode == UIMODE_BOOKMARKLIST) {
+      this._showToolbar(false);
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._edit.reallyClosePopup();
       this._caption.hidden = false;
 
       window.addEventListener("keypress", this.closePopup, false);
 
       bookmark.hidden = true;
-      addons.hidden = true;
-      sidebar.left = toolbar.boxObject.width;
-      tablist.left = -tablist.boxObject.width;
-
       urllist.hidden = false;
       urllist.width = container.boxObject.width;
       urllist.height = container.boxObject.height;
     }
-    else if (aMode == PANELMODE_ADDONS) {
-      this._showToolbar();
+    else if (aMode == UIMODE_PANEL) {
+      this._showToolbar(true);
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._edit.reallyClosePopup();
       this._caption.hidden = false;
 
       bookmark.hidden = true;
-      sidebar.left = toolbar.boxObject.width;
-      tablist.left = -tablist.boxObject.width;
 
-      var iframe = document.getElementById("addons-items-container");
-      if (iframe.getAttribute("src") == "")
-        iframe.setAttribute("src", "chrome://mozapps/content/extensions/extensions.xul");
-
-      addons.hidden = false;
-      addons.width = container.boxObject.width;
-      addons.height = container.boxObject.height - toolbar.boxObject.height;
+      let addons = document.getElementById("addons-container");
+      if (addons.getAttribute("src") == "")
+        addons.setAttribute("src", "chrome://mozapps/content/extensions/extensions.xul");
+      let dloads = document.getElementById("downloads-container");
+      if (dloads.getAttribute("src") == "")
+        dloads.setAttribute("src", "chrome://mozapps/content/downloads/downloads.xul");
     }
-    else if (aMode == PANELMODE_NONE) {
-      this._hideToolbar();
-      sidebar.left = toolbar.boxObject.width;
-      tablist.left = -tablist.boxObject.width;
-
+    else if (aMode == UIMODE_NONE) {
+      this._showToolbar(false);
       this._edit.reallyClosePopup();
       urllist.hidden = true;
       bookmark.hidden = true;
-      addons.hidden = true;
     }
   },
 
   _showPlaces : function(aItems) {
     var list = document.getElementById("urllist-items");
     while (list.firstChild) {
       list.removeChild(list.firstChild);
     }
@@ -598,25 +669,25 @@ var BrowserUI = {
       BrowserUI.show(PANELMODE_NONE);
   },
 
   showHistory : function() {
     this._showPlaces(this._getHistory(6));
   },
 
   showBookmarks : function () {
-    this.show(PANELMODE_BOOKMARKLIST);
+    this.show(UIMODE_BOOKMARKLIST);
 
     var bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
     this._showPlaces(this._getBookmarks([bms.bookmarksMenuFolder]));
   },
 
   newTab : function() {
     Browser.content.newTab(true);
-    this.show(PANELMODE_URLEDIT);
+    this.show(UIMODE_URLEDIT);
   },
 
   selectTab : function(aTab) {
     Browser.content.selectTab(aTab);
   },
 
   handleEvent: function (aEvent) {
     switch (aEvent.type) {
@@ -627,60 +698,52 @@ var BrowserUI = {
       case "DOMLinkAdded":
         this._linkAdded(aEvent);
         break;
       case "TabSelect":
         this._tabSelect(aEvent);
         break;
       // URL textbox events
       case "click":
-        this.show(PANELMODE_URLEDIT);
+        this.show(UIMODE_URLEDIT);
         this.openDefaultHistory();
         break;
       case "input":
         if (this._edit.value) {
           this.updateSearchEngines();
           this._autocompleteNavbuttons.hidden = false;
         }
         break;
       case "keypress":
         if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
           this._edit.reallyClosePopup();
-          this.show(PANELMODE_URLVIEW);
+          this.show(UIMODE_URLVIEW);
         }
         break;
       // Favicon events
       case "error":
         this._favicon.setAttribute("src", "chrome://browser/skin/images/default-favicon.png");
         break;
-      case "overpan": {
-        // Default to hide the controls when user overpans
-        let mode = PANELMODE_NONE;
-
-        // Open the sidebar controls if we get a right side overpan
-        if (aEvent.detail == 2 && (this.mode == PANELMODE_NONE || this.mode == PANELMODE_URLVIEW))
-          mode = PANELMODE_SIDEBAR;
-        // Close the sidebar controls if we get a left side overpan
-        else if (aEvent.detail == 1 && (this.mode == PANELMODE_NONE || this.mode == PANELMODE_URLVIEW))
-          mode = PANELMODE_TABLIST;
-
-        this.show(mode);
-        break;
-      }
+      // UI panning events
       case "mousedown":
         this._dragData.dragging = true;
-        this._dragData.sY = aEvent.screenY;
+        this._dragData.dragX = 0;
+        this._dragData.dragY = 0;
+        this._dragData.screenX = this._dragData.lastX = aEvent.screenX;
+        this._dragData.screenY = this._dragData.lastY = aEvent.screenY;
         this._dragData.sTop = document.getElementById("toolbar-main").top;
+        this._dragData.sLeft = document.getElementById("tab-list-container").left;
         break;
       case "mouseup":
         this._dragData.dragging = false;
         break;
       case "mousemove":
         this._scrollToolbar(aEvent);
         break;
+      // Window size events
       case "resize":
         this._sizeControls(aEvent);
         break;
     }
   },
 
   supportsCommand : function(cmd) {
     var isSupported = false;
@@ -691,19 +754,18 @@ var BrowserUI = {
       case "cmd_stop":
       case "cmd_search":
       case "cmd_go":
       case "cmd_star":
       case "cmd_bookmarks":
       case "cmd_menu":
       case "cmd_newTab":
       case "cmd_closeTab":
-      case "cmd_addons":
       case "cmd_actions":
-      case "cmd_prefs":
+      case "cmd_panel":
         isSupported = true;
         break;
       default:
         isSupported = false;
         break;
     }
     return isSupported;
   },
@@ -746,47 +808,44 @@ var BrowserUI = {
 
           var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
           var favicon = document.getElementById("urlbar-favicon");
           var faviconURI = ios.newURI(favicon.src, null, null);
 
           var fis = Cc["@mozilla.org/browser/favicon-service;1"].getService(Ci.nsIFaviconService);
           fis.setAndLoadFaviconForPage(bookmarkURI, faviconURI, true);
 
-          this.show(PANELMODE_NONE);
+          this.show(UIMODE_NONE);
         }
         else {
-          this.show(PANELMODE_BOOKMARK);
+          this.show(UIMODE_BOOKMARK);
           BookmarkHelper.edit(bookmarkURI);
         }
         break;
       }
       case "cmd_bookmarks":
         this.showBookmarks();
         break;
       case "cmd_menu":
-        // XXX Remove PANELMODE_ADDON when design changes
-        if (this.mode == PANELMODE_FULL || this.mode == PANELMODE_ADDONS)
-          this.show(PANELMODE_NONE);
-        else
-          this.show(PANELMODE_FULL);
         break;
       case "cmd_newTab":
         this.newTab();
         break;
       case "cmd_closeTab":
         Browser.content.removeTab(Browser.content.browser);
         break;
-      case "cmd_addons":
       case "cmd_actions":
-        this.show(PANELMODE_ADDONS);
         break;
-      case "cmd_prefs":
-        //XXX
+      case "cmd_panel":
+      {
+        var mode = (this.mode != UIMODE_PANEL ? UIMODE_PANEL : UIMODE_URLVIEW);
+        this.show(mode);
+        this._showPanel(mode == UIMODE_PANEL);
         break;
+      }
     }
   }
 };
 
 var BookmarkHelper = {
   _item : null,
   _uri : null,
   _bmksvc : null,
@@ -861,17 +920,17 @@ var BookmarkHelper = {
 
     }
     this.close();
   },
 
   close : function() {
     window.removeEventListener("keypress", this, true);
     this._item = null;
-    BrowserUI.show(PANELMODE_NONE);
+    BrowserUI.show(UIMODE_NONE);
   },
 
   handleEvent: function (aEvent) {
     switch (aEvent.type) {
       case "keypress":
         if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE)
           this.close();
         break;
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -225,19 +225,16 @@ var Browser = {
 
   doCommand : function(cmd) {
     var browser = this.content.browser;
 
     switch (cmd) {
       case "cmd_fullscreen":
         window.fullScreen = !window.fullScreen;
         break;
-      case "cmd_downloads":
-        Cc["@mozilla.org/download-manager-ui;1"].getService(Ci.nsIDownloadManagerUI).show(window);
-        break;
     }
   },
 
   getNotificationBox : function() {
     return document.getElementById("notifications");
   },
 
   findState: FINDSTATE_FIND,
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -82,19 +82,18 @@
 
     <!-- bookmarking -->
     <command id="cmd_star" label="&star.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_bookmarks" label="&bookmarks.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- misc -->
     <command id="cmd_menu" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_fullscreen" oncommand="CommandUpdater.doCommand(this.id);"/>
-    <command id="cmd_addons" oncommand="CommandUpdater.doCommand(this.id);"/>
-    <command id="cmd_downloads" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_actions" oncommand="CommandUpdater.doCommand(this.id);"/>
+    <command id="cmd_panel" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- scrolling -->
     <command id="cmd_scrollPageUp" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_scrollPageDown" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- editing -->
     <command id="cmd_cut" label="&cut.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_copy" label="&copy.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
@@ -119,18 +118,16 @@
 
     <!-- scrolling -->
     <key id="key_pageUp" keycode="VK_UP" command="cmd_scrollPageUp" modifiers="shift"/>
     <key id="key_pageDown" keycode="VK_DOWN" command="cmd_scrollPageDown" modifiers="shift"/>
 
     <!-- misc -->
     <key id="key_menu" keycode="VK_F4" command="cmd_menu"/>
     <key id="key_fullscreen" keycode="VK_F6" command="cmd_fullscreen"/>
-    <key id="key_addons" key="E" command="cmd_addons" modifiers="control"/>
-    <key id="key_downloads" key="J" command="cmd_downloads" modifiers="control"/>
 
     <!-- tabs -->
     <key id="key_newTab" key="T" modifiers="accel" command="cmd_newTab"/>
     <key id="key_closeTab" key="W" modifiers="accel" command="cmd_closeTab"/>
 
     <!-- find -->
     <key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/>
     <key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/>
@@ -213,24 +210,36 @@
           <toolbarbutton id="tool-reload" class="urlbar-icon-button" command="cmd_reload"/>
           <toolbarbutton id="tool-stop" class="urlbar-icon-button" command="cmd_stop"/>
           <toolbarbutton id="tool-go" class="urlbar-icon-button" command="cmd_go"/>
         </hbox>
       </hbox>
       <toolbarbutton id="tool-bookmarks" class="urlbar-icon-button" command="cmd_bookmarks"/>
     </toolbar>
 
-    <vbox id="browser-controls" style="-moz-stack-sizing: ignore; width: 80px;" top="60" left="0">
+    <vbox id="browser-controls" style="-moz-stack-sizing: ignore;" top="60" left="0">
       <toolbarbutton id="tool-back" class="browser-control-button" command="cmd_back"/>
       <toolbarbutton id="tool-forward" class="browser-control-button" command="cmd_forward"/>
       <toolbarbutton id="tool-star" class="browser-control-button" command="cmd_star"/>
       <toolbarbutton id="tool-actions" class="browser-control-button" command="cmd_actions"/>
-      <toolbarbutton id="tool-prefs" class="browser-control-button" command="cmd_prefs"/>
+      <toolbarbutton id="tool-prefs" class="browser-control-button" command="cmd_panel"/>
     </vbox>
 
+    <hbox id="panel-container" style="-moz-stack-sizing: ignore;" top="0" left="0">
+      <vbox id="panel-controls">
+        <toolbarbutton id="tool-addons" class="browser-control-button" oncommand="this.parentNode.nextSibling.selectedIndex=0"/>
+        <toolbarbutton id="tool-downloads" class="browser-control-button" oncommand="this.parentNode.nextSibling.selectedIndex=1"/>
+        <toolbarbutton id="tool-preferences" class="browser-control-button" command="cmd_preferences"/>
+      </vbox>
+      <deck id="panel-items" flex="1">
+        <iframe id="addons-container" flex="1" src=""/>
+        <iframe id="downloads-container" flex="1" src=""/>
+      </deck>
+    </hbox>
+
     <vbox id="tab-list-container" style="-moz-stack-sizing: ignore;" top="60" left="0">
       <richlistbox id="tab-list" onselect="BrowserUI.selectTab(this.selectedItem);"/>
       <hbox>
         <toolbarbutton id="newtab-button" command="cmd_newTab"/>
         <toolbarbutton id="retrievetab-button" command=""/>
       </hbox>
     </vbox>
 
@@ -273,19 +282,16 @@
           <button label="&bookmarkRemove.label;" oncommand="BookmarkHelper.remove()"/>
           <spacer flex="1"/>
           <button label="&bookmarkCancel.label;" oncommand="BookmarkHelper.close()"/>
           <button label="&bookmarkDone.label;" oncommand="BookmarkHelper.save()"/>
         </hbox>
       </vbox>
     </vbox>
 
-    <vbox id="addons-container" hidden="true" style="-moz-stack-sizing: ignore;" top="60" left="0">
-      <iframe id="addons-items-container" flex="1" src=""/>
-    </vbox>
   </stack>
 
   <vbox id="findpanel-placeholder" sizetopopup="always">
     <panel id="findpanel" onpopupshown="Browser.doFind()">
       <findbar id="findbar"/>
    </panel>
   </vbox>
 
--- a/mobile/chrome/content/deckbrowser.xml
+++ b/mobile/chrome/content/deckbrowser.xml
@@ -674,86 +674,49 @@
       </property>
 
       <property name="scrollY" readonly="true">
         <getter><![CDATA[
           return this.dragData.pageY - this.dragData.dragY / this._zoomLevel;
          ]]></getter>
       </property>
 
-      <field name="_fireOverpan">
-        0
-      </field>
-
       /**
        * Given a set of page coordinates, constrain them such that they
        * fit within the rect defined by [0,0] and [x,y], where x and y are
        * the maximum values that can be used for the canvas' .top and .left
        * such that it is still within the scrollable area of the page, taking
        * into account the current zoomLevel.
        */
       <method name="_constrainPanCoords">
         <parameter name="aX"/>
         <parameter name="aY"/>
         <body><![CDATA[
-          const OVERPAN_LIMIT = 30;
-          const OVERPAN_LEFT = 1;
-          const OVERPAN_RIGHT = 2;
-          const OVERPAN_TOP = 3;
-          const OVERPAN_BOTTOM = 4;
-
-          var origX = aX;
-          var origY = aY;
-
           var [contentAreaWidth, contentAreaHeight] = this._contentAreaDimensions;
           var [canvasW, canvasH] = this._effectiveCanvasDimensions;
 
           var offscreenWidth = contentAreaWidth - canvasW;
           if (offscreenWidth <= 0) {
             // Content is narrower than viewport, no need to pan horizontally
             aX = 0;
-
-            // Check for an overpan
-            if (origX < -OVERPAN_LIMIT)
-              this._fireOverpan = OVERPAN_LEFT;
-            else if (origX > OVERPAN_LIMIT)
-              this._fireOverpan = OVERPAN_RIGHT;
           } else {
             var newPageX = Math.min(this.dragData.pageX + aX, offscreenWidth);
             newPageX = Math.max(newPageX, 0);
             aX = newPageX - this.dragData.pageX;
-
-            // Check for an overpan
-            if (origX < -OVERPAN_LIMIT && aX <= 0 && newPageX == 0)
-              this._fireOverpan = OVERPAN_LEFT;
-            else if (origX > OVERPAN_LIMIT && aX >= 0 && (offscreenWidth - newPageX) == 0)
-              this._fireOverpan = OVERPAN_RIGHT;
           }
 
           var offscreenHeight = contentAreaHeight - canvasH;
           if (offscreenHeight <= 0) {
             // Content is shorter than viewport, no need to pan vertically
             aY = 0;
-
-            // Check for an overpan
-            if (origY < -OVERPAN_LIMIT)
-              this._fireOverpan = OVERPAN_TOP;
-            else if (origY > OVERPAN_LIMIT)
-              this._fireOverpan = OVERPAN_BOTTOM;
           } else {
             // min of 0, max of contentAreaHeight - canvasHeight
             var newPageY = Math.min(this.dragData.pageY + aY, offscreenHeight);
             newPageY = Math.max(newPageY, 0);
             aY = newPageY - this.dragData.pageY;
-
-            // Check for an overpan
-            if (origY < -OVERPAN_LIMIT && aY <= 0 && newPageY == 0)
-              this._fireOverpan = OVERPAN_TOP;
-            else if (origY > OVERPAN_LIMIT && aY >= 0 && (offscreenHeight - newPageY) == 0)
-              this._fireOverpan = OVERPAN_BOTTOM;
           }
 
           return [aX, aY];
         ]]></body>
       </method>
 
       <method name="_moveCanvas">
         <parameter name="aDx"/>
@@ -903,24 +866,16 @@
           this.dragData.dragX = 0;
           this.dragData.dragY = 0;
 
           // update canvas position and draw the canvas at the new location
           this._browserToCanvas();
           this._updateCanvasPosition();
 
           this.dragData.dragging = false;
-
-          // Do we need to fire a content overpan event
-          if (this._fireOverpan > 0) {
-            var event = document.createEvent("UIEvents");
-            event.initUIEvent("overpan", true, false, window, this._fireOverpan);
-            this.dispatchEvent(event);
-          }
-          this._fireOverpan = 0;
         ]]></body>
       </method>
 
       <field name="stackEventHandler">
         <![CDATA[
         ({
           deckbrowser: this,
 
@@ -962,24 +917,18 @@
             for (var i = 0; i < self.PAN_EVENTS_TO_TRACK; i++) {
               self._panEventTracker[i] = null;
             }
             if (self.dragData.kineticId)
                self._endPan();
           },
 
           mouseup: function seh_mouseup(aEvent) {
-            var skipKinetic = false;
             if (aEvent.button == 0 && this.deckbrowser.dragData.dragging) {
-              if (this.deckbrowser._fireOverpan) {
-                this.deckbrowser._endPan();
-                skipKinetic = true;
-              } else {
-                this.deckbrowser.dragData.dragging = false;
-              }
+              this.deckbrowser.dragData.dragging = false;
             } else if (aEvent.originalTarget == this.deckbrowser._canvas) {
               // Mouseup on canvas that isn't releasing from a drag
               // cancel scrollStart timer
               clearTimeout(this.deckbrowser._dragStartTimeout);
               this.deckbrowser._dragStartTimeout = -1;
 
               // send mousedown & mouseup
               this.deckbrowser._redispatchMouseEvent(this._lastMouseDown);
@@ -994,18 +943,17 @@
                   Math.abs(aEvent.clientY - this._lastMouseUp.clientY) < 30) {
                 this.dblclick(aEvent);
                 return;
               }
 
               this._lastMouseUp = aEvent;
             }
 
-            if (!skipKinetic)
-              this.deckbrowser._startKinetic();
+            this.deckbrowser._startKinetic();
             for (var i = 0; i < this.deckbrowser.PAN_EVENTS_TO_TRACK; i++) {
               this.deckbrowser._panEventTracker[i] = null;
             }
           },
 
           mousemove: function seh_mousemove(aEvent) {
             if (!this.deckbrowser.dragData.dragging) {
               // If we've moved more than N pixels lets go ahead and assume we're dragging
--- a/mobile/chrome/skin/browser.css
+++ b/mobile/chrome/skin/browser.css
@@ -182,16 +182,28 @@ toolbarbutton.browser-control-button {
 #tool-prefs {
   -moz-image-region: rect(208px 48px 256px 0px);
 }
 
 #tool-prefs:hover:active {
   -moz-image-region: rect(208px 96px 256px 48px);
 }
 
+#tool-addons {
+  -moz-image-region: rect(2px 151px 39px 113px);
+}
+
+#tool-downloads {
+  -moz-image-region: rect(2px 151px 39px 113px);
+}
+
+#tool-preferences {
+  -moz-image-region: rect(2px 151px 39px 113px);
+}
+
 .tool-search {
   list-style-image: url("chrome://browser/skin/images/mono-toolbar.png");
   -moz-image-region: rect(0px 36px 36px 0px);
 }
 
 #toolbar-main[mode="loading"] > #urlbar-container > #urlbar-icons > #tool-go,
 #toolbar-main[mode="loading"] > #urlbar-container > #urlbar-icons > #tool-reload {
   visibility: collapse;
--- a/mobile/chrome/skin/extensions.css
+++ b/mobile/chrome/skin/extensions.css
@@ -1,18 +1,18 @@
 label,
 description,
 textbox {
   font-family: "Nokia Sans", sans-serif !important;
-  font-size: 16.75pt !important;
+  font-size: 12pt !important;
 }
 
 richlistitem {
   font-family: "Nokia Sans", sans-serif !important;
-  font-size: 16.75pt !important;
+  font-size: 12pt !important;
 }
 
 #extensionsManager {
   -moz-appearance: none;
   background-color: rgba(123,125,123,0.9);
 }
 
 #extensionsView {