merge fx-team into mozilla-central
authorGavin Sharp <gavin@gavinsharp.com>
Tue, 21 Aug 2012 10:27:25 -0700
changeset 104890 688a80cdf39fc0c26b4558702800a6cc279ddea0
parent 104881 f9a8fdb081936577a38d596b3514f1a78a0c17a0 (current diff)
parent 104889 6bcd473bcf339816960093cf6e83180184b4c58e (diff)
child 104891 1b51c7bf1e05aa084372f0a017f1e45ca1b2502c
child 105041 327883b4f2fe04f20f7ab0d1f0cd3603d85307d2
push id45
push usershu@rfrn.org
push dateThu, 23 Aug 2012 00:57:43 +0000
milestone17.0a1
merge fx-team into mozilla-central
browser/base/content/browser-social.js
browser/base/content/test/Makefile.in
browser/base/content/test/browser_social_mozSocial_API.js
browser/base/content/test/social_worker.js
docshell/test/browser/browser_bug435325.js
mobile/android/base/Makefile.in
mobile/android/base/resources/drawable-hdpi/abouthome_sync_bg.9.png
mobile/android/base/resources/drawable-hdpi/abouthome_sync_pressed_bg.9.png
mobile/android/base/resources/drawable-xhdpi/abouthome_sync_bg.9.png
mobile/android/base/resources/drawable-xhdpi/abouthome_sync_pressed_bg.9.png
mobile/android/base/resources/drawable/abouthome_sync_bg.9.png
mobile/android/base/resources/drawable/abouthome_sync_box.xml
mobile/android/base/resources/drawable/abouthome_sync_pressed_bg.9.png
mobile/android/base/resources/layout/abouthome_content.xml.in
--- a/browser/base/content/aboutRobots.xhtml
+++ b/browser/base/content/aboutRobots.xhtml
@@ -37,16 +37,20 @@
           var newLabel = button.getAttribute("label2");
           button.textContent = newLabel;
           buttonClicked = true;
         }
       }
     ]]></script>
 
     <style type="text/css"><![CDATA[
+      #errorPageContainer {
+        background-image: none;
+      }
+
       #errorPageContainer:before {
         content: url('chrome://browser/content/aboutRobots-icon.png');
         position: absolute;
       }
 
       body[dir=rtl] #icon,
       body[dir=rtl] #errorPageContainer:before {
         transform: scaleX(-1);
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -286,17 +286,17 @@ var SocialToolbar = {
     let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
     let label = gNavigatorBundle.getFormattedString("social.remove.label",
                                                     [brandShortName]);
     let accesskey = gNavigatorBundle.getString("social.remove.accesskey");
     removeItem.setAttribute("label", label);
     removeItem.setAttribute("accesskey", accesskey);
 
     let statusAreaPopup = document.getElementById("social-statusarea-popup");
-    statusAreaPopup.addEventListener("popupshowing", function(e) {
+    statusAreaPopup.addEventListener("popupshown", function(e) {
       this.button.setAttribute("open", "true");
     }.bind(this));
     statusAreaPopup.addEventListener("popuphidden", function(e) {
       this.button.removeAttribute("open");
     }.bind(this));
 
     this.updateButton();
     this.updateProfile();
@@ -434,22 +434,44 @@ var SocialToolbar = {
 
       let [height, width] = [body.firstChild.offsetHeight || 300, 330];
       notifBrowser.style.width = width + "px";
       notifBrowser.style.height = height + "px";
     }
 
     sizePanelToContent();
 
+    function dispatchPanelEvent(name) {
+      let evt = notifBrowser.contentDocument.createEvent("CustomEvent");
+      evt.initCustomEvent(name, true, true, {});
+      notifBrowser.contentDocument.documentElement.dispatchEvent(evt);
+    }
+
     panel.addEventListener("popuphiding", function onpopuphiding() {
       panel.removeEventListener("popuphiding", onpopuphiding);
       SocialToolbar.button.removeAttribute("open");
+      dispatchPanelEvent("socialFrameHide");
     });
 
-    this.button.setAttribute("open", "true");
+    panel.addEventListener("popupshown", function onpopupshown() {
+      panel.removeEventListener("popupshown", onpopupshown);
+      SocialToolbar.button.setAttribute("open", "true");
+      if (notifBrowser.contentDocument.readyState == "complete") {
+        dispatchPanelEvent("socialFrameShow");
+      } else {
+        // first time load, wait for load and dispatch after load
+        notifBrowser.addEventListener("load", function panelBrowserOnload(e) {
+          notifBrowser.removeEventListener("load", panelBrowserOnload, true);
+          setTimeout(function() {
+            dispatchPanelEvent("socialFrameShow");
+          }, 0);
+        }, true);
+      }
+    });
+
     panel.openPopup(iconImage, "bottomcenter topleft", 0, 0, false, false);
   }
 }
 
 var SocialSidebar = {
   // Called once, after window load, when the Social.provider object is initialized
   init: function SocialSidebar_init() {
     let sbrowser = document.getElementById("social-sidebar-browser");
@@ -494,32 +516,32 @@ var SocialSidebar = {
     let hideSidebar = !this.canShow || !this.enabled;
     let broadcaster = document.getElementById("socialSidebarBroadcaster");
     broadcaster.hidden = hideSidebar;
     command.setAttribute("checked", !hideSidebar);
 
     let sbrowser = document.getElementById("social-sidebar-browser");
     sbrowser.docShell.isActive = !hideSidebar;
     if (hideSidebar) {
-      this.dispatchEvent("sidebarhide");
+      this.dispatchEvent("socialFrameHide");
       // If we're disabled, unload the sidebar content
       if (!this.canShow) {
         sbrowser.removeAttribute("origin");
         sbrowser.setAttribute("src", "about:blank");
       }
     } else {
       // Make sure the right sidebar URL is loaded
       if (sbrowser.getAttribute("origin") != Social.provider.origin) {
         sbrowser.setAttribute("origin", Social.provider.origin);
         sbrowser.setAttribute("src", Social.provider.sidebarURL);
         sbrowser.addEventListener("load", function sidebarOnShow() {
           sbrowser.removeEventListener("load", sidebarOnShow);
           // let load finish, then fire our event
           setTimeout(function () {
-            SocialSidebar.dispatchEvent("sidebarshow");
+            SocialSidebar.dispatchEvent("socialFrameShow");
           }, 0);
         });
       } else {
-        this.dispatchEvent("sidebarshow");
+        this.dispatchEvent("socialFrameShow");
       }
     }
   }
 }
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -87,16 +87,17 @@ endif
                  browser_bug417483.js \
                  browser_bug419612.js \
                  browser_identity_UI.js \
                  browser_bug422590.js \
                  browser_bug424101.js \
                  browser_bug427559.js \
                  browser_bug432599.js \
                  browser_bug435035.js \
+                 browser_bug435325.js \
                  browser_bug441778.js \
                  browser_popupNotification.js \
                  browser_bug455852.js \
                  browser_bug460146.js \
                  browser_bug462673.js \
                  browser_bug477014.js \
                  browser_bug479408.js \
                  browser_bug479408_sample.html \
rename from docshell/test/browser/browser_bug435325.js
rename to browser/base/content/test/browser_bug435325.js
--- a/docshell/test/browser/browser_bug435325.js
+++ b/browser/base/content/test/browser_bug435325.js
@@ -1,13 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* Ensure that clicking the button in the Offline mode neterror page makes the browser go online. See bug 435325. */
-/* TEST_PATH=docshell/test/browser/browser_bug435325.js make -C $(OBJDIR) mochitest-browser-chrome */
 
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   window.addEventListener("DOMContentLoaded", checkPage, false);
 
   // Go offline and disable the cache, then try to load the test URL.
@@ -29,19 +28,18 @@ function checkPage() {
   is(gBrowser.contentDocument.documentURI.substring(0,27),
     "about:neterror?e=netOffline", "Loading the Offline mode neterror page.");
 
   // Now press the "Try Again" button
   ok(gBrowser.contentDocument.getElementById("errorTryAgain"),
     "The error page has got a #errorTryAgain element");
   gBrowser.contentDocument.getElementById("errorTryAgain").click();
 
-  ok(!Services.io.offline, "After clicking the Try Again button, we're back "
-   +" online. This depends on Components.interfaces.nsIDOMWindowUtils being "
-   +"available from untrusted content (bug 435325).");
+  ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
+                           "online.");
 
   finish();
 }
 
 registerCleanupFunction(function() {
   Services.prefs.setBoolPref("browser.cache.disk.enable", true);
   Services.prefs.setBoolPref("browser.cache.memory.enable", true);
   Services.io.offline = false;
--- a/browser/base/content/test/browser_social_mozSocial_API.js
+++ b/browser/base/content/test/browser_social_mozSocial_API.js
@@ -32,38 +32,40 @@ var tests = {
       ok(!statusIcons.firstChild.hidden, "status icon is visible");
       // Click the button to trigger its contentPanel
       let panel = document.getElementById("social-notification-panel");
       EventUtils.synthesizeMouseAtCenter(statusIcons.firstChild, {});
     }
 
     let port = Social.provider.port;
     ok(port, "provider has a port");
-    port.postMessage({topic: "test-init"});
-    Social.provider.port.onmessage = function (e) {
+    port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-panel-message":
           ok(true, "got panel message");
-          // Wait for the panel to close before ending the test
-          let panel = document.getElementById("social-notification-panel");
-          panel.addEventListener("popuphidden", function hiddenListener() {
-            panel.removeEventListener("popuphidden", hiddenListener);
+          break;
+        case "got-social-panel-visibility":
+          if (e.data.result == "shown") {
+            ok(true, "panel shown");
+            let panel = document.getElementById("social-notification-panel");
+            panel.hidePopup();
+          } else if (e.data.result == "hidden") {
+            ok(true, "panel hidden");
             next();
-          });
-          panel.hidePopup();
-          break;
+          }
         case "got-sidebar-message":
           // The sidebar message will always come first, since it loads by default
           ok(true, "got sidebar message");
           gotSidebarMessage = true;
           checkNext();
           break;
       }
     }
+    port.postMessage({topic: "test-init"});
 
     // Our worker sets up ambient notification at the same time as it responds to
     // the workerAPI initialization. If it's already initialized, we can
     // immediately check the icons, otherwise wait for initialization by
     // observing the topic sent out by the social service.
     if (Social.provider.workerAPI.initialized) {
       iconsReady = true;
       checkNext();
--- a/browser/base/content/test/browser_social_sidebar.js
+++ b/browser/base/content/test/browser_social_sidebar.js
@@ -33,23 +33,23 @@ function doTest(finishcb) {
     if (shouldBeShown)
       is(browser.getAttribute('src'), Social.provider.sidebarURL, "sidebar url should be set");
   }
 
   // First check the the sidebar is initially visible, and loaded
   ok(!command.hidden, "toggle command should be visible");
   checkShown(true);
 
-  browser.addEventListener("sidebarhide", function sidebarhide() {
-    browser.removeEventListener("sidebarhide", sidebarhide);
+  browser.addEventListener("socialFrameHide", function sidebarhide() {
+    browser.removeEventListener("socialFrameHide", sidebarhide);
 
     checkShown(false);
 
-    browser.addEventListener("sidebarshow", function sidebarshow() {
-      browser.removeEventListener("sidebarshow", sidebarshow);
+    browser.addEventListener("socialFrameShow", function sidebarshow() {
+      browser.removeEventListener("socialFrameShow", sidebarshow);
 
       checkShown(true);
 
       // Finish the test
       finishcb();
     });
 
     // Toggle it back on
--- a/browser/base/content/test/social_panel.html
+++ b/browser/base/content/test/social_panel.html
@@ -1,14 +1,22 @@
 <html>
   <head>
     <meta charset="utf-8">
     <script>
       function pingWorker() {
         var port = navigator.mozSocial.getWorker().port;
         port.postMessage({topic: "panel-message", result: "ok"});
       }
+      window.addEventListener("socialFrameShow", function(e) {
+        var port = navigator.mozSocial.getWorker().port;
+        port.postMessage({topic: "status-panel-visibility", result: "shown"});
+      }, false);
+      window.addEventListener("socialFrameHide", function(e) {
+        var port = navigator.mozSocial.getWorker().port;
+        port.postMessage({topic: "status-panel-visibility", result: "hidden"});
+      }, false);
     </script>
   </head>
   <body onload="pingWorker();">
     <p>This is a test social panel.</p>
   </body>
 </html>
--- a/browser/base/content/test/social_worker.js
+++ b/browser/base/content/test/social_worker.js
@@ -34,16 +34,19 @@ onconnect = function(e) {
         break;
       case "test-close-service-window":
         sidebarPort.postMessage({topic:"test-close-service-window"});
         break;
       case "panel-message":
         if (testPort && event.data.result == "ok")
           testPort.postMessage({topic:"got-panel-message"});
         break;
+      case "status-panel-visibility":
+        testPort.postMessage({topic:"got-social-panel-visibility", result: event.data.result });
+        break;
       case "test-chatbox-open":
         sidebarPort.postMessage({topic:"test-chatbox-open"});
         break;
       case "chatbox-message":
         testPort.postMessage({topic:"got-chatbox-message", result: event.data.result});
         break;
       case "chatbox-visibility":
         testPort.postMessage({topic:"got-chatbox-visibility", result: event.data.result});
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -1,14 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/orion.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/debugger.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/debugger.css" type="text/css"?>
 <!DOCTYPE window [
 <!ENTITY % debuggerDTD SYSTEM "chrome://browser/locale/devtools/debugger.dtd" >
  %debuggerDTD;
 ]>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
--- a/docshell/test/browser/Makefile.in
+++ b/docshell/test/browser/Makefile.in
@@ -12,17 +12,16 @@ include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_BROWSER_FILES =	\
 		browser_bug92473.js \
 		test-form_sjis.html \
 		browser_bug134911.js \
 		browser_bug349769.js \
 		browser_bug388121-1.js \
 		browser_bug388121-2.js \
-		browser_bug435325.js \
 		browser_bug441169.js \
 		browser_bug420605.js \
 		file_bug420605.html \
 		browser_bug503832.js \
 		browser_loadDisallowInherit.js \
 		file_bug503832.html \
 		browser_bug554155.js \
 		browser_bug655273.js \
--- a/mobile/android/base/AboutHomeContent.java
+++ b/mobile/android/base/AboutHomeContent.java
@@ -80,16 +80,17 @@ public class AboutHomeContent extends Sc
     private LayoutInflater mInflater;
 
     private AccountManager mAccountManager;
     private OnAccountsUpdateListener mAccountListener = null;
 
     protected SimpleCursorAdapter mTopSitesAdapter;
     protected GridView mTopSitesGrid;
 
+    private AboutHomePromoBox mPromoBox;
     protected AboutHomeSection mAddons;
     protected AboutHomeSection mLastTabs;
     protected AboutHomeSection mRemoteTabs;
 
     private View.OnClickListener mRemoteTabClickListener;
     private OnInterceptTouchListener mOnInterceptTouchListener;
 
     public interface UriLoadCallback {
@@ -150,16 +151,17 @@ public class AboutHomeContent extends Sc
                 String spec = c.getString(c.getColumnIndex(URLColumns.URL));
                 Log.i(LOGTAG, "clicked: " + spec);
 
                 if (mUriLoadCallback != null)
                     mUriLoadCallback.callback(spec);
             }
         });
 
+        mPromoBox = (AboutHomePromoBox) findViewById(R.id.promo_box);
         mAddons = (AboutHomeSection) findViewById(R.id.recommended_addons);
         mLastTabs = (AboutHomeSection) findViewById(R.id.last_tabs);
         mRemoteTabs = (AboutHomeSection) findViewById(R.id.remote_tabs);
 
         TextView allTopSitesText = (TextView) findViewById(R.id.all_top_sites_text);
         allTopSitesText.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 mActivity.showAwesomebar(AwesomeBar.Target.CURRENT_TAB);
@@ -174,38 +176,16 @@ public class AboutHomeContent extends Sc
         });
 
         mRemoteTabs.setOnMoreTextClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 mActivity.showRemoteTabs();
             }
         });
 
-        TextView syncTextView = (TextView) findViewById(R.id.sync_text);
-        String syncText = syncTextView.getText().toString() + " \u00BB";
-        String boldName = getContext().getResources().getString(R.string.abouthome_sync_bold_name);
-        int styleIndex = syncText.indexOf(boldName);
-
-        // Highlight any occurrence of "Firefox Sync" in the string
-        // with a bold style.
-        if (styleIndex >= 0) {
-            SpannableString spannableText = new SpannableString(syncText);
-            spannableText.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), styleIndex, styleIndex + 12, 0);
-            syncTextView.setText(spannableText, TextView.BufferType.SPANNABLE);
-        }
-
-        LinearLayout syncBox = (LinearLayout) findViewById(R.id.sync_box);
-        syncBox.setOnClickListener(new View.OnClickListener() {
-            public void onClick(View v) {
-                Context context = v.getContext();
-                Intent intent = new Intent(context, SetupSyncActivity.class);
-                context.startActivity(intent);
-            }
-        });
-
         setTopSitesConstants();
     }
 
     public void onDestroy() {
         if (mAccountListener != null) {
             mAccountManager.removeOnAccountsUpdatedListener(mAccountListener);
             mAccountListener = null;
         }
@@ -227,31 +207,33 @@ public class AboutHomeContent extends Sc
         int visibilityWithoutTopSites = visible && !hasTopSites ? View.VISIBLE : View.GONE;
 
         findViewById(R.id.top_sites_grid).setVisibility(visibilityWithTopSites);
         findViewById(R.id.top_sites_title).setVisibility(visibility);
         findViewById(R.id.all_top_sites_text).setVisibility(visibilityWithTopSites);
         findViewById(R.id.no_top_sites_text).setVisibility(visibilityWithoutTopSites);
     }
 
