Merge m-c --> cedar
authorChris Jones <jones.chris.g@gmail.com>
Wed, 08 Sep 2010 17:25:30 -0500
changeset 54089 b10c1169cc964aa672356d29b2b5d1b78711242a
parent 54088 fd22a96116a1b0cc386afe016f0afd5b78cee910 (current diff)
parent 52195 ed4a1318e3e9438783cefb2dc7a669f378775c3f (diff)
child 54090 44273fae4f96e0d28eb1cd3a8ad0f1f656212805
push idunknown
push userunknown
push dateunknown
milestone2.0b6pre
Merge m-c --> cedar
browser/base/content/browser.xul
browser/themes/gnomestripe/browser/browser.css
browser/themes/gnomestripe/browser/sync-merge.png
browser/themes/gnomestripe/browser/sync-wipeClient.png
browser/themes/gnomestripe/browser/sync-wipeServer.png
browser/themes/pinstripe/browser/sync-merge.png
browser/themes/pinstripe/browser/sync-wipeClient.png
browser/themes/pinstripe/browser/sync-wipeServer.png
browser/themes/winstripe/browser/sync-merge.png
browser/themes/winstripe/browser/sync-wipeClient.png
browser/themes/winstripe/browser/sync-wipeServer.png
content/canvas/src/nsCanvasRenderingContext2D.cpp
content/events/src/nsEventStateManager.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/opengl/ThebesLayerOGL.cpp
gfx/thebes/gfxASurface.cpp
gfx/thebes/gfxXlibSurface.cpp
ipc/ipdl/ipdl/lower.py
layout/base/nsDisplayList.cpp
layout/base/nsDocumentViewer.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsGfxScrollFrame.cpp
media/libvpx/safeseh.asm
media/libvpx/yasm2masm-as.sh
media/libvpx/yasm2masm.py
widget/src/windows/nsWindow.cpp
widget/src/xpwidgets/nsBaseWidget.cpp
--- a/browser/base/content/aboutHome.css
+++ b/browser/base/content/aboutHome.css
@@ -62,24 +62,26 @@ html {
   color: #688196;
   margin-top: 18px;
   margin-bottom: 6px;
 }
 #brandStart > span {
   color: white;
 }
 
-#brandLogo {
+#brandStart:before {
+  content: url("chrome://branding/content/icon128.png");
   position: absolute;
   top: 0;
 }
-body[dir="ltr"] #brandLogo {
+
+body[dir="ltr"] #brandStart:before {
   right: 0;
 }
-body[dir="rtl"] #brandLogo {
+body[dir="rtl"] #brandStart:before {
   left: -15px;
 }
 
 #searchContainer {
   border: 1px solid ThreeDShadow;
   -moz-border-radius: 5.6px;
   padding: 3em;
 }
--- a/browser/base/content/aboutHome.js
+++ b/browser/base/content/aboutHome.js
@@ -90,18 +90,21 @@ function setupSearchEngine() {
       gSearchEngine[prop] = searchEngineInfo[prop];
   }
 
   // Enqueue additional params if required by the engine definition.
   if (gSearchEngine.params)
     gSearchEngine.searchUrl += "&" + gSearchEngine.params;
 
   // Add search engine logo.
-  if (gSearchEngine.image)
-    document.getElementById("searchEngineLogo").src = gSearchEngine.image;
+  if (gSearchEngine.image) {
+    let logoElt = document.getElementById("searchEngineLogo");
+    logoElt.src = gSearchEngine.image;
+    logoElt.alt = gSearchEngine.name;
+  }
 
   if (gSearchEngine.links) {
     // Add search engine links.
     if (gSearchEngine.links.advanced) {
       let advancedLink = document.getElementById("searchEngineAdvancedLink");
       advancedLink.setAttribute("href", gSearchEngine.links.advanced);
       advancedLink.hidden = false;
     }
--- a/browser/base/content/aboutHome.xhtml
+++ b/browser/base/content/aboutHome.xhtml
@@ -60,23 +60,21 @@
 
     <script type="text/javascript;version=1.8"
             src="chrome://browser/content/aboutHome.js"/>
   </head>
 
   <body dir="&locale.dir;" onload="onLoad(event)">
     <div id="pageContainer">
       <div id="topSection">
-        <img id="brandLogo" src="chrome://branding/content/icon128.png"
-             title="&abouthome.brandLogo.title;"/>
         <div id="brandStart">
           &abouthome.brandStart;
         </div>
         <div id ="searchContainer">
-          <img id="searchEngineLogo" title="&abouthome.searchEngineLogo.title;"/>
+          <img id="searchEngineLogo"/>
           <form name="searchForm" onsubmit="onSearchSubmit(event)">
             <input type="text" name="searchText" value="" id="searchText" maxLength="256"/>
             <br/>
             <input type="submit" value="&abouthome.searchEngineButton.label;"/>
             <span id="searchEngineLinks">
               <a hidden="true" id="searchEngineAdvancedLink">&abouthome.searchEngineLinks.advanced;</a>
               <a hidden="true" id="searchEngineAdvancedPreferences">&abouthome.searchEngineLinks.preferences;</a>
             </span>
--- a/browser/base/content/aboutSyncTabs.js
+++ b/browser/base/content/aboutSyncTabs.js
@@ -34,17 +34,17 @@
  * 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 ***** */
 
 
 const Cu = Components.utils;
 
-Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource:///modules/PlacesUIUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 let RemoteTabViewer = {
   _tabsList: null,
 
   init: function () {
     Services.obs.addObserver(this, "weave:service:login:finish", false);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-charsetmenu.inc
@@ -0,0 +1,153 @@
+# ***** 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.org code.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# 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
+
+#expand <menu id="__ID_PREFIX__charsetMenu"
+    label="&charsetMenu.label;"
+    accesskey="&charsetMenu.accesskey;"
+    datasources="rdf:charset-menu"
+    ref="NC:BrowserCharsetMenuRoot"
+    oncommand="MultiplexHandler(event)"
+    onpopupshowing="CreateMenu('browser');UpdateMenus(event)"
+    onpopupshown="CreateMenu('more-menu');"
+    observes="isImage">
+  <template>
+    <rule rdf:type="http://home.netscape.com/NC-rdf#BookmarkSeparator">
+      <menupopup>
+      <menuseparator uri="..." />
+      </menupopup>
+    </rule>
+    <rule>
+      <menupopup>
+      <menuitem type="radio" name="charsetGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+      </menupopup>
+    </rule>
+  </template>
+
+  <menupopup>
+  <menu label="&charsetMenuAutodet.label;" accesskey="&charsetMenuAutodet.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserAutodetMenuRoot">
+    <template>
+      <rule rdf:type="http://home.netscape.com/NC-rdf#CharsetDetector">
+        <menupopup>
+        <menuitem type="radio" name="detectorGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+        </menupopup>
+      </rule>
+    </template>
+    <menupopup>
+    </menupopup>
+  </menu>
+  <menu label="&charsetMenuMore.label;" accesskey="&charsetMenuMore.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMoreCharsetMenuRoot">
+    <template>
+      <rule>
+        <menupopup>
+        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+        </menupopup>
+      </rule>
+    </template>
+    <menupopup>
+      <menu label="&charsetMenuMore1.label;" accesskey="&charsetMenuMore1.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore1CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore2.label;" accesskey="&charsetMenuMore2.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore2CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore3.label;" accesskey="&charsetMenuMore3.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore3CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore4.label;" accesskey="&charsetMenuMore4.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore4CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore5.label;" accesskey="&charsetMenuMore5.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore5CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuUnicode.label;" accesskey="&charsetMenuUnicode.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserUnicodeCharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menuseparator />
+    </menupopup>
+  </menu>
+  <menuitem name="charsetCustomize" accesskey="&charsetCustomize.accesskey;" label="&charsetCustomize.label;" oncommand="window.openDialog('chrome://global/content/customizeCharset.xul','PrefWindow', 'chrome,modal=yes,resizable=yes', 'browser')"/>
+  </menupopup>
+</menu>
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -312,131 +312,17 @@
                     <menuitem id="menu_pageStylePersistentOnly"
                               label="&pageStylePersistentOnly.label;"
                               accesskey="&pageStylePersistentOnly.accesskey;"
                               type="radio"
                               checked="true"/>
                     <menuseparator/>
                   </menupopup>
                 </menu>
-                <menu id="charsetMenu"
-                      label="&charsetMenu.label;"
-                      accesskey="&charsetMenu.accesskey;"
-                      datasources="rdf:charset-menu"
-                      ref="NC:BrowserCharsetMenuRoot"
-                      oncommand="MultiplexHandler(event)"
-                      onpopupshowing="CreateMenu('browser');UpdateMenus(event)"
-                      onpopupshown="CreateMenu('more-menu');"
-                      observes="isImage">
-                    <template>
-                        <rule rdf:type="http://home.netscape.com/NC-rdf#BookmarkSeparator">
-                            <menupopup>
-                            <menuseparator uri="..." />
-                            </menupopup>
-                        </rule>
-                        <rule>
-                            <menupopup>
-                            <menuitem type="radio" name="charsetGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                            </menupopup>
-                        </rule>
-                    </template>
-
-                    <menupopup>
-                    <menu label="&charsetMenuAutodet.label;" accesskey="&charsetMenuAutodet.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserAutodetMenuRoot">
-                        <template>
-                            <rule rdf:type="http://home.netscape.com/NC-rdf#CharsetDetector">
-                                <menupopup>
-                                <menuitem type="radio" name="detectorGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                                </menupopup>
-                            </rule>
-                        </template>
-                        <menupopup>
-                        </menupopup>
-                    </menu>
-                    <menu label="&charsetMenuMore.label;" accesskey="&charsetMenuMore.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMoreCharsetMenuRoot">
-                        <template>
-                            <rule>
-                                <menupopup>
-                                <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                                </menupopup>
-                            </rule>
-                        </template>
-                        <menupopup>
-                            <menu label="&charsetMenuMore1.label;" accesskey="&charsetMenuMore1.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore1CharsetMenuRoot">
-                                <template>
-                                    <rule>
-                                        <menupopup>
-                                        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                                        </menupopup>
-                                    </rule>
-                                </template>
-                                <menupopup>
-                                </menupopup>
-                            </menu>
-                            <menu label="&charsetMenuMore2.label;" accesskey="&charsetMenuMore2.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore2CharsetMenuRoot">
-                                <template>
-                                    <rule>
-                                        <menupopup>
-                                        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                                        </menupopup>
-                                    </rule>
-                                </template>
-                                <menupopup>
-                                </menupopup>
-                            </menu>
-                            <menu label="&charsetMenuMore3.label;" accesskey="&charsetMenuMore3.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore3CharsetMenuRoot">
-                                <template>
-                                    <rule>
-                                        <menupopup>
-                                        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                                        </menupopup>
-                                    </rule>
-                                </template>
-                                <menupopup>
-                                </menupopup>
-                            </menu>
-                            <menu label="&charsetMenuMore4.label;" accesskey="&charsetMenuMore4.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore4CharsetMenuRoot">
-                                <template>
-                                    <rule>
-                                        <menupopup>
-                                        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                                        </menupopup>
-                                    </rule>
-                                </template>
-                                <menupopup>
-                                </menupopup>
-                            </menu>
-                            <menu label="&charsetMenuMore5.label;" accesskey="&charsetMenuMore5.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserMore5CharsetMenuRoot">
-                                <template>
-                                    <rule>
-                                        <menupopup>
-                                        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                                        </menupopup>
-                                    </rule>
-                                </template>
-                                <menupopup>
-                                </menupopup>
-                            </menu>
-                            <menu label="&charsetMenuUnicode.label;" accesskey="&charsetMenuUnicode.accesskey;" datasources="rdf:charset-menu" ref="NC:BrowserUnicodeCharsetMenuRoot">
-                                <template>
-                                    <rule>
-                                        <menupopup>
-                                        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                                        </menupopup>
-                                    </rule>
-                                </template>
-                                <menupopup>
-                                </menupopup>
-                            </menu>
-                            <menuseparator />
-                        </menupopup>
-                    </menu>
-                    <menuitem name="charsetCustomize" accesskey="&charsetCustomize.accesskey;" label="&charsetCustomize.label;" oncommand="window.openDialog('chrome://global/content/customizeCharset.xul','PrefWindow', 'chrome,modal=yes,resizable=yes', 'browser')"/>
-                    </menupopup>
-                </menu>
+#include browser-charsetmenu.inc
                 <menuseparator/>
                 <menuitem id="menu_pageSource"
                           accesskey="&pageSourceCmd.accesskey;"
                           label="&pageSourceCmd.label;"
                           key="key_viewSource"
                           command="View:PageSource"/>
                 <menuitem id="fullScreenItem"
                           accesskey="&fullScreenCmd.accesskey;"
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -738,17 +738,17 @@ HistoryMenu.prototype = {
 
   toggleTabsFromOtherComputers: function PHM_toggleTabsFromOtherComputers() {
     // This is a no-op if MOZ_SERVICES_SYNC isn't defined
 #ifdef MOZ_SERVICES_SYNC
     // enable/disable the Tabs From Other Computers menu
     let menuitem = document.getElementById("sync-tabs-menuitem");
 
     // If Sync isn't configured yet, then don't show the menuitem.
-    if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
+    if (Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
         Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
       menuitem.setAttribute("hidden", true);
       return;
     }
 
     // The tabs engine might never be inited (if services.sync.registerEngines
     // is modified), so make sure we avoid undefined errors.
     let enabled = Weave.Service.isLoggedIn && Weave.Engines.get("tabs") &&
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -86,33 +86,39 @@ let gSyncUI = {
 
   _wasDelayed: false,
 
   _needsSetup: function SUI__needsSetup() {
     let firstSync = "";
     try {
       firstSync = Services.prefs.getCharPref("services.sync.firstSync");
     } catch (e) { }
-    return Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
+    return Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
            firstSync == "notReady";
   },
 
+  _isLoggedIn: function() {
+    if (this._needsSetup())
+      return false;
+    return Weave.Service.isLoggedIn;
+  },
+
   updateUI: function SUI_updateUI() {
     let needsSetup = this._needsSetup();
     document.getElementById("sync-setup").hidden = !needsSetup;
     document.getElementById("sync-menu").hidden = needsSetup;
 
     if (gBrowser) {
-      let showLabel = !Weave.Service.isLoggedIn && !needsSetup;
+      let showLabel = !this._isLoggedIn() && !needsSetup;
       let button = document.getElementById("sync-status-button");
       button.setAttribute("class", showLabel ? "statusbarpanel-iconic-text"
                                              : "statusbarpanel-iconic");
       button.image = "chrome://browser/skin/sync-16.png";
 
-      if (!Weave.Service.isLoggedIn) {
+      if (!this._isLoggedIn()) {
         //XXXzpao When we move the string bundle, we can add more and make this
         //        say "needs setup" or something similar. (bug 583381)
         button.removeAttribute("tooltiptext");
       }
     }
   },
 
   alltabsPopupShowing: function(event) {
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -128,16 +128,26 @@ toolbar[mode="icons"] > #reload-button[d
   background-position: bottom left;
 }
 
 .split-menuitem-menu > .menu-text,
 .split-menuitem-menu > .menu-accel-container {
   display: none;
 }
 
+.menuitem-tooltip {
+  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-tooltip");
+}
+
+.menuitem-iconic-tooltip,
+.menuitem-tooltip[type="checkbox"],
+.menuitem-tooltip[type="radio"] {
+  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-iconic-tooltip");
+}
+
 /* ::::: location bar ::::: */
 #urlbar {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
 /* Some child nodes want to be ordered based on the locale's direction, while
    everything else should be ltr. */
 #urlbar:-moz-locale-dir(rtl) > .autocomplete-textbox-container > .textbox-input-box {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -146,17 +146,17 @@ XPCOMUtils.defineLazyGetter(window, "gFi
 __defineSetter__("PluralForm", function (val) {
   delete this.PluralForm;
   return this.PluralForm = val;
 });
 
 #ifdef MOZ_SERVICES_SYNC
 XPCOMUtils.defineLazyGetter(this, "Weave", function() {
   let tmp = {};
-  Cu.import("resource://services-sync/service.js", tmp);
+  Cu.import("resource://services-sync/main.js", tmp);
   return tmp.Weave;
 });
 #endif
 
 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
   let tmp = {};
   Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
   try {
@@ -7679,22 +7679,52 @@ var LightWeightThemeWebInstaller = {
     var notificationBox = gBrowser.getNotificationBox();
     var notificationBar =
       notificationBox.appendNotification(message, "lwtheme-install-request", "",
                                          notificationBox.PRIORITY_INFO_MEDIUM,
                                          buttons);
     notificationBar.persistence = 1;
   },
 
-  _install: function (newTheme) {
-    var previousTheme = this._manager.currentTheme;
-    this._manager.currentTheme = newTheme;
-    if (this._manager.currentTheme &&
-        this._manager.currentTheme.id == newTheme.id)
-      this._postInstallNotification(newTheme, previousTheme);
+  _install: function (newLWTheme) {
+    var previousLWTheme = this._manager.currentTheme;
+
+    var listener = {
+      onEnabling: function(aAddon, aRequiresRestart) {
+        if (!aRequiresRestart)
+          return;
+
+        let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
+          [aAddon.name], 1);
+
+        let action = {
+          label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
+          accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
+          callback: function () {
+            Application.restart();
+          }
+        };
+
+        let options = {
+          timeout: Date.now() + 30000
+        };
+
+        PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",
+                                messageString, "addons-notification-icon",
+                                action, null, options);
+      },
+
+      onEnabled: function(aAddon) {
+        LightWeightThemeWebInstaller._postInstallNotification(newLWTheme, previousLWTheme);
+      }
+    };
+
+    AddonManager.addAddonListener(listener);
+    this._manager.currentTheme = newLWTheme;
+    AddonManager.removeAddonListener(listener);
   },
 
   _postInstallNotification: function (newTheme, previousTheme) {
     function text(id) {
       return gNavigatorBundle.getString("lwthemePostInstallNotification." + id);
     }
 
     var buttons = [{
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -446,42 +446,47 @@
           label="&brandShortName;"
           style="-moz-user-focus: ignore;">
     <menupopup id="appmenu-popup"
                onpopupshowing="updateEditUIVisibility();">
       <hbox>
         <vbox id="appmenuPrimaryPane">
           <hbox flex="1"
                 class="split-menuitem">
-            <menuitem id="appmenu_newTab"
+            <menuitem id="menuitem-tooltip appmenu_newTab"
                       class="split-menuitem-item"
                       flex="1"
                       label="&tabCmd.label;"
-                      command="cmd_newNavigatorTab"/>
+                      command="cmd_newNavigatorTab"
+                      key="key_newNavigatorTab"/>
               <menu class="split-menuitem-menu">
                 <menupopup>
                   <menuitem id="appmenu_newTab_popup"
                             label="&tabCmd.label;"
-                            command="cmd_newNavigatorTab"/>
+                            command="cmd_newNavigatorTab"
+                            key="key_newNavigatorTab"/>
                   <menuitem id="appmenu_newNavigator"
                             label="&newNavigatorCmd.label;"
-                            command="cmd_newNavigator"/>
+                            command="cmd_newNavigator"
+                            key="key_newNavigator"/>
                   <menuseparator/>
                   <menuitem id="appmenu_openFile"
                             label="&openFileCmd.label;"
-                            command="Browser:OpenFile"/>
+                            command="Browser:OpenFile"
+                            key="openFileKb"/>
                 </menupopup>
                </menu>
           </hbox>
           <menuitem id="appmenu_privateBrowsing"
-                    class="menuitem-iconic"
+                    class="menuitem-iconic menuitem-iconic-tooltip"
                     label="&privateBrowsingCmd.start.label;"
                     startlabel="&privateBrowsingCmd.start.label;"
                     stoplabel="&privateBrowsingCmd.stop.label;"
-                    command="Tools:PrivateBrowsing"/>
+                    command="Tools:PrivateBrowsing"
+                    key="key_privatebrowsing"/>
           <menuseparator class="appmenu-menuseparator"/>
           <hbox class="split-menuitem">
             <menuitem id="appmenu-edit-menuitem"
                       label="&editMenu.label;"
                       disabled="true"/>
             <toolbarbutton id="appmenu-cut"
                            class="appmenu-edit-button"
                            command="cmd_cut"
@@ -494,32 +499,37 @@
                            tooltiptext="&copyButton.tooltip;"/>
             <toolbarbutton id="appmenu-paste"
                            class="appmenu-edit-button"
                            command="cmd_paste"
                            onclick="if (!this.disabled) hidePopup();"
                            tooltiptext="&pasteButton.tooltip;"/>
           </hbox>
           <menuitem id="appmenu_find"
+                    class="menuitem-tooltip"
                     label="&appMenuFind.label;"
-                    command="cmd_find"/>
+                    command="cmd_find"
+                    key="key_find"/>
           <menuseparator class="appmenu-menuseparator"/>
           <menuitem id="appmenu_savePage"
+                    class="menuitem-tooltip"
                     label="&savePageCmd.label;"
-                    command="Browser:SavePage"/>
+                    command="Browser:SavePage"
+                    key="key_savePage"/>
           <menuitem id="appmenu_sendLink"
                     label="&sendPageCmd.label;"
                     command="Browser:SendLink"/>
           <hbox flex="1"
                 class="split-menuitem">
             <menuitem id="appmenu_print"
-                      class="menuitem-iconic split-menuitem-item"
+                      class="menuitem-iconic menuitem-iconic-tooltip split-menuitem-item"
                       flex="1"
                       label="&printCmd.label;"
-                      command="cmd_print"/>
+                      command="cmd_print"
+                      key="printKb"/>
             <menu class="split-menuitem-menu">
               <menupopup>
                 <menuitem id="appmenu_print_popup"
                           class="menuitem-iconic"
                           label="&printCmd.label;"
                           command="cmd_print"/>
                 <menuitem id="appmenu_printPreview"
                           label="&printPreviewCmd.label;"
@@ -532,52 +542,62 @@
           </hbox>
           <menuseparator class="appmenu-menuseparator"/>
           <menu id="appmenu_developer"
                 label="&developerMenu.label;">
             <menupopup id="appmenu_developer_popup">
               <menuitem id="appmenu_webConsole"
                         label="&webConsoleCmd.label;"
                         type="checkbox"
-                        oncommand="HUDConsoleUI.toggleHUD();"/>
+                        oncommand="HUDConsoleUI.toggleHUD();"
+                        key="key_webConsole"/>
               <menuitem id="appmenu_pageInspect"
                         label="&inspectMenu.label;"
                         type="checkbox"
-                        command="Tools:Inspect"/>
+                        command="Tools:Inspect"
+                        key="key_inspect"/>
               <menuseparator/>
               <menuitem id="appmenu_pageSource"
                         label="&viewPageSourceCmd.label;"
-                        command="View:PageSource"/>
+                        command="View:PageSource"
+                        key="key_viewSource"/>
+              <menuseparator/>
+#define ID_PREFIX appmenu_
+#include browser-charsetmenu.inc
+#undef ID_PREFIX
               <menuseparator/>
               <menuitem label="&goOfflineCmd.label;"
                         type="checkbox"
                         oncommand="BrowserOffline.toggleOfflineStatus();"/>
             </menupopup>
           </menu>
           <menuseparator class="appmenu-menuseparator"/>
           <menuitem id="appmenu_fullScreen"
+                    class="menuitem-tooltip"
                     label="&fullScreenCmd.label;"
                     type="checkbox"
-                    observes="View:FullScreen"/>
+                    observes="View:FullScreen"
+                    key="key_fullScreen"/>
           <menuitem id="appmenu-quit"
                     class="menuitem-iconic"
 #ifdef XP_WIN
                     label="&quitApplicationCmdWin.label;"
 #else
                     label="&quitApplicationCmd.label;"
 #endif
                     command="cmd_quitApplication"/>
         </vbox>
         <vbox id="appmenuSecondaryPane">
           <hbox class="split-menuitem">
             <menuitem id="appmenu_bookmarks"
-                      class="menuitem-iconic split-menuitem-item"
+                      class="menuitem-iconic menuitem-iconic-tooltip split-menuitem-item"
                       flex="1"
                       label="&bookmarksMenu.label;"
-                      command="Browser:ShowAllBookmarks"/>
+                      command="Browser:ShowAllBookmarks"
+                      key="manBookmarkKb"/>
             <menu id="appmenu_bookmarksMenu"
                   class="split-menuitem-menu">
               <menupopup id="appmenu_bookmarksMenupopup"
                          placespopup="true"
                          context="placesContext"
                          openInTabs="children"
                          oncommand="BookmarksEventHandler.onCommand(event);"
                          onclick="BookmarksEventHandler.onClick(event);"
@@ -590,19 +610,19 @@
                          popupsinherittooltip="true">
                 <menuseparator id="appmenu_unsortedBookmarks_seperator"/>
                 <menuitem id="appmenu_unsortedBookmarks"
                           label="&appMenuUnsorted.label;"
                           oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
                           class="menuitem-iconic"/>
                 <menuitem id="appmenu_showAllBookmarks"
                           label="&showAllBookmarks.label;"
-                          key="manBookmarkKb"
                           command="Browser:ShowAllBookmarks"
-                          context=""/>
+                          context=""
+                          key="manBookmarkKb"/>
                 <menuseparator/>
                 <menuitem id="appmenu_bookmarkThisPage"
                           class="menuitem-iconic"
                           label="&bookmarkThisPageCmd.label;"
                           command="Browser:AddBookmarkAs"
                           key="addBookmarkAsKb"/>
                 <menuitem id="appmenu_subscribeToPage"
                           class="menuitem-iconic"
@@ -620,34 +640,35 @@
                              onclick="checkForMiddleClick(this, event);"/>
                 </menu>
                 <menuseparator/>
               </menupopup>
             </menu>
           </hbox>
           <hbox class="split-menuitem">
             <menuitem id="appmenu_history"
-                      class="menuitem-iconic split-menuitem-item"
+                      class="menuitem-iconic menuitem-iconic-tooltip split-menuitem-item"
                       flex="1"
                       label="&historyMenu.label;"
-                      command="Browser:ShowAllHistory"/>
+                      command="Browser:ShowAllHistory"
+                      key="showAllHistoryKb"/>
             <menu id="appmenu_historyMenu"
                   class="split-menuitem-menu">
               <menupopup id="appmenu_historyMenupopup"
                          placespopup="true"
                          oncommand="this.parentNode._placesView._onCommand(event);"
                          onclick="checkForMiddleClick(this, event);"
                          onpopupshowing="if (!this.parentNode._placesView)
                                            new HistoryMenu(event);"
                          tooltip="bhTooltip"
                          popupsinherittooltip="true">
                 <menuitem id="appmenu_showAllHistory"
                           label="&showAllHistoryCmd2.label;"
-                          key="showAllHistoryKb"
-                          command="Browser:ShowAllHistory"/>
+                          command="Browser:ShowAllHistory"
+                          key="showAllHistoryKb"/>
                 <menuseparator/>
                 <menuitem id="appmenu_sanitizeHistory"
                           label="&clearRecentHistory.label;"
                           key="key_sanitize"
                           command="Tools:Sanitize"/>
                 <menuseparator class="hide-if-empty-places-result"/>
                 <menu id="appmenu_recentlyClosedTabsMenu"
                       class="recentlyClosedTabsMenu"
@@ -663,23 +684,26 @@
                   <menupopup id="appmenu_recentlyClosedWindowsMenupopup"
                              onpopupshowing="document.getElementById('appmenu_historyMenu')._placesView.populateUndoWindowSubmenu();"/>
                 </menu>
                 <menuseparator/>
               </menupopup>
             </menu>
           </hbox>
             <menuitem id="appmenu_downloads"
+                      class="menuitem-tooltip"
                       label="&downloads.label;"
-                      command="Tools:Downloads"/>
+                      command="Tools:Downloads"
+                      key="key_openDownloads"/>
             <spacer id="appmenuSecondaryPane-spacer"/>
             <menuitem id="appmenu_addons"
-                      class="menuitem-iconic"
+                      class="menuitem-iconic menuitem-iconic-tooltip"
                       label="&addons.label;"
-                      command="Tools:Addons"/>
+                      command="Tools:Addons"
+                      key="key_openAddons"/>
             <hbox class="split-menuitem">
               <menuitem id="appmenu_customize"
                         label="&preferencesCmd.label;"
                         class="split-menuitem-item"
                         flex="1"
                         oncommand="openPreferences();"/>
               <menu class="split-menuitem-menu"
                     label="&preferencesCmd.label;">
--- a/browser/base/content/syncGenericChange.js
+++ b/browser/base/content/syncGenericChange.js
@@ -16,54 +16,61 @@
  * The Initial Developer of the Original Code is the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Edward Lee <edilee@mozilla.com>
  *   Mike Connor <mconnor@mozilla.com>
  *   Paul O’Shannessy <paul@oshannessy.com>
+ *   Philipp von Weitershausen <philipp@weitershausen.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 ***** */
 
-Components.utils.import("resource://services-sync/service.js");
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+Components.utils.import("resource://services-sync/main.js");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 let Change = {
   _dialog: null,
   _dialogType: null,
   _status: null,
   _statusIcon: null,
   _firstBox: null,
   _secondBox: null,
 
+  get _passphraseBox() {
+    delete this._passphraseBox;
+    return this._passphraseBox = document.getElementById("passphraseBox");
+  },
+
   get _currentPasswordInvalid() {
     return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
   },
 
   get _updatingPassphrase() {
     return this._dialogType == "UpdatePassphrase";
   },
 
   onLoad: function Change_onLoad() {
     /* Load labels */
-    let box1label = document.getElementById("textBox1Label");
-    let box2label = document.getElementById("textBox2Label");
     let introText = document.getElementById("introText");
     let introText2 = document.getElementById("introText2");
     let warningText = document.getElementById("warningText");
 
     // load some other elements & info from the window
     this._dialog = document.getElementById("change-dialog");
     this._dialogType = window.arguments[0];
     this._status = document.getElementById("status");
@@ -72,36 +79,47 @@ let Change = {
     this._secondBox = document.getElementById("textBox2");
 
     this._stringBundle =
       Services.strings.createBundle("chrome://browser/locale/syncGenericChange.properties");
 
     switch (this._dialogType) {
       case "UpdatePassphrase":
       case "ResetPassphrase":
-        box1label.value = this._str("new.passphrase.label");
+        document.getElementById("textBox1Row").hidden = true;
+        document.getElementById("textBox2Row").hidden = true;
+        let passphraseLabel = document.getElementById("passphraseLabel");
+        passphraseLabel.value = this._str("new.synckey.label");
 
         if (this._updatingPassphrase) {
-          document.title = this._str("new.passphrase.title");
-          introText.textContent = this._str("new.passphrase.introText");
+          document.getElementById("passphraseBackupButtons").hidden = true;
+          document.getElementById("generatePassphraseButton").hidden = true;
+          document.title = this._str("new.synckey.title");
+          introText.textContent = this._str("new.synckey.introText");
           this._dialog.getButton("accept")
-              .setAttribute("label", this._str("new.passphrase.acceptButton"));
-          document.getElementById("textBox2Row").hidden = true;
+              .setAttribute("label", this._str("new.synckey.acceptButton"));
         }
         else {
-          document.title = this._str("change.passphrase.title");
-          box2label.value = this._str("new.passphrase.confirm");
-          introText.textContent = this._str("change.passphrase.introText");
-          introText2.textContent = this._str("change.passphrase.introText2");
-          warningText.textContent = this._str("change.passphrase.warningText");
+          let pp = Weave.Service.passphrase;
+          if (pp.length == 20)
+            pp = gSyncUtils.hyphenatePassphrase(pp);
+          document.getElementById("passphraseBox").value = pp;
+          document.title = this._str("change.synckey.title");
+          introText.innerHTML = this._str("change.synckey.introText");
+          introText2.innerHTML = this._str("change.synckey.introText2");
+          warningText.innerHTML = this._str("change.synckey.warningText");
           this._dialog.getButton("accept")
-              .setAttribute("label", this._str("change.passphrase.acceptButton"));
+              .setAttribute("label", this._str("change.synckey.acceptButton"));
         }
         break;
       case "ChangePassword":
+        document.getElementById("passphraseRow").hidden = true;
+        document.getElementById("passphraseBackupButtons").hidden = true;
+        let box1label = document.getElementById("textBox1Label");
+        let box2label = document.getElementById("textBox2Label");
         box1label.value = this._str("new.password.label");
 
         if (this._currentPasswordInvalid) {
           document.title = this._str("new.password.title");
           introText.textContent = this._str("new.password.introText");
           this._dialog.getButton("accept")
               .setAttribute("label", this._str("new.password.acceptButton"));
           document.getElementById("textBox2Row").hidden = true;
@@ -146,34 +164,42 @@ let Change = {
         return this.doChangePassphrase();
         break;
       case "ChangePassword":
         return this.doChangePassword();
         break;
     }
   },
 
+  doGeneratePassphrase: function () {
+    let passphrase = gSyncUtils.generatePassphrase();
+    let el = document.getElementById("passphraseBox");
+    el.value = gSyncUtils.hyphenatePassphrase(passphrase);
+    this._dialog.getButton("accept").disabled = false;
+  },
+
   doChangePassphrase: function Change_doChangePassphrase() {
+    let pp = gSyncUtils.normalizePassphrase(this._passphraseBox.value);
     if (this._updatingPassphrase) {
-      Weave.Service.passphrase = this._firstBox.value;
+      Weave.Service.passphrase = pp;
       if (Weave.Service.login()) {
-        this._updateStatus("change.passphrase.success", "success");
+        this._updateStatus("change.synckey.success", "success");
         Weave.Service.persistLogin();
       }
       else {
         this._updateStatus("new.passphrase.status.incorrect", "error");
       }
     }
     else {
-      this._updateStatus("change.passphrase.label", "active");
+      this._updateStatus("change.synckey.label", "active");
 
-      if (Weave.Service.changePassphrase(this._firstBox.value))
-        this._updateStatus("change.passphrase.success", "success");
+      if (Weave.Service.changePassphrase(pp))
+        this._updateStatus("change.synckey.success", "success");
       else
-        this._updateStatus("change.passphrase.error", "error");
+        this._updateStatus("change.synckey.error", "error");
     }
 
     return false;
   },
 
   doChangePassword: function Change_doChangePassword() {
     if (this._currentPasswordInvalid) {
       Weave.Service.password = this._firstBox.value;
@@ -192,31 +218,31 @@ let Change = {
         this._updateStatus("change.password.status.success", "success");
       else
         this._updateStatus("change.password.status.error", "error");
     }
 
     return false;
   },
 
-  validate: function (event) {
+  validate: function () {
     let valid = false;
     let errorString = "";
 
     if (this._dialogType == "ChangePassword") {
       if (this._currentPasswordInvalid)
         [valid, errorString] = gSyncUtils.validatePassword(this._firstBox);
       else
         [valid, errorString] = gSyncUtils.validatePassword(this._firstBox, this._secondBox);
     }
     else {
       if (this._updatingPassphrase)
-        [valid, errorString] = gSyncUtils.validatePassphrase(this._firstBox);
+        [valid, errorString] = gSyncUtils.validatePassphrase(this._passphraseBox);
       else
-        [valid, errorString] = gSyncUtils.validatePassphrase(this._firstBox, this._secondBox);
+        [valid, errorString] = gSyncUtils.validatePassphrase(this._passphraseBox, true);
     }
 
     if (errorString == "")
       this._clearStatus();
     else
       this._updateStatusWithString(errorString, "error");
 
     this._dialog.getButton("accept").disabled = !valid;
--- a/browser/base/content/syncGenericChange.xul
+++ b/browser/base/content/syncGenericChange.xul
@@ -18,16 +18,17 @@
 # The Initial Developer of the Original Code is the Mozilla Foundation.
 # Portions created by the Initial Developer are Copyright (C) 2009
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Edward Lee <edilee@mozilla.com>
 #   Mike Connor <mconnor@mozilla.com>
 #   Paul O’Shannessy <paul@oshannessy.com>
+#   Philipp von Weitershausen <philipp@weitershausen.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
@@ -36,57 +37,92 @@
 # 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 *****
 
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?>
 
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
+<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
+%brandDTD;
+%syncBrandDTD;
+%syncSetupDTD;
+]>
 <dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         id="change-dialog"
         windowtype="Weave:ChangeSomething"
         buttons="accept,cancel"
         onload="Change.onLoad()"
         buttondisabledaccept="true"
         ondialogaccept="return Change.onDialogAccept();"
         defaultButton="accept">
 
   <script type="application/javascript"
           src="chrome://browser/content/syncGenericChange.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/syncUtils.js"/>
+  <script type="application/javascript"
+          src="chrome://global/content/printUtils.js"/>
 
   <hbox align="top">
     <image id="syncIcon"/>
     <spacer style="width: 1em"/>
     <description flex="1">
       <html:p id="introText"/>
       <html:p id="introText2"/>
     </description>
   </hbox>
   <separator class="thin"/>
   <vbox>
     <grid>
       <columns>
         <column align="right"/>
-        <column/>
+        <column flex="3"/>
+        <column flex="1"/>
       </columns>
       <rows>
-        <row>
+        <row id="textBox1Row" align="center">
           <label id="textBox1Label" control="textBox1"/>
-          <textbox id="textBox1" type="password" oninput="Change.validate(event)"/>
+          <textbox id="textBox1" type="password" oninput="Change.validate()"/>
+          <spacer/>
+        </row>
+        <row id="textBox2Row" align="center">
+          <label id="textBox2Label" control="textBox2"/>
+          <textbox id="textBox2" type="password" oninput="Change.validate()"/>
+          <spacer/>
         </row>
-        <row id="textBox2Row">
-          <label id="textBox2Label" control="textBox2"/>
-          <textbox id="textBox2" type="password" oninput="Change.validate(event)"/>
+        <row id="passphraseRow" align="center">
+          <label id="passphraseLabel" control="passphraseBox"/>
+          <textbox id="passphraseBox"
+                   onfocus="this.select()"
+                   oninput="Change.validate()"/>
+          <label id="generatePassphraseButton"
+                 value="&syncKeyGenerate.label;"
+                 class="text-link inline-link"
+                 onclick="event.stopPropagation();
+                          Change.doGeneratePassphrase();"/>
         </row>
       </rows>
     </grid>
+    <hbox id="passphraseBackupButtons">
+      <button label="&button.syncKeyBackup.email.label;"
+              accesskey="&button.syncKeyBackup.email.accesskey;"
+              oncommand="gSyncUtils.passphraseEmail('passphraseBox');"/>
+      <button label="&button.syncKeyBackup.print.label;"
+              accesskey="&button.syncKeyBackup.print.accesskey;"
+              oncommand="gSyncUtils.passphrasePrint('passphraseBox');"/>
+      <button label="&button.syncKeyBackup.save.label;"
+              accesskey="&button.syncKeyBackup.save.accesskey;"
+              oncommand="gSyncUtils.passphraseSave('passphraseBox');"/>
+    </hbox>
 
     <description>
       <html:p class="data" id="warningText"/>
     </description>
 
     <hbox align="center">
       <image id="statusIcon" class="statusIcon"/>
       <label id="status" class="status" value=" "/>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/syncKey.xhtml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+# ***** 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 Firefox Sync.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Philipp von Weitershausen <philipp@weitershausen.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 *****
+
+<!DOCTYPE html [
+  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+  %htmlDTD;
+  <!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
+  %syncSetupDTD;
+]>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>&page.syncKeyBackup.title;</title>
+  <style type="text/css">
+    #synckey { font-size: 150% }
+  </style>
+</head>
+
+<body>
+<h1>&page.syncKeyBackup.title;</h1>
+
+<p id="synckey">SYNCKEY</p>
+
+<p>&page.syncKeyBackup.descr;</p>
+
+</body>
+</html>
--- a/browser/base/content/syncSetup.js
+++ b/browser/base/content/syncSetup.js
@@ -43,25 +43,24 @@ const Cc = Components.classes;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 // page consts
 
 const INTRO_PAGE                    = 0;
 const NEW_ACCOUNT_START_PAGE        = 1;
 const NEW_ACCOUNT_PP_PAGE           = 2;
-const NEW_ACCOUNT_PREFS_PAGE        = 3;
-const NEW_ACCOUNT_CAPTCHA_PAGE      = 4;
-const EXISTING_ACCOUNT_LOGIN_PAGE   = 5;
-const EXISTING_ACCOUNT_PP_PAGE      = 6;
-const EXISTING_ACCOUNT_MERGE_PAGE   = 7;
-const EXISTING_ACCOUNT_CONFIRM_PAGE = 8;
-const SETUP_SUCCESS_PAGE            = 9;
+const NEW_ACCOUNT_CAPTCHA_PAGE      = 3;
+const EXISTING_ACCOUNT_LOGIN_PAGE   = 4;
+const EXISTING_ACCOUNT_PP_PAGE      = 5;
+const OPTIONS_PAGE                  = 6;
+const OPTIONS_CONFIRM_PAGE          = 7;
+const SETUP_SUCCESS_PAGE            = 8;
 
-Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/main.js");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 var gSyncSetup = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
                                          Ci.nsIWebProgressListener,
                                          Ci.nsISupportsWeakReference]),
 
@@ -71,16 +70,17 @@ var gSyncSetup = {
   _remoteSites: [Weave.Service.serverURL, "https://api-secure.recaptcha.net"],
 
   status: {
     username: false,
     password: false,
     email: false,
     server: false
   },
+  _haveSyncKeyBackup: false,
 
   get _usingMainServers() {
     if (this._settingUpNew)
       return document.getElementById("serverType").selectedItem.value == "main";
 
     return document.getElementById("existingServerType").selectedItem.value == "main";
   },
 
@@ -107,36 +107,34 @@ var gSyncSetup = {
     window.addEventListener("unload", function() addRem(false), false);
 
     this.captchaBrowser = document.getElementById("captcha");
     this.wizard = document.getElementById("accountSetup");
 
     if (window.arguments && window.arguments[0] == true) {
       // we're resetting sync
       this._resettingSync = true;
-      this.wizard.pageIndex = EXISTING_ACCOUNT_MERGE_PAGE;
+      this.wizard.pageIndex = OPTIONS_PAGE;
     }
     else {
       this.wizard.canAdvance = false;
       this.captchaBrowser.addProgressListener(this);
       Weave.Svc.Prefs.set("firstSync", "notReady");
     }
-  },
 
-  updateSyncPrefs: function () {
-    let syncEverything = document.getElementById("weaveSyncMode").selectedItem.value == "syncEverything";
-    document.getElementById("syncModeOptions").selectedIndex = syncEverything ? 0 : 1;
+    this.wizard.getButton("extra1").label =
+      this._stringBundle.GetStringFromName("button.syncOptions.label");
 
-    if (syncEverything) {
-      document.getElementById("engine.bookmarks").checked = true;
-      document.getElementById("engine.passwords").checked = true;
-      document.getElementById("engine.history").checked   = true;
-      document.getElementById("engine.tabs").checked      = true;
-      document.getElementById("engine.prefs").checked     = true;
-    }
+    // Remember these values because the options pages change them temporarily.
+    this._nextButtonLabel = this.wizard.getButton("next").label;
+    this._nextButtonAccesskey = this.wizard.getButton("next")
+                                           .getAttribute("accesskey");
+    this._backButtonLabel = this.wizard.getButton("back").label;
+    this._backButtonAccesskey = this.wizard.getButton("back")
+                                           .getAttribute("accesskey");
   },
 
   startNewAccountSetup: function () {
     this._settingUpNew = true;
     this.wizard.pageIndex = NEW_ACCOUNT_START_PAGE;
   },
 
   useExistingAccount: function () {
@@ -180,24 +178,16 @@ var gSyncSetup = {
         }
         else
           this._setFeedbackMessage(feedback, true);
 
         break;
     }
   },
 
-  handleExpanderClick: function (event) {
-    let expander = document.getElementById("setupAccountExpander");
-    let expand = expander.className == "expander-down";
-    expander.className =
-       expand ? "expander-up" : "expander-down";
-    document.getElementById("signInBox").hidden = !expand;
-  },
-
   setupInitialSync: function () {
     let action = document.getElementById("mergeChoiceRadio").selectedItem.id;
     switch (action) {
       case "resetClient":
         // if we're not resetting sync, we don't need to explicitly
         // call resetClient
         if (!this._resettingSync)
           return;
@@ -223,17 +213,17 @@ var gSyncSetup = {
           if (!this.status[i])
             return false;
         }
         if (this._usingMainServers)
           return document.getElementById("tos").checked;
 
         return true;
       case NEW_ACCOUNT_PP_PAGE:
-        return this.onPassphraseChange();
+        return this._haveSyncKeyBackup && this.checkPassphrase();
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         let hasUser = document.getElementById("existingUsername").value != "";
         let hasPass = document.getElementById("existingPassword").value != "";
         if (hasUser && hasPass) {
           if (this._usingMainServers)
             return true;
 
           if (this._validateServer(document.getElementById("existingServerURL"), false))
@@ -274,17 +264,17 @@ var gSyncSetup = {
   },
 
   onPasswordChange: function () {
     let password = document.getElementById("weavePassword");
     let valid, str;
     if (password.value == document.getElementById("weavePassphrase").value) {
       // xxxmpc - hack, sigh
       valid = false;
-      errorString = Weave.Utils.getErrorString("change.password.pwSameAsPassphrase");
+      errorString = Weave.Utils.getErrorString("change.password.pwSameAsSyncKey");
     }
     else {
       let pwconfirm = document.getElementById("weavePasswordConfirm");
       [valid, errorString] = gSyncUtils.validatePassword(password, pwconfirm);
     }
 
     let feedback = document.getElementById("passwordFeedbackRow");
     this._setFeedback(feedback, valid, errorString);
@@ -301,111 +291,166 @@ var gSyncSetup = {
     this._setFeedbackMessage(document.getElementById("emailFeedbackRow"),
                              this.status.email,
                              "invalidEmail.label");
 
     this.checkFields();
   },
 
   onPassphraseChange: function () {
+    this._haveSyncKeyBackup = true;
+    this._haveCustomSyncKey = true;
+    let el = document.getElementById("generatePassphraseButton");
+    el.hidden = false;
+    this.checkFields();
+  },
+
+  onPassphraseGenerate: function () {
+    let passphrase = gSyncUtils.generatePassphrase();
+    Weave.Service.passphrase = passphrase;
+    let el = document.getElementById("weavePassphrase");
+    el.value = gSyncUtils.hyphenatePassphrase(passphrase);
+
+    el = document.getElementById("generatePassphraseButton");
+    el.hidden = true;
+    let feedback = document.getElementById("passphraseFeedbackRow");
+    this._setFeedback(feedback, true, "");
+  },
+
+  afterBackup: function () {
+    this._haveSyncKeyBackup = true;
+    this.checkFields();
+  },
+
+  checkPassphrase: function () {
     let el1 = document.getElementById("weavePassphrase");
     let valid, str;
     // xxxmpc - hack, sigh
     if (el1.value == document.getElementById("weavePassword").value) {
       valid = false;
       str = Weave.Utils.getErrorString("change.passphrase.ppSameAsPassword");
     }
     else {
-      let el2 = document.getElementById("weavePassphraseConfirm");
-      [valid, str] = gSyncUtils.validatePassphrase(el1, el2);
+      [valid, str] = gSyncUtils.validatePassphrase(el1);
     }
 
     let feedback = document.getElementById("passphraseFeedbackRow");
     this._setFeedback(feedback, valid, str);
     return valid;
   },
 
   onPageShow: function() {
     switch (this.wizard.pageIndex) {
       case INTRO_PAGE:
         this.wizard.getButton("next").hidden = true;
         this.wizard.getButton("back").hidden = true;
-        this.wizard.getButton("cancel").label =
-          this._stringBundle.GetStringFromName("cancelSetup.label");
+        this.wizard.getButton("extra1").hidden = true;
         break;
       case NEW_ACCOUNT_PP_PAGE:
+        let el = document.getElementById("weavePassphrase");
+        el.blur();
+        if (!el.value)
+          this.onPassphraseGenerate();
         this.checkFields();
         break;
       case NEW_ACCOUNT_START_PAGE:
+        this.wizard.getButton("extra1").hidden = false;
         this.onServerChange();
-        this.checkFields(); // fall through
+        // fall through
       case EXISTING_ACCOUNT_LOGIN_PAGE:
-      case EXISTING_ACCOUNT_MERGE_PAGE:
+        this.checkFields();
         this.wizard.getButton("next").hidden = false;
         this.wizard.getButton("back").hidden = false;
-        this.wizard.canRewind = !this._resettingSync;
+        this.wizard.getButton("extra1").hidden = false;
+        this.wizard.canRewind = true;
         break;
       case SETUP_SUCCESS_PAGE:
         this.wizard.canRewind = false;
         this.wizard.getButton("back").hidden = true;
+        this.wizard.getButton("next").hidden = true;
         this.wizard.getButton("cancel").hidden = true;
+        this.wizard.getButton("finish").hidden = false;
+        this._handleSuccess();
+        break;
+      case OPTIONS_PAGE:
+        this.wizard.canRewind = false;
+        this.wizard.canAdvance = true;
+        if (!this._resettingSync) {
+          this.wizard.getButton("next").label =
+            this._stringBundle.GetStringFromName("button.syncOptionsDone.label");
+          this.wizard.getButton("next").removeAttribute("accesskey");
+        }
+        this.wizard.getButton("next").hidden = false;
+        this.wizard.getButton("back").hidden = true;
+        this.wizard.getButton("cancel").hidden = !this._resettingSync;
+        this.wizard.getButton("extra1").hidden = true;
+        document.getElementById("syncComputerName").value = Weave.Clients.localName;
+        document.getElementById("syncOptions").collapsed = this._resettingSync;
+        document.getElementById("mergeOptions").collapsed = this._settingUpNew;
+        break;
+      case OPTIONS_CONFIRM_PAGE:
+        this.wizard.canRewind = true;
+        this.wizard.canAdvance = true;
+        this.wizard.getButton("back").label =
+          this._stringBundle.GetStringFromName("button.syncOptionsCancel.label");
+        this.wizard.getButton("back").removeAttribute("accesskey");
+        this.wizard.getButton("back").hidden = this._resettingSync;
+        this.wizard.getButton("next").hidden = false;
+        this.wizard.getButton("finish").hidden = true;
         break;
     }
   },
 
   onWizardAdvance: function () {
     if (!this.wizard.pageIndex)
       return true;
 
     switch (this.wizard.pageIndex) {
-      case NEW_ACCOUNT_PREFS_PAGE:
-        if (this._settingUpNew) {
-          // time to load the captcha
-          // first check for NoScript and whitelist the right sites
-          this._handleNoScript(true);
-          this.captchaBrowser.loadURI(Weave.Service.miscAPI + "captcha_html");
-          return true;
-        }
-
-        this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
-        return false;
       case NEW_ACCOUNT_CAPTCHA_PAGE:
         let doc = this.captchaBrowser.contentDocument;
         let getField = function getField(field) {
           let node = doc.getElementById("recaptcha_" + field + "_field");
           return node && node.value;
         };
 
-        this.startThrobber(true);
+        // Display throbber
+        let feedback = document.getElementById("captchaFeedback");
+        let image = feedback.firstChild;
+        let label = image.nextSibling;
+        image.setAttribute("status", "active");
+        label.value = this._stringBundle.GetStringFromName("verifying.label");
+        feedback.hidden = false;
+
         let username = document.getElementById("weaveUsername").value;
         let password = document.getElementById("weavePassword").value;
         let email    = document.getElementById("weaveEmail").value;
         let challenge = getField("challenge");
         let response = getField("response");
 
         let error = Weave.Service.createAccount(username, password, email,
                                                 challenge, response);
-        this.startThrobber(false);
 
         if (error == null) {
           Weave.Service.username = username;
           Weave.Service.password = password;
           this._handleNoScript(false);
           this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
-          return true;
+          return false;
         }
 
-        // this could be nicer, but it'll do for now
-        Weave.Svc.Prompt.alert(window,
-                               this._stringBundle.GetStringFromName("errorCreatingAccount.title"),
-                               Weave.Utils.getErrorString(error));
+        image.setAttribute("status", "error");
+        label.value = Weave.Utils.getErrorString(error);
         return false;
       case NEW_ACCOUNT_PP_PAGE:
-        Weave.Service.passphrase = document.getElementById("weavePassphrase").value;
-        document.getElementById("syncComputerName").value = Weave.Clients.localName;
+        if (this._haveCustomSyncKey)
+          Weave.Service.passphrase = document.getElementById("weavePassphrase").value;
+        // Time to load the captcha.
+        // First check for NoScript and whitelist the right sites.
+        this._handleNoScript(true);
+        this.captchaBrowser.loadURI(Weave.Service.miscAPI + "captcha_html");
         break;
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         Weave.Service.username = document.getElementById("existingUsername").value;
         Weave.Service.password = document.getElementById("existingPassword").value;
         Weave.Service.passphrase = document.getElementById("existingPassphrase").value;
         // verifyLogin() will likely return false because we probably don't
         // have a passphrase yet (unless the user already entered it
         // and hit the back button).
@@ -413,59 +458,58 @@ var gSyncSetup = {
             && Weave.Status.login != Weave.LOGIN_FAILED_NO_PASSPHRASE
             && Weave.Status.login != Weave.LOGIN_FAILED_INVALID_PASSPHRASE) {
           let feedback = document.getElementById("existingPasswordFeedbackRow");
           this._setFeedbackMessage(feedback, false, Weave.Status.login);
           return false;
         }
         break;
       case EXISTING_ACCOUNT_PP_PAGE:
-        Weave.Service.passphrase = document.getElementById("existingPassphrase").value;
+        let pp = document.getElementById("existingPassphrase").value;
+        Weave.Service.passphrase = gSyncUtils.normalizePassphrase(pp);
         if (Weave.Service.login())
-          return true;
-
+          this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
         return false;
-      case EXISTING_ACCOUNT_MERGE_PAGE:
+      case OPTIONS_PAGE:
+        let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
+        // No confirmation needed on new account setup or merge option
+        // with existing account.
+        if (this._settingUpNew || (!this._resettingSync && desc == 0))
+          return this.returnFromOptions();
         return this._handleChoice();
-      case EXISTING_ACCOUNT_CONFIRM_PAGE:
-        this.setupInitialSync();
+      case OPTIONS_CONFIRM_PAGE:
         if (this._resettingSync) {
           this.onWizardFinish();
           window.close();
           return false;
         }
-
-        this.wizard.pageIndex = NEW_ACCOUNT_PREFS_PAGE;
-        document.getElementById("syncComputerName").value = Weave.Clients.localName;
-        return false;
+        return this.returnFromOptions();
     }
     return true;
   },
 
   onWizardBack: function () {
     switch (this.wizard.pageIndex) {
       case NEW_ACCOUNT_START_PAGE:
       case EXISTING_ACCOUNT_LOGIN_PAGE:
         this.wizard.pageIndex = INTRO_PAGE;
         return false;
       case EXISTING_ACCOUNT_PP_PAGE: // no idea wtf is up here, but meh!
         this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
         return false;
-      case NEW_ACCOUNT_PREFS_PAGE:
-        if (this._settingUpNew)
-          return true;
-
-        this.wizard.pageIndex = EXISTING_ACCOUNT_CONFIRM_PAGE;
-        return false;
+      case OPTIONS_CONFIRM_PAGE:
+        // Backing up from the confirmation page = resetting first sync to merge.
+        document.getElementById("mergeChoiceRadio").selectedIndex = 0;
+        return this.returnFromOptions();
     }
     return true;
   },
 
   onWizardFinish: function () {
-    Weave.Status.service == Weave.STATUS_OK;
+    this.setupInitialSync();
 
     if (!this._resettingSync) {
       function isChecked(element) {
         return document.getElementById(element).hasAttribute("checked");
       }
 
       let prefs = ["engine.bookmarks", "engine.passwords", "engine.history", "engine.tabs", "engine.prefs"];
       for (let i = 0;i < prefs.length;i++) {
@@ -488,24 +532,42 @@ var gSyncSetup = {
 
     Weave.Service.syncOnIdle(1);
   },
 
   onWizardCancel: function () {
     if (this._resettingSync)
       return;
 
-    if (this.wizard.pageIndex == 9) {
+    if (this.wizard.pageIndex == SETUP_SUCCESS_PAGE) {
       this.onWizardFinish();
       return;
     }
     this._handleNoScript(false);
     Weave.Service.startOver();
   },
 
+  onSyncOptions: function () {
+    this._beforeOptionsPage = this.wizard.pageIndex;
+    this.wizard.pageIndex = OPTIONS_PAGE;
+  },
+
+  returnFromOptions: function() {
+    this.wizard.getButton("next").label = this._nextButtonLabel;
+    this.wizard.getButton("next").setAttribute("accesskey",
+                                               this._nextButtonAccesskey);
+    this.wizard.getButton("back").label = this._backButtonLabel;
+    this.wizard.getButton("back").setAttribute("accesskey",
+                                               this._backButtonAccesskey);
+    this.wizard.getButton("cancel").hidden = false;
+    this.wizard.getButton("extra1").hidden = false;
+    this.wizard.pageIndex = this._beforeOptionsPage;
+    return false;
+  },
+
   // _handleNoScript is needed because it blocks the captcha. So we temporarily
   // allow the necessary sites so that we can verify the user is in fact a human.
   // This was done with the help of Giorgio (NoScript author). See bug 508112.
   _handleNoScript: function (addExceptions) {
     // if NoScript isn't installed, or is disabled, bail out.
     let ns = Cc["@maone.net/noscript-service;1"];
     if (ns == null)
       return;
@@ -523,20 +585,16 @@ var gSyncSetup = {
     else {
       this._disabledSites.forEach(function(site) {
         ns.setJSEnabled(site, false);
       });
       this._disabledSites = [];
     }
   },
 
-  startThrobber: function (start) {
-    // FIXME: stubbed (bug 583653)
-  },
-
   onServerChange: function () {
     if (this.wizard.pageIndex == EXISTING_ACCOUNT_LOGIN_PAGE) {
       if (this._usingMainServers)
         Weave.Svc.Prefs.reset("serverURL");
       document.getElementById("existingServerRow").hidden = this._usingMainServers;
       this.checkFields();
       return;
     }
@@ -610,16 +668,35 @@ var gSyncSetup = {
     if (valid)
       element.value = Weave.Service.serverURL;
     else
       Weave.Svc.Prefs.reset("serverURL");
 
     return valid;
   },
 
+  _handleSuccess: function() {
+    let self = this;
+    function fill(id, string)
+      document.getElementById(id).firstChild.nodeValue =
+        string ? self._stringBundle.GetStringFromName(string) : "";
+
+    fill("firstSyncAction", "");
+    fill("firstSyncActionWarning", "");
+    if (this._settingUpNew) {
+      fill("firstSyncAction", "newAccount.action.label");
+      fill("firstSyncActionChange", "newAccount.change.label");
+      return;
+    }
+    fill("firstSyncActionChange", "existingAccount.change.label");
+    let action = document.getElementById("mergeChoiceRadio").selectedItem.id;
+    let id = action == "resetClient" ? "firstSyncAction" : "firstSyncActionWarning";
+    fill(id, action + ".change.label");
+  },
+
   _handleChoice: function () {
     let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
     document.getElementById("chosenActionDeck").selectedIndex = desc;
     switch (desc) {
       case 1:
         if (this._case1Setup)
           break;
 
--- a/browser/base/content/syncSetup.xul
+++ b/browser/base/content/syncSetup.xul
@@ -19,16 +19,17 @@
 # Mozilla Foundation.
 # Portions created by the Initial Developer are Copyright (C) 2009
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Edward Lee <edilee@mozilla.com>
 #   Mike Connor <mconnor@mozilla.com>
 #   Paul O’Shannessy <paul@oshannessy.com>
+#   Philipp von Weitershausen <philipp@weitershausen.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
@@ -62,80 +63,55 @@
         onload="gSyncSetup.init()">
 
   <script type="application/javascript"
           src="chrome://browser/content/syncSetup.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/syncUtils.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/utilityOverlay.js"/>
+  <script type="application/javascript"
+          src="chrome://global/content/printUtils.js"/>
 
   <wizardpage id="pickSetupType"
-              label="&setup.choicePage.title.label;"
+              label="&syncBrand.fullName.label;"
               onpageshow="gSyncSetup.onPageShow()">
-    <button id="newAccount"
-            class="accountChoiceButton"
-            aria-labelledby="createNewDesc"
-            oncommand="gSyncSetup.startNewAccountSetup()"
-            align="center">
-      <image class="mergeChoiceImage"/>
-      <vbox class="mergeChoiceButtonBox" flex="1">
-        <description class="mainDesc" id="createNewDesc">
-          &setup.choicePage.new.label;
-        </description>
-      </vbox>
-    </button>
-    <button id="existingAccount"
-            class="accountChoiceButton"
-            aria-labelledby="useExistingButton"
-            oncommand="gSyncSetup.useExistingAccount()"
-            align="center">
-      <image class="mergeChoiceImage"/>
-      <vbox class="mergeChoiceButtonBox" flex="1">
-        <description class="mainDesc" id="useExistingDesc">
-          &setup.choicePage.existing.label;
-        </description>
-      </vbox>
-    </button>
+    <vbox align="center" flex="1">
+      <description style="padding: 0 7em;">
+        &setup.pickSetupType.description;
+      </description>
+      <spacer flex="1"/>
+      <button id="newAccount"
+              class="accountChoiceButton"
+              label="&button.createNewAccount.label;"
+              oncommand="gSyncSetup.startNewAccountSetup()"
+              align="center"/>
+      <spacer flex="3"/>
+    </vbox>
+    <separator class="groove"/>
+    <vbox align="center" flex="1">
+      <spacer flex="3"/>
+      <button id="existingAccount"
+              class="accountChoiceButton"
+              label="&button.haveAccount.label;"
+              oncommand="gSyncSetup.useExistingAccount()"/>
+      <spacer flex="3"/>
+    </vbox>
   </wizardpage>
 
-  <wizardpage label="&setup.newAccountPage.title.label;"
+  <wizardpage label="&setup.newAccountDetailsPage.title.label;"
               id="newAccountStart"
+              onextra1="gSyncSetup.onSyncOptions()"
               onpageshow="gSyncSetup.onPageShow();">
     <grid>
       <columns>
         <column/>
         <column class="inputColumn" flex="1"/>
       </columns>
       <rows>
-        <row align="center">
-          <label control="serverType"
-                 value="&connectTo.label;"/>
-          <menulist id="serverType" oncommand="gSyncSetup.onServerChange()">
-            <menupopup>
-              <menuitem label="&serverType.main.label;"
-                        value="main"/>
-              <menuitem label="&serverType.custom.label;"
-                        value="custom"/>
-            </menupopup>
-          </menulist>
-        </row>
-        <row id="serverRow" hidden="true" align="center">
-          <label value="&signIn.serverURL.label;"
-                 accesskey="&signIn.serverURL.accesskey;"
-                 control="weaveServerURL"/>
-          <textbox id="weaveServerURL" onchange="gSyncSetup.onServerChange()"/>
-        </row>
-        <row id="serverFeedbackRow" align="center" hidden="true">
-          <spacer/>
-          <hbox>
-            <image class="statusIcon"/>
-            <label class="status" value=" "/>
-          </hbox>
-        </row>
         <row id="usernameRow" align="center">
           <label value="&signIn.username.label;"
                  accesskey="&signIn.username.accesskey;"
                  control="weaveUsername"/>
           <textbox id="weaveUsername"
                    onchange="gSyncSetup.onUsernameChange()"/>
         </row>
         <row id="usernameFeedbackRow"
@@ -170,166 +146,156 @@
             <label class="status" value=" "/>
           </hbox>
         </row>
         <row id="emailRow" align="center">
           <label value="&setup.emailAddress.label;"
                  accesskey="&setup.emailAddress.accesskey;"
                  control="weaveEmail"/>
           <textbox id="weaveEmail"
-                   oninput="gSyncSetup.onEmailChange()"/>
+                   onchange="gSyncSetup.onEmailChange()"/>
         </row>
         <row id="emailFeedbackRow" align="center" hidden="true">
           <spacer/>
           <hbox>
             <image class="statusIcon"/>
             <label class="status" value=" "/>
           </hbox>
         </row>
+        <row align="center">
+          <label control="serverType"
+                 value="&server.label;"/>
+          <menulist id="serverType" oncommand="gSyncSetup.onServerChange()">
+            <menupopup>
+              <menuitem label="&serverType.main.label;"
+                        value="main"/>
+              <menuitem label="&serverType.custom.label;"
+                        value="custom"/>
+            </menupopup>
+          </menulist>
+        </row>
+        <row id="serverRow" hidden="true" align="center">
+          <label value="&signIn.serverURL.label;"
+                 accesskey="&signIn.serverURL.accesskey;"
+                 control="weaveServerURL"/>
+          <textbox id="weaveServerURL" onchange="gSyncSetup.onServerChange()"/>
+        </row>
+        <row id="serverFeedbackRow" align="center" hidden="true">
+          <spacer/>
+          <hbox>
+            <image class="statusIcon"/>
+            <label class="status" value=" "/>
+          </hbox>
+        </row>
         <row id="TOSRow" align="center">
           <spacer/>
-          <hbox align="top">
+          <hbox align="center">
             <checkbox id="tos"
                       accesskey="&setup.tosAgree1.accesskey;"
                       oncommand="gSyncSetup.checkFields();"/>
-            <description id="tosDesc" onclick="document.getElementById('tos').click()">
+            <description id="tosDesc"
+                         onclick="document.getElementById('tos').focus();
+                                  document.getElementById('tos').click()">
               &setup.tosAgree1.label;
               <label class="text-link inline-link"
                      onclick="event.stopPropagation();gSyncUtils.openToS();"
                      value="&setup.tosLink.label;"/>
               &setup.tosAgree2.label;
               <label class="text-link inline-link"
                      onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();"
                      value="&setup.ppLink.label;"/>
               &setup.tosAgree3.label;
             </description>
           </hbox>
         </row>
       </rows>
     </grid>
   </wizardpage>
 
-  <wizardpage label="&setup.newPPPage.title.label;"
+  <wizardpage label="&setup.newSyncKeyPage.title.label;"
+              onextra1="gSyncSetup.onSyncOptions()"
               onpageshow="gSyncSetup.onPageShow();">
     <description>
-      &passphraseDesc.label;
+      &setup.newSyncKeyPage.description.label;
     </description>
     <spacer/>
-    <grid>
-      <columns>
-        <column/>
-        <column flex="1"/>
-      </columns>
-      <rows>
-        <row align="center">
-          <label value="&passphraseEntry.label;"
-                 accesskey="&passphraseEntry.accesskey;"
-                 control="weavePassphrase"/>
-          <textbox type="password" id="weavePassphrase"
-                   onkeyup="gSyncSetup.checkFields()"
-                   onchange="gSyncSetup.checkFields()"/>
-        </row>
-        <row align="center">
-          <label value="&passphraseConfirm.label;"
-                 accesskey="&passphraseConfirm.accesskey;"
-                 control="weavePassphraseConfirm"/>
-          <textbox type="password" id="weavePassphraseConfirm"
-                   onkeyup="gSyncSetup.checkFields()"
-                   onchange="gSyncSetup.checkFields()"/>
-        </row>
-      </rows>
-    </grid>
-    <hbox id="passphraseFeedbackRow" align="center" hidden="true">
-      <spacer/>
+
+    <groupbox>
       <hbox>
+        <label value="&syncKeyEntry.label;"
+               accesskey="&syncKeyEntry.accesskey;"
+               control="weavePassphrase"/>
+        <label id="generatePassphraseButton"
+               value="&syncKeyGenerate.label;"
+               class="text-link inline-link"
+               onclick="event.stopPropagation();
+                        gSyncSetup.onPassphraseGenerate();"/>
+      </hbox>
+      <textbox id="weavePassphrase"
+               onkeyup="gSyncSetup.onPassphraseChange()"
+               onchange="gSyncSetup.onPassphraseChange()"
+               onfocus="this.select()"/>
+      <hbox id="passphraseFeedbackRow" align="center" hidden="true">
+        <spacer/>
+        <hbox>
+          <image class="statusIcon"/>
+          <label class="status" value=" "/>
+        </hbox>
+      </hbox>
+    </groupbox>
+
+    <groupbox align="center">
+      <description>&syncKeyBackup.description;</description>
+      <hbox>
+        <button label="&button.syncKeyBackup.email.label;"
+                accesskey="&button.syncKeyBackup.email.accesskey;"
+                oncommand="gSyncUtils.passphraseEmail('weavePassphrase');
+                           gSyncSetup.afterBackup();"/>
+        <button label="&button.syncKeyBackup.print.label;"
+                accesskey="&button.syncKeyBackup.print.accesskey;"
+                oncommand="gSyncUtils.passphrasePrint('weavePassphrase');
+                           gSyncSetup.afterBackup();"/>
+        <button label="&button.syncKeyBackup.save.label;"
+                accesskey="&button.syncKeyBackup.save.accesskey;"
+                oncommand="gSyncUtils.passphraseSave('weavePassphrase');
+                           gSyncSetup.afterBackup();"/>
+      </hbox>
+    </groupbox>
+  </wizardpage>
+
+  <wizardpage label="&setup.captchaPage2.title.label;"
+              onextra1="gSyncSetup.onSyncOptions()">
+    <vbox flex="1" align="center">
+      <browser height="150"
+               width="450"
+               id="captcha"
+               type="content"
+               disablehistory="true"/>
+      <spacer flex="1"/>
+      <hbox id="captchaFeedback" hidden="true">
         <image class="statusIcon"/>
         <label class="status" value=" "/>
       </hbox>
-    </hbox>
-    <separator/>
-    <description class="small">
-      &passphraseDesc2.label;
-    </description>
-  </wizardpage>
-
-  <wizardpage label="&setup.newAccountPrefs2.title.label;">
-    <grid>
-      <rows>
-        <row align="center">
-          <label value="&syncComputerName.label;"
-                 accesskey="&syncComputerName.accesskey;"
-                 control="syncComputerName"/>
-          <textbox id="syncComputerName" flex="1"
-                   onchange="gSyncUtils.changeName(this)"/>
-        </row>
-        <row align="center">
-      <label value="&syncModeSwitchDesc.label;"
-             accesskey="&syncModeSwitchDesc.accesskey;"
-             control="weaveSyncMode"/>
-      <menulist id="weaveSyncMode"
-                oncommand="gSyncSetup.updateSyncPrefs()">
-        <menupopup>
-          <menuitem label="&syncEverything.label;" value="syncEverything"/>
-          <menuitem label="&customSync.label;"     value="customSync"/>
-        </menupopup>
-      </menulist>
-        </row>
-      </rows>
-    </grid>
-    <separator/>
-    <deck id="syncModeOptions" class="indent">
-      <description id="syncEverythingDesc">
-        &syncEverythingDescription.label;
-      </description>
-      <vbox>
-        <checkbox label="&syncItem.bookmarks.label;"
-                  accesskey="&syncItem.bookmarks.accesskey;"
-                  id="engine.bookmarks"
-                  checked="true"/>
-        <checkbox label="&syncItem.passwords.label;"
-                  accesskey="&syncItem.passwords.accesskey;"
-                  id="engine.passwords"
-                  checked="true"/>
-        <checkbox label="&syncItem.prefs.label;"
-                  accesskey="&syncItem.prefs.accesskey;"
-                  id="engine.prefs"
-                  checked="true"/>
-        <checkbox label="&syncItem.history.label;"
-                  accesskey="&syncItem.history.accesskey;"
-                  id="engine.history"
-                  checked="true"/>
-        <checkbox label="&syncItem.tabs.label;"
-                  accesskey="&syncItem.tabs.accesskey;"
-                  id="engine.tabs"
-                  checked="true"/>
-      </vbox>
-    </deck>
-
-  </wizardpage>
-
-  <wizardpage label="&setup.captchaPage.title.label;">
-    <browser height="150"
-             width="322"
-             id="captcha"
-             type="content"
-             disablehistory="true"/>
+      <spacer flex="3"/>
+    </vbox>
   </wizardpage>
 
   <wizardpage id="useExisting"
               label="&setup.existingAccount.title.label;"
+              onextra1="gSyncSetup.onSyncOptions()"
               onpageshow="gSyncSetup.onPageShow()">
       <grid>
         <columns>
           <column/>
-          <column/>
+          <column class="inputColumn" flex="1"/>
         </columns>
         <rows>
           <row align="center">
             <label control="existingServerType"
-                   value="&connectTo.label;"/>
+                   value="&server.label;"/>
             <menulist id="existingServerType" oncommand="gSyncSetup.onServerChange()">
               <menupopup>
                 <menuitem label="&serverType.main.label;"
                           value="main"/>
                 <menuitem label="&serverType.custom.label;"
                           value="custom"/>
               </menupopup>
             </menulist>
@@ -381,98 +347,137 @@
             <hbox id="connect-throbber" hidden="true">
               <image/>
               <label value="&connecting.label;"/>
           </hbox>
           </row>
         </rows>
       </grid>
   </wizardpage>
-  <wizardpage id="existingPassphraseEntry" label="&passphraseGroupbox.label;">
-    <description>&passphraseDesc3.label;</description>
-    <textbox type="password" id="existingPassphrase"
+
+  <wizardpage id="existingPassphraseEntry"
+              onextra1="gSyncSetup.onSyncOptions()"
+              label="&setup.existingSyncKeyPage.title;">
+    <description>&setup.existingSyncKeyPage.description;</description>
+    <textbox id="existingPassphrase"
              onkeyup="gSyncSetup.checkFields(event)"
              onchange="gSyncSetup.checkFields(event)"/>
     <hbox id="passphrase-throbber" hidden="true">
       <image/>
       <label value="&verifying.label;"/>
     </hbox>
     <hbox align="left" id="existingPassphraseFeedbackBox">
       <spacer/>
       <hbox>
         <image class="statusIcon"/>
         <label class="status" value=" "/>
       </hbox>
     </hbox>
     <vbox class="small" id="passphraseHelpBox" hidden="true">
-      <description class="small">&passphraseHelp.label;</description>
-      <label class="text-link small" value="&changePassphrase.label;"
+      <description class="small">&existingSyncKeyHelp.description;</description>
+      <label class="text-link small" value="&lostSyncKey.label;"
              onclick="gSyncUtils.resetPassphrase(); return false;"/>
     </vbox>
   </wizardpage>
-  <wizardpage id="mergeOptionsChoice"
-              label="&setup.mergeChoicePage.title.label;"
+
+  <wizardpage id="syncOptionsPage"
+              label="&setup.optionsPage.title;"
               onpageshow="gSyncSetup.onPageShow()">
-      <radiogroup id="mergeChoiceRadio" pack="start">
-        <radio id="resetClient"
-               class="mergeChoiceButton"
-               aria-labelledby="mergeMain"
-               aria-describedby="mergeSecondary1 mergeSecondary2"
-               align="top">
-          <image class="mergeChoiceImage"/>
-          <vbox class="mergeChoiceButtonBox" flex="1">
-            <description class="mainDesc" id="mergeMain">
-              &choice.merge.main.label;
-            </description>
-            <separator class="thin"/>
-            <description class="normal" id="mergeSecondary2">
-              &choice.merge.recommend.label;
-            </description>
+    <groupbox id="syncOptions">
+    <grid>
+      <columns>
+        <column/>
+        <column flex="1" style="-moz-margin-end: 2px"/>
+      </columns>
+      <rows>
+        <row align="center">
+          <label value="&syncComputerName.label;"
+                 accesskey="&syncComputerName.accesskey;"
+                 control="syncComputerName"/>
+          <textbox id="syncComputerName" flex="1"
+                   onchange="gSyncUtils.changeName(this)"/>
+        </row>
+        <row>
+          <label value="&syncMy.label;" />
+          <vbox>
+            <checkbox label="&engine.bookmarks.label;"
+                      accesskey="&engine.bookmarks.accesskey;"
+                      id="engine.bookmarks"
+                      checked="true"/>
+            <checkbox label="&engine.passwords.label;"
+                      accesskey="&engine.passwords.accesskey;"
+                      id="engine.passwords"
+                      checked="true"/>
+            <checkbox label="&engine.prefs.label;"
+                      accesskey="&engine.prefs.accesskey;"
+                      id="engine.prefs"
+                      checked="true"/>
+            <checkbox label="&engine.history.label;"
+                      accesskey="&engine.history.accesskey;"
+                      id="engine.history"
+                      checked="true"/>
+            <checkbox label="&engine.tabs.label;"
+                      accesskey="&engine.tabs.accesskey;"
+                      id="engine.tabs"
+                      checked="true"/>
           </vbox>
-        </radio>
-        <radio id="wipeClient"
-               class="mergeChoiceButton"
-               aria-labelledby="wipeClientMain"
-               align="top">
-            <image class="mergeChoiceImage"/>
-            <vbox class="mergeChoiceButtonBox" flex="1">
-              <description class="mainDesc" id="wipeClientMain">
-                &choice.client.main.label;
-              </description>
-            </vbox>
-        </radio>
-        <radio id="wipeRemote"
-               class="mergeChoiceButton"
-               aria-labelledby="wipeServerMain"
-               align="top">
-            <image class="mergeChoiceImage"/>
-            <vbox class="mergeChoiceButtonBox" flex="1">
-              <description class="mainDesc" id="wipeServerMain">
-                &choice.server.main.label;
-              </description>
-            </vbox>
-        </radio>
-    </radiogroup>
+        </row>
+      </rows>
+    </grid>
+    </groupbox>
+
+    <groupbox id="mergeOptions">
+      <radiogroup id="mergeChoiceRadio" pack="start">
+        <grid>
+          <columns>
+            <column/>
+            <column flex="1"/>
+          </columns>
+          <rows>
+            <row align="center">
+              <radio id="resetClient"
+                     class="mergeChoiceButton"
+                     aria-labelledby="resetClientLabel"/>
+              <label id="resetClientLabel" control="resetClient">
+                <html:strong>&choice2.merge.recommended.label;</html:strong>
+                &choice2.merge.main.label;
+              </label>
+            </row>
+            <row align="center">
+              <radio id="wipeClient"
+                     class="mergeChoiceButton"
+                     aria-labelledby="wipeClientLabel"/>
+              <label id="wipeClientLabel"
+                     control="wipeClient"
+                     value="&choice2.client.main.label;"/>
+            </row>
+            <row align="center">
+              <radio id="wipeRemote"
+                     class="mergeChoiceButton"
+                     aria-labelledby="wipeRemoteLabel"/>
+              <label id="wipeRemoteLabel"
+                     control="wipeRemote"
+                     value="&choice2.server.main.label;"/>
+            </row>
+          </rows>
+        </grid>
+      </radiogroup>
+    </groupbox>
   </wizardpage>
-  <wizardpage id="mergeOptionsConfirm" label="&confirm.caption.label;">
+
+  <wizardpage id="syncOptionsConfirm"
+              label="&setup.optionsConfirmPage.title;"
+              onpageshow="gSyncSetup.onPageShow()">
       <deck id="chosenActionDeck">
-        <vbox id="chosenActionMerge">
-          <hbox pack="start" align="baseline">
-            <image/>
-          </hbox>
-          <separator class="thin"/>
+        <vbox id="chosenActionMerge" class="confirm">
           <description class="normal">
             &confirm.merge.label;
           </description>
         </vbox>
-        <vbox id="chosenActionWipeClient">
-          <hbox pack="start" align="baseline">
-            <image/>
-          </hbox>
-          <separator class="thin"/>
+        <vbox id="chosenActionWipeClient" class="confirm">
           <description class="normal">
             &confirm.client.label;
           </description>
           <separator class="thin"/>
           <vbox id="dataList">
             <label class="data indent" id="bookmarkCount"/>
             <label class="data indent" id="historyCount"/>
             <label class="data indent" id="passwordCount"/>
@@ -481,35 +486,43 @@
           <description class="normal">
             &confirm.client.moreinfo.label;
           </description>
           <separator class="thin"/>
           <description class="warning">
             &confirm.client.warning.label;
           </description>
         </vbox>
-        <vbox id="chosenActionWipeServer">
-          <hbox pack="start" align="baseline">
-            <image/>
-          </hbox>
-          <separator class="thin"/>
+        <vbox id="chosenActionWipeServer" class="confirm">
           <description class="normal">
             &confirm.server.label;
           </description>
           <separator class="thin"/>
           <vbox id="clientList">
           </vbox>
           <separator class="thin"/>
           <description class="warning">
             &confirm.server.warning.label;
           </description>
         </vbox>
       </deck>
   </wizardpage>
-  <wizardpage label="&setup.successPage.title.label;" 
+
+  <wizardpage label="&setup.successPage.title;" 
               id="successfulSetup"
+              onextra1="gSyncSetup.onSyncOptions()"
               onpageshow="gSyncSetup.onPageShow()">
+    <vbox align="center">
+      <image id="successPageIcon"/>
+    </vbox>
+    <separator/>
+    <description class="normal">
+      <html:span id="firstSyncAction">replace me</html:span>
+      <html:strong id="firstSyncActionWarning">replace me</html:strong>
+      <html:span id="firstSyncActionChange">replace me</html:span>
+    </description>
     <description>
-      &setup.successPage.desc.label;
+      &continueUsing.label;
     </description>
+    <separator flex="1"/>
   </wizardpage>
 </wizard>
 
--- a/browser/base/content/syncUtils.js
+++ b/browser/base/content/syncUtils.js
@@ -17,16 +17,17 @@
  * Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Edward Lee <edilee@mozilla.com>
  *   Mike Connor <mconnor@mozilla.com>
  *   Paul O’Shannessy <paul@oshannessy.com>
+ *   Philipp von Weitershausen <philipp@weitershausen.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
@@ -34,16 +35,21 @@
  * 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 ***** */
 
 // Weave should always exist before before this file gets included.
 let gSyncUtils = {
+  get bundle() {
+    delete this.bundle;
+    return this.bundle = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
+  },
+
   // opens in a new window if we're in a modal prefwindow world, in a new tab otherwise
   _openLink: function (url) {
     let thisDocEl = document.documentElement,
         openerDocEl = window.opener && window.opener.document.documentElement;
     if (thisDocEl.id == "accountSetup" && window.opener &&
         openerDocEl.id == "BrowserPreferences" && !openerDocEl.instantApply)
       openUILinkIn(url, "window");
     else if (thisDocEl.id == "BrowserPreferences" && !thisDocEl.instantApply)
@@ -105,72 +111,199 @@ let gSyncUtils = {
   },
 
   openAddedClientFirstrun: function () {
     let url = this._baseURL + "secondrun.html";
     this._openLink(url);
   },
 
   /**
-   * validatePassword / validatePassphrase
+   * Generate 20 random characters a-z
+   */
+  generatePassphrase: function() {
+    let rng = Cc["@mozilla.org/security/random-generator;1"]
+                .createInstance(Ci.nsIRandomGenerator);
+    let bytes = rng.generateRandomBytes(20);
+    return [String.fromCharCode(97 + Math.floor(byte * 26 / 256))
+            for each (byte in bytes)].join("");
+  },
+
+  /**
+   * Hyphenate a 20 character passphrase in 4 groups of 5.
+   */
+  hyphenatePassphrase: function(passphrase) {
+    return passphrase.slice(0, 5) + '-'
+         + passphrase.slice(5, 10) + '-'
+         + passphrase.slice(10, 15) + '-'
+         + passphrase.slice(15, 20);
+  },
+
+  /**
+   * Remove hyphens as inserted by hyphenatePassphrase().
+   */
+  normalizePassphrase: function(pp) {
+    if (pp.length == 23 && pp[5] == '-' && pp[11] == '-' && pp[17] == '-')
+      return pp.slice(0, 5) + pp.slice(6, 11)
+           + pp.slice(12, 17) + pp.slice(18, 23);
+    return pp;
+  },
+
+  /**
+   * Trigger the mailto protocol handler to send a passphrase backup email.
+   * 
+   * @param elid : ID of the form element containing the passphrase.
+   */
+  passphraseEmail: function(elid) {
+    let pp = document.getElementById(elid).value;
+    let subject = this.bundle.GetStringFromName("email.synckey.subject");
+    let body = this.bundle.formatStringFromName("email.synckey.body", [pp], 1);
+    let uri = Weave.Utils.makeURI("mailto:?subject=" + subject + "&body=" + body);
+    let protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+                     .getService(Ci.nsIExternalProtocolService);
+    protoSvc.loadURI(uri);
+  },
+
+  /**
+   * Prepare an invisible iframe with the passphrase backup document.
+   * Used by both the print and saving methods.
+   *
+   * @param elid : ID of the form element containing the passphrase.
+   * @param callback : Function called once the iframe has loaded.
+   */
+  _preparePPiframe: function(elid, callback) {
+    let pp = document.getElementById(elid).value;
+
+    // Create an invisible iframe whose contents we can print.
+    let iframe = document.createElement("iframe");
+    iframe.setAttribute("src", "chrome://browser/content/syncKey.xhtml");
+    iframe.collapsed = true;
+    document.documentElement.appendChild(iframe);
+    iframe.contentWindow.addEventListener("load", function() {
+      iframe.contentWindow.removeEventListener("load", arguments.callee, false);
+
+      // Insert the Sync Key into the page.
+      let el = iframe.contentDocument.getElementById("synckey");
+      el.firstChild.nodeValue = pp;
+
+      callback(iframe);
+    }, false);
+  },
+
+  /**
+   * Print passphrase backup document.
+   * 
+   * @param elid : ID of the form element containing the passphrase.
+   */
+  passphrasePrint: function(elid) {
+    this._preparePPiframe(elid, function(iframe) {
+      let webBrowserPrint = iframe.contentWindow
+                                  .QueryInterface(Ci.nsIInterfaceRequestor)
+                                  .getInterface(Ci.nsIWebBrowserPrint);
+      let printSettings = PrintUtils.getPrintSettings();
+
+      // Display no header/footer decoration except for the date.
+      printSettings.headerStrLeft
+        = printSettings.headerStrCenter
+        = printSettings.headerStrRight
+        = printSettings.footerStrLeft
+        = printSettings.footerStrCenter = "";
+      printSettings.footerStrRight = "&D";
+
+      try {
+        webBrowserPrint.print(printSettings, null);
+      } catch (ex) {
+        // print()'s return codes are expressed as exceptions. Ignore.
+      }
+    });
+  },
+
+  /**
+   * Save passphrase backup document to disk as HTML file.
+   * 
+   * @param elid : ID of the form element containing the passphrase.
+   */
+  passphraseSave: function(elid) {
+    let dialogTitle = this.bundle.GetStringFromName("save.synckey.title");
+    this._preparePPiframe(elid, function(iframe) {
+      let filepicker = Cc["@mozilla.org/filepicker;1"]
+                         .createInstance(Ci.nsIFilePicker);
+      filepicker.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
+      filepicker.appendFilters(Ci.nsIFilePicker.filterHTML);
+      filepicker.defaultString = "Firefox Sync Key.html";
+      let rv = filepicker.show();
+      if (rv == Ci.nsIFilePicker.returnOK
+          || rv == Ci.nsIFilePicker.returnReplace) {
+        let stream = Cc["@mozilla.org/network/file-output-stream;1"]
+                       .createInstance(Ci.nsIFileOutputStream);
+        stream.init(filepicker.file, -1, -1, 0);
+
+        let serializer = new XMLSerializer();
+        let output = serializer.serializeToString(iframe.contentDocument);
+        output = Weave.Utils.encodeUTF8(output);
+        stream.write(output, output.length);
+      }
+      return false;
+    });
+  },
+
+  /**
+   * validatePassword
    *
    * @param el1 : the first textbox element in the form
    * @param el2 : the second textbox element, if omitted it's an update form
    * 
    * returns [valid, errorString]
    */
-
   validatePassword: function (el1, el2) {
-    return this._validate(el1, el2, true);
-  },
-
-  validatePassphrase: function (el1, el2) {
-    return this._validate(el1, el2, false);
-  },
-
-  _validate: function (el1, el2, isPassword) {
     let valid = false;
     let val1 = el1.value;
     let val2 = el2 ? el2.value : "";
     let error = "";
 
-    if (isPassword) {
-      if (!el2)
-        valid = val1.length >= Weave.MIN_PASS_LENGTH;
-      else if (val1 && val1 == Weave.Service.username)
-        error = "change.password.pwSameAsUsername";
-      else if (val1 && val1 == Weave.Service.password)
-        error = "change.password.pwSameAsPassword";
-      else if (val1 && val1 == Weave.Service.passphrase)
-        error = "change.password.pwSameAsPassphrase";
-      else if (val1 && val2) {
-        if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH)
-          valid = true;
-        else if (val1.length < Weave.MIN_PASS_LENGTH)
-          error = "change.password.tooShort";
-        else if (val1 != val2)
-          error = "change.password.mismatch";
-      }
-    }
-    else {
-      if (!el2)
-        valid = val1.length >= Weave.MIN_PP_LENGTH;
-      else if (val1 == Weave.Service.username)
-        error = "change.passphrase.ppSameAsUsername";
-      else if (val1 == Weave.Service.password)
-        error = "change.passphrase.ppSameAsPassword";
-      else if (val1 == Weave.Service.passphrase)
-        error = "change.passphrase.ppSameAsPassphrase";
-      else if (val1 && val2) {
-        if (val1 == val2 && val1.length >= Weave.MIN_PP_LENGTH)
-          valid = true;
-        else if (val1.length < Weave.MIN_PP_LENGTH)
-          error = "change.passphrase.tooShort";
-        else if (val1 != val2)
-          error = "change.passphrase.mismatch";
-      }
+    if (!el2)
+      valid = val1.length >= Weave.MIN_PASS_LENGTH;
+    else if (val1 && val1 == Weave.Service.username)
+      error = "change.password.pwSameAsUsername";
+    else if (val1 && val1 == Weave.Service.password)
+      error = "change.password.pwSameAsPassword";
+    else if (val1 && val1 == Weave.Service.passphrase)
+      error = "change.password.pwSameAsSyncKey";
+    else if (val1 && val2) {
+      if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH)
+        valid = true;
+      else if (val1.length < Weave.MIN_PASS_LENGTH)
+        error = "change.password.tooShort";
+      else if (val1 != val2)
+        error = "change.password.mismatch";
     }
     let errorString = error ? Weave.Utils.getErrorString(error) : "";
-    dump("valid: " + valid + " error: " + errorString + "\n");
+    return [valid, errorString];
+  },
+
+  /**
+   * validatePassphrase
+   *
+   * @param el : the textbox element
+   * @param change : indicate whether this signifies a passphrase change
+   * 
+   * returns [valid, errorString]
+   */
+  validatePassphrase: function (el, change) {
+    let valid = false;
+    let val = el.value;
+    let error = "";
+
+    if (val == Weave.Service.username)
+      error = "change.synckey.sameAsUsername";
+    else if (val == Weave.Service.password)
+      error = "change.synckey.sameAsPassword";
+    else if (change && val == Weave.Service.passphrase)
+      error = "change.synckey.sameAsSyncKey";
+    else if (val.length < Weave.MIN_PP_LENGTH)
+      error = "change.synckey.tooShort";
+    else
+      valid = true;
+
+    let errorString = error ? Weave.Utils.getErrorString(error) : "";
     return [valid, errorString];
   }
 }
 
--- a/browser/base/content/tabview/drag.js
+++ b/browser/base/content/tabview/drag.js
@@ -56,17 +56,17 @@ var drag = {
 // Called to create a Drag in response to an <Item> draggable "start" event.
 // Note that it is also used partially during <Item>'s resizable method as well.
 //
 // Parameters:
 //   item - The <Item> being dragged
 //   event - The DOM event that kicks off the drag
 //   isResizing - (boolean) is this a resizing instance? or (if false) dragging?
 //   isFauxDrag - (boolean) true if a faux drag, which is used when simply snapping.
-var Drag = function(item, event, isResizing, isFauxDrag) {
+function Drag(item, event, isResizing, isFauxDrag) {
   Utils.assert(item && (item.isAnItem || item.isAFauxItem), 
       'must be an item, or at least a faux item');
 
   this.isResizing = isResizing || false;
   this.item = item;
   this.el = item.container;
   this.$el = iQ(this.el);
   this.parent = this.item.parent;
@@ -252,32 +252,32 @@ Drag.prototype = {
       return rect;
     }
     return false;
   },
 
   // ----------
   // Function: drag
   // Called in response to an <Item> draggable "drag" event.
-  drag: function(event) {
+  drag: function Drag_drag(event) {
     this.snap('topleft', true);
 
     if (this.parent && this.parent.expanded) {
       var distance = this.startPosition.distance(new Point(event.clientX, event.clientY));
       if (distance > 100) {
         this.parent.remove(this.item);
         this.parent.collapse();
       }
     }
   },
 
   // ----------
   // Function: stop
   // Called in response to an <Item> draggable "stop" event.
-  stop: function() {
+  stop: function Drag_stop() {
     Trenches.hideGuides();
     this.item.isDragging = false;
 
     if (this.parent && !this.parent.locked.close && this.parent != this.item.parent &&
        this.parent.isEmpty()) {
       this.parent.close();
     }
 
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -58,17 +58,17 @@
 // Possible options:
 //   id - specifies the groupItem's id; otherwise automatically generated
 //   locked - see <Item.locked>; default is {}
 //   userSize - see <Item.userSize>; default is null
 //   bounds - a <Rect>; otherwise based on the locations of the provided elements
 //   container - a DOM element to use as the container for this groupItem; otherwise will create
 //   title - the title for the groupItem; otherwise blank
 //   dontPush - true if this groupItem shouldn't push away on creation; default is false
-let GroupItem = function GroupItem(listOfEls, options) {
+function GroupItem(listOfEls, options) {
   try {
   if (typeof options == 'undefined')
     options = {};
 
   this._inited = false;
   this._children = []; // an array of Items
   this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
   this.isAGroupItem = true;
@@ -287,41 +287,41 @@ let GroupItem = function GroupItem(listO
   this.save();
   } catch(e) {
     Utils.log("Error in GroupItem()");
     Utils.log(e.stack);
   }
 };
 
 // ----------
-window.GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
+GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
   // ----------
   // Variable: defaultName
   // The prompt text for the title field.
   defaultName: "Name this tab group…",
 
   // -----------
   // Function: setActiveTab
   // Sets the active <TabItem> for this groupItem
-  setActiveTab: function(tab) {
+  setActiveTab: function GroupItem_setActiveTab(tab) {
     Utils.assert(tab && tab.isATabItem, 'tab must be a TabItem');
     this._activeTab = tab;
   },
 
   // -----------
   // Function: getActiveTab
   // Gets the active <TabItem> for this groupItem
-  getActiveTab: function() {
+  getActiveTab: function GroupItem_getActiveTab() {
     return this._activeTab;
   },
 
   // ----------
   // Function: getStorageData
   // Returns all of the info worth storing about this groupItem.
-  getStorageData: function() {
+  getStorageData: function GroupItem_getStorageData() {
     var data = {
       bounds: this.getBounds(),
       userSize: null,
       locked: Utils.copy(this.locked),
       title: this.getTitle(),
       id: this.id
     };
 
@@ -329,67 +329,67 @@ window.GroupItem.prototype = Utils.exten
       data.userSize = new Point(this.userSize);
 
     return data;
   },
 
   // ----------
   // Function: isEmpty
   // Returns true if the tab groupItem is empty and unnamed.
-  isEmpty: function() {
+  isEmpty: function GroupItem_isEmpty() {
     return !this._children.length && !this.getTitle();
   },
 
   // ----------
   // Function: save
   // Saves this groupItem to persistent storage.
-  save: function() {
+  save: function GroupItem_save() {
     if (!this._inited) // too soon to save now
       return;
 
     var data = this.getStorageData();
     if (GroupItems.groupItemStorageSanity(data))
       Storage.saveGroupItem(gWindow, data);
   },
 
   // ----------
   // Function: getTitle
   // Returns the title of this groupItem as a string.
-  getTitle: function() {
+  getTitle: function GroupItem_getTitle() {
     var value = (this.$title ? this.$title.val() : '');
     return (value == this.defaultName ? '' : value);
   },
 
   // ----------
   // Function: setTitle
   // Sets the title of this groupItem with the given string
-  setTitle: function(value) {
+  setTitle: function GroupItem_setTitle(value) {
     this.$title.val(value);
     this.save();
   },
 
   // ----------
   // Function: adjustTitleSize
   // Used to adjust the width of the title box depending on groupItem width and title size.
-  adjustTitleSize: function() {
+  adjustTitleSize: function GroupItem_adjustTitleSize() {
     Utils.assert(this.bounds, 'bounds needs to have been set');
     let closeButton = iQ('.close', this.container);
     var w = Math.min(this.bounds.width - parseInt(closeButton.width()) - parseInt(closeButton.css('right')),
                      Math.max(150, this.getTitle().length * 6));
     // The * 6 multiplier calculation is assuming that characters in the title
     // are approximately 6 pixels wide. Bug 586545
     var css = {width: w};
     this.$title.css(css);
     this.$titleShield.css(css);
   },
 
   // ----------
   // Function: getContentBounds
   // Returns a <Rect> for the groupItem's content area (which doesn't include the title, etc).
-  getContentBounds: function() {
+  getContentBounds: function GroupItem_getContentBounds() {
     var box = this.getBounds();
     var titleHeight = this.$titlebar.height();
     box.top += titleHeight;
     box.height -= titleHeight;
 
     // Make the computed bounds' "padding" and new tab button margin actually be
     // themeable --OR-- compute this from actual bounds. Bug 586546
     box.inset(6, 6);
@@ -404,17 +404,17 @@ window.GroupItem.prototype = Utils.exten
   //
   // Parameters:
   //   rect - a <Rect> giving the new bounds
   //   immediately - true if it should not animate; default false
   //   options - an object with additional parameters, see below
   //
   // Possible options:
   //   force - true to always update the DOM even if the bounds haven't changed; default false
-  setBounds: function(rect, immediately, options) {
+  setBounds: function GroupItem_setBounds(rect, immediately, options) {
     if (!Utils.isRect(rect)) {
       Utils.trace('GroupItem.setBounds: rect is not a real rectangle!', rect);
       return;
     }
 
     if (!options)
       options = {};
 
@@ -486,17 +486,17 @@ window.GroupItem.prototype = Utils.exten
     this.setTrenches(rect);
 
     this.save();
   },
 
   // ----------
   // Function: setZ
   // Set the Z order for the groupItem's container, as well as its children.
-  setZ: function(value) {
+  setZ: function GroupItem_setZ(value) {
     this.zIndex = value;
 
     iQ(this.container).css({zIndex: value});
 
     if (this.$debug)
       this.$debug.css({zIndex: value + 1});
 
     var count = this._children.length;
@@ -513,33 +513,33 @@ window.GroupItem.prototype = Utils.exten
         }
       });
     }
   },
 
   // ----------
   // Function: close
   // Closes the groupItem, removing (but not closing) all of its children.
-  close: function() {
+  close: function GroupItem_close() {
     this.removeAll();
     GroupItems.unregister(this);
     this._sendToSubscribers("close");
     this.removeTrenches();
     iQ(this.container).fadeOut(function() {
       iQ(this).remove();
       Items.unsquish();
     });
 
     Storage.deleteGroupItem(gWindow, this.id);
   },
 
   // ----------
   // Function: closeAll
   // Closes the groupItem and all of its children.
-  closeAll: function() {
+  closeAll: function GroupItem_closeAll() {
     var self = this;
     if (this._children.length) {
       var toClose = this._children.concat();
       toClose.forEach(function(child) {
         child.removeSubscriber(self, "close");
         child.close();
       });
     }
@@ -552,17 +552,17 @@ window.GroupItem.prototype = Utils.exten
   // Function: add
   // Adds an item to the groupItem.
   // Parameters:
   //
   //   a - The item to add. Can be an <Item>, a DOM element or an iQ object.
   //       The latter two must refer to the container of an <Item>.
   //   dropPos - An object with left and top properties referring to the location dropped at.  Optional.
   //   options - An object with optional settings for this call. Currently the only one is dontArrange.
-  add: function(a, dropPos, options) {
+  add: function GroupItem_add(a, dropPos, options) {
     try {
       var item;
       var $el;
       if (a.isAnItem) {
         item = a;
         $el = iQ(a.container);
       } else {
         $el = iQ(a);
@@ -664,17 +664,17 @@ window.GroupItem.prototype = Utils.exten
   // ----------
   // Function: remove
   // Removes an item from the groupItem.
   // Parameters:
   //
   //   a - The item to remove. Can be an <Item>, a DOM element or an iQ object.
   //       The latter two must refer to the container of an <Item>.
   //   options - An object with optional settings for this call. Currently the only one is dontArrange.
-  remove: function(a, options) {
+  remove: function GroupItem_remove(a, options) {
     try {
       var $el;
       var item;
 
       if (a.isAnItem) {
         item = a;
         $el = iQ(item.container);
       } else {
@@ -709,35 +709,35 @@ window.GroupItem.prototype = Utils.exten
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: removeAll
   // Removes all of the groupItem's children.
-  removeAll: function() {
+  removeAll: function GroupItem_removeAll() {
     var self = this;
     var toRemove = this._children.concat();
     toRemove.forEach(function(child) {
       self.remove(child, {dontArrange: true});
     });
   },
 
   // ----------
   // Function: hideExpandControl
   // Hide the control which expands a stacked groupItem into a quick-look view.
-  hideExpandControl: function() {
+  hideExpandControl: function GroupItem_hideExpandControl() {
     this.$expander.hide();
   },
 
   // ----------
   // Function: showExpandControl
   // Show the control which expands a stacked groupItem into a quick-look view.
-  showExpandControl: function() {
+  showExpandControl: function GroupItem_showExpandControl() {
     var childBB = this.getChild(0).getBounds();
     var dT = childBB.top - this.getBounds().top;
     var dL = childBB.left - this.getBounds().left;
 
     this.$expander
         .show()
         .css({
           opacity: .2,
@@ -747,17 +747,17 @@ window.GroupItem.prototype = Utils.exten
           // It has to impress the ladies somehow. Bug 586549
           left: dL + childBB.width/2 - this.$expander.width()/2 - 6,
         });
   },
 
   // ----------
   // Function: shouldStack
   // Returns true if the groupItem, given "count", should stack (instead of grid).
-  shouldStack: function(count) {
+  shouldStack: function GroupItem_shouldStack(count) {
     if (count <= 1)
       return false;
 
     var bb = this.getContentBounds();
     var options = {
       pretend: true,
       count: count
     };
@@ -767,17 +767,17 @@ window.GroupItem.prototype = Utils.exten
   },
 
   // ----------
   // Function: arrange
   // Lays out all of the children.
   //
   // Parameters:
   //   options - passed to <Items.arrange> or <_stackArrange>
-  arrange: function(options) {
+  arrange: function GroupItem_arrange(options) {
     if (this.expanded) {
       this.topChild = null;
       var box = new Rect(this.expanded.bounds);
       box.inset(8, 8);
       Items.arrange(this._children, box, Utils.extend({}, options, {padding: 8, z: 99999}));
     } else {
       var bb = this.getContentBounds();
       var count = this._children.length;
@@ -852,17 +852,17 @@ window.GroupItem.prototype = Utils.exten
   // Arranges the children in a stack.
   //
   // Parameters:
   //   bb - <Rect> to arrange within
   //   options - see below
   //
   // Possible "options" properties:
   //   animate - whether to animate; default: true.
-  _stackArrange: function(bb, options) {
+  _stackArrange: function GroupItem__stackArrange(bb, options) {
     var animate;
     if (!options || typeof options.animate == 'undefined')
       animate = true;
     else
       animate = options.animate;
 
     if (typeof options == 'undefined')
       options = {};
@@ -924,17 +924,17 @@ window.GroupItem.prototype = Utils.exten
     });
 
     self._isStacked = true;
   },
 
   // ----------
   // Function: _randRotate
   // Random rotation generator for <_stackArrange>
-  _randRotate: function(spread, index) {
+  _randRotate: function GroupItem__randRotate(spread, index) {
     if (index >= this._stackAngles.length) {
       var randAngle = 5*index + parseInt((Math.random()-.5)*1);
       this._stackAngles.push(randAngle);
       return randAngle;
     }
 
     if (index > 5) index = 5;
 
@@ -943,34 +943,34 @@ window.GroupItem.prototype = Utils.exten
 
   // ----------
   // Function: childHit
   // Called by one of the groupItem's children when the child is clicked on.
   //
   // Returns an object:
   //   shouldZoom - true if the browser should launch into the tab represented by the child
   //   callback - called after the zoom animation is complete
-  childHit: function(child) {
+  childHit: function GroupItem_childHit(child) {
     var self = this;
 
     // ___ normal click
     if (!this._isStacked || this.expanded) {
       return {
         shouldZoom: true,
         callback: function() {
           self.collapse();
         }
       };
     }
 
     GroupItems.setActiveGroupItem(self);
     return { shouldZoom: true };
   },
 
-  expand: function() {
+  expand: function GroupItem_expand() {
     var self = this;
     // ___ we're stacked, and command is held down so expand
     GroupItems.setActiveGroupItem(self);
     var startBounds = this.getChild(0).getBounds();
     var $tray = iQ("<div>").css({
       top: startBounds.top,
       left: startBounds.left,
       width: startBounds.width,
@@ -1046,17 +1046,17 @@ window.GroupItem.prototype = Utils.exten
     };
 
     this.arrange();
   },
 
   // ----------
   // Function: collapse
   // Collapses the groupItem from the expanded "tray" mode.
-  collapse: function() {
+  collapse: function GroupItem_collapse() {
     if (this.expanded) {
       var z = this.getZ();
       var box = this.getBounds();
       this.expanded.$tray
         .css({
           zIndex: z + 1
         })
         .animate({
@@ -1082,17 +1082,17 @@ window.GroupItem.prototype = Utils.exten
 
       this.arrange({z: z + 2});
     }
   },
 
   // ----------
   // Function: _addHandlers
   // Helper routine for the constructor; adds various event handlers to the container.
-  _addHandlers: function(container) {
+  _addHandlers: function GroupItem__addHandlers(container) {
     var self = this;
 
     this.dropOptions.over = function() {
       iQ(this.container).addClass("acceptsDrop");
     };
     this.dropOptions.drop = function(event) {
       iQ(this.container).removeClass("acceptsDrop");
       this.add(drag.info.$el, {left:event.pageX, top:event.pageY});
@@ -1146,33 +1146,33 @@ window.GroupItem.prototype = Utils.exten
     this.$expander.click(function() {
       self.expand();
     });
   },
 
   // ----------
   // Function: setResizable
   // Sets whether the groupItem is resizable and updates the UI accordingly.
-  setResizable: function(value) {
+  setResizable: function GroupItem_setResizable(value) {
     this.resizeOptions.minWidth = 90;
     this.resizeOptions.minHeight = 90;
 
     if (value) {
       this.$resizer.fadeIn();
       this.resizable(true);
     } else {
       this.$resizer.fadeOut();
       this.resizable(false);
     }
   },
 
   // ----------
   // Function: newTab
   // Creates a new tab within this groupItem.
-  newTab: function(url) {
+  newTab: function GroupItem_newTab(url) {
     GroupItems.setActiveGroupItem(this);
     let newTab = gBrowser.loadOneTab(url || "about:blank", {inBackground: true});
 
     // TabItems will have handled the new tab and added the tabItem property
     let newItem = newTab.tabItem;
 
     var self = this;
     iQ(newItem.container).css({opacity: 0});
@@ -1209,27 +1209,27 @@ window.GroupItem.prototype = Utils.exten
   },
 
   // ----------
   // Function: reorderTabItemsBasedOnTabOrder
   // Reorders the tabs in a groupItem based on the arrangment of the tabs
   // shown in the tab bar. It does it by sorting the children
   // of the groupItem by the positions of their respective tabs in the
   // tab bar.
-  reorderTabItemsBasedOnTabOrder: function() {
+  reorderTabItemsBasedOnTabOrder: function GroupItem_reorderTabItemsBasedOnTabOrder() {
     this._children.sort(function(a,b) a.tab._tPos - b.tab._tPos);
 
     this.arrange({animate: false});
     // this.arrange calls this.save for us
   },
 
   // Function: reorderTabsBasedOnTabItemOrder
   // Reorders the tabs in the tab bar based on the arrangment of the tabs
   // shown in the groupItem.
-  reorderTabsBasedOnTabItemOrder: function() {
+  reorderTabsBasedOnTabItemOrder: function GroupItem_reorderTabsBasedOnTabItemOrder() {
     var tabBarTabs = Array.slice(gBrowser.tabs);
     var currentIndex;
 
     // ToDo: optimisation is needed to further reduce the tab move.
     // Bug 586553
     this._children.forEach(function(tabItem) {
       tabBarTabs.some(function(tab, i) {
         if (tabItem.tab == tab) {
@@ -1252,103 +1252,103 @@ window.GroupItem.prototype = Utils.exten
         return false;
       });
     });
   },
 
   // ----------
   // Function: setTopChild
   // Sets the <Item> that should be displayed on top when in stack mode.
-  setTopChild: function(topChild) {
+  setTopChild: function GroupItem_setTopChild(topChild) {
     this.topChild = topChild;
 
     this.arrange({animate: false});
     // this.arrange calls this.save for us
   },
 
   // ----------
   // Function: getChild
   // Returns the nth child tab or null if index is out of range.
   //
   // Parameters:
   //  index - the index of the child tab to return, use negative
   //          numbers to index from the end (-1 is the last child)
-  getChild: function(index) {
+  getChild: function GroupItem_getChild(index) {
     if (index < 0)
       index = this._children.length + index;
     if (index >= this._children.length || index < 0)
       return null;
     return this._children[index];
   },
 
   // ----------
   // Function: getChildren
   // Returns all children.
-  getChildren: function() {
+  getChildren: function GroupItem_getChildren() {
     return this._children;
   }
 });
 
 // ##########
 // Class: GroupItems
 // Singelton for managing all <GroupItem>s.
-window.GroupItems = {
+let GroupItems = {
   groupItems: [],
   nextID: 1,
   _inited: false,
   _activeGroupItem: null,
   _activeOrphanTab: null,
 
   // ----------
   // Function: init
-  init: function() {
+  init: function GroupItems_init() {
   },
 
   // ----------
   // Function: uninit
-  uninit : function() {
+  uninit : function GroupItems_uninit () {
     this.groupItems = null;
   },
 
   // ----------
   // Function: getNextID
   // Returns the next unused groupItem ID.
-  getNextID: function() {
+  getNextID: function GroupItems_getNextID() {
     var result = this.nextID;
     this.nextID++;
     this.save();
     return result;
   },
 
   // ----------
   // Function: getStorageData
   // Returns an object for saving GroupItems state to persistent storage.
-  getStorageData: function() {
+  getStorageData: function GroupItems_getStorageData() {
     var data = {nextID: this.nextID, groupItems: []};
     this.groupItems.forEach(function(groupItem) {
       data.groupItems.push(groupItem.getStorageData());
     });
 
     return data;
   },
 
   // ----------
   // Function: saveAll
   // Saves GroupItems state, as well as the state of all of the groupItems.
-  saveAll: function() {
+  saveAll: function GroupItems_saveAll() {
     this.save();
     this.groupItems.forEach(function(groupItem) {
       groupItem.save();
     });
   },
 
   // ----------
   // Function: save
   // Saves GroupItems state.
-  save: function() {
+  save: function GroupItems_save() {
     if (!this._inited) // too soon to save now
       return;
 
     Storage.saveGroupItemsData(gWindow, {nextID:this.nextID});
   },
 
   // ----------
   // Function: getBoundingBox
@@ -1362,17 +1362,17 @@ window.GroupItems = {
 
     return new Rect(left, top, right-left, bottom-top);
   },
 
   // ----------
   // Function: reconstitute
   // Restores to stored state, creating groupItems as needed.
   // If no data, sets up blank slate (including "new tabs" groupItem).
-  reconstitute: function(groupItemsData, groupItemData) {
+  reconstitute: function GroupItems_reconstitute(groupItemsData, groupItemData) {
     try {
       if (groupItemsData && groupItemsData.nextID)
         this.nextID = groupItemsData.nextID;
 
       if (groupItemData) {
         for (var id in groupItemData) {
           var groupItem = groupItemData[id];
           if (this.groupItemStorageSanity(groupItem)) {
@@ -1390,82 +1390,82 @@ window.GroupItems = {
     } catch(e) {
       Utils.log("error in recons: "+e);
     }
   },
 
   // ----------
   // Function: groupItemStorageSanity
   // Given persistent storage data for a groupItem, returns true if it appears to not be damaged.
-  groupItemStorageSanity: function(groupItemData) {
+  groupItemStorageSanity: function GroupItems_groupItemStorageSanity(groupItemData) {
     // TODO: check everything
     // Bug 586555
     var sane = true;
     if (!Utils.isRect(groupItemData.bounds)) {
       Utils.log('GroupItems.groupItemStorageSanity: bad bounds', groupItemData.bounds);
       sane = false;
     }
 
     return sane;
   },
 
   // ----------
   // Function: getGroupItemWithTitle
   // Returns the <GroupItem> that has the given title, or null if none found.
   // TODO: what if there are multiple groupItems with the same title??
   //       Right now, looks like it'll return the last one. Bug 586557
-  getGroupItemWithTitle: function(title) {
+  getGroupItemWithTitle: function GroupItems_getGroupItemWithTitle(title) {
     var result = null;
     this.groupItems.forEach(function(groupItem) {
       if (groupItem.getTitle() == title)
         result = groupItem;
     });
 
     return result;
   },
 
   // ----------
   // Function: register
   // Adds the given <GroupItem> to the list of groupItems we're tracking.
-  register: function(groupItem) {
+  register: function GroupItems_register(groupItem) {
     Utils.assert(groupItem, 'groupItem');
     Utils.assert(this.groupItems.indexOf(groupItem) == -1, 'only register once per groupItem');
     this.groupItems.push(groupItem);
   },
 
   // ----------
   // Function: unregister
   // Removes the given <GroupItem> from the list of groupItems we're tracking.
-  unregister: function(groupItem) {
+  unregister: function GroupItems_unregister(groupItem) {
     var index = this.groupItems.indexOf(groupItem);
     if (index != -1)
       this.groupItems.splice(index, 1);
 
     if (groupItem == this._activeGroupItem)
       this._activeGroupItem = null;
   },
 
   // ----------
   // Function: groupItem
   // Given some sort of identifier, returns the appropriate groupItem.
   // Currently only supports groupItem ids.
-  groupItem: function(a) {
+  groupItem: function GroupItems_groupItem(a) {
     var result = null;
     this.groupItems.forEach(function(candidate) {
       if (candidate.id == a)
         result = candidate;
     });
 
     return result;
   },
 
   // ----------
   // Function: arrange
   // Arranges all of the groupItems into a grid.
-  arrange: function() {
+  arrange: function GroupItems_arrange() {
     var bounds = Items.getPageBounds();
     bounds.bottom -= 20; // for the dev menu
 
     var count = this.groupItems.length - 1;
     var columns = Math.ceil(Math.sqrt(count));
     var rows = ((columns * columns) - count >= columns ? columns - 1 : columns);
     var padding = 12;
     var startX = bounds.left + padding;
@@ -1490,27 +1490,27 @@ window.GroupItems = {
         box.top += box.height + padding;
       }
     });
   },
 
   // ----------
   // Function: removeAll
   // Removes all tabs from all groupItems (which automatically closes all unnamed groupItems).
-  removeAll: function() {
+  removeAll: function GroupItems_removeAll() {
     var toRemove = this.groupItems.concat();
     toRemove.forEach(function(groupItem) {
       groupItem.removeAll();
     });
   },
 
   // ----------
   // Function: newTab
   // Given a <TabItem>, files it in the appropriate groupItem.
-  newTab: function(tabItem) {
+  newTab: function GroupItems_newTab(tabItem) {
     let activeGroupItem = this.getActiveGroupItem();
     let orphanTab = this.getActiveOrphanTab();
 //    Utils.log('newTab', activeGroupItem, orphanTab);
     if (activeGroupItem) {
       activeGroupItem.add(tabItem);
     } else if (orphanTab) {
       let newGroupItemBounds = orphanTab.getBoundsWithTitle();
       newGroupItemBounds.inset(-40,-40);
@@ -1523,46 +1523,46 @@ window.GroupItems = {
   },
 
   // ----------
   // Function: positionNewTabAtBottom
   // Does what it says on the tin.
   // TODO: Make more robust and improve documentation,
   // Also, this probably belongs in tabitems.js
   // Bug 586558
-  positionNewTabAtBottom: function(tabItem) {
+  positionNewTabAtBottom: function GroupItems_positionNewTabAtBottom(tabItem) {
     let windowBounds = Items.getSafeWindowBounds();
 
     let itemBounds = new Rect(
       windowBounds.right - TabItems.tabWidth,
       windowBounds.bottom - TabItems.tabHeight,
       TabItems.tabWidth,
       TabItems.tabHeight
     );
 
     tabItem.setBounds(itemBounds);
   },
 
   // ----------
   // Function: getActiveGroupItem
   // Returns the active groupItem. Active means its tabs are
   // shown in the tab bar when not in the TabView interface.
-  getActiveGroupItem: function() {
+  getActiveGroupItem: function GroupItems_getActiveGroupItem() {
     return this._activeGroupItem;
   },
 
   // ----------
   // Function: setActiveGroupItem
   // Sets the active groupItem, thereby showing only the relevent tabs, and
   // setting the groupItem which will receive new tabs.
   //
   // Paramaters:
   //  groupItem - the active <GroupItem> or <null> if no groupItem is active
   //          (which means we have an orphaned tab selected)
-  setActiveGroupItem: function(groupItem) {
+  setActiveGroupItem: function GroupItems_setActiveGroupItem(groupItem) {
 
     if (this._activeGroupItem)
       iQ(this._activeGroupItem.container).removeClass('activeGroupItem');
 
     if (groupItem !== null) {
       if (groupItem)
         iQ(groupItem.container).addClass('activeGroupItem');
       // if a groupItem is active, we surely are not in an orphaned tab.
@@ -1570,81 +1570,81 @@ window.GroupItems = {
     }
 
     this._activeGroupItem = groupItem;
   },
 
   // ----------
   // Function: getActiveOrphanTab
   // Returns the active orphan tab, in cases when there is no active groupItem.
-  getActiveOrphanTab: function() {
+  getActiveOrphanTab: function GroupItems_getActiveOrphanTab() {
     return this._activeOrphanTab;
   },
 
   // ----------
   // Function: setActiveOrphanTab
   // In cases where an orphan tab (not in a groupItem) is active by itself,
   // this function is called and the "active orphan tab" is set.
   //
   // Paramaters:
   //  groupItem - the active <TabItem> or <null>
-  setActiveOrphanTab: function(tabItem) {
+  setActiveOrphanTab: function GroupItems_setActiveOrphanTab(tabItem) {
     this._activeOrphanTab = tabItem;
   },
 
   // ----------
   // Function: _updateTabBar
   // Hides and shows tabs in the tab bar based on the active groupItem or
   // currently active orphan tabItem
-  _updateTabBar: function() {
+  _updateTabBar: function GroupItems__updateTabBar() {
     if (!window.UI)
       return; // called too soon
 
     if (!this._activeGroupItem && !this._activeOrphanTab) {
       Utils.assert(false, "There must be something to show in the tab bar!");
       return;
     }
 
     let tabItems = this._activeGroupItem == null ?
       [this._activeOrphanTab] : this._activeGroupItem._children;
     gBrowser.showOnlyTheseTabs(tabItems.map(function(item) item.tab));
   },
 
   // ----------
   // Function: updateActiveGroupItemAndTabBar
   // Sets active group item and updates tab bar
-  updateActiveGroupItemAndTabBar: function(tabItem) {
+  updateActiveGroupItemAndTabBar: function GroupItems_updateActiveGroupItemAndTabBar(tabItem) {
     if (tabItem.parent) {
       let groupItem = tabItem.parent;
       this.setActiveGroupItem(groupItem);
       groupItem.setActiveTab(tabItem);
     } else {
       this.setActiveGroupItem(null);
       this.setActiveOrphanTab(tabItem);
     }
     this._updateTabBar();
   },
 
   // ----------
   // Function: getOrphanedTabs
   // Returns an array of all tabs that aren't in a groupItem.
-  getOrphanedTabs: function() {
+  getOrphanedTabs: function GroupItems_getOrphanedTabs() {
     var tabs = TabItems.getItems();
     tabs = tabs.filter(function(tab) {
       return tab.parent == null;
     });
     return tabs;
   },
 
   // ----------
   // Function: getNextGroupItemTab
   // Paramaters:
   //  reverse - the boolean indicates the direction to look for the next groupItem.
   // Returns the <tabItem>. If nothing is found, return null.
-  getNextGroupItemTab: function(reverse) {
+  getNextGroupItemTab: function GroupItems_getNextGroupItemTab(reverse) {
     var groupItems = Utils.copy(GroupItems.groupItems);
     var activeGroupItem = GroupItems.getActiveGroupItem();
     var activeOrphanTab = GroupItems.getActiveOrphanTab();
     var tabItem = null;
 
     if (reverse)
       groupItems = groupItems.reverse();
 
@@ -1697,17 +1697,17 @@ window.GroupItems = {
     return tabItem;
   },
 
   // ----------
   // Function: moveTabToGroupItem
   // Paramaters:
   //  tab - the <xul:tab>.
   //  groupItemId - the <groupItem>'s id.  If nothing, create a new <groupItem>.
-  moveTabToGroupItem : function(tab, groupItemId) {
+  moveTabToGroupItem : function GroupItems_moveTabToGroupItem (tab, groupItemId) {
     let shouldUpdateTabBar = false;
     let shouldShowTabView = false;
     let groupItem;
 
     // switch to the appropriate tab first.
     if (gBrowser.selectedTab == tab) {
       let list = gBrowser.visibleTabs;
       let listLength = list.length;
@@ -1751,17 +1751,17 @@ window.GroupItems = {
       tab.tabItem.setZoomPrep(false);
       UI.showTabView();
     }
   },
 
   // ----------
   // Function: killNewTabGroup
   // Removes the New Tab Group, which is now defunct. See bug 575851 and comments therein.
-  killNewTabGroup: function() {
+  killNewTabGroup: function GroupItems_killNewTabGroup() {
     let newTabGroupTitle = "New Tabs";
     this.groupItems.forEach(function(groupItem) {
       if (groupItem.getTitle() == newTabGroupTitle && groupItem.locked.title) {
         groupItem.removeAll();
         groupItem.close();
       }
     });
   }
--- a/browser/base/content/tabview/infoitems.js
+++ b/browser/base/content/tabview/infoitems.js
@@ -36,34 +36,32 @@
  * 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 ***** */
 
 // **********
 // Title: infoitems.js
 
-(function() {
-
 // ##########
 // Class: InfoItem
 // An <Item> in TabView used for displaying information, such as the welcome video.
 // Note that it implements the <Subscribable> interface.
 //
 // ----------
 // Constructor: InfoItem
 //
 // Parameters:
 //   bounds - a <Rect> for where the item should be located
 //   options - various options for this infoItem (see below)
 //
 // Possible options:
 //   locked - see <Item.locked>; default is {}
 //   dontPush - true if this infoItem shouldn't push away on creation; default is false
-window.InfoItem = function(bounds, options) {
+function InfoItem(bounds, options) {
   try {
     Utils.assertThrow(Utils.isRect(bounds), 'bounds');
 
     if (typeof options == 'undefined')
       options = {};
 
     this._inited = false;
     this.isAnInfoItem = true;
@@ -116,22 +114,22 @@ window.InfoItem = function(bounds, optio
     this._inited = true;
     this.save();
   } catch(e) {
     Utils.log(e);
   }
 };
 
 // ----------
-window.InfoItem.prototype = Utils.extend(new Item(), new Subscribable(), {
+InfoItem.prototype = Utils.extend(new Item(), new Subscribable(), {
 
   // ----------
   // Function: getStorageData
   // Returns all of the info worth storing about this item.
-  getStorageData: function() {
+  getStorageData: function InfoItem_getStorageData() {
     var data = null;
 
     try {
       data = {
         bounds: this.getBounds(),
         locked: Utils.copy(this.locked)
       };
     } catch(e) {
@@ -139,32 +137,32 @@ window.InfoItem.prototype = Utils.extend
     }
 
     return data;
   },
 
   // ----------
   // Function: save
   // Saves this item to persistent storage.
-  save: function() {
+  save: function InfoItem_save() {
     try {
       if (!this._inited) // too soon to save now
         return;
 
       var data = this.getStorageData();
 
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: setBounds
   // Sets the bounds with the given <Rect>, animating unless "immediately" is false.
-  setBounds: function(rect, immediately) {
+  setBounds: function InfoItem_setBounds(rect, immediately) {
     try {
       Utils.assertThrow(Utils.isRect(rect), 'InfoItem.setBounds: rect must be a real rectangle!');
 
       // ___ Determine what has changed
       var css = {};
 
       if (rect.left != this.bounds.left)
         css.left = rect.left;
@@ -205,17 +203,17 @@ window.InfoItem.prototype = Utils.extend
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: setZ
   // Set the Z order for the item's container.
-  setZ: function(value) {
+  setZ: function InfoItem_setZ(value) {
     try {
       Utils.assertThrow(typeof value == 'number', 'value must be a number');
 
       this.zIndex = value;
 
       iQ(this.container).css({zIndex: value});
 
       if (this.$debug)
@@ -223,36 +221,34 @@ window.InfoItem.prototype = Utils.extend
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: close
   // Closes the item.
-  close: function() {
+  close: function InfoItem_close() {
     try {
       this._sendToSubscribers("close");
       this.removeTrenches();
       iQ(this.container).fadeOut(function() {
         iQ(this).remove();
         Items.unsquish();
       });
 
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: html
   // Sets the item's container's html to the specified value.
-  html: function(value) {
+  html: function InfoItem_html(value) {
     try {
       Utils.assertThrow(typeof value == 'string', 'value must be a string');
       this.$contents.html(value);
     } catch(e) {
       Utils.log(e);
     }
   }
 });
-
-})();
--- a/browser/base/content/tabview/iq.js
+++ b/browser/base/content/tabview/iq.js
@@ -69,17 +69,17 @@ let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1
 // ##########
 // Class: iQClass
 // The actual class of iQ result objects, representing an individual element
 // or a group of elements.
 //
 // ----------
 // Function: iQClass
 // You don't call this directly; this is what's called by iQ().
-let iQClass = function(selector, context) {
+function iQClass(selector, context) {
 
   // Handle $(""), $(null), or $(undefined)
   if (!selector) {
     return this;
   }
 
   // Handle $(DOMElement)
   if (selector.nodeType) {
@@ -189,31 +189,31 @@ iQClass.prototype = {
   selector: "",
 
   // The default length of a iQ object is 0
   length: 0,
 
   // ----------
   // Function: each
   // Execute a callback for every element in the matched set.
-  each: function(callback) {
+  each: function iQClass_each(callback) {
     if (typeof callback != "function") {
       Utils.assert(false, "each's argument must be a function");
       return null;
     }
     for (let i = 0; this[i] != null; i++) {
       callback(this[i]);
     }
     return this;
   },
 
   // ----------
   // Function: addClass
   // Adds the given class(es) to the receiver.
-  addClass: function(value) {
+  addClass: function iQClass_addClass(value) {
     Utils.assertThrow(typeof value == "string" && value,
                       'requires a valid string argument');
 
     let length = this.length;
     for (let i = 0; i < length; i++) {
       let elem = this[i];
       if (elem.nodeType === 1) {
         value.split(/\s+/).forEach(function(className) {
@@ -223,17 +223,17 @@ iQClass.prototype = {
     }
 
     return this;
   },
 
   // ----------
   // Function: removeClass
   // Removes the given class(es) from the receiver.
-  removeClass: function(value) {
+  removeClass: function iQClass_removeClass(value) {
     if (typeof value != "string" || !value) {
       Utils.assert(false, 'does not support function argument');
       return null;
     }
 
     let length = this.length;
     for (let i = 0; i < length; i++) {
       let elem = this[i];
@@ -245,31 +245,31 @@ iQClass.prototype = {
     }
 
     return this;
   },
 
   // ----------
   // Function: hasClass
   // Returns true is the receiver has the given css class.
-  hasClass: function(singleClassName) {
+  hasClass: function iQClass_hasClass(singleClassName) {
     let length = this.length;
     for (let i = 0; i < length; i++) {
       if (this[i].classList.contains(singleClassName)) {
         return true;
       }
     }
     return false;
   },
 
   // ----------
   // Function: find
   // Searches the receiver and its children, returning a new iQ object with
   // elements that match the given selector.
-  find: function(selector) {
+  find: function iQClass_find(selector) {
     let ret = [];
     let length = 0;
 
     let l = this.length;
     for (let i = 0; i < l; i++) {
       length = ret.length;
       try {
         Utils.merge(ret, this[i].querySelectorAll(selector));
@@ -291,79 +291,79 @@ iQClass.prototype = {
     }
 
     return iQ(ret);
   },
 
   // ----------
   // Function: remove
   // Removes the receiver from the DOM.
-  remove: function() {
+  remove: function iQClass_remove() {
     for (let i = 0; this[i] != null; i++) {
       let elem = this[i];
       if (elem.parentNode) {
         elem.parentNode.removeChild(elem);
       }
     }
     return this;
   },
 
   // ----------
   // Function: empty
   // Removes all of the reciever's children and HTML content from the DOM.
-  empty: function() {
+  empty: function iQClass_empty() {
     for (let i = 0; this[i] != null; i++) {
       let elem = this[i];
       while (elem.firstChild) {
         elem.removeChild(elem.firstChild);
       }
     }
     return this;
   },
 
   // ----------
   // Function: width
   // Returns the width of the receiver.
-  width: function() {
+  width: function iQClass_width() {
     let bounds = this.bounds();
     return bounds.width;
   },
 
   // ----------
   // Function: height
   // Returns the height of the receiver.
-  height: function() {
+  height: function iQClass_height() {
     let bounds = this.bounds();
     return bounds.height;
   },
 
   // ----------
   // Function: position
   // Returns an object with the receiver's position in left and top
   // properties.
-  position: function() {
+  position: function iQClass_position() {
     let bounds = this.bounds();
     return new Point(bounds.left, bounds.top);
   },
 
   // ----------
   // Function: bounds
   // Returns a <Rect> with the receiver's bounds.
-  bounds: function() {
+  bounds: function iQClass_bounds() {
     Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
     let rect = this[0].getBoundingClientRect();
     return new Rect(Math.floor(rect.left), Math.floor(rect.top),
                     Math.floor(rect.width), Math.floor(rect.height));
   },
 
   // ----------
   // Function: data
   // Pass in both key and value to attach some data to the receiver;
   // pass in just key to retrieve it.
-  data: function(key, value) {
+  data: function iQClass_data(key, value) {
     let data = null;
     if (typeof value === "undefined") {
       Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
       data = this[0].iQData;
       if (data)
         return data[key];
       else
         return null;
@@ -381,75 +381,75 @@ iQClass.prototype = {
 
     return this;
   },
 
   // ----------
   // Function: html
   // Given a value, sets the receiver's innerHTML to it; otherwise returns
   // what's already there.
-  html: function(value) {
+  html: function iQClass_html(value) {
     Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
     if (typeof value === "undefined")
       return this[0].innerHTML;
 
     this[0].innerHTML = value;
     return this;
   },
 
   // ----------
   // Function: text
   // Given a value, sets the receiver's textContent to it; otherwise returns
   // what's already there.
-  text: function(value) {
+  text: function iQClass_text(value) {
     Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
     if (typeof value === "undefined") {
       return this[0].textContent;
     }
 
     return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value));
   },
 
   // ----------
   // Function: val
   // Given a value, sets the receiver's value to it; otherwise returns what's already there.
-  val: function(value) {
+  val: function iQClass_val(value) {
     Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
     if (typeof value === "undefined") {
       return this[0].value;
     }
 
     this[0].value = value;
     return this;
   },
 
   // ----------
   // Function: appendTo
   // Appends the receiver to the result of iQ(selector).
-  appendTo: function(selector) {
+  appendTo: function iQClass_appendTo(selector) {
     Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
     iQ(selector).append(this);
     return this;
   },
 
   // ----------
   // Function: append
   // Appends the result of iQ(selector) to the receiver.
-  append: function(selector) {
+  append: function iQClass_append(selector) {
     let object = iQ(selector);
     Utils.assert(object.length == 1 && this.length == 1, 
         'does not yet support multi-objects (or null objects)');
     this[0].appendChild(object[0]);
     return this;
   },
 
   // ----------
   // Function: attr
   // Sets or gets an attribute on the element(s).
-  attr: function(key, value) {
+  attr: function iQClass_attr(key, value) {
     Utils.assert(typeof key === 'string', 'string key');
     if (typeof value === "undefined") {
       Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
       return this[0].getAttribute(key);
     }
 
     for (let i = 0; this[i] != null; i++)
       this[i].setAttribute(key, value);
@@ -461,17 +461,17 @@ iQClass.prototype = {
   // Function: css
   // Sets or gets CSS properties on the receiver. When setting certain numerical properties,
   // will automatically add "px". A property can be removed by setting it to null.
   //
   // Possible call patterns:
   //   a: object, b: undefined - sets with properties from a
   //   a: string, b: undefined - gets property specified by a
   //   a: string, b: string/number - sets property specified by a to b
-  css: function(a, b) {
+  css: function iQClass_css(a, b) {
     let properties = null;
 
     if (typeof a === 'string') {
       let key = a;
       if (typeof b === "undefined") {
         Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
 
         return window.getComputedStyle(this[0], null).getPropertyValue(key);
@@ -519,17 +519,17 @@ iQClass.prototype = {
   //   options - an object with various properites (see below)
   //
   // Possible "options" properties:
   //   duration - how long to animate, in milliseconds
   //   easing - easing function to use. Possibilities include
   //     "tabviewBounce", "easeInQuad". Default is "ease".
   //   complete - function to call once the animation is done, takes nothing
   //     in, but "this" is set to the element that was animated.
-  animate: function(css, options) {
+  animate: function iQClass_animate(css, options) {
     Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
 
     if (!options)
       options = {};
 
     let easings = {
       tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.29)", 
       easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care
@@ -572,17 +572,17 @@ iQClass.prototype = {
     }, duration);
 
     return this;
   },
 
   // ----------
   // Function: fadeOut
   // Animates the receiver to full transparency. Calls callback on completion.
-  fadeOut: function(callback) {
+  fadeOut: function iQClass_fadeOut(callback) {
     Utils.assert(typeof callback == "function" || typeof callback === "undefined", 
         'does not yet support duration');
 
     this.animate({
       opacity: 0
     }, {
       duration: 400,
       complete: function() {
@@ -593,48 +593,48 @@ iQClass.prototype = {
     });
 
     return this;
   },
 
   // ----------
   // Function: fadeIn
   // Animates the receiver to full opacity.
-  fadeIn: function() {
+  fadeIn: function iQClass_fadeIn() {
     this.css({display: ''});
     this.animate({
       opacity: 1
     }, {
       duration: 400
     });
 
     return this;
   },
 
   // ----------
   // Function: hide
   // Hides the receiver.
-  hide: function() {
+  hide: function iQClass_hide() {
     this.css({display: 'none', opacity: 0});
     return this;
   },
 
   // ----------
   // Function: show
   // Shows the receiver.
-  show: function() {
+  show: function iQClass_show() {
     this.css({display: '', opacity: 1});
     return this;
   },
 
   // ----------
   // Function: bind
   // Binds the given function to the given event type. Also wraps the function
   // in a try/catch block that does a Utils.log on any errors.
-  bind: function(type, func) {
+  bind: function iQClass_bind(type, func) {
     let handler = function(event) func.apply(this, [event]);
 
     for (let i = 0; this[i] != null; i++) {
       let elem = this[i];
       if (!elem.iQEventData)
         elem.iQEventData = {};
 
       if (!elem.iQEventData[type])
@@ -650,31 +650,31 @@ iQClass.prototype = {
 
     return this;
   },
 
   // ----------
   // Function: one
   // Binds the given function to the given event type, but only for one call;
   // automatically unbinds after the event fires once.
-  one: function(type, func) {
+  one: function iQClass_one(type, func) {
     Utils.assert(typeof func == "function", 'does not support eventData argument');
 
     let handler = function(e) {
       iQ(this).unbind(type, handler);
       return func.apply(this, [e]);
     };
 
     return this.bind(type, handler);
   },
 
   // ----------
   // Function: unbind
   // Unbinds the given function from the given event type.
-  unbind: function(type, func) {
+  unbind: function iQClass_unbind(type, func) {
     Utils.assert(typeof func == "function", 'Must provide a function');
 
     for (let i = 0; this[i] != null; i++) {
       let elem = this[i];
       let handler = func;
       if (elem.iQEventData && elem.iQEventData[type]) {
         let count = elem.iQEventData[type].length;
         for (let a = 0; a < count; a++) {
--- a/browser/base/content/tabview/items.js
+++ b/browser/base/content/tabview/items.js
@@ -52,17 +52,17 @@
 //
 // Subclasses of Item must also provide the <Subscribable> interface.
 //
 // ... and this property:
 //   defaultSize - a Point
 //   locked - an object (see below)
 //
 // Make sure to call _init() from your subclass's constructor.
-window.Item = function() {
+function Item() {
   // Variable: isAnItem
   // Always true for Items
   this.isAnItem = true;
 
   // Variable: bounds
   // The position and size of this Item, represented as a <Rect>.
   this.bounds = null;
 
@@ -133,24 +133,24 @@ window.Item = function() {
   //   stop - A function to be called when the resize is done
   this.resizeOptions = null;
 
   // Variable: isDragging
   // Boolean for whether the item is currently being dragged or not.
   this.isDragging = false;
 };
 
-window.Item.prototype = {
+Item.prototype = {
   // ----------
   // Function: _init
   // Initializes the object. To be called from the subclass's intialization function.
   //
   // Parameters:
   //   container - the outermost DOM element that describes this item onscreen.
-  _init: function(container) {
+  _init: function Item__init(container) {
     Utils.assert(typeof this.addSubscriber == 'function' && 
         typeof this.removeSubscriber == 'function' && 
         typeof this._sendToSubscribers == 'function',
         'Subclass must implement the Subscribable interface');
     Utils.assert(Utils.isDOMElement(container), 'container must be a DOM element');
     Utils.assert(typeof this.setBounds == 'function', 'Subclass must provide setBounds');
     Utils.assert(typeof this.setZ == 'function', 'Subclass must provide setZ');
     Utils.assert(typeof this.close == 'function', 'Subclass must provide close');
@@ -234,25 +234,25 @@ window.Item.prototype = {
         resizeInfo = null;
       }
     };
   },
 
   // ----------
   // Function: getBounds
   // Returns a copy of the Item's bounds as a <Rect>.
-  getBounds: function() {
+  getBounds: function Item_getBounds() {
     Utils.assert(Utils.isRect(this.bounds), 'this.bounds');
     return new Rect(this.bounds);
   },
 
   // ----------
   // Function: overlapsWithOtherItems
   // Returns true if this Item overlaps with any other Item on the screen.
-  overlapsWithOtherItems: function() {
+  overlapsWithOtherItems: function Item_overlapsWithOtherItems() {
     var self = this;
     var items = Items.getTopLevelItems();
     var bounds = this.getBounds();
     return items.some(function(item) {
       if (item == self) // can't overlap with yourself.
         return false;
       var myBounds = item.getBounds();
       return myBounds.intersects(bounds);
@@ -263,72 +263,72 @@ window.Item.prototype = {
   // Function: setPosition
   // Moves the Item to the specified location.
   //
   // Parameters:
   //   left - the new left coordinate relative to the window
   //   top - the new top coordinate relative to the window
   //   immediately - if false or omitted, animates to the new position;
   //   otherwise goes there immediately
-  setPosition: function(left, top, immediately) {
+  setPosition: function Item_setPosition(left, top, immediately) {
     Utils.assert(Utils.isRect(this.bounds), 'this.bounds');
     this.setBounds(new Rect(left, top, this.bounds.width, this.bounds.height), immediately);
   },
 
   // ----------
   // Function: setSize
   // Resizes the Item to the specified size.
   //
   // Parameters:
   //   width - the new width in pixels
   //   height - the new height in pixels
   //   immediately - if false or omitted, animates to the new size;
   //   otherwise resizes immediately
-  setSize: function(width, height, immediately) {
+  setSize: function Item_setSize(width, height, immediately) {
     Utils.assert(Utils.isRect(this.bounds), 'this.bounds');
     this.setBounds(new Rect(this.bounds.left, this.bounds.top, width, height), immediately);
   },
 
   // ----------
   // Function: setUserSize
   // Remembers the current size as one the user has chosen.
-  setUserSize: function() {
+  setUserSize: function Item_setUserSize() {
     Utils.assert(Utils.isRect(this.bounds), 'this.bounds');
     this.userSize = new Point(this.bounds.width, this.bounds.height);
     this.save();
   },
 
   // ----------
   // Function: getZ
   // Returns the zIndex of the Item.
-  getZ: function() {
+  getZ: function Item_getZ() {
     return this.zIndex;
   },
 
   // ----------
   // Function: setRotation
   // Rotates the object to the given number of degrees.
-  setRotation: function(degrees) {
+  setRotation: function Item_setRotation(degrees) {
     var value = degrees ? "rotate(%deg)".replace(/%/, degrees) : null;
     iQ(this.container).css({"-moz-transform": value});
   },
 
   // ----------
   // Function: setParent
   // Sets the receiver's parent to the given <Item>.
-  setParent: function(parent) {
+  setParent: function Item_setParent(parent) {
     this.parent = parent;
     this.removeTrenches();
     this.save();
   },
 
   // ----------
   // Function: pushAway
   // Pushes all other items away so none overlap this Item.
-  pushAway: function() {
+  pushAway: function Item_pushAway() {
     var buffer = Math.floor(Items.defaultGutter / 2);
 
     var items = Items.getTopLevelItems();
     // setup each Item's pushAwayData attribute:
     items.forEach(function pushAway_setupPushAwayData(item) {
       var data = {};
       data.bounds = item.getBounds();
       data.startBounds = new Rect(data.bounds);
@@ -500,26 +500,26 @@ window.Item.prototype = {
       }
     });
   },
 
   // ----------
   // Function: _updateDebugBounds
   // Called by a subclass when its bounds change, to update the debugging rectangles on screen.
   // This functionality is enabled only by the debug property.
-  _updateDebugBounds: function() {
+  _updateDebugBounds: function Item__updateDebugBounds() {
     if (this.$debug) {
       this.$debug.css(this.bounds.css());
     }
   },
 
   // ----------
   // Function: setTrenches
   // Sets up/moves the trenches for snapping to this item.
-  setTrenches: function(rect) {
+  setTrenches: function Item_setTrenches(rect) {
     if (this.parent !== null)
       return;
 
     if (!this.borderTrenches)
       this.borderTrenches = Trenches.registerWithItem(this,"border");
 
     var bT = this.borderTrenches;
     Trenches.getById(bT.left).setWithRect(rect);
@@ -536,17 +536,17 @@ window.Item.prototype = {
     Trenches.getById(gT.top).setWithRect(rect);
     Trenches.getById(gT.bottom).setWithRect(rect);
 
   },
 
   // ----------
   // Function: removeTrenches
   // Removes the trenches for snapping to this item.
-  removeTrenches: function() {
+  removeTrenches: function Item_removeTrenches() {
     for (var edge in this.borderTrenches) {
       Trenches.unregister(this.borderTrenches[edge]); // unregister can take an array
     }
     this.borderTrenches = null;
     for (var edge in this.guideTrenches) {
       Trenches.unregister(this.guideTrenches[edge]); // unregister can take an array
     }
     this.guideTrenches = null;
@@ -567,17 +567,17 @@ window.Item.prototype = {
     FauxDragInfo.stop();
 
     Trenches.defaultRadius = defaultRadius;
   },
 
   // ----------
   // Function: draggable
   // Enables dragging on this item. Note: not to be called multiple times on the same item!
-  draggable: function() {
+  draggable: function Item_draggable() {
     try {
       Utils.assert(this.dragOptions, 'dragOptions');
 
       var cancelClasses = [];
       if (typeof this.dragOptions.cancelClass == 'string')
         cancelClasses = this.dragOptions.cancelClass.split(' ');
 
       var self = this;
@@ -716,17 +716,17 @@ window.Item.prototype = {
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: droppable
   // Enables or disables dropping on this item.
-  droppable: function(value) {
+  droppable: function Item_droppable(value) {
     try {
       var $container = iQ(this.container);
       if (value)
         $container.addClass('iq-droppable');
       else {
         Utils.assert(this.dropOptions, 'dropOptions');
 
         $container.removeClass('iq-droppable');
@@ -734,17 +734,17 @@ window.Item.prototype = {
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: resizable
   // Enables or disables resizing of this item.
-  resizable: function(value) {
+  resizable: function Item_resizable(value) {
     try {
       var $container = iQ(this.container);
       iQ('.iq-resizable-handle', $container).remove();
 
       if (!value) {
         $container.removeClass('iq-resizable');
       } else {
         Utils.assert(this.resizeOptions, 'resizeOptions');
@@ -818,58 +818,58 @@ window.Item.prototype = {
       Utils.log(e);
     }
   }
 };
 
 // ##########
 // Class: Items
 // Keeps track of all Items.
-window.Items = {
+let Items = {
   // ----------
   // Variable: defaultGutter
   // How far apart Items should be from each other and from bounds
   defaultGutter: 15,
 
   // ----------
   // Function: item
   // Given a DOM element representing an Item, returns the Item.
-  item: function(el) {
+  item: function Items_item(el) {
     return iQ(el).data('item');
   },
 
   // ----------
   // Function: getTopLevelItems
   // Returns an array of all Items not grouped into groupItems.
-  getTopLevelItems: function() {
+  getTopLevelItems: function Items_getTopLevelItems() {
     var items = [];
 
     iQ('.tab, .groupItem, .info-item').each(function(elem) {
       var $this = iQ(elem);
       var item = $this.data('item');
       if (item && !item.parent && !$this.hasClass('phantom'))
         items.push(item);
     });
 
     return items;
   },
 
   // ----------
   // Function: getPageBounds
   // Returns a <Rect> defining the area of the page <Item>s should stay within.
-  getPageBounds: function() {
+  getPageBounds: function Items_getPageBounds() {
     var width = Math.max(100, window.innerWidth);
     var height = Math.max(100, window.innerHeight);
     return new Rect(0, 0, width, height);
   },
 
   // ----------
   // Function: getSafeWindowBounds
   // Returns the bounds within which it is safe to place all non-stationary <Item>s.
-  getSafeWindowBounds: function() {
+  getSafeWindowBounds: function Items_getSafeWindowBounds() {
     // the safe bounds that would keep it "in the window"
     var gutter = Items.defaultGutter;
     // Here, I've set the top gutter separately, as the top of the window has its own
     // extra chrome which makes a large top gutter unnecessary.
     // TODO: set top gutter separately, elsewhere.
     var topGutter = 5;
     return new Rect(gutter, topGutter,
         window.innerWidth - 2 * gutter, window.innerHeight - gutter - topGutter);
@@ -890,17 +890,17 @@ window.Items = {
   //   animate - whether to animate; default: true.
   //   z - the z index to set all the items; default: don't change z.
   //   pretend - whether to collect and return the rectangle rather than moving the items; default: false
   //   count - overrides the item count for layout purposes; default: the actual item count
   //   padding - pixels between each item
   //
   // Returns:
   //   the list of rectangles if the pretend option is set; otherwise null
-  arrange: function(items, bounds, options) {
+  arrange: function Items_arrange(items, bounds, options) {
     var animate;
     if (!options || typeof options.animate == 'undefined')
       animate = true;
     else
       animate = options.animate;
 
     if (typeof options == 'undefined')
       options = {};
@@ -980,17 +980,17 @@ window.Items = {
   // Function: unsquish
   // Checks to see which items can now be unsquished.
   //
   // Parameters:
   //   pairs - an array of objects, each with two properties: item and bounds. The bounds are
   //     modified as appropriate, but the items are not changed. If pairs is null, the
   //     operation is performed directly on all of the top level items.
   //   ignore - an <Item> to not include in calculations (because it's about to be closed, for instance)
-  unsquish: function(pairs, ignore) {
+  unsquish: function Items_unsquish(pairs, ignore) {
     var pairsProvided = (pairs ? true : false);
     if (!pairsProvided) {
       var items = Items.getTopLevelItems();
       pairs = [];
       items.forEach(function(item) {
         pairs.push({
           item: item,
           bounds: item.getBounds()
@@ -1059,9 +1059,9 @@ window.Items = {
     });
 
     if (!pairsProvided) {
       pairs.forEach(function(pair) {
         pair.item.setBounds(pair.bounds);
       });
     }
   }
-};
\ No newline at end of file
+};
--- a/browser/base/content/tabview/modules/utils.jsm
+++ b/browser/base/content/tabview/modules/utils.jsm
@@ -72,17 +72,17 @@ function Point(a, y) {
     this.y = (Utils.isNumber(y) ? y : 0);
   }
 };
 
 Point.prototype = {
   // ----------
   // Function: distance
   // Returns the distance from this point to the given <Point>.
-  distance: function(point) {
+  distance: function Point_distance(point) {
     var ax = this.x - point.x;
     var ay = this.y - point.y;
     return Math.sqrt((ax * ax) + (ay * ay));
   }
 };
 
 // ##########
 // Class: Rect
@@ -128,87 +128,87 @@ Rect.prototype = {
   // ----------
   // Variable: yRange
   // Gives you a new <Range> for the vertical dimension.
   get yRange() new Range(this.top, this.bottom),
 
   // ----------
   // Function: intersects
   // Returns true if this rectangle intersects the given <Rect>.
-  intersects: function(rect) {
+  intersects: function Rect_intersects(rect) {
     return (rect.right > this.left &&
             rect.left < this.right &&
             rect.bottom > this.top &&
             rect.top < this.bottom);
   },
 
   // ----------
   // Function: intersection
   // Returns a new <Rect> with the intersection of this rectangle and the give <Rect>,
   // or null if they don't intersect.
-  intersection: function(rect) {
+  intersection: function Rect_intersection(rect) {
     var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0);
     box.right = Math.min(rect.right, this.right);
     box.bottom = Math.min(rect.bottom, this.bottom);
     if (box.width > 0 && box.height > 0)
       return box;
 
     return null;
   },
 
   // ----------
   // Function: contains
   // Returns a boolean denoting if the <Rect> is contained inside
   // of the bounding rect.
   //
   // Paramaters
   //  - A <Rect>
-  contains: function(rect) {
+  contains: function Rect_contains(rect) {
     return (rect.left > this.left &&
             rect.right < this.right &&
             rect.top > this.top &&
             rect.bottom < this.bottom);
   },
 
   // ----------
   // Function: center
   // Returns a new <Point> with the center location of this rectangle.
-  center: function() {
+  center: function Rect_center() {
     return new Point(this.left + (this.width / 2), this.top + (this.height / 2));
   },
 
   // ----------
   // Function: size
   // Returns a new <Point> with the dimensions of this rectangle.
-  size: function() {
+  size: function Rect_size() {
     return new Point(this.width, this.height);
   },
 
   // ----------
   // Function: position
   // Returns a new <Point> with the top left of this rectangle.
-  position: function() {
+  position: function Rect_position() {
     return new Point(this.left, this.top);
   },
 
   // ----------
   // Function: area
   // Returns the area of this rectangle.
-  area: function() {
+  area: function Rect_area() {
     return this.width * this.height;
   },
 
   // ----------
   // Function: inset
   // Makes the rect smaller (if the arguments are positive) as if a margin is added all around
   // the initial rect, with the margin widths (symmetric) being specified by the arguments.
   //
   // Paramaters
   //  - A <Point> or two arguments: x and y
-  inset: function(a, b) {
+  inset: function Rect_inset(a, b) {
     if (Utils.isPoint(a)) {
       b = a.y;
       a = a.x;
     }
 
     this.left += a;
     this.width -= a * 2;
     this.top += b;
@@ -216,67 +216,67 @@ Rect.prototype = {
   },
 
   // ----------
   // Function: offset
   // Moves (translates) the rect by the given vector.
   //
   // Paramaters
   //  - A <Point> or two arguments: x and y
-  offset: function(a, b) {
+  offset: function Rect_offset(a, b) {
     if (Utils.isPoint(a)) {
       this.left += a.x;
       this.top += a.y;
     } else {
       this.left += a;
       this.top += b;
     }
   },
 
   // ----------
   // Function: equals
   // Returns true if this rectangle is identical to the given <Rect>.
-  equals: function(rect) {
+  equals: function Rect_equals(rect) {
     return (rect.left == this.left &&
             rect.top == this.top &&
             rect.width == this.width &&
             rect.height == this.height);
   },
 
   // ----------
   // Function: union
   // Returns a new <Rect> with the union of this rectangle and the given <Rect>.
-  union: function(a) {
+  union: function Rect_union(a) {
     var newLeft = Math.min(a.left, this.left);
     var newTop = Math.min(a.top, this.top);
     var newWidth = Math.max(a.right, this.right) - newLeft;
     var newHeight = Math.max(a.bottom, this.bottom) - newTop;
     var newRect = new Rect(newLeft, newTop, newWidth, newHeight);
 
     return newRect;
   },
 
   // ----------
   // Function: copy
   // Copies the values of the given <Rect> into this rectangle.
-  copy: function(a) {
+  copy: function Rect_copy(a) {
     this.left = a.left;
     this.top = a.top;
     this.width = a.width;
     this.height = a.height;
   },
 
   // ----------
   // Function: css
   // Returns an object with the dimensions of this rectangle, suitable for
   // passing into iQ's css method. You could of course just pass the rectangle
   // straight in, but this is cleaner, as it removes all the extraneous
   // properties. If you give a <Rect> to <iQClass.css> without this, it will
   // ignore the extraneous properties, but result in CSS warnings.
-  css: function() {
+  css: function Rect_css() {
     return {
       left: this.left,
       top: this.top,
       width: this.width,
       height: this.height
     };
   }
 };
@@ -309,33 +309,33 @@ Range.prototype = {
   },
 
   // ----------
   // Function: contains
   // Whether the <Range> contains the given <Range> or value or not.
   //
   // Paramaters
   //  - a number or <Range>
-  contains: function(value) {
+  contains: function Range_contains(value) {
     if (Utils.isNumber(value))
       return value >= this.min && value <= this.max;
     if (Utils.isRange(value))
       return value.min >= this.min && value.max <= this.max;
     return false;
   },
 
   // ----------
   // Function: proportion
   // Maps the given value to the range [0,1], so that it returns 0 if the value is <= the min,
   // returns 1 if the value >= the max, and returns an interpolated "proportion" in (min, max).
   //
   // Paramaters
   //  - a number
   //  - (bool) smooth? If true, a smooth tanh-based function will be used instead of the linear.
-  proportion: function(value, smooth) {
+  proportion: function Range_proportion(value, smooth) {
     if (value <= this.min)
       return 0;
     if (this.max <= value)
       return 1;
 
     var proportion = (value - this.min) / this.extent;
 
     if (smooth) {
@@ -354,17 +354,17 @@ Range.prototype = {
   },
 
   // ----------
   // Function: scale
   // Takes the given value in [0,1] and maps it to the associated value on the Range.
   //
   // Paramaters
   //  - a number in [0,1]
-  scale: function(value) {
+  scale: function Range_scale(value) {
     if (value > 1)
       value = 1;
     if (value < 0)
       value = 0;
     return this.min + this.extent * value;
   }
 };
 
@@ -375,17 +375,17 @@ function Subscribable() {
   this.subscribers = null;
 };
 
 Subscribable.prototype = {
   // ----------
   // Function: addSubscriber
   // The given callback will be called when the Subscribable fires the given event.
   // The refObject is used to facilitate removal if necessary.
-  addSubscriber: function(refObject, eventName, callback) {
+  addSubscriber: function Subscribable_addSubscriber(refObject, eventName, callback) {
     try {
       Utils.assertThrow(refObject, "refObject");
       Utils.assertThrow(typeof callback == "function", "callback must be a function");
       Utils.assertThrow(eventName && typeof eventName == "string",
           "eventName must be a non-empty string");
     } catch(e) {
       Utils.log(e);
       return;
@@ -411,17 +411,17 @@ Subscribable.prototype = {
         callback: callback
       });
     }
   },
 
   // ----------
   // Function: removeSubscriber
   // Removes the callback associated with refObject for the given event.
-  removeSubscriber: function(refObject, eventName) {
+  removeSubscriber: function Subscribable_removeSubscriber(refObject, eventName) {
     try {
       Utils.assertThrow(refObject, "refObject");
       Utils.assertThrow(eventName && typeof eventName == "string",
           "eventName must be a non-empty string");
     } catch(e) {
       Utils.log(e);
       return;
     }
@@ -432,17 +432,17 @@ Subscribable.prototype = {
     this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) {
       return element.refObject != refObject;
     });
   },
 
   // ----------
   // Function: _sendToSubscribers
   // Internal routine. Used by the Subscribable to fire events.
-  _sendToSubscribers: function(eventName, eventInfo) {
+  _sendToSubscribers: function Subscribable__sendToSubscribers(eventName, eventInfo) {
     try {
       Utils.assertThrow(eventName && typeof eventName == "string",
           "eventName must be a non-empty string");
     } catch(e) {
       Utils.log(e);
       return;
     }
 
@@ -468,44 +468,44 @@ let Utils = {
 
   useConsole: true, // as opposed to dump
   showTime: false,
 
   // ----------
   // Function: log
   // Prints the given arguments to the JavaScript error console as a message.
   // Pass as many arguments as you want, it'll print them all.
-  log: function() {
+  log: function Utils_log() {
     var text = this.expandArgumentsForLog(arguments);
     var prefix = this.showTime ? Date.now() + ': ' : '';
     if (this.useConsole)    
       Services.console.logStringMessage(prefix + text);
     else
       dump(prefix + text + '\n');
   },
 
   // ----------
   // Function: error
   // Prints the given arguments to the JavaScript error console as an error.
   // Pass as many arguments as you want, it'll print them all.
-  error: function() {
+  error: function Utils_error() {
     var text = this.expandArgumentsForLog(arguments);
     var prefix = this.showTime ? Date.now() + ': ' : '';
     if (this.useConsole)    
       Cu.reportError(prefix + "tabview error: " + text);
     else
       dump(prefix + "TABVIEW ERROR: " + text + '\n');
   },
 
   // ----------
   // Function: trace
   // Prints the given arguments to the JavaScript error console as a message,
   // along with a full stack trace.
   // Pass as many arguments as you want, it'll print them all.
-  trace: function() {
+  trace: function Utils_trace() {
     var text = this.expandArgumentsForLog(arguments);
     // cut off the first two lines of the stack trace, because they're just this function.
     let stack = Error().stack.replace(/^.*?\n.*?\n/, "");
     // if the caller was assert, cut out the line for the assert function as well.
     if (this.trace.caller.name == 'Utils_assert')
       stack = stack.replace(/^.*?\n/, "");
     this.log('trace: ' + text + '\n' + stack);
   },
@@ -523,17 +523,17 @@ let Utils = {
 
       this.trace(text);
     }
   },
 
   // ----------
   // Function: assertThrow
   // Throws label as an exception if condition is false.
-  assertThrow: function(condition, label) {
+  assertThrow: function Utils_assertThrow(condition, label) {
     if (!condition) {
       let text;
       if (typeof label != 'string')
         text = 'badly formed assert';
       else
         text = "tabview assert: " + label;
 
       // cut off the first two lines of the stack trace, because they're just this function.
@@ -541,17 +541,17 @@ let Utils = {
 
       throw text;
     }
   },
 
   // ----------
   // Function: expandObject
   // Prints the given object to a string, including all of its properties.
-  expandObject: function(obj) {
+  expandObject: function Utils_expandObject(obj) {
     var s = obj + ' = {';
     for (let prop in obj) {
       let value;
       try {
         value = obj[prop];
       } catch(e) {
         value = '[!!error retrieving property]';
       }
@@ -567,77 +567,77 @@ let Utils = {
       s += ', ';
     }
     return s + '}';
   },
 
   // ----------
   // Function: expandArgumentsForLog
   // Expands all of the given args (an array) into a single string.
-  expandArgumentsForLog: function(args) {
+  expandArgumentsForLog: function Utils_expandArgumentsForLog(args) {
     var that = this;
     return Array.map(args, function(arg) {
       return typeof arg == 'object' ? that.expandObject(arg) : arg;
     }).join('; ');
   },
 
   // ___ Misc
 
   // ----------
   // Function: isRightClick
   // Given a DOM mouse event, returns true if it was for the right mouse button.
-  isRightClick: function(event) {
+  isRightClick: function Utils_isRightClick(event) {
     return event.button == 2;
   },
 
   // ----------
   // Function: isDOMElement
   // Returns true if the given object is a DOM element.
-  isDOMElement: function(object) {
+  isDOMElement: function Utils_isDOMElement(object) {
     return object instanceof Ci.nsIDOMElement;
   },
 
   // ----------
   // Function: isNumber
   // Returns true if the argument is a valid number.
-  isNumber: function(n) {
+  isNumber: function Utils_isNumber(n) {
     return typeof n == 'number' && !isNaN(n);
   },
 
   // ----------
   // Function: isRect
   // Returns true if the given object (r) looks like a <Rect>.
-  isRect: function(r) {
+  isRect: function Utils_isRect(r) {
     return (r &&
             this.isNumber(r.left) &&
             this.isNumber(r.top) &&
             this.isNumber(r.width) &&
             this.isNumber(r.height));
   },
 
   // ----------
   // Function: isRange
   // Returns true if the given object (r) looks like a <Range>.
-  isRange: function(r) {
+  isRange: function Utils_isRange(r) {
     return (r &&
             this.isNumber(r.min) &&
             this.isNumber(r.max));
   },
 
   // ----------
   // Function: isPoint
   // Returns true if the given object (p) looks like a <Point>.
-  isPoint: function(p) {
+  isPoint: function Utils_isPoint(p) {
     return (p && this.isNumber(p.x) && this.isNumber(p.y));
   },
 
   // ----------
   // Function: isPlainObject
   // Check to see if an object is a plain object (created using "{}" or "new Object").
-  isPlainObject: function(obj) {
+  isPlainObject: function Utils_isPlainObject(obj) {
     // Must be an Object.
     // Make sure that DOM nodes and window objects don't pass through, as well
     if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" ||
        obj.nodeType || obj.setInterval) {
       return false;
     }
 
     // Not own constructor property must be Object
@@ -656,47 +656,47 @@ let Utils = {
     for (key in obj) {}
 
     return key === undefined || hasOwnProperty.call(obj, key);
   },
 
   // ----------
   // Function: isEmptyObject
   // Returns true if the given object has no members.
-  isEmptyObject: function(obj) {
+  isEmptyObject: function Utils_isEmptyObject(obj) {
     for (let name in obj)
       return false;
     return true;
   },
 
   // ----------
   // Function: copy
   // Returns a copy of the argument. Note that this is a shallow copy; if the argument
   // has properties that are themselves objects, those properties will be copied by reference.
-  copy: function(value) {
+  copy: function Utils_copy(value) {
     if (value && typeof value == 'object') {
       if (Array.isArray(value))
         return this.extend([], value);
       return this.extend({}, value);
     }
     return value;
   },
 
   // ----------
   // Function: merge
   // Merge two array-like objects into the first and return it.
-  merge: function(first, second) {
+  merge: function Utils_merge(first, second) {
     Array.forEach(second, function(el) Array.push(first, el));
     return first;
   },
 
   // ----------
   // Function: extend
   // Pass several objects in and it will combine them all into the first object and return it.
-  extend: function() {
+  extend: function Utils_extend() {
 
     // copy reference to target object
     let target = arguments[0] || {};
     // Deep copy is not supported
     if (typeof target === "boolean") {
       this.assert(false, "The first argument of extend cannot be a boolean." +
           "Deep copy is not supported.");
       return target;
--- a/browser/base/content/tabview/storage.js
+++ b/browser/base/content/tabview/storage.js
@@ -46,32 +46,32 @@ let Storage = {
   GROUP_DATA_IDENTIFIER: "tabview-group",
   GROUPS_DATA_IDENTIFIER: "tabview-groups",
   TAB_DATA_IDENTIFIER: "tabview-tab",
   UI_DATA_IDENTIFIER: "tabview-ui",
 
   // ----------
   // Function: init
   // Sets up the object.
-  init: function() {
+  init: function Storage_init() {
     this._sessionStore =
       Cc["@mozilla.org/browser/sessionstore;1"].
         getService(Ci.nsISessionStore);
   },
 
   // ----------
   // Function: uninit
-  uninit : function() {
+  uninit: function Storage_uninit () {
     this._sessionStore = null;
   },
 
   // ----------
   // Function: wipe
   // Cleans out all the stored data, leaving empty objects.
-  wipe: function() {
+  wipe: function Storage_wipe() {
     try {
       var self = this;
 
       // ___ Tabs
       AllTabs.tabs.forEach(function(tab) {
         if (tab.ownerDocument.defaultView != gWindow)
           return;
 
@@ -87,27 +87,27 @@ let Storage = {
     } catch (e) {
       Utils.log("Error in wipe: "+e);
     }
   },
 
   // ----------
   // Function: saveTab
   // Saves the data for a single tab.
-  saveTab: function(tab, data) {
+  saveTab: function Storage_saveTab(tab, data) {
     Utils.assert(tab, "tab");
 
     this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER,
       JSON.stringify(data));
   },
 
   // ----------
   // Function: getTabData
   // Returns the data object associated with a single tab.
-  getTabData: function(tab) {
+  getTabData: function Storage_getTabData(tab) {
     Utils.assert(tab, "tab");
 
     var existingData = null;
     try {
       var tabData = this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER);
       if (tabData != "") {
         existingData = JSON.parse(tabData);
       }
@@ -117,93 +117,93 @@ let Storage = {
     }
 
     return existingData;
   },
 
   // ----------
   // Function: saveGroupItem
   // Saves the data for a single groupItem, associated with a specific window.
-  saveGroupItem: function(win, data) {
+  saveGroupItem: function Storage_saveGroupItem(win, data) {
     var id = data.id;
     var existingData = this.readGroupItemData(win);
     existingData[id] = data;
     this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
       JSON.stringify(existingData));
   },
 
   // ----------
   // Function: deleteGroupItem
   // Deletes the data for a single groupItem from the given window.
-  deleteGroupItem: function(win, id) {
+  deleteGroupItem: function Storage_deleteGroupItem(win, id) {
     var existingData = this.readGroupItemData(win);
     delete existingData[id];
     this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
       JSON.stringify(existingData));
   },
 
   // ----------
   // Function: readGroupItemData
   // Returns the data for all groupItems associated with the given window.
-  readGroupItemData: function(win) {
+  readGroupItemData: function Storage_readGroupItemData(win) {
     var existingData = {};
     try {
       existingData = JSON.parse(
         this._sessionStore.getWindowValue(win, this.GROUP_DATA_IDENTIFIER)
       );
     } catch (e) {
       // getWindowValue will fail if the property doesn't exist
       Utils.log("Error in readGroupItemData: "+e);
     }
     return existingData;
   },
 
   // ----------
   // Function: saveGroupItemsData
   // Saves the global data for the <GroupItems> singleton for the given window.
-  saveGroupItemsData: function(win, data) {
+  saveGroupItemsData: function Storage_saveGroupItemsData(win, data) {
     this.saveData(win, this.GROUPS_DATA_IDENTIFIER, data);
   },
 
   // ----------
   // Function: readGroupItemsData
   // Reads the global data for the <GroupItems> singleton for the given window.
-  readGroupItemsData: function(win) {
+  readGroupItemsData: function Storage_readGroupItemsData(win) {
     return this.readData(win, this.GROUPS_DATA_IDENTIFIER);
   },
 
   // ----------
   // Function: saveUIData
   // Saves the global data for the <UIManager> singleton for the given window.
-  saveUIData: function(win, data) {
+  saveUIData: function Storage_saveUIData(win, data) {
     this.saveData(win, this.UI_DATA_IDENTIFIER, data);
   },
 
   // ----------
   // Function: readUIData
   // Reads the global data for the <UIManager> singleton for the given window.
-  readUIData: function(win) {
+  readUIData: function Storage_readUIData(win) {
     return this.readData(win, this.UI_DATA_IDENTIFIER);
   },
 
   // ----------
   // Function: saveData
   // Generic routine for saving data to a window.
-  saveData: function(win, id, data) {
+  saveData: function Storage_saveData(win, id, data) {
     try {
       this._sessionStore.setWindowValue(win, id, JSON.stringify(data));
     } catch (e) {
       Utils.log("Error in saveData: "+e);
     }
   },
 
   // ----------
   // Function: readData
   // Generic routine for reading data from a window.
-  readData: function(win, id) {
+  readData: function Storage_readData(win, id) {
     var existingData = {};
     try {
       var data = this._sessionStore.getWindowValue(win, id);
       if (data)
         existingData = JSON.parse(data);
     } catch (e) {
       Utils.log("Error in readData: "+e);
     }
--- a/browser/base/content/tabview/tabitems.js
+++ b/browser/base/content/tabview/tabitems.js
@@ -43,17 +43,17 @@
 // Title: tabitems.js
 
 // ##########
 // Class: TabItem
 // An <Item> that represents a tab. Also implements the <Subscribable> interface.
 //
 // Parameters:
 //   tab - a xul:tab
-window.TabItem = function(tab) {
+function TabItem(tab) {
 
   Utils.assert(tab, "tab");
 
   this.tab = tab;
   // register this as the tab's tabItem
   this.tab.tabItem = this;
 
   // ___ set up div
@@ -202,70 +202,70 @@ window.TabItem = function(tab) {
   this._updateDebugBounds();
 
   TabItems.register(this);
 
   if (!TabItems.reconnect(this))
     GroupItems.newTab(this);
 };
 
-window.TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
+TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
   // ----------
   // Function: forceCanvasSize
   // Repaints the thumbnail with the given resolution, and forces it
   // to stay that resolution until unforceCanvasSize is called.
-  forceCanvasSize: function(w, h) {
+  forceCanvasSize: function TabItem_forceCanvasSize(w, h) {
     this.canvasSizeForced = true;
     this.canvasEl.width = w;
     this.canvasEl.height = h;
     this.tabCanvas.paint();
   },
 
   // ----------
   // Function: unforceCanvasSize
   // Stops holding the thumbnail resolution; allows it to shift to the
   // size of thumbnail on screen. Note that this call does not nest, unlike
   // <TabItems.resumePainting>; if you call forceCanvasSize multiple
   // times, you just need a single unforce to clear them all.
-  unforceCanvasSize: function() {
+  unforceCanvasSize: function TabItem_unforceCanvasSize() {
     this.canvasSizeForced = false;
   },
 
   // ----------
   // Function: showCachedData
   // Shows the cached data i.e. image and title.  Note: this method should only
   // be called at browser startup with the cached data avaliable.
-  showCachedData: function(tabData) {
+  showCachedData: function TabItem_showCachedData(tabData) {
     this.isShowingCachedData = true;
     var $nameElement = iQ(this.nameEl);
     var $canvasElement = iQ(this.canvasEl);
     var $cachedThumbElement = iQ(this.cachedThumbEl);
     $cachedThumbElement.attr("src", tabData.imageData).show();
     $canvasElement.css({opacity: 0.0});
     $nameElement.text(tabData.title ? tabData.title : "");
   },
 
   // ----------
   // Function: hideCachedData
   // Hides the cached data i.e. image and title and show the canvas.
-  hideCachedData: function() {
+  hideCachedData: function TabItem_hideCachedData() {
     var $canvasElement = iQ(this.canvasEl);
     var $cachedThumbElement = iQ(this.cachedThumbEl);
     $cachedThumbElement.hide();
     $canvasElement.css({opacity: 1.0});
     this.isShowingCachedData = false;
   },
 
   // ----------
   // Function: getStorageData
   // Get data to be used for persistent storage of this object.
   //
   // Parameters:
   //   getImageData - true to include thumbnail pixels (and page title as well); default false
-  getStorageData: function(getImageData) {
+  getStorageData: function TabItem_getStorageData(getImageData) {
     return {
       bounds: this.getBounds(),
       userSize: (Utils.isPoint(this.userSize) ? new Point(this.userSize) : null),
       url: this.tab.linkedBrowser.currentURI.spec,
       groupID: (this.parent ? this.parent.id : 0),
       imageData: (getImageData && this.tabCanvas ?
                   this.tabCanvas.toImageData() : null),
       title: getImageData && this.tab.label || null
@@ -273,17 +273,17 @@ window.TabItem.prototype = Utils.extend(
   },
 
   // ----------
   // Function: save
   // Store persistent for this object.
   //
   // Parameters:
   //   saveImageData - true to include thumbnail pixels (and page title as well); default false
-  save: function(saveImageData) {
+  save: function TabItem_save(saveImageData) {
     try{
       if (!this.tab || this.tab.parentNode == null || !this.reconnected) // too soon/late to save
         return;
 
       var data = this.getStorageData(saveImageData);
       if (TabItems.storageSanity(data))
         Storage.saveTab(this.tab, data);
     } catch(e) {
@@ -297,17 +297,17 @@ window.TabItem.prototype = Utils.extend(
   //
   // Parameters:
   //   rect - a <Rect> giving the new bounds
   //   immediately - true if it should not animate; default false
   //   options - an object with additional parameters, see below
   //
   // Possible options:
   //   force - true to always update the DOM even if the bounds haven't changed; default false
-  setBounds: function(rect, immediately, options) {
+  setBounds: function TabItem_setBounds(rect, immediately, options) {
     if (!Utils.isRect(rect)) {
       Utils.trace('TabItem.setBounds: rect is not a real rectangle!', rect);
       return;
     }
 
     if (!options)
       options = {};
 
@@ -411,71 +411,71 @@ window.TabItem.prototype = Utils.extend(
       this.setTrenches(rect);
 
     this.save();
   },
 
   // ----------
   // Function: getBoundsWithTitle
   // Returns a <Rect> for the groupItem's bounds, including the title
-  getBoundsWithTitle: function() {
+  getBoundsWithTitle: function TabItem_getBoundsWithTitle() {
     var b = this.getBounds();
     var $title = iQ(this.container).find('.tab-title');
     var height = b.height;
     if ( Utils.isNumber($title.height()) )
       height += $title.height();
     return new Rect(b.left, b.top, b.width, height);
   },
 
   // ----------
   // Function: inStack
   // Returns true if this item is in a stacked groupItem.
-  inStack: function() {
+  inStack: function TabItem_inStack() {
     return iQ(this.container).hasClass("stacked");
   },
 
   // ----------
   // Function: setZ
   // Sets the z-index for this item.
-  setZ: function(value) {
+  setZ: function TabItem_setZ(value) {
     this.zIndex = value;
     iQ(this.container).css({zIndex: value});
   },
 
   // ----------
   // Function: close
   // Closes this item (actually closes the tab associated with it, which automatically
   // closes the item.
-  close: function() {
+  close: function TabItem_close() {
     gBrowser.removeTab(this.tab);
     this._sendToSubscribers("tabRemoved");
 
     // No need to explicitly delete the tab data, becasue sessionstore data
     // associated with the tab will automatically go away
   },
 
   // ----------
   // Function: addClass
   // Adds the specified CSS class to this item's container DOM element.
-  addClass: function(className) {
+  addClass: function TabItem_addClass(className) {
     iQ(this.container).addClass(className);
   },
 
   // ----------
   // Function: removeClass
   // Removes the specified CSS class from this item's container DOM element.
-  removeClass: function(className) {
+  removeClass: function TabItem_removeClass(className) {
     iQ(this.container).removeClass(className);
   },
 
   // ----------
   // Function: setResizable
   // If value is true, makes this item resizable, otherwise non-resizable.
   // Shows/hides a visible resize handle as appropriate.
-  setResizable: function(value) {
+  setResizable: function TabItem_setResizable(value) {
     var $resizer = iQ('.expander', this.container);
 
     this.resizeOptions.minWidth = TabItems.minTabWidth;
     this.resizeOptions.minHeight = TabItems.minTabWidth * (TabItems.tabHeight / TabItems.tabWidth);
 
     if (value) {
       $resizer.fadeIn();
       this.resizable(true);
@@ -483,37 +483,37 @@ window.TabItem.prototype = Utils.extend(
       $resizer.fadeOut();
       this.resizable(false);
     }
   },
 
   // ----------
   // Function: makeActive
   // Updates this item to visually indicate that it's active.
-  makeActive: function() {
+  makeActive: function TabItem_makeActive() {
    iQ(this.container).find("canvas").addClass("focus");
    iQ(this.container).find("img.cached-thumb").addClass("focus");
 
   },
 
   // ----------
   // Function: makeDeactive
   // Updates this item to visually indicate that it's not active.
-  makeDeactive: function() {
+  makeDeactive: function TabItem_makeDeactive() {
    iQ(this.container).find("canvas").removeClass("focus");
    iQ(this.container).find("img.cached-thumb").removeClass("focus");
   },
 
   // ----------
   // Function: zoomIn
   // Allows you to select the tab and zoom in on it, thereby bringing you
   // to the tab in Firefox to interact with.
   // Parameters:
   //   isNewBlankTab - boolean indicates whether it is a newly opened blank tab.
-  zoomIn: function(isNewBlankTab) {
+  zoomIn: function TabItem_zoomIn(isNewBlankTab) {
     var self = this;
     var $tabEl = iQ(this.container);
     var childHitResult = { shouldZoom: true };
     if (this.parent)
       childHitResult = this.parent.childHit(this);
 
     if (childHitResult.shouldZoom) {
       // Zoom in!
@@ -566,17 +566,17 @@ window.TabItem.prototype = Utils.extend(
 
   // ----------
   // Function: zoomOut
   // Handles the zoom down animation after returning to TabView.
   // It is expected that this routine will be called from the chrome thread
   //
   // Parameters:
   //   complete - a function to call after the zoom down animation
-  zoomOut: function(complete) {
+  zoomOut: function TabItem_zoomOut(complete) {
     var $tab = iQ(this.container);
 
     var box = this.getBounds();
     box.width -= this.sizeExtra.x;
     box.height -= this.sizeExtra.y;
 
     TabItems.pausePainting();
 
@@ -602,17 +602,17 @@ window.TabItem.prototype = Utils.extend(
     });
   },
 
   // ----------
   // Function: setZoomPrep
   // Either go into or return from (depending on <value>) "zoom prep" mode,
   // where the tab fills a large portion of the screen in anticipation of
   // the zoom out animation.
-  setZoomPrep: function(value) {
+  setZoomPrep: function TabItem_setZoomPrep(value) {
     var $div = iQ(this.container);
     var data;
 
     var box = this.getBounds();
     if (value) {
       this._zoomPrep = true;
 
       // The divide by two part here is a clever way to speed up the zoom-out code.
@@ -639,33 +639,33 @@ window.TabItem.prototype = Utils.extend(
       this.setBounds(box, true, {force: true});
     }
   }
 });
 
 // ##########
 // Class: TabItems
 // Singleton for managing <TabItem>s
-window.TabItems = {
+let TabItems = {
   minTabWidth: 40,
   tabWidth: 160,
   tabHeight: 120,
   fontSize: 9,
   items: [],
   paintingPaused: 0,
   _tabsWaitingForUpdate: [],
   _heartbeatOn: false,
   _heartbeatTiming: 100, // milliseconds between beats
   _lastUpdateTime: Date.now(),
   _eventListeners: [],
 
   // ----------
   // Function: init
   // Set up the necessary tracking to maintain the <TabItems>s.
-  init: function() {
+  init: function TabItems_init() {
     Utils.assert(window.AllTabs, "AllTabs must be initialized first");
     var self = this;
 
     // When a tab is opened, create the TabItem
     this._eventListeners["open"] = function(tab) {
       if (tab.ownerDocument.defaultView != gWindow)
         return;
 
@@ -697,17 +697,17 @@ window.TabItems = {
 
       self.link(tab);
       self.update(tab);
     });
   },
 
   // ----------
   // Function: uninit
-  uninit: function() {
+  uninit: function TabItems_uninit() {
     for (let name in this._eventListeners) {
       AllTabs.unregister(name, this._eventListeners[name]);
     }
     this.items.forEach(function(tabItem) {
       for (let x in tabItem) {
         if (typeof tabItem[x] == "object")
           tabItem[x] = null;
       }
@@ -717,17 +717,17 @@ window.TabItems = {
     this._eventListeners = null;
     this._lastUpdateTime = null;
     this._tabsWaitingForUpdate = null;
   },
 
   // ----------
   // Function: update
   // Takes in a xul:tab.
-  update: function(tab) {
+  update: function TabItems_update(tab) {
     try {
       Utils.assertThrow(tab, "tab");
 
       let shouldDefer = (
         this.isPaintingPaused() ||
         this._tabsWaitingForUpdate.length ||
         Date.now() - this._lastUpdateTime < this._heartbeatTiming
       );
@@ -745,17 +745,17 @@ window.TabItems = {
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: _update
   // Takes in a xul:tab.
-  _update: function(tab) {
+  _update: function TabItems__update(tab) {
     try {
       Utils.assertThrow(tab, "tab");
 
       // ___ remove from waiting list if needed
       let index = this._tabsWaitingForUpdate.indexOf(tab);
       if (index != -1)
         this._tabsWaitingForUpdate.splice(index, 1);
 
@@ -811,30 +811,30 @@ window.TabItems = {
     }
 
     this._lastUpdateTime = Date.now();
   },
 
   // ----------
   // Function: link
   // Takes in a xul:tab.
-  link: function(tab){
+  link: function TabItems_link(tab){
     try {
       Utils.assertThrow(tab, "tab");
       Utils.assertThrow(!tab.tabItem, "shouldn't already be linked");
       new TabItem(tab); // sets tab.tabItem to itself
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: unlink
   // Takes in a xul:tab.
-  unlink: function(tab) {
+  unlink: function TabItems_unlink(tab) {
     try {
       Utils.assertThrow(tab, "tab");
       Utils.assertThrow(tab.tabItem, "should already be linked");
 
       this.unregister(tab.tabItem);
       tab.tabItem._sendToSubscribers("close");
       iQ(tab.tabItem.container).remove();
       tab.tabItem.removeTrenches();
@@ -849,17 +849,17 @@ window.TabItems = {
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: heartbeat
   // Allows us to spreadout update calls over a period of time.
-  heartbeat: function() {
+  heartbeat: function TabItems_heartbeat() {
     if (!this._heartbeatOn)
       return;
 
     if (this._tabsWaitingForUpdate.length) {
       this._update(this._tabsWaitingForUpdate[0]);
       // _update will remove the tab from the waiting list
     }
 
@@ -873,104 +873,104 @@ window.TabItems = {
   },
 
   // ----------
   // Function: pausePainting
   // Tells TabItems to stop updating thumbnails (so you can do
   // animations without thumbnail paints causing stutters).
   // pausePainting can be called multiple times, but every call to
   // pausePainting needs to be mirrored with a call to <resumePainting>.
-  pausePainting: function() {
+  pausePainting: function TabItems_pausePainting() {
     this.paintingPaused++;
 
     if (this.isPaintingPaused() && this._heartbeatOn)
       this._heartbeatOn = false;
   },
 
   // ----------
   // Function: resumePainting
   // Undoes a call to <pausePainting>. For instance, if you called
   // pausePainting three times in a row, you'll need to call resumePainting
   // three times before TabItems will start updating thumbnails again.
-  resumePainting: function() {
+  resumePainting: function TabItems_resumePainting() {
     this.paintingPaused--;
 
     if (!this.isPaintingPaused() &&
         this._tabsWaitingForUpdate.length &&
         !this._heartbeatOn) {
       this._heartbeatOn = true;
       this.heartbeat();
     }
   },
 
   // ----------
   // Function: isPaintingPaused
   // Returns a boolean indicating whether painting
   // is paused or not.
-  isPaintingPaused: function() {
+  isPaintingPaused: function TabItems_isPaintingPaused() {
     return this.paintingPaused > 0;
   },
 
   // ----------
   // Function: register
   // Adds the given <TabItem> to the master list.
-  register: function(item) {
+  register: function TabItems_register(item) {
     Utils.assert(item && item.isAnItem, 'item must be a TabItem');
     Utils.assert(this.items.indexOf(item) == -1, 'only register once per item');
     this.items.push(item);
   },
 
   // ----------
   // Function: unregister
   // Removes the given <TabItem> from the master list.
-  unregister: function(item) {
+  unregister: function TabItems_unregister(item) {
     var index = this.items.indexOf(item);
     if (index != -1)
       this.items.splice(index, 1);
   },
 
   // ----------
   // Function: getItems
   // Returns a copy of the master array of <TabItem>s.
-  getItems: function() {
+  getItems: function TabItems_getItems() {
     return Utils.copy(this.items);
   },
 
   // ----------
   // Function: saveAll
   // Saves all open <TabItem>s.
   //
   // Parameters:
   //   saveImageData - true to include thumbnail pixels (and page title as well); default false
-  saveAll: function(saveImageData) {
+  saveAll: function TabItems_saveAll(saveImageData) {
     var items = this.getItems();
     items.forEach(function(item) {
       item.save(saveImageData);
     });
   },
 
   // ----------
   // Function: storageSanity
   // Checks the specified data (as returned by TabItem.getStorageData or loaded from storage)
   // and returns true if it looks valid.
   // TODO: check everything
-  storageSanity: function(data) {
+  storageSanity: function TabItems_storageSanity(data) {
     var sane = true;
     if (!Utils.isRect(data.bounds)) {
       Utils.log('TabItems.storageSanity: bad bounds', data.bounds);
       sane = false;
     }
 
     return sane;
   },
 
   // ----------
   // Function: reconnect
   // Given a <TabItem>, attempts to load its persistent data from storage.
-  reconnect: function(item) {
+  reconnect: function TabItems_reconnect(item) {
     var found = false;
 
     try{
       Utils.assert(item, 'item');
       Utils.assert(item.tab, 'item.tab');
 
       if (item.reconnected)
         return true;
@@ -1022,37 +1022,37 @@ window.TabItems = {
     return found;
   }
 };
 
 // ##########
 // Class: TabCanvas
 // Takes care of the actual canvas for the tab thumbnail
 // Does not need to be accessed from outside of tabitems.js
-var TabCanvas = function(tab, canvas) {
+function TabCanvas(tab, canvas) {
   this.init(tab, canvas);
 };
 
 TabCanvas.prototype = {
   // ----------
   // Function: init
-  init: function(tab, canvas) {
+  init: function TabCanvas_init(tab, canvas) {
     this.tab = tab;
     this.canvas = canvas;
 
     var $canvas = iQ(canvas);
     var w = $canvas.width();
     var h = $canvas.height();
     canvas.width = w;
     canvas.height = h;
   },
 
   // ----------
   // Function: paint
-  paint: function(evt) {
+  paint: function TabCanvas_paint(evt) {
     var ctx = this.canvas.getContext("2d");
 
     var w = this.canvas.width;
     var h = this.canvas.height;
     if (!w || !h)
       return;
 
     let fromWin = this.tab.linkedBrowser.contentWindow;
@@ -1073,12 +1073,12 @@ TabCanvas.prototype = {
       Utils.error('paint', e);
     }
 
     ctx.restore();
   },
 
   // ----------
   // Function: toImageData
-  toImageData: function() {
+  toImageData: function TabCanvas_toImageData() {
     return this.canvas.toDataURL("image/png", "");
   }
 };
--- a/browser/base/content/tabview/trench.js
+++ b/browser/base/content/tabview/trench.js
@@ -50,17 +50,17 @@
 // Parameters:
 //   element - the DOM element for Item (GroupItem or TabItem) from which the trench is projected
 //   xory - either "x" or "y": whether the trench's <position> is along the x- or y-axis.
 //     In other words, if "x", the trench is vertical; if "y", the trench is horizontal.
 //   type - either "border" or "guide". Border trenches mark the border of an Item.
 //     Guide trenches extend out (unless they are intercepted) and act as "guides".
 //   edge - which edge of the Item that this trench corresponds to.
 //     Either "top", "left", "bottom", or "right".
-var Trench = function(element, xory, type, edge) {
+function Trench(element, xory, type, edge) {
   //----------
   // Variable: id
   // (integer) The id for the Trench. Set sequentially via <Trenches.nextId>
   this.id = Trenches.nextId++;
 
   // ---------
   // Variables: Initial parameters
   //   element - (DOMElement)
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -37,24 +37,22 @@
  * 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 ***** */
 
 // **********
 // Title: ui.js
 
-(function() {
-
-window.Keys = { meta: false };
+let Keys = { meta: false };
 
 // ##########
-// Class: UIManager
+// Class: UI
 // Singleton top-level UI manager.
-var UIManager = {
+let UI = {
   // Variable: _frameInitalized
   // True if the Tab View UI frame has been initialized.
   _frameInitalized: false,
 
   // Variable: _pageBounds
   // Stores the page bounds.
   _pageBounds : null,
 
@@ -83,17 +81,17 @@ var UIManager = {
 
   // Variable: _eventListeners
   // Keeps track of event listeners added to the AllTabs object.
   _eventListeners: {},
 
   // ----------
   // Function: init
   // Must be called after the object is created.
-  init: function() {
+  init: function UI_init() {
     try {
       let self = this;
 
       // ___ storage
       Storage.init();
       let data = Storage.readUIData(gWindow);
       this._storageSanity(data);
       this._pageBounds = data.pageBounds;
@@ -232,46 +230,46 @@ var UIManager = {
       // ___ Done
       this._frameInitalized = true;
       this._save();
     } catch(e) {
       Utils.log(e);
     }
   },
 
-  uninit: function() {
+  uninit: function UI_uninit() {
     TabItems.uninit();
     GroupItems.uninit();
     Storage.uninit();
 
     this._removeTabActionHandlers();
     this._currentTab = null;
     this._pageBounds = null;
     this._reorderTabItemsOnShow = null;
     this._reorderTabsOnHide = null;
   },
 
   // ----------
   // Function: getActiveTab
   // Returns the currently active tab as a <TabItem>
   //
-  getActiveTab: function() {
+  getActiveTab: function UI_getActiveTab() {
     return this._activeTab;
   },
 
   // ----------
   // Function: setActiveTab
   // Sets the currently active tab. The idea of a focused tab is useful
   // for keyboard navigation and returning to the last zoomed-in tab.
   // Hitting return/esc brings you to the focused tab, and using the
   // arrow keys lets you navigate between open tabs.
   //
   // Parameters:
   //  - Takes a <TabItem>
-  setActiveTab: function(tab) {
+  setActiveTab: function UI_setActiveTab(tab) {
     if (tab == this._activeTab)
       return;
 
     if (this._activeTab) {
       this._activeTab.makeDeactive();
       this._activeTab.removeSubscriber(this, "close");
     }
     this._activeTab = tab;
@@ -284,26 +282,26 @@ var UIManager = {
 
       this._activeTab.makeActive();
     }
   },
 
   // ----------
   // Function: _isTabViewVisible
   // Returns true if the TabView UI is currently shown.
-  _isTabViewVisible: function() {
+  _isTabViewVisible: function UI__isTabViewVisible() {
     return gTabViewDeck.selectedIndex == 1;
   },
 
   // ----------
   // Function: showTabView
   // Shows TabView and hides the main browser UI.
   // Parameters:
   //   zoomOut - true for zoom out animation, false for nothing.
-  showTabView: function(zoomOut) {
+  showTabView: function UI_showTabView(zoomOut) {
     if (this._isTabViewVisible())
       return;
 
     var self = this;
     var currentTab = this._currentTab;
     var item = null;
 
     this._reorderTabItemsOnShow.forEach(function(groupItem) {
@@ -351,17 +349,17 @@ var UIManager = {
       dispatchEvent(event);
 
     TabItems.resumePainting();
   },
 
   // ----------
   // Function: hideTabView
   // Hides TabView and shows the main browser UI.
-  hideTabView: function() {
+  hideTabView: function UI_hideTabView() {
     if (!this._isTabViewVisible())
       return;
 
     TabItems.pausePainting();
 
     this._reorderTabsOnHide.forEach(function(groupItem) {
       groupItem.reorderTabsBasedOnTabItemOrder();
     });
@@ -391,30 +389,30 @@ var UIManager = {
 #ifdef XP_MACOSX
   // ----------
   // Function: _setActiveTitleColor
   // Used on the Mac to make the title bar match the gradient in the rest of the
   // TabView UI.
   //
   // Parameters:
   //   set - true for the special TabView color, false for the normal color.
-  _setActiveTitleColor: function(set) {
+  _setActiveTitleColor: function UI__setActiveTitleColor(set) {
     // Mac Only
     var mainWindow = gWindow.document.getElementById("main-window");
     if (set)
       mainWindow.setAttribute("activetitlebarcolor", "#C4C4C4");
     else
       mainWindow.removeAttribute("activetitlebarcolor");
   },
 #endif
 
   // ----------
   // Function: _addTabActionHandlers
   // Adds handlers to handle tab actions.
-  _addTabActionHandlers: function() {
+  _addTabActionHandlers: function UI__addTabActionHandlers() {
     var self = this;
 
     this._eventListeners.close = function(tab) {
       if (tab.ownerDocument.defaultView != gWindow)
         return;
 
       if (self._isTabViewVisible()) {
         // just closed the selected tab in the TabView interface.
@@ -463,25 +461,25 @@ var UIManager = {
 
     for (let name in this._eventListeners)
       AllTabs.register(name, this._eventListeners[name]);
   },
 
   // ----------
   // Function: _removeTabActionHandlers
   // Removes handlers to handle tab actions.
-  _removeTabActionHandlers: function() {
+  _removeTabActionHandlers: function UI__removeTabActionHandlers() {
     for (let name in this._eventListeners)
       AllTabs.unregister(name, this._eventListeners[name]);
   },
 
   // ----------
   // Function: onTabSelect
   // Called when the user switches from one tab to another outside of the TabView UI.
-  onTabSelect: function(tab) {
+  onTabSelect: function UI_onTabSelect(tab) {
     let currentTab = this._currentTab;
     this._currentTab = tab;
 
     // if the last visible tab has just been closed, don't show the chrome UI.
     if (this._isTabViewVisible() &&
         (this._closedLastVisibleTab || this._closedSelectedTabInTabView)) {
       this._closedLastVisibleTab = false;
       this._closedSelectedTabInTabView = false;
@@ -517,50 +515,50 @@ var UIManager = {
   },
 
   // ----------
   // Function: setReorderTabsOnHide
   // Sets the groupItem which the tab items' tabs should be re-ordered when
   // switching to the main browser UI.
   // Parameters:
   //   groupItem - the groupItem which would be used for re-ordering tabs.
-  setReorderTabsOnHide: function(groupItem) {
+  setReorderTabsOnHide: function UI_setReorderTabsOnHide(groupItem) {
     if (this._isTabViewVisible()) {
       var index = this._reorderTabsOnHide.indexOf(groupItem);
       if (index == -1)
         this._reorderTabsOnHide.push(groupItem);
     }
   },
 
   // ----------
   // Function: setReorderTabItemsOnShow
   // Sets the groupItem which the tab items should be re-ordered when
   // switching to the tab view UI.
   // Parameters:
   //   groupItem - the groupItem which would be used for re-ordering tab items.
-  setReorderTabItemsOnShow: function(groupItem) {
+  setReorderTabItemsOnShow: function UI_setReorderTabItemsOnShow(groupItem) {
     if (!this._isTabViewVisible()) {
       var index = this._reorderTabItemsOnShow.indexOf(groupItem);
       if (index == -1)
         this._reorderTabItemsOnShow.push(groupItem);
     }
   },
 
   // ----------
   // Function: _setTabViewFrameKeyHandlers
   // Sets up the key handlers for navigating between tabs within the TabView UI.
-  _setTabViewFrameKeyHandlers: function() {
+  _setTabViewFrameKeyHandlers: function UI__setTabViewFrameKeyHandlers() {
     var self = this;
 
     iQ(window).keyup(function(event) {
-      if (!event.metaKey) window.Keys.meta = false;
+      if (!event.metaKey) Keys.meta = false;
     });
 
     iQ(window).keydown(function(event) {
-      if (event.metaKey) window.Keys.meta = true;
+      if (event.metaKey) Keys.meta = true;
 
       if (!self.getActiveTab() || iQ(":focus").length > 0) {
         // prevent the default action when tab is pressed so it doesn't gives
         // us problem with content focus.
         if (event.keyCode == KeyEvent.DOM_VK_TAB) {
           event.stopPropagation();
           event.preventDefault();
         }
@@ -664,17 +662,17 @@ var UIManager = {
       }
     });
   },
 
   // ----------
   // Function: _createGroupItemOnDrag
   // Called in response to a mousedown in empty space in the TabView UI;
   // creates a new groupItem based on the user's drag.
-  _createGroupItemOnDrag: function(e) {
+  _createGroupItemOnDrag: function UI__createGroupItemOnDrag(e) {
     const minSize = 60;
     const minMinSize = 15;
 
     let lastActiveGroupItem = GroupItems.getActiveGroupItem();
     GroupItems.setActiveGroupItem(null);
 
     var startPos = { x: e.clientX, y: e.clientY };
     var phantom = iQ("<div>")
@@ -790,17 +788,17 @@ var UIManager = {
   },
 
   // ----------
   // Function: _resize
   // Update the TabView UI contents in response to a window size change.
   // Won't do anything if it doesn't deem the resize necessary.
   // Parameters:
   //   force - true to update even when "unnecessary"; default false
-  _resize: function(force) {
+  _resize: function UI__resize(force) {
     if (typeof force == "undefined")
       force = false;
 
     // If TabView isn't focused and is not showing, don't perform a resize.
     // This resize really slows things down.
     if (!force && !this._isTabViewVisible())
       return;
 
@@ -875,17 +873,17 @@ var UIManager = {
 
     this._pageBounds = Items.getPageBounds();
     this._save();
   },
 
   // ----------
   // Function: _addDevMenu
   // Fills out the "dev menu" in the TabView UI.
-  _addDevMenu: function() {
+  _addDevMenu: function UI__addDevMenu() {
     try {
       var self = this;
 
       var $select = iQ("<select>")
         .css({
           position: "absolute",
           bottom: 5,
           right: 5,
@@ -947,67 +945,67 @@ var UIManager = {
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // -----------
   // Function: _reset
   // Wipes all TabView storage and refreshes, giving you the "first-run" state.
-  _reset: function() {
+  _reset: function UI__reset() {
     Storage.wipe();
     location.href = "";
   },
 
   // ----------
   // Function: storageSanity
   // Given storage data for this object, returns true if it looks valid.
-  _storageSanity: function(data) {
+  _storageSanity: function UI__storageSanity(data) {
     if (Utils.isEmptyObject(data))
       return true;
 
     if (!Utils.isRect(data.pageBounds)) {
       Utils.log("UI.storageSanity: bad pageBounds", data.pageBounds);
       data.pageBounds = null;
       return false;
     }
 
     return true;
   },
 
   // ----------
   // Function: _save
   // Saves the data for this object to persistent storage
-  _save: function() {
+  _save: function UI__save() {
     if (!this._frameInitalized)
       return;
 
     var data = {
       pageBounds: this._pageBounds
     };
 
     if (this._storageSanity(data))
       Storage.saveUIData(gWindow, data);
   },
 
   // ----------
   // Function: _saveAll
   // Saves all data associated with TabView.
   // TODO: Save info items
-  _saveAll: function() {
+  _saveAll: function UI__saveAll() {
     this._save();
     GroupItems.saveAll();
     TabItems.saveAll();
   },
 
   // ----------
   // Function: _arrangeBySite
   // Blows away all existing groupItems and organizes the tabs into new groupItems based
   // on domain.
-  _arrangeBySite: function() {
+  _arrangeBySite: function UI__arrangeBySite() {
     function putInGroupItem(set, key) {
       var groupItem = GroupItems.getGroupItemWithTitle(key);
       if (groupItem) {
         set.forEach(function(el) {
           groupItem.add(el);
         });
       } else
         new GroupItem(set, { dontPush: true, dontArrange: true, title: key });
@@ -1045,12 +1043,9 @@ var UIManager = {
     if (leftovers.length)
       putInGroupItem(leftovers, "mixed");
 
     GroupItems.arrange();
   },
 };
 
 // ----------
-window.UI = UIManager;
-window.UI.init();
-
-})();
+UI.init();
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -144,16 +144,17 @@ endif
                  browser_bug562649.js \
                  browser_bug563588.js \
                  browser_bug577121.js \
                  browser_bug579872.js \
                  browser_bug580956.js \
                  browser_bug581242.js \
                  browser_bug581947.js \
                  browser_bug585830.js \
+                 browser_bug592338.js \
                  browser_contextSearchTabPosition.js \
                  browser_ctrlTab.js \
                  browser_discovery.js \
                  browser_duplicateIDs.js \
                  browser_gestureSupport.js \
                  browser_getshortcutoruri.js \
                  browser_hide_removing.js \
                  browser_inspector_initialization.js \
@@ -183,16 +184,17 @@ endif
                  browser_selectTabAtIndex.js \
                  browser_tabfocus.js \
                  browser_tabs_owner.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
                  browser_visibleTabs_bookmarkAllTabs.js \
                  browser_visibleTabs_tabPreview.js \
+                 bug592338.html \
                  discovery.html \
                  domplate_test.js \
                  moz.png \
                  test_bug435035.html \
                  test_bug462673.html \
                  page_style_sample.html \
                  feed_tab.html \
                  plugin_unknown.html \
--- a/browser/base/content/test/browser_bug553455.js
+++ b/browser/base/content/test/browser_bug553455.js
@@ -428,17 +428,29 @@ function test_theme() {
       is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
       is(notification.button.label, "Restart Now", "Should have seen the right button");
       is(notification.getAttribute("label"),
          "Theme Test will be installed after you restart " + gApp + ".",
          "Should have seen the right message");
 
       gBrowser.removeTab(gBrowser.selectedTab);
       Services.perms.remove("example.com", "install");
-      runNextTest();
+
+      AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", function(aAddon) {
+        ok(aAddon.userDisabled, "Should be switching away from the default theme.");
+        // Undo the pending theme switch
+        aAddon.userDisabled = false;
+
+        AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
+          isnot(aAddon, null, "Test theme will have been installed");
+          aAddon.uninstall();
+
+          runNextTest();
+        });
+      });
     });
   });
 },
 
 function test_renotify_blocked() {
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "unsigned.xpi"
   }));
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug592338.js
@@ -0,0 +1,134 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/";
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+function wait_for_notification(aCallback) {
+  PopupNotifications.panel.addEventListener("popupshown", function() {
+    PopupNotifications.panel.removeEventListener("popupshown", arguments.callee, false);
+    aCallback(PopupNotifications.panel);
+  }, false);
+}
+
+var TESTS = [
+function test_install_lwtheme() {
+  is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected");
+
+  var pm = Services.perms;
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  gBrowser.selectedTab = gBrowser.addTab("http://example.com/browser/browser/base/content/test/bug592338.html");
+  gBrowser.selectedBrowser.addEventListener("pageshow", function() {
+    if (gBrowser.contentDocument.location.href == "about:blank")
+      return;
+
+    gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
+
+    executeSoon(function() {
+      var link = gBrowser.contentDocument.getElementById("theme-install");
+      EventUtils.synthesizeMouse(link, 2, 2, {}, gBrowser.contentWindow);
+
+      is(LightweightThemeManager.currentTheme.id, "test", "Should have installed the test theme");
+
+      LightweightThemeManager.currentTheme = null;
+      gBrowser.removeTab(gBrowser.selectedTab);
+
+      Services.perms.remove("example.com", "install");
+
+      runNextTest();
+    });
+  }, false);
+},
+
+function test_lwtheme_switch_theme() {
+  is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected");
+
+  AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
+    aAddon.userDisabled = false;
+    ok(aAddon.isActive, "Theme should have immediately enabled");
+    Services.prefs.setBoolPref("extensions.dss.enabled", false);
+
+    var pm = Services.perms;
+    pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+    gBrowser.selectedTab = gBrowser.addTab("http://example.com/browser/browser/base/content/test/bug592338.html");
+    gBrowser.selectedBrowser.addEventListener("pageshow", function() {
+      if (gBrowser.contentDocument.location.href == "about:blank")
+        return;
+
+      gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee, false);
+
+      executeSoon(function() {
+        var link = gBrowser.contentDocument.getElementById("theme-install");
+        wait_for_notification(function(aPanel) {
+          is(LightweightThemeManager.currentTheme, null, "Should not have installed the test lwtheme");
+          ok(aAddon.isActive, "Test theme should still be active");
+
+          let notification = aPanel.childNodes[0];
+          is(notification.button.label, "Restart Now", "Should have seen the right button");
+
+          ok(aAddon.userDisabled, "Should be waiting to disable the test theme");
+          aAddon.userDisabled = false;
+          Services.prefs.setBoolPref("extensions.dss.enabled", true);
+
+          gBrowser.removeTab(gBrowser.selectedTab);
+
+          Services.perms.remove("example.com", "install");
+
+          runNextTest();
+        });
+        EventUtils.synthesizeMouse(link, 2, 2, {}, gBrowser.contentWindow);
+      });
+    }, false);
+  });
+}
+];
+
+function runNextTest() {
+  AddonManager.getAllInstalls(function(aInstalls) {
+    is(aInstalls.length, 0, "Should be no active installs");
+
+    if (TESTS.length == 0) {
+      AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
+        aAddon.uninstall();
+
+        Services.prefs.setBoolPref("extensions.logging.enabled", false);
+        Services.prefs.setBoolPref("extensions.dss.enabled", false);
+
+        finish();
+      });
+      return;
+    }
+
+    info("Running " + TESTS[0].name);
+    TESTS.shift()();
+  });
+};
+
+function test() {
+  waitForExplicitFinish();
+
+  Services.prefs.setBoolPref("extensions.logging.enabled", true);
+
+  AddonManager.getInstallForURL(TESTROOT + "theme.xpi", function(aInstall) {
+    aInstall.addListener({
+      onInstallEnded: function(aInstall, aAddon) {
+        AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
+          isnot(aAddon, null, "Should have installed the test theme.");
+
+          // In order to switch themes while the test is running we turn on dynamic
+          // theme switching. This means the test isn't exactly correct but should
+          // do some good
+          Services.prefs.setBoolPref("extensions.dss.enabled", true);
+
+          runNextTest();
+        });
+      }
+    });
+
+    aInstall.install();
+  }, "application/x-xpinstall");
+}
--- a/browser/base/content/test/browser_discovery.js
+++ b/browser/base/content/test/browser_discovery.js
@@ -31,31 +31,31 @@ var iconDiscoveryTests = [
   { href: "notthere.png", text: "404'd icon is removed properly" },
   { href: "data:image/x-icon,%00", type: "image/x-icon", text: "data: URIs work" },
   { type: "image/png; charset=utf-8", text: "type may have optional parameters (RFC2046)" }
 ];
 
 function runIconDiscoveryTest() {
   var test = iconDiscoveryTests[0];
   var head = doc().getElementById("linkparent");
-  var hasSrc = gProxyFavIcon.hasAttribute("src");
+  var hasSrc = gBrowser.getIcon() != null;
   if (test.pass)
     ok(hasSrc, test.text);
   else
     ok(!hasSrc, test.text);
 
   head.removeChild(head.getElementsByTagName('link')[0]);
   iconDiscoveryTests.shift();
   iconDiscovery(); // Run the next test.
 }
 
 function iconDiscovery() {
   setHandlerFunc(runIconDiscoveryTest);
   if (iconDiscoveryTests.length) {
-    gProxyFavIcon.removeAttribute("src");
+    gBrowser.setIcon(gBrowser.selectedTab, null);
 
     var test = iconDiscoveryTests[0];
     var head = doc().getElementById("linkparent");
     var link = doc().createElement("link");
 
     var rel = test.rel || "icon";
     var href = test.href || "chrome://mochikit/content/browser/browser/base/content/test/moz.png";
     var type = test.type || "image/png";
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/bug592338.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<script type="text/javascript">
+var theme = {
+  id: "test",
+  name: "Test Background",
+  headerURL: "http://example.com/firefox/personas/01/header.jpg",
+  footerURL: "http://example.com/firefox/personas/01/footer.jpg",
+  textcolor: "#fff",
+  accentcolor: "#6b6b6b"
+};
+
+function setTheme(node) {
+  node.setAttribute("data-browsertheme", JSON.stringify(theme));
+  var event = document.createEvent("Events");
+  event.initEvent("InstallBrowserTheme", true, false);
+  node.dispatchEvent(event);
+}
+</script>
+</head>
+<body>
+<a id="theme-install" href="#" onclick="setTheme(this)">Install</a>
+</body>
+</html>
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -716,9 +716,31 @@
         let link = document.getAnonymousElementByAttribute(this, "anonid", "learnmore");
         link.value = gNavigatorBundle.getString("geolocation.learnMore");
         
         let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
         link.href = formatter.formatURLPref("browser.geolocation.warning.infoURL");
       ]]></constructor>
     </implementation>
   </binding>
+
+  <binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem">
+    <implementation>
+      <constructor><![CDATA[
+        this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
+        // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
+        // 592424 is fixed
+        document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
+      ]]></constructor>
+    </implementation>
+  </binding>
+
+  <binding id="menuitem-iconic-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem-iconic">
+    <implementation>
+      <constructor><![CDATA[
+        this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
+        // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
+        // 592424 is fixed
+        document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
+      ]]></constructor>
+    </implementation>
+  </binding>
 </bindings>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -61,16 +61,17 @@ browser.jar:
 *       content/browser/aboutSyncTabs.xul             (content/aboutSyncTabs.xul)
         content/browser/aboutSyncTabs.js              (content/aboutSyncTabs.js)
         content/browser/aboutSyncTabs.css             (content/aboutSyncTabs.css)
 *       content/browser/aboutSyncTabs-bindings.xml    (content/aboutSyncTabs-bindings.xml)
 *       content/browser/syncSetup.xul                 (content/syncSetup.xul)
         content/browser/syncSetup.js                  (content/syncSetup.js)
 *       content/browser/syncGenericChange.xul         (content/syncGenericChange.xul)
         content/browser/syncGenericChange.js          (content/syncGenericChange.js)
+*       content/browser/syncKey.xhtml                 (content/syncKey.xhtml)
 *       content/browser/syncNotification.xml          (content/syncNotification.xml)
         content/browser/syncUtils.js                  (content/syncUtils.js)
 #endif
 # XXX: We should exclude this one as well (bug 71895)
 *       content/browser/hiddenWindow.xul              (content/hiddenWindow.xul)
 #ifdef XP_MACOSX
 *       content/browser/macBrowserOverlay.xul         (content/macBrowserOverlay.xul)
 *       content/browser/downloadManagerOverlay.xul    (content/downloadManagerOverlay.xul)
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -16,16 +16,17 @@
  * The Initial Developer of the Original Code is the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Edward Lee <edilee@mozilla.com>
  *   Mike Connor <mconnor@mozilla.com>
  *   Paul O’Shannessy <paul@oshannessy.com>
+ *   Philipp von Weitershausen <philipp@weitershausen.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
@@ -118,19 +119,16 @@ let gSyncPane = {
       this.page = PAGE_NO_ACCOUNT;
     else {
       this.page = PAGE_HAS_ACCOUNT;
       document.getElementById("currentUser").value = Weave.Service.username;
       document.getElementById("syncComputerName").value = Weave.Clients.localName;
       if (Weave.Status.service == Weave.LOGIN_FAILED)
         this.onLoginError();
       this.updateConnectButton();
-      let syncEverything = this._checkDefaultValues();
-      document.getElementById("weaveSyncMode").selectedIndex = syncEverything ? 0 : 1;
-      document.getElementById("syncModeOptions").selectedIndex = syncEverything ? 0 : 1;
       document.getElementById("tosPP").hidden = this._usingCustomServer;
     }
   },
 
   updateConnectButton: function () {
     let str = Weave.Service.isLoggedIn ? this._stringBundle.GetStringFromName("disconnect.label")
                                        : this._stringBundle.GetStringFromName("connect.label");
     document.getElementById("connectButton").label = str;
@@ -173,45 +171,16 @@ let gSyncPane = {
 
   resetPass: function () {
     if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED)
       gSyncUtils.resetPassword();
     else
       gSyncUtils.resetPassphrase();
   },
 
-  updateSyncPrefs: function () {
-    let syncEverything = document.getElementById("weaveSyncMode").selectedItem.value == "syncEverything";
-    document.getElementById("syncModeOptions").selectedIndex = syncEverything ? 0 : 1;
-
-    if (syncEverything) {
-      let prefs = this.prefArray;
-      for (let i = 0; i < prefs.length; ++i)
-        document.getElementById(prefs[i]).value = true;
-    }
-  },
-
-  /**
-   * Check whether all the preferences values are set to their default values
-   *
-   * @param aPrefs an array of pref names to check for
-   * @returns boolean true if all of the prefs are set to their default values,
-   *                  false otherwise
-   */
-  _checkDefaultValues: function () {
-    let prefs = this.prefArray;
-    for (let i = 0; i < prefs.length; ++i) {
-      let pref = document.getElementById(prefs[i]);
-      if (pref.value != pref.defaultValue)
-        return false;
-    }
-    return true;
-  },
-
-
   handleExpanderClick: function () {
     //XXXzpao Might be fixed in bug 583441, otherwise we'll need a new bug.
     // ok, this is pretty evil, and likely fragile if the prefwindow
     // binding changes, but that won't happen in 3.6 *fingers crossed*
     let prefwindow = document.documentElement;
     let pane = document.getElementById("paneSync");
     if (prefwindow._shouldAnimate)
       prefwindow._currentHeight = pane.contentHeight;
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -18,16 +18,17 @@
 # The Initial Developer of the Original Code is the Mozilla Foundation.
 # Portions created by the Initial Developer are Copyright (C) 2009
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Edward Lee <edilee@mozilla.com>
 #   Mike Connor <mconnor@mozilla.com>
 #   Paul O’Shannessy <paul@oshannessy.com>
+#   Philipp von Weitershausen <philipp@weitershausen.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
@@ -116,17 +117,17 @@
                 <row id="manageAccountControls" hidden="true">
                   <spacer/>
                     <vbox class="indent">
                       <label class="text-link"
                              onclick="gSyncUtils.changePassword(); return false;"
                              value="&changePassword.label;"/>
                       <label class="text-link"
                              onclick="gSyncUtils.resetPassphrase(); return false;"
-                             value="&changePassphrase.label;"/>
+                             value="&mySyncKey.label;"/>
                       <label class="text-link"
                              onclick="gSyncPane.resetSync(); return false;"
                              value="&resetSync.label;"/>
                       <label class="text-link"
                              onclick="gSyncPane.startOver(true); return false;"
                              value="&differentAccount.label;"/>
                     </vbox>
                   <spacer/>
@@ -142,61 +143,51 @@
                   <spacer/>
                 </row>
               </rows>
             </grid>
           </groupbox>
           <groupbox>
             <caption label="&syncPrefsCaption.label;"/>
             <grid>
+              <columns>
+                <column/>
+                <column flex="1"/>
+              </columns>
               <rows>
                 <row align="center">
                   <label value="&syncComputerName.label;"
                          accesskey="&syncComputerName.accesskey;"
                          control="syncComputerName"/>
                   <textbox id="syncComputerName"
                            onchange="gSyncUtils.changeName(this)"/>
                 </row>
-                <row align="center">
-              <label value="&syncModeSwitchDesc.label;"
-                     accesskey="&syncModeSwitchDesc.accesskey;"
-                     control="weaveSyncMode"/>
-              <menulist id="weaveSyncMode" oncommand="gSyncPane.updateSyncPrefs()">
-                <menupopup>
-                  <menuitem label="&syncEverything.label;" value="syncEverything"/>
-                  <menuitem label="&customSync.label;"     value="customSync"/>
-                </menupopup>
-              </menulist>
+                <row>
+                  <label value="&syncMy.label;" />
+                  <vbox>
+                    <checkbox label="&engine.bookmarks.label;"
+                              accesskey="&engine.bookmarks.accesskey;"
+                              preference="engine.bookmarks"/>
+                    <checkbox label="&engine.passwords.label;"
+                              accesskey="&engine.passwords.accesskey;"
+                              preference="engine.passwords"/>
+                    <checkbox label="&engine.prefs.label;"
+                              accesskey="&engine.prefs.accesskey;"
+                              preference="engine.prefs"/>
+                    <checkbox label="&engine.history.label;"
+                              accesskey="&engine.history.accesskey;"
+                              preference="engine.history"/>
+                    <checkbox label="&engine.tabs.label;"
+                              accesskey="&engine.tabs.accesskey;"
+                              preference="engine.tabs"/>
+                  </vbox>
                 </row>
               </rows>
             </grid>
             <separator/>
-            <deck id="syncModeOptions" class="indent">
-              <description id="syncEverythingDesc">
-                &syncEverythingDescription.label;
-              </description>
-              <vbox>
-                <checkbox label="&syncItem.bookmarks.label;"
-                          accesskey="&syncItem.bookmarks.accesskey;"
-                          preference="engine.bookmarks"/>
-                <checkbox label="&syncItem.passwords.label;"
-                          accesskey="&syncItem.passwords.accesskey;"
-                          preference="engine.passwords"/>
-                <checkbox label="&syncItem.prefs.label;"
-                          accesskey="&syncItem.prefs.accesskey;"
-                          preference="engine.prefs"/>
-                <checkbox label="&syncItem.history.label;"
-                          accesskey="&syncItem.history.accesskey;"
-                          preference="engine.history"/>
-                <checkbox label="&syncItem.tabs.label;"
-                          accesskey="&syncItem.tabs.accesskey;"
-                          preference="engine.tabs"/>
-              </vbox>
-            </deck>
-            <separator/>
           </groupbox>
           <hbox id="tosPP" pack="center">
             <label class="text-link"
                    onclick="event.stopPropagation();gSyncUtils.openToS();"
                    value="&prefs.tosLink.label;"/>
             <label class="text-link"
                    onclick="event.stopPropagation();gSyncUtils.openPP();"
                    value="&prefs.ppLink.label;"/>
--- a/browser/components/sidebar/src/nsSidebar.js
+++ b/browser/components/sidebar/src/nsSidebar.js
@@ -192,17 +192,17 @@ function (aDescriptionURL)
   var win = WINMEDSVC.getMostRecentWindow("navigator:browser");
   var browser = win.document.getElementById("content");
   var iconURL = "";
   // Use documentURIObject in the check for shouldLoadFavIcon so that we
   // do the right thing with about:-style error pages.  Bug 453442
   if (browser.shouldLoadFavIcon(browser.selectedBrowser
                                        .contentDocument
                                        .documentURIObject))
-    iconURL = win.gProxyFavIcon.getAttribute("src");
+    iconURL = win.gBrowser.getIcon();
   
   if (!this.validateSearchEngine(aDescriptionURL, iconURL))
     return;
 
   const typeXML = Components.interfaces.nsISearchEngine.DATA_XML;
   this.searchService.addEngine(aDescriptionURL, typeXML, iconURL, true);
 }
 
--- a/browser/locales/en-US/chrome/browser/aboutHome.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutHome.dtd
@@ -1,20 +1,17 @@
 <!ENTITY % brandDTD
     SYSTEM "chrome://branding/locale/brand.dtd">
   %brandDTD;
 
 <!-- These strings are used in the about:home page -->
 
 <!ENTITY abouthome.pageTitle "&brandFullName; Start Page">
-<!ENTITY abouthome.brandLogo.title "&brandFullName; logo">
 
 <!-- LOCALIZATION NOTE (brandStart): brandShortName must be in a <span/> -->
 <!ENTITY abouthome.brandStart "<span>&brandShortName;</span> Start">
 
-<!ENTITY abouthome.searchEngineLogo.title "Search engine logo">
-
 <!ENTITY abouthome.searchEngineButton.label "Search">
 
 <!ENTITY abouthome.searchEngineLinks.advanced "Advanced Search">
 <!ENTITY abouthome.searchEngineLinks.preferences "Preferences">
 
 <!ENTITY abouthome.aboutMozilla "About Mozilla">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -68,16 +68,22 @@ lwthemeInstallRequest.allowButton=Allow
 lwthemeInstallRequest.allowButton.accesskey=a
 
 lwthemePostInstallNotification.message=A new theme has been installed.
 lwthemePostInstallNotification.undoButton=Undo
 lwthemePostInstallNotification.undoButton.accesskey=U
 lwthemePostInstallNotification.manageButton=Manage Themes…
 lwthemePostInstallNotification.manageButton.accesskey=M
 
+# LOCALIZATION NOTE (lwthemeNeedsRestart.message):
+# %S will be replaced with the new theme name.
+lwthemeNeedsRestart.message=%S will be installed after you restart.
+lwthemeNeedsRestart.button=Restart Now
+lwthemeNeedsRestart.accesskey=R
+
 popupWarning=%S prevented this site from opening a pop-up window.
 popupWarningMultiple=%S prevented this site from opening %S pop-up windows.
 popupWarningButton=Options
 popupWarningButton.accesskey=O
 popupWarningButtonUnix=Preferences
 popupWarningButtonUnix.accesskey=P
 popupAllow=Allow pop-ups for %S
 popupBlock=Block pop-ups for %S
--- a/browser/locales/en-US/chrome/browser/preferences/sync.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/sync.dtd
@@ -10,35 +10,33 @@
 <!-- Login error feedback -->
 <!ENTITY updatePass.label             "Update">
 <!ENTITY resetPass.label              "Reset">
 
 <!-- Manage Account -->
 <!ENTITY manageAccount.label          "Manage Account">
 <!ENTITY manageAccount.accesskey      "A">
 <!ENTITY changePassword.label         "Change Password">
-<!ENTITY changePassphrase.label       "Change Secret Phrase">
+<!ENTITY mySyncKey.label              "My Sync Key">
 <!ENTITY resetSync.label              "Reset Sync">
 <!ENTITY differentAccount.label       "Use a Different Account">
 
 <!-- Sync Settings -->
 <!ENTITY syncPrefsCaption.label       "Browser Sync">
 <!ENTITY syncComputerName.label       "Computer Name:">
 <!ENTITY syncComputerName.accesskey   "c">
-<!ENTITY syncModeSwitchDesc.label     "&brandShortName; will: ">
-<!ENTITY syncModeSwitchDesc.accesskey "w">
-<!ENTITY syncEverything.label         "Sync Everything">
-<!ENTITY customSync.label             "Use my custom settings">
-<!ENTITY syncEverythingDescription.label  "Your bookmarks, history, passwords, preferences, and tabs will be synced.">
-<!ENTITY syncItem.bookmarks.label     "Sync Bookmarks">
-<!ENTITY syncItem.bookmarks.accesskey "m">
-<!ENTITY syncItem.tabs.label          "Sync Tabs">
-<!ENTITY syncItem.tabs.accesskey      "T">
-<!ENTITY syncItem.history.label       "Sync History">
-<!ENTITY syncItem.history.accesskey   "r">
-<!ENTITY syncItem.passwords.label     "Sync Passwords">
-<!ENTITY syncItem.passwords.accesskey "P">
-<!ENTITY syncItem.prefs.label         "Sync Preferences">
-<!ENTITY syncItem.prefs.accesskey     "S">
+
+<!ENTITY syncMy.label               "Sync My">
+<!ENTITY engine.bookmarks.label     "Bookmarks">
+<!ENTITY engine.bookmarks.accesskey "m">
+<!ENTITY engine.tabs.label          "Tabs">
+<!ENTITY engine.tabs.accesskey      "T">
+<!ENTITY engine.history.label       "History">
+<!ENTITY engine.history.accesskey   "r">
+<!ENTITY engine.passwords.label     "Passwords">
+<!ENTITY engine.passwords.accesskey "P">
+<!ENTITY engine.prefs.label         "Preferences">
+<!ENTITY engine.prefs.accesskey     "S">
+
 
 <!-- Footer stuff -->
 <!ENTITY prefs.tosLink.label        "Terms of Service">
 <!ENTITY prefs.ppLink.label         "Privacy Policy">
--- a/browser/locales/en-US/chrome/browser/syncGenericChange.properties
+++ b/browser/locales/en-US/chrome/browser/syncGenericChange.properties
@@ -3,35 +3,33 @@ change.password.title = Change your Pass
 change.password.acceptButton = Change Password
 change.password.status.active = Changing your password…
 change.password.status.success = Your password has been changed.
 change.password.status.error = There was an error changing your password.
 
 change.password.introText = Your password must be at least 8 characters long.  It cannot be the same as either your user name or your secret phrase.
 change.password.warningText = Note: All of your other devices will be unable to connect to your account once you change this password.
 
-change.passphrase.title = Change your Secret Phrase
-change.passphrase.acceptButton = Change Secret Phrase
-change.passphrase.label = Changing secret phrase and uploading local data, please wait…
-change.passphrase.error = There was an error while changing your secret phrase!
-change.passphrase.success = Your secret phrase was successfully changed!
+change.synckey.title = Change your Sync Key
+change.synckey.acceptButton = Change Sync Key
+change.synckey.label = Changing Sync Key and uploading local data, please wait…
+change.synckey.error = There was an error while changing your secret phrase!
+change.synckey.success = Your secret phrase was successfully changed!
 
-# LOCALIZATION NOTE (change.passphrase.introText) "Sync" should match &syncBrand.shortName.label; from syncBrand.dtd
-change.passphrase.introText = Your secret phrase must be at least 12 characters long.  Sync uses this phrase as part of encrypting your data.
-# LOCALIZATION NOTE (change.passphrase.introText2) "Firefox Sync" should match syncBrand.fullName.label from syncBrand.dtd
-change.passphrase.introText2 = You may wish to write this down, as this is never sent over the Internet and is not backed up or synced by Firefox Sync for your security.
-# LOCALIZATION NOTE (change.passphrase.warningText) "Sync" should match &syncBrand.shortName.label; from syncBrand.dtd
-change.passphrase.warningText = Note: This will erase all data stored on the Sync server and upload new data secured by this phrase. Your other devices will not sync until the secret phrase is entered for that device.
+change.synckey.introText = Firefox Cares About Your Privacy
+change.synckey.introText2 = To ensure your total privacy, all of your data is encrypted prior to being uploaded. The key to decrypt your data is not uploaded.
+# LOCALIZATION NOTE (change.synckey.warningText) "Sync" should match &syncBrand.shortName.label; from syncBrand.dtd
+change.synckey.warningText = Note: Changing this will erase all data stored on the Sync server and upload new data secured by this phrase. Your other devices will not sync until the new Sync Key is entered for that device.
+
+new.synckey.label = Your Sync Key
 
 # LOCALIZATION NOTE (new.password.title): This (and associated new.password/passphrase) are used on a second computer when it detects that your password or passphrase has been changed on a different device.
 new.password.title            = Update Password
 new.password.introText        = Your password was rejected by the server, please update your password.
 new.password.label            = Enter your new password
 new.password.confirm          = Confirm your new password
 new.password.acceptButton     = Update Password
 new.password.status.incorrect = Password incorrect, please try again.
 
-new.passphrase.title            = Update Secret Phrase
-new.passphrase.introText        = Your secret phrase has changed, please enter your new secret phrase
-new.passphrase.label            = New secret phrase
-new.passphrase.confirm          = Confirm secret phrase
-new.passphrase.acceptButton     = Update Secret Phrase
-new.passphrase.status.incorrect = Secret phrase incorrect, please try again.
+new.synckey.title          = Update Sync Key
+new.synckey.introText        = Your Sync Key has changed, please enter your new Sync Key
+new.synckey.acceptButton     = Update Sync Key
+new.synckey.status.incorrect = Sync Key incorrect, please try again.
--- a/browser/locales/en-US/chrome/browser/syncSetup.dtd
+++ b/browser/locales/en-US/chrome/browser/syncSetup.dtd
@@ -1,98 +1,107 @@
 <!ENTITY accountSetupTitle.label    "&syncBrand.fullName.label; Setup">
 
 <!-- First page of the wizard -->
 
+<!ENTITY setup.pickSetupType.description "Welcome, if you've never used &syncBrand.fullName.label; before, you will need to create a new account.">
+<!ENTITY button.createNewAccount.label "Create a New Account">
+<!ENTITY button.haveAccount.label "I Have a &syncBrand.fullName.label; Account">
+
 <!ENTITY setup.choicePage.title.label     "Have you used &syncBrand.fullName.label; before?">
 <!ENTITY setup.choicePage.new.label       "I've never used &syncBrand.shortName.label; before">
 <!ENTITY setup.choicePage.existing.label  "I'm already using &syncBrand.shortName.label; on another computer">
 
 <!-- New Account AND Existing Account -->
-<!ENTITY connectTo.label            "Connect to">
+<!ENTITY server.label               "Server">
 <!ENTITY serverType.main.label      "&syncBrand.fullName.label; Server">
 <!ENTITY serverType.custom.label    "Use a custom server">
 <!ENTITY signIn.username.label      "User Name">
 <!ENTITY signIn.username.accesskey  "U">
 <!ENTITY signIn.password.label      "Password">
 <!ENTITY signIn.password.accesskey  "P">
 <!ENTITY signIn.serverURL.label     "Server URL">
 <!ENTITY signIn.serverURL.accesskey "L">
 
 <!-- New Account Page 1: Basic Account Info -->
-<!ENTITY setup.newAccountPage.title.label  "Create your &syncBrand.shortName.label; Account">
+<!ENTITY setup.newAccountDetailsPage.title.label "Account Details">
 <!ENTITY setup.confirmPassword.label  "Confirm Password">
 <!ENTITY setup.confirmPassword.accesskey  "m">
 <!ENTITY setup.emailAddress.label     "Email Address">
 <!ENTITY setup.emailAddress.accesskey "E">
 <!-- LOCALIZATION NOTE: tosAgree1, tosLink, tosAgree2, ppLink, tosAgree3 are
      joined with implicit white space, so spaces in the strings aren't necessary -->
 <!ENTITY setup.tosAgree1.label      "I agree to the">
 <!ENTITY setup.tosAgree1.accesskey  "a">
 <!ENTITY setup.tosLink.label        "Terms of Service">
 <!ENTITY setup.tosAgree2.label      "and the">
 <!ENTITY setup.ppLink.label         "Privacy Policy">
 <!ENTITY setup.tosAgree3.label      "">
 <!ENTITY setup.tosAgree2.accesskey  "">
 
-<!-- New Account Page 2: Passphrase Entry -->
-<!ENTITY setup.newPPPage.title.label       "Enter your Secret Phrase">
-<!ENTITY passphraseDesc.label         "Please enter a secret phrase. This will be used to encrypt all your data so only you can access it. It is never stored on the server, so don't lose this!">
-<!ENTITY passphraseDesc2.label        "This must be at least 12 characters long and cannot match your account password.">
-<!ENTITY passphraseEntry.label        "Secret Phrase">
-<!ENTITY passphraseEntry.accesskey    "S">
-<!ENTITY passphraseConfirm.label      "Confirm">
-<!ENTITY passphraseConfirm.accesskey  "m">
+<!-- New Account Page 2: Sync Key -->
+<!ENTITY setup.newSyncKeyPage.title.label "&brandShortName; Cares About Your Privacy">
+<!ENTITY setup.newSyncKeyPage.description.label "To ensure your total privacy, all of your data is encrypted prior to being uploaded. The Sync Key which is necessary to decrypt your data is not uploaded.">
+<!ENTITY syncKeyEntry.label        "Your Sync Key">
+<!ENTITY syncKeyEntry.accesskey    "K">
+<!ENTITY syncKeyGenerate.label     "Generate">
+<!ENTITY syncKeyBackup.description "Your Sync Key is required to access &syncBrand.fullName.label; on other machines. Please create a backup copy. We cannot help you recover your Sync Key.">
 
-<!-- New Account Page 3: Sync Type Options -->
-<!ENTITY setup.newAccountPrefs2.title.label "Browser Sync Preferences">
-<!ENTITY syncComputerName.label       "Computer Name:">
-<!ENTITY syncComputerName.accesskey   "c">
-<!ENTITY syncModeSwitchDesc.label     "&brandShortName; will: ">
-<!ENTITY syncModeSwitchDesc.accesskey "w">
-<!ENTITY syncEverything.label         "Sync Everything">
-<!ENTITY customSync.label             "Use my custom settings">
-<!ENTITY syncEverythingDescription.label  "Your bookmarks, history, passwords, preferences, and tabs will be synced.">
-<!ENTITY syncItem.bookmarks.label     "Sync Bookmarks">
-<!ENTITY syncItem.bookmarks.accesskey "m">
-<!ENTITY syncItem.tabs.label          "Sync Tabs">
-<!ENTITY syncItem.tabs.accesskey      "T">
-<!ENTITY syncItem.history.label       "Sync History">
-<!ENTITY syncItem.history.accesskey   "r">
-<!ENTITY syncItem.passwords.label     "Sync Passwords">
-<!ENTITY syncItem.passwords.accesskey "P">
-<!ENTITY syncItem.prefs.label         "Sync Preferences">
-<!ENTITY syncItem.prefs.accesskey     "S">
+<!ENTITY button.syncKeyBackup.email.label     "Email…">
+<!ENTITY button.syncKeyBackup.email.accesskey "E">
+<!ENTITY button.syncKeyBackup.print.label     "Print…">
+<!ENTITY button.syncKeyBackup.print.accesskey "P">
+<!ENTITY button.syncKeyBackup.save.label      "Save…">
+<!ENTITY button.syncKeyBackup.save.accesskey  "S">
 
-<!-- New Account Page 4: Captcha -->
-<!ENTITY setup.captchaPage.title.label     "Please confirm you're not a robot ;)">
+<!ENTITY page.syncKeyBackup.title     "Your Firefox Sync Key">
+<!ENTITY page.syncKeyBackup.descr     "This secret key is used to encrypt your personal data. It is not uploaded to the server and cannot be recovered. Do not lose it or share with other people.">
+
+<!-- New Account Page 3: Captcha -->
+<!ENTITY setup.captchaPage2.title.label     "Please Confirm You're Not a Robot">
 
 <!-- Existing Account Page 1: Login -->
 <!ENTITY setup.existingAccount.title.label "Enter Account Information">
 <!ENTITY resetPassword.label          "Reset Password">
 <!ENTITY connecting.label             "Connecting…">
 
-<!-- Existing Account Page 2: Passphrase -->
-<!ENTITY passphraseGroupbox.label     "Enter Secret Phrase">
-<!ENTITY passphraseDesc3.label        "Please enter your secret phrase. This must be the same secret phrase that you used to encrypt your data.">
-<!ENTITY passphraseHelp.label         "Your secret phrase is at least 12 characters long and is not your password. You can find your saved secret phrase by going to your other computer and checking the Saved Passwords under Security. If you still cannot get the correct secret phrase, you can choose to reset it, but you will lose any data stored on the server.">
-<!ENTITY changePassphrase.label       "Change Secret Phrase">
+<!-- Existing Account Page 2: Sync Key -->
+<!ENTITY setup.existingSyncKeyPage.title "Please Enter Your Sync Key">
+<!ENTITY setup.existingSyncKeyPage.description "The Sync Key was generated for you, or you entered your own, when you first signed up for &syncBrand.fullName.label;. It is not the same as your password.">
+<!ENTITY existingSyncKeyHelp.description "You can find your saved Sync Key by going to your other computer and checking the Saved Passwords under Security. If you still cannot get the correct Sync Key, you can choose to reset it, but you will lose any data stored on the server.">
+<!ENTITY lostSyncKey.label            "I have lost my Sync Key">
 <!ENTITY verifying.label              "Verifying…">
 
-<!-- New & Existing Account: Merge Options -->
-<!ENTITY setup.mergeChoicePage.title.label "How do you want to start off?">
-<!ENTITY choice.merge.main.label      "Merge this computer's data with your &syncBrand.shortName.label; data">
-<!ENTITY choice.merge.recommend.label "This option is strongly recommended by Mozilla for all users.">
-<!ENTITY choice.client.main.label     "Replace all data on this computer with your &syncBrand.fullName.label; data">
-<!ENTITY choice.server.main.label     "Replace all other devices with your local data">
+<!-- Sync Options -->
+<!ENTITY setup.optionsPage.title      "Sync Options">
+<!ENTITY syncComputerName.label       "Computer Name:">
+<!ENTITY syncComputerName.accesskey   "c">
 
-<!-- New & Existing Account: Confirm Merge Options -->
-<!ENTITY confirm.caption.label        "Confirm your choice">
-<!ENTITY confirm.merge.label          "You have chosen to merge your data on this computer with the data on your other devices running &syncBrand.fullName.label;.">
+<!ENTITY syncMy.label               "Sync My">
+<!ENTITY engine.bookmarks.label     "Bookmarks">
+<!ENTITY engine.bookmarks.accesskey "m">
+<!ENTITY engine.tabs.label          "Tabs">
+<!ENTITY engine.tabs.accesskey      "T">
+<!ENTITY engine.history.label       "History">
+<!ENTITY engine.history.accesskey   "r">
+<!ENTITY engine.passwords.label     "Passwords">
+<!ENTITY engine.passwords.accesskey "P">
+<!ENTITY engine.prefs.label         "Preferences">
+<!ENTITY engine.prefs.accesskey     "S">
+
+<!ENTITY choice2.merge.main.label      "Merge this computer's data with my &syncBrand.shortName.label; data">
+<!ENTITY choice2.merge.recommended.label "Recommended:">
+<!ENTITY choice2.client.main.label     "Replace all data on this computer with my &syncBrand.shortName.label; data">
+<!ENTITY choice2.server.main.label     "Replace all other devices with this computer's data">
+
+<!-- Confirm Merge Options -->
+<!ENTITY setup.optionsConfirmPage.title "Confirm">
+<!ENTITY confirm.merge.label    "&syncBrand.fullName.label; will now merge all this computer's browser data into your Sync account.">
 <!ENTITY confirm.client.label         "All &brandShortName; data on this computer will be deleted, including the following:">
 <!ENTITY confirm.client.moreinfo.label "&brandShortName; will then copy your &syncBrand.fullName.label; data to this computer.">
 <!ENTITY confirm.client.warning.label "WARNING: This will result in all &brandShortName; data on this computer being replaced!">
 <!ENTITY confirm.server.label         "The following devices will be overwritten with your local data:">
 <!ENTITY confirm.server.warning.label "WARNING: Your local data will replace all &brandShortName; data on these devices!">
 
 <!-- New & Existing Account: Setup Complete -->
-<!ENTITY setup.successPage.title.label   "Setup Complete!">
-<!ENTITY setup.successPage.desc.label    "Congratulations! &brandShortName; is now set up to automatically sync your information. Don't forget to install &syncBrand.fullName.label; on all your devices. You can now continue using &brandShortName;.">
+<!ENTITY setup.successPage.title "Setup Complete">
+<!ENTITY changeOptions.label "You can change this preference by selecting Sync Options below.">
+<!ENTITY continueUsing.label "You may now continue using &brandShortName;.">
--- a/browser/locales/en-US/chrome/browser/syncSetup.properties
+++ b/browser/locales/en-US/chrome/browser/syncSetup.properties
@@ -1,16 +1,30 @@
-cancelSetup.label           = Cancel Setup
-errorCreatingAccount.title  = Error Creating Account
+button.syncOptions.label       = Sync Options
+button.syncOptionsDone.label   = Done
+button.syncOptionsCancel.label = Cancel
 
 invalidEmail.label          = Invalid email address
 serverInvalid.label         = Please enter a valid server URL
 usernameNotAvailable.label  = Already in use
 
+verifying.label = Verifying…
+
 # LOCALIZATION NOTE (additionalClients.label, bookmarkCount.label, historyCount.label, passwordCount.label).
 # We'll fix the lack of PluralForms in bug 583661.
 additionalClients.label     = and %S additional devices
 bookmarkCount.label         = %S bookmarks
 historyCount.label          = %S days of history
 passwordCount.label         = %S passwords
 
+email.synckey.subject       = Your Firefox Sync Key
+email.synckey.body          = Congratulations for signing up for Firefox Sync! Your secret Firefox Sync key is %S. Do not lose it or share with other people.
+save.synckey.title = Save Sync Key
+
+newAccount.action.label = Firefox Sync is now set up to automatically sync all of your browser data.
+newAccount.change.label = You can choose exactly what to sync by selecting Sync Options below.
+resetClient.change.label = Firefox Sync will now merge all this computer's browser data into your Sync account.
+wipeClient.change.label = Firefox Sync will now replace all of the browser data on this computer with the data in your Sync account.
+wipeRemote.change.label = Firefox Sync will now replace all of the browser data in your Sync account with the data on this computer.
+existingAccount.change.label = You can change this preference by selecting Sync Options below.
+
 # Several other strings are used (via Weave.Status.login), but they come from
 #  /services/sync */
--- a/browser/themes/gnomestripe/browser/jar.mn
+++ b/browser/themes/gnomestripe/browser/jar.mn
@@ -83,17 +83,14 @@ browser.jar:
   skin/classic/browser/tabview/stack-expander.png     (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png            (tabview/tabview.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-desktopIcon.png
-  skin/classic/browser/sync-merge.png
   skin/classic/browser/sync-mobileIcon.png
   skin/classic/browser/sync-usedBefore.png
   skin/classic/browser/sync-usedNever.png
-  skin/classic/browser/sync-wipeClient.png
-  skin/classic/browser/sync-wipeServer.png
   skin/classic/browser/syncSetup.css
   skin/classic/browser/syncCommon.css
 #endif
deleted file mode 100644
index b99bf3e848b9034c2c41255af1183db51ee6557b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 56871afd21d637257e67b3c85e31f17649328857..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 98ddca3b2f6ff64d172e0db4cd838dd6ebb06971..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/gnomestripe/browser/syncSetup.css
+++ b/browser/themes/gnomestripe/browser/syncSetup.css
@@ -1,55 +1,75 @@
-.wizard-header-icon {
-  list-style-image: url(chrome://browser/skin/sync-32.png);
+wizard {
+  -moz-appearance: none;
+  width: 55em;
+  height: 42em;
+  padding: 0;
+  background-color: Window;
+}
+
+.wizard-page-box {
+  -moz-appearance: none;
+  padding-left: 0;
+  padding-right: 0;
+  margin: 0;
+}
+
+wizardpage {
+  -moz-box-pack: center;
+  -moz-box-align: center;
+  margin: 0;
+  padding: 0 8em;
+  background-color: Window;
 }
 
-wizard,
-.wizard-page-box,
 .wizard-header {
-  -moz-appearance: none !important;
+  -moz-appearance: none;
+  border: none;
+  padding: 2em 0 1em 0;
+  text-align: center;
+}
+.wizard-header-label {
+  font-size: 24pt;
+  font-weight: normal;
+}
+
+.wizard-buttons {
+  border-top: 2px solid #ccd9ea;
+  background-color: #f1f5fb;
+  padding: 1em;
 }
 
-wizard {
-  background-color: #FFF;
-  background: url(chrome://browser/skin/sync-bg.png) #FFF repeat-x center -80px;
+.wizard-buttons-separator {
+  visibility: collapse;
+}
+
+.wizard-header-icon {
+  visibility: collapse;
+}
+
+.accountChoiceButton {
+  font: menu;
+}
+
+.confirm {
+  border: 1px solid black;
+  padding: 1em;
+  -moz-border-radius: 5px;
 }
 
 /* Override the text-link style from global.css */
 .text-link,
 .text-link:focus {
   margin: 0px;
   padding: 0px;
   border: 0px;
 }
 
 
-.accountChoiceButton,
-.mergeChoiceButton {
-  -moz-appearance: none;
-  border: #999999 1px solid !important;
-  padding: 2px 3px;
-  -moz-border-radius: 6px;
-  background-color: menu;
-  -moz-box-shadow:
-    inset rgba(255, 255, 255, 0.5) 0 1px 0px,
-    inset rgba(0, 0, 0, 0.1) 0 -2px 0px,
-    rgba(0, 0, 0, 0.1) 0px 1px 0px;
-}
-
-.accountChoiceButton:hover,
-.mergeChoiceButton[selected="true"] {
-  background-color: -moz-MenuHover;
-  color: -moz-MenuHoverText;
-}
-
-.mergeChoiceButtonBox {
-  padding: 0.2em;
-}
-
 .success,
 .error {
   padding: 2px;
   -moz-border-radius: 2px;
 }
 
 .error {
   background-color: #FF0000 !important;
@@ -70,48 +90,21 @@ wizard {
   font-weight: bold;
   font-size: 100%;
 }
 
 .normal {
   font-size: 100%;
 }
 
-/* Buttons on first page of wizard */
-#newAccount,
-#existingAccount {
-  min-height: 5em;
-}
-#newAccount image {
-  list-style-image: url("chrome://browser/skin/sync-usedNever.png");
-}
-#existingAccount image {
-  list-style-image: url("chrome://browser/skin/sync-usedBefore.png");
-}
-
 .inputColumn {
   -moz-margin-end: 2px
 }
 
-#tosDesc {
-  width: 16em;
-}
-
 #connect-throbber image,
 #passphrase-throbber image {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
-/* Merge choice images */
-#resetClient .mergeChoiceImage,
-#chosenActionMerge image {
-  list-style-image: url("chrome://browser/skin/sync-merge.png");
+#successPageIcon {
+  /* TODO replace this with a 128px version (bug 591122) */
+  list-style-image: url("chrome://browser/skin/sync-32.png");
 }
-
-#wipeClient .mergeChoiceImage,
-#chosenActionWipeClient image {
-  list-style-image: url("chrome://browser/skin/sync-wipeClient.png");
-}
-
-#wipeRemote .mergeChoiceImage,
-#chosenActionWipeServer image {
-  list-style-image: url("chrome://browser/skin/sync-wipeServer.png");
-}
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -1037,17 +1037,17 @@ richlistitem[type~="action"][actiontype=
 .editBookmarkPanelBottomButton:hover:active {
   background-color: #86888B;
 }
 
 .editBookmarkPanelHeaderButton:focus,
 .editBookmarkPanelBottomButton:focus {
   outline: 2px solid -moz-mac-focusring;
   outline-offset: -2px;
-  -moz-outline-radius: 100%;
+  -moz-outline-radius: 10000px;
 }
 
 .editBookmarkPanelBottomButton[default="true"] {
   background-color: #666;
 }
 
 #editBookmarkPanelHeader {
   margin-bottom: 6px;
--- a/browser/themes/pinstripe/browser/jar.mn
+++ b/browser/themes/pinstripe/browser/jar.mn
@@ -123,17 +123,14 @@ browser.jar:
   skin/classic/browser/tabview/stack-expander.png           (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png                  (tabview/tabview.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16-throbber.png
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-desktopIcon.png
-  skin/classic/browser/sync-merge.png
   skin/classic/browser/sync-mobileIcon.png
   skin/classic/browser/sync-usedBefore.png
   skin/classic/browser/sync-usedNever.png
-  skin/classic/browser/sync-wipeClient.png
-  skin/classic/browser/sync-wipeServer.png
   skin/classic/browser/syncSetup.css
   skin/classic/browser/syncCommon.css
 #endif
deleted file mode 100644
index b99bf3e848b9034c2c41255af1183db51ee6557b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 56871afd21d637257e67b3c85e31f17649328857..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 98ddca3b2f6ff64d172e0db4cd838dd6ebb06971..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/pinstripe/browser/syncSetup.css
+++ b/browser/themes/pinstripe/browser/syncSetup.css
@@ -1,55 +1,75 @@
-.wizard-header-icon {
-  list-style-image: url(chrome://browser/skin/sync-32.png);
+wizard {
+  -moz-appearance: none;
+  width: 55em;
+  height: 42em;
+  padding: 0;
+  background-color: Window;
+}
+
+.wizard-page-box {
+  -moz-appearance: none;
+  padding-left: 0;
+  padding-right: 0;
+  margin: 0;
+}
+
+wizardpage {
+  -moz-box-pack: center;
+  -moz-box-align: center;
+  margin: 0;
+  padding: 0 8em;
+  background-color: Window;
 }
 
-wizard,
-.wizard-page-box,
 .wizard-header {
-  -moz-appearance: none !important;
+  -moz-appearance: none;
+  border: none;
+  padding: 2em 0 1em 0;
+  text-align: center;
+}
+.wizard-header-label {
+  font-size: 24pt;
+  font-weight: normal;
+}
+
+.wizard-buttons {
+  border-top: 2px solid #ccd9ea;
+  background-color: #f1f5fb;
+  padding: 1em;
 }
 
-wizard {
-  background-color: #FFF;
-  background: url(chrome://browser/skin/sync-bg.png) #FFF repeat-x center -80px;
+.wizard-buttons-separator {
+  visibility: collapse;
+}
+
+.wizard-header-icon {
+  visibility: collapse;
+}
+
+.accountChoiceButton {
+  font: menu;
+}
+
+.confirm {
+  border: 1px solid black;
+  padding: 1em;
+  -moz-border-radius: 5px;
 }
 
 /* Override the text-link style from global.css */
 .text-link,
 .text-link:focus {
   margin: 0px;
   padding: 0px;
   border: 0px;
 }
 
 
-.accountChoiceButton,
-.mergeChoiceButton {
-  -moz-appearance: none;
-  border: #999999 1px solid !important;
-  padding: 2px 3px;
-  -moz-border-radius: 6px;
-  background-color: menu;
-  -moz-box-shadow:
-    inset rgba(255, 255, 255, 0.5) 0 1px 0px,
-    inset rgba(0, 0, 0, 0.1) 0 -2px 0px,
-    rgba(0, 0, 0, 0.1) 0px 1px 0px;
-}
-
-.accountChoiceButton:hover,
-.mergeChoiceButton[selected="true"] {
-  background-color: -moz-MenuHover;
-  color: -moz-MenuHoverText;
-}
-
-.mergeChoiceButtonBox {
-  padding: 0.2em;
-}
-
 .success,
 .error {
   padding: 2px;
   -moz-border-radius: 2px;
 }
 
 .error {
   background-color: #FF0000 !important;
@@ -70,48 +90,21 @@ wizard {
   font-weight: bold;
   font-size: 100%;
 }
 
 .normal {
   font-size: 100%;
 }
 
-/* Buttons on first page of wizard */
-#newAccount,
-#existingAccount {
-  min-height: 5em;
-}
-#newAccount image {
-  list-style-image: url("chrome://browser/skin/sync-usedNever.png");
-}
-#existingAccount image {
-  list-style-image: url("chrome://browser/skin/sync-usedBefore.png");
-}
-
 .inputColumn {
   -moz-margin-end: 2px
 }
 
-#tosDesc {
-  width: 16em;
-}
-
 #connect-throbber image,
 #passphrase-throbber image {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
-/* Merge choice images */
-#resetClient .mergeChoiceImage,
-#chosenActionMerge image {
-  list-style-image: url("chrome://browser/skin/sync-merge.png");
+#successPageIcon {
+  /* TODO replace this with a 128px version (bug 591122) */
+  list-style-image: url("chrome://browser/skin/sync-32.png");
 }
-
-#wipeClient .mergeChoiceImage,
-#chosenActionWipeClient image {
-  list-style-image: url("chrome://browser/skin/sync-wipeClient.png");
-}
-
-#wipeRemote .mergeChoiceImage,
-#chosenActionWipeServer image {
-  list-style-image: url("chrome://browser/skin/sync-wipeServer.png");
-}
--- a/browser/themes/winstripe/browser/jar.mn
+++ b/browser/themes/winstripe/browser/jar.mn
@@ -102,22 +102,19 @@ browser.jar:
         skin/classic/browser/tabview/stack-expander.png             (tabview/stack-expander.png)
         skin/classic/browser/tabview/tabview.png                    (tabview/tabview.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/sync-16-throbber.png
         skin/classic/browser/sync-16.png
         skin/classic/browser/sync-32.png
         skin/classic/browser/sync-bg.png
         skin/classic/browser/sync-desktopIcon.png
-        skin/classic/browser/sync-merge.png
         skin/classic/browser/sync-mobileIcon.png
         skin/classic/browser/sync-usedBefore.png
         skin/classic/browser/sync-usedNever.png
-        skin/classic/browser/sync-wipeClient.png
-        skin/classic/browser/sync-wipeServer.png
         skin/classic/browser/syncSetup.css
         skin/classic/browser/syncCommon.css
 #endif
 
 #ifdef XP_WIN
 browser.jar:
 % skin browser classic/1.0 %skin/classic/aero/browser/ os=WINNT osversion>=6
         skin/classic/aero/browser/sanitizeDialog.css                       (sanitizeDialog.css)
@@ -220,18 +217,15 @@ browser.jar:
         skin/classic/aero/browser/tabview/stack-expander.png         (tabview/stack-expander.png)
         skin/classic/aero/browser/tabview/tabview.png                (tabview/tabview.png)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/sync-16-throbber.png
         skin/classic/aero/browser/sync-16.png
         skin/classic/aero/browser/sync-32.png
         skin/classic/aero/browser/sync-bg.png
         skin/classic/aero/browser/sync-desktopIcon.png
-        skin/classic/aero/browser/sync-merge.png
         skin/classic/aero/browser/sync-mobileIcon.png
         skin/classic/aero/browser/sync-usedBefore.png
         skin/classic/aero/browser/sync-usedNever.png
-        skin/classic/aero/browser/sync-wipeClient.png
-        skin/classic/aero/browser/sync-wipeServer.png
         skin/classic/aero/browser/syncSetup.css
         skin/classic/aero/browser/syncCommon.css
 #endif
 #endif
deleted file mode 100644
index b99bf3e848b9034c2c41255af1183db51ee6557b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 56871afd21d637257e67b3c85e31f17649328857..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 98ddca3b2f6ff64d172e0db4cd838dd6ebb06971..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/winstripe/browser/syncSetup.css
+++ b/browser/themes/winstripe/browser/syncSetup.css
@@ -1,55 +1,75 @@
-.wizard-header-icon {
-  list-style-image: url(chrome://browser/skin/sync-32.png);
+wizard {
+  -moz-appearance: none;
+  width: 55em;
+  height: 42em;
+  padding: 0;
+  background-color: Window;
+}
+
+.wizard-page-box {
+  -moz-appearance: none;
+  padding-left: 0;
+  padding-right: 0;
+  margin: 0;
+}
+
+wizardpage {
+  -moz-box-pack: center;
+  -moz-box-align: center;
+  margin: 0;
+  padding: 0 8em;
+  background-color: Window;
 }
 
-wizard,
-.wizard-page-box,
 .wizard-header {
-  -moz-appearance: none !important;
+  -moz-appearance: none;
+  border: none;
+  padding: 2em 0 1em 0;
+  text-align: center;
+}
+.wizard-header-label {
+  font-size: 24pt;
+  font-weight: normal;
+}
+
+.wizard-buttons {
+  border-top: 2px solid #ccd9ea;
+  background-color: #f1f5fb;
+  padding: 1em;
 }
 
-wizard {
-  background-color: #FFF;
-  background: url(chrome://browser/skin/sync-bg.png) #FFF repeat-x center -80px;
+.wizard-buttons-separator {
+  visibility: collapse;
+}
+
+.wizard-header-icon {
+  visibility: collapse;
+}
+
+.accountChoiceButton {
+  font: menu;
+}
+
+.confirm {
+  border: 1px solid black;
+  padding: 1em;
+  -moz-border-radius: 5px;
 }
 
 /* Override the text-link style from global.css */
 .text-link,
 .text-link:focus {
   margin: 0px;
   padding: 0px;
   border: 0px;
 }
 
 
-.accountChoiceButton,
-.mergeChoiceButton {
-  -moz-appearance: none;
-  border: #999999 1px solid !important;
-  padding: 2px 3px;
-  -moz-border-radius: 6px;
-  background-color: menu;
-  -moz-box-shadow:
-    inset rgba(255, 255, 255, 0.5) 0 1px 0px,
-    inset rgba(0, 0, 0, 0.1) 0 -2px 0px,
-    rgba(0, 0, 0, 0.1) 0px 1px 0px;
-}
-
-.accountChoiceButton:hover,
-.mergeChoiceButton[selected="true"] {
-  background-color: -moz-MenuHover;
-  color: -moz-MenuHoverText;
-}
-
-.mergeChoiceButtonBox {
-  padding: 0.2em;
-}
-
 .success,
 .error {
   padding: 2px;
   -moz-border-radius: 2px;
 }
 
 .error {
   background-color: #FF0000 !important;
@@ -70,48 +90,21 @@ wizard {
   font-weight: bold;
   font-size: 100%;
 }
 
 .normal {
   font-size: 100%;
 }
 
-/* Buttons on first page of wizard */
-#newAccount,
-#existingAccount {
-  min-height: 5em;
-}
-#newAccount image {
-  list-style-image: url("chrome://browser/skin/sync-usedNever.png");
-}
-#existingAccount image {
-  list-style-image: url("chrome://browser/skin/sync-usedBefore.png");
-}
-
 .inputColumn {
   -moz-margin-end: 2px
 }
 
-#tosDesc {
-  width: 16em;
-}
-
 #connect-throbber image,
 #passphrase-throbber image {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
-/* Merge choice images */
-#resetClient .mergeChoiceImage,
-#chosenActionMerge image {
-  list-style-image: url("chrome://browser/skin/sync-merge.png");
+#successPageIcon {
+  /* TODO replace this with a 128px version (bug 591122) */
+  list-style-image: url("chrome://browser/skin/sync-32.png");
 }
-
-#wipeClient .mergeChoiceImage,
-#chosenActionWipeClient image {
-  list-style-image: url("chrome://browser/skin/sync-wipeClient.png");
-}
-
-#wipeRemote .mergeChoiceImage,
-#chosenActionWipeServer image {
-  list-style-image: url("chrome://browser/skin/sync-wipeServer.png");
-}
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -855,21 +855,22 @@ user_pref("camino.use_system_proxy_setti
                                os.path.join(profileDir, "ssltunnel.cfg")], 
                                env = self.environment(xrePath = xrePath))
       self.log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid)
 
     cmd, args = self.buildCommandLine(app, debuggerInfo, profileDir, testURL, extraArgs)
     startTime = datetime.now()
 
     if debuggerInfo and debuggerInfo["interactive"]:
-      # If an interactive debugger is attached, don't redirect output
-      # and don't use timeouts.
+      # If an interactive debugger is attached, don't redirect output,
+      # don't use timeouts, and don't capture ctrl-c.
       timeout = None
       maxTime = None
       outputPipe = None
+      signal.signal(signal.SIGINT, lambda sigid, frame: None)
     else:
       outputPipe = subprocess.PIPE
 
     self.lastTestSeen = "automation.py"
     proc = self.Process([cmd] + args,
                  env = self.environment(env, xrePath = xrePath,
                                    crashreporter = not debuggerInfo),
                  stdout = outputPipe,
--- a/configure.in
+++ b/configure.in
@@ -4677,16 +4677,36 @@ else
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)ssl$NSS_VERSION.\$(LIB_SUFFIX) \
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)nss$NSS_VERSION.\$(LIB_SUFFIX) \
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)nssutil$NSS_VERSION.\$(LIB_SUFFIX)"
    else
        NSS_LIBS='$(LIBS_DIR)'" -lcrmf -lsmime$NSS_VERSION -lssl$NSS_VERSION -lnss$NSS_VERSION -lnssutil$NSS_VERSION"
    fi
 fi
 
+dnl ======================
+dnl Detect yasm
+dnl ======================
+
+AC_MSG_CHECKING([for YASM assembler])
+AC_CHECK_PROGS(YASM, yasm, "")
+
+if test -n "$YASM"; then
+  dnl Pull out yasm's version string
+  changequote(,)
+  _YASM_VER_FILTER='s|.* ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*|\1|p'
+  changequote([,])
+
+  YASM_VERSION=`yasm --version | sed -nre "$_YASM_VER_FILTER"`
+  _YASM_MAJOR_VERSION=`echo ${YASM_VERSION} | $AWK -F\. '{ print $1 }'`
+  _YASM_MINOR_VERSION=`echo ${YASM_VERSION} | $AWK -F\. '{ print $2 }'`
+  _YASM_RELEASE=`      echo ${YASM_VERSION} | $AWK -F\. '{ print $3 }'`
+  _YASM_BUILD=`        echo ${YASM_VERSION} | $AWK -F\. '{ print $4 }'`
+fi
+
 if test -z "$SKIP_LIBRARY_CHECKS"; then
 dnl system JPEG support
 dnl ========================================================
 MOZ_ARG_WITH_STRING(system-jpeg,
 [  --with-system-jpeg[=PFX]
                           Use system libjpeg [installed at prefix PFX]],
     JPEG_DIR=$withval)
 
@@ -5938,74 +5958,72 @@ MOZ_ARG_DISABLE_BOOL(webm,
 
 if test -n "$MOZ_WEBM"; then
     AC_DEFINE(MOZ_WEBM)
     MOZ_SYDNEYAUDIO=1
     MOZ_MEDIA=1
     MOZ_VORBIS=1
 
     dnl Detect if we can use an assembler to compile optimized assembly for libvpx.
-    dnl For Win32 (MSVC) we must use MASM.
-    if test "$OS_ARCH" = "WINNT" -a "$CPU_ARCH" = "x86" -a -z "$GNU_CC"; then
-        AC_MSG_CHECKING([for MASM assembler])
-        AC_CHECK_PROGS(VPX_AS, ml, "")
-        if test -n "$VPX_AS"; then
-          VPX_X86_ASM=1
-          VPX_AS='sh $(topsrcdir)/media/libvpx/yasm2masm-as.sh'
-        else
-            AC_MSG_ERROR([Need MASM (ml.exe) in order to assemble libvpx optimized assembly. Either disable webm (reconfigure with --disable-webm) or install MASM. MASM is included in the Windows 7 SDK, or you can download MASM directly from http://www.microsoft.com/downloads/details.aspx?familyid=7a1c9da0-0510-44a2-b042-7ef370530c64&displaylang=en])
-        fi
-    else 
-
-      dnl For Darwin x86, Darwin x86_64, Linux x86, and WINNT x86_64
-      dnl we can use YASM.
-      AC_MSG_CHECKING([for YASM assembler])
-      AC_CHECK_PROGS(VPX_AS, $YASM yasm, "")
-      dnl We have YASM, see if we have assembly on this platform.  
-      case "$OS_ARCH:$OS_TEST" in
-      Linux:x86|Linux:i?86)
-        VPX_ASFLAGS="-f elf32 -rnasm -pnasm"
-        VPX_X86_ASM=1
-      ;;
-      Linux:x86_64)
-        VPX_ASFLAGS="-f elf64 -rnasm -pnasm -DPIC"
+    dnl We currently require yasm on all platforms and require yasm 1.1.0 on Win32.
+    VPX_AS=$YASM
+
+    dnl See if we have assembly on this platform.  
+    case "$OS_ARCH:$CPU_ARCH" in
+    Linux:x86)
+      VPX_ASFLAGS="-f elf32 -rnasm -pnasm"
+      VPX_X86_ASM=1
+    ;;
+    Linux:x86_64)
+      VPX_ASFLAGS="-f elf64 -rnasm -pnasm -DPIC"
+      VPX_X86_ASM=1
+    ;;
+    SunOS:x86)
+      VPX_ASFLAGS="-f elf32 -rnasm -pnasm"
+      VPX_X86_ASM=1
+    ;;
+    SunOS:x86_64)
+      VPX_ASFLAGS="-f elf64 -rnasm -pnasm -DPIC"
+      VPX_X86_ASM=1
+    ;;
+    Darwin:x86)
+      VPX_ASFLAGS="-f macho32 -rnasm -pnasm -DPIC"
+      VPX_X86_ASM=1
+    ;;
+    Darwin:x86_64)
+      VPX_ASFLAGS="-f macho64 -rnasm -pnasm -DPIC"
+      VPX_X86_ASM=1
+    ;;
+    WINNT:x86_64)
+      if test -z "$GNU_CC"; then
+        VPX_ASFLAGS="-f x64 -rnasm -pnasm"
         VPX_X86_ASM=1
-      ;;
-      SunOS:i?86)
-        VPX_ASFLAGS="-f elf32 -rnasm -pnasm"
-        VPX_X86_ASM=1
-      ;;
-      SunOS:x86_64)
-        VPX_ASFLAGS="-f elf64 -rnasm -pnasm -DPIC"
-        VPX_X86_ASM=1
-      ;;
-      Darwin:i?86)
-        VPX_ASFLAGS="-f macho32 -rnasm -pnasm -DPIC"
-        VPX_X86_ASM=1
-      ;;
-      Darwin:x86_64)
-        VPX_ASFLAGS="-f macho64 -rnasm -pnasm -DPIC"
-        VPX_X86_ASM=1
-      ;;
-      WINNT:x86_64)
-        if test -z "$GNU_CC"; then
-          VPX_ASFLAGS="-f x64 -rnasm -pnasm"
+      fi
+    ;;
+    WINNT:x86)
+      if test -z "$GNU_CC"; then
+        dnl Check for yasm 1.1 or greater.
+        if test "$_YASM_MAJOR_VERSION" -gt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -ge "1" \) ; then
+          VPX_ASFLAGS="-f win32 -rnasm -pnasm -DPIC"
           VPX_X86_ASM=1
+        else
+          AC_MSG_ERROR([yasm 1.1 or greater is required to build libvpx on Win32, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.  Upgrade to the newest version (included in MozillaBuild 1.5.1 and newer) or configure with --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
         fi
-      ;;
-      esac
-      if test "$COMPILE_ENVIRONMENT" -a -n "$VPX_X86_ASM" -a -z "$VPX_AS"; then
-        AC_MSG_ERROR([yasm is a required build tool for this architecture when webm is enabled. You may either install yasm or --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
       fi
-    fi # end !WINNT_x86_MSVC
+    ;;
+    esac
+
+    if test "$COMPILE_ENVIROMENT" -a -n "$VPX_X86_ASM" -a -z "$VPX_AS"; then
+      AC_MSG_ERROR([yasm is a required build tool for this architecture when webm is enabled. You may either install yasm or --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
+    fi
 
     if test -n "$VPX_X86_ASM"; then
       AC_DEFINE(VPX_X86_ASM)
     else
-      AC_MSG_WARN([No assembler or assembly support for libvpx, using unoptimized C routines.])
+      AC_MSG_WARN([No assembler or assembly support for libvpx. Using unoptimized C routines.])
     fi
 fi
 
 dnl ========================================================
 dnl = Disable Wave decoder support
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(wave,
 [  --disable-wave          Disable Wave decoder support],
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -1848,16 +1848,17 @@ nsIAtom** const kDefaultAllowedAttribute
   &nsGkAtoms::controls,
 #endif
   &nsGkAtoms::compact,
   &nsGkAtoms::coords,
   &nsGkAtoms::datetime,
   &nsGkAtoms::dir,
   &nsGkAtoms::disabled,
   &nsGkAtoms::enctype,
+  &nsGkAtoms::face,
   &nsGkAtoms::_for,
   &nsGkAtoms::frame,
   &nsGkAtoms::headers,
   &nsGkAtoms::height,
   &nsGkAtoms::href,
   &nsGkAtoms::hreflang,
   &nsGkAtoms::hspace,
   &nsGkAtoms::id,
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1522,16 +1522,17 @@ nsDOMImplementation::CreateHTMLDocument(
 // =
 // ==================================================================
 
   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 
 nsDocument::nsDocument(const char* aContentType)
   : nsIDocument()
+  , mAnimatingImages(PR_TRUE)
 {
   SetContentTypeInternal(nsDependentCString(aContentType));
   
 #ifdef PR_LOGGING
   if (!gDocumentLeakPRLog)
     gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak");
 
   if (gDocumentLeakPRLog)
@@ -7374,16 +7375,21 @@ nsDocument::OnPageShow(PRBool aPersisted
     mIsShowing = PR_TRUE;
   }
  
 #ifdef MOZ_SMIL
   if (mAnimationController) {
     mAnimationController->OnPageShow();
   }
 #endif
+
+  if (aPersisted) {
+    SetImagesNeedAnimating(PR_TRUE);
+  }
+
   nsCOMPtr<nsPIDOMEventTarget> target =
     aDispatchStartTarget ? do_QueryInterface(aDispatchStartTarget) :
                            do_QueryInterface(GetWindow());
   DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted);
 }
 
 static PRBool
 NotifyPageHide(nsIDocument* aDocument, void* aData)
@@ -7424,16 +7430,20 @@ nsDocument::OnPageHide(PRBool aPersisted
   }
 
 #ifdef MOZ_SMIL
   if (mAnimationController) {
     mAnimationController->OnPageHide();
   }
 #endif
   
+  if (aPersisted) {
+    SetImagesNeedAnimating(PR_FALSE);
+  }
+
   // Now send out a PageHide event.
   nsCOMPtr<nsPIDOMEventTarget> target =
     aDispatchStartTarget ? do_QueryInterface(aDispatchStartTarget) :
                            do_QueryInterface(GetWindow());
   DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
 
   mVisible = PR_FALSE;
   EnumerateExternalResources(NotifyPageHide, &aPersisted);
@@ -8088,17 +8098,24 @@ nsDocument::AddImage(imgIRequest* aImage
   if (!success)
     return NS_ERROR_OUT_OF_MEMORY;
 
   // If this is the first insertion and we're locking images, lock this image
   // too.
   if ((oldCount == 0) && mLockingImages) {
     nsresult rv = aImage->LockImage();
     NS_ENSURE_SUCCESS(rv, rv);
-    return aImage->RequestDecode();
+    rv = aImage->RequestDecode();
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // If this is the first insertion and we're animating images, request
+  // that this image be animated too.
+  if (oldCount == 0 && mAnimatingImages) {
+    return aImage->IncrementAnimationConsumers();
   }
 
   return NS_OK;
 }
 
 nsresult
 nsDocument::RemoveImage(imgIRequest* aImage)
 {
@@ -8121,16 +8138,21 @@ nsDocument::RemoveImage(imgIRequest* aIm
     mImageTracker.Put(aImage, count);
   }
 
   // If we removed the image from the tracker and we're locking images, unlock
   // this image.
   if ((count == 0) && mLockingImages)
     return aImage->UnlockImage();
 
+  // If we removed the image from the tracker and we're animating images,
+  // remove our request to animate this image.
+  if (count == 0 && mAnimatingImages)
+    return aImage->DecrementAnimationConsumers();
+
   return NS_OK;
 }
 
 PLDHashOperator LockEnumerator(imgIRequest* aKey,
                                PRUint32 aData,
                                void*    userArg)
 {
   aKey->LockImage();
@@ -8159,8 +8181,40 @@ nsDocument::SetImageLockingState(PRBool 
                                       : UnlockEnumerator,
                               nsnull);
 
   // Update state.
   mLockingImages = aLocked;
 
   return NS_OK;
 }
+
+PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey,
+                                             PRUint32 aData,
+                                             void*    userArg)
+{
+  aKey->IncrementAnimationConsumers();
+  return PL_DHASH_NEXT;
+}
+
+PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey,
+                                             PRUint32 aData,
+                                             void*    userArg)
+{
+  aKey->DecrementAnimationConsumers();
+  return PL_DHASH_NEXT;
+}
+
+void
+nsDocument::SetImagesNeedAnimating(PRBool aAnimating)
+{
+  // If there's no change, there's nothing to do.
+  if (mAnimatingImages == aAnimating)
+    return;
+
+  // Otherwise, iterate over our images and perform the appropriate action.
+  mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator
+                                         : DecrementAnimationEnumerator,
+                              nsnull);
+
+  // Update state.
+  mAnimatingImages = aAnimating;
+}
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1139,16 +1139,19 @@ protected:
   // This flag is only set in nsXMLDocument, for e.g. documents used in XBL. We
   // don't want animations to play in such documents, so we need to store the
   // flag here so that we can check it in nsDocument::GetAnimationController.
   PRPackedBool mLoadedAsInteractiveData:1;
 
   // Whether we're currently holding a lock on all of our images.
   PRPackedBool mLockingImages:1;
 
+  // Whether we currently require our images to animate
+  PRPackedBool mAnimatingImages:1;
+
   PRUint8 mXMLDeclarationBits;
 
   PRUint8 mDefaultElementType;
 
   nsInterfaceHashtable<nsVoidPtrHashKey, nsPIBoxObject> *mBoxObjectTable;
 
   // The channel that got passed to StartDocumentLoad(), if any
   nsCOMPtr<nsIChannel> mChannel;
@@ -1245,16 +1248,22 @@ private:
 
   // Tracking for images in the document.
   nsDataHashtable< nsPtrHashKey<imgIRequest>, PRUint32> mImageTracker;
 
 #ifdef DEBUG
 protected:
   PRBool mWillReparent;
 #endif
+
+protected:
+  // Makes the images on this document capable of having their animation
+  // active or suspended. An Image will animate as long as at least one of its
+  // owning Documents needs it to animate; otherwise it can suspend.
+  void SetImagesNeedAnimating(PRBool aAnimating);
 };
 
 #define NS_DOCUMENT_INTERFACE_TABLE_BEGIN(_class)                             \
   NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(_class)                            \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMDocument, nsDocument)      \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMNSDocument, nsDocument)    \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMDocumentEvent, nsDocument) \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMDocumentView, nsDocument)  \
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -1090,18 +1090,32 @@ nsCanvasRenderingContext2D::SetDimension
     gfxIntSize size(width, height);
     if (gfxASurface::CheckSurfaceSize(size, 0xffff)) {
 
         gfxASurface::gfxImageFormat format = GetImageFormat();
 
         if (PR_GetEnv("MOZ_CANVAS_IMAGE_SURFACE")) {
             surface = new gfxImageSurface(gfxIntSize(width, height), format);
         } else {
-            surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface
-                (gfxIntSize(width, height), format);
+            nsCOMPtr<nsIContent> content =
+                do_QueryInterface(static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement));
+            nsIDocument* ownerDoc = nsnull;
+            if (content)
+                ownerDoc = content->GetOwnerDoc();
+            nsRefPtr<LayerManager> layerManager = nsnull;
+
+            if (ownerDoc)
+              layerManager = nsContentUtils::LayerManagerForDocument(ownerDoc);
+
+            if (layerManager) {
+              surface = layerManager->CreateOptimalSurface(gfxIntSize(width, height), format);
+            } else {
+              surface = gfxPlatform::GetPlatform()->
+                CreateOffscreenSurface(gfxIntSize(width, height), format);
+            }
         }
 
         if (surface && surface->CairoStatus() != 0)
             surface = NULL;
 
 #ifdef MOZ_IPC
         if (mIPC && surface) {
 #ifdef MOZ_X11
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -3968,17 +3968,17 @@ nsEventStateManager::SetClickCount(nsPre
 
   case nsMouseEvent::eMiddleButton:
     if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
       mLastMiddleMouseDownContent = mouseContent;
       mLastMiddleMouseDownContentParent = mouseContentParent;
     } else if (aEvent->message == NS_MOUSE_BUTTON_UP) {
       if (mLastMiddleMouseDownContent == mouseContent ||
           mLastMiddleMouseDownContentParent == mouseContent ||
-          mLastLeftMouseDownContent == mouseContentParent) {
+          mLastMiddleMouseDownContent == mouseContentParent) {
         aEvent->clickCount = mMClickCount;
         mMClickCount = 0;
       } else {
         aEvent->clickCount = 0;
       }
       mLastMiddleMouseDownContent = nsnull;
       mLastMiddleMouseDownContentParent = nsnull;
     }
@@ -3986,17 +3986,17 @@ nsEventStateManager::SetClickCount(nsPre
 
   case nsMouseEvent::eRightButton:
     if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
       mLastRightMouseDownContent = mouseContent;
       mLastRightMouseDownContentParent = mouseContentParent;
     } else if (aEvent->message == NS_MOUSE_BUTTON_UP) {
       if (mLastRightMouseDownContent == mouseContent ||
           mLastRightMouseDownContentParent == mouseContent ||
-          mLastLeftMouseDownContent == mouseContentParent) {
+          mLastRightMouseDownContent == mouseContentParent) {
         aEvent->clickCount = mRClickCount;
         mRClickCount = 0;
       } else {
         aEvent->clickCount = 0;
       }
       mLastRightMouseDownContent = nsnull;
       mLastRightMouseDownContentParent = nsnull;
     }
--- a/content/events/test/test_clickevent_on_input.html
+++ b/content/events/test/test_clickevent_on_input.html
@@ -19,38 +19,79 @@
 <pre id="test">
 <script type="application/javascript">
 
 var gClickCount = 0;
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(runTests);
 
+var input = document.getElementById("input");
+
 function runTests()
 {
-  var input = document.getElementById("input");
-  // click on border of input
-  synthesizeMouse(input, 5, 5, { });
-  is(gClickCount, 1, "click event doesn't fired on input element");
+  for (var i = 0; i < 3; i++) {
+    doTest(i);
+  }
+  input.style.display = "none";
+  SimpleTest.finish();
+}
+
+function isEnabledMiddleClickPaste()
+{
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+                         getService(Components.interfaces.nsIPrefBranch2);
+  try {
+    return prefs.getBoolPref("middlemouse.paste");
+  } catch (e) {
+    return false;
+  }
+}
+
+function doTest(aButton)
+{
+  // NOTE #1: Right click causes a context menu to popup, then, the click event
+  //          isn't generated.
+  // NOTE #2: If middle click causes text to be pasted, then, the click event
+  //          isn't generated.
+  if (aButton != 2 && (aButton != 1 || !isEnabledMiddleClickPaste())) {
+    gClickCount = 0;
+    // click on border of input
+    synthesizeMouse(input, 5, 5, { button: aButton });
+    is(gClickCount, 1,
+       "click event doesn't fired on input element (button is " +
+         aButton + ")");
+
+    gClickCount = 0;
+    // down on border
+    synthesizeMouse(input, 5, 5, { type: "mousedown", button: aButton });
+    // up on anonymous div of input
+    synthesizeMouse(input, 20, 20, { type: "mouseup", button: aButton });
+    is(gClickCount, 1,
+       "click event doesn't fired on input element (button is " +
+         aButton + ")");
+
+    gClickCount = 0;
+    // down on anonymous div of input
+    synthesizeMouse(input, 20, 20, { type: "mousedown", button: aButton });
+    // up on border
+    synthesizeMouse(input, 5, 5, { type: "mouseup", button: aButton });
+    is(gClickCount, 1,
+       "click event doesn't fired on input element (button is " +
+         aButton + ")");
+  }
 
   gClickCount = 0;
-  // down on border
-  synthesizeMouse(input, 5, 5, { type: "mousedown" });
-  // up on anonymous div of input
-  synthesizeMouse(input, 20, 20, { type: "mouseup" });
-  is(gClickCount, 1, "click event doesn't fired on input element");
-
-  gClickCount = 0;
-  // down on anonymous div of input
-  synthesizeMouse(input, 20, 20, { type: "mousedown" });
+  // down on outside of input
+  synthesizeMouse(input, -3, -3, { type: "mousedown", button: aButton });
   // up on border
-  synthesizeMouse(input, 5, 5, { type: "mouseup" });
-  is(gClickCount, 1, "click event doesn't fired on input element");
-
-  input.style.display = "none";
-
-  SimpleTest.finish();
+  synthesizeMouse(input, 5, 5, { type: "mouseup", button: aButton });
+  is(gClickCount, 0,
+     "click event is fired on input element unexpectedly (button is " +
+       aButton + ")");
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/extensions/cookie/test/browser_test_favicon.js
+++ b/extensions/cookie/test/browser_test_favicon.js
@@ -1,33 +1,27 @@
 // tests third party cookie blocking using a favicon load directly from chrome.
 // in this case, the docshell of the channel is chrome, not content; thus
 // the cookie should be considered third party.
 
 function test() {
   waitForExplicitFinish();
 
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-                        .getService(Components.interfaces.nsIPrefBranch);
-  prefs.setIntPref("network.cookie.cookieBehavior", 1);
+  Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
 
-  var os = Components.classes["@mozilla.org/observer-service;1"]
-                     .getService(Components.interfaces.nsIObserverService);
-  os.addObserver(function (theSubject, theTopic, theData) {
+  Services.obs.addObserver(function (theSubject, theTopic, theData) {
     var uri = theSubject.QueryInterface(Components.interfaces.nsIURI);
     var domain = uri.host;
 
     if (domain == "example.org") {
       ok(true, "foreign favicon cookie was blocked");
 
-      var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-                            .getService(Components.interfaces.nsIPrefBranch);
-      prefs.setIntPref("network.cookie.cookieBehavior", 0);
+      Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
 
-      os.removeObserver(arguments.callee, "cookie-rejected");
+      Services.obs.removeObserver(arguments.callee, "cookie-rejected");
 
       finish();
     }
   }, "cookie-rejected", false);
 
   // kick off a favicon load
-  PageProxySetIcon("http://example.org/tests/extensions/cookie/test/damonbowling.jpg");
+  gBrowser.setIcon(gBrowser.selectedTab, "http://example.org/tests/extensions/cookie/test/damonbowling.jpg");
 }
--- a/extensions/cookie/test/unit/test_domain_eviction.js
+++ b/extensions/cookie/test/unit/test_domain_eviction.js
@@ -1,14 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 function run_test()
 {
+  // Set the base domain limit to 50 so we have a known value.
+  Services.prefs.setIntPref("network.cookie.maxPerHost", 50);
+
   var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
 
   cm.removeAll();
 
   // test eviction under the 50 cookies per base domain limit. this means
   // that cookies for foo.com and bar.foo.com should count toward this limit,
   // while cookies for baz.com should not. there are several tests we perform
   // to make sure the base domain logic is working correctly.
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -35,16 +35,17 @@
  * 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 ***** */
 
 #include "ImageLayers.h"
 #include "Layers.h"
+#include "gfxPlatform.h"
 
 using namespace mozilla::layers;
  
 #ifdef MOZ_LAYERS_HAVE_LOG
 FILE*
 FILEOrDefault(FILE* aFile)
 {
   return aFile ? aFile : stderr;
@@ -166,16 +167,25 @@ AppendToString(nsACString& s, const Fram
 }
 
 } // namespace <anon>
 
 namespace mozilla {
 namespace layers {
 
 //--------------------------------------------------
+// LayerManager
+already_AddRefed<gfxASurface>
+LayerManager::CreateOptimalSurface(const gfxIntSize &aSize,
+                                   gfxASurface::gfxImageFormat aFormat)
+{
+  return gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, aFormat);
+}
+
+//--------------------------------------------------
 // Layer
 
 PRBool
 Layer::CanUseOpaqueSurface()
 {
   // If the visible content in the layer is opaque, there is no need
   // for an alpha channel.
   if (GetContentFlags() & CONTENT_OPAQUE)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -346,16 +346,24 @@ public:
   virtual already_AddRefed<ImageContainer> CreateImageContainer() = 0;
 
   /**
    * Type of layer manager his is. This is to be used sparsely in order to
    * avoid a lot of Layers backend specific code. It should be used only when
    * Layers backend specific functionality is necessary.
    */
   virtual LayersBackend GetBackendType() = 0;
+ 
+  /**
+   * Creates a layer which is optimized for inter-operating with this layer
+   * manager.
+   */
+  virtual already_AddRefed<gfxASurface>
+    CreateOptimalSurface(const gfxIntSize &aSize,
+                         gfxASurface::gfxImageFormat imageFormat);
 
   /**
    * Return the name of the layer manager's backend.
    */
   virtual void GetBackendName(nsAString& aName) = 0;
 
   /**
    * This setter can be used anytime. The user data for all keys is
--- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "CanvasLayerD3D9.h"
 
 #include "gfxImageSurface.h"
 #include "gfxWindowsSurface.h"
+#include "gfxWindowsPlatform.h"
 
 namespace mozilla {
 namespace layers {
 
 CanvasLayerD3D9::~CanvasLayerD3D9()
 {
 }
 
@@ -66,16 +67,27 @@ CanvasLayerD3D9::Initialize(const Data& 
     mDataIsPremultiplied = aData.mGLBufferIsPremultiplied;
     mNeedsYFlip = PR_TRUE;
   } else {
     NS_ERROR("CanvasLayer created without mSurface or mGLContext?");
   }
 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
 
+  if (mSurface && mSurface->GetType() == gfxASurface::SurfaceTypeD2D) {
+    void *data = mSurface->GetData(&gKeyD3D9Texture);
+    if (data) {
+      mTexture = static_cast<IDirect3DTexture9*>(data);
+      mIsInteropTexture = true;
+      return;
+    }
+  }
+
+  mIsInteropTexture = false;
+
   if (mD3DManager->deviceManager()->HasDynamicTextures()) {
     device()->CreateTexture(mBounds.width, mBounds.height, 1, D3DUSAGE_DYNAMIC,
                             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
                             getter_AddRefs(mTexture), NULL);    
   } else {
     // D3DPOOL_MANAGED is fine here since we require Dynamic Textures for D3D9Ex
     // devices.
     device()->CreateTexture(mBounds.width, mBounds.height, 1, 0,
@@ -87,16 +99,24 @@ CanvasLayerD3D9::Initialize(const Data& 
 void
 CanvasLayerD3D9::Updated(const nsIntRect& aRect)
 {
   if (!mTexture) {
     NS_WARNING("CanvasLayerD3D9::Updated called but no texture present!");
     return;
   }
 
+#ifdef CAIRO_HAS_D2D_SURFACE
+  if (mIsInteropTexture) {
+    mSurface->Flush();
+    cairo_d2d_finish_device(gfxWindowsPlatform::GetPlatform()->GetD2DDevice());
+    return;
+  }
+#endif
+
   if (mGLContext) {
     // WebGL reads entire surface.
     D3DLOCKED_RECT r;
     mTexture->LockRect(0, &r, NULL, 0);
 
     PRUint8 *destination;
     if (r.Pitch != mBounds.width * 4) {
       destination = new PRUint8[mBounds.width * mBounds.height * 4];
--- a/gfx/layers/d3d9/CanvasLayerD3D9.h
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.h
@@ -69,16 +69,19 @@ public:
 
   // LayerD3D9 implementation
   virtual Layer* GetLayer();
   virtual void RenderLayer();
 
 protected:
   typedef mozilla::gl::GLContext GLContext;
 
+  // Indicates whether our texture was obtained through D2D interop.
+  bool mIsInteropTexture;
+
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<GLContext> mGLContext;
 
   PRUint32 mCanvasFramebuffer;
 
   nsRefPtr<IDirect3DTexture9> mTexture;
 
   nsIntRect mBounds;
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -39,16 +39,20 @@
 
 #include "ThebesLayerD3D9.h"
 #include "ContainerLayerD3D9.h"
 #include "ImageLayerD3D9.h"
 #include "ColorLayerD3D9.h"
 #include "CanvasLayerD3D9.h"
 #include "nsIServiceManager.h"
 #include "nsIPrefService.h"
+#include "gfxWindowsPlatform.h"
+#ifdef CAIRO_HAS_D2D_SURFACE
+#include "gfxD2DSurface.h"
+#endif
 
 namespace mozilla {
 namespace layers {
 
 DeviceManagerD3D9 *LayerManagerD3D9::mDeviceManager = nsnull;
 
 LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget)
   : mIs3DEnabled(PR_FALSE)
@@ -179,16 +183,61 @@ LayerManagerD3D9::CreateCanvasLayer()
 
 already_AddRefed<ImageContainer>
 LayerManagerD3D9::CreateImageContainer()
 {
   nsRefPtr<ImageContainer> container = new ImageContainerD3D9(this);
   return container.forget();
 }
 
+cairo_user_data_key_t gKeyD3D9Texture;
+
+void ReleaseTexture(void *texture)
+{
+  static_cast<IDirect3DTexture9*>(texture)->Release();
+}
+
+already_AddRefed<gfxASurface>
+LayerManagerD3D9::CreateOptimalSurface(const gfxIntSize &aSize,
+                                   gfxASurface::gfxImageFormat aFormat)
+{
+#ifdef CAIRO_HAS_D2D_SURFACE
+  if ((aFormat != gfxASurface::ImageFormatRGB24 &&
+       aFormat != gfxASurface::ImageFormatARGB32) ||
+      gfxWindowsPlatform::GetPlatform()->GetRenderMode() !=
+        gfxWindowsPlatform::RENDER_DIRECT2D ||
+      !deviceManager()->IsD3D9Ex()) {
+    return LayerManager::CreateOptimalSurface(aSize, aFormat);
+  }
+
+  nsRefPtr<IDirect3DTexture9> texture;
+  
+  HANDLE sharedHandle = 0;
+  device()->CreateTexture(aSize.width, aSize.height, 1,
+                          D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
+                          D3DPOOL_DEFAULT, getter_AddRefs(texture), &sharedHandle);
+
+  nsRefPtr<gfxD2DSurface> surface =
+    new gfxD2DSurface(sharedHandle, aFormat == gfxASurface::ImageFormatRGB24 ?
+      gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA);
+
+  if (!surface || surface->CairoStatus()) {
+    return LayerManager::CreateOptimalSurface(aSize, aFormat);
+  }
+
+  surface->SetData(&gKeyD3D9Texture,
+                   texture.forget().get(),
+                   ReleaseTexture);
+
+  return surface.forget();
+#else
+  return LayerManager::CreateOptimalSurface(aSize, aFormat);
+#endif
+}
+
 void
 LayerManagerD3D9::Render()
 {
   if (!mSwapChain->PrepareForRendering()) {
     return;
   }
   deviceManager()->SetupRenderState();
 
@@ -250,16 +299,22 @@ LayerManagerD3D9::SetupPipeline()
   viewMatrix[3][1] = 1.0f;
   viewMatrix[3][3] = 1.0f;
 
   HRESULT hr = device()->SetVertexShaderConstantF(8, &viewMatrix[0][0], 4);
 
   if (FAILED(hr)) {
     NS_WARNING("Failed to set projection shader constant!");
   }
+
+  hr = device()->SetVertexShaderConstantF(13, ShaderConstantRect(0, 0, 1.0f, 1.0f), 1);
+
+  if (FAILED(hr)) {
+    NS_WARNING("Failed to set texCoords shader constant!");
+  }
 }
 
 void
 LayerManagerD3D9::PaintToTarget()
 {
   nsRefPtr<IDirect3DSurface9> backBuff;
   nsRefPtr<IDirect3DSurface9> destSurf;
   device()->GetRenderTarget(0, getter_AddRefs(backBuff));
--- a/gfx/layers/d3d9/LayerManagerD3D9.h
+++ b/gfx/layers/d3d9/LayerManagerD3D9.h
@@ -46,19 +46,39 @@
 #include "gfxContext.h"
 #include "nsIWidget.h"
 
 #include "DeviceManagerD3D9.h"
 
 namespace mozilla {
 namespace layers {
 
+extern cairo_user_data_key_t gKeyD3D9Texture;
+
 class LayerD3D9;
 class ThebesLayerD3D9;
 
+/**
+ * This structure is used to pass rectangles to our shader constant. We can use
+ * this for passing rectangular areas to SetVertexShaderConstant. In the format
+ * of a 4 component float(x,y,width,height). Our vertex shader can then use
+ * this to construct rectangular positions from the 0,0-1,1 quad that we source
+ * it with.
+ */
+struct ShaderConstantRect
+{
+  float mX, mY, mWidth, mHeight;
+  ShaderConstantRect(float aX, float aY, float aWidth, float aHeight)
+    : mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight)
+  { }
+
+  // For easy passing to SetVertexShaderConstantF.
+  operator float* () { return &mX; }
+};
+
 /*
  * This is the LayerManager used for Direct3D 9. For now this will render on
  * the main thread.
  */
 class THEBES_API LayerManagerD3D9 : public LayerManager {
 public:
   LayerManagerD3D9(nsIWidget *aWidget);
   virtual ~LayerManagerD3D9();
@@ -112,16 +132,20 @@ public:
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
 
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
 
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
 
   virtual already_AddRefed<ImageContainer> CreateImageContainer();
 
+  virtual already_AddRefed<gfxASurface>
+    CreateOptimalSurface(const gfxIntSize &aSize,
+                         gfxASurface::gfxImageFormat imageFormat);
+
   virtual LayersBackend GetBackendType() { return LAYERS_D3D9; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Direct3D 9"); }
 
   /*
    * Helper methods.
    */
   void SetClippingEnabled(PRBool aEnabled);
 
--- a/gfx/layers/d3d9/LayerManagerD3D9Shaders.h
+++ b/gfx/layers/d3d9/LayerManagerD3D9Shaders.h
@@ -1,160 +1,172 @@
 #if 0
 //
-// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
 //
 //   fxc LayerManagerD3D9Shaders.hlsl -ELayerQuadVS -nologo -FhtmpShaderHeader
 //    -VnLayerQuadVS
 //
 //
 // Parameters:
 //
 //   float4x4 mLayerQuadTransform;
 //   float4x4 mLayerTransform;
 //   float4x4 mProjection;
 //   float4 vRenderTargetOffset;
+//   float4 vTextureCoords;
 //
 //
 // Registers:
 //
 //   Name                Reg   Size
 //   ------------------- ----- ----
 //   mLayerQuadTransform c0       4
 //   mLayerTransform     c4       4
 //   mProjection         c8       4
 //   vRenderTargetOffset c12      1
+//   vTextureCoords      c13      1
 //
 
     vs_2_0
-    def c13, -0.5, 0, 0, 0
+    def c14, -0.5, 0, 0, 0
     dcl_position v0
     mul r0, v0.y, c1
     mad r0, c0, v0.x, r0
     mad r0, c2, v0.z, r0
     mad r0, c3, v0.w, r0
     mul r1, r0.y, c5
     mad r1, c4, r0.x, r1
     mad r1, c6, r0.z, r1
     mad r0, c7, r0.w, r1
     add r0, r0, -c12
-    add r0.xy, r0, c13.x
+    add r0.xy, r0, c14.x
     mul r1, r0.y, c9
     mad r1, c8, r0.x, r1
     mad r1, c10, r0.z, r1
     mad oPos, c11, r0.w, r1
-    mov oT0.xy, v0
+    mad oT0.xy, v0, c13.zwzw, c13
 
 // approximately 15 instruction slots used
 #endif
 
 const BYTE LayerQuadVS[] =
 {
       0,   2, 254, 255, 254, 255, 
-     67,   0,  67,  84,  65,  66, 
-     28,   0,   0,   0, 215,   0, 
+     80,   0,  67,  84,  65,  66, 
+     28,   0,   0,   0,  11,   1, 
       0,   0,   0,   2, 254, 255, 
-      4,   0,   0,   0,  28,   0, 
+      5,   0,   0,   0,  28,   0, 
       0,   0,   0,   1,   0,   0, 
-    208,   0,   0,   0, 108,   0, 
+      4,   1,   0,   0, 128,   0, 
       0,   0,   2,   0,   0,   0, 
-      4,   0,   0,   0, 128,   0, 
+      4,   0,   0,   0, 148,   0, 
       0,   0,   0,   0,   0,   0, 
-    144,   0,   0,   0,   2,   0, 
+    164,   0,   0,   0,   2,   0, 
       4,   0,   4,   0,   0,   0, 
-    128,   0,   0,   0,   0,   0, 
-      0,   0, 160,   0,   0,   0, 
+    148,   0,   0,   0,   0,   0, 
+      0,   0, 180,   0,   0,   0, 
       2,   0,   8,   0,   4,   0, 
-      0,   0, 128,   0,   0,   0, 
-      0,   0,   0,   0, 172,   0, 
+      0,   0, 148,   0,   0,   0, 
+      0,   0,   0,   0, 192,   0, 
       0,   0,   2,   0,  12,   0, 
-      1,   0,   0,   0, 192,   0, 
+      1,   0,   0,   0, 212,   0, 
       0,   0,   0,   0,   0,   0, 
-    109,  76,  97, 121, 101, 114, 
-     81, 117,  97, 100,  84, 114, 
-     97, 110, 115, 102, 111, 114, 
-    109,   0,   3,   0,   3,   0, 
-      4,   0,   4,   0,   1,   0, 
-      0,   0,   0,   0,   0,   0, 
-    109,  76,  97, 121, 101, 114, 
+    228,   0,   0,   0,   2,   0, 
+     13,   0,   1,   0,   0,   0, 
+    244,   0,   0,   0,   0,   0, 
+      0,   0, 109,  76,  97, 121, 
+    101, 114,  81, 117,  97, 100, 
      84, 114,  97, 110, 115, 102, 
-    111, 114, 109,   0, 109,  80, 
-    114, 111, 106, 101,  99, 116, 
-    105, 111, 110,   0, 118,  82, 
-    101, 110, 100, 101, 114,  84, 
-     97, 114, 103, 101, 116,  79, 
-    102, 102, 115, 101, 116,   0, 
-      1,   0,   3,   0,   1,   0, 
-      4,   0,   1,   0,   0,   0, 
-      0,   0,   0,   0, 118, 115, 
-     95,  50,  95,  48,   0,  77, 
-    105,  99, 114, 111, 115, 111, 
-    102, 116,  32,  40,  82,  41, 
-     32,  72,  76,  83,  76,  32, 
-     83, 104,  97, 100, 101, 114, 
-     32,  67, 111, 109, 112, 105, 
-    108, 101, 114,  32,  57,  46, 
-     50,  55,  46,  57,  53,  50, 
-     46,  51,  48,  50,  50,   0, 
-     81,   0,   0,   5,  13,   0, 
-     15, 160,   0,   0,   0, 191, 
-      0,   0,   0,   0,   0,   0, 
+    111, 114, 109,   0,   3,   0, 
+      3,   0,   4,   0,   4,   0, 
+      1,   0,   0,   0,   0,   0, 
+      0,   0, 109,  76,  97, 121, 
+    101, 114,  84, 114,  97, 110, 
+    115, 102, 111, 114, 109,   0, 
+    109,  80, 114, 111, 106, 101, 
+     99, 116, 105, 111, 110,   0, 
+    118,  82, 101, 110, 100, 101, 
+    114,  84,  97, 114, 103, 101, 
+    116,  79, 102, 102, 115, 101, 
+    116,   0,   1,   0,   3,   0, 
+      1,   0,   4,   0,   1,   0, 
       0,   0,   0,   0,   0,   0, 
-     31,   0,   0,   2,   0,   0, 
-      0, 128,   0,   0,  15, 144, 
-      5,   0,   0,   3,   0,   0, 
-     15, 128,   0,   0,  85, 144, 
-      1,   0, 228, 160,   4,   0, 
+    118,  84, 101, 120, 116, 117, 
+    114, 101,  67, 111, 111, 114, 
+    100, 115,   0, 171,   1,   0, 
+      3,   0,   1,   0,   4,   0, 
+      1,   0,   0,   0,   0,   0, 
+      0,   0, 118, 115,  95,  50, 
+     95,  48,   0,  77, 105,  99, 
+    114, 111, 115, 111, 102, 116, 
+     32,  40,  82,  41,  32,  72, 
+     76,  83,  76,  32,  83, 104, 
+     97, 100, 101, 114,  32,  67, 
+    111, 109, 112, 105, 108, 101, 
+    114,  32,  57,  46,  50,  57, 
+     46,  57,  53,  50,  46,  51, 
+     49,  49,  49,   0,  81,   0, 
+      0,   5,  14,   0,  15, 160, 
+      0,   0,   0, 191,   0,   0, 
+      0,   0,   0,   0,   0,   0, 
+      0,   0,   0,   0,  31,   0, 
+      0,   2,   0,   0,   0, 128, 
+      0,   0,  15, 144,   5,   0, 
+      0,   3,   0,   0,  15, 128, 
+      0,   0,  85, 144,   1,   0, 
+    228, 160,   4,   0,   0,   4, 
+      0,   0,  15, 128,   0,   0, 
+    228, 160,   0,   0,   0, 144, 
+      0,   0, 228, 128,   4,   0, 
       0,   4,   0,   0,  15, 128, 
-      0,   0, 228, 160,   0,   0, 
-      0, 144,   0,   0, 228, 128, 
+      2,   0, 228, 160,   0,   0, 
+    170, 144,   0,   0, 228, 128, 
       4,   0,   0,   4,   0,   0, 
-     15, 128,   2,   0, 228, 160, 
-      0,   0, 170, 144,   0,   0, 
+     15, 128,   3,   0, 228, 160, 
+      0,   0, 255, 144,   0,   0, 
+    228, 128,   5,   0,   0,   3, 
+      1,   0,  15, 128,   0,   0, 
+     85, 128,   5,   0, 228, 160, 
+      4,   0,   0,   4,   1,   0, 
+     15, 128,   4,   0, 228, 160, 
+      0,   0,   0, 128,   1,   0, 
     228, 128,   4,   0,   0,   4, 
-      0,   0,  15, 128,   3,   0, 
-    228, 160,   0,   0, 255, 144, 
-      0,   0, 228, 128,   5,   0, 
-      0,   3,   1,   0,  15, 128, 
-      0,   0,  85, 128,   5,   0, 
-    228, 160,   4,   0,   0,   4, 
-      1,   0,  15, 128,   4,   0, 
-    228, 160,   0,   0,   0, 128, 
+      1,   0,  15, 128,   6,   0, 
+    228, 160,   0,   0, 170, 128, 
       1,   0, 228, 128,   4,   0, 
-      0,   4,   1,   0,  15, 128, 
-      6,   0, 228, 160,   0,   0, 
-    170, 128,   1,   0, 228, 128, 
-      4,   0,   0,   4,   0,   0, 
-     15, 128,   7,   0, 228, 160, 
-      0,   0, 255, 128,   1,   0, 
-    228, 128,   2,   0,   0,   3, 
-      0,   0,  15, 128,   0,   0, 
-    228, 128,  12,   0, 228, 161, 
+      0,   4,   0,   0,  15, 128, 
+      7,   0, 228, 160,   0,   0, 
+    255, 128,   1,   0, 228, 128, 
       2,   0,   0,   3,   0,   0, 
-      3, 128,   0,   0, 228, 128, 
-     13,   0,   0, 160,   5,   0, 
-      0,   3,   1,   0,  15, 128, 
-      0,   0,  85, 128,   9,   0, 
-    228, 160,   4,   0,   0,   4, 
-      1,   0,  15, 128,   8,   0, 
-    228, 160,   0,   0,   0, 128, 
+     15, 128,   0,   0, 228, 128, 
+     12,   0, 228, 161,   2,   0, 
+      0,   3,   0,   0,   3, 128, 
+      0,   0, 228, 128,  14,   0, 
+      0, 160,   5,   0,   0,   3, 
+      1,   0,  15, 128,   0,   0, 
+     85, 128,   9,   0, 228, 160, 
+      4,   0,   0,   4,   1,   0, 
+     15, 128,   8,   0, 228, 160, 
+      0,   0,   0, 128,   1,   0, 
+    228, 128,   4,   0,   0,   4, 
+      1,   0,  15, 128,  10,   0, 
+    228, 160,   0,   0, 170, 128, 
       1,   0, 228, 128,   4,   0, 
-      0,   4,   1,   0,  15, 128, 
-     10,   0, 228, 160,   0,   0, 
-    170, 128,   1,   0, 228, 128, 
+      0,   4,   0,   0,  15, 192, 
+     11,   0, 228, 160,   0,   0, 
+    255, 128,   1,   0, 228, 128, 
       4,   0,   0,   4,   0,   0, 
-     15, 192,  11,   0, 228, 160, 
-      0,   0, 255, 128,   1,   0, 
-    228, 128,   1,   0,   0,   2, 
-      0,   0,   3, 224,   0,   0, 
-    228, 144, 255, 255,   0,   0
+      3, 224,   0,   0, 228, 144, 
+     13,   0, 238, 160,  13,   0, 
+    228, 160, 255, 255,   0,   0
 };
 #if 0
 //
-// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
 //
 //   fxc LayerManagerD3D9Shaders.hlsl -ERGBAShader -nologo -Tps_2_0
 //    -FhtmpShaderHeader -VnRGBAShaderPS
 //
 //
 // Parameters:
 //
 //   float fLayerOpacity;
@@ -205,35 +217,35 @@ const BYTE RGBAShaderPS[] =
       0,   0,   0,   0,   0,   0, 
     112, 115,  95,  50,  95,  48, 
       0,  77, 105,  99, 114, 111, 
     115, 111, 102, 116,  32,  40, 
      82,  41,  32,  72,  76,  83, 
      76,  32,  83, 104,  97, 100, 
     101, 114,  32,  67, 111, 109, 
     112, 105, 108, 101, 114,  32, 
-     57,  46,  50,  55,  46,  57, 
-     53,  50,  46,  51,  48,  50, 
-     50,   0,  31,   0,   0,   2, 
+     57,  46,  50,  57,  46,  57, 
+     53,  50,  46,  51,  49,  49, 
+     49,   0,  31,   0,   0,   2, 
       0,   0,   0, 128,   0,   0, 
       3, 176,  31,   0,   0,   2, 
       0,   0,   0, 144,   0,   8, 
      15, 160,  66,   0,   0,   3, 
       0,   0,  15, 128,   0,   0, 
     228, 176,   0,   8, 228, 160, 
       5,   0,   0,   3,   0,   0, 
      15, 128,   0,   0, 228, 128, 
       0,   0,   0, 160,   1,   0, 
       0,   2,   0,   8,  15, 128, 
       0,   0, 228, 128, 255, 255, 
       0,   0
 };
 #if 0
 //
-// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
 //
 //   fxc LayerManagerD3D9Shaders.hlsl -ERGBShader -nologo -Tps_2_0
 //    -FhtmpShaderHeader -VnRGBShaderPS
 //
 //
 // Parameters:
 //
 //   float fLayerOpacity;
@@ -286,19 +298,19 @@ const BYTE RGBShaderPS[] =
       0,   0,   0,   0,   0,   0, 
     112, 115,  95,  50,  95,  48, 
       0,  77, 105,  99, 114, 111, 
     115, 111, 102, 116,  32,  40, 
      82,  41,  32,  72,  76,  83, 
      76,  32,  83, 104,  97, 100, 
     101, 114,  32,  67, 111, 109, 
     112, 105, 108, 101, 114,  32, 
-     57,  46,  50,  55,  46,  57, 
-     53,  50,  46,  51,  48,  50, 
-     50,   0,  81,   0,   0,   5, 
+     57,  46,  50,  57,  46,  57, 
+     53,  50,  46,  51,  49,  49, 
+     49,   0,  81,   0,   0,   5, 
       1,   0,  15, 160,   0,   0, 
     128,  63,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,  31,   0,   0,   2, 
       0,   0,   0, 128,   0,   0, 
       3, 176,  31,   0,   0,   2, 
       0,   0,   0, 144,   0,   8, 
      15, 160,  66,   0,   0,   3, 
@@ -310,17 +322,17 @@ const BYTE RGBShaderPS[] =
       0,   2,   0,   0,   8, 128, 
       1,   0,   0, 160,   1,   0, 
       0,   2,   0,   8,  15, 128, 
       0,   0, 228, 128, 255, 255, 
       0,   0
 };
 #if 0
 //
-// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
 //
 //   fxc LayerManagerD3D9Shaders.hlsl -EYCbCrShader -nologo -Tps_2_0
 //    -FhtmpShaderHeader -VnYCbCrShaderPS
 //
 //
 // Parameters:
 //
 //   float fLayerOpacity;
@@ -405,19 +417,19 @@ const BYTE YCbCrShaderPS[] =
       1,   0,   0,   0,   0,   0, 
       0,   0, 112, 115,  95,  50, 
      95,  48,   0,  77, 105,  99, 
     114, 111, 115, 111, 102, 116, 
      32,  40,  82,  41,  32,  72, 
      76,  83,  76,  32,  83, 104, 
      97, 100, 101, 114,  32,  67, 
     111, 109, 112, 105, 108, 101, 
-    114,  32,  57,  46,  50,  55, 
+    114,  32,  57,  46,  50,  57, 
      46,  57,  53,  50,  46,  51, 
-     48,  50,  50,   0,  81,   0, 
+     49,  49,  49,   0,  81,   0, 
       0,   5,   1,   0,  15, 160, 
       0,   0,   0, 191,   0,   0, 
     128, 189, 244, 253, 148,  63, 
     186,  73, 204,  63,  81,   0, 
       0,   5,   2,   0,  15, 160, 
     197,  32,  80,  63,  39,  49, 
     200,  62, 233,  38,   1,  64, 
       0,   0, 128,  63,  31,   0, 
@@ -466,17 +478,17 @@ const BYTE YCbCrShaderPS[] =
       0,   3,   0,   0,  15, 128, 
       1,   0, 228, 128,   0,   0, 
       0, 160,   1,   0,   0,   2, 
       0,   8,  15, 128,   0,   0, 
     228, 128, 255, 255,   0,   0
 };
 #if 0
 //
-// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
 //
 //   fxc LayerManagerD3D9Shaders.hlsl -ESolidColorShader -nologo -Tps_2_0
 //    -FhtmpShaderHeader -VnSolidColorShaderPS
 //
 //
 // Parameters:
 //
 //   float4 fLayerColor;
@@ -514,14 +526,14 @@ const BYTE SolidColorShaderPS[] =
       0,   0,   0,   0, 112, 115, 
      95,  50,  95,  48,   0,  77, 
     105,  99, 114, 111, 115, 111, 
     102, 116,  32,  40,  82,  41, 
      32,  72,  76,  83,  76,  32, 
      83, 104,  97, 100, 101, 114, 
      32,  67, 111, 109, 112, 105, 
     108, 101, 114,  32,  57,  46, 
-     50,  55,  46,  57,  53,  50, 
-     46,  51,  48,  50,  50,   0, 
+     50,  57,  46,  57,  53,  50, 
+     46,  51,  49,  49,  49,   0, 
       1,   0,   0,   2,   0,   8, 
      15, 128,   0,   0, 228, 160, 
     255, 255,   0,   0
 };
--- a/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl
+++ b/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl
@@ -1,13 +1,16 @@
 float4x4 mLayerQuadTransform;
 float4x4 mLayerTransform;
 float4 vRenderTargetOffset;
 float4x4 mProjection;
 
+typedef float4 rect;
+rect vTextureCoords;
+
 texture tex0;
 sampler s2D;
 sampler s2DY;
 sampler s2DCb;
 sampler s2DCr;
 
 float fLayerOpacity;
 float4 fLayerColor;
@@ -30,17 +33,26 @@ VS_OUTPUT LayerQuadVS(const VS_INPUT aVe
   outp.vPosition = outp.vPosition - vRenderTargetOffset;
   
   // adjust our vertices to match d3d9's pixel coordinate system
   // which has pixel centers at integer locations
   outp.vPosition.xy -= 0.5;
   
   outp.vPosition = mul(mProjection, outp.vPosition);
   
-  outp.vTexCoords = aVertex.vPosition.xy;
+  // We use 4 component floats to uniquely describe a rectangle, by the structure
+  // of x, y, width, height. This allows us to easily generate the 4 corners
+  // of any rectangle from the 4 corners of the 0,0-1,1 quad that we use as the
+  // stream source for our LayerQuad vertex shader. We do this by doing:
+  // Xout = x + Xin * width
+  // Yout = y + Yin * height
+  float2 position = vTextureCoords.xy;
+  float2 size = vTextureCoords.zw;
+  outp.vTexCoords.x = position.x + aVertex.vPosition.x * size.x;
+  outp.vTexCoords.y = position.y + aVertex.vPosition.y * size.y;
   return outp;
 }
 
 float4 RGBAShader(const VS_OUTPUT aVertex) : COLOR
 {
   return tex2D(s2D, aVertex.vTexCoords) * fLayerOpacity;
 }
 
--- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp
@@ -195,48 +195,40 @@ ThebesLayerD3D9::RenderLayer()
     }
   }
 
   if (!mTexture) {
     CreateNewTexture(gfxIntSize(visibleRect.width, visibleRect.height));
     mValidRegion.SetEmpty();
   }
 
-  if (!mValidRegion.IsEqual(mVisibleRegion.GetBounds())) {
+  if (!mValidRegion.IsEqual(mVisibleRegion)) {
     /* We use the bounds of the visible region because we draw the bounds of
      * this region when we draw this entire texture. We have to make sure that
      * the areas that aren't filled with content get their background drawn.
      * This is an issue for opaque surfaces, which otherwise won't get their
      * background painted.
      */
     nsIntRegion region;
-    region.Sub(mVisibleRegion.GetBounds(), mValidRegion);
+    region.Sub(mVisibleRegion, mValidRegion);
 
     DrawRegion(region);
 
-    mValidRegion = mVisibleRegion.GetBounds();
+    mValidRegion = mVisibleRegion;
   }
 
   float quadTransform[4][4];
   /*
    * Matrix to transform the <0.0,0.0>, <1.0,1.0> quad to the correct position
-   * and size. To get pixel perfect mapping we offset the quad half a pixel
-   * to the top-left.
-   *
-   * See: http://msdn.microsoft.com/en-us/library/bb219690%28VS.85%29.aspx
+   * and size.
    */
   memset(&quadTransform, 0, sizeof(quadTransform));
-  quadTransform[0][0] = (float)visibleRect.width;
-  quadTransform[1][1] = (float)visibleRect.height;
   quadTransform[2][2] = 1.0f;
-  quadTransform[3][0] = (float)visibleRect.x;
-  quadTransform[3][1] = (float)visibleRect.y;
   quadTransform[3][3] = 1.0f;
 
-  device()->SetVertexShaderConstantF(0, &quadTransform[0][0], 4);
   device()->SetVertexShaderConstantF(4, &mTransform._11, 4);
 
   float opacity[4];
   /*
    * We always upload a 4 component float, but the shader will use only the
    * first component since it's declared as a 'float'.
    */
   opacity[0] = GetOpacity();
@@ -245,17 +237,37 @@ ThebesLayerD3D9::RenderLayer()
 #ifdef CAIRO_HAS_D2D_SURFACE
   if (mD2DSurface && CanUseOpaqueSurface()) {
     mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER);
   } else
 #endif
   mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER);
 
   device()->SetTexture(0, mTexture);
-  device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+
+  nsIntRegionRectIterator iter(mVisibleRegion);
+
+  const nsIntRect *iterRect;
+  while ((iterRect = iter.Next())) {
+    quadTransform[0][0] = (float)iterRect->width;
+    quadTransform[1][1] = (float)iterRect->height;
+    quadTransform[3][0] = (float)iterRect->x;
+    quadTransform[3][1] = (float)iterRect->y;
+    
+    device()->SetVertexShaderConstantF(0, &quadTransform[0][0], 4);
+    device()->SetVertexShaderConstantF(13, ShaderConstantRect(
+        (float)(iterRect->x - visibleRect.x) / (float)visibleRect.width,
+        (float)(iterRect->y - visibleRect.y) / (float)visibleRect.height,
+        (float)iterRect->width / (float)visibleRect.width,
+        (float)iterRect->height / (float)visibleRect.height), 1);
+    device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
+  }
+
+  // Set back to default.
+  device()->SetVertexShaderConstantF(13, ShaderConstantRect(0, 0, 1.0f, 1.0f), 1);
 }
 
 void
 ThebesLayerD3D9::CleanResources()
 {
   mTexture = nsnull;
 }
 
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -154,23 +154,25 @@ ContainerLayerOGL::RenderLayer(int aPrev
   GLuint containerSurface;
   GLuint frameBuffer;
 
   nsIntPoint childOffset(aOffset);
   bool needsFramebuffer = false;
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
 
   float opacity = GetOpacity();
-  if (opacity != 1.0) {
+  if (opacity != 1.0 || !mTransform.IsIdentity()) {
     mOGLManager->CreateFBOWithTexture(visibleRect.width,
                                       visibleRect.height,
                                       &frameBuffer,
                                       &containerSurface);
     childOffset.x = visibleRect.x;
     childOffset.y = visibleRect.y;
+    mOGLManager->gl()->fClearColor(0, 0, 0, 0);
+    mOGLManager->gl()->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
   } else {
     frameBuffer = aPreviousFrameBuffer;
   }
 
   /**
    * Render this container's contents.
    */
   LayerOGL *layerToRender = GetFirstChildOGL();
@@ -188,17 +190,17 @@ ContainerLayerOGL::RenderLayer(int aPrev
     layerToRender->RenderLayer(frameBuffer, childOffset);
 
     Layer *nextSibling = layerToRender->GetLayer()->GetNextSibling();
     layerToRender = nextSibling ? static_cast<LayerOGL*>(nextSibling->
                                                          ImplData())
                                 : nsnull;
   }
 
-  if (opacity != 1.0) {
+  if (opacity != 1.0 || !mTransform.IsIdentity()) {
     // Unbind the current framebuffer and rebind the previous one.
     gl()->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aPreviousFrameBuffer);
     gl()->fDeleteFramebuffers(1, &frameBuffer);
 
     gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
 
     gl()->fBindTexture(mOGLManager->FBOTextureTarget(), containerSurface);
 
--- a/gfx/layers/opengl/LayerManagerOGL.h
+++ b/gfx/layers/opengl/LayerManagerOGL.h
@@ -160,16 +160,29 @@ public:
     return static_cast<ColorTextureLayerProgram*>(mPrograms[BGRALayerProgramType]);
   }
   ColorTextureLayerProgram *GetRGBXLayerProgram() {
     return static_cast<ColorTextureLayerProgram*>(mPrograms[RGBXLayerProgramType]);
   }
   ColorTextureLayerProgram *GetBGRXLayerProgram() {
     return static_cast<ColorTextureLayerProgram*>(mPrograms[BGRXLayerProgramType]);
   }
+  ColorTextureLayerProgram *GetBasicLayerProgram(PRBool aOpaque, PRBool aIsRGB)
+  {
+    if (aIsRGB) {
+      return aOpaque
+        ? GetRGBXLayerProgram()
+        : GetRGBALayerProgram();
+    } else {
+      return aOpaque
+        ? GetBGRXLayerProgram()
+        : GetBGRALayerProgram();
+    }
+  }
+
   ColorTextureLayerProgram *GetRGBARectLayerProgram() {
     return static_cast<ColorTextureLayerProgram*>(mPrograms[RGBARectLayerProgramType]);
   }
   SolidColorLayerProgram *GetColorLayerProgram() {
     return static_cast<SolidColorLayerProgram*>(mPrograms[ColorLayerProgramType]);
   }
   YCbCrTextureLayerProgram *GetYCbCrLayerProgram() {
     return static_cast<YCbCrTextureLayerProgram*>(mPrograms[YCbCrLayerProgramType]);
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -138,19 +138,18 @@ protected:
 
 void
 ThebesLayerBufferOGL::RenderTo(const nsIntPoint& aOffset,
                                LayerManagerOGL* aManager)
 {
   // Note BGR: Cairo's image surfaces are always in what
   // OpenGL and our shaders consider BGR format.
   ColorTextureLayerProgram *program =
-    mLayer->CanUseOpaqueSurface()
-    ? aManager->GetBGRXLayerProgram()
-    : aManager->GetBGRALayerProgram();
+    aManager->GetBasicLayerProgram(mLayer->CanUseOpaqueSurface(),
+                                   mTexImage->IsRGB());
 
   if (!mTexImage->InUpdate() || !mTexImage->EndUpdate()) {
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexImage->Texture());
   }
 
   nsIntRect quadRect = mLayer->GetVisibleRegion().GetBounds();
   program->Activate();
   program->SetLayerQuadRect(quadRect);
--- a/gfx/thebes/GLContext.h
+++ b/gfx/thebes/GLContext.h
@@ -194,32 +194,36 @@ public:
      */
     virtual already_AddRefed<gfxASurface> GetBackingSurface()
     { return NULL; }
 
     const nsIntSize& GetSize() const { return mSize; }
     ContentType GetContentType() const { return mContentType; }
     virtual PRBool InUpdate() const = 0;
 
+    PRBool IsRGB() const { return mIsRGBFormat; }
+
 protected:
     /**
      * After the ctor, the TextureImage is invalid.  Implementations
      * must allocate resources successfully before returning the new
      * TextureImage from GLContext::CreateTextureImage().  That is,
      * clients must not be given partially-constructed TextureImages.
      */
-    TextureImage(GLuint aTexture, const nsIntSize& aSize, ContentType aContentType)
+    TextureImage(GLuint aTexture, const nsIntSize& aSize, ContentType aContentType, PRBool aIsRGB = PR_FALSE)
         : mTexture(aTexture)
         , mSize(aSize)
         , mContentType(aContentType)
+        , mIsRGBFormat(aIsRGB)
     {}
 
     GLuint mTexture;
     nsIntSize mSize;
     ContentType mContentType;
+    PRPackedBool mIsRGBFormat;
 };
 
 /**
  * BasicTextureImage is the baseline TextureImage implementation ---
  * it updates its texture by allocating a scratch buffer for the
  * client to draw into, then using glTexSubImage2D() to upload the new
  * pixels.  Platforms must provide the code to create a new surface
  * into which the updated pixels will be drawn, and the code to
--- a/gfx/thebes/GLContextProviderEGL.cpp
+++ b/gfx/thebes/GLContextProviderEGL.cpp
@@ -814,18 +814,19 @@ GetGlobalContextEGL()
 
 class TextureImageEGL : public TextureImage
 {
 public:
     TextureImageEGL(GLuint aTexture,
                     const nsIntSize& aSize,
                     ContentType aContentType,
                     GLContext* aContext,
-                    GLContextEGL* aImpl)
-        : TextureImage(aTexture, aSize, aContentType)
+                    GLContextEGL* aImpl,
+                    PRBool aIsRGB)
+        : TextureImage(aTexture, aSize, aContentType, aIsRGB)
         , mGLContext(aContext)
         , mImpl(aImpl)
     { }
 
     virtual ~TextureImageEGL()
     {
         mGLContext->MakeCurrent();
         mImpl->ReleaseTexImage();
@@ -878,16 +879,18 @@ GLContextEGL::CreateTextureImage(const n
                                  TextureImage::ContentType aContentType,
                                  GLint aWrapMode,
                                  PRBool aUseNearestFilter)
 {
   gfxASurface::gfxImageFormat imageFormat =
       (gfxASurface::CONTENT_COLOR == aContentType) ?
       gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
 
+  PRBool isRGB = PR_TRUE; // ^ this is always RGB
+
   nsRefPtr<gfxASurface> pixmap =
     gfxPlatform::GetPlatform()->
       CreateOffscreenSurface(gfxIntSize(aSize.width, aSize.height),
                              imageFormat);
 
   nsRefPtr<GLContext> impl =
       GLContextProviderEGL::CreateForNativePixmapSurface(pixmap);
   if (!impl)
@@ -907,17 +910,18 @@ GLContextEGL::CreateTextureImage(const n
   fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
   fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
   fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
 
   impl->BindTexImage();
 
   nsRefPtr<TextureImageEGL> teximage =
       new TextureImageEGL(texture, aSize, aContentType, this,
-                          static_cast<GLContextEGL*>(impl.get()));
+                          static_cast<GLContextEGL*>(impl.get()),
+                          isRGB);
   return teximage.forget();
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
         return nsnull;
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -455,20 +455,29 @@ static const char *sSurfaceNamesForSurfa
     "gfx/surface/beos",
     "gfx/surface/directfb",
     "gfx/surface/svg",
     "gfx/surface/os2",
     "gfx/surface/win32printing",
     "gfx/surface/quartzimage",
     "gfx/surface/script",
     "gfx/surface/qpainter",
-    "gfx/surface/ddraw"
+    "gfx/surface/recording",
+    "gfx/surface/vg",
+    "gfx/surface/gl",
+    "gfx/surface/drm",
+    "gfx/surface/tee",
+    "gfx/surface/xml",
+    "gfx/surface/skia",
+    "gfx/surface/ddraw",
+    "gfx/surface/d2d"
 };
 
 PR_STATIC_ASSERT(NS_ARRAY_LENGTH(sSurfaceNamesForSurfaceType) == gfxASurface::SurfaceTypeMax);
+PR_STATIC_ASSERT(CAIRO_SURFACE_TYPE_D2D == gfxASurface::SurfaceTypeD2D);
 
 static const char *
 SurfaceMemoryReporterPathForType(gfxASurface::gfxSurfaceType aType)
 {
     if (aType < 0 ||
         aType >= gfxASurface::SurfaceTypeMax)
         return "gfx/surface/unknown";
 
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -84,17 +84,25 @@ public:
         SurfaceTypeBeOS,
         SurfaceTypeDirectFB,
         SurfaceTypeSVG,
         SurfaceTypeOS2,
         SurfaceTypeWin32Printing,
         SurfaceTypeQuartzImage,
         SurfaceTypeScript,
         SurfaceTypeQPainter,
+        SurfaceTypeRecording,
+        SurfaceTypeVG,
+        SurfaceTypeGL,
+        SurfaceTypeDRM,
+        SurfaceTypeTee,
+        SurfaceTypeXML,
+        SurfaceTypeSkia,
         SurfaceTypeDDraw,
+        SurfaceTypeD2D,
         SurfaceTypeMax
     } gfxSurfaceType;
 
     typedef enum {
         CONTENT_COLOR       = 0x1000,
         CONTENT_ALPHA       = 0x2000,
         CONTENT_COLOR_ALPHA = 0x3000
     } gfxContentType;
@@ -220,16 +228,18 @@ public:
      */
     void RecordMemoryUsed(PRInt32 aBytes);
     void RecordMemoryFreed();
 
     PRInt32 KnownMemoryUsed() { return mBytesRecorded; }
 
     static PRInt32 BytePerPixelFromFormat(gfxImageFormat format);
 
+    virtual const gfxIntSize GetSize() const { return gfxIntSize(-1, -1); }
+
 protected:
     gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mBytesRecorded(0), mSurfaceValid(PR_FALSE)
     {
         MOZ_COUNT_CTOR(gfxASurface);
     }
 
     static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf);
     static void SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf);
--- a/gfx/thebes/gfxImageSurface.h
+++ b/gfx/thebes/gfxImageSurface.h
@@ -72,17 +72,17 @@ public:
     gfxImageSurface(const gfxIntSize& size, gfxImageFormat format);
     gfxImageSurface(cairo_surface_t *csurf);
 
     virtual ~gfxImageSurface();
 
     // ImageSurface methods
     gfxImageFormat Format() const { return mFormat; }
 
-    const gfxIntSize& GetSize() const { return mSize; }
+    virtual const gfxIntSize GetSize() const { return mSize; }
     PRInt32 Width() const { return mSize.width; }
     PRInt32 Height() const { return mSize.height; }
 
     /**
      * Distance in bytes between the start of a line and the start of the
      * next line.
      */
     PRInt32 Stride() const { return mStride; }
--- a/gfx/thebes/gfxOS2Surface.h
+++ b/gfx/thebes/gfxOS2Surface.h
@@ -62,17 +62,17 @@ public:
     // While gfxOS2Surface keeps track of the presentation handle itself,
     // use the one from WinBeginPaint() here.
     void Refresh(RECTL *aRect, HPS aPS);
 
     // Reset the cairo surface to the given size.
     int Resize(const gfxIntSize& aSize);
 
     HPS GetPS();
-    gfxIntSize GetSize() { return mSize; }
+    virtual const gfxIntSize GetSize() const { return mSize; }
 
 private:
     HWND mWnd; // non-null if created through the HWND constructor
     HDC mDC; // memory device context
     HPS mPS; // presentation space connected to window or memory device
     HBITMAP mBitmap; // bitmap for initialization of memory surface
     gfxIntSize mSize; // current size of the surface
 };
--- a/gfx/thebes/gfxPDFSurface.h
+++ b/gfx/thebes/gfxPDFSurface.h
@@ -56,17 +56,20 @@ public:
     virtual nsresult BeginPage();
     virtual nsresult EndPage();
     virtual void Finish();
 
     void SetDPI(double x, double y);
     void GetDPI(double *xDPI, double *yDPI);
 
     // this is in points!
-    const gfxSize& GetSize() const { return mSize; }
+    virtual const gfxIntSize GetSize() const
+    {
+        return gfxIntSize(mSize.width, mSize.height);
+    }
 
     virtual PRInt32 GetDefaultContextFlags() const
     {
         return gfxContext::FLAG_SIMPLIFY_OPERATORS |
                gfxContext::FLAG_DISABLE_SNAPPING;
     }
 
 private:
--- a/gfx/thebes/gfxPSSurface.h
+++ b/gfx/thebes/gfxPSSurface.h
@@ -56,17 +56,20 @@ public:
     virtual nsresult BeginPage();
     virtual nsresult EndPage();
     virtual void Finish();
 
     void SetDPI(double x, double y);
     void GetDPI(double *xDPI, double *yDPI);
 
     // this is in points!
-    const gfxSize& GetSize() const { return mSize; }
+    virtual const gfxIntSize GetSize() const
+    {
+        return gfxIntSize(mSize.width, mSize.height);
+    }
 
     virtual PRInt32 GetDefaultContextFlags() const
     {
         return gfxContext::FLAG_SIMPLIFY_OPERATORS |
                gfxContext::FLAG_DISABLE_SNAPPING;
     }
 
 private:
--- a/gfx/thebes/gfxQuartzPDFSurface.h
+++ b/gfx/thebes/gfxQuartzPDFSurface.h
@@ -49,18 +49,18 @@ public:
     virtual ~gfxQuartzPDFSurface();
 
     nsresult BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName);
     nsresult EndPrinting();
     nsresult AbortPrinting();
     nsresult BeginPage();
     nsresult EndPage();
 
-    gfxSize GetSize() {
-        gfxSize size(mRect.size.width, mRect.size.height);
+    virtual const gfxIntSize GetSize() const {
+        gfxIntSize size(mRect.size.width, mRect.size.height);
         return size;
     }
 
     CGContextRef GetCGContext() { return mCGContext; }
 
     virtual PRInt32 GetDefaultContextFlags() const { return gfxContext::FLAG_DISABLE_SNAPPING; }
 
 protected:
--- a/gfx/thebes/gfxQuartzSurface.h
+++ b/gfx/thebes/gfxQuartzSurface.h
@@ -60,17 +60,17 @@ public:
     {
       return PR_FALSE;
     }
     virtual TextQuality GetTextQualityInTransparentSurfaces()
     {
       return TEXT_QUALITY_OK_OVER_OPAQUE_PIXELS;
     }
 
-    const gfxSize& GetSize() const { return mSize; }
+    virtual const gfxIntSize GetSize() const { return gfxIntSize(mSize.width, mSize.height); }
 
     CGContextRef GetCGContext() { return mCGContext; }
 
     CGContextRef GetCGContextWithClip(gfxContext *ctx);
 
     virtual PRInt32 GetDefaultContextFlags() const;
 
 protected:
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -414,16 +414,31 @@ gfxUtils::DrawPixelSnapped(gfxContext*  
         aContext->SetOperator(OptimalFillOperator());
     }
 
     drawable->Draw(aContext, aFill, doTile, aFilter, aUserSpaceToImageSpace);
 
     aContext->SetOperator(op);
 }
 
+/* static */ int
+gfxUtils::ImageFormatToDepth(gfxASurface::gfxImageFormat aFormat)
+{
+    switch (aFormat) {
+        case gfxASurface::ImageFormatARGB32:
+            return 32;
+        case gfxASurface::ImageFormatRGB24:
+            return 24;
+        case gfxASurface::ImageFormatRGB16_565:
+            return 16;
+        default:
+            break;
+    }
+    return 0;
+}
 static void
 ClipToRegionInternal(gfxContext* aContext, const nsIntRegion& aRegion,
                      PRBool aSnap)
 {
   aContext->NewPath();
   nsIntRegionRectIterator iter(aRegion);
   const nsIntRect* r;
   while ((r = iter.Next()) != nsnull) {
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -89,11 +89,16 @@ public:
      * Clip aContext to the region aRegion.
      */
     static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion);
 
     /**
      * Clip aContext to the region aRegion, snapping the rectangles.
      */
     static void ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion);
+
+    /*
+     * Convert image format to depth value
+     */
+    static int ImageFormatToDepth(gfxASurface::gfxImageFormat aFormat);
 };
 
 #endif
--- a/gfx/thebes/gfxXlibSurface.cpp
+++ b/gfx/thebes/gfxXlibSurface.cpp
@@ -42,16 +42,17 @@
 #include "cairo-xlib.h"
 #include "cairo-xlib-xrender.h"
 #include <X11/Xlibint.h>	/* For XESetCloseDisplay */
 
 #include "nsTArray.h"
 #include "nsAlgorithm.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIPrefService.h"
+#include "cairo-xlib-xrender.h"
 
 // Although the dimension parameters in the xCreatePixmapReq wire protocol are
 // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
 // either dimension cannot be represented by a 16-bit *signed* integer.
 #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
 
 gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual)
     : mPixmapTaken(PR_FALSE), mDisplay(dpy), mDrawable(drawable)
@@ -489,8 +490,15 @@ gfxXlibSurface::FindRenderFormat(Display
         case ImageFormatA1:
             return XRenderFindStandardFormat (dpy, PictStandardA1);
         default:
             break;
     }
 
     return (XRenderPictFormat*)NULL;
 }
+
+XRenderPictFormat*
+gfxXlibSurface::XRenderFormat()
+{
+    return cairo_xlib_surface_get_xrender_format(CairoSurface());
+}
+
--- a/gfx/thebes/gfxXlibSurface.h
+++ b/gfx/thebes/gfxXlibSurface.h
@@ -77,20 +77,21 @@ public:
     virtual already_AddRefed<gfxASurface>
     CreateSimilarSurface(gfxContentType aType, const gfxIntSize& aSize);
 
     virtual TextQuality GetTextQualityInTransparentSurfaces()
     {
       return TEXT_QUALITY_OK_OVER_OPAQUE_PIXELS;
     }
 
-    const gfxIntSize& GetSize() { return mSize; }
+    virtual const gfxIntSize GetSize() const { return mSize; }
 
     Display* XDisplay() { return mDisplay; }
     Drawable XDrawable() { return mDrawable; }
+    XRenderPictFormat* XRenderFormat();
 
     static int DepthOfVisual(const Screen* screen, const Visual* visual);
     static Visual* FindVisual(Screen* screen, gfxImageFormat format);
     static XRenderPictFormat *FindRenderFormat(Display *dpy, gfxImageFormat format);
 
     // take ownership of a passed-in Pixmap, calling XFreePixmap on it
     // when the gfxXlibSurface is destroyed.
     void TakePixmap() {
--- a/ipc/glue/Shmem.h
+++ b/ipc/glue/Shmem.h
@@ -285,20 +285,16 @@ private:
 #else
   void AssertInvariants() const;
 #endif
 
   SharedMemory* mSegment;
   void* mData;
   size_t mSize;
   id_t mId;
-
-  // disable these
-  static void* operator new(size_t) CPP_THROW_NEW;
-  static void operator delete(void*);  
 };
 
 
 } // namespace ipc
 } // namespace mozilla
 
 
 namespace IPC {
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -791,16 +791,23 @@ IPDL union type."""
         return t
 
     def defaultValue(self):
         if self.ipdltype.isIPDL() and self.ipdltype.isActor():
             return ExprCast(ExprLiteral.NULL, self.bareType(), static=1)
         # XXX sneaky here, maybe need ExprCtor()?
         return ExprCall(self.bareType())
 
+    def getConstValue(self):
+        v = ExprDeref(self.callGetConstPtr())
+        # sigh
+        if 'Shmem' == self.ipdltype.name():
+            v = ExprCast(v, Type('Shmem', ref=1), const=1)
+        return v
+
 ##--------------------------------------------------
 
 class MessageDecl(ipdl.ast.MessageDecl):
     def baseName(self):
         return self.name
     
     def recvMethod(self):
         name = _recvPrefix(self.decl.type) + self.baseName()
@@ -2138,17 +2145,17 @@ def _generateCxxUnion(ud):
             StmtReturn(ExprDeref(c.callGetPtr()))
         ])
 
         getconstvalue = MethodDefn(MethodDecl(
             getConstValueVar.name, ret=c.constRefType(),
             const=1, force_inline=1))
         getconstvalue.addstmts([
             StmtExpr(callAssertSanity(expectTypeVar=c.enumvar())),
-            StmtReturn(ExprDeref(c.callGetConstPtr()))
+            StmtReturn(c.getConstValue())
         ])
 
         optype = MethodDefn(MethodDecl('', typeop=c.refType(), force_inline=1))
         optype.addstmt(StmtReturn(ExprCall(getValueVar)))
         opconsttype = MethodDefn(MethodDecl(
             '', const=1, typeop=c.constRefType(), force_inline=1))
         opconsttype.addstmt(StmtReturn(ExprCall(getConstValueVar)))
 
@@ -3894,18 +3901,21 @@ class _GenerateProtocolActorCode(ipdl.as
                 # the type comes across flipped from what the actor
                 # will be on this side; i.e. child->parent messages
                 # have type PFooChild when received on the parent side
                 # XXX: better error message
                 readcase.addstmt(StmtReturn.FALSE)
             else:
                 if c.special:
                     c = c.other       # see above
+                tmpvar = ExprVar('tmp')
+                ct = c.bareType()
                 readcase.addstmts([
-                    StmtExpr(ExprAssn(ExprDeref(var), c.defaultValue())),
+                    StmtDecl(Decl(ct, tmpvar.name), init=c.defaultValue()),
+                    StmtExpr(ExprAssn(ExprDeref(var), tmpvar)),
                     StmtReturn(self.read(
                         c.ipdltype,
                         ExprAddrOf(ExprCall(ExprSelect(var, '->',
                                                        c.getTypeName()))),
                         msgvar, itervar))
                 ])
 
             readswitch.addcase(caselabel, readcase)
--- a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
+++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
@@ -87,16 +87,21 @@ struct SetAttrs {
 };
 union Op { null_t; SetAttrs; };
 
 struct ShmemStruct {
     int i;
     Shmem mem;
 };
 
+union ShmemUnion {
+    int;
+    Shmem;
+};
+
 } // namespace _foo
 } // namespace mozilla
 
 namespace mozilla {
 namespace _ipdltest {
 
 sync protocol PTestDataStructures {
     manages PTestDataStructuresSub;
@@ -183,16 +188,18 @@ parent:
 
     sync Test17(Op[] ops);
 
     // test that the ParamTraits<nsTArray>::Read() workaround for
     // nsTArray's incorrect memmove() semantics works properly
     // (nsIntRegion isn't memmove()able)
     sync Test18(nsIntRegion[] ops);
 
+    sync Dummy(ShmemUnion su) returns (ShmemUnion rsu);
+
 state CONSTRUCTING:
     send PTestDataStructuresSub goto CONSTRUCTING;
     send Start goto TEST1;
 state TEST1:   recv Test1 goto TEST2;
 state TEST2:   recv Test2 goto TEST3;
 state TEST3:   recv Test3 goto TEST4;
 state TEST4:   recv Test4 goto TEST5;
 state TEST5:   recv Test5 goto TEST6;
--- a/ipc/ipdl/test/cxx/TestDataStructures.h
+++ b/ipc/ipdl/test/cxx/TestDataStructures.h
@@ -157,16 +157,23 @@ protected:
 
     NS_OVERRIDE
     virtual bool RecvTest17(const nsTArray<Op>& sa);
 
     NS_OVERRIDE
     virtual bool RecvTest18(const nsTArray<nsIntRegion>& ra);
 
     NS_OVERRIDE
+    virtual bool RecvDummy(const ShmemUnion& su, ShmemUnion* rsu)
+    {
+        *rsu = su;
+        return true;
+    }
+
+    NS_OVERRIDE
     virtual void ActorDestroy(ActorDestroyReason why)
     {
         if (NormalShutdown != why)
             fail("unexpected destruction!");  
         passed("ok");
         QuitParent();
     }
 
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -452,95 +452,28 @@ MakeBevelColor(mozilla::css::Side whichS
     break;
   }
   return theColor;
 }
 
 //----------------------------------------------------------------------
 // Thebes Border Rendering Code Start
 
-// helper function to convert a nsRect to a gfxRect
-static gfxRect
-RectToGfxRect(const nsRect& rect, nscoord twipsPerPixel)
-{
-  return gfxRect(gfxFloat(rect.x) / twipsPerPixel,
-                 gfxFloat(rect.y) / twipsPerPixel,
-                 gfxFloat(rect.width) / twipsPerPixel,
-                 gfxFloat(rect.height) / twipsPerPixel);
-}
-
 /*
  * Compute the float-pixel radii that should be used for drawing
  * this border/outline, given the various input bits.
- *
- * If a side is skipped via skipSides, its corners are forced to 0.
- * All corner radii are then adjusted so they do not require more
- * space than outerRect, according to the algorithm in css3-background.
  */
-static void
-ComputePixelRadii(const nscoord *aTwipsRadii,
-                  const nsRect& outerRect,
-                  PRIntn skipSides,
-                  nscoord twipsPerPixel,
-                  gfxCornerSizes *oBorderRadii)
+/* static */ void
+nsCSSRendering::ComputePixelRadii(const nscoord *aAppUnitsRadii,
+                                  nscoord aAppUnitsPerPixel,
+                                  gfxCornerSizes *oBorderRadii)
 {
-  nscoord twipsRadii[8];
-  memcpy(twipsRadii, aTwipsRadii, sizeof twipsRadii);
-
-  if (skipSides & SIDE_BIT_TOP) {
-    twipsRadii[NS_CORNER_TOP_LEFT_X] = 0;
-    twipsRadii[NS_CORNER_TOP_LEFT_Y] = 0;
-    twipsRadii[NS_CORNER_TOP_RIGHT_X] = 0;
-    twipsRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
-  }
-
-  if (skipSides & SIDE_BIT_RIGHT) {
-    twipsRadii[NS_CORNER_TOP_RIGHT_X] = 0;
-    twipsRadii[NS_CORNER_TOP_RIGHT_Y] = 0;
-    twipsRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
-    twipsRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
-  }
-
-  if (skipSides & SIDE_BIT_BOTTOM) {
-    twipsRadii[NS_CORNER_BOTTOM_RIGHT_X] = 0;
-    twipsRadii[NS_CORNER_BOTTOM_RIGHT_Y] = 0;
-    twipsRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
-    twipsRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
-  }
-
-  if (skipSides & SIDE_BIT_LEFT) {
-    twipsRadii[NS_CORNER_BOTTOM_LEFT_X] = 0;
-    twipsRadii[NS_CORNER_BOTTOM_LEFT_Y] = 0;
-    twipsRadii[NS_CORNER_TOP_LEFT_X] = 0;
-    twipsRadii[NS_CORNER_TOP_LEFT_Y] = 0;
-  }
-
   gfxFloat radii[8];
   NS_FOR_CSS_HALF_CORNERS(corner)
-    radii[corner] = twipsRadii[corner] / twipsPerPixel;
-
-  // css3-background specifies this algorithm for reducing
-  // corner radii when they are too big.
-  gfxFloat maxWidth = outerRect.width / twipsPerPixel;
-  gfxFloat maxHeight = outerRect.height / twipsPerPixel;
-  gfxFloat f = 1.0f;
-  NS_FOR_CSS_SIDES(side) {
-    PRUint32 hc1 = NS_SIDE_TO_HALF_CORNER(side, PR_FALSE, PR_TRUE);
-    PRUint32 hc2 = NS_SIDE_TO_HALF_CORNER(side, PR_TRUE, PR_TRUE);
-    gfxFloat length = NS_SIDE_IS_VERTICAL(side) ? maxHeight : maxWidth;
-    gfxFloat sum = radii[hc1] + radii[hc2];
-    // avoid floating point division in the normal case
-    if (length < sum)
-      f = NS_MIN(f, length/sum);
-  }
-  if (f < 1.0) {
-    NS_FOR_CSS_HALF_CORNERS(corner) {
-      radii[corner] *= f;
-    }
-  }
+    radii[corner] = gfxFloat(aAppUnitsRadii[corner]) / aAppUnitsPerPixel;
 
   (*oBorderRadii)[C_TL] = gfxSize(radii[NS_CORNER_TOP_LEFT_X],
                                   radii[NS_CORNER_TOP_LEFT_Y]);
   (*oBorderRadii)[C_TR] = gfxSize(radii[NS_CORNER_TOP_RIGHT_X],
                                   radii[NS_CORNER_TOP_RIGHT_Y]);
   (*oBorderRadii)[C_BR] = gfxSize(radii[NS_CORNER_BOTTOM_RIGHT_X],
                                   radii[NS_CORNER_BOTTOM_RIGHT_Y]);
   (*oBorderRadii)[C_BL] = gfxSize(radii[NS_CORNER_BOTTOM_LEFT_X],
@@ -563,24 +496,34 @@ nsCSSRendering::PaintBorder(nsPresContex
   if (!styleIfVisited) {
     PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                                aDirtyRect, aBorderArea, *styleBorder,
                                aStyleContext, aSkipSides);
     return;
   }
 
   nsStyleBorder newStyleBorder(*styleBorder);
+  // We're making an ephemeral stack copy here, so just copy this debug-only
+  // member to prevent assertions.
+#ifdef DEBUG
+  newStyleBorder.mImageTracked = styleBorder->mImageTracked;
+#endif
+
   NS_FOR_CSS_SIDES(side) {
     newStyleBorder.SetBorderColor(side,
       aStyleContext->GetVisitedDependentColor(
         nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]));
   }
   PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                              aDirtyRect, aBorderArea, newStyleBorder,
                              aStyleContext, aSkipSides);
+
+#ifdef DEBUG
+  newStyleBorder.mImageTracked = false;
+#endif
 }
 
 void
 nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
                                            nsIRenderingContext& aRenderingContext,
                                            nsIFrame* aForFrame,
                                            const nsRect& aDirtyRect,
                                            const nsRect& aBorderArea,
@@ -624,18 +567,23 @@ nsCSSRendering::PaintBorderWithStyleBord
   border = aStyleBorder.GetComputedBorder();
   if ((0 == border.left) && (0 == border.right) &&
       (0 == border.top) && (0 == border.bottom)) {
     // Empty border area
     return;
   }
 
   nsSize frameSize = aForFrame->GetSize();
-  GetBorderRadiusTwips(aStyleBorder.mBorderRadius, frameSize.width,
-                       frameSize.height, twipsRadii);
+  if (&aStyleBorder == aForFrame->GetStyleBorder() &&
+      frameSize == aBorderArea.Size()) {
+    aForFrame->GetBorderRadii(twipsRadii);
+  } else {
+    nsIFrame::ComputeBorderRadii(aStyleBorder.mBorderRadius, frameSize,
+                                 aBorderArea.Size(), aSkipSides, twipsRadii);
+  }
 
   // Turn off rendering for all of the zero sized sides
   if (aSkipSides & SIDE_BIT_TOP) border.top = 0;
   if (aSkipSides & SIDE_BIT_RIGHT) border.right = 0;
   if (aSkipSides & SIDE_BIT_BOTTOM) border.bottom = 0;
   if (aSkipSides & SIDE_BIT_LEFT) border.left = 0;
 
   // get the inside and outside parts of the border
@@ -644,28 +592,27 @@ nsCSSRendering::PaintBorderWithStyleBord
   SF(" outerRect: %d %d %d %d\n", outerRect.x, outerRect.y, outerRect.width, outerRect.height);
 
   // we can assume that we're already clipped to aDirtyRect -- I think? (!?)
 
   // Get our conversion values
   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
 
   // convert outer and inner rects
-  gfxRect oRect(RectToGfxRect(outerRect, twipsPerPixel));
+  gfxRect oRect(nsLayoutUtils::RectToGfxRect(outerRect, twipsPerPixel));
 
   // convert the border widths
   gfxFloat borderWidths[4] = { gfxFloat(border.top / twipsPerPixel),
                                gfxFloat(border.right / twipsPerPixel),
                                gfxFloat(border.bottom / twipsPerPixel),
                                gfxFloat(border.left / twipsPerPixel) };
 
   // convert the radii
   gfxCornerSizes borderRadii;
-  ComputePixelRadii(twipsRadii, outerRect, aSkipSides, twipsPerPixel,
-                    &borderRadii);
+  ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
 
   PRUint8 borderStyles[4];
   nscolor borderColors[4];
   nsBorderColors *compositeColors[4];
 
   // pull out styles, colors, composite colors
   NS_FOR_CSS_SIDES (i) {
     PRBool foreground;
@@ -744,20 +691,16 @@ nsCSSRendering::PaintOutline(nsPresConte
   }
 
   nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
     (aForFrame, PR_FALSE);
   nsStyleContext* bgContext = bgFrame->GetStyleContext();
   nscolor bgColor =
     bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
 
-  // get the radius for our outline
-  GetBorderRadiusTwips(ourOutline->mOutlineRadius, aBorderArea.width,
-                       aBorderArea.height, twipsRadii);
-
   // When the outline property is set on :-moz-anonymous-block or
   // :-moz-anonyomus-positioned-block pseudo-elements, it inherited that
   // outline from the inline that was broken because it contained a
   // block.  In that case, we don't want a really wide outline if the
   // block inside the inline is narrow, so union the actual contents of
   // the anonymous blocks.
   nsIFrame *frameForArea = aForFrame;
   do {
@@ -793,27 +736,30 @@ nsCSSRendering::PaintOutline(nsPresConte
   // encroach into the content area.  A safer calculation would be to
   // shorten insideRect by the radius one each side before performing this test.
   if (innerRect.Contains(aDirtyRect))
     return;
 
   nsRect outerRect = innerRect;
   outerRect.Inflate(width, width);
 
+  // get the radius for our outline
+  nsIFrame::ComputeBorderRadii(ourOutline->mOutlineRadius, aBorderArea.Size(),
+                               outerRect.Size(), 0, twipsRadii);
+
   // Get our conversion values
   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
 
   // get the outer rectangles
-  gfxRect oRect(RectToGfxRect(outerRect, twipsPerPixel));
+  gfxRect oRect(nsLayoutUtils::RectToGfxRect(outerRect, twipsPerPixel));
 
   // convert the radii
   nsMargin outlineMargin(width, width, width, width);
   gfxCornerSizes outlineRadii;
-  ComputePixelRadii(twipsRadii, outerRect, 0, twipsPerPixel,
-                    &outlineRadii);
+  ComputePixelRadii(twipsRadii, twipsPerPixel, &outlineRadii);
 
   PRUint8 outlineStyle = ourOutline->GetOutlineStyle();
   PRUint8 outlineStyles[4] = { outlineStyle,
                                outlineStyle,
                                outlineStyle,
                                outlineStyle };
 
   // This handles treating the initial color as 'currentColor'; if we
@@ -856,22 +802,22 @@ void
 nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
                            nsIRenderingContext& aRenderingContext,
                            const nsRect& aFocusRect,
                            nscolor aColor)
 {
   nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1);
   nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
 
-  gfxRect focusRect(RectToGfxRect(aFocusRect, oneDevPixel));
+  gfxRect focusRect(nsLayoutUtils::RectToGfxRect(aFocusRect, oneDevPixel));
 
   gfxCornerSizes focusRadii;
   {
     nscoord twipsRadii[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
-    ComputePixelRadii(twipsRadii, aFocusRect, 0, oneDevPixel, &focusRadii);
+    ComputePixelRadii(twipsRadii, oneDevPixel, &focusRadii);
   }
   gfxFloat focusWidths[4] = { gfxFloat(oneCSSPixel / oneDevPixel),
                               gfxFloat(oneCSSPixel / oneDevPixel),
                               gfxFloat(oneCSSPixel / oneDevPixel),
                               gfxFloat(oneCSSPixel / oneDevPixel) };
 
   PRUint8 focusStyles[4] = { NS_STYLE_BORDER_STYLE_DOTTED,
                              NS_STYLE_BORDER_STYLE_DOTTED,
@@ -1133,50 +1079,16 @@ nsCSSRendering::FindBackground(nsPresCon
 }
 
 void
 nsCSSRendering::DidPaint()
 {
   gInlineBGData->Reset();
 }
 
-PRBool
-nsCSSRendering::GetBorderRadiusTwips(const nsStyleCorners& aBorderRadius,
-                                     const nscoord aFrameWidth,
-                                     const nscoord aFrameHeight,
-                                     nscoord aRadii[8])
-{
-  PRBool result = PR_FALSE;
-
-  // Percentages are relative to whichever side they're on.
-  NS_FOR_CSS_HALF_CORNERS(i) {
-    const nsStyleCoord c = aBorderRadius.Get(i);
-    nscoord axis = NS_HALF_CORNER_IS_X(i) ? aFrameWidth : aFrameHeight;
-
-    switch (c.GetUnit()) {
-      case eStyleUnit_Percent:
-        aRadii[i] = (nscoord)(c.GetPercentValue() * axis);
-        break;
-
-      case eStyleUnit_Coord:
-        aRadii[i] = c.GetCoordValue();
-        break;
-
-      default:
-        NS_NOTREACHED("GetBorderRadiusTwips: bad unit");
-        aRadii[i] = 0;
-        break;
-    }
-
-    if (aRadii[i])
-      result = PR_TRUE;
-  }
-  return result;
-}
-
 void
 nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
                                     nsIRenderingContext& aRenderingContext,
                                     nsIFrame* aForFrame,
                                     const nsRect& aFrameArea,
                                     const nsRect& aDirtyRect)
 {
   const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
@@ -1196,47 +1108,42 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
     // We don't respect border-radius for native-themed widgets
     hasBorderRadius = PR_FALSE;
     // For opaque (rectangular) theme widgets we can take the generic
     // border-box path with border-radius disabled.
     nativeTheme = transparency != nsITheme::eOpaque;
   } else {
     nativeTheme = PR_FALSE;
     nscoord twipsRadii[8];
-    hasBorderRadius =
-      GetBorderRadiusTwips(styleBorder->mBorderRadius,
-                           aFrameArea.width,
-                           aFrameArea.height,
-                           twipsRadii);
+    NS_ASSERTION(aFrameArea.Size() == aForFrame->GetSize(), "unexpected size");
+    hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii);
     if (hasBorderRadius) {
-      PRIntn sidesToSkip = aForFrame->GetSkipSides();
-      ComputePixelRadii(twipsRadii, aFrameArea, sidesToSkip, twipsPerPixel,
-                        &borderRadii);
+      ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
     }
   }
 
   nsRect frameRect =
     nativeTheme ? aForFrame->GetOverflowRectRelativeToSelf() + aFrameArea.TopLeft() : aFrameArea;
-  gfxRect frameGfxRect = RectToGfxRect(frameRect, twipsPerPixel);
+  gfxRect frameGfxRect(nsLayoutUtils::RectToGfxRect(frameRect, twipsPerPixel));
   frameGfxRect.Round();
 
   // We don't show anything that intersects with the frame we're blurring on. So tell the
   // blurrer not to do unnecessary work there.
   gfxRect skipGfxRect = frameGfxRect;
   PRBool useSkipGfxRect = PR_TRUE;
   if (nativeTheme) {
     // Optimize non-leaf native-themed frames by skipping computing pixels
     // in the padding-box. We assume the padding-box is going to be painted
     // opaquely for non-leaf frames.
     // XXX this may not be a safe assumption; we should make this go away
     // by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.
     useSkipGfxRect = !aForFrame->IsLeaf();
     nsRect paddingRect =
       aForFrame->GetPaddingRect() - aForFrame->GetPosition() + aFrameArea.TopLeft();
-    skipGfxRect = RectToGfxRect(paddingRect, twipsPerPixel);
+    skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
   } else if (hasBorderRadius) {
     skipGfxRect.Inset(
         PR_MAX(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
         PR_MAX(borderRadii[C_BL].height, borderRadii[C_BR].height), 0);
   }
 
   for (PRUint32 i = shadows->Length(); i > 0; --i) {
     nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
@@ -1254,18 +1161,20 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
     }
 
     // shadowRect won't include the blur, so make an extra rect here that includes the blur
     // for use in the even-odd rule below.
     nsRect shadowRectPlusBlur = shadowRect;
     nscoord blurRadius = shadowItem->mRadius;
     shadowRectPlusBlur.Inflate(blurRadius, blurRadius);
 
-    gfxRect shadowGfxRect = RectToGfxRect(shadowRect, twipsPerPixel);
-    gfxRect shadowGfxRectPlusBlur = RectToGfxRect(shadowRectPlusBlur, twipsPerPixel);
+    gfxRect shadowGfxRect =
+      nsLayoutUtils::RectToGfxRect(shadowRect, twipsPerPixel);
+    gfxRect shadowGfxRectPlusBlur =
+      nsLayoutUtils::RectToGfxRect(shadowRectPlusBlur, twipsPerPixel);
     shadowGfxRect.Round();
     shadowGfxRectPlusBlur.RoundOut();
 
     gfxContext* renderContext = aRenderingContext.ThebesContext();
     nsRefPtr<gfxContext> shadowContext;
     nsContextBoxBlur blurringArea;
 
     // When getting the widget shape from the native theme, we're going
@@ -1379,34 +1288,30 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
     // "padding-box" for native-themed widgets, so just don't draw
     // inner box-shadows for them. But we allow chrome to paint inner
     // box shadows since chrome can be aware of the platform theme.
     return;
   }
 
   // Get any border radius, since box-shadow must also have rounded corners if the frame does
   nscoord twipsRadii[8];
-  PRBool hasBorderRadius = GetBorderRadiusTwips(styleBorder->mBorderRadius,
-                                                aFrameArea.width,
-                                                aFrameArea.height,
-                                                twipsRadii);
+  NS_ASSERTION(aFrameArea.Size() == aForFrame->GetSize(), "unexpected size");
+  PRBool hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii);
   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
 
   nsRect paddingRect = aFrameArea;
   nsMargin border = aForFrame->GetUsedBorder();
   aForFrame->ApplySkipSides(border);
   paddingRect.Deflate(border);
 
   gfxCornerSizes innerRadii;
   if (hasBorderRadius) {
     gfxCornerSizes borderRadii;
-    PRIntn sidesToSkip = aForFrame->GetSkipSides();
-
-    ComputePixelRadii(twipsRadii, aFrameArea, sidesToSkip,
-                      twipsPerPixel, &borderRadii);
+
+    ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
     gfxFloat borderSizes[4] = {
       gfxFloat(border.top / twipsPerPixel),
       gfxFloat(border.right / twipsPerPixel),
       gfxFloat(border.bottom / twipsPerPixel),
       gfxFloat(border.left / twipsPerPixel)
     };
     nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
                                            &innerRadii);
@@ -1458,17 +1363,17 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
       nsCSSBorderRenderer::ComputeInnerRadii(innerRadii, borderSizes,
                                              &clipRectRadii);
     }
 
     // Set the "skip rect" to the area within the frame that we don't paint in,
     // including after blurring. We also use this for clipping later on.
     nsRect skipRect = shadowClipRect;
     skipRect.Deflate(blurRadius, blurRadius);
-    gfxRect skipGfxRect = RectToGfxRect(skipRect, twipsPerPixel);
+    gfxRect skipGfxRect = nsLayoutUtils::RectToGfxRect(skipRect, twipsPerPixel);
     if (hasBorderRadius) {
       skipGfxRect.Inset(PR_MAX(clipRectRadii[C_TL].height, clipRectRadii[C_TR].height), 0,
                         PR_MAX(clipRectRadii[C_BL].height, clipRectRadii[C_BR].height), 0);
     }
 
     gfxContext* renderContext = aRenderingContext.ThebesContext();
     nsRefPtr<gfxContext> shadowContext;
     nsContextBoxBlur blurringArea;
@@ -1486,32 +1391,35 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
       shadowColor = aForFrame->GetStyleColor()->mColor;
 
     renderContext->Save();
     renderContext->SetColor(gfxRGBA(shadowColor));
 
     // Clip the context to the area of the frame's padding rect, so no part of the
     // shadow is painted outside. Also cut out anything beyond where the inset shadow
     // will be.
-    gfxRect shadowGfxRect = RectToGfxRect(paddingRect, twipsPerPixel);
+    gfxRect shadowGfxRect =
+      nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
     shadowGfxRect.Round();
     renderContext->NewPath();
     if (hasBorderRadius)
       renderContext->RoundedRectangle(shadowGfxRect, innerRadii, PR_FALSE);
     else
       renderContext->Rectangle(shadowGfxRect);
     renderContext->Rectangle(skipGfxRect);
     renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
     renderContext->Clip();
 
     // Fill the temporary surface minus the area within the frame that we should
     // not paint in, and blur and apply it
-    gfxRect shadowPaintGfxRect = RectToGfxRect(shadowPaintRect, twipsPerPixel);
+    gfxRect shadowPaintGfxRect =
+      nsLayoutUtils::RectToGfxRect(shadowPaintRect, twipsPerPixel);
     shadowPaintGfxRect.RoundOut();
-    gfxRect shadowClipGfxRect = RectToGfxRect(shadowClipRect, twipsPerPixel);
+    gfxRect shadowClipGfxRect =
+      nsLayoutUtils::RectToGfxRect(shadowClipRect, twipsPerPixel);
     shadowClipGfxRect.Round();
     shadowContext->NewPath();
     shadowContext->Rectangle(shadowPaintGfxRect);
     if (hasBorderRadius)
       shadowContext->RoundedRectangle(shadowClipGfxRect, clipRectRadii, PR_FALSE);
     else
       shadowContext->Rectangle(shadowClipGfxRect);
     shadowContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
@@ -1613,17 +1521,17 @@ static inline void
 SetupDirtyRects(const nsRect& aBGClipArea, const nsRect& aCallerDirtyRect,
                 nscoord aAppUnitsPerPixel,
                 /* OUT: */
                 nsRect* aDirtyRect, gfxRect* aDirtyRectGfx)
 {
   aDirtyRect->IntersectRect(aBGClipArea, aCallerDirtyRect);
 
   // Compute the Thebes equivalent of the dirtyRect.
-  *aDirtyRectGfx = RectToGfxRect(*aDirtyRect, aAppUnitsPerPixel);
+  *aDirtyRectGfx = nsLayoutUtils::RectToGfxRect(*aDirtyRect, aAppUnitsPerPixel);
   NS_WARN_IF_FALSE(aDirtyRect->IsEmpty() || !aDirtyRectGfx->IsEmpty(),
                    "converted dirty rect should not be empty");
   NS_ABORT_IF_FALSE(!aDirtyRect->IsEmpty() || aDirtyRectGfx->IsEmpty(),
                     "second should be empty if first is");
 }
 
 static void
 SetupBackgroundClip(gfxContext *aCtx, PRUint8 aBackgroundClip,
@@ -1673,17 +1581,18 @@ SetupBackgroundClip(gfxContext *aCtx, PR
   // If we have rounded corners, clip all subsequent drawing to the
   // rounded rectangle defined by bgArea and bgRadii (we don't know
   // whether the rounded corners intrude on the dirtyRect or not).
   // Do not do this if we have a caller-provided clip rect --
   // as above with bgArea, arguably a bug, but table painting seems
   // to depend on it.
 
   if (aHaveRoundedCorners) {
-    gfxRect bgAreaGfx(RectToGfxRect(*aBGClipArea, aAppUnitsPerPixel));
+    gfxRect bgAreaGfx =
+      nsLayoutUtils::RectToGfxRect(*aBGClipArea, aAppUnitsPerPixel);
     bgAreaGfx.Round();
     bgAreaGfx.Condition();
 
     if (bgAreaGfx.IsEmpty()) {
       // I think it's become possible to hit this since
       // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
       NS_WARNING("converted background area should not be empty");
       // Make our caller not do anything.
@@ -1948,17 +1857,18 @@ nsCSSRendering::PaintGradient(nsPresCont
                               const nsRect& aOneCellArea,