Backout the backout. This is an infrastructure issue, not an issue with the code in this CLOSED TREE.
authorKyle Huey <khuey@kylehuey.com>
Wed, 08 Jun 2011 07:56:25 -0700
changeset 71241 6031f209f2dfab13884d80b64444d07eae447eca
parent 71240 dbade3c7ec03fa3372c067ce65f9d48897eaac38
child 71242 b9354cf9282d8b4f8fae3bf43144e3bcf0e6c592
push id159
push usereakhgari@mozilla.com
push dateTue, 16 Aug 2011 17:53:11 +0000
treeherdermozilla-beta@8786e3e49240 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone7.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backout the backout. This is an infrastructure issue, not an issue with the code in this CLOSED TREE.
build/automation.py.in
chrome/src/nsChromeRegistry.h
chrome/src/nsChromeRegistryChrome.cpp
chrome/src/nsChromeRegistryChrome.h
chrome/src/nsChromeRegistryContent.cpp
chrome/src/nsChromeRegistryContent.h
mobile/chrome/content/aboutHome.xhtml
mobile/chrome/content/browser.js
mobile/chrome/content/browser.xul
mobile/chrome/content/firstrun/facebook.png
mobile/chrome/content/firstrun/features.png
mobile/chrome/content/firstrun/firstrun.xhtml
mobile/chrome/content/firstrun/mozilla.png
mobile/chrome/content/firstrun/nav-arrow.png
mobile/chrome/content/firstrun/twitter.png
mobile/chrome/content/input.js
mobile/chrome/content/localePicker.js
mobile/chrome/content/localePicker.xul
mobile/chrome/jar.mn
mobile/components/BrowserCLH.js
mobile/locales/en-US/chrome/firstrun.dtd
mobile/locales/en-US/chrome/localepicker.properties
mobile/locales/jar.mn
mobile/modules/LocaleRepository.jsm
mobile/modules/Makefile.in
mobile/themes/core/aboutHome.css
mobile/themes/core/browser.css
mobile/themes/core/firstRun.css
mobile/themes/core/gingerbread/browser.css
mobile/themes/core/gingerbread/defines.inc
mobile/themes/core/gingerbread/localePicker.css
mobile/themes/core/gingerbread/platform.css
mobile/themes/core/jar.mn
mobile/themes/core/localePicker.css
mobile/themes/core/platform.css
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -329,16 +329,17 @@ class Automation(object):
     # Set up permissions database
     locations = self.readLocations()
     self.setupPermissionsDatabase(profileDir,
       {'allowXULXBL':[(l.host, 'noxul' not in l.options) for l in locations]});
 
     part = """\
 user_pref("browser.console.showInPanel", true);
 user_pref("browser.dom.window.dump.enabled", true);
+user_pref("browser.firstrun.show.localepicker", false);
 user_pref("dom.allow_scripts_to_close_windows", true);
 user_pref("dom.disable_open_during_load", false);
 user_pref("dom.max_script_run_time", 0); // no slow script dialogs
 user_pref("dom.max_chrome_script_run_time", 0);
 user_pref("dom.popup_maximum", -1);
 user_pref("dom.send_after_paint_to_content", true);
 user_pref("dom.successive_dialog_time_limit", 0);
 user_pref("signed.applets.codebase_principal_support", true);
--- a/chrome/src/nsChromeRegistry.h
+++ b/chrome/src/nsChromeRegistry.h
@@ -111,17 +111,17 @@ public:
   static nsresult Canonify(nsIURL* aChromeURL);
 
 protected:
   void FlushSkinCaches();
   void FlushAllCaches();
 
   // Update the selected locale used by the chrome registry, and fire a
   // notification about this change
-  virtual void UpdateSelectedLocale() = 0;
+  virtual nsresult UpdateSelectedLocale() = 0;
 
   static void LogMessage(const char* aMsg, ...);
   static void LogMessageWithContext(nsIURI* aURL, PRUint32 aLineNumber, PRUint32 flags,
                                     const char* aMsg, ...);
 
   virtual nsIURI* GetBaseURIFromPackage(const nsCString& aPackage,
                                         const nsCString& aProvider,
                                         const nsCString& aPath) = 0;
--- a/chrome/src/nsChromeRegistryChrome.cpp
+++ b/chrome/src/nsChromeRegistryChrome.cpp
@@ -363,22 +363,19 @@ nsChromeRegistryChrome::Observe(nsISuppo
   if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
     nsCOMPtr<nsIPrefBranch> prefs (do_QueryInterface(aSubject));
     NS_ASSERTION(prefs, "Bad observer call!");
 
     NS_ConvertUTF16toUTF8 pref(someData);
 
     if (pref.EqualsLiteral(MATCH_OS_LOCALE_PREF) ||
         pref.EqualsLiteral(SELECTED_LOCALE_PREF)) {
-      if (!mProfileLoaded) {
-        rv = SelectLocaleFromPref(prefs);
-        if (NS_FAILED(rv))
-          return rv;
-      }
-      FlushAllCaches();
+        rv = UpdateSelectedLocale();
+        if (NS_SUCCEEDED(rv) && mProfileLoaded)
+          FlushAllCaches();
     }
     else if (pref.EqualsLiteral(SELECTED_SKIN_PREF)) {
       nsXPIDLCString provider;
       rv = prefs->GetCharPref(pref.get(), getter_Copies(provider));
       if (NS_FAILED(rv)) {
         NS_ERROR("Couldn't get new skin pref!");
         return rv;
       }
@@ -421,29 +418,32 @@ nsChromeRegistryChrome::CheckForNewChrom
   mOverlayHash.Clear();
   mStyleHash.Clear();
   mOverrideTable.Clear();
 
   nsComponentManagerImpl::gComponentManager->RereadChromeManifests();
   return NS_OK;
 }
 
-void nsChromeRegistryChrome::UpdateSelectedLocale()
+nsresult nsChromeRegistryChrome::UpdateSelectedLocale()
 {
+  nsresult rv = NS_OK;
   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (prefs) {
-    nsresult rv = SelectLocaleFromPref(prefs);
+    rv = SelectLocaleFromPref(prefs);
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIObserverService> obsSvc =
         mozilla::services::GetObserverService();
       NS_ASSERTION(obsSvc, "Couldn't get observer service.");
       obsSvc->NotifyObservers((nsIChromeRegistry*) this,
                               "selected-locale-has-changed", nsnull);
     }
   }
+
+  return rv;
 }
 
 static void
 SerializeURI(nsIURI* aURI,
              SerializedURI& aSerializedURI)
 {
   if (!aURI)
     return;
--- a/chrome/src/nsChromeRegistryChrome.h
+++ b/chrome/src/nsChromeRegistryChrome.h
@@ -78,17 +78,17 @@ class nsChromeRegistryChrome : public ns
   void SendRegisteredChrome(mozilla::dom::PContentParent* aChild);
 
  private:
   static PLDHashOperator CollectPackages(PLDHashTable *table,
                                          PLDHashEntryHdr *entry,
                                          PRUint32 number, void *arg);
 
   nsresult SelectLocaleFromPref(nsIPrefBranch* prefs);
-  NS_OVERRIDE void UpdateSelectedLocale();
+  NS_OVERRIDE nsresult UpdateSelectedLocale();
   NS_OVERRIDE nsIURI* GetBaseURIFromPackage(const nsCString& aPackage,
                                              const nsCString& aProvider,
                                              const nsCString& aPath);
   NS_OVERRIDE nsresult GetFlagsFromPackage(const nsCString& aPackage,
                                            PRUint32* aFlags);
 
   static const PLDHashTableOps kTableOps;
   static PLDHashNumber HashKey(PLDHashTable *table, const void *key);
--- a/chrome/src/nsChromeRegistryContent.cpp
+++ b/chrome/src/nsChromeRegistryContent.cpp
@@ -273,19 +273,19 @@ nsChromeRegistryContent::GetStyleOverlay
 
 NS_IMETHODIMP
 nsChromeRegistryContent::GetXULOverlays(nsIURI *aChromeURL,
                                         nsISimpleEnumerator **aResult)
 {
   CONTENT_NOT_IMPLEMENTED();
 }
 
-void nsChromeRegistryContent::UpdateSelectedLocale()
+nsresult nsChromeRegistryContent::UpdateSelectedLocale()
 {
-  CONTENT_NOTREACHED();
+  CONTENT_NOT_IMPLEMENTED();
 }
 
 void
 nsChromeRegistryContent::ManifestContent(ManifestProcessingContext& cx,
                                          int lineno, char *const * argv,
                                          bool platform, bool contentaccessible)
 {
   CONTENT_NOTREACHED();
--- a/chrome/src/nsChromeRegistryContent.h
+++ b/chrome/src/nsChromeRegistryContent.h
@@ -84,17 +84,17 @@ class nsChromeRegistryContent : public n
     nsCOMPtr<nsIURI> skinBaseURI;
     PRUint32         flags;
   };
   
   void RegisterPackage(const ChromePackage& aPackage);
   void RegisterResource(const ResourceMapping& aResource);
   void RegisterOverride(const OverrideMapping& aOverride);
 
-  NS_OVERRIDE void UpdateSelectedLocale();
+  NS_OVERRIDE nsresult UpdateSelectedLocale();
   NS_OVERRIDE nsIURI* GetBaseURIFromPackage(const nsCString& aPackage,
                                  const nsCString& aProvider,
                                  const nsCString& aPath);
   NS_OVERRIDE nsresult GetFlagsFromPackage(const nsCString& aPackage, PRUint32* aFlags);
 
   nsClassHashtable<nsCStringHashKey, PackageEntry> mPackagesHash;
   nsCString mLocale;
 
--- a/mobile/chrome/content/aboutHome.xhtml
+++ b/mobile/chrome/content/aboutHome.xhtml
@@ -57,39 +57,41 @@
   <link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
   <link rel="stylesheet" href="chrome://browser/skin/aboutHome.css" type="text/css"/>
 </head>
 
 <body dir="&locale.dir;" onload="init();" onunload="uninit();">
   <div id="wrapper">
     <div id="header"><div id="logo"><img src="chrome://branding/content/logo.png"/></div>&aboutHome.header;</div>
 
-    <div id="recentTabs" class="section-box">
-      <h1>&aboutHome.recentTabs;</h1>
-      <div id="loadingTabs" class="loading">
-        <img src="chrome://browser/skin/images/throbber.png"/>
-      </div>
-    </div>
-
-    <div id="remoteTabs" class="section-row" onclick="openRemoteTabs();" role="button">
-      <div>
-        <img class="favicon" src="chrome://browser/skin/images/remotetabs-32.png"/>
-        <div>&aboutHome.remoteTabs;</div>
+    <div id="content" hidden="true">
+      <div id="recentTabs" class="section-box">
+        <h1>&aboutHome.recentTabs;</h1>
+        <div id="loadingTabs" class="loading">
+          <img src="chrome://browser/skin/images/throbber.png"/>
+        </div>
       </div>
-    </div>
-
-    <div id="newAddons" class="section-box">
-      <h1>&aboutHome.recommendedAddons2;</h1>
-      <div id="loadingAddons" class="loading">
-        <img src="chrome://browser/skin/images/throbber.png"/>
+  
+      <div id="remoteTabs" class="section-row" onclick="openRemoteTabs();" role="button">
+        <div>
+          <img class="favicon" src="chrome://browser/skin/images/remotetabs-32.png"/>
+          <div>&aboutHome.remoteTabs;</div>
+        </div>
       </div>
-    </div>
-
-    <div id="footer-wrapper">
-      <span id="feedback" style="width: &aboutHome.footerWidth;" class="section-row" pref="app.feedbackURL" onclick="openLink(this);" role="button">&aboutHome.giveFeedback;</span><span id="support" style="width: &aboutHome.footerWidth;" class="section-row" pref="app.support.baseURL" onclick="openLink(this);" role="button">&aboutHome.getHelp;</span>
+  
+      <div id="newAddons" class="section-box">
+        <h1>&aboutHome.recommendedAddons2;</h1>
+        <div id="loadingAddons" class="loading">
+          <img src="chrome://browser/skin/images/throbber.png"/>
+        </div>
+      </div>
+  
+      <div id="footer-wrapper">
+        <span id="feedback" style="width: &aboutHome.footerWidth;" class="section-row" pref="app.feedbackURL" onclick="openLink(this);" role="button">&aboutHome.giveFeedback;</span><span id="support" style="width: &aboutHome.footerWidth;" class="section-row" pref="app.support.baseURL" onclick="openLink(this);" role="button">&aboutHome.getHelp;</span>
+      </div>
     </div>
   </div>
 
   <!-- l10n hack -->
   <div style="display: none">
     <span id="text-openalltabs">&aboutHome.openAllTabs;</span>
     <span id="text-notabs">&aboutHome.noTabs;</span>
     <span id="text-noaddons">&aboutHome.noAddons;</span>
@@ -120,16 +122,24 @@
                     .QueryInterface(Ci.nsIDOMChromeWindow);
       }
       return gChromeWin;
     }
 
     function init() {
       initTabs();
       initAddons();
+
+      let prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).QueryInterface(Ci.nsIPrefBranch2);
+      if (prefs.getBoolPref("browser.firstrun.show.uidiscovery")) {
+        startDiscovery();
+        prefs.setBoolPref("browser.firstrun.show.uidiscovery", false);
+      } else {
+        endDiscovery();
+      }
     }
 
     function uninit() {
       uninitAddons();
     }
 
     function _readFile(aFile) {
       try {
@@ -378,11 +388,39 @@
     function initAddons() {
       getChromeWin().Services.obs.addObserver(updateAddons, "recommended-addons-cache-updated", false);
       updateAddons();
     }
 
     function uninitAddons() {
       getChromeWin().Services.obs.removeObserver(updateAddons, "recommended-addons-cache-updated");
     }
+   
+   function startDiscovery() {
+     let win = getChromeWin();
+     let [leftWidth, rightWidth] = win.Browser.computeSidebarVisibility();
+     if (leftWidth > 0 || rightWidth > 0) {
+       endDiscovery();
+       return;
+     }
+
+     let doc = win.document;
+     let broadcaster = doc.getElementById("bcast_uidiscovery");
+
+     doc.addEventListener("animationend", endDiscovery, false);
+     doc.addEventListener("PanBegin", endDiscovery, false);
+     broadcaster.setAttribute("mode", "discovery");
+   }
+
+   function endDiscovery() {
+     let doc = getChromeWin().document;
+     let broadcaster = doc.getElementById("bcast_uidiscovery");
+
+     broadcaster.removeAttribute("mode");
+     doc.removeEventListener("animationend", endDiscovery, false);
+     doc.removeEventListener("PanBegin", endDiscovery, false);
+     setTimeout(function() {
+       document.getElementById("content").removeAttribute("hidden");
+     }, 0);
+   }
   ]]></script>
 </body>
 </html>
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -261,17 +261,17 @@ var Browser = {
       Browser.styles["window-height"].height = h + "px";
       Browser.styles["toolbar-height"].height = toolbarHeight + "px";
 
       // Tell the UI to resize the browser controls
       BrowserUI.sizeControls(w, h);
       ViewableAreaObserver.update();
 
       // Restore the previous scroll position
-      let restorePosition = Browser.controlsPosition;
+      let restorePosition = Browser.controlsPosition || { hideSidebars: true };
       if (restorePosition.hideSidebars) {
         restorePosition.hideSidebars = false;
         Browser.hideSidebars();
       } else {
         // Handle Width transformation of the tabs sidebar
         if (restorePosition.x) {
           let [,, leftWidth, rightWidth] = Browser.computeSidebarVisibility();
           let delta = ((restorePosition.leftSidebar - leftWidth) || (restorePosition.rightSidebar - rightWidth));
@@ -373,16 +373,21 @@ var Browser = {
     messageManager.addMessageListener("scroll", this);
     messageManager.addMessageListener("Browser:CertException", this);
     messageManager.addMessageListener("Browser:BlockedSite", this);
 
     // broadcast a UIReady message so add-ons know we are finished with startup
     let event = document.createEvent("Events");
     event.initEvent("UIReady", true, false);
     window.dispatchEvent(event);
+
+    // if we have an opener this was not the first window opened and will not
+    // receive an initial resize event. instead we fire the resize handler manually
+    if (window.opener)
+      resizeHandler({ target: window });
   },
 
   _alertShown: function _alertShown() {
     // ensure that the full notification still visible, even if the urlbar is floating
     if (BrowserUI.isToolbarLocked())
       Browser.pageScrollboxScroller.scrollTo(0, 0);
   },
 
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -531,25 +531,25 @@
       <dialog id="syncsetup-dialog" class="content-dialog" flex="1">
         <hbox class="prompt-title">
           <description>&sync.setup.title;</description>
         </hbox>
         <separator class="prompt-line"/>
         <vbox id="syncsetup-simple" class="syncsetup-page" flex="1">
           <scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
             <description class="syncsetup-desc syncsetup-center" flex="1">&sync.setup.jpake;</description>
-            <description class="syncsetup-center syncsetup-link" flex="1" onclick="WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
+            <description class="syncsetup-center link" flex="1" onclick="WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
             <separator/>
             <vbox align="center" flex="1">
               <description id="syncsetup-code1" class="syncsetup-code">....</description>
               <description id="syncsetup-code2" class="syncsetup-code">....</description>
               <description id="syncsetup-code3" class="syncsetup-code">....</description>
             </vbox>
             <separator/>
-            <description class="syncsetup-center syncsetup-link" flex="1" onclick="WeaveGlue.openManual();">&sync.fallback;</description>
+            <description class="syncsetup-center link" flex="1" onclick="WeaveGlue.openManual();">&sync.fallback;</description>
             <separator flex="1"/>
           </scrollbox>
           <hbox class="prompt-buttons" pack="center">
             <button oncommand="WeaveGlue.close();">&sync.setup.cancel;</button>
           </hbox>
         </vbox>
         <vbox id="syncsetup-fallback" class="syncsetup-page" flex="1" hidden="true">
           <scrollbox class="prompt-message" orient="vertical" flex="1">
deleted file mode 100644
index e60ee7a520b90adf2f6ca483f45731e9310b4452..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ae9cdc076cb56922513c08f70c73bcae6b4b46ea..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/mobile/chrome/content/firstrun/firstrun.xhtml
+++ /dev/null
@@ -1,184 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
-  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
-<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
-%brandDTD;
-<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
-%globalDTD;
-<!ENTITY % firstrunDTD SYSTEM "chrome://browser/locale/firstrun.dtd" >
-%firstrunDTD;
-]>
-
-<!-- ***** BEGIN LICENSE BLOCK *****
-   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
-   -
-   - The contents of this file are subject to the Mozilla Public License Version
-   - 1.1 (the "License"); you may not use this file except in compliance with
-   - the License. You may obtain a copy of the License at
-   - http://www.mozilla.org/MPL/
-   -
-   - Software distributed under the License is distributed on an "AS IS" basis,
-   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-   - for the specific language governing rights and limitations under the
-   - License.
-   -
-   - The Original Code is Mozilla Communicator client code, released
-   - March 31, 1998.
-   -
-   - The Initial Developer of the Original Code is
-   - Netscape Communications Corporation.
-   - Portions created by the Initial Developer are Copyright (C) 1998-1999
-   - the Initial Developer. All Rights Reserved.
-   -
-   - Contributor(s):
-   -   Henrik Gemal <mozilla@gemal.dk>
-   -   Daniel Veditz <dveditz@netscape.com>
-   -   Alexey Chernyak <alexeyc@bigfoot.com>
-   -   Steffen Wilberg <steffen.wilberg@web.de>
-   -
-   - Alternatively, the contents of this file may be used under the terms of
-   - either the GNU General Public License Version 2 or later (the "GPL"), or
-   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-   - in which case the provisions of the GPL or the LGPL are applicable instead
-   - of those above. If you wish to allow use of your version of this file only
-   - under the terms of either the GPL or the LGPL, and not to allow others to
-   - use your version of this file under the terms of the MPL, indicate your
-   - decision by deleting the provisions above and replace them with the notice
-   - and other provisions required by the GPL or the LGPL. If you do not delete
-   - the provisions above, a recipient may use your version of this file under
-   - the terms of any one of the MPL, the GPL or the LGPL.
-   -
-   - ***** END LICENSE BLOCK ***** -->
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
-  <meta name="viewport" content="width=480; initial-scale=.6667; user-scalable=0;" />
-  <title>&firstrun.title;</title>
-  <link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
-  <link rel="stylesheet" href="chrome://browser/skin/firstRun.css" type="text/css"/>
-</head>
-
-<body id="firstrun" dir="&locale.dir;" class="">
-  <div id="wrapper">
-    <div id="header">
-      <a href="http://mozilla.com/m"><img src="chrome://browser/content/firstrun/mozilla.png" width="64" height="17"/></a>
-    </div>
-
-    <div id="head">
-      <h1><img src="chrome://branding/content/logoWordmark.png"/></h1>
-      <p>&firstrun.heading2;</p>
-    </div>
-
-    <div id="content" hidden="true">
-      <h2></h2>
-      <ul id="recommended">
-        <li id="sync"><a href="javascript:loadSync();"><b>&firstrun.sync2.title;</b> <span>&firstrun.sync2.description;</span></a></li>
-        <li id="addons"><a href="javascript:loadAddons();"><b>&firstrun.addons2.title;</b> <span>&firstrun.addons2.description;</span></a></li>
-        <li id="home"><a href="about:home"><b>&firstrun.home2.title;</b> <span>&firstrun.home2.description;</span></a></li>
-      </ul>
-
-      <p id="features"><a id="featuresURL"><span>&firstrun.features;</span><img id="nav-arrow" src="chrome://browser/content/firstrun/nav-arrow.png" /></a></p>
-      <script type="application/javascript;version=1.8"><![CDATA[
-        let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
-        let gChromeWin = null;
-
-        let links = [
-          {id: "releaseNotesURL", pref: "app.releaseNotesURL"},
-          {id: "featuresURL", pref: "app.featuresURL"},
-          {id: "faqURL", pref: "app.faqURL"},
-          {id: "privacyURL", pref: "app.privacyURL"}
-        ];
-
-        function getChromeWin() {
-          if (!gChromeWin) {
-            gChromeWin = window
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow)
-                    .QueryInterface(Ci.nsIDOMChromeWindow);
-          }
-          return gChromeWin;
-        }
-
-        function loadSync() {
-          let win = getChromeWin();
-          // If Sync is not setup, we'll open the setup dialog. Otherwise,
-          // we show the remote tabs view
-          win.BrowserUI.doCommand("cmd_remoteTabs");
-        }
-
-        function loadAddons() {
-          let win = getChromeWin();
-          win.BrowserUI.showPanel("addons-container");
-        }
-
-        function init() {
-          setupLinks();
-          startDiscovery();
-        }
-        
-        function startDiscovery() {
-          let win = getChromeWin();
-          let [leftWidth, rightWidth] = win.Browser.computeSidebarVisibility();
-          if (leftWidth > 0 || rightWidth > 0) {
-            endDiscovery();
-            return;
-          }
-
-          let doc = win.document;
-          let broadcaster = doc.getElementById("bcast_uidiscovery");
-
-          doc.addEventListener("animationend", endDiscovery, false);
-          doc.addEventListener("PanBegin", endDiscovery, false);
-          broadcaster.setAttribute("mode", "discovery");
-        }
-
-        function endDiscovery() {
-          let doc = getChromeWin().document;
-          let broadcaster = doc.getElementById("bcast_uidiscovery");
-
-          broadcaster.removeAttribute("mode");
-          doc.removeEventListener("animationend", endDiscovery, false);
-          doc.removeEventListener("PanBegin", endDiscovery, false);
-          document.getElementById("content").removeAttribute("hidden");
-        }
-
-        function setupLinks() {
-          try {
-            let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
-            links.forEach(function(link) {
-              let element = document.getElementById(link.id);
-              if (element) {
-                let url = formatter.formatURLPref(link.pref);
-                element.setAttribute("href", url);
-              }
-            });
-          } catch (ex) {}
-        }
-
-        document.addEventListener("DOMContentLoaded", init, false);
-      ]]></script>
-    </div>
-
-    <div id="footer">
-
-      <ul class="nav">
-        <li><a id="faqURL">&firstrun.faq;</a></li>
-        <li><a id="privacyURL">&firstrun.privacy;</a></li>
-      </ul>
-
-      <div id="follow">
-        <p>&firstrun.follow;</p>
-        <ul>
-          <li id="follow-twitter"><a href="http://twitter.com/mozmobile"><img src="chrome://browser/content/firstrun/twitter.png" height="41" width="42" /></a></li>
-          <li id="follow-facebook"><a href="http://www.facebook.com/firefoxformobile"><img src="chrome://browser/content/firstrun/facebook.png" height="41" width="42" /></a></li>
-        </ul>
-      </div>
-    </div>
-  </div>
-</body>
-</html>
deleted file mode 100644
index a6de797c13843c90a5cc0bdfac439278accefe04..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 75d3135493498f0f2bf9e53b4a5b67d54f2d324c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 369450985a1ad4b6e9a413b0a1f71167611fc1cf..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/mobile/chrome/content/input.js
+++ b/mobile/chrome/content/input.js
@@ -37,16 +37,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+Components.utils.import("resource://gre/modules/Geometry.jsm");
+
 // Maximum delay in ms between the two taps of a double-tap
 const kDoubleClickInterval = 400;
 
 // Maximum distance in inches between the taps of a double-tap
 const kDoubleClickRadius = 0.4;
 
 // Amount of time to wait before tap is generate a mousemove 
 const kOverTapWait = 150;
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/content/localePicker.js
@@ -0,0 +1,365 @@
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource:///modules/LocaleRepository.jsm");
+
+let stringPrefs = [
+  { selector: "#continue-in-button", pref: "continueIn", data: ["CURRENT_LANGUAGE"] },
+  { selector: "#change-language", pref: "choose", data: null },
+  { selector: "#picker-title", pref: "chooseLanguage", data: null },
+  { selector: "#continue-button", pref: "continue", data: null },
+  { selector: "#cancel-button", pref: "cancel", data: null },
+  { selector: "#intalling-message", pref: "installing", data: ["CURRENT_LANGUAGE"]  },
+  { selector: "#cancel-install-button", pref: "cancel", data: null },
+  { selector: "#installing-error", pref: "installerror", data: null },
+  { selector: "#install-continue", pref: "continue", data: null }
+];
+
+let LocaleUI = {
+  _strings: null,
+
+  get strings() {
+    if (!this._strings)
+      this._strings = Services.strings.createBundle("chrome://browser/locale/localepicker.properties");
+    return this._strings;
+  },
+
+  set strings(aVal) {
+    this._strings = aVal;
+  },
+
+  get _mainPage() {
+    delete this._mainPage;
+    return this._mainPage = document.getElementById("main-page");
+  },
+
+  get _pickerPage() {
+    delete this._pickerPage;
+    return this._pickerPage = document.getElementById("picker-page");
+  },
+
+  get _installerPage() {
+    delete this._installerPage;
+    return this._installerPage = document.getElementById("installer-page");
+  },
+
+  get _deck() {
+    delete this._deck;
+    return this._deck = document.getElementById("language-deck");
+  },
+
+  _currentInstall: null, // used to cancel an install
+
+  get selectedPanel() {
+    return this._deck.selectedPanel;
+  },
+
+  set selectedPanel(aPanel) {
+    this._deck.selectedPanel = aPanel;
+  },
+
+  get list() {
+    delete this.list;
+    return this.list = document.getElementById("language-list");
+  },
+
+  _createItem: function(aId, aText, aLocale) {
+    let item = document.createElement("richlistitem");
+    item.setAttribute("id", aId);
+
+    let description = document.createElement("description");
+    description.appendChild(document.createTextNode(aText));
+    description.setAttribute('flex', 1);
+    item.appendChild(description);
+    item.setAttribute("locale", getTargetLanguage(aLocale.addon));
+
+    if (aLocale) {
+      item.locale = aLocale.addon;
+      let checkbox = document.createElement("image");
+      checkbox.classList.add("checkbox");
+      item.appendChild(checkbox);
+    } else {
+      item.classList.add("message");
+    }
+    return item;
+  },
+
+  addLocales: function(aLocales) {
+    let fragment = document.createDocumentFragment();
+    let selectedItem = null;
+    let bestMatch = NO_MATCH;
+
+    for each (let locale in aLocales) {
+      let targetLang = getTargetLanguage(locale.addon);
+      if (document.querySelector('[locale="' + targetLang + '"]'))
+        continue;
+
+      let item = this._createItem(targetLang, locale.addon.name, locale);
+      let match = localesMatch(targetLang, this.language);
+      if (match > bestMatch) {
+        bestMatch = match;
+        selectedItem = item;
+      }
+      fragment.appendChild(item);
+    }
+    this.list.appendChild(fragment);
+    if (selectedItem && !this.list.selectedItem);
+      this.list.selectedItem = selectedItem;
+  },
+
+  loadLocales: function() {
+    while (this.list.firstChild)
+      this.list.removeChild(this.list.firstChild);
+    this.addLocales(this.availableLocales);
+    LocaleRepository.getLocales(this.addLocales.bind(this));
+  },
+
+  showPicker: function() {
+    LocaleUI.selectedPanel = LocaleUI._pickerPage;
+    LocaleUI.loadLocales();
+  },
+
+  closePicker: function() {
+    if (this._currentInstall) {
+      Services.prefs.setBoolPref("intl.locale.matchOS", false);
+      Services.prefs.setCharPref("general.useragent.locale", getTargetLanguage(this._currentInstall));
+    }
+    this.selectedPanel = this._mainPage;
+  },
+
+  _language: "",
+
+  set language(aVal) {
+    if (aVal == this._language)
+      return;
+
+    Services.prefs.setBoolPref("intl.locale.matchOS", false);
+    Services.prefs.setCharPref("general.useragent.locale", aVal);
+    this._language = aVal;
+
+    this.strings = null;
+    this.updateStrings();
+  },
+
+  get language() {
+    return this._language;
+  },
+
+  set installStatus(aVal) {
+    this._installerPage.selectedPanel = document.getElementById("installer-page-" + aVal);
+  },
+
+  clearInstallError: function() {
+    this.installStatus = "installing";
+    this.selectedPanel = this._pickerPage;
+  },
+
+  selectLanguage: function(aEvent) {
+    let locale = this.list.selectedItem.locale;
+    if (locale.install)
+      this.updateStrings(locale);
+    else {
+      this.language = getTargetLanguage(locale);
+      if (this._currentInstall)
+        this._currentInstall = null;
+    }
+  },
+
+  installAddon: function() {
+    let locale = LocaleUI.list.selectedItem.locale;
+    LocaleUI._currentInstall = locale;
+
+    if (locale.install) {
+      LocaleUI.selectedPanel = LocaleUI._installerPage;
+      locale.install.addListener(installListener);
+      locale.install.install();
+    } else {
+      this.closePicker();
+    }
+  },
+
+  cancelPicker: function() {
+    if (this._currentInstall)
+      this._currentInstall = null;
+    // restore the last known "good" locale
+    this.language = this.defaultLanguage;
+    this.updateStrings();
+    this.closePicker();
+  },
+
+  closeWindow : function() {
+    // Trying to close this window and open a new one results in a corrupt UI.
+    if (false && LocaleUI._currentInstall) {
+      let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+    
+      if (cancelQuit.data == false) {
+        let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
+        appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eForceQuit);
+        Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", false);
+      }
+    } else {
+      let argString = null;
+      if (window.arguments) {
+        argString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+        argString.data = window.arguments.join(",");
+      }
+      let win = Services.ww.openWindow(window, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", argString);
+      window.close();
+    }
+  },
+
+  cancelInstall: function () {
+    if (LocaleUI._currentInstall) {
+      let addonInstall = LocaleUI._currentInstall.install;
+      try { addonInstall.cancel(); }
+      catch(ex) { }
+      LocaleUI._currentInstall = null;
+
+      this.language = this.defaultLanguage;
+    }
+  },
+
+  updateStrings: function (aAddon) {
+    stringPrefs.forEach(function(aPref) {
+      if (!aPref.element)
+        aPref.element = document.querySelector(aPref.selector);
+  
+      let string = "";
+      try {
+        string = getString(aPref.pref, aPref.data, aAddon);
+      } catch(ex) { }
+      aPref.element.textContent = string;
+    });
+  }
+}
+
+// Gets the target language for a locale addon
+// For now this returns the targetLocale, although if and addon doesn't
+// specify a targetLocale we could attempt to guess the locale from the addon's name
+function getTargetLanguage(aAddon) {
+  return aAddon.targetLocale;
+}
+
+// Gets a particular string for the passed in locale
+// Parameters: aStringName - The name of the string property to get
+//             aDataset - an array of properties to use in a formatted string
+//             aAddon - An addon to attempt to get dataset properties from
+function getString(aStringName, aDataSet, aAddon) {
+  if (aDataSet) {
+    let databundle = aDataSet.map(function(aData) {
+      switch (aData) {
+        case "CURRENT_LANGUAGE" :
+          if (aAddon)
+            return aAddon.name;
+          try { return LocaleUI.strings.GetStringFromName("name"); }
+          catch(ex) { return null; }
+          break;
+        default :
+      }
+      return "";
+    });
+    if (databundle.some(function(aItem) aItem === null))
+      throw("String not found");
+    return LocaleUI.strings.formatStringFromName(aStringName, databundle, databundle.length);
+  }
+
+  return LocaleUI.strings.GetStringFromName(aStringName);
+}
+
+let installListener = {
+  onNewInstall: function(install) { },
+  onDownloadStarted: function(install) { },
+  onDownloadProgress: function(install) { },
+  onDownloadEnded: function(install) { },
+  onDownloadCancelled: function(install) {
+    LocaleUI.cancelInstall();
+    LocaleUI.showPicker();
+  },
+  onDownloadFailed: function(install) {
+    LocaleUI.cancelInstall();
+    LocaleUI.installStatus = "error";
+  },
+  onInstallStarted: function(install) { },
+  onInstallEnded: function(install, addon) {
+    LocaleUI.updateStrings(LocaleUI._currentInstall);
+    LocaleUI.closePicker();
+  },
+  onInstallCancelled: function(install) {
+    LocaleUI.cancelInstall();
+    LocaleUI.showPicker();
+  },
+  onInstallFailed: function(install) {
+    LocaleUI.cancelInstall();
+    LocaleUI.installStatus = "error";
+  },
+  onExternalInstall: function(install, existingAddon, needsRestart) { }
+}
+
+const PERFECT_MATCH = 2;
+const GOOD_MATCH = 1;
+const NO_MATCH = 0;
+//Compares two locales of the form AB or AB-CD
+//returns GOOD_MATCH if AB == AB in both locales, PERFECT_MATCH if AB-CD == AB-CD
+function localesMatch(aLocale1, aLocale2) {
+  if (aLocale1 == aLocale2)
+    return PERFECT_MATCH;
+
+  let short1 = aLocale1.split("-")[0];
+  let short2 = aLocale2.split("-")[0];
+  return (short1 == short2) ? GOOD_MATCH : NO_MATCH;
+}
+
+function start() {
+  let mouseModule = new MouseModule();
+
+  let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
+  chrome.QueryInterface(Ci.nsIToolkitChromeRegistry);
+  let availableLocales = chrome.getLocalesForPackage("browser");
+
+  let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci.nsILocaleService);
+  let systemLocale = localeService.getSystemLocale().getCategory("NSILOCALE_CTYPE");
+  let matchingLocale = "";
+
+  let bestMatch = NO_MATCH;
+
+  let strings = Services.strings.createBundle("chrome://browser/content/languages.properties");
+  LocaleUI.availableLocales = [];
+  while (availableLocales.hasMore()) {
+    let locale = availableLocales.getNext();
+
+    let label = locale;
+    try { label = strings.GetStringFromName(locale); }
+    catch (e) { }
+    LocaleUI.availableLocales.push({addon: { id: locale, name: label, targetLocale: locale }});
+
+    // see if we have a locale that looks like the system locale
+    // if it is not the current locale, switch to it
+    let match = localesMatch(systemLocale, locale);
+    if (match > bestMatch) {
+      bestMatch = match;
+      matchingLocale = locale;
+    }
+  }
+
+  if (matchingLocale != chrome.getSelectedLocale("browser"))
+    LocaleUI.language = matchingLocale;
+  else {
+    LocaleUI._language = chrome.getSelectedLocale("browser");
+    LocaleUI.updateStrings();
+  }
+
+  // update the page strings and show the correct page
+  LocaleUI.defaultLanguage = LocaleUI._language;
+  window.addEventListener("resize", resizeHandler, false);
+}
+
+function resizeHandler() {
+  let elements = document.getElementsByClassName("window-width");
+  for (let i = 0; i < elements.length; i++)
+    elements[i].setAttribute("width", Math.min(800, window.innerWidth));
+}
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/content/localePicker.xul
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/localePicker.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="start();"
+        width="480"
+        height="800">
+  <script src="chrome://browser/content/Util.js" type="application/javascript;version=1.8"/>
+  <script src="chrome://browser/content/input.js" type="application/javascript;version=1.8"/>
+  <script src="chrome://browser/content/localePicker.js" type="application/javascript;version=1.8"/>
+  <deck id="language-deck" flex="1">
+    <vbox id="main-page" class="pane" flex="1">
+      <spacer flex="1"/>
+      <button class="continue" id="continue-in-button" onclick="LocaleUI.closeWindow();" crop="center"/>
+      <description id="change-language" class="link" onclick="LocaleUI.showPicker();" role="button"/>
+    </vbox>
+
+    <vbox id="picker-page" class="pane" flex="1">
+      <description id="picker-title"/>
+      <richlistbox id="language-list" onclick="LocaleUI.selectLanguage(event);" flex="1" class="window-width"/>
+      <hbox class="footer">
+        <button id="cancel-button" class="cancel" onclick="LocaleUI.cancelPicker();" crop="center"/>
+        <button id="continue-button" class="continue" onclick="LocaleUI.installAddon();" crop="center"/>
+      </hbox>
+    </vbox>
+
+    <deck id="installer-page" class="pane" flex="1">
+      <vbox id="installer-page-installing" flex="1" pack="center" align="center">
+        <description id="intalling-message" class="install-message"/>
+        <button id="cancel-install-button" class="cancel" onclick="LocaleUI.cancelInstall();" crop="center"/>
+      </vbox>
+      <vbox id="installer-page-error" flex="1" pack="center" align="center">
+        <description id="installing-error" class="install-message"/>
+        <button id="install-continue" class="continue" onclick="LocaleUI.clearInstallError();" crop="center"/>
+      </vbox>
+    </deck>
+
+  </deck>
+</window>
--- a/mobile/chrome/jar.mn
+++ b/mobile/chrome/jar.mn
@@ -1,25 +1,21 @@
 #filter substitution
 
 chrome.jar:
 % content browser %content/
-  content/firstrun/firstrun.xhtml      (content/firstrun/firstrun.xhtml)
-  content/firstrun/mozilla.png         (content/firstrun/mozilla.png)
-  content/firstrun/features.png        (content/firstrun/features.png)
-  content/firstrun/twitter.png         (content/firstrun/twitter.png)
-  content/firstrun/facebook.png        (content/firstrun/facebook.png)
-  content/firstrun/nav-arrow.png       (content/firstrun/nav-arrow.png)
 
 * content/about.xhtml                  (content/about.xhtml)
   content/config.xul                   (content/config.xul)
   content/config.js                    (content/config.js)
   content/aboutCertError.xhtml         (content/aboutCertError.xhtml)
   content/aboutCertError.css           (content/aboutCertError.css)
   content/aboutHome.xhtml              (content/aboutHome.xhtml)
+  content/localePicker.xul             (content/localePicker.xul)
+  content/localePicker.js              (content/localePicker.js)
 * content/aboutRights.xhtml            (content/aboutRights.xhtml)
   content/blockedSite.xhtml            (content/blockedSite.xhtml)
   content/languages.properties         (content/languages.properties)
 * content/browser.xul                  (content/browser.xul)
 * content/browser.js                   (content/browser.js)
 * content/browser-ui.js                (content/browser-ui.js)
 * content/browser-scripts.js           (content/browser-scripts.js)
 * content/common-ui.js                 (content/common-ui.js)
--- a/mobile/components/BrowserCLH.js
+++ b/mobile/components/BrowserCLH.js
@@ -189,26 +189,29 @@ BrowserCLH.prototype = {
     // Open the main browser window, if we don't already have one
     let win;
     try {
       win = Services.wm.getMostRecentWindow("navigator:browser");
       if (!win) {
         // Default to the saved homepage
         let defaultURL = getHomePage();
 
-        // Override the default if we have a new profile
-        if (needHomepageOverride() == "new profile")
-            defaultURL = "about:firstrun";
-
         // Override the default if we have a URL passed on command line
         if (uris.length > 0) {
           defaultURL = uris[0].spec;
           uris = uris.slice(1);
         }
 
+        // Show the locale selector if we have a new profile
+        if (needHomepageOverride() == "new profile" && Services.prefs.getBoolPref("browser.firstrun.show.localepicker")) {
+          win = openWindow(null, "chrome://browser/content/localePicker.xul", "_blank", "chrome,dialog=no,all", defaultURL);
+          aCmdLine.preventDefault = true;
+          return;
+        }
+
         win = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", defaultURL);
       }
 
       win.focus();
 
       // Stop the normal commandline processing from continuing. We just opened the main browser window
       aCmdLine.preventDefault = true;
     } catch (e) { }
deleted file mode 100644
--- a/mobile/locales/en-US/chrome/firstrun.dtd
+++ /dev/null
@@ -1,12 +0,0 @@
-<!ENTITY firstrun.title                "Welcome to &brandShortName;">
-<!ENTITY firstrun.heading2             "Welcome to &brandShortName;">
-<!ENTITY firstrun.sync2.title          "Sync Up">
-<!ENTITY firstrun.sync2.description    "Login with your Sync account info and share your history, passwords, bookmarks &amp; tabs between your desktop and mobile">
-<!ENTITY firstrun.addons2.title        "Customize">
-<!ENTITY firstrun.addons2.description  "Discover and install add-ons to add new functionality to your &brandShortName;">
-<!ENTITY firstrun.home2.title          "Browse">
-<!ENTITY firstrun.home2.description    "Skip all this and go to your personalized Start page to start browsing the web">
-<!ENTITY firstrun.features             "See More Features">
-<!ENTITY firstrun.faq                  "FAQ">
-<!ENTITY firstrun.privacy              "Privacy Policy">
-<!ENTITY firstrun.follow               "Follow us on:">
new file mode 100644
--- /dev/null
+++ b/mobile/locales/en-US/chrome/localepicker.properties
@@ -0,0 +1,10 @@
+title=Select a language
+continueIn=Continue in %S
+name=English
+choose=Choose a different language
+chooseLanguage=Choose a Language
+cancel=Cancel
+continue=Continue
+installing=Installing %S
+installerror=Error installing language
+list.loading=Loading...
--- a/mobile/locales/jar.mn
+++ b/mobile/locales/jar.mn
@@ -3,17 +3,17 @@
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/@AB_CD@/browser/
   locale/@AB_CD@/browser/about.dtd                (%chrome/about.dtd)
   locale/@AB_CD@/browser/aboutCertError.dtd       (%chrome/aboutCertError.dtd)
   locale/@AB_CD@/browser/aboutHome.dtd            (%chrome/aboutHome.dtd)
   locale/@AB_CD@/browser/browser.dtd              (%chrome/browser.dtd)
   locale/@AB_CD@/browser/browser.properties       (%chrome/browser.properties)
   locale/@AB_CD@/browser/config.dtd               (%chrome/config.dtd)
-  locale/@AB_CD@/browser/firstrun.dtd             (%chrome/firstrun.dtd)
+  locale/@AB_CD@/browser/localepicker.properties  (%chrome/localepicker.properties)
   locale/@AB_CD@/browser/region.properties        (%chrome/region.properties)
   locale/@AB_CD@/browser/preferences.dtd          (%chrome/preferences.dtd)
   locale/@AB_CD@/browser/checkbox.dtd             (%chrome/checkbox.dtd)
   locale/@AB_CD@/browser/notification.dtd         (%chrome/notification.dtd)
   locale/@AB_CD@/browser/sync.dtd                 (%chrome/sync.dtd)
   locale/@AB_CD@/browser/sync.properties          (%chrome/sync.properties)
   locale/@AB_CD@/browser/prompt.dtd               (%chrome/prompt.dtd)
   locale/@AB_CD@/browser/feedback.dtd             (%chrome/feedback.dtd)
new file mode 100644
--- /dev/null
+++ b/mobile/modules/LocaleRepository.jsm
@@ -0,0 +1,334 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Mobile Browser.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mark Finkle <mfinkle@mozilla.com>
+ *  Wes Johnston <wjohnston@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+let EXPORTED_SYMBOLS = ["LocaleRepository"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+var gServiceURL = Services.prefs.getCharPref("extensions.getLocales.get.url");
+
+// A map between XML keys to LocaleSearchResult keys for string values
+// that require no extra parsing from XML
+const STRING_KEY_MAP = {
+  name:               "name",
+  target_locale:      "targetLocale",
+  version:            "version",
+  icon:               "iconURL",
+  homepage:           "homepageURL",
+  support:            "supportURL"
+};
+
+var LocaleRepository = {
+  loggingEnabled: false,
+
+  log: function(aMessage) {
+    if (this.loggingEnabled)
+      dump(aMessage + "\n");
+  },
+
+  _getUniqueDescendant: function _getUniqueDescendant(aElement, aTagName) {
+    let elementsList = aElement.getElementsByTagName(aTagName);
+    return (elementsList.length == 1) ? elementsList[0] : null;
+  },
+  
+  _getTextContent: function _getTextContent(aElement) {
+    let textContent = aElement.textContent.trim();
+    return (textContent.length > 0) ? textContent : null;
+  },
+  
+  _getDescendantTextContent: function _getDescendantTextContent(aElement, aTagName) {
+    let descendant = this._getUniqueDescendant(aElement, aTagName);
+    return (descendant != null) ? this._getTextContent(descendant) : null;
+  },
+
+  getLocales: function getLocales(aCallback) {
+    if (!gServiceURL) {
+      aCallback([]);
+      return;
+    }
+    let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
+    request.mozBackgroundRequest = true;
+    request.open("GET", gServiceURL, true);
+    request.overrideMimeType("text/xml");
+  
+    let self = this;
+    request.onreadystatechange = function () {
+      if (request.readyState == 4) {
+        if (request.status == 200) {
+          self.log("---- got response")
+          let documentElement = request.responseXML.documentElement;
+          let elements = documentElement.getElementsByTagName("addon");
+          let totalResults = elements.length;
+          let parsedTotalResults = parseInt(documentElement.getAttribute("total_results"));
+          if (parsedTotalResults >= totalResults)
+            totalResults = parsedTotalResults;
+
+          // TODO: Create a real Skip object from installed locales
+          self._parseLocales(elements, totalResults, { ids: [], sourceURIs: [] }, aCallback);
+        } else {
+          Cu.reportError("Locale Repository: Error getting locale from AMO [" + request.status + "]");
+        }
+      }
+    };
+  
+    request.send(null);
+  },
+
+  _parseLocale: function _parseLocale(aElement, aSkip) {
+    let skipIDs = (aSkip && aSkip.ids) ? aSkip.ids : [];
+    let skipSourceURIs = (aSkip && aSkip.sourceURIs) ? aSkip.sourceURIs : [];
+  
+    let guid = this._getDescendantTextContent(aElement, "guid");
+    if (guid == null || skipIDs.indexOf(guid) != -1)
+      return null;
+  
+    let addon = new LocaleSearchResult(guid);
+    let result = {
+      addon: addon,
+      xpiURL: null,
+      xpiHash: null
+    };
+  
+    let self = this;
+    for (let node = aElement.firstChild; node; node = node.nextSibling) {
+      if (!(node instanceof Ci.nsIDOMElement))
+        continue;
+  
+      let localName = node.localName;
+  
+      // Handle case where the wanted string value is located in text content
+      // but only if the content is not empty
+      if (localName in STRING_KEY_MAP) {
+        addon[STRING_KEY_MAP[localName]] = this._getTextContent(node) || addon[STRING_KEY_MAP[localName]];
+        continue;
+      }
+  
+      // Handle cases that aren't as simple as grabbing the text content
+      switch (localName) {
+        case "type":
+          // Map AMO's type id to corresponding string
+          let id = parseInt(node.getAttribute("id"));
+          switch (id) {
+            case 5:
+              addon.type = "language";
+              break;
+            default:
+              WARN("Unknown type id when parsing addon: " + id);
+          }
+          break;
+        case "authors":
+          let authorNodes = node.getElementsByTagName("author");
+          Array.forEach(authorNodes, function(aAuthorNode) {
+            let name = self._getDescendantTextContent(aAuthorNode, "name");
+            if (name == null)
+              name = self._getTextContent(aAuthorNode);
+            let link = self._getDescendantTextContent(aAuthorNode, "link");
+            if (name == null && link == null)
+              return;
+  
+            let author = { name: name, link: link };
+            if (addon.creator == null) {
+              addon.creator = author;
+            } else {
+              if (addon.developers == null)
+                addon.developers = [];
+  
+              addon.developers.push(author);
+            }
+          });
+          break;
+        case "status":
+          let repositoryStatus = parseInt(node.getAttribute("id"));
+          if (!isNaN(repositoryStatus))
+            addon.repositoryStatus = repositoryStatus;
+          break;
+        case "all_compatible_os":
+          let nodes = node.getElementsByTagName("os");
+          addon.isPlatformCompatible = Array.some(nodes, function(aNode) {
+            let text = aNode.textContent.toLowerCase().trim();
+            return text == "all" || text == Services.appinfo.OS.toLowerCase();
+          });
+          break;
+        case "install":
+          // No os attribute means the xpi is compatible with any os
+          if (node.hasAttribute("os") && node.getAttribute("os")) {
+            let os = node.getAttribute("os").trim().toLowerCase();
+            // If the os is not ALL and not the current OS then ignore this xpi
+            if (os != "all" && os != Services.appinfo.OS.toLowerCase())
+              break;
+          }
+  
+          let xpiURL = this._getTextContent(node);
+          if (xpiURL == null)
+            break;
+  
+          if (skipSourceURIs.indexOf(xpiURL) != -1)
+            return null;
+  
+          result.xpiURL = xpiURL;
+          addon.sourceURI = NetUtil.newURI(xpiURL);
+  
+          let size = parseInt(node.getAttribute("size"));
+          addon.size = (size >= 0) ? size : null;
+  
+          let xpiHash = node.getAttribute("hash");
+          if (xpiHash != null)
+            xpiHash = xpiHash.trim();
+          result.xpiHash = xpiHash ? xpiHash : null;
+          break;
+      }
+    }
+  
+    return result;
+  },
+
+  _parseLocales: function _parseLocales(aElements, aTotalResults, aSkip, aCallback) {
+    let self = this;
+    let results = [];
+    for (let i = 0; i < aElements.length; i++) {
+      let element = aElements[i];
+
+      // Ignore add-ons not compatible with this Application
+      let tags = this._getUniqueDescendant(element, "compatible_applications");
+      if (tags == null)
+        continue;
+
+      let applications = tags.getElementsByTagName("appID");
+      let compatible = Array.some(applications, function(aAppNode) {
+        if (self._getTextContent(aAppNode) != Services.appinfo.ID)
+          return false;
+
+        let parent = aAppNode.parentNode;
+        let minVersion = self._getDescendantTextContent(parent, "min_version");
+        let maxVersion = self._getDescendantTextContent(parent, "max_version");
+        if (minVersion == null || maxVersion == null)
+          return false;
+
+        let currentVersion = Services.appinfo.version;
+        return (Services.vc.compare(minVersion, currentVersion) <= 0 && Services.vc.compare(currentVersion, maxVersion) <= 0);
+      });
+
+      if (!compatible)
+        continue;
+
+      // Add-on meets all requirements, so parse out data
+      let result = this._parseLocale(element, aSkip);
+      if (result == null)
+        continue;
+
+      // Ignore add-on missing a required attribute
+      let requiredAttributes = ["id", "name", "version", "type", "targetLocale"];
+      if (requiredAttributes.some(function(aAttribute) !result.addon[aAttribute]))
+        continue;
+
+      // Add only if the add-on is compatible with the platform
+      if (!result.addon.isPlatformCompatible)
+        continue;
+
+      // Add only if there was an xpi compatible with this OS
+      if (!result.xpiURL)
+        continue;
+
+      results.push(result);
+
+      // Ignore this add-on from now on by adding it to the skip array
+      aSkip.ids.push(result.addon.id);
+    }
+
+    // Immediately report success if no AddonInstall instances to create
+    let pendingResults = results.length;
+    if (pendingResults == 0) {
+      aCallback([]);
+      return;
+    }
+
+    // Create an AddonInstall for each result
+    let self = this;
+    results.forEach(function(aResult) {
+      let addon = aResult.addon;
+      let callback = function(aInstall) {
+        aResult.addon.install = aInstall;
+        pendingResults--;
+        if (pendingResults == 0)
+          aCallback(results);
+      }
+
+      if (aResult.xpiURL) {
+        AddonManager.getInstallForURL(aResult.xpiURL, callback,
+                                      "application/x-xpinstall", aResult.xpiHash,
+                                      addon.name, addon.iconURL, addon.version);
+      } else {
+        callback(null);
+      }
+    });
+  }
+};
+
+function LocaleSearchResult(aId) {
+  this.id = aId;
+}
+
+LocaleSearchResult.prototype = {
+  id: null,
+  type: null,
+  targetLocale: null,
+  name: null,
+  addon: null,
+  version: null,
+  iconURL: null,
+  install: null,
+  sourceURI: null,
+  repositoryStatus: null,
+  size: null,
+  updateDate: null,
+  isCompatible: true,
+  isPlatformCompatible: true,
+  providesUpdatesSecurely: true,
+  blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
+  appDisabled: false,
+  userDisabled: false,
+  scope: AddonManager.SCOPE_PROFILE,
+  isActive: true,
+  pendingOperations: AddonManager.PENDING_NONE,
+  permissions: 0
+};
--- a/mobile/modules/Makefile.in
+++ b/mobile/modules/Makefile.in
@@ -38,16 +38,17 @@
 DEPTH      = ../..
 topsrcdir  = @top_srcdir@
 srcdir     = @srcdir@
 VPATH      = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
+  LocaleRepository.jsm \
   linuxTypes.jsm \
   video.jsm \
   $(NULL)
 
 EXTRA_PP_JS_MODULES = \
   contacts.jsm \
   $(NULL)
 
--- a/mobile/themes/core/aboutHome.css
+++ b/mobile/themes/core/aboutHome.css
@@ -46,16 +46,18 @@ html {
   max-width: 600px;
   margin: 0 auto;
 }
 
 #logo {
   float: right;
   margin-top: -24px;
   margin-right: -34px;
+  /* Trick to ensure this is shown above the opacity transform happening to content*/
+  -moz-transform: translate(0,0);
 }
 
 #header {
   font-weight: bold;
   color: white;
   background: transparent -moz-linear-gradient(top, rgba(57,89,117,1) 0%, rgba(30,66,98,1) 50%, rgba(24,54,82,1) 90%);
   border-radius: 6px;
   padding: 12px;
@@ -209,16 +211,26 @@ body[dir="rtl"] {
   margin: 0 auto;
 }
 
 .no-items {
   text-align: center;
   -moz-margin-start: 0 !important;
 }
 
+#content {
+  opacity: 1;
+  -moz-transition: opacity 500ms;
+}
+
+#content[hidden] {
+  display: block;
+  opacity: 0;
+}
+
 #footer-wrapper {
   font-size: 18px;
   margin-top: 24px;
   text-align: center;
 }
 
 #feedback {
   display: inline-block;
--- a/mobile/themes/core/browser.css
+++ b/mobile/themes/core/browser.css
@@ -1448,20 +1448,16 @@ setting {
   font-size: @font_xlarge@ !important;
   padding: 0.2em 0.4em;
   -moz-padding-end: 0.2em;
   letter-spacing: 0.2em;
   text-align: center;
   min-width: 5.5em;
 }
 
-.syncsetup-link {
-  text-decoration: underline;
-}
-
 .syncsetup-label {
   color: #fff;
 }
 
 #syncsetup-customserver {
   -moz-margin-start: @margin_xnormal@;
 }
 
deleted file mode 100644
--- a/mobile/themes/core/firstRun.css
+++ /dev/null
@@ -1,253 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Mobile Browser.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2008
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Gavin Sharp <gavin@gavinsharp.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-html {
-  font-family: "Nokia Sans", Tahoma, sans-serif !important;
-  background: #fff;
-}
-
-body {
-  font-size: 16px;
-  color: #444;
-  border-top: 4px solid #1e4262;
-  margin: 0;
-}
-
-h1 {
-  font-size: 19px;
-  margin: 0 0 10px;
-}
-
-a {
-  color: #0489b7;
-  text-decoration: none;
-}
-
-img {
-  border: none;
-}
-
-#wrapper {
-    width: 96%;
-    max-width: 600px;
-    margin: 0 auto;
-}
-
-#header {
-    text-align: right;
-    height: 30px;
-    padding: 0;
-    margin: 0 8px 20px;
-}
-
-#header a {
-  display: inline-block;
-  padding: 4px 6px 2px 6px;
-  background: #1e4262;
- -moz-border-radius-bottomright: 4px;
- -moz-border-radius-bottomleft: 4px;
-}
-
-#head, #content, #footer {
-  margin: 0 8px;
-  clear: both;
-}
-
-#content {
-  opacity: 1;
-  -moz-transition: opacity 1s linear;
-}
-
-#content[hidden] {
-  display: block;
-  opacity: 0;
-}
-
-#head p {
-  font-size: xx-large;
-}
-
-#footer {
-  border-top: 2px solid #e7e7e7;
-  padding: 10px;
-  color: #999;
-  margin-top: 10px;
-}
-
-#footer ul {
-  margin: 0;
-  padding: 0;
-  float: left;
-}
-
-#footer .nav li {
-  list-style: none;
-  float: left;
-  margin: 0 1em 0 0;
-  padding-right: 1em;
-  border-right: 1px solid #e7e7e7;
-}
-
-#footer .nav li:last-child {
-  border: 0;
-}
-
-#footer #follow {
-  float: right;
-  padding-bottom: 10px
-}
-
-#footer #follow p {
-  display: inline;
-}
-
-#footer #follow ul {
-  display: inline;
-  float: none;
-}
-
-#footer #follow ul li {
-  display: inline;
-  list-style-type: none;
-  margin-left: 10px;
-}
-
-#footer #follow img {
-  vertical-align: top;
-}
-
-#firstrun #header {
-  margin-bottom: 0;
-}
-
-#firstrun #head h1 {
-  float: left;
-  margin: 0;
-}
-
-#firstrun #head p {
-  margin: 0 0 10px 245px;
-  padding-top: 18px;
-}
-
-#firstrun ul#recommended {
-  margin: 0;
-  padding: 0;
-}
-
-#firstrun ul#recommended li {
-  float: left;
-  width: 32%;
-  list-style-type: none;
-  margin-right: 2%;
-  text-align: center;
-  margin-bottom: 20px;
-}
-
-#firstrun ul#recommended li:last-child {
-  margin-right: 0;
-}
-
-#firstrun ul#recommended li a {
-  display: block;
-  padding: 20px 0;
-  -moz-border-radius: 10px;
-  background: #d8ecf1;
-}
-
-#firstrun ul#recommended li a:active {
-  text-decoration: none;
-  background: #b5d7e0;
-  outline: none;
-}
-
-#firstrun ul#recommended li b {
-  display: block;
-  padding: 90px 10px 0 10px;
-  background-repeat: no-repeat;
-  background-image: url("chrome://browser/content/firstrun/features.png");
-}
-
-#firstrun ul#recommended li span {
-  display: block;
-  padding: 0 10px;
-  min-height: 150px;
-}
-
-@media (max-width: 499px) {
-  #firstrun ul#recommended li span {
-    min-height: 180px;
-  }
-}
-
-#firstrun ul#recommended li#sync b {
-  background-position: 50% 0;
-}
-
-#firstrun ul#recommended li#addons b {
-  background-position: 50% -150px;
-}
-
-#firstrun ul#recommended li#home b {
-  background-position: 50% -280px;
-}
-
-#firstrun #features {
-  clear: both;
-  text-align: center;
-}
-
-#firstrun #features a {
-  display: inline-block;
-  font-size: 16px;
-}
-
-#firstrun #features a span {
-  vertical-align: 35%;
-}
-
-#firstrun #features a #nav-arrow {
-  -moz-margin-start: 5px; 
-}
-
-#firstrun[dir="rtl"] #features a #nav-arrow {
-  -moz-transform: scaleX(-1);
-}
-
-#firstrun #demo {
-  text-align: center;
-}
--- a/mobile/themes/core/gingerbread/browser.css
+++ b/mobile/themes/core/gingerbread/browser.css
@@ -1425,20 +1425,16 @@ setting {
   font-size: @font_xlarge@ !important;
   padding: 0.2em 0.4em;
   -moz-padding-end: 0.2em;
   letter-spacing: 0.2em;
   text-align: center;
   min-width: 5.5em;
 }
 
-.syncsetup-link {
-  text-decoration: underline;
-}
-
 .syncsetup-label {
   color: @color_text_default@;
 }
 
 #syncsetup-customserver {
   -moz-margin-start: @margin_xnormal@;
 }
 
--- a/mobile/themes/core/gingerbread/defines.inc
+++ b/mobile/themes/core/gingerbread/defines.inc
@@ -11,16 +11,17 @@
 %define color_background_header #292929
 %define color_text_header #999999
 %define color_background_scroller #9a9a9a
 %define color_background_inverse #fff
 %define color_text_inverse #000
 %define color_text_button #000
 %define color_text_disabled #808080
 %define color_text_placeholder #808080
+%define color_text_as_link #febc2b
 
 %define color_background_highlight #febc2b
 %define color_background_highlight_overlay rgba(254, 188, 43, 0.8)
 %define color_text_highlight #000
 
 %define color_subtext_default lightgray
 %define color_subtext_inverse #414141
 
new file mode 100644
--- /dev/null
+++ b/mobile/themes/core/gingerbread/localePicker.css
@@ -0,0 +1,90 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Mobile Browser.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Wes Johnston <wjohnston@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+%filter substitution
+%include defines.inc
+
+.pane {
+  -moz-box-pack: center;
+  -moz-box-align: center;
+  -moz-box-flex: 1;
+}
+
+#main-page {
+  background-image: url("chrome://branding/content/logo.png");
+  background-repeat: no-repeat;
+  background-position: center center;
+}
+
+#picker-title {
+  font-weight: bold;
+  font-size: @font_normal@;
+}
+
+.link {
+  padding: @padding_xlarge@ 0px;
+  font-weight: bold;
+}
+
+richlistbox {
+  padding: 0px;
+  margin: 0px;
+  background-color: transparent;
+}
+
+#installer-page {
+  background-color: black;
+  color: white;
+}
+
+richlistitem {
+  height: @touch_row@;
+  font-size: @font_normal@;
+  border-bottom: @border_width_tiny@ solid gray;
+  padding: 0px @padding_normal@;
+  -moz-box-align: center;
+}
+
+richlistitem .checkbox {
+  width: 46px;
+  height: 46px;
+  list-style-image: url("chrome://browser/skin/images/radio-unselected-hdpi.png");
+}
+
+richlistitem[selected] .checkbox {
+  list-style-image: url("chrome://browser/skin/images/radio-selected-hdpi.png");
+}
--- a/mobile/themes/core/gingerbread/platform.css
+++ b/mobile/themes/core/gingerbread/platform.css
@@ -40,16 +40,18 @@
 
 %filter substitution
 %include defines.inc
 
 /* general stuff ------------------------------------------------------------ */
 :root {
   font-family: "Nokia Sans", Tahoma, sans-serif !important;
   font-size: @font_normal@ !important;
+  background-color: @color_background_default@; /* force */
+  color: @color_text_default@; /* force */
 }
 
 ::-moz-selection {
   background-color: @color_background_highlight@;
   color: @color_text_highlight@;
 }
 
 menu,
@@ -91,16 +93,21 @@ textbox.search-bar {
   background: url("chrome://browser/skin/images/textbox-bg.png") top left repeat-x;
   background-size: 100% 100%; 
 }
 
 textbox[disabled="true"] {
   background-color: lightgray;
 }
 
+.link {
+  color: @color_text_as_link@;
+  text-decoration: underline;
+}
+
 /* sidebars spacer --------------------------------------------------------- */
 .sidebar-spacer {
   background-color: #767973;
 }
 
 /* prompt dialogs ---------------------------------------------------------- */
 .context-block,
 .modal-block,
--- a/mobile/themes/core/jar.mn
+++ b/mobile/themes/core/jar.mn
@@ -7,22 +7,22 @@ chrome.jar:
 # and honeycomb sections at the bottom of this file
   skin/aboutCertError.css                   (aboutCertError.css)
   skin/aboutPage.css                        (aboutPage.css)
   skin/about.css                            (about.css)
   skin/aboutHome.css                        (aboutHome.css)
 * skin/browser.css                          (browser.css)
 * skin/content.css                          (content.css)
   skin/config.css                           (config.css)
-  skin/firstRun.css                         (firstRun.css)
 * skin/forms.css                            (forms.css)
   skin/header.css                           (header.css)
 * skin/notification.css                     (notification.css)
 * skin/platform.css                         (platform.css)
   skin/touchcontrols.css                    (touchcontrols.css)
+* skin/localePicker.css                     (localePicker.css)
 % override chrome://global/skin/about.css chrome://browser/skin/about.css
 % override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css
 
   skin/images/appmenu-addons-hdpi.png       (images/appmenu-addons-hdpi.png)
   skin/images/appmenu-active-hdpi.png       (images/appmenu-active-hdpi.png)
   skin/images/appmenu-downloads-hdpi.png    (images/appmenu-downloads-hdpi.png)
   skin/images/appmenu-findinpage-hdpi.png   (images/appmenu-findinpage-hdpi.png)
   skin/images/appmenu-more-hdpi.png         (images/appmenu-more-hdpi.png)
@@ -127,22 +127,22 @@ chrome.jar:
 % skin browser classic/1.0 %skin/gingerbread/ os=Android osversion=2.3 osversion=2.3.3 osversion=2.3.4
   skin/gingerbread/aboutCertError.css                   (aboutCertError.css)
   skin/gingerbread/aboutPage.css                        (aboutPage.css)
   skin/gingerbread/about.css                            (about.css)
   skin/gingerbread/aboutHome.css                        (aboutHome.css)
 * skin/gingerbread/browser.css                          (gingerbread/browser.css)
 * skin/gingerbread/content.css                          (gingerbread/content.css)
   skin/gingerbread/config.css                           (config.css)
-  skin/gingerbread/firstRun.css                         (firstRun.css)
 * skin/gingerbread/forms.css                            (gingerbread/forms.css)
   skin/gingerbread/header.css                           (header.css)
 * skin/gingerbread/notification.css                     (notification.css)
 * skin/gingerbread/platform.css                         (gingerbread/platform.css)
   skin/gingerbread/touchcontrols.css                    (touchcontrols.css)
+* skin/gingerbread/localePicker.css                     (gingerbread/localePicker.css)
 % override chrome://global/skin/about.css chrome://browser/skin/about.css
 % override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css
 
   skin/gingerbread/images/appmenu-addons-hdpi.png       (gingerbread/images/appmenu-addons-hdpi.png)
   skin/gingerbread/images/appmenu-active-hdpi.png       (gingerbread/images/appmenu-active-hdpi.png)
   skin/gingerbread/images/appmenu-downloads-hdpi.png    (gingerbread/images/appmenu-downloads-hdpi.png)
   skin/gingerbread/images/appmenu-findinpage-hdpi.png   (gingerbread/images/appmenu-findinpage-hdpi.png)
   skin/gingerbread/images/appmenu-more-hdpi.png         (gingerbread/images/appmenu-more-hdpi.png)
@@ -244,22 +244,22 @@ chrome.jar:
 % skin browser classic/1.0 %skin/honeycomb/ os=Android osversion>=3.0
   skin/honeycomb/aboutCertError.css                   (aboutCertError.css)
   skin/honeycomb/aboutPage.css                        (aboutPage.css)
   skin/honeycomb/about.css                            (about.css)
   skin/honeycomb/aboutHome.css                        (aboutHome.css)
 * skin/honeycomb/browser.css                          (browser.css)
 * skin/honeycomb/content.css                          (content.css)
   skin/honeycomb/config.css                           (config.css)
-  skin/honeycomb/firstRun.css                         (firstRun.css)
 * skin/honeycomb/forms.css                            (forms.css)
   skin/honeycomb/header.css                           (header.css)
 * skin/honeycomb/notification.css                     (notification.css)
 * skin/honeycomb/platform.css                         (platform.css)
   skin/honeycomb/touchcontrols.css                    (touchcontrols.css)
+* skin/honeycomb/localePicker.css                     (localePicker.css)
 % override chrome://global/skin/about.css chrome://browser/skin/about.css
 % override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css
 
   skin/honeycomb/images/appmenu-addons-hdpi.png       (honeycomb/images/appmenu-addons-hdpi.png)
   skin/honeycomb/images/appmenu-active-hdpi.png       (honeycomb/images/appmenu-active-hdpi.png)
   skin/honeycomb/images/appmenu-downloads-hdpi.png    (honeycomb/images/appmenu-downloads-hdpi.png)
   skin/honeycomb/images/appmenu-findinpage-hdpi.png   (honeycomb/images/appmenu-findinpage-hdpi.png)
   skin/honeycomb/images/appmenu-more-hdpi.png         (honeycomb/images/appmenu-more-hdpi.png)
new file mode 100644
--- /dev/null
+++ b/mobile/themes/core/localePicker.css
@@ -0,0 +1,90 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Mobile Browser.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Wes Johnston <wjohnston@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+%filter substitution
+%include defines.inc
+
+.pane {
+  -moz-box-pack: center;
+  -moz-box-align: center;
+  -moz-box-flex: 1;
+}
+
+#main-page {
+  background-image: url("chrome://branding/content/logo.png");
+  background-repeat: no-repeat;
+  background-position: center center;
+}
+
+#picker-title {
+  font-weight: bold;
+  font-size: @font_normal@;
+}
+
+.link {
+  padding: @padding_xlarge@ 0px;
+  font-weight: bold;
+}
+
+richlistbox {
+  padding: 0px;
+  margin: 0px;
+  background-color: transparent;
+}
+
+#installer-page {
+  background-color: black;
+  color: white;
+}
+
+richlistitem {
+  height: @touch_row@;
+  font-size: @font_normal@;
+  border-bottom: @border_width_tiny@ solid gray;
+  padding: 0px @padding_normal@;
+  -moz-box-align: center;
+}
+
+richlistitem .checkbox {
+  width: 30px;
+  height: 30px;
+  list-style-image: url("chrome://browser/skin/images/check-unselected-30.png");
+}
+
+richlistitem[selected] .checkbox {
+  list-style-image: url("chrome://browser/skin/images/check-selected-30.png");
+}
--- a/mobile/themes/core/platform.css
+++ b/mobile/themes/core/platform.css
@@ -40,16 +40,18 @@
 
 %filter substitution
 %include defines.inc
 
 /* general stuff ------------------------------------------------------------ */
 :root {
   font-family: "Nokia Sans", Tahoma, sans-serif !important;
   font-size: @font_normal@ !important;
+  background-color: white; /* force */
+  color: black; /* force */
 }
 
 ::-moz-selection {
   background-color: #8db8d8;
   color: black;
 }
 
 menu,
@@ -93,16 +95,21 @@ textbox.search-bar {
   background: url("chrome://browser/skin/images/textbox-bg.png") top left repeat-x;
   background-size: 100% 100%; 
 }
 
 textbox[disabled="true"] {
   background-color: lightgray;
 }
 
+.link {
+  color: blue;
+  text-decoration: underline;
+}
+
 /* sidebars spacer --------------------------------------------------------- */
 .sidebar-spacer {
   background-color: #767973;
 }
 
 /* prompt dialogs ---------------------------------------------------------- */
 .context-block,
 .modal-block,
@@ -576,16 +583,20 @@ dialog {
 
 .prompt-message {
   text-align: center;
   -moz-box-pack: center;
   font-size: @font_snormal@;
   margin: @padding_normal@;
 }
 
+.prompt-message .link {
+  color: white;
+}
+
 .prompt-title {
   text-align: center;
   font-size: @font_xnormal@;
   -moz-box-align: center;
   -moz-box-pack: center;
   padding-top: @padding_xnormal@;
 }