-    private void setSyncVisibility(boolean visible) {
-        int visibility = visible ? View.VISIBLE : View.GONE;
-        findViewById(R.id.sync_box).setVisibility(visibility);
+    private void setPromoBoxVisibility(boolean visible, AboutHomePromoBox.Type type) {
+        if (visible)
+            mPromoBox.show(type);
+        else
+            mPromoBox.hide();
     }
 
     private void updateLayout(GeckoApp.StartupMode startupMode, boolean syncIsSetup) {
         // The idea here is that we only show the sync invitation
         // on the very first run. Show sync banner below the top
         // sites section in all other cases.
 
         boolean hasTopSites = mTopSitesAdapter.getCount() > 0;
         boolean isFirstRun = (startupMode == GeckoApp.StartupMode.NEW_PROFILE);
 
         setTopSitesVisibility(!isFirstRun || hasTopSites, hasTopSites);
-        setSyncVisibility(!syncIsSetup);
+        setPromoBoxVisibility(!syncIsSetup, AboutHomePromoBox.Type.SYNC);
     }
 
     private void updateLayoutForSync() {
         final GeckoApp.StartupMode startupMode = mActivity.getStartupMode();
         final boolean syncIsSetup = SyncAccounts.syncAccountsExist(mContext);
 
         post(new Runnable() {
             public void run() {
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/AboutHomePromoBox.java
@@ -0,0 +1,126 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * A promotional box for the about:home page. The layout contains an ImageView to the left of a
+ * TextView whose resources may be overidden to display custom values for a new type of promo box.
+ * To do this, add a new Type value and update show() to call setResources() for your values -
+ * including a set[Box Type]Resources() helper method is recommended.
+ */
+public class AboutHomePromoBox extends LinearLayout implements View.OnClickListener {
+    private static final String LOGTAG = "AboutHomePromoBox";
+
+    public enum Type { SYNC };
+
+    private Type mType;
+
+    private final Context mContext;
+    private final TextView mTextView;
+    private final ImageView mImageView;
+
+    // Use setResources() to set these variables for each PromoBox type.
+    private int mTextResource;
+    private int mBoldTextResource;
+    private int mImageResource;
+
+    public AboutHomePromoBox(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        inflater.inflate(R.layout.abouthome_promo_box, this);
+
+        mContext = context;
+        mTextView = (TextView) findViewById(R.id.text);
+        mImageView = (ImageView) findViewById(R.id.icon);
+        setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        Log.d(LOGTAG, "I work out.");
+        switch (mType) {
+            case SYNC:
+                final Context context = v.getContext();
+                final Intent intent = new Intent(context, SetupSyncActivity.class);
+                context.startActivity(intent);
+                break;
+
+            default:
+                Log.e(LOGTAG, "Invalid type was set when promo box was clicked.");
+                break;
+        }
+    }
+
+    /**
+     * Shows the specified promo box. If a promo box is already active, it will be overidden with a
+     * promo box of the specified type.
+     */
+    public void show(Type type) {
+        mType = type;
+        switch (type) {
+            case SYNC:
+                setSyncResources();
+                break;
+
+            default:
+                Log.e(LOGTAG, "Invalid PromoBoxType specified.");
+                break;
+        }
+        updateViewResources();
+        setVisibility(View.VISIBLE);
+    }
+
+    public void hide() {
+        setVisibility(View.GONE);
+        mType = null;
+    }
+
+    private void setResources(int textResource, int boldTextResource, int imageResource) {
+        mTextResource = textResource;
+        mBoldTextResource = boldTextResource;
+        mImageResource = imageResource;
+    }
+
+    private void updateViewResources() {
+        updateTextViewResources();
+        mImageView.setImageResource(mImageResource);
+    }
+
+    private void updateTextViewResources() {
+        final String promoText = mContext.getResources().getString(mTextResource) + " \u00BB";
+
+        final String boldName = mContext.getResources().getString(mBoldTextResource);
+        final int styleIndex = promoText.indexOf(boldName);
+        if (styleIndex < 0)
+            mTextView.setText(promoText);
+        else {
+            final SpannableString spannableText = new SpannableString(promoText);
+            spannableText.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), styleIndex,
+                    styleIndex + boldName.length(), 0);
+            mTextView.setText(spannableText, TextView.BufferType.SPANNABLE);
+        }
+    }
+
+    // Type.SYNC: Setup Firefox sync.
+    private void setSyncResources() {
+        setResources(R.string.abouthome_about_sync, R.string.abouthome_sync_bold_name,
+                R.drawable.abouthome_sync_logo);
+    }
+}
--- a/mobile/android/base/FontSizePreference.java
+++ b/mobile/android/base/FontSizePreference.java
@@ -2,92 +2,88 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import android.app.AlertDialog;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.preference.DialogPreference;
 import android.preference.Preference.OnPreferenceChangeListener;
-import android.text.method.ScrollingMovementMethod;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.ViewGroup;
 import android.widget.Button;
+import android.widget.ScrollView;
 import android.widget.TextView;
 
 import java.util.HashMap;
 
 class FontSizePreference extends DialogPreference {
     private static final String LOGTAG = "FontSizePreference";
     private static final int TWIP_TO_PT_RATIO = 20; // 20 twip = 1 point.
     private static final int PREVIEW_FONT_SIZE_UNIT = TypedValue.COMPLEX_UNIT_PT;
     private static final int DEFAULT_FONT_INDEX = 2;
 
-    // Final line height = line height * line_mult + line_mult;
-    private static final float LINE_SPACING_ADD = 0f; // Units unknown.
-    private static final float LINE_SPACING_MULT = 1.0f;
-
-    private static final float MIN_TEXTVIEW_WIDTH_DIP = 360; // Width of the Galaxy Nexus (portrait).
-    /** The dialog will encompass the minimum width + (1 / scaleFactor) of the remaining space. */
-    private static final float TEXTVIEW_WIDTH_SCALE_FACTOR = 3;
-
     private final Context mContext;
-    private int mCurrentOrientation;
+    /** Container for mPreviewFontView to allow for scrollable padding at the top of the view. */
+    private ScrollView mScrollingContainer;
     private TextView mPreviewFontView;
     private Button mIncreaseFontButton;
     private Button mDecreaseFontButton;
 
     private final String[] mFontTwipValues;
     private final String[] mFontSizeNames; // Ex: "Small".
     /** Index into the above arrays for the saved preference value (from Gecko). */
     private int mSavedFontIndex = DEFAULT_FONT_INDEX;
     /** Index into the above arrays for the currently displayed font size (the preview). */
     private int mPreviewFontIndex = mSavedFontIndex;
     private final HashMap<String, Integer> mFontTwipToIndexMap;
 
-    private boolean mPreviewFontViewHeightSet;
-
     public FontSizePreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
 
         final Resources res = mContext.getResources();
         mFontTwipValues = res.getStringArray(R.array.pref_font_size_values);
         mFontSizeNames = res.getStringArray(R.array.pref_font_size_entries);
         mFontTwipToIndexMap = new HashMap<String, Integer>();
         for (int i = 0; i < mFontTwipValues.length; ++i) {
             mFontTwipToIndexMap.put(mFontTwipValues[i], i);
         }
-        mCurrentOrientation = res.getConfiguration().orientation;
     }
 
     @Override
     protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
         final LayoutInflater inflater =
             (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         View dialogView = inflater.inflate(R.layout.font_size_preference, null);
+        initInternalViews(dialogView);
+        updatePreviewFontSize(mFontTwipValues[mPreviewFontIndex]);
+
+        builder.setTitle(null);
+        builder.setView(dialogView);
+    }
+
+    /** Saves relevant views to instance variables and initializes their settings. */
+    private void initInternalViews(View dialogView) {
+        mScrollingContainer = (ScrollView) dialogView.findViewById(R.id.scrolling_container);
+        // Background cannot be set in XML (see bug 783597 - TODO: Change this to XML when bug is fixed).
+        mScrollingContainer.setBackgroundColor(Color.WHITE);
         mPreviewFontView = (TextView) dialogView.findViewById(R.id.preview);
-        mPreviewFontView.setMovementMethod(new ScrollingMovementMethod());
-        // There is no way to get the value for this padding so we turn it off.
-        mPreviewFontView.setIncludeFontPadding(false);
-        // Retrieving line spacing is not available until API 16 so we override the values instead.
-        mPreviewFontView.setLineSpacing(LINE_SPACING_ADD, LINE_SPACING_MULT);
 
         mDecreaseFontButton = (Button) dialogView.findViewById(R.id.decrease_preview_font_button);
         mIncreaseFontButton = (Button) dialogView.findViewById(R.id.increase_preview_font_button);
+        setButtonState(mPreviewFontIndex);
         mDecreaseFontButton.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 updatePreviewFontSize(mFontTwipValues[--mPreviewFontIndex]);
                 mIncreaseFontButton.setEnabled(true);
                 // If we reached the minimum index, disable the button.
                 if (mPreviewFontIndex == 0) {
                     mDecreaseFontButton.setEnabled(false);
                 }
@@ -99,23 +95,16 @@ class FontSizePreference extends DialogP
         
                 mDecreaseFontButton.setEnabled(true);
                 // If we reached the maximum index, disable the button.
                 if (mPreviewFontIndex == mFontTwipValues.length - 1) {
                     mIncreaseFontButton.setEnabled(false);
                 }
             }
         });
-
-        // Set mPreviewFontView dimensions.
-        setPreviewFontViewWidth();
-        setPreviewFontViewHeightListener();
-        setFontSizeToMaximum(); // Expects onGlobalLayout() to be called.
-
-        builder.setView(dialogView);
     }
 
     @Override
     protected void onDialogClosed(boolean positiveResult) {
         super.onDialogClosed(positiveResult);
         if (!positiveResult) {
             mPreviewFontIndex = mSavedFontIndex;
             return;
@@ -125,116 +114,53 @@ class FontSizePreference extends DialogP
         final OnPreferenceChangeListener prefChangeListener = getOnPreferenceChangeListener();
         if (prefChangeListener == null) {
             Log.e(LOGTAG, "PreferenceChangeListener is null. FontSizePreference will not be saved to Gecko.");
             return;
         }
         prefChangeListener.onPreferenceChange(this, twipVal);
     }
 
-    protected void onConfigurationChanged(Configuration newConfig) {
-        if (mCurrentOrientation != newConfig.orientation) {
-            mCurrentOrientation = newConfig.orientation;
-
-            // mPreviewFontView will be null if the dialog has not yet been shown.
-            if (mPreviewFontView != null) {
-                // Recalculate the mPreviewFontView dimensions since we have new screen dimensions.
-                setPreviewFontViewWidth();
-                mPreviewFontViewHeightSet = false;
-                setFontSizeToMaximum(); // Expects onGlobalLayout() to be called.
-            }
-        }
-    }
-
-    /**
-     * Sets the preview font size to the maximum given size.
-     */
-    private void setFontSizeToMaximum() {
-        updatePreviewFontSize(mFontTwipValues[mFontTwipValues.length - 1]);
-        // NOTE: If this method is being used with onGlobalLayout() to set mFontPreviewView height,
-        // the font size cannot be changed past this point or the dialog height will not calculate
-        // correctly. We must wait for the global layout state to change, calculate the height in
-        // onGlobalLayout(), and only then can changes be made.
-    }
-
-    /**
-     * Sets a global layout listener that will calculate the maximum required height of
-     * mPreviewFontView based upon the current font values and then reset the TextView to the saved
-     * preference values. This listener will only run once. mPreviewFontViewHeightSet must be set to
-     * false before being used again.
-     */
-    private void setPreviewFontViewHeightListener() {
-        mPreviewFontViewHeightSet = false;
-        final ViewTreeObserver vto = mPreviewFontView.getViewTreeObserver(); 
-        vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 
-            @Override 
-            public void onGlobalLayout() { 
-                if (!mPreviewFontViewHeightSet) {
-                    mPreviewFontViewHeightSet = true;
-                    final int desiredHeight = (int) (mPreviewFontView.getLineCount() *
-                            mPreviewFontView.getLineHeight() * LINE_SPACING_MULT + LINE_SPACING_ADD +
-                            mPreviewFontView.getPaddingTop() + mPreviewFontView.getPaddingBottom());
-                    mPreviewFontView.setHeight(desiredHeight);
-
-                    // Set the dialog state to the saved preference values.
-                    setButtonState(mPreviewFontIndex);
-                    updatePreviewFontSize(mFontTwipValues[mPreviewFontIndex]);
-                }
-            } 
-        }); 
-    }
-
-    /**
-     * Sets the width of the mPreviewFontView TextView. The width is calculated by adding a constant
-     * minimum width to a fraction of the remaining space on screen (if any), which is determined by
-     * the given scale factor. Note that in ICS, it appears that the dialog will not wrap content
-     * and will set the view size itself. In Gingerbread, this code executes and sets the dialog
-     * width dynamically.
-     */
-    private void setPreviewFontViewWidth() {
-        final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
-        final float density = metrics.density;
-
-        final float actualWidthDip = metrics.widthPixels / density;
-        float scaleExtraDip = (actualWidthDip - MIN_TEXTVIEW_WIDTH_DIP) / TEXTVIEW_WIDTH_SCALE_FACTOR;
-        scaleExtraDip = scaleExtraDip >= 0 ? scaleExtraDip : 0;
-        final float desiredWidthDip = MIN_TEXTVIEW_WIDTH_DIP + scaleExtraDip;
-        final int desiredWidthPx = Math.round(desiredWidthDip * density);
-        mPreviewFontView.setWidth(desiredWidthPx);
-    }
-
     /**
      * Finds the index of the given twip value and sets it as the saved preference value. Also the
      * current preview text size to the given value. Does not update the mPreviewFontView text size.
      */
     protected void setSavedFontSize(String twip) {
         final Integer index = mFontTwipToIndexMap.get(twip);
         if (index != null) {
             mSavedFontIndex = index;
             mPreviewFontIndex = mSavedFontIndex;
             return;
         }
         resetSavedFontSizeToDefault();
         Log.e(LOGTAG, "setSavedFontSize: Given font size does not exist in twip values map. Reverted to default font size.");
     }
 
     /**
-     * Updates the mPreviewFontView to the given text size, resets the scroll to the top left, and
-     * invalidates the view. Does not update the font indices.
+     * Updates the mPreviewFontView to the given text size, resets the container's scroll to the top
+     * left, and invalidates the view. Does not update the font indices.
      */
     private void updatePreviewFontSize(String twip) {
         float pt = convertTwipStrToPT(twip);
         // Android will not render a font size of 0 pt but for Gecko, 0 twip turns off font
         // inflation. Thus we special case 0 twip to display a renderable font size.
         if (pt == 0) {
+            // Android adds an inexplicable extra margin on the smallest font size so to get around
+            // this, we reinflate the view.
+            ViewGroup parentView = (ViewGroup) mScrollingContainer.getParent();
+            parentView.removeAllViews();
+            final LayoutInflater inflater =
+                (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            View dialogView = inflater.inflate(R.layout.font_size_preference, parentView);
+            initInternalViews(dialogView);
             mPreviewFontView.setTextSize(PREVIEW_FONT_SIZE_UNIT, 1);
         } else {
             mPreviewFontView.setTextSize(PREVIEW_FONT_SIZE_UNIT, pt);
         }
-        mPreviewFontView.scrollTo(0, 0);
+        mScrollingContainer.scrollTo(0, 0);
     }
 
     /**
      * Resets the font indices to the default value. Does not update the mPreviewFontView text size.
      */
     private void resetSavedFontSizeToDefault() {
         mSavedFontIndex = DEFAULT_FONT_INDEX;
         mPreviewFontIndex = mSavedFontIndex;
--- a/mobile/android/base/GeckoPreferences.java
+++ b/mobile/android/base/GeckoPreferences.java
@@ -43,17 +43,16 @@ public class GeckoPreferences
     implements OnPreferenceChangeListener, GeckoEventListener
 {
     private static final String LOGTAG = "GeckoPreferences";
 
     private ArrayList<String> mPreferencesList;
     private PreferenceScreen mPreferenceScreen;
     private static boolean sIsCharEncodingEnabled = false;
     private static final String NON_PREF_PREFIX = "android.not_a_preference.";
-    private static final String FONT_SIZE_PREF_KEY = "font.size.inflation.minTwips";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         addPreferencesFromResource(R.xml.preferences);
         registerEventListener("Preferences:Data");
         registerEventListener("Sanitize:Finished");
    }
@@ -65,25 +64,16 @@ public class GeckoPreferences
 
         mPreferencesList = new ArrayList<String>();
         mPreferenceScreen = getPreferenceScreen();
         initGroups(mPreferenceScreen);
         initValues();
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-
-        final FontSizePreference fontSizePref =
-                (FontSizePreference) mPreferenceScreen.findPreference(FONT_SIZE_PREF_KEY);
-        fontSizePref.onConfigurationChanged(newConfig);
-    }
-
-    @Override
     protected void onDestroy() {
         super.onDestroy();
         unregisterEventListener("Preferences:Data");
         unregisterEventListener("Sanitize:Finished");
     }
 
     public void handleMessage(String event, JSONObject message) {
         try {
--- a/mobile/android/base/GeckoViewsFactory.java
+++ b/mobile/android/base/GeckoViewsFactory.java
@@ -33,17 +33,19 @@ public final class GeckoViewsFactory imp
         if (!TextUtils.isEmpty(name) && name.startsWith(GECKO_VIEW_IDENTIFIER)) {
             String viewName = name.substring(GECKO_VIEW_IDENTIFIER_LENGTH);
 
             if (TextUtils.isEmpty(viewName))
                 return null;
         
             Log.i(LOGTAG, "Creating custom Gecko view: " + viewName);
 
-            if (TextUtils.equals(viewName, "AboutHomeSection"))
+            if (TextUtils.equals(viewName, "AboutHomePromoBox"))
+                return new AboutHomePromoBox(context, attrs);
+            else if (TextUtils.equals(viewName, "AboutHomeSection"))
                 return new AboutHomeSection(context, attrs);
             else if (TextUtils.equals(viewName, "AwesomeBarTabs"))
                 return new AwesomeBarTabs(context, attrs);
             else if (TextUtils.equals(viewName, "FormAssistPopup"))
                 return new FormAssistPopup(context, attrs);
             else if (TextUtils.equals(viewName, "LinkTextView"))
                 return new LinkTextView(context, attrs);
             else if (TextUtils.equals(viewName, "FindInPageBar"))
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -31,16 +31,17 @@ UTIL_JAVA_FILES := \
   INIParser.java \
   INISection.java \
   util/EventDispatcher.java \
   util/FloatUtils.java \
   $(NULL)
 
 FENNEC_JAVA_FILES = \
   AboutHomeContent.java \
+  AboutHomePromoBox.java \
   AboutHomeSection.java \
   ActivityHandlerHelper.java \
   AndroidImport.java \
   AndroidImportPreference.java \
   AlertNotification.java \
   AwesomeBar.java \
   AwesomebarResultHandler.java \
   AwesomeBarTabs.java \
@@ -328,16 +329,17 @@ RES_LAYOUT = \
   res/layout/tabs_panel_toolbar.xml \
   res/layout/tabs_panel_toolbar_menu.xml \
   res/layout/tabs_row.xml \
   res/layout/tabs_tray.xml \
   res/layout/list_item_header.xml \
   res/layout/select_dialog_list.xml \
   res/layout/abouthome_addon_row.xml \
   res/layout/abouthome_last_tabs_row.xml \
+  res/layout/abouthome_promo_box.xml \
   res/layout/abouthome_section.xml \
   res/layout/abouthome_remote_tab_row.xml \
   res/layout/abouthome_topsite_item.xml \
   res/layout/validation_message.xml \
   $(NULL)
  
 RES_LAYOUT_LAND_V14 = \
   res/layout-land-v14/browser_toolbar.xml \
@@ -352,16 +354,17 @@ RES_LAYOUT_LARGE_V11 = \
   res/layout-large-v11/doorhangerpopup.xml \
   res/layout-large-v11/site_identity_popup.xml \
   res/layout-large-v11/tabs_panel_toolbar_menu.xml \
   $(NULL)
 
 RES_LAYOUT_XLARGE_V11 = \
   res/layout-xlarge-v11/awesomebar_search.xml \
   res/layout-xlarge-v11/browser_toolbar_menu.xml \
+  res/layout-xlarge-v11/font_size_preference.xml \
   res/layout-xlarge-v11/remote_tabs_child.xml \
   res/layout-xlarge-v11/remote_tabs_group.xml \
   res/layout-xlarge-v11/tabs_panel_toolbar_menu.xml \
   res/layout-xlarge-v11/tabs_row.xml \
   $(NULL)
 
 RES_VALUES = \
   $(SYNC_RES_VALUES) \
@@ -413,19 +416,19 @@ RES_DRAWABLE_NODPI = \
   res/drawable-nodpi/tabs_tray_selected_bg.png \
   $(NULL)
 
 RES_DRAWABLE_BASE = \
   res/drawable/favicon.png \
   res/drawable/folder.png \
   res/drawable/abouthome_icon.png \
   res/drawable/abouthome_logo.png \
+  res/drawable/abouthome_promo_box_bg.9.png \
   res/drawable/abouthome_sync_logo.png \
-  res/drawable/abouthome_sync_bg.9.png \
-  res/drawable/abouthome_sync_pressed_bg.9.png \
+  res/drawable/abouthome_promo_box_pressed_bg.9.png \
   res/drawable/abouthome_thumbnail.png \
   res/drawable/address_bar_bg_shadow.png \
   res/drawable/alert_addon.png \
   res/drawable/alert_app.png \
   res/drawable/alert_download.png \
   res/drawable/awesomebar_tab_center.9.png \
   res/drawable/awesomebar_tab_left.9.png \
   res/drawable/awesomebar_tab_right.9.png \
@@ -497,19 +500,19 @@ RES_DRAWABLE_LDPI = \
 
 RES_DRAWABLE_HDPI = \
   res/drawable-hdpi/favicon.png \
   res/drawable-hdpi/folder.png \
   res/drawable-hdpi/home_bg.png \
   res/drawable-hdpi/home_star.png \
   res/drawable-hdpi/abouthome_icon.png \
   res/drawable-hdpi/abouthome_logo.png \
+  res/drawable-hdpi/abouthome_promo_box_bg.9.png \
   res/drawable-hdpi/abouthome_sync_logo.png \
-  res/drawable-hdpi/abouthome_sync_bg.9.png \
-  res/drawable-hdpi/abouthome_sync_pressed_bg.9.png \
+  res/drawable-hdpi/abouthome_promo_box_pressed_bg.9.png \
   res/drawable-hdpi/abouthome_thumbnail.png \
   res/drawable-hdpi/address_bar_bg_shadow.png \
   res/drawable-hdpi/alert_addon.png \
   res/drawable-hdpi/alert_app.png \
   res/drawable-hdpi/alert_download.png \
   res/drawable-hdpi/awesomebar_tab_center.9.png \
   res/drawable-hdpi/awesomebar_tab_left.9.png \
   res/drawable-hdpi/awesomebar_tab_right.9.png \
@@ -563,19 +566,19 @@ RES_DRAWABLE_HDPI = \
   $(addprefix res/drawable-hdpi/,$(notdir $(SYNC_RES_DRAWABLE_HDPI))) \
   $(NULL)
 
 RES_DRAWABLE_XHDPI = \
   res/drawable-xhdpi/favicon.png \
   res/drawable-xhdpi/folder.png \
   res/drawable-xhdpi/abouthome_icon.png \
   res/drawable-xhdpi/abouthome_logo.png \
+  res/drawable-xhdpi/abouthome_promo_box_bg.9.png \
   res/drawable-xhdpi/abouthome_sync_logo.png \
-  res/drawable-xhdpi/abouthome_sync_bg.9.png \
-  res/drawable-xhdpi/abouthome_sync_pressed_bg.9.png \
+  res/drawable-xhdpi/abouthome_promo_box_pressed_bg.9.png \
   res/drawable-xhdpi/abouthome_thumbnail.png \
   res/drawable-xhdpi/address_bar_bg_curve.png \
   res/drawable-xhdpi/address_bar_bg_shadow.png \
   res/drawable-xhdpi/address_bar_texture_port.png \
   res/drawable-xhdpi/address_bar_url_default.9.png \
   res/drawable-xhdpi/address_bar_url_pressed.9.png \
   res/drawable-xhdpi/alert_addon.png \
   res/drawable-xhdpi/alert_app.png \
@@ -949,17 +952,17 @@ FENNEC_PP_JAVA_FILES += CrashReporter.ja
 MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/crash_reporter.png
 RES_LAYOUT += res/layout/crash_reporter.xml
 endif
 
 MOZ_ANDROID_DRAWABLES += \
   $(SYNC_RES_DRAWABLE)                                                          \
   mobile/android/base/resources/drawable/abouthome_bg_repeat.xml                \
   mobile/android/base/resources/drawable/abouthome_divider.xml                  \
-  mobile/android/base/resources/drawable/abouthome_sync_box.xml                 \
+  mobile/android/base/resources/drawable/abouthome_promo_box.xml                \
   mobile/android/base/resources/drawable/action_bar_button.xml                  \
   mobile/android/base/resources/drawable/address_bar_bg.xml                     \
   mobile/android/base/resources/drawable/address_bar_bg_shadow_repeat.xml       \
   mobile/android/base/resources/drawable/autocomplete_list_bg.9.png             \
   mobile/android/base/resources/drawable/awesomebar_tab_indicator.xml           \
   mobile/android/base/resources/drawable/awesomebar_tab_selected.xml            \
   mobile/android/base/resources/drawable/awesomebar_tab_unselected.xml          \
   mobile/android/base/resources/drawable/desktop_notification.png               \
--- a/mobile/android/base/TabsPanel.java
+++ b/mobile/android/base/TabsPanel.java
@@ -138,16 +138,17 @@ public class TabsPanel extends LinearLay
 
     public void show(Panel panel) {
         if (mPanel != null) {
             // Remove the old panel.
             mPanel.hide();
             mListContainer.removeAllViews();
         }
 
+        final boolean showAnimation = !mVisible;
         mVisible = true;
         mCurrentPanel = panel;
 
         if (panel == Panel.LOCAL_TABS) {
             mPanel = new TabsTray(mContext, null);
             mTitle.setText("");
             mRemoteTabs.setImageLevel(REMOTE_TABS_HIDDEN);
         } else {
@@ -157,26 +158,28 @@ public class TabsPanel extends LinearLay
             mRemoteTabs.setImageLevel(REMOTE_TABS_SHOWN);
         }
 
         mPanel.setTabsPanel(this);
         mPanel.show();
         mListContainer.addView(mPanel.getLayout());
 
         if (isSideBar()) {
-            dispatchLayoutChange(getWidth(), getHeight());
+            if (showAnimation)
+                dispatchLayoutChange(getWidth(), getHeight());
         } else {
             int actionBarHeight = (int) (mContext.getResources().getDimension(R.dimen.browser_toolbar_height));
 
             // TabsListContainer takes time to resize on rotation.
             // It's better to add 50% of the screen-size and dispatch it as height.
             int listHeight = (int) (0.5 * mContext.getResources().getDisplayMetrics().heightPixels);
 
             int height = actionBarHeight + listHeight; 
-            dispatchLayoutChange(getWidth(), height);
+            if (showAnimation)
+                dispatchLayoutChange(getWidth(), height);
         }
 
         // If Sync is set up, query the database for remote clients.
         final Context context = mContext;
         new SyncAccounts.AccountsExistTask() {
             @Override
             protected void onPostExecute(Boolean result) {
                 if (!result.booleanValue()) {
rename from mobile/android/base/resources/drawable-hdpi/abouthome_sync_bg.9.png
rename to mobile/android/base/resources/drawable-hdpi/abouthome_promo_box_bg.9.png
rename from mobile/android/base/resources/drawable-hdpi/abouthome_sync_pressed_bg.9.png
rename to mobile/android/base/resources/drawable-hdpi/abouthome_promo_box_pressed_bg.9.png
rename from mobile/android/base/resources/drawable-xhdpi/abouthome_sync_bg.9.png
rename to mobile/android/base/resources/drawable-xhdpi/abouthome_promo_box_bg.9.png
rename from mobile/android/base/resources/drawable-xhdpi/abouthome_sync_pressed_bg.9.png
rename to mobile/android/base/resources/drawable-xhdpi/abouthome_promo_box_pressed_bg.9.png
rename from mobile/android/base/resources/drawable/abouthome_sync_box.xml
rename to mobile/android/base/resources/drawable/abouthome_promo_box.xml
--- a/mobile/android/base/resources/drawable/abouthome_sync_box.xml
+++ b/mobile/android/base/resources/drawable/abouthome_promo_box.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <item android:state_pressed="true" android:drawable="@drawable/abouthome_sync_pressed_bg"/>
-    <item android:drawable="@drawable/abouthome_sync_bg"/>
+    <item android:state_pressed="true" android:drawable="@drawable/abouthome_promo_box_pressed_bg"/>
+    <item android:drawable="@drawable/abouthome_promo_box_bg"/>
 
 </selector>
rename from mobile/android/base/resources/drawable/abouthome_sync_bg.9.png
rename to mobile/android/base/resources/drawable/abouthome_promo_box_bg.9.png
rename from mobile/android/base/resources/drawable/abouthome_sync_pressed_bg.9.png
rename to mobile/android/base/resources/drawable/abouthome_promo_box_pressed_bg.9.png
--- a/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in
+++ b/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in
@@ -64,47 +64,28 @@
                                             android:text="@string/abouthome_top_sites_browse"
                                             android:layout_width="fill_parent"
                                             android:layout_height="30dip"
                                             android:layout_marginTop="7dip"
                                             android:textColor="#22629e"
                                             android:textSize="12sp"
                                             android:gravity="top|center_horizontal"/>
 
-                <LinearLayout android:id="@+id/sync_box"
-                              android:background="@drawable/abouthome_sync_box"
-                              android:orientation="horizontal"
-                              android:layout_width="fill_parent"
-                              android:layout_height="wrap_content"
-                              android:layout_marginLeft="12dp"
-                              android:layout_marginRight="12dp"
-                              android:layout_marginTop="17dp"
-                              android:layout_marginBottom="14dp"
-                              android:gravity="center"
-                              android:clickable="true"
-                              android:visibility="gone">
-
-                    <ImageView android:id="@+id/sync_logo"
-                               android:src="@drawable/abouthome_sync_logo"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_weight="0"
-                               android:gravity="left|center_vertical"/>
-
-                    <TextView android:id="@+id/sync_text"
-                              android:text="@string/abouthome_about_sync"
-                              android:layout_width="0dp"
-                              android:layout_height="wrap_content"
-                              android:layout_weight="1"
-                              android:layout_marginLeft="7dp"
-                              android:gravity="center"
-                              android:textSize="15sp"
-                              android:textColor="#FFFFFF"/>
-
-                </LinearLayout>
+            <org.mozilla.gecko.AboutHomePromoBox android:id="@+id/promo_box"
+                                                 android:orientation="horizontal"
+                                                 android:background="@drawable/abouthome_promo_box"
+                                                 android:layout_width="fill_parent"
+                                                 android:layout_height="wrap_content"
+                                                 android:layout_marginLeft="12dp"
+                                                 android:layout_marginRight="12dp"
+                                                 android:layout_marginTop="17dp"
+                                                 android:layout_marginBottom="14dp"
+                                                 android:gravity="center"
+                                                 android:clickable="true"
+                                                 android:visibility="gone"/>
 
         </LinearLayout>
 
         <RelativeLayout android:layout_width="0dp"
                         android:layout_height="fill_parent"
                         android:layout_weight="0.4"
                         android:layout_marginLeft="@dimen/abouthome_gutter_small"
                         android:layout_marginRight="@dimen/abouthome_gutter_large">
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout-xlarge-v11/font_size_preference.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
+   - You can obtain one at http://mozilla.org/MPL/2.0/.  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:orientation="vertical"> 
+
+    <ScrollView android:id="@+id/scrolling_container"
+                android:layout_width="fill_parent"
+                android:layout_height="350dp"
+                android:layout_margin="8dp"
+                android:padding="8dp"
+                android:scrollbars="vertical"
+                android:scrollbarStyle="outsideOverlay"
+                android:fadeScrollbars="true"
+                android:requiresFadingEdge="vertical">
+
+            <TextView android:id="@+id/preview"
+                      android:layout_width="fill_parent"
+                      android:layout_height="wrap_content"
+                      android:text="@string/pref_font_size_preview_text"
+                      android:textColor="#ff000000"/>
+
+    </ScrollView>
+
+    <LinearLayout android:id="@+id/button_container"
+                  android:layout_width="fill_parent"
+                  android:layout_height="wrap_content"
+                  android:layout_marginLeft="4dp"
+                  android:layout_marginRight="4dp"
+                  android:orientation="horizontal">
+
+        <Button android:id="@+id/decrease_preview_font_button"
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_weight="1"
+                android:text="@string/pref_font_size_adjust_char"
+                android:textSize="8sp"/>
+
+        <Button android:id="@+id/increase_preview_font_button"
+                android:layout_width="0dp"
+                android:layout_height="fill_parent"
+                android:layout_weight="1"
+                android:text="@string/pref_font_size_adjust_char"
+                android:textSize="16sp"/>
+
+    </LinearLayout>
+
+</LinearLayout>
--- a/mobile/android/base/resources/layout/abouthome_content.xml.in
+++ b/mobile/android/base/resources/layout/abouthome_content.xml.in
@@ -75,47 +75,28 @@
                                             android:text="@string/abouthome_top_sites_browse"
                                             android:layout_width="fill_parent"
                                             android:layout_height="30dip"
                                             android:layout_marginTop="7dip"
                                             android:textColor="#22629e"
                                             android:textSize="12sp"
                                             android:gravity="top|center_horizontal"/>
 
-            <LinearLayout android:id="@+id/sync_box"
-                          android:background="@drawable/abouthome_sync_box"
-                          android:orientation="horizontal"
-                          android:layout_width="fill_parent"
-                          android:layout_height="wrap_content"
-                          android:layout_marginLeft="12dp"
-                          android:layout_marginRight="12dp"
-                          android:layout_marginTop="17dp"
-                          android:layout_marginBottom="14dp"
-                          android:gravity="center"
-                          android:clickable="true"
-                          android:visibility="gone">
-
-                <ImageView android:id="@+id/sync_logo"
-                           android:src="@drawable/abouthome_sync_logo"
-                           android:layout_width="wrap_content"
-                           android:layout_height="wrap_content"
-                           android:layout_weight="0"
-                           android:gravity="left|center_vertical"/>
-
-                <TextView android:id="@+id/sync_text"
-                          android:text="@string/abouthome_about_sync"
-                          android:layout_width="0dp"
-                          android:layout_height="wrap_content"
-                          android:layout_weight="1"
-                          android:layout_marginLeft="7dp"
-                          android:gravity="center"
-                          android:textSize="15sp"
-                          android:textColor="#FFFFFF"/>
-
-            </LinearLayout>
+            <org.mozilla.gecko.AboutHomePromoBox android:id="@+id/promo_box"
+                                                 android:orientation="horizontal"
+                                                 android:background="@drawable/abouthome_promo_box"
+                                                 android:layout_width="fill_parent"
+                                                 android:layout_height="wrap_content"
+                                                 android:layout_marginLeft="12dp"
+                                                 android:layout_marginRight="12dp"
+                                                 android:layout_marginTop="17dp"
+                                                 android:layout_marginBottom="14dp"
+                                                 android:gravity="center"
+                                                 android:clickable="true"
+                                                 android:visibility="gone"/>
 
             <org.mozilla.gecko.AboutHomeSection android:id="@+id/last_tabs"
                                                 android:layout_width="fill_parent"
                                                 android:layout_height="wrap_content"
                                                 android:visibility="gone"
                                                 gecko:title="@string/abouthome_last_tabs_title"
                                                 gecko:more_text="@string/abouthome_last_tabs_open"/>
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/layout/abouthome_promo_box.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:gecko="http://schemas.android.com/apk/res/@ANDROID_PACKAGE_NAME@">
+
+    <ImageView android:id="@+id/icon"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:layout_weight="0"
+               android:gravity="left|center_vertical"/>
+
+    <TextView android:id="@+id/text"
+              android:layout_width="0dp"
+              android:layout_height="wrap_content"
+              android:layout_weight="1"
+              android:layout_marginLeft="7dp"
+              android:gravity="center"
+              android:textSize="15sp"
+              android:textColor="#FFFFFF"/>
+
+</merge>
--- a/mobile/android/base/resources/layout/font_size_preference.xml
+++ b/mobile/android/base/resources/layout/font_size_preference.xml
@@ -1,35 +1,24 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this file,
    - You can obtain one at http://mozilla.org/MPL/2.0/.  -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="fill_parent"
-              android:layout_height="fill_parent"
-              android:orientation="vertical"> 
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent">
 
-    <TextView android:id="@+id/preview"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:layout_weight="1"
-              android:background="#ffffffff"
-              android:paddingTop="2dp"
-              android:paddingBottom="2dp"
-              android:paddingLeft="3dp"
-              android:paddingRight="3dp"
-              android:text="@string/pref_font_size_preview_text"
-              android:textColor="#ff000000"
-              android:scrollbars="vertical"
-              android:fadeScrollbars="true"/>
-
-    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    <LinearLayout android:id="@+id/button_container"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
+                  android:layout_marginLeft="4dp"
+                  android:layout_marginRight="4dp"
+                  android:layout_alignParentBottom="true"
+                  android:layout_alignParentLeft="true"
                   android:orientation="horizontal">
 
         <Button android:id="@+id/decrease_preview_font_button"
                 android:layout_width="0dp"
                 android:layout_height="fill_parent"
                 android:layout_weight="1"
                 android:text="@string/pref_font_size_adjust_char"
                 android:textSize="8sp"/>
@@ -38,9 +27,28 @@
                 android:layout_width="0dp"
                 android:layout_height="fill_parent"
                 android:layout_weight="1"
                 android:text="@string/pref_font_size_adjust_char"
                 android:textSize="16sp"/>
 
     </LinearLayout>
 
-</LinearLayout>
+    <ScrollView android:id="@+id/scrolling_container"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:layout_above="@id/button_container"
+                android:layout_margin="8dp"
+                android:padding="8dp"
+                android:scrollbars="vertical"
+                android:scrollbarStyle="outsideOverlay"
+                android:fadeScrollbars="true"
+                android:requiresFadingEdge="vertical">
+
+            <TextView android:id="@+id/preview"
+                      android:layout_width="fill_parent"
+                      android:layout_height="wrap_content"
+                      android:text="@string/pref_font_size_preview_text"
+                      android:textColor="#ff000000"/>
+
+    </ScrollView>
+
+</RelativeLayout>
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -1472,18 +1472,18 @@ var gCategories = {
     category.setAttribute("id", "category-" + aId);
     category.setAttribute("value", aView);
     category.setAttribute("class", "category");
     category.setAttribute("name", aName);
     category.setAttribute("tooltiptext", aName);
     category.setAttribute("priority", aPriority);
     category.setAttribute("hidden", aStartHidden);
 
-    var node = this.node.firstChild;
-    while (node = node.nextSibling) {
+    var node;
+    for (node of this.node.children) {
       var nodePriority = parseInt(node.getAttribute("priority"));
       // If the new type's priority is higher than this one then this is the
       // insertion point
       if (aPriority < nodePriority)
         break;
       // If the new type's priority is lower than this one then this is isn't
       // the insertion point
       if (aPriority > nodePriority)
@@ -1645,17 +1645,17 @@ var gHeader = {
   _dest: "",
 
   initialize: function gHeader_initialize() {
     this._search = document.getElementById("header-search");
 
     this._search.addEventListener("command", function search_onCommand(aEvent) {
       var query = aEvent.target.value;
       if (query.length == 0)
-        return false;
+        return;
 
       gViewController.loadView("addons://search/" + encodeURIComponent(query));
     }, false);
 
     function updateNavButtonVisibility() {
       var shouldShow = gHeader.shouldShowNavButtons;
       document.getElementById("back-btn").hidden = !shouldShow;
       document.getElementById("forward-btn").hidden = !shouldShow;
@@ -2301,16 +2301,17 @@ var gSearchView = {
 
   getListItemForID: function gSearchView_getListItemForID(aId) {
     var listitem = this._listBox.firstChild;
     while (listitem) {
       if (listitem.getAttribute("status") == "installed" && listitem.mAddon.id == aId)
         return listitem;
       listitem = listitem.nextSibling;
     }
+    return null;
   }
 };
 
 
 var gListView = {
   node: null,
   _listBox: null,
   _emptyNotice: null,
@@ -2453,16 +2454,17 @@ var gListView = {
 
   getListItemForID: function gListView_getListItemForID(aId) {
     var listitem = this._listBox.firstChild;
     while (listitem) {
       if (listitem.getAttribute("status") == "installed" && listitem.mAddon.id == aId)
         return listitem;
       listitem = listitem.nextSibling;
     }
+    return null;
   }
 };
 
 
 var gDetailView = {
   node: null,
   _addon: null,
   _loadingTimer: null,