Merge
authorDoug Turner <dougt@dougt.org>
Mon, 01 Mar 2010 15:36:51 -0800
changeset 46697 ecf6874e899016eb98cca43502fc79d31b8527fb
parent 46696 eb3a2e3e7f265560aea28af465dbe63d720cfb5a (current diff)
parent 38824 57b360ef1208e4ffc0dc44b2ddf59d2e8de0b548 (diff)
child 46698 385cc0738bf947ed1b5dd79f063ebabe9f2ecf03
push idunknown
push userunknown
push dateunknown
milestone1.9.3a3pre
Merge
build/automation.py.in
content/base/public/Makefile.in
content/base/public/nsContentUtils.h
content/base/src/Makefile.in
content/base/src/nsContentUtils.cpp
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
content/base/src/nsGkAtomList.h
content/base/src/nsScriptLoader.cpp
content/canvas/src/nsCanvasRenderingContext2D.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoID.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/interfaces/canvas/nsIDOMCanvasRenderingContext2D.idl
dom/plugins/PluginInstanceChild.cpp
dom/plugins/PluginInstanceParent.cpp
dom/plugins/PluginMessageUtils.h
dom/plugins/PluginModuleChild.cpp
dom/plugins/PluginModuleChild.h
dom/plugins/PluginModuleParent.cpp
dom/tests/mochitest/dom-level2-html/test_HTMLIsIndexElement01.html
dom/tests/mochitest/dom-level2-html/test_HTMLIsIndexElement02.html
dom/tests/mochitest/dom-level2-html/test_HTMLIsIndexElement03.html
gfx/src/thebes/nsThebesDeviceContext.cpp
gfx/thebes/public/gfxAtsuiFonts.h
gfx/thebes/src/gfxAtsuiFonts.cpp
ipc/Makefile.in
ipc/glue/RPCChannel.cpp
ipc/ipdl/ipdl/lower.py
js/src/jsfile.cpp
js/src/jsfile.h
js/src/jsfile.msg
layout/base/nsPresContext.cpp
media/liboggz/endian.patch
modules/plugin/base/src/nsPluginHost.cpp
modules/plugin/test/mochitest/Makefile.in
modules/plugin/test/testplugin/nptest.cpp
netwerk/protocol/http/src/nsHttpHandler.cpp
testing/xpcshell/head.js
toolkit/components/places/tests/mochitest/bug_461710/visited_page-2.html
toolkit/components/places/tests/mochitest/bug_461710/visited_page-3.html
toolkit/library/libxul-config.mk
toolkit/toolkit-tiers.mk
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
widget/src/windows/nsWindow.cpp
widget/src/windows/nsWindow.h
widget/src/windows/nsWindowGfx.cpp
xpcom/build.mk
--- a/Makefile.in
+++ b/Makefile.in
@@ -121,45 +121,21 @@ ifdef ENABLE_TESTS
 include $(topsrcdir)/testing/testsuite-targets.mk
 else
 # OS X Universal builds will want to call this, so stub it out
 package-tests:
 endif
 
 include $(topsrcdir)/config/rules.mk
 
-# After we build tier toolkit, go back and build the tools from previous dirs
-tier_toolkit::
-	$(MAKE) tools_tier_js
-	$(MAKE) tools_tier_xpcom
-	$(MAKE) tools_tier_necko
-	$(MAKE) tools_tier_gecko
-	$(MAKE) tools_tier_toolkit
-
-ifeq (netwerk,$(MOZ_BUILD_APP))
-tier_necko::
-	$(EXIT_ON_ERROR) \
-	$(foreach tier,$(TIERS),$(MAKE) tools_tier_$(tier); )
-endif
-
 distclean::
 	cat unallmakefiles | $(XARGS) rm -f
 	rm -f unallmakefiles $(DIST_GARBAGE)
 
 ifeq ($(OS_ARCH),WINNT)
-rebase:
-ifdef MOZILLA_OFFICIAL
-	echo rebasing $(DIST)
-	/bin/find $(DIST) -name "*.dll" -a -not -name "msvc*" > rebase.lst
-	rebase -b 60000000 -R . -G rebase.lst
-	rm rebase.lst
-endif
-endif # WINNT
-
-ifeq ($(OS_ARCH),WINNT)
 # we want to copy PDB files on Windows
 MAKE_SYM_STORE_ARGS := -c
 ifdef PDBSTR_PATH
 MAKE_SYM_STORE_ARGS += -i
 endif
 ifeq (,$(CYGWIN_WRAPPER))
 # this doesn't work with Cygwin Python
 MAKE_SYM_STORE_ARGS += --vcs-info
@@ -230,36 +206,17 @@ endif # SYMBOLSTORE_PATH
 endif # WINCE
 endif # MOZ_CRASHREPORTER
 
 uploadsymbols:
 ifdef MOZ_CRASHREPORTER
 	$(SHELL) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh "$(DIST)/$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip"
 endif
 
-ifeq ($(OS_ARCH),WINNT)
-signnss:
-ifdef MOZILLA_OFFICIAL
-	echo signing NSS libs
-	cd $(DIST)/bin; ./shlibsign.exe -v -i softokn3.dll
-	cd $(DIST)/bin; ./shlibsign.exe -v -i freebl3.dll
-	cd $(DIST)/bin; ./shlibsign.exe -v -i nssdbm3.dll
-endif # MOZILLA_OFFICIAL
-
-deliver: rebase signnss
-
-endif # WINNT
-
-ifneq (,$(wildcard $(DIST)/bin/application.ini))
-BUILDID = $(shell $(PYTHON) $(srcdir)/config/printconfigsetting.py $(DIST)/bin/application.ini App BuildID)
-else
-BUILDID = $(shell $(PYTHON) $(srcdir)/config/printconfigsetting.py $(DIST)/bin/platform.ini Build BuildID)
-endif
-
-MOZ_SOURCE_STAMP = $(shell hg -R $(srcdir) parent --template="{node|short}\n" 2>/dev/null)
+# defined in package-name.mk
 export MOZ_SOURCE_STAMP
 
 #XXX: this is a hack, since we don't want to clobber for MSVC
 # PGO support, but we can't do this test in client.mk
 ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
 # No point in clobbering if PGO has been explicitly disabled.
 ifndef NO_PROFILE_GUIDED_OPTIMIZE
 maybe_clobber_profiledbuild: clean
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -2493,17 +2493,17 @@ NS_IMETHODIMP nsAccessible::GetNativeInt
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 void
 nsAccessible::DoCommand(nsIContent *aContent, PRUint32 aActionIndex)
 {
   nsCOMPtr<nsIContent> content = aContent;
   if (!content)
-    content = do_QueryInterface(mDOMNode);
+    content = nsCoreUtils::GetRoleContent(mDOMNode);
 
   NS_DISPATCH_RUNNABLEMETHOD_ARG2(DispatchClickEvent, this,
                                   content, aActionIndex)
 }
 
 void
 nsAccessible::DispatchClickEvent(nsIContent *aContent, PRUint32 aActionIndex)
 {
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -629,18 +629,20 @@ nsDocAccessible::Init()
 
 nsresult
 nsDocAccessible::Shutdown()
 {
   if (!mWeakShell) {
     return NS_OK;  // Already shutdown
   }
 
-  mEventQueue->Shutdown();
-  mEventQueue = nsnull;
+  if (mEventQueue) {
+    mEventQueue->Shutdown();
+    mEventQueue = nsnull;
+  }
 
   nsCOMPtr<nsIDocShellTreeItem> treeItem =
     nsCoreUtils::GetDocShellTreeItemFor(mDOMNode);
   ShutdownChildDocuments(treeItem);
 
   RemoveEventListeners();
 
   mWeakShell = nsnull;  // Avoid reentrancy
--- a/accessible/src/base/nsTextAttrs.cpp
+++ b/accessible/src/base/nsTextAttrs.cpp
@@ -652,17 +652,17 @@ nsFontWeightTextAttr::GetFontWeight(nsIF
   // value of font weight (used font might not have some font weight values).
   nsStyleFont* styleFont =
     (nsStyleFont*)(aFrame->GetStyleDataExternal(eStyleStruct_Font));
 
   gfxUserFontSet *fs = aFrame->PresContext()->GetUserFontSet();
 
   nsCOMPtr<nsIFontMetrics> fm;
   aFrame->PresContext()->DeviceContext()->
-    GetMetricsFor(styleFont->mFont, aFrame->GetStyleVisibility()->mLangGroup,
+    GetMetricsFor(styleFont->mFont, aFrame->GetStyleVisibility()->mLanguage,
                   fs, *getter_AddRefs(fm));
 
   nsCOMPtr<nsIThebesFontMetrics> tfm = do_QueryInterface(fm);
   gfxFontGroup *fontGroup = tfm->GetThebesFontGroup();
   gfxFont *font = fontGroup->GetFontAt(0);
 
   // When there doesn't exist a bold font in the family and so the rendering of
   // a non-bold font face is changed so that the user sees what looks like a
--- a/accessible/src/msaa/nsTextAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsTextAccessibleWrap.cpp
@@ -256,17 +256,17 @@ STDMETHODIMP nsTextAccessibleWrap::get_f
   if (!rc) {
     return E_FAIL;
   }
 
   const nsStyleFont *font = frame->GetStyleFont();
 
   const nsStyleVisibility *visibility = frame->GetStyleVisibility();
 
-  if (NS_FAILED(rc->SetFont(font->mFont, visibility->mLangGroup,
+  if (NS_FAILED(rc->SetFont(font->mFont, visibility->mLanguage,
                             presShell->GetPresContext()->GetUserFontSet()))) {
     return E_FAIL;
   }
 
   nsCOMPtr<nsIDeviceContext> deviceContext;
   rc->GetDeviceContext(*getter_AddRefs(deviceContext));
   if (!deviceContext) {
     return E_FAIL;
--- a/accessible/tests/mochitest/tree/test_combobox.xul
+++ b/accessible/tests/mochitest/tree/test_combobox.xul
@@ -47,17 +47,21 @@
       //////////////////////////////////////////////////////////////////////////
       // editable menulist
 
       accTree = {
         role: ROLE_COMBOBOX,
         children: [
           {
             role: ROLE_ENTRY,
-            children: []
+            children: [
+              {
+                role: ROLE_TEXT_LEAF // HTML 5 placeholder attribute value
+              }
+            ]
           },
           {
             role: ROLE_COMBOBOX_LIST, // context menu
             children: []
           },
           {
             role: ROLE_PUSHBUTTON, // dropmarker
           },
@@ -109,17 +113,21 @@
             children: [
               {
                 role: ROLE_COMBOBOX_OPTION
               }
             ]
           },
           {
             role: ROLE_ENTRY,
-            children: [ ]
+            children: [
+              {
+                role: ROLE_TEXT_LEAF // HTML 5 placeholder attribute value
+              }
+            ]
           },
           {
             role: ROLE_COMBOBOX_LIST, // context menu popup
             children: [ ]
           }
         ]
       };
       testAccessibleTree("autocomplete2", accTree);
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -505,19 +505,18 @@
                 </menu>
               </menupopup>
             </menu>
 
   <menu id="bookmarksMenu"
         label="&bookmarksMenu.label;"
         accesskey="&bookmarksMenu.accesskey;"
         ondragenter="PlacesMenuDNDController.onBookmarksMenuDragEnter(event);"
-        ondrop="nsDragAndDrop.drop(event, BookmarksMenuDropHandler);"
-        ondragover="nsDragAndDrop.dragOver(event, BookmarksMenuDropHandler);"
-        ondragexit="nsDragAndDrop.dragExit(event, BookmarksMenuDropHandler);">
+        ondragover="BookmarksMenuDropHandler.onDragOver(event);"
+        ondrop="BookmarksMenuDropHandler.onDrop(event);">
     <menupopup id="bookmarksMenuPopup"
                type="places"
                place="place:folder=BOOKMARKS_MENU"
                context="placesContext"
                openInTabs="children"
                oncommand="BookmarksEventHandler.onCommand(event);"
                onclick="BookmarksEventHandler.onClick(event);"
                onpopupshowing="BookmarksEventHandler.onPopupShowing(event);"
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -224,17 +224,17 @@ var StarUI = {
                                { hiddenRows: ["description", "location",
                                               "loadInSidebar", "keyword"] });
   },
 
   panelShown:
   function SU_panelShown(aEvent) {
     if (aEvent.target == this.panel) {
       if (!this._element("editBookmarkPanelContent").hidden) {
-        fieldToFocus = "editBMPanel_" +
+        let fieldToFocus = "editBMPanel_" +
           gPrefService.getCharPref("browser.bookmarks.editDialog.firstEditField");
         var elt = this._element(fieldToFocus);
         elt.focus();
         elt.select();
       }
       else {
         // Note this isn't actually used anymore, we should remove this
         // once we decide not to bring back the page bookmarked notification
@@ -987,81 +987,36 @@ var BookmarksEventHandler = {
       tooltipUrl.value = url;
 
     // Show tooltip.
     return true;
   }
 };
 
 /**
- * Drag and Drop handling specifically for the Bookmarks Menu item in the
- * top level menu bar
+ * Drag and Drop handler for the Bookmarks menu in the top level menu bar.
  */
-var BookmarksMenuDropHandler = {
-  /**
-   * Need to tell the session to update the state of the cursor as we drag
-   * over the Bookmarks Menu to show the "can drop" state vs. the "no drop"
-   * state.
-   */
-  onDragOver: function BMDH_onDragOver(event, flavor, session) {
-    if (!this.canDrop(event, session))
-      event.dataTransfer.effectAllowed = "none";
-  },
+let BookmarksMenuDropHandler = {
+  onDragOver: function BMDH_onDragOver(event) {
+    let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
+                                PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                Ci.nsITreeView.DROP_ON);
+    if (ip && PlacesControllerDragHelper.canDrop(ip, event.dataTransfer))
+      event.preventDefault();
 
-  /**
-   * Advertises the set of data types that can be dropped on the Bookmarks
-   * Menu
-   * @returns a FlavourSet object per nsDragAndDrop parlance.
-   */
-  getSupportedFlavours: function BMDH_getSupportedFlavours() {
-    var view = document.getElementById("bookmarksMenuPopup");
-    return view.getSupportedFlavours();
+    event.stopPropagation();
   },
 
-  /**
-   * Determine whether or not the user can drop on the Bookmarks Menu.
-   * @param   event
-   *          A dragover event
-   * @param   session
-   *          The active DragSession
-   * @returns true if the user can drop onto the Bookmarks Menu item, false 
-   *          otherwise.
-   */
-  canDrop: function BMDH_canDrop(event, session) {
-    PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer;
-
-    var ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, -1);  
-    return ip && PlacesControllerDragHelper.canDrop(ip);
-  },
-
-  /**
-   * Called when the user drops onto the top level Bookmarks Menu item.
-   * @param   event
-   *          A drop event
-   * @param   data
-   *          Data that was dropped
-   * @param   session
-   *          The active DragSession
-   */
-  onDrop: function BMDH_onDrop(event, data, session) {
-    PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer;
-
-  // Put the item at the end of bookmark menu
-    var ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, -1,
+  onDrop: function BMDH_onDrop(event) {
+    // Put the item at the end of bookmark menu.
+    let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
+                                PlacesUtils.bookmarks.DEFAULT_INDEX,
                                 Ci.nsITreeView.DROP_ON);
-    PlacesControllerDragHelper.onDrop(ip);
-  },
-
-  /**
-   * Called when drop target leaves the menu or after a drop.
-   * @param   aEvent
-   *          A drop event
-   */
-  onDragExit: function BMDH_onDragExit(event, session) {
-    PlacesControllerDragHelper.currentDataTransfer = null;
+    PlacesControllerDragHelper.onDrop(ip, event.dataTransfer);
+    event.stopPropagation();
   }
 };
 
 /**
  * Handles special drag and drop functionality for menus on the Bookmarks 
  * Toolbar and Bookmarks Menu.
  */
 var PlacesMenuDNDController = {
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -79,17 +79,17 @@ toolbar[mode="icons"] > #reload-button[d
 #identity-icon-labels {
   max-width: 18em;
 }
 
 #identity-icon-country-label {
   direction: ltr;
 }
 
-#identity-box.verifiedIdentity > hbox > #identity-icon-labels > #identity-icon-label {
+#identity-box.verifiedIdentity > #identity-box-inner > #identity-icon-labels > #identity-icon-label {
   -moz-margin-end: 0.25em !important;
 }
 
 #wrapper-search-container > #search-container > #searchbar > .searchbar-textbox > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input {
   visibility: hidden;
 }
 
 /* ::::: Unified Back-/Forward Button ::::: */
@@ -98,16 +98,20 @@ toolbar[mode="icons"] > #reload-button[d
 #back-forward-dropmarker > .toolbarbutton-icon,
 #back-forward-dropmarker > .toolbarbutton-text {
   display: none;
 }
 .unified-nav-current {
   font-weight: bold;
 }
 
+toolbarbutton.bookmark-item {
+  max-width: 13em;
+}
+
 %ifdef MOZ_WIDGET_GTK2
 /* Bookmarks override the "images-in-menus" metric in xul.css */
 .bookmark-item > .menu-iconic-left,
 .searchbar-engine-menuitem > .menu-iconic-left {
   visibility: inherit;
 }
 %endif
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -44,16 +44,17 @@
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Dão Gottwald <dao@mozilla.com>
 #   Thomas K. Dyas <tdyas@zecador.org>
 #   Edward Lee <edward.lee@engineering.uiuc.edu>
 #   Paul O’Shannessy <paul@oshannessy.com>
 #   Nils Maier <maierman@web.de>
 #   Rob Arnold <robarnold@cmu.edu>
 #   Dietrich Ayala <dietrich@mozilla.com>
+#   Gavin Sharp <gavin@gavinsharp.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -61,16 +62,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 *****
 
 let Ci = Components.interfaces;
 let Cu = Components.utils;
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gCharsetMenu = null;
 var gLastBrowserCharset = null;
 var gPrevCharset = null;
 var gProxyFavIcon = null;
@@ -101,19 +103,17 @@ var gEditUIVisible = true;
   window.__defineSetter__(name, function (val) {
     delete window[name];
     return window[name] = val;
   });
 });
 
 __defineGetter__("gPrefService", function() {
   delete this.gPrefService;
-  return this.gPrefService = Cc["@mozilla.org/preferences-service;1"]
-                               .getService(Ci.nsIPrefBranch2)
-                               .QueryInterface(Ci.nsIPrefService);
+  return this.gPrefService = Services.prefs;
 });
 
 __defineGetter__("PluralForm", function() {
   Cu.import("resource://gre/modules/PluralForm.jsm");
   return this.PluralForm;
 });
 __defineSetter__("PluralForm", function (val) {
   delete this.PluralForm;
@@ -321,19 +321,16 @@ function findChildShell(aDocument, aDocS
       return docShell;
   }
   return null;
 }
 
 const gPopupBlockerObserver = {
   _reportButton: null,
 
-  get _pm ()
-    Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager),
-
   onUpdatePageReport: function (aEvent)
   {
     if (aEvent.originalTarget != gBrowser.selectedBrowser)
       return;
 
     if (!this._reportButton)
       this._reportButton = document.getElementById("page-report-button");
 
@@ -390,17 +387,17 @@ const gPopupBlockerObserver = {
       // Record the fact that we've reported this blocked popup, so we don't
       // show it again.
       gBrowser.pageReport.reported = true;
     }
   },
 
   toggleAllowPopupsForSite: function (aEvent)
   {
-    var pm = this._pm;
+    var pm = Services.perms;
     var shouldBlock = aEvent.target.getAttribute("block") == "true";
     var perm = shouldBlock ? pm.DENY_ACTION : pm.ALLOW_ACTION;
     pm.add(gBrowser.currentURI, "popup", perm);
 
     gBrowser.getNotificationBox().removeCurrentNotification();
   },
 
   fillPopupList: function (aEvent)
@@ -414,17 +411,17 @@ const gPopupBlockerObserver = {
     //          also back out the fix for bug 343772 where
     //          nsGlobalWindow::CheckOpenAllow() was changed to also
     //          check if the top window's location is whitelisted.
     var uri = gBrowser.currentURI;
     var blockedPopupAllowSite = document.getElementById("blockedPopupAllowSite");
     try {
       blockedPopupAllowSite.removeAttribute("hidden");
 
-      var pm = this._pm;
+      var pm = Services.perms;
       if (pm.testPermission(uri, "popup") == pm.ALLOW_ACTION) {
         // Offer an item to block popups for this site, if a whitelist entry exists
         // already for it.
         let blockString = gNavigatorBundle.getFormattedString("popupBlock", [uri.host]);
         blockedPopupAllowSite.setAttribute("label", blockString);
         blockedPopupAllowSite.setAttribute("block", "true");
       }
       else {
@@ -531,19 +528,17 @@ const gPopupBlockerObserver = {
     var bundlePreferences = document.getElementById("bundle_preferences");
     var params = { blockVisible   : false,
                    sessionVisible : false,
                    allowVisible   : true,
                    prefilledHost  : host,
                    permissionType : "popup",
                    windowTitle    : bundlePreferences.getString("popuppermissionstitle"),
                    introText      : bundlePreferences.getString("popuppermissionstext") };
-    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                        .getService(Components.interfaces.nsIWindowMediator);
-    var existingWindow = wm.getMostRecentWindow("Browser:Permissions");
+    var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
     if (existingWindow) {
       existingWindow.initWithParams(params);
       existingWindow.focus();
     }
     else
       window.openDialog("chrome://browser/content/preferences/permissions.xul",
                         "_blank", "resizable,dialog=no,centerscreen", params);
   },
@@ -1024,19 +1019,17 @@ function BrowserStartup() {
           box.removeAttribute("sidebarcommand");
         }
       }
     }
   }
 
   // Certain kinds of automigration rely on this notification to complete their
   // tasks BEFORE the browser window is shown.
-  Cc["@mozilla.org/observer-service;1"]
-    .getService(Ci.nsIObserverService)
-    .notifyObservers(null, "browser-window-before-show", "");
+  Services.obs.notifyObservers(null, "browser-window-before-show", "");
 
   // Set a sane starting width/height for all resolutions on new profiles.
   if (!document.documentElement.hasAttribute("width")) {
     let defaultWidth = 994;
     let defaultHeight;
     if (screen.availHeight <= 600) {
       document.documentElement.setAttribute("sizemode", "maximized");
       defaultWidth = 610;
@@ -1096,28 +1089,29 @@ function HandleAppCommandEvent(evt) {
     BrowserHome();
     break;
   default:
     break;
   }
 }
 
 function prepareForStartup() {
-  var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-
   gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false);
+
   // Note: we need to listen to untrusted events, because the pluginfinder XBL
   // binding can't fire trusted ones (runs with page privileges).
   gBrowser.addEventListener("PluginNotFound", gMissingPluginInstaller.newMissingPlugin, true, true);
   gBrowser.addEventListener("PluginCrashed", gMissingPluginInstaller.pluginInstanceCrashed, true, true);
   gBrowser.addEventListener("PluginBlocklisted", gMissingPluginInstaller.newMissingPlugin, true, true);
   gBrowser.addEventListener("PluginOutdated", gMissingPluginInstaller.newMissingPlugin, true, true);
   gBrowser.addEventListener("PluginDisabled", gMissingPluginInstaller.newDisabledPlugin, true, true);
   gBrowser.addEventListener("NewPluginInstalled", gMissingPluginInstaller.refreshBrowser, false);
-  os.addObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed", false);
+
+  Services.obs.addObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed", false);
+
   window.addEventListener("AppCommand", HandleAppCommandEvent, true);
 
   var webNavigation;
   try {
     webNavigation = getWebNavigation();
     if (!webNavigation)
       throw "no XBL binding for browser";
   } catch (e) {
@@ -1151,17 +1145,17 @@ function prepareForStartup() {
 
   // Manually hook up session and global history for the first browser
   // so that we don't have to load global history before bringing up a
   // window.
   // Wire up session and global history before any possible
   // progress notifications for back/forward button updating
   webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"]
                                            .createInstance(Components.interfaces.nsISHistory);
-  os.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
+  Services.obs.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
 
   // remove the disablehistory attribute so the browser cleans up, as
   // though it had done this work itself
   gBrowser.browsers[0].removeAttribute("disablehistory");
 
   // enable global history
   try {
     gBrowser.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = true;
@@ -1180,19 +1174,18 @@ function prepareForStartup() {
   gBrowser.addEventListener("MozApplicationManifest",
                             OfflineApps, false);
 
   // setup simple gestures support
   gGestureSupport.init(true);
 }
 
 function delayedStartup(isLoadingBlank, mustLoadSidebar) {
-  var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
-  os.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
-  os.addObserver(gXPInstallObserver, "xpinstall-install-blocked", false);
+  Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
+  Services.obs.addObserver(gXPInstallObserver, "xpinstall-install-blocked", false);
 
   BrowserOffline.init();
   OfflineApps.init();
 
   gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
 
   // Ensure login manager is up and running.
   Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
@@ -1251,22 +1244,20 @@ function delayedStartup(isLoadingBlank, 
       var shellBundle = document.getElementById("bundle_shell");
 
       var brandShortName = brandBundle.getString("brandShortName");
       var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
       var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
                                                          [brandShortName]);
       var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
                                                          [brandShortName]);
-      const IPS = Components.interfaces.nsIPromptService;
-      var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
-                                                .getService(IPS);
       var checkEveryTime = { value: shouldCheck };
+      var ps = Services.prompt;
       var rv = ps.confirmEx(window, promptTitle, promptMessage,
-                            IPS.STD_YES_NO_BUTTONS,
+                            ps.STD_YES_NO_BUTTONS,
                             null, null, null, checkboxLabel, checkEveryTime);
       if (rv == 0)
         shell.setDefaultBrowser(true, false);
       shell.shouldCheckDefaultBrowser = checkEveryTime.value;
     }
   }
 #endif
 
@@ -1396,21 +1387,19 @@ function BrowserShutdown()
 
   try {
     FullZoom.destroy();
   }
   catch(ex) {
     Components.utils.reportError(ex);
   }
 
-  var os = Components.classes["@mozilla.org/observer-service;1"]
-    .getService(Components.interfaces.nsIObserverService);
-  os.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
-  os.removeObserver(gXPInstallObserver, "xpinstall-install-blocked");
-  os.removeObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed");
+  Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
+  Services.obs.removeObserver(gXPInstallObserver, "xpinstall-install-blocked");
+  Services.obs.removeObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed");
 
   try {
     gBrowser.removeProgressListener(window.XULBrowserWindow);
     gBrowser.removeTabsProgressListener(window.TabsProgressListener);
   } catch (ex) {
   }
 
   PlacesStarButton.uninit();
@@ -1423,19 +1412,17 @@ function BrowserShutdown()
     Components.utils.reportError(ex);
   }
 
   BrowserOffline.uninit();
   OfflineApps.uninit();
   DownloadMonitorPanel.uninit();
   gPrivateBrowsingUI.uninit();
 
-  var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
-  var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator);
-  var enumerator = windowManagerInterface.getEnumerator(null);
+  var enumerator = Services.wm.getEnumerator(null);
   enumerator.getNext();
   if (!enumerator.hasMoreElements()) {
     document.persist("sidebar-box", "sidebarcommand");
     document.persist("sidebar-box", "width");
     document.persist("sidebar-box", "src");
     document.persist("sidebar-title", "value");
   }
 
@@ -1944,29 +1931,27 @@ function loadURI(uri, referrer, postData
   } catch (e) {
   }
 }
 
 function getShortcutOrURI(aURL, aPostDataRef) {
   var shortcutURL = null;
   var keyword = aURL;
   var param = "";
-  var searchService = Cc["@mozilla.org/browser/search-service;1"].
-                      getService(Ci.nsIBrowserSearchService);
 
   var offset = aURL.indexOf(" ");
   if (offset > 0) {
     keyword = aURL.substr(0, offset);
     param = aURL.substr(offset + 1);
   }
 
   if (!aPostDataRef)
     aPostDataRef = {};
 
-  var engine = searchService.getEngineByAlias(keyword);
+  var engine = Services.search.getEngineByAlias(keyword);
   if (engine) {
     var submission = engine.getSubmission(param, null);
     aPostDataRef.value = submission.postData;
     return submission.uri.spec;
   }
 
   [shortcutURL, aPostDataRef.value] =
     PlacesUtils.getURLAndPostDataForKeyword(keyword);
@@ -2663,23 +2648,35 @@ function FillInHTMLTooltip(tipElement)
     return retVal;
 
   const XLinkNS = "http://www.w3.org/1999/xlink";
 
 
   var titleText = null;
   var XLinkTitleText = null;
   var SVGTitleText = null;
+  var lookingForSVGTitle = true;
   var direction = tipElement.ownerDocument.dir;
 
   while (!titleText && !XLinkTitleText && !SVGTitleText && tipElement) {
     if (tipElement.nodeType == Node.ELEMENT_NODE) {
       titleText = tipElement.getAttribute("title");
-      XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
-      if (tipElement instanceof SVGElement) {
+      if ((tipElement instanceof HTMLAnchorElement && tipElement.href) ||
+          (tipElement instanceof HTMLAreaElement && tipElement.href) ||
+          (tipElement instanceof HTMLLinkElement && tipElement.href) ||
+          (tipElement instanceof SVGAElement && tipElement.hasAttributeNS(XLinkNS, "href"))) {
+        XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
+      }
+      if (lookingForSVGTitle && 
+          !(tipElement instanceof SVGElement &&
+            tipElement.parentNode instanceof SVGElement &&
+            !(tipElement.parentNode instanceof SVGForeignObjectElement))) {
+        lookingForSVGTitle = false;
+      }
+      if (lookingForSVGTitle) {
         let length = tipElement.childNodes.length;
         for (let i = 0; i < length; i++) {
           let childNode = tipElement.childNodes[i];
           if (childNode instanceof SVGTitleElement) {
             SVGTitleText = childNode.textContent;
             break;
           }
         }
@@ -2805,21 +2802,20 @@ var homeButtonObserver = {
     {
       var statusTextFld = document.getElementById("statusbar-display");
       statusTextFld.label = "";
     }
 }
 
 function openHomeDialog(aURL)
 {
-  var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
   var promptTitle = gNavigatorBundle.getString("droponhometitle");
   var promptMsg   = gNavigatorBundle.getString("droponhomemsg");
-  var pressedVal  = promptService.confirmEx(window, promptTitle, promptMsg,
-                          promptService.STD_YES_NO_BUTTONS,
+  var pressedVal  = Services.prompt.confirmEx(window, promptTitle, promptMsg,
+                          Services.prompt.STD_YES_NO_BUTTONS,
                           null, null, null, null, {value:0});
 
   if (pressedVal == 0) {
     try {
       var str = Components.classes["@mozilla.org/supports-string;1"]
                           .createInstance(Components.interfaces.nsISupportsString);
       str.data = aURL;
       gPrefService.setComplexValue("browser.startup.homepage",
@@ -3062,19 +3058,17 @@ const BrowserSearch = {
     if (gBrowser.shouldLoadFavIcon(targetDoc.documentURIObject))
       iconURL = targetDoc.documentURIObject.prePath + "/favicon.ico";
 
     var hidden = false;
     // If this engine (identified by title) is already in the list, add it
     // to the list of hidden engines rather than to the main list.
     // XXX This will need to be changed when engines are identified by URL;
     // see bug 335102.
-    var searchService = Cc["@mozilla.org/browser/search-service;1"].
-                        getService(Ci.nsIBrowserSearchService);
-    if (searchService.getEngineByName(engine.title))
+    if (Services.search.getEngineByName(engine.title))
       hidden = true;
 
     var engines = (hidden ? browser.hiddenEngines : browser.engines) || [];
 
     engines.push({ uri: engine.href,
                    title: engine.title,
                    icon: iconURL });
 
@@ -3140,45 +3134,40 @@ const BrowserSearch = {
     var searchBar = this.searchBar;
     if (searchBar && window.fullScreen)
       FullScreen.mouseoverToggle(true);
 
     if (isElementVisible(searchBar)) {
       searchBar.select();
       searchBar.focus();
     } else {
-      var ss = Cc["@mozilla.org/browser/search-service;1"].
-               getService(Ci.nsIBrowserSearchService);
-      var searchForm = ss.defaultEngine.searchForm;
-      openUILinkIn(searchForm, "current");
+      openUILinkIn(Services.search.defaultEngine.searchForm, "current");
     }
   },
 
   /**
    * Loads a search results page, given a set of search terms. Uses the current
    * engine if the search bar is visible, or the default engine otherwise.
    *
    * @param searchText
    *        The search terms to use for the search.
    *
    * @param useNewTab
    *        Boolean indicating whether or not the search should load in a new
    *        tab.
    */
   loadSearch: function BrowserSearch_search(searchText, useNewTab) {
-    var ss = Cc["@mozilla.org/browser/search-service;1"].
-             getService(Ci.nsIBrowserSearchService);
     var engine;
   
     // If the search bar is visible, use the current engine, otherwise, fall
     // back to the default engine.
     if (isElementVisible(this.searchBar))
-      engine = ss.currentEngine;
+      engine = Services.search.currentEngine;
     else
-      engine = ss.defaultEngine;
+      engine = Services.search.defaultEngine;
   
     var submission = engine.getSubmission(searchText, null); // HTML response
 
     // getSubmission can return null if the engine doesn't have a URL
     // with a text/html response type.  This is unlikely (since
     // SearchService._addEngineToStore() should fail for such an engine),
     // but let's be on the safe side.
     if (!submission)
@@ -3301,19 +3290,17 @@ function toJavaScriptConsole()
 function BrowserDownloadsUI()
 {
   Cc["@mozilla.org/download-manager-ui;1"].
   getService(Ci.nsIDownloadManagerUI).show(window);
 }
 
 function toOpenWindowByType(inType, uri, features)
 {
-  var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
-  var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator);
-  var topWindow = windowManagerInterface.getMostRecentWindow(inType);
+  var topWindow = Services.wm.getMostRecentWindow(inType);
 
   if (topWindow)
     topWindow.focus();
   else if (features)
     window.open(uri, "_blank", features);
   else
     window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
 }
@@ -4317,39 +4304,34 @@ var XULBrowserWindow = {
   startDocumentLoad: function (aRequest) {
     // clear out feed data
     gBrowser.selectedBrowser.feeds = null;
 
     // clear out search-engine data
     gBrowser.selectedBrowser.engines = null;    
 
     var uri = aRequest.QueryInterface(Ci.nsIChannel).URI;
-    var observerService = Cc["@mozilla.org/observer-service;1"]
-                            .getService(Ci.nsIObserverService);
 
     if (gURLBar &&
         gURLBar.value == "" &&
         getWebNavigation().currentURI.spec == "about:blank")
       URLBarSetURI(uri);
 
     try {
-      observerService.notifyObservers(content, "StartDocumentLoad", uri.spec);
+      Services.obs.notifyObservers(content, "StartDocumentLoad", uri.spec);
     } catch (e) {
     }
   },
 
   endDocumentLoad: function (aRequest, aStatus) {
     var urlStr = aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec;
 
-    var observerService = Cc["@mozilla.org/observer-service;1"]
-                            .getService(Ci.nsIObserverService);
-
     var notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";
     try {
-      observerService.notifyObservers(content, notification, urlStr);
+      Services.obs.notifyObservers(content, notification, urlStr);
     } catch (e) {
     }
   }
 };
 
 var CombinedStopReload = {
   init: function () {
     if (this._initialized)
@@ -5161,25 +5143,23 @@ function SelectDetector(event, doReload)
 {
     var uri =  event.target.getAttribute("id");
     var prefvalue = uri.substring('chardet.'.length, uri.length);
     if ("off" == prefvalue) { // "off" is special value to turn off the detectors
         prefvalue = "";
     }
 
     try {
-        var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                             .getService(Components.interfaces.nsIPrefBranch);
-        var str =  Components.classes["@mozilla.org/supports-string;1"]
-                             .createInstance(Components.interfaces.nsISupportsString);
+        var str =  Cc["@mozilla.org/supports-string;1"].
+                   createInstance(Ci.nsISupportsString);
 
         str.data = prefvalue;
-        pref.setComplexValue("intl.charset.detector",
-                             Components.interfaces.nsISupportsString, str);
-        if (doReload) window.content.location.reload();
+        gPrefService.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
+        if (doReload)
+          window.content.location.reload();
     }
     catch (ex) {
         dump("Failed to set the intl.charset.detector preference.\n");
     }
 }
 
 function SetForcedDetector(doReload)
 {
@@ -5221,69 +5201,55 @@ function UpdateCurrentCharset()
     }
 
     var menuitem = document.getElementById('charset.' + wnd.document.characterSet);
     if (menuitem) {
         menuitem.setAttribute('checked', 'true');
     }
 }
 
-function UpdateCharsetDetector()
-{
-    var prefvalue;
-
-    try {
-        var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                             .getService(Components.interfaces.nsIPrefBranch);
-        prefvalue = pref.getComplexValue("intl.charset.detector",
-                                         Components.interfaces.nsIPrefLocalizedString).data;
-    }
-    catch (ex) {
-        prefvalue = "";
-    }
-
-    if (prefvalue == "") prefvalue = "off";
-    dump("intl.charset.detector = "+ prefvalue + "\n");
-
-    prefvalue = 'chardet.' + prefvalue;
-    var menuitem = document.getElementById(prefvalue);
-
-    if (menuitem) {
-        menuitem.setAttribute('checked', 'true');
-    }
+function UpdateCharsetDetector() {
+  var prefvalue = "off";
+
+  try {
+    prefvalue = gPrefService.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString).data;
+  }
+  catch (ex) {}
+
+  prefvalue = "chardet." + prefvalue;
+
+  var menuitem = document.getElementById(prefvalue);
+  if (menuitem)
+    menuitem.setAttribute("checked", "true");
 }
 
-function UpdateMenus(event)
-{
-    // use setTimeout workaround to delay checkmark the menu
-    // when onmenucomplete is ready then use it instead of oncreate
-    // see bug 78290 for the detail
-    UpdateCurrentCharset();
-    setTimeout(UpdateCurrentCharset, 0);
-    UpdateCharsetDetector();
-    setTimeout(UpdateCharsetDetector, 0);
+function UpdateMenus(event) {
+  // use setTimeout workaround to delay checkmark the menu
+  // when onmenucomplete is ready then use it instead of oncreate
+  // see bug 78290 for the detail
+  UpdateCurrentCharset();
+  setTimeout(UpdateCurrentCharset, 0);
+  UpdateCharsetDetector();
+  setTimeout(UpdateCharsetDetector, 0);
 }
 
-function CreateMenu(node)
-{
-  var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-  observerService.notifyObservers(null, "charsetmenu-selected", node);
+function CreateMenu(node) {
+  Services.obs.notifyObservers(null, "charsetmenu-selected", node);
 }
 
-function charsetLoadListener (event)
-{
-    var charset = window.content.document.characterSet;
-
-    if (charset.length > 0 && (charset != gLastBrowserCharset)) {
-        if (!gCharsetMenu)
-          gCharsetMenu = Components.classes['@mozilla.org/rdf/datasource;1?name=charset-menu'].getService().QueryInterface(Components.interfaces.nsICurrentCharsetListener);
-        gCharsetMenu.SetCurrentCharset(charset);
-        gPrevCharset = gLastBrowserCharset;
-        gLastBrowserCharset = charset;
-    }
+function charsetLoadListener(event) {
+  var charset = window.content.document.characterSet;
+
+  if (charset.length > 0 && (charset != gLastBrowserCharset)) {
+    if (!gCharsetMenu)
+      gCharsetMenu = Cc['@mozilla.org/rdf/datasource;1?name=charset-menu'].getService(Ci.nsICurrentCharsetListener);
+    gCharsetMenu.SetCurrentCharset(charset);
+    gPrevCharset = gLastBrowserCharset;
+    gLastBrowserCharset = charset;
+  }
 }
 
 /* Begin Page Style Functions */
 function getAllStyleSheets(frameset) {
   var styleSheetsArray = Array.slice(frameset.document.styleSheets);
   for (let i = 0; i < frameset.frames.length; i++) {
     let frameSheets = getAllStyleSheets(frameset.frames[i]);
     styleSheetsArray = styleSheetsArray.concat(frameSheets);
@@ -5383,38 +5349,32 @@ function setStyleDisabled(disabled) {
 var BrowserOffline = {
   /////////////////////////////////////////////////////////////////////////////
   // BrowserOffline Public Methods
   init: function ()
   {
     if (!this._uiElement)
       this._uiElement = document.getElementById("goOfflineMenuitem");
 
-    var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-    os.addObserver(this, "network:offline-status-changed", false);
-
-    var ioService = Components.classes["@mozilla.org/network/io-service;1"].
-      getService(Components.interfaces.nsIIOService2);
-
-    this._updateOfflineUI(ioService.offline);
+    Services.obs.addObserver(this, "network:offline-status-changed", false);
+
+    this._updateOfflineUI(Services.io.offline);
   },
 
   uninit: function ()
   {
     try {
-      var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-      os.removeObserver(this, "network:offline-status-changed");
+      Services.obs.removeObserver(this, "network:offline-status-changed");
     } catch (ex) {
     }
   },
 
   toggleOfflineStatus: function ()
   {
-    var ioService = Components.classes["@mozilla.org/network/io-service;1"].
-      getService(Components.interfaces.nsIIOService2);
+    var ioService = Services.io;
 
     // Stop automatic management of the offline status
     try {
       ioService.manageOfflineStatus = false;
     } catch (ex) {
     }
   
     if (!ioService.offline && !this._canGoOffline()) {
@@ -5438,29 +5398,27 @@ var BrowserOffline = {
 
     this._updateOfflineUI(aState == "offline");
   },
 
   /////////////////////////////////////////////////////////////////////////////
   // BrowserOffline Implementation Methods
   _canGoOffline: function ()
   {
-    var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
-    if (os) {
-      try {
-        var cancelGoOffline = Components.classes["@mozilla.org/supports-PRBool;1"].createInstance(Components.interfaces.nsISupportsPRBool);
-        os.notifyObservers(cancelGoOffline, "offline-requested", null);
-
-        // Something aborted the quit process.
-        if (cancelGoOffline.data)
-          return false;
-      }
-      catch (ex) {
-      }
-    }
+    try {
+      var cancelGoOffline = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelGoOffline, "offline-requested", null);
+
+      // Something aborted the quit process.
+      if (cancelGoOffline.data)
+        return false;
+    }
+    catch (ex) {
+    }
+
     return true;
   },
 
   _uiElement: null,
   _updateOfflineUI: function (aOffline)
   {
     var offlineLocked = gPrefService.prefIsLocked("network.online");
     if (offlineLocked)
@@ -5470,28 +5428,24 @@ var BrowserOffline = {
   }
 };
 
 var OfflineApps = {
   /////////////////////////////////////////////////////////////////////////////
   // OfflineApps Public Methods
   init: function ()
   {
-    var obs = Cc["@mozilla.org/observer-service;1"].
-              getService(Ci.nsIObserverService);
-    obs.addObserver(this, "dom-storage-warn-quota-exceeded", false);
-    obs.addObserver(this, "offline-cache-update-completed", false);
+    Services.obs.addObserver(this, "dom-storage-warn-quota-exceeded", false);
+    Services.obs.addObserver(this, "offline-cache-update-completed", false);
   },
 
   uninit: function ()
   {
-    var obs = Cc["@mozilla.org/observer-service;1"].
-              getService(Ci.nsIObserverService);
-    obs.removeObserver(this, "dom-storage-warn-quota-exceeded");
-    obs.removeObserver(this, "offline-cache-update-completed");
+    Services.obs.removeObserver(this, "dom-storage-warn-quota-exceeded");
+    Services.obs.removeObserver(this, "offline-cache-update-completed");
   },
 
   handleEvent: function(event) {
     if (event.type == "MozApplicationManifest") {
       this.offlineAppRequested(event.originalTarget.defaultView);
     }
   },
 
@@ -5578,55 +5532,47 @@ var OfflineApps = {
 
       notificationBox.appendNotification(message, "offline-app-usage",
                                          "chrome://browser/skin/Info.png",
                                          priority, buttons);
     }
 
     // Now that we've warned once, prevent the warning from showing up
     // again.
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
-    pm.add(aURI, "offline-app",
-           Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
+    Services.perms.add(aURI, "offline-app",
+                       Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
   },
 
   // XXX: duplicated in preferences/advanced.js
   _getOfflineAppUsage: function (host, groups)
   {
-    var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
-                       getService(Components.interfaces.nsIApplicationCacheService);
+    var cacheService = Cc["@mozilla.org/network/application-cache-service;1"].
+                       getService(Ci.nsIApplicationCacheService);
     if (!groups)
       groups = cacheService.getGroups();
 
-    var ios = Components.classes["@mozilla.org/network/io-service;1"].
-              getService(Components.interfaces.nsIIOService);
-
     var usage = 0;
     for (var i = 0; i < groups.length; i++) {
-      var uri = ios.newURI(groups[i], null, null);
+      var uri = Services.io.newURI(groups[i], null, null);
       if (uri.asciiHost == host) {
         var cache = cacheService.getActiveCache(groups[i]);
         usage += cache.usage;
       }
     }
 
-    var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"].
-                         getService(Components.interfaces.nsIDOMStorageManager);
+    var storageManager = Cc["@mozilla.org/dom/storagemanager;1"].
+                         getService(Ci.nsIDOMStorageManager);
     usage += storageManager.getUsage(host);
 
     return usage;
   },
 
   _checkUsage: function(aURI) {
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
-
     // if the user has already allowed excessive usage, don't bother checking
-    if (pm.testExactPermission(aURI, "offline-app") !=
+    if (Services.perms.testExactPermission(aURI, "offline-app") !=
         Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN) {
       var usage = this._getOfflineAppUsage(aURI.asciiHost);
       var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
       if (usage >= warnQuota * 1024) {
         return true;
       }
     }
 
@@ -5638,22 +5584,19 @@ var OfflineApps = {
       return;
     }
 
     var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
     var browser = this._getBrowserForContentWindow(browserWindow,
                                                    aContentWindow);
 
     var currentURI = aContentWindow.document.documentURIObject;
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
 
     // don't bother showing UI if the user has already made a decision
-    if (pm.testExactPermission(currentURI, "offline-app") !=
-        Ci.nsIPermissionManager.UNKNOWN_ACTION)
+    if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
       return;
 
     try {
       if (gPrefService.getBoolPref("offline-apps.allow_by_default")) {
         // all pages can use offline capabilities, no need to ask the user
         return;
       }
     } catch(e) {
@@ -5697,32 +5640,26 @@ var OfflineApps = {
         notificationBox.appendNotification(message, notificationID,
                                            "chrome://browser/skin/Info.png",
                                            priority, buttons);
       notification.documents = [ aContentWindow.document ];
     }
   },
 
   allowSite: function(aDocument) {
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
-    pm.add(aDocument.documentURIObject, "offline-app",
-           Ci.nsIPermissionManager.ALLOW_ACTION);
+    Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.ALLOW_ACTION);
 
     // When a site is enabled while loading, manifest resources will
     // start fetching immediately.  This one time we need to do it
     // ourselves.
     this._startFetching(aDocument);
   },
 
   disallowSite: function(aDocument) {
-    var pm = Cc["@mozilla.org/permissionmanager;1"].
-             getService(Ci.nsIPermissionManager);
-    pm.add(aDocument.documentURIObject, "offline-app",
-           Ci.nsIPermissionManager.DENY_ACTION);
+    Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.DENY_ACTION);
   },
 
   manage: function() {
     openAdvancedPreferences("networkTab");
   },
 
   _startFetching: function(aDocument) {
     if (!aDocument.documentElement)
@@ -5794,32 +5731,31 @@ function WindowIsClosing()
  */
 function warnAboutClosingWindow() {
   // Popups aren't considered full browser windows.
   if (!toolbar.visible)
     return gBrowser.warnAboutClosingTabs(true);
 
   // Figure out if there's at least one other browser window around.
   let foundOtherBrowserWindow = false;
-  let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
-  let e = wm.getEnumerator("navigator:browser");
+  let e = Services.wm.getEnumerator("navigator:browser");
   while (e.hasMoreElements() && !foundOtherBrowserWindow) {
     let win = e.getNext();
     if (win != window && win.toolbar.visible)
       foundOtherBrowserWindow = true;
   }
   if (foundOtherBrowserWindow)
     return gBrowser.warnAboutClosingTabs(true);
 
-  let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+  let os = Services.obs;
 
   let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
                         createInstance(Ci.nsISupportsPRBool);
   os.notifyObservers(closingCanceled,
-                                   "browser-lastwindow-close-requested", null);
+                     "browser-lastwindow-close-requested", null);
   if (closingCanceled.data)
     return false;
 
   os.notifyObservers(null, "browser-lastwindow-close-granted", null);
 
 #ifdef XP_MACOSX
   // OS X doesn't quit the application when the last window is closed, but keeps
   // the session alive. Hence don't prompt users to save tabs, but warn about
@@ -5850,46 +5786,42 @@ var MailIntegration = {
     this._launchExternalUrl(uri);
   },
 
   // a generic method which can be used to pass arbitrary urls to the operating
   // system.
   // aURL --> a nsIURI which represents the url to launch
   _launchExternalUrl: function (aURL) {
     var extProtocolSvc =
-       Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
-                 .getService(Components.interfaces.nsIExternalProtocolService);
+       Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+         .getService(Ci.nsIExternalProtocolService);
     if (extProtocolSvc)
       extProtocolSvc.loadUrl(aURL);
   }
 };
 
-function BrowserOpenAddonsMgr(aPane)
-{
+function BrowserOpenAddonsMgr(aPane) {
   const EMTYPE = "Extension:Manager";
-  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                     .getService(Components.interfaces.nsIWindowMediator);
-  var theEM = wm.getMostRecentWindow(EMTYPE);
+  var theEM = Services.wm.getMostRecentWindow(EMTYPE);
   if (theEM) {
     theEM.focus();
     if (aPane)
       theEM.showView(aPane);
     return;
   }
 
   const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
   const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable";
   if (aPane)
     window.openDialog(EMURL, "", EMFEATURES, aPane);
   else
     window.openDialog(EMURL, "", EMFEATURES);
 }
 
-function AddKeywordForSearchField()
-{
+function AddKeywordForSearchField() {
   var node = document.popupNode;
 
   var charset = node.ownerDocument.characterSet;
 
   var docURI = makeURI(node.ownerDocument.URL,
                        charset);
 
   var formURI = makeURI(node.form.getAttribute("action"),
@@ -6192,22 +6124,22 @@ var gMissingPluginInstaller = {
   pluginCrashed : function(subject, topic, data) {
     let propertyBag = subject;
     if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
         !(propertyBag instanceof Ci.nsIWritablePropertyBag2))
      return;
 
 #ifdef MOZ_CRASHREPORTER
     let minidumpID = subject.getPropertyAsAString("minidumpID");
-    let submitReports = gCrashReporter.submitReports;
+    let submitted = gCrashReporter.submitReports && minidumpID.length;
     // The crash reporter wants a DOM element it can append an IFRAME to,
     // which it uses to submit a form. Let's just give it gBrowser.
-    if (submitReports)
-      gMissingPluginInstaller.CrashSubmit.submit(minidumpID, gBrowser, null, null);
-    propertyBag.setPropertyAsBool("submittedCrashReport", submitReports);
+    if (submitted)
+      submitted = gMissingPluginInstaller.CrashSubmit.submit(minidumpID, gBrowser, null, null);
+    propertyBag.setPropertyAsBool("submittedCrashReport", submitted);
 #endif
   },
 
   pluginInstanceCrashed: function (aEvent) {
     // Evil content could fire a fake event at us, ignore them.
     if (!aEvent.isTrusted)
       return;
 
@@ -6234,22 +6166,36 @@ var gMissingPluginInstaller = {
     let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
 
     // The binding has role="link" here, since missing/disabled/blocked
     // plugin UI has a onclick handler on the whole thing. This isn't needed
     // for the plugin-crashed UI, because we use actual HTML links in the text.
     overlay.removeAttribute("role");
 
 #ifdef MOZ_CRASHREPORTER
-    let helpClass = submittedReport ? "submitLink" : "notSubmitLink";
-    let helpLink = doc.getAnonymousElementByAttribute(plugin, "class", helpClass);
-    helpLink.href = gMissingPluginInstaller.crashReportHelpURL;
-    let showClass = submittedReport ? "msg msgSubmitted" : "msg msgNotSubmitted";
-    let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", showClass);
-    textToShow.style.display = "block";
+    let helpClass, showClass;
+
+    // If we didn't submit a report but don't have submission disabled,
+    // we probably just didn't collect a crash report; don't put up any 
+    // special crashing text.
+    if (submittedReport) {
+      helpClass = "submitLink";
+      showClass = "msg msgSubmitted";
+    }
+    else if (!gCrashReporter.submitReports) {
+      helpClass = "notSubmitLink";
+      showClass = "msg msgNotSubmitted";
+    }
+
+    if (helpClass) {
+      let helpLink = doc.getAnonymousElementByAttribute(plugin, "class", helpClass);
+      helpLink.href = gMissingPluginInstaller.crashReportHelpURL;
+      let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", showClass);
+      textToShow.style.display = "block";
+    }
 #endif
 
     let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed");
     crashText.textContent = messageString;
 
     let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
     link.addEventListener("click", function(e) { if (e.isTrusted) browser.reload(); }, true);
 
@@ -7132,37 +7078,34 @@ function getNotificationBox(aWindow) {
   return null;
 };
 
 /* DEPRECATED */
 function getBrowser() gBrowser;
 function getNavToolbox() gNavToolbox;
 
 let gPrivateBrowsingUI = {
-  _observerService: null,
   _privateBrowsingService: null,
   _searchBarValue: null,
   _findBarValue: null,
 
   init: function PBUI_init() {
-    this._observerService = Cc["@mozilla.org/observer-service;1"].
-                            getService(Ci.nsIObserverService);
-    this._observerService.addObserver(this, "private-browsing", false);
-    this._observerService.addObserver(this, "private-browsing-transition-complete", false);
+    Services.obs.addObserver(this, "private-browsing", false);
+    Services.obs.addObserver(this, "private-browsing-transition-complete", false);
 
     this._privateBrowsingService = Cc["@mozilla.org/privatebrowsing;1"].
                                    getService(Ci.nsIPrivateBrowsingService);
 
     if (this.privateBrowsingEnabled)
       this.onEnterPrivateBrowsing(true);
   },
 
   uninit: function PBUI_unint() {
-    this._observerService.removeObserver(this, "private-browsing");
-    this._observerService.removeObserver(this, "private-browsing-transition-complete");
+    Services.obs.removeObserver(this, "private-browsing");
+    Services.obs.removeObserver(this, "private-browsing-transition-complete");
   },
 
   get _disableUIOnToggle PBUI__disableUIOnTogle() {
     if (this._privateBrowsingService.autoStarted)
       return false;
 
     try {
       return !gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session");
@@ -7211,32 +7154,31 @@ let gPrivateBrowsingUI = {
     var dialogTitle = pbBundle.GetStringFromName("privateBrowsingMessageHeader");
     var header = "";
 #else
     var dialogTitle = pbBundle.GetStringFromName("privateBrowsingDialogTitle");
     var header = pbBundle.GetStringFromName("privateBrowsingMessageHeader") + "\n\n";
 #endif
     var message = pbBundle.formatStringFromName("privateBrowsingMessage", [appName], 1);
 
-    var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
-                        getService(Ci.nsIPromptService);
-
-    var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
-                promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
-                promptService.BUTTON_POS_0_DEFAULT;
+    var ps = Services.prompt;
+
+    var flags = ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
+                ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1 +
+                ps.BUTTON_POS_0_DEFAULT;
 
     var neverAsk = {value:false};
     var button0Title = pbBundle.GetStringFromName("privateBrowsingYesTitle");
     var button1Title = pbBundle.GetStringFromName("privateBrowsingNoTitle");
     var neverAskText = pbBundle.GetStringFromName("privateBrowsingNeverAsk");
 
     var result;
-    var choice = promptService.confirmEx(null, dialogTitle, header + message,
-                               flags, button0Title, button1Title, null,
-                               neverAskText, neverAsk);
+    var choice = ps.confirmEx(null, dialogTitle, header + message,
+                              flags, button0Title, button1Title, null,
+                              neverAskText, neverAsk);
 
     switch (choice) {
     case 0: // Start Private Browsing
       result = true;
       if (neverAsk.value)
         gPrefService.setBoolPref("browser.privatebrowsing.dont_prompt_on_enter", true);
       break;
     case 1: // Keep
@@ -7511,17 +7453,17 @@ var LightWeightThemeWebInstaller = {
     this._previewWindow.removeEventListener("pagehide", this, true);
     this._previewWindow = null;
     gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
 
     this._manager.resetPreview();
   },
 
   _isAllowed: function (node) {
-    var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+    var pm = Services.perms;
 
     var prefs = [["xpinstall.whitelist.add", pm.ALLOW_ACTION],
                  ["xpinstall.whitelist.add.36", pm.ALLOW_ACTION],
                  ["xpinstall.blacklist.add", pm.DENY_ACTION]];
     prefs.forEach(function ([pref, permission]) {
       try {
         var hosts = gPrefService.getCharPref(pref);
       } catch (e) {}
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -96,16 +96,20 @@
 
 #ifdef MOZ_SAFE_BROWSING
 <script type="application/javascript" src="chrome://browser/content/safebrowsing/sb-loader.js"/>
 #endif
 <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
 
 <script type="application/javascript" src="chrome://browser/content/places/editBookmarkOverlay.js"/>
 
+<!-- This is still used for dragDropSecurityCheck and transferUtils.  Since we
+     rely on the new Drag and Drop API these dependencies should be removed. -->
+<script type="application/javascript" src="chrome://global/content/nsDragAndDrop.js"/>
+
 # All sets except for popupsets (commands, keys, stringbundles and broadcasters) *must* go into the 
 # browser-sets.inc file for sharing with hiddenWindow.xul.
 #include browser-sets.inc
 
   <popupset id="mainPopupSet">
     <menupopup id="backForwardMenu"
                onpopupshowing="return FillHistoryMenu(event.target);"
                oncommand="gotoHistoryIndex(event);"
@@ -326,20 +330,30 @@
              context="toolbar-context-menu">
       <toolbaritem id="menubar-items" align="center">
 # The entire main menubar is placed into browser-menubar.inc, so that it can be shared by 
 # hiddenWindow.xul.
 #include browser-menubar.inc
       </toolbaritem>
     </toolbar>
 
-    <toolbarpalette id="BrowserToolbarPalette">
+    <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
+             toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
+             fullscreentoolbar="true" mode="icons" customizable="true"
+#ifdef WINCE
+             iconsize="small" defaulticonsize="small"
+             defaultset="unified-back-forward-button,reload-button,stop-button,home-button,urlbar-container,search-container,navigator-throbber,fullscreenflex,window-controls"
+#else
+             iconsize="large"
+             defaultset="unified-back-forward-button,reload-button,stop-button,home-button,urlbar-container,search-container,fullscreenflex,window-controls"
+#endif
+             context="toolbar-context-menu">
 
       <toolbaritem id="unified-back-forward-button" class="chromeclass-toolbar-additional"
-                   context="backForwardMenu">
+                   context="backForwardMenu" removable="true">
         <toolbarbutton id="back-button" class="toolbarbutton-1"
                        label="&backCmd.label;"
                        command="Browser:BackOrBackDuplicate"
                        onclick="checkForMiddleClick(this, event);"
                        tooltiptext="&backButton.tooltip;"/>
         <toolbarbutton id="forward-button" class="toolbarbutton-1"
                        label="&forwardCmd.label;"
                        command="Browser:ForwardOrForwardDuplicate"
@@ -358,37 +372,37 @@
                      oncommand="gotoHistoryIndex(event); event.stopPropagation();"
                      onclick="checkForMiddleClick(this, event);"/>
           <observes element="Browser:Back" attribute="disabled"/>
           <observes element="Browser:Forward" attribute="disabled"/>
         </toolbarbutton>
       </toolbaritem>
 
       <toolbarbutton id="reload-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     label="&reloadCmd.label;"
+                     label="&reloadCmd.label;" removable="true"
                      command="Browser:ReloadOrDuplicate"
                      onclick="checkForMiddleClick(this, event);"
                      tooltiptext="&reloadButton.tooltip;"/>
 
       <toolbarbutton id="stop-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     label="&stopCmd.label;"
+                     label="&stopCmd.label;" removable="true"
                      command="Browser:Stop"
                      tooltiptext="&stopButton.tooltip;"/>
 
       <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     persist="class"
+                     persist="class" removable="true"
                      label="&homeButton.label;"
                      ondragover="homeButtonObserver.onDragOver(event)"
                      ondragenter="homeButtonObserver.onDragOver(event)"
                      ondrop="homeButtonObserver.onDrop(event)"
                      ondragleave="homeButtonObserver.onDragLeave(event)"
                      onclick="BrowserGoHome(event);"/>
 
       <toolbaritem id="urlbar-container" align="center" flex="400" persist="width"
-                   title="&locationItem.title;" class="chromeclass-location">
+                   title="&locationItem.title;" class="chromeclass-location" removable="true">
         <textbox id="urlbar" flex="1"
                  bookmarkhistoryemptytext="&urlbar.bookmarkhistory.emptyText;"
                  bookmarkemptytext="&urlbar.bookmark.emptyText;"
                  historyemptytext="&urlbar.history.emptyText;"
                  noneemptytext="&urlbar.none.emptyText;"
                  type="autocomplete"
                  autocompletesearch="history"
                  autocompletepopup="PopupAutoCompleteRichResult"
@@ -451,115 +465,20 @@
                    tooltiptext="&goEndCap.tooltip;"
                    onclick="gURLBar.handleCommand(event);"/>
           </hbox>
         </textbox>
       </toolbaritem>
 
       <toolbaritem id="search-container" title="&searchItem.title;"
                    align="center" class="chromeclass-toolbar-additional"
-                   flex="100" persist="width">
+                   flex="100" persist="width" removable="true">
         <searchbar id="searchbar" flex="1"/>
       </toolbaritem>
 
-      <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                     label="&printButton.label;" command="cmd_print"
-                     tooltiptext="&printButton.tooltip;"/>
-
-      <toolbaritem id="navigator-throbber" title="&throbberItem.title;" align="center" pack="center"
-                   mousethrough="always">
-        <image/>
-      </toolbaritem>
-
-      <toolbaritem flex="1" id="personal-bookmarks" title="&bookmarksItem.title;">
-         <hbox id="bookmarksBarContent" flex="1"
-               type="places"
-               place="place:folder=TOOLBAR"
-               context="placesContext"
-               onclick="BookmarksEventHandler.onClick(event);"
-               oncommand="BookmarksEventHandler.onCommand(event);"
-               onpopupshowing="BookmarksEventHandler.onPopupShowing(event);"
-               tooltip="bhTooltip" popupsinherittooltip="true"/>
-      </toolbaritem>
-
-        <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       observes="Tools:Downloads"
-                       ondrop="DownloadsButtonDNDObserver.onDrop(event)"
-                       ondragover="DownloadsButtonDNDObserver.onDragOver(event)"
-                       ondragenter="DownloadsButtonDNDObserver.onDragOver(event)"
-                       ondragleave="DownloadsButtonDNDObserver.onDragLeave(event)"
-                       label="&downloads.label;"
-                       tooltiptext="&downloads.tooltip;"/>
-
-        <toolbarbutton id="history-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       observes="viewHistorySidebar" label="&historyButton.label;"
-                       tooltiptext="&historyButton.tooltip;"/>
-
-        <toolbarbutton id="bookmarks-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       observes="viewBookmarksSidebar"
-                       tooltiptext="&bookmarksButton.tooltip;"
-                       ondrop="bookmarksButtonObserver.onDrop(event)"
-                       ondragover="bookmarksButtonObserver.onDragOver(event)"
-                       ondragenter="bookmarksButtonObserver.onDragOver(event)"
-                       ondragleave="bookmarksButtonObserver.onDragLeave(event)"/>
-
-        <toolbarbutton id="new-tab-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       label="&tabCmd.label;"
-                       command="cmd_newNavigatorTab"
-                       tooltiptext="&newTabButton.tooltip;"
-                       ondrop="newTabButtonObserver.onDrop(event)"
-                       ondragover="newTabButtonObserver.onDragOver(event)"
-                       ondragenter="newTabButtonObserver.onDragOver(event)"
-                       ondragleave="newTabButtonObserver.onDragLeave(event)"/>
-
-        <toolbarbutton id="new-window-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       label="&newNavigatorCmd.label;"
-                       command="key_newNavigator"
-                       tooltiptext="&newWindowButton.tooltip;"
-                       ondrop="newWindowButtonObserver.onDrop(event)"
-                       ondragover="newWindowButtonObserver.onDragOver(event)"
-                       ondragenter="newWindowButtonObserver.onDragOver(event)"
-                       ondragleave="newWindowButtonObserver.onDragLeave(event)"/>
-
-        <toolbarbutton id="cut-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       label="&cutCmd.label;"
-                       command="cmd_cut"
-                       tooltiptext="&cutButton.tooltip;"/>
-
-        <toolbarbutton id="copy-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       label="&copyCmd.label;"
-                       command="cmd_copy"
-                       tooltiptext="&copyButton.tooltip;"/>
-
-        <toolbarbutton id="paste-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       label="&pasteCmd.label;"
-                       command="cmd_paste"
-                       tooltiptext="&pasteButton.tooltip;"/>
-        <toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                       observes="View:FullScreen"
-                       type="checkbox"
-                       label="&fullScreenCmd.label;"
-                       tooltiptext="&fullScreenButton.tooltip;"/>
-    </toolbarpalette>
-
-    <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
-             toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
-             fullscreentoolbar="true" mode="icons"
-#ifdef WINCE
-             iconsize="small" defaulticonsize="small"
-#else
-             iconsize="large"
-#endif
-             customizable="true"
-#ifdef WINCE
-             defaultset="unified-back-forward-button,reload-button,stop-button,home-button,urlbar-container,search-container,navigator-throbber,fullscreenflex,window-controls"
-#else
-             defaultset="unified-back-forward-button,reload-button,stop-button,home-button,urlbar-container,search-container,fullscreenflex,window-controls"
-#endif
-             context="toolbar-context-menu">
       <hbox id="fullscreenflex" flex="1" hidden="true" fullscreencontrol="true"/>
       <hbox id="window-controls" hidden="true" fullscreencontrol="true">
         <toolbarbutton id="minimize-button"
                        tooltiptext="&fullScreenMinimize.tooltip;"
                        oncommand="window.minimize();"/>
 
         <toolbarbutton id="restore-button"
                        tooltiptext="&fullScreenRestore.tooltip;"
@@ -578,17 +497,99 @@
              lockiconsize="true"
              class="chromeclass-directories"
              context="toolbar-context-menu"
              defaultset="personal-bookmarks"
              toolbarname="&personalbarCmd.label;" accesskey="&personalbarCmd.accesskey;"
 #ifdef WINCE
              collapsed="true"
 #endif
-             customizable="true"/>
+             customizable="true">
+      <toolbaritem flex="1" id="personal-bookmarks" title="&bookmarksItem.title;">
+         <hbox id="bookmarksBarContent" flex="1"
+               type="places"
+               place="place:folder=TOOLBAR"
+               context="placesContext"
+               onclick="BookmarksEventHandler.onClick(event);"
+               oncommand="BookmarksEventHandler.onCommand(event);"
+               onpopupshowing="BookmarksEventHandler.onPopupShowing(event);"
+               tooltip="bhTooltip" popupsinherittooltip="true"/>
+      </toolbaritem>
+    </toolbar>
+
+    <toolbarpalette id="BrowserToolbarPalette">
+
+      <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&printButton.label;" command="cmd_print"
+                     tooltiptext="&printButton.tooltip;"/>
+
+      <toolbaritem id="navigator-throbber" title="&throbberItem.title;" align="center" pack="center"
+                   mousethrough="always">
+        <image/>
+      </toolbaritem>
+
+      <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     observes="Tools:Downloads"
+                     ondrop="DownloadsButtonDNDObserver.onDrop(event)"
+                     ondragover="DownloadsButtonDNDObserver.onDragOver(event)"
+                     ondragenter="DownloadsButtonDNDObserver.onDragOver(event)"
+                     ondragleave="DownloadsButtonDNDObserver.onDragLeave(event)"
+                     label="&downloads.label;"
+                     tooltiptext="&downloads.tooltip;"/>
+
+      <toolbarbutton id="history-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     observes="viewHistorySidebar" label="&historyButton.label;"
+                     tooltiptext="&historyButton.tooltip;"/>
+
+      <toolbarbutton id="bookmarks-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     observes="viewBookmarksSidebar"
+                     tooltiptext="&bookmarksButton.tooltip;"
+                     ondrop="bookmarksButtonObserver.onDrop(event)"
+                     ondragover="bookmarksButtonObserver.onDragOver(event)"
+                     ondragenter="bookmarksButtonObserver.onDragOver(event)"
+                     ondragleave="bookmarksButtonObserver.onDragLeave(event)"/>
+
+      <toolbarbutton id="new-tab-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&tabCmd.label;"
+                     command="cmd_newNavigatorTab"
+                     tooltiptext="&newTabButton.tooltip;"
+                     ondrop="newTabButtonObserver.onDrop(event)"
+                     ondragover="newTabButtonObserver.onDragOver(event)"
+                     ondragenter="newTabButtonObserver.onDragOver(event)"
+                     ondragleave="newTabButtonObserver.onDragLeave(event)"/>
+
+      <toolbarbutton id="new-window-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&newNavigatorCmd.label;"
+                     command="key_newNavigator"
+                     tooltiptext="&newWindowButton.tooltip;"
+                     ondrop="newWindowButtonObserver.onDrop(event)"
+                     ondragover="newWindowButtonObserver.onDragOver(event)"
+                     ondragenter="newWindowButtonObserver.onDragOver(event)"
+                     ondragleave="newWindowButtonObserver.onDragLeave(event)"/>
+
+      <toolbarbutton id="cut-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&cutCmd.label;"
+                     command="cmd_cut"
+                     tooltiptext="&cutButton.tooltip;"/>
+
+      <toolbarbutton id="copy-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&copyCmd.label;"
+                     command="cmd_copy"
+                     tooltiptext="&copyButton.tooltip;"/>
+
+      <toolbarbutton id="paste-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&pasteCmd.label;"
+                     command="cmd_paste"
+                     tooltiptext="&pasteButton.tooltip;"/>
+      <toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     observes="View:FullScreen"
+                     type="checkbox"
+                     label="&fullScreenCmd.label;"
+                     tooltiptext="&fullScreenButton.tooltip;"/>
+    </toolbarpalette>
   </toolbox>
 
   <hbox flex="1" id="browser">
     <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
       <sidebarheader id="sidebar-header" align="center">
         <label id="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
         <image id="sidebar-throbber"/>
         <toolbarbutton class="tabs-closebutton" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="toggleSidebar();"/>
@@ -638,13 +639,17 @@
         </menupopup>
       </statusbarpanel>
     </statusbar>
   </vbox>
 #ifndef XP_UNIX
   <svg:svg height="0">
     <svg:mask id="winstripe-keyhole-forward-mask" maskContentUnits="objectBoundingBox">
       <svg:rect x="0" y="0" width="1" height="1" fill="white"/>
-      <svg:circle cx="-0.42" cy="0.5" r="0.63" id="circle"/>
+      <svg:circle cx="-0.46" cy="0.5" r="0.63" id="circle"/>
+    </svg:mask>
+    <svg:mask id="winstripe-keyhole-forward-mask-hover" maskContentUnits="objectBoundingBox">
+      <svg:rect x="0" y="0" width="1" height="1" fill="white"/>
+      <svg:circle cx="-0.35" cy="0.5" r="0.58" id="circle"/>
     </svg:mask>
   </svg:svg>
 #endif
 </window>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -61,17 +61,17 @@
   <binding id="tabbrowser">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
     <content>
       <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
       <xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
-                  onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();">
+                  onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
         <xul:hbox class="tab-drop-indicator-bar" collapsed="true"
                   ondragover="this.parentNode.parentNode._onDragOver(event);"
                   ondragleave="this.parentNode.parentNode._onDragLeave(event);"
                   ondrop="this.parentNode.parentNode._onDrop(event);">
           <xul:hbox class="tab-drop-indicator" mousethrough="always"/>
         </xul:hbox>
         <xul:hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child"
                   anonid="strip"
@@ -122,37 +122,33 @@
                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
                                      tabbrowser.removeTab(tabbrowser.mContextTab);"/>
           </xul:menupopup>
 
           <xul:tabs class="tabbrowser-tabs" flex="1"
                     anonid="tabcontainer"
                     setfocus="false"
                     onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
-                    ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);"
-                    onclosetab="var node = this.parentNode;
-                                while (node.localName != 'tabbrowser')
-                                  node = node.parentNode;
-                                node.removeCurrentTab();">
+                    ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);">
             <xul:tab selected="true" validate="never"
                      onerror="this.removeAttribute('image');"
                      maxwidth="250" width="0" minwidth="100" flex="100"
                      class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
           </xul:tabs>
         </xul:hbox>
         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
           <xul:notificationbox flex="1">
             <xul:browser flex="1" type="content-primary" message="true" disablehistory="true"
                          xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
           </xul:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
     </content>
-    <implementation>
+    <implementation implements="nsIDOMEventListener">
       <field name="mPrefs" readonly="true">
         Components.classes['@mozilla.org/preferences-service;1']
                   .getService(Components.interfaces.nsIPrefService)
                   .getBranch(null);
       </field>
       <field name="mURIFixup" readonly="true">
         Components.classes["@mozilla.org/docshell/urifixup;1"]
                   .getService(Components.interfaces.nsIURIFixup);
@@ -267,46 +263,50 @@
           ]]>
         </body>
       </method>
 
       <method name="getBrowserIndexForDocument">
         <parameter name="aDocument"/>
         <body>
           <![CDATA[
-            var browsers = this.browsers;
-            for (var i = 0; i < browsers.length; i++)
-              if (browsers[i].contentDocument == aDocument)
-                return i;
-            return -1;
+            var tab = this._getTabForContentWindow(aDocument.defaultView);
+            return tab ? tab._tPos : -1;
           ]]>
         </body>
       </method>
 
       <method name="getBrowserForDocument">
         <parameter name="aDocument"/>
         <body>
           <![CDATA[
-            var index = this.getBrowserIndexForDocument(aDocument);
-            if (index < 0)
-              return null;
-            return this.getBrowserAtIndex(index);
+            var tab = this._getTabForContentWindow(aDocument.defaultView);
+            return tab ? tab.linkedBrowser : null;
           ]]>
         </body>
       </method>
 
+      <method name="_getTabForContentWindow">
+        <parameter name="aWindow"/>
+        <body>
+        <![CDATA[
+          for (let i = 0; i < this.browsers.length; i++) {
+            if (this.browsers[i].contentWindow == aWindow)
+              return this.mTabs[i];
+          }
+          return null;
+        ]]>
+        </body>
+      </method>
+
       <method name="getNotificationBox">
         <parameter name="aBrowser"/>
         <body>
           <![CDATA[
-            if (aBrowser)
-              return aBrowser.parentNode;
-            else if (this.mCurrentBrowser)
-              return this.mCurrentBrowser.parentNode;
-            return null;
+            return (aBrowser || this.mCurrentBrowser).parentNode;
           ]]>
         </body>
       </method>
 
       <!-- A web progress listener object definition for a given tab. -->
       <method name="mTabProgressListener">
         <parameter name="aTab"/>
         <parameter name="aBrowser"/>
@@ -1013,34 +1013,16 @@
                 !this.mPrefs.getBoolPref("browser.tabs.closeWindowWithLastTab"))
               this.removeTab(event.target);
 
             event.stopPropagation();
           ]]>
         </body>
       </method>
 
-      <method name="onTitleChanged">
-        <parameter name="evt"/>
-        <body>
-          <![CDATA[
-            if (evt.target != this.contentDocument)
-              return;
-
-            var tabBrowser = this.parentNode.parentNode.parentNode.parentNode;
-
-            var tab = document.getAnonymousElementByAttribute(tabBrowser, "linkedpanel", this.parentNode.id);
-            tabBrowser.setTabTitle(tab);
-
-            if (tab == tabBrowser.mCurrentTab)
-              tabBrowser.updateTitlebar();
-          ]]>
-        </body>
-      </method>
-
       <method name="setTabTitleLoading">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             aTab.label = this.mStringBundle.getString("tabs.loading");
             aTab.setAttribute("crop", "end");
             this._tabAttrModified(aTab);
           ]]>
@@ -1115,26 +1097,22 @@
         </body>
       </method>
 
       <method name="enterTabbedMode">
         <body>
           <![CDATA[
             this.mTabbedMode = true; // Welcome to multi-tabbed mode.
 
-            // Get the first tab all hooked up with a title listener and popup blocking listener.
-            this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
             if (XULBrowserWindow.isBusy) {
               this.mCurrentTab.setAttribute("busy", "true");
               this.mIsBusy = true;
               this.setTabTitleLoading(this.mCurrentTab);
               this.updateIcon(this.mCurrentTab);
             } else {
-              this.setTabTitle(this.mCurrentTab);
               this.setIcon(this.mCurrentTab, this.mCurrentBrowser.mIconURL);
             }
 
             var filter;
             if (this.mTabFilters.length > 0) {
               // Use the filter hooked up in our addProgressListener
               filter = this.mTabFilters[0];
             } else {
@@ -1337,18 +1315,16 @@
             var notificationbox = document.createElementNS(
                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                     "notificationbox");
             notificationbox.setAttribute("flex", "1");
             notificationbox.appendChild(b);
             b.setAttribute("flex", "1");
             this.mPanelContainer.appendChild(notificationbox);
 
-            b.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
             if (this.mStrip.collapsed &&
                 this.mTabs.length - this._removingTabs.length > 1)
               this.setStripVisibilityTo(true);
 
             // wire up a progress listener for the new browser object.
             var position = this.mTabContainer.childNodes.length-1;
             var tabListener = this.mTabProgressListener(t, b, blank);
             const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
@@ -1578,19 +1554,16 @@
             aTab.dispatchEvent(evt);
 
             // Remove the tab's filter and progress listener.
             const filter = this.mTabFilters[aTab._tPos];
             browser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
             this.mTabListeners[aTab._tPos].destroy();
 
-            // Remove our title change and blocking listeners
-            browser.removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
             // We are no longer the primary content area.
             browser.setAttribute("type", "content-targetable");
 
             // Remove this tab as the owner of any other tabs, since it's going away.
             Array.forEach(this.mTabs, function (tab) {
               if ("owner" in tab && tab.owner == aTab)
                 // |tab| is a child of the tab we're removing, make it an orphan
                 tab.owner = null;
@@ -2685,87 +2658,85 @@
         <parameter name="aUri"/>
         <body>
           <![CDATA[
             nsDragAndDrop.dragDropSecurityCheck(aEvent, aDragSession, aUri);
           ]]>
         </body>
       </method>
 
-      <field name="_keyEventHandler" readonly="true">
-      <![CDATA[({
-        tabbrowser: this,
-        handleEvent: function handleEvent(aEvent) {
+      <method name="_handleKeyEvent">
+        <parameter name="aEvent"/>
+        <body><![CDATA[
           if (!aEvent.isTrusted) {
             // Don't let untrusted events mess with tabs.
             return;
           }
 
           if ('altKey' in aEvent && aEvent.altKey)
             return;
 #ifdef XP_MACOSX
           if ('metaKey' in aEvent && aEvent.metaKey) {
             var offset = 1;
             switch (aEvent.charCode) {
               case '}'.charCodeAt(0):
                 offset *= -1;
               case '{'.charCodeAt(0):
-                if (window.getComputedStyle(this.tabbrowser, null).direction == "ltr")
+                if (window.getComputedStyle(this, null).direction == "ltr")
                   offset *= -1;
 
-                this.tabbrowser.mTabContainer.advanceSelectedTab(offset, true);
+                this.mTabContainer.advanceSelectedTab(offset, true);
                 aEvent.stopPropagation();
                 aEvent.preventDefault();
                 return;
             }
             if ('shiftKey' in aEvent && aEvent.shiftKey)
               return;
 #else
           if (('ctrlKey' in aEvent && aEvent.ctrlKey) &&
               !('shiftKey' in aEvent && aEvent.shiftKey) &&
               !('metaKey' in aEvent && aEvent.metaKey)) {
             if (aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
-                this.tabbrowser.mTabBox.handleCtrlPageUpDown) {
-              this.tabbrowser.removeCurrentTab();
+                this.mTabBox.handleCtrlPageUpDown) {
+              this.removeCurrentTab();
 
               aEvent.stopPropagation();
               aEvent.preventDefault();
               return;
             }
 #endif
-            if (aEvent.target.localName == "tabbrowser") {
+            if (aEvent.target == this) {
               switch (aEvent.keyCode) {
                 case KeyEvent.DOM_VK_UP:
-                  this.tabbrowser.moveTabBackward();
+                  this.moveTabBackward();
                   break;
                 case KeyEvent.DOM_VK_DOWN:
-                  this.tabbrowser.moveTabForward();
+                  this.moveTabForward();
                   break;
                 case KeyEvent.DOM_VK_RIGHT:
                 case KeyEvent.DOM_VK_LEFT:
-                  this.tabbrowser.moveTabOver(aEvent);
+                  this.moveTabOver(aEvent);
                   break;
                 case KeyEvent.DOM_VK_HOME:
-                  this.tabbrowser.moveTabToStart();
+                  this.moveTabToStart();
                   break;
                 case KeyEvent.DOM_VK_END:
-                  this.tabbrowser.moveTabToEnd();
+                  this.moveTabToEnd();
                   break;
                 default:
                   // Stop the keypress event for the above keyboard
                   // shortcuts only.
                   return;
               }
               aEvent.stopPropagation();
               aEvent.preventDefault();
             }
           }
-        }
-      })]]>
-      </field>
+        ]]></body>
+      </method>
 
       <property name="userTypedClear"
                 onget="return this.mCurrentBrowser.userTypedClear;"
                 onset="return this.mCurrentBrowser.userTypedClear = val;"/>
 
       <property name="userTypedValue"
                 onget="return this.mCurrentBrowser.userTypedValue;"
                 onset="return this.mCurrentBrowser.userTypedValue = val;"/>
@@ -2786,21 +2757,32 @@
               event.target.setAttribute("label", tn.getAttribute("label"));
               return true;
             }
             return false;
           ]]>
         </body>
       </method>
 
+      <method name="handleEvent">
+        <parameter name="aEvent"/>
+        <body><![CDATA[
+          switch (aEvent.type) {
+            case "keypress":
+              this._handleKeyEvent(aEvent);
+              break;
+          }
+        ]]></body>
+      </method>
+
       <constructor>
         <![CDATA[
           this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild;
           this.mCurrentTab = this.mTabContainer.firstChild;
-          document.addEventListener("keypress", this._keyEventHandler, false);
+          document.addEventListener("keypress", this, false);
 
           var uniqueId = "panel" + Date.now();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mTabContainer.childNodes[0].linkedPanel = uniqueId;
           this.mTabContainer.childNodes[0]._tPos = 0;
           this.mTabContainer.childNodes[0].linkedBrowser = this.mPanelContainer.childNodes[0].firstChild;
 
           // set up the shared autoscroll popup
@@ -2814,70 +2796,61 @@
       <destructor>
         <![CDATA[
           for (var i = 0; i < this.mTabListeners.length; ++i) {
             this.getBrowserAtIndex(i).webProgress.removeProgressListener(this.mTabFilters[i]);
             this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
             this.mTabFilters[i] = null;
             this.mTabListeners[i].destroy();
             this.mTabListeners[i] = null;
-            this.getBrowserAtIndex(i).removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
           }
-          document.removeEventListener("keypress", this._keyEventHandler, false);
+          document.removeEventListener("keypress", this, false);
         ]]>
       </destructor>
     </implementation>
 
     <handlers>
       <handler event="DOMWindowClose" phase="capturing">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
-          const browsers = this.mPanelContainer.childNodes;
-          if (browsers.length == 1) {
-            // There's only one browser left. If a window is being
-            // closed and the window is *not* the window in the
-            // browser that's still around, prevent the event's default
-            // action to prevent closing a window that's being closed
-            // already.
-            if (this.getBrowserAtIndex(0).contentWindow != event.target)
-              event.preventDefault();
-
+          if (this.mTabs.length == 1)
             return;
-          }
-
-          var i = 0;
-          for (; i < browsers.length; ++i) {
-            if (this.getBrowserAtIndex(i).contentWindow == event.target) {
-              this.removeTab(this.mTabContainer.childNodes[i]);
-              event.preventDefault();
-
-              break;
-            }
+
+          var tab = this._getTabForContentWindow(event.target);
+          if (tab) {
+            this.removeTab(tab);
+            event.preventDefault();
           }
         ]]>
       </handler>
       <handler event="DOMWillOpenModalDialog" phase="capturing">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
           // We're about to open a modal dialog, make sure the opening
           // tab is brought to the front.
-
-          var targetTop = event.target.top;
-
-          for (var i = 0; i < browsers.length; ++i) {
-            if (this.getBrowserAtIndex(i).contentWindow == targetTop) {
-              this.selectedTab = this.mTabContainer.childNodes[i];
-
-              break;
-            }
-          }
+          this.selectedTab = this._getTabForContentWindow(event.target.top);
+        ]]>
+      </handler>
+      <handler event="DOMTitleChanged">
+        <![CDATA[
+          if (!event.isTrusted)
+            return;
+
+          var contentWin = event.target.defaultView;
+          if (contentWin != contentWin.top)
+            return;
+
+          var tab = this._getTabForContentWindow(contentWin);
+          this.setTabTitle(tab);
+          if (tab == this.mCurrentTab)
+            this.updateTitlebar();
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
     <implementation>
       <!-- Override scrollbox.xml method, since our scrollbox's children are
--- a/browser/base/content/test/browser_bug329212.js
+++ b/browser/base/content/test/browser_bug329212.js
@@ -21,16 +21,18 @@ function test () {
     ok(!FillInHTMLTooltip(doc.getElementById("link2"), "should not get title"));
 
     ok(FillInHTMLTooltip(doc.getElementById("link3"), "should get title"));
     ok(tooltip.getAttribute("label") != "");
 
     ok(FillInHTMLTooltip(doc.getElementById("link4"), "should get title"));
     is(tooltip.getAttribute("label"), "This is an xlink:title attribute");
 
+    ok(!FillInHTMLTooltip(doc.getElementById("text5"), "should not get title"));
+
     gBrowser.removeCurrentTab();
     finish();
   }, true);
 
   content.location = 
     "http://localhost:8888/browser/browser/base/content/test/title_test.svg";
 }
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_utilityOverlay.js
@@ -0,0 +1,35 @@
+var gTestTab;
+
+function test() {
+  waitForExplicitFinish();
+
+  is(getTopWin(), window, "got top window");
+  is(getBoolPref("general.startup.browser", false), true, "getBoolPref");
+  is(getBoolPref("this.pref.doesnt.exist", true), true, "getBoolPref fallback");
+  is(getBoolPref("this.pref.doesnt.exist", false), false, "getBoolPref fallback #2");
+
+
+  gTestTab = openNewTabWith("http://example.com");
+  gBrowser.selectedTab = gTestTab;
+  gTestTab.linkedBrowser.addEventListener("load", function () {
+    gTestTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    is(gTestTab.linkedBrowser.currentURI.spec, "http://example.com/", "example.com loaded");
+
+    test_openUILink();
+  }, true);
+}
+
+function test_openUILink() {
+  gTestTab.linkedBrowser.addEventListener("load", function () {
+    gTestTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+
+    is(gTestTab.linkedBrowser.currentURI.spec, "http://example.org/", "example.org loaded");
+
+    gBrowser.removeTab(gTestTab);
+    finish();
+  }, true);
+
+  //openUILink(url, e, ignoreButton, ignoreAlt, allowKeywordFixup, postData, referrerUrl);
+  openUILink("http://example.org"); // defaults to "current"
+}
--- a/browser/base/content/test/title_test.svg
+++ b/browser/base/content/test/title_test.svg
@@ -1,9 +1,10 @@
 <svg width="640px" height="480px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0">
+  <title>This is a root SVG element's title</title>
   <text id="text1" x="10px" y="32px" font-size="24px">
     This contains only &lt;title&gt;
     <title>
 
 
     This            is a title
 
     </title>
@@ -37,9 +38,13 @@
       <title>This is a title</title>
     </text>
   </a>
   <a id="link4" xlink:href="#" xlink:title="This is an xlink:title attribute">
     <text x="10px" y="256px" font-size="24px">
       This link contains xlink:title attr.
     </text>
   </a>
+  <text id="text5" x="10px" y="160px" font-size="24px"
+        xlink:title="This is an xlink:title attribute but it isn't on a link" >
+    This contains nothing.
+  </text>
 </svg>
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -17,35 +17,34 @@
 # The Initial Developer of the Original Code is
 # Netscape Communications Corporation.
 # Portions created by the Initial Developer are Copyright (C) 1998
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Alec Flett <alecf@netscape.com>
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
+#   Gavin Sharp <gavin@gavinsharp.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
-/**
- * Communicator Shared Utility Library
- * for shared application glue for the Communicator suite of applications
- **/
+// Services = object with smart getters for common XPCOM services
+Components.utils.import("resource://gre/modules/Services.jsm");
 
 var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
 
 var gBidiUI = false;
 
 function getBrowserURL()
 {
   return "chrome://browser/content/browser.xul";
@@ -64,32 +63,28 @@ function goToggleToolbar( id, elementID 
       element.setAttribute("checked", isHidden ? "true" : "false");
       document.persist(elementID, 'checked');
     }
   }
 }
 
 function getTopWin()
 {
-  var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
-                                .getService(Components.interfaces.nsIWindowMediator);
-  return windowManager.getMostRecentWindow("navigator:browser");
+  return Services.wm.getMostRecentWindow("navigator:browser");
 }
 
 function openTopWin( url )
 {
   openUILink(url, {})
 }
 
-function getBoolPref ( prefname, def )
+function getBoolPref(prefname, def)
 {
-  try { 
-    var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                       .getService(Components.interfaces.nsIPrefBranch);
-    return pref.getBoolPref(prefname);
+  try {
+    return Services.prefs.getBoolPref(prefname);
   }
   catch(er) {
     return def;
   }
 }
 
 // openUILink handles clicks on UI elements that cause URLs to load.
 function openUILink( url, e, ignoreButton, ignoreAlt, allowKeywordFixup, postData, referrerUrl )
@@ -226,17 +221,17 @@ function openUILinkIn(url, where, aAllow
                   getBrowserURL(),
                   null,
                   "chrome,dialog=no,all",
                   sa);
 
     return;
   }
 
-  var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground", false);
+  var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground");
 
   switch (where) {
   case "current":
     w.loadURI(url, aReferrerURI, aPostData, aAllowThirdPartyFixup);
     break;
   case "tabshifted":
     loadInBackground = !loadInBackground;
     // fall through
@@ -370,50 +365,44 @@ function isBidiEnabled() {
 
     switch (systemLocale) {
       case "ar-":
       case "he-":
       case "fa-":
       case "ur-":
       case "syr":
         rv = true;
-        var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                             .getService(Components.interfaces.nsIPrefBranch);
-        pref.setBoolPref("bidi.browser.ui", true);
+        Services.prefs.setBoolPref("bidi.browser.ui", true);
     }
   } catch (e) {}
 
   return rv;
 }
 
 function openAboutDialog()
 {
 #ifdef XP_MACOSX
-  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                     .getService(Components.interfaces.nsIWindowMediator);
-  var win = wm.getMostRecentWindow("Browser:About");
+  var win = Services.wm.getMostRecentWindow("Browser:About");
   if (win)
     win.focus();
   else {
     window.openDialog("chrome://browser/content/aboutDialog.xul", "About",
                       "chrome, resizable=no, minimizable=no");
   }
 #else
   window.openDialog("chrome://browser/content/aboutDialog.xul", "About", "centerscreen,chrome,resizable=no");
 #endif
 }
 
 function openPreferences(paneID, extraArgs)
 {
   var instantApply = getBoolPref("browser.preferences.instantApply", false);
   var features = "chrome,titlebar,toolbar,centerscreen" + (instantApply ? ",dialog=no" : ",modal");
 
-  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                     .getService(Components.interfaces.nsIWindowMediator);
-  var win = wm.getMostRecentWindow("Browser:Preferences");
+  var win = Services.wm.getMostRecentWindow("Browser:Preferences");
   if (win) {
     win.focus();
     if (paneID) {
       var pane = win.document.getElementById(paneID);
       win.document.documentElement.showPane(pane);
     }
 
     if (extraArgs && extraArgs["advancedTab"]) {
@@ -582,27 +571,17 @@ function makeURLAbsolute(aBase, aUrl)
  *        There will be no security check.
  */ 
 function openNewTabWith(aURL, aDocument, aPostData, aEvent,
                         aAllowThirdPartyFixup, aReferrer)
 {
   if (aDocument)
     urlSecurityCheck(aURL, aDocument.nodePrincipal);
 
-  var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
-                          .getService(Components.interfaces.nsIPrefService);
-  prefSvc = prefSvc.getBranch(null);
-
-  // should we open it in a new tab?
-  var loadInBackground = true;
-  try {
-    loadInBackground = prefSvc.getBoolPref("browser.tabs.loadInBackground");
-  }
-  catch(ex) {
-  }
+  var loadInBackground = getBoolPref("browser.tabs.loadInBackground");
 
   if (aEvent && aEvent.shiftKey)
     loadInBackground = !loadInBackground;
 
   // As in openNewWindowWith(), we want to pass the charset of the
   // current document over to a new tab. 
   var wintype = document.documentElement.getAttribute("windowtype");
   var originCharset;
@@ -683,18 +662,15 @@ function openHelpLink(aHelpTopic, aCalle
                       .formatURLPref("app.support.baseURL");
   url += aHelpTopic;
 
   var where = aCalledFromModal ? "window" : "tab";
   openUILinkIn(url, where);
 }
 
 function openPrefsHelp() {
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-                        .getService(Components.interfaces.nsIPrefBranch2);
-
   // non-instant apply prefwindows are usually modal, so we can't open in the topmost window, 
   // since its probably behind the window.
-  var instantApply = prefs.getBoolPref("browser.preferences.instantApply");
+  var instantApply = getBoolPref("browser.preferences.instantApply");
 
   var helpTopic = document.getElementsByTagName("prefwindow")[0].currentPane.helpTopic;
   openHelpLink(helpTopic, !instantApply);
 }
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ b/browser/components/migration/src/nsIEProfileMigrator.cpp
@@ -1415,17 +1415,17 @@ nsIEProfileMigrator::CopyFavoritesBatche
     }
     folder = bookmarksMenuFolderId;
   }
 
   nsCOMPtr<nsIProperties> fileLocator =
     do_GetService("@mozilla.org/file/directory_service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIFile> favoritesDirectory;
-  (void)fileLocator->Get("Favs", NS_GET_IID(nsIFile),
+  (void)fileLocator->Get(NS_WIN_FAVORITES_DIR, NS_GET_IID(nsIFile),
                          getter_AddRefs(favoritesDirectory));
 
   // If |favoritesDirectory| is null, it means that we're on a Windows
   // platform that does not have a Favorites folder, e.g. Windows 95
   // (early SRs, before IE integrated with the shell).
   // Only try to read Favorites folder if it exists on the machine.
   if (favoritesDirectory) {
     rv = ParseFavoritesFolder(favoritesDirectory, folder, bms,
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -33,16 +33,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 // XXXmano: we should move most/all of these constants to PlacesUtils
 const ORGANIZER_ROOT_BOOKMARKS = "place:folder=BOOKMARKS_MENU&excludeItems=1&queryType=1";
 const ORGANIZER_SUBSCRIPTIONS_QUERY = "place:annotation=livemark%2FfeedURI";
 
 // No change to the view, preserve current selection
 const RELOAD_ACTION_NOTHING = 0;
 // Inserting items new to the view, select the inserted rows
 const RELOAD_ACTION_INSERT = 1;
@@ -1325,120 +1327,109 @@ PlacesController.prototype = {
 };
 
 /**
  * Handles drag and drop operations for views. Note that this is view agnostic!
  * You should not use PlacesController._view within these methods, since
  * the view that the item(s) have been dropped on was not necessarily active. 
  * Drop functions are passed the view that is being dropped on. 
  */
-var PlacesControllerDragHelper = {
+let PlacesControllerDragHelper = {
   /**
    * DOM Element currently being dragged over
    */
   currentDropTarget: null,
 
   /**
-   * Current nsIDOMDataTransfer
-   * We need to cache this because we don't have access to the event in the
-   * treeView's canDrop or drop methods, and session.dataTransfer would not be
-   * filled for drag and drop from external sources (eg. the OS).
-   */
-  currentDataTransfer: null,
-
-  /**
    * Determines if the mouse is currently being dragged over a child node of
    * this menu. This is necessary so that the menu doesn't close while the
    * mouse is dragging over one of its submenus
    * @param   node
    *          The container node
    * @returns true if the user is dragging over a node within the hierarchy of
    *          the container, false otherwise.
    */
   draggingOverChildNode: function PCDH_draggingOverChildNode(node) {
-    var currentNode = this.currentDropTarget;
+    let currentNode = this.currentDropTarget;
     while (currentNode) {
       if (currentNode == node)
         return true;
       currentNode = currentNode.parentNode;
     }
     return false;
   },
 
   /**
    * @returns The current active drag session. Returns null if there is none.
    */
   getSession: function PCDH__getSession() {
-    var dragService = Cc["@mozilla.org/widget/dragservice;1"].
-                      getService(Ci.nsIDragService);
-    return dragService.getCurrentSession();
+    return this.dragService.getCurrentSession();
   },
 
   /**
    * Extract the first accepted flavor from a flavors array.
    * @param aFlavors
    *        The flavors array.
    */
   getFirstValidFlavor: function PCDH_getFirstValidFlavor(aFlavors) {
-    for (var i = 0; i < aFlavors.length; i++) {
+    for (let i = 0; i < aFlavors.length; i++) {
       if (this.GENERIC_VIEW_DROP_TYPES.indexOf(aFlavors[i]) != -1)
         return aFlavors[i];
     }
     return null;
   },
 
   /**
    * Determines whether or not the data currently being dragged can be dropped
    * on a places view.
    * @param ip
-   *        The insertion point where the items should be dropped
+   *        The insertion point where the items should be dropped.
    */
-  canDrop: function PCDH_canDrop(ip) {
-    var dt = this.currentDataTransfer;
-    var dropCount = dt.mozItemCount;
+  canDrop: function PCDH_canDrop(ip, dt) {
+    let dropCount = dt.mozItemCount;
 
-    // Check every dragged item
-    for (var i = 0; i < dropCount; i++) {
-      var flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
+    // Check every dragged item.
+    for (let i = 0; i < dropCount; i++) {
+      let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
       if (!flavor)
         return false;
 
-      var data = dt.mozGetDataAt(flavor, i);
-
-      // urls can be dropped on any insertionpoint
-      // XXXmano: // Remember: this method is called for each dragover event!
+      // Urls can be dropped on any insertionpoint.
+      // XXXmano: remember that this method is called for each dragover event!
       // Thus we shouldn't use unwrapNodes here at all if possible.
       // I think it would be OK to accept bogus data here (e.g. text which was
       // somehow wrapped as TAB_DROP_TYPE, this is not in our control, and
       // will just case the actual drop to be a no-op), and only rule out valid
       // expected cases, which are either unsupported flavors, or items which
       // cannot be dropped in the current insertionpoint. The last case will
       // likely force us to use unwrapNodes for the private data types of
       // places.
       if (flavor == TAB_DROP_TYPE)
         continue;
 
+      let data = dt.mozGetDataAt(flavor, i);
+      let dragged;
       try {
-        var dragged = PlacesUtils.unwrapNodes(data, flavor)[0];
+        dragged = PlacesUtils.unwrapNodes(data, flavor)[0];
       } catch (e) {
         return false;
       }
 
-      // Only bookmarks and urls can be dropped into tag containers
+      // Only bookmarks and urls can be dropped into tag containers.
       if (ip.isTag && ip.orientation == Ci.nsITreeView.DROP_ON &&
           dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
           (dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
            /^place:/.test(dragged.uri)))
         return false;
 
       // The following loop disallows the dropping of a folder on itself or
       // on any of its descendants.
       if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER ||
           /^place:/.test(dragged.uri)) {
-        var parentId = ip.itemId;
+        let parentId = ip.itemId;
         while (parentId != PlacesUtils.placesRootId) {
           if (dragged.concreteId == parentId || dragged.id == parentId)
             return false;
           parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
         }
       }
     }
     return true;
@@ -1449,32 +1440,32 @@ var PlacesControllerDragHelper = {
    * Determines if a node can be moved.
    * 
    * @param   aNode
    *          A nsINavHistoryResultNode node.
    * @returns True if the node can be moved, false otherwise.
    */
   canMoveNode:
   function PCDH_canMoveNode(aNode) {
-    // can't move query root
+    // Can't move query root.
     if (!aNode.parent)
       return false;
 
-    var parentId = PlacesUtils.getConcreteItemId(aNode.parent);
-    var concreteId = PlacesUtils.getConcreteItemId(aNode);
+    let parentId = PlacesUtils.getConcreteItemId(aNode.parent);
+    let concreteId = PlacesUtils.getConcreteItemId(aNode);
 
-    // can't move children of tag containers
+    // Can't move children of tag containers.
     if (PlacesUtils.nodeIsTagQuery(aNode.parent))
       return false;
 
-    // can't move children of read-only containers
+    // Can't move children of read-only containers.
     if (PlacesUtils.nodeIsReadOnly(aNode.parent))
       return false;
 
-    // check for special folders, etc
+    // Check for special folders, etc.
     if (PlacesUtils.nodeIsContainer(aNode) &&
         !this.canMoveContainer(aNode.itemId, parentId))
       return false;
 
     return true;
   },
 
   /**
@@ -1486,134 +1477,127 @@ var PlacesControllerDragHelper = {
    *          The parent id of the folder.
    * @returns True if the container can be moved to the target.
    */
   canMoveContainer:
   function PCDH_canMoveContainer(aId, aParentId) {
     if (aId == -1)
       return false;
 
-    // Disallow moving of roots and special folders
+    // Disallow moving of roots and special folders.
     const ROOTS = [PlacesUtils.placesRootId, PlacesUtils.bookmarksMenuFolderId,
                    PlacesUtils.tagsFolderId, PlacesUtils.unfiledBookmarksFolderId,
                    PlacesUtils.toolbarFolderId];
     if (ROOTS.indexOf(aId) != -1)
       return false;
 
-    // Get parent id if necessary
+    // Get parent id if necessary.
     if (aParentId == null || aParentId == -1)
       aParentId = PlacesUtils.bookmarks.getFolderIdForItem(aId);
 
     if (PlacesUtils.bookmarks.getFolderReadonly(aParentId))
       return false;
 
     return true;
   },
 
   /**
    * Handles the drop of one or more items onto a view.
    * @param   insertionPoint
    *          The insertion point where the items should be dropped
    */
-  onDrop: function PCDH_onDrop(insertionPoint) {
-    var dt = this.currentDataTransfer;
-    var doCopy = ["copy", "link"].indexOf(dt.dropEffect) != -1;
+  onDrop: function PCDH_onDrop(insertionPoint, dt) {
+    let doCopy = ["copy", "link"].indexOf(dt.dropEffect) != -1;
 
-    var transactions = [];
-    var dropCount = dt.mozItemCount;
-    var movedCount = 0;
-    for (var i = 0; i < dropCount; ++i) {
-      var flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
+    let transactions = [];
+    let dropCount = dt.mozItemCount;
+    let movedCount = 0;
+    for (let i = 0; i < dropCount; ++i) {
+      let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
       if (!flavor)
         return false;
 
-      var data = dt.mozGetDataAt(flavor, i);
-      var unwrapped;
+      let data = dt.mozGetDataAt(flavor, i);
+      let unwrapped;
       if (flavor != TAB_DROP_TYPE) {
         // There's only ever one in the D&D case.
         unwrapped = PlacesUtils.unwrapNodes(data, flavor)[0];
       }
       else if (data instanceof XULElement && data.localName == "tab" &&
                data.ownerDocument.defaultView instanceof ChromeWindow) {
-        var uri = data.linkedBrowser.currentURI;
-        var spec = uri ? uri.spec : "about:blank";
-        var title = data.label;
+        let uri = data.linkedBrowser.currentURI;
+        let spec = uri ? uri.spec : "about:blank";
+        let title = data.label;
         unwrapped = { uri: spec,
                       title: data.label,
                       type: PlacesUtils.TYPE_X_MOZ_URL};
       }
       else
         throw("bogus data was passed as a tab")
 
-      var index = insertionPoint.index;
+      let index = insertionPoint.index;
 
       // Adjust insertion index to prevent reversal of dragged items. When you
       // drag multiple elts upward: need to increment index or each successive
       // elt will be inserted at the same index, each above the previous.
-      var dragginUp = insertionPoint.itemId == unwrapped.parent &&
+      let dragginUp = insertionPoint.itemId == unwrapped.parent &&
                       index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id);
       if (index != -1 && dragginUp)
         index+= movedCount++;
 
-      // if dragging over a tag container we should tag the item
+      // If dragging over a tag container we should tag the item.
       if (insertionPoint.isTag &&
           insertionPoint.orientation == Ci.nsITreeView.DROP_ON) {
-        var uri = PlacesUtils._uri(unwrapped.uri);
-        var tagItemId = insertionPoint.itemId;
+        let uri = PlacesUtils._uri(unwrapped.uri);
+        let tagItemId = insertionPoint.itemId;
         transactions.push(PlacesUIUtils.ptm.tagURI(uri,[tagItemId]));
       }
       else {
         transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
                           flavor, insertionPoint.itemId,
                           index, doCopy));
       }
     }
 
-    var txn = PlacesUIUtils.ptm.aggregateTransactions("DropItems", transactions);
+    let txn = PlacesUIUtils.ptm.aggregateTransactions("DropItems", transactions);
     PlacesUIUtils.ptm.doTransaction(txn);
   },
 
   /**
    * Checks if we can insert into a container.
    * @param   aContainer
    *          The container were we are want to drop
    */
   disallowInsertion: function(aContainer) {
     NS_ASSERT(aContainer, "empty container");
-    // allow dropping into Tag containers
+    // Allow dropping into Tag containers.
     if (PlacesUtils.nodeIsTagQuery(aContainer))
       return false;
-    // Disallow insertion of items under readonly folders
+    // Disallow insertion of items under readonly folders.
     return (!PlacesUtils.nodeIsFolder(aContainer) ||
              PlacesUtils.nodeIsReadOnly(aContainer));
   },
 
   placesFlavors: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
                   PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
                   PlacesUtils.TYPE_X_MOZ_PLACE],
 
   // The order matters.
   GENERIC_VIEW_DROP_TYPES: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
                             PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
                             PlacesUtils.TYPE_X_MOZ_PLACE,
                             PlacesUtils.TYPE_X_MOZ_URL,
                             TAB_DROP_TYPE,
                             PlacesUtils.TYPE_UNICODE],
+};
 
-  /**
-   * Returns our flavourSet
-   */
-  get flavourSet() {
-    delete this.flavourSet;
-    var flavourSet = new FlavourSet();
-    var acceptedDropFlavours = this.GENERIC_VIEW_DROP_TYPES;
-    acceptedDropFlavours.forEach(flavourSet.appendFlavour, flavourSet);
-    return this.flavourSet = flavourSet;
-  }
-};
+
+XPCOMUtils.defineLazyServiceGetter(PlacesControllerDragHelper, "dragService",
+                                   "@mozilla.org/widget/dragservice;1",
+                                   "nsIDragService");
 
 function goUpdatePlacesCommands() {
   // Get the controller for one of the places commands.
   var placesController = doGetPlacesControllerForCommand("placesCmd_open");
   if (!placesController)
     return;
 
   function updatePlacesCommand(aCommand) {
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -74,210 +74,47 @@
 
       <!-- markers for start and end of valid places items -->
       <field name="_startMarker">-1</field>
       <field name="_endMarker">-1</field>
 
       <!-- This is the view that manage the popup -->
       <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
 
-      <method name="onDragOver">
-        <parameter name="aEvent"/>
-        <parameter name="aFlavour"/>
-        <parameter name="aDragSession"/>
-        <body><![CDATA[
-          PlacesControllerDragHelper.currentDropTarget = aEvent.target;
-          // check if we have a valid dropPoint
-          var dropPoint = this._getDropPoint(aEvent);
-          if (!dropPoint || !dropPoint.ip ||
-              !PlacesControllerDragHelper.canDrop(dropPoint.ip)) {
-            aEvent.dataTransfer.effectAllowed = "none";
-            return;
-          }
-
-          // add a dragover attribute to this popup
-          this.setAttribute("dragover", "true");
-
-          if (dropPoint.folderNode) {
-            // We are dragging over a folder
-            // _overFolder should take the care of opening it on a timer
-            if (this._overFolder.node &&
-                this._overFolder.node != dropPoint.folderNode) {
-              // we are dragging over a new folder, let's clear old values
-              this._overFolder.clear();
-            }
-            if (!this._overFolder.node) {
-              this._overFolder.node = dropPoint.folderNode;
-              // create the timer to open this folder
-              this._overFolder.openTimer = this._overFolder
-                                               .setTimer(this._overFolder.hoverTime);
-            }
-            // since we are dropping into a folder set the corresponding style
-            dropPoint.folderNode.setAttribute("_moz-menuactive", true);
-          }
-          else {
-            // We are not dragging over a folder
-            // Clear out old _overFolder information
-            this._overFolder.clear();
-          }
-
-          // Autoscroll the popup strip if we drag over the scroll buttons
-          var anonid = aEvent.originalTarget.getAttribute('anonid');
-          var scrollDir = anonid == "scrollbutton-up" ? -1 :
-                          anonid == "scrollbutton-down" ? 1 : 0;
-          if (scrollDir != 0) {
-            this._scrollBox.scrollByIndex(scrollDir, false);
-          }
-
-          // Check if we should hide the drop indicator for this target
-          if (!aDragSession.canDrop ||
-              !dropPoint || dropPoint.folderNode ||
-              this._hideDropIndicator(aEvent, dropPoint)) {
-            this._indicatorBar.hidden = true;
-            return;
-          }
-
-          // We should display the drop indicator relative to the arrowscrollbox
-          var sbo = this._scrollBox.scrollBoxObject;
-          var newMarginTop = 0;
-          if (scrollDir == 0) {
-            var node = this.firstChild;
-            while (node && aEvent.screenY > node.boxObject.screenY +
-                                            node.boxObject.height / 2)
-              node = node.nextSibling;
-            newMarginTop = node ? node.boxObject.screenY - sbo.screenY :
-                                  sbo.height;
-          }
-          else if (scrollDir == 1)
-            newMarginTop = sbo.height;
-
-          // set the new marginTop based on arrowscrollbox
-          newMarginTop += sbo.y - this._scrollBox.boxObject.y;
-          this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
-          this._indicatorBar.hidden = false;
-          aEvent.stopPropagation();
-        ]]></body>
-      </method>
-
-      <method name="onDragExit">
-        <parameter name="aEvent"/>
-        <parameter name="aDragSession"/>
-        <body><![CDATA[
-          PlacesControllerDragHelper.currentDropTarget = null;
-          PlacesControllerDragHelper.currentDataTransfer = null;
-          this.removeAttribute("dragover");
-
-          // if we have not moved to a valid new target clear the drop indicator
-          // this happens when moving out of the popup
-          var target = aEvent.relatedTarget;
-          if (!target)
-            this._indicatorBar.hidden = true;
-
-          // Close any folder being hovered over
-          if (this._overFolder.node) {
-            this._overFolder.closeTimer = this._overFolder
-                                              .setTimer(this._overFolder.hoverTime);
-          }
-
-          // The autoopened attribute is set when this folder was automatically
-          // opened after the user dragged over it.  If this attribute is set,
-          // auto-close the folder on drag exit.
-          // We should also try to close this popup if the drag has started
-          // from here, the timer will check if we are dragging over a child.
-          if (this.hasAttribute("autoopened") ||
-              this.hasAttribute("dragstart")) {
-            this._overFolder.closeMenuTimer = this._overFolder
-                                                  .setTimer(this._overFolder.hoverTime);
-          }
-
-          this._rootView._draggedNode = null;
-        ]]></body>
-      </method>
-
-      <method name="onDragStart">
-        <parameter name="aEvent"/>
-        <parameter name="aXferData"/>
-        <parameter name="aDragAction"/>
-        <body><![CDATA[
-          var draggedNode = aEvent.target.node;
-
-          // Force a copy action if parent node is a query or we are dragging a
-          // not-removable node
-          if (!PlacesControllerDragHelper.canMoveNode(draggedNode))
-            aEvent.dataTransfer.effectAllowed = "copyLink";
-
-          // activate the view and cache the dragged node
-          this._rootView._draggedNode = draggedNode;
-          this._rootView.focus();
-
-          // Fill the dataTransfer
-          this._rootView._controller.setDataTransfer(aEvent);
-
-          this.setAttribute("dragstart", "true");
-          aEvent.stopPropagation();
-        ]]></body>
-      </method>
-
-      <method name="onDrop">
-        <parameter name="aEvent"/>
-        <parameter name="aDropData"/>
-        <parameter name="aSession"/>
-        <body><![CDATA[
-          // Cache the dataTransfer
-          PlacesControllerDragHelper.currentDataTransfer = aEvent.dataTransfer;
-
-          var dropPoint = this._getDropPoint(aEvent);
-          if (!dropPoint)
-            return;
-
-          PlacesControllerDragHelper.onDrop(dropPoint.ip);
-          aEvent.stopPropagation();
-        ]]></body>
-      </method>
-
-      <!-- This returns the FavourSet accepted by this popup -->
-      <method name="getSupportedFlavours">
-        <body><![CDATA[
-          return PlacesControllerDragHelper.flavourSet;
-        ]]></body>
-      </method>
-
       <!-- Check if we should hide the drop indicator for the target -->
       <method name="_hideDropIndicator">
         <parameter name="aEvent"/>
         <body><![CDATA[
           var target = aEvent.target;
 
           // in some view we have _startMarker and _endMarker, we should not
           // draw the drop indicator outside of them
           var betweenMarkers = true;
           if (this._startMarker != -1 &&
               target.boxObject.y <= this.childNodes[this._startMarker].boxObject.y)
             betweenMarkers = false;
           if (this._endMarker != -1 &&
               target.boxObject.y >= this.childNodes[this._endMarker].boxObject.y)
             betweenMarkers = false;
 
-          // hide the dropmarker if current node is not a places bookmark item
-          return !(target && target.node && betweenMarkers &&
-                   this.canDrop(aEvent));
+          // Hide the dropmarker if current node is not a Places node.
+          return !(target && target.node && betweenMarkers);
         ]]></body>
       </method>
 
       <!-- This function returns information about where to drop when
            dragging over this popup insertion point -->
       <method name="_getDropPoint">
         <parameter name="aEvent"/>
           <body><![CDATA[
             // Can't drop if the menu isn't a folder
             var resultNode = this._resultNode;
 
             if (!PlacesUtils.nodeIsFolder(resultNode) ||
                 PlacesControllerDragHelper.disallowInsertion(resultNode)) {
-              aEvent.dataTransfer.effectAllowed = "none";
               return null;
             }
 
             var dropPoint = { ip: null, folderNode: null };
 
             // The node we are dragging over
             var xulNode = aEvent.target;
 
@@ -340,27 +177,16 @@
                                 -1,
                                 Ci.nsITreeView.DROP_AFTER,
                                 PlacesUtils.nodeIsTagQuery(xulNode.node),
                                 xulNode.node.itemId);
             return dropPoint;
         ]]></body>
       </method>
 
-      <method name="canDrop">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          // Cache the dataTransfer
-          PlacesControllerDragHelper.currentDataTransfer = aEvent.dataTransfer;
-
-          var ip = this._rootView.insertionPoint;
-          return ip && PlacesControllerDragHelper.canDrop(ip);
-        ]]></body>
-      </method>
-
       <!-- Sub-menus should be opened when the mouse drags over them, and closed
            when the mouse drags off.  The overFolder object manages opening and
            closing of folders when the mouse hovers. -->
       <field name="_overFolder"><![CDATA[({
         _self: this,
         _folder: {node: null,
                   openTimer: null,
                   hoverTime: 350,
@@ -485,16 +311,27 @@
           }
           if (this._folder.closeTimer) {
             this._folder.closeTimer.cancel();
             this._folder.closeTimer = null;
           }
         }
       })]]></field>
 
+      <method name="_cleanupDragDetails">
+        <body><![CDATA[
+          // Called on dragend and drop.
+          PlacesControllerDragHelper.currentDropTarget = null;
+          this._rootView._draggedNode = null;
+          this.removeAttribute("dragover");
+          this.removeAttribute("dragstart");
+          this._indicatorBar.hidden = true;
+        ]]></body>
+      </method>
+
     </implementation>
 
     <handlers>
       <handler event="DOMMenuItemActive"><![CDATA[
         var node = event.target;
         if (node.parentNode != this)
           return;
 
@@ -517,28 +354,171 @@
             linkURI = nodeItem.uri;
           else if (node.hasAttribute("targetURI"))
             linkURI = node.getAttribute("targetURI");
 
           if (linkURI)
             window.XULBrowserWindow.setOverLink(linkURI, null);
         }
       ]]></handler>
+
       <handler event="DOMMenuItemInactive"><![CDATA[
         var node = event.target;
         if (node.parentNode != this)
           return;
 
         if (window.XULBrowserWindow)
           window.XULBrowserWindow.setOverLink("", null);
       ]]></handler>
-      <handler event="draggesture" action="if (event.target.node) nsDragAndDrop.startDrag(event, this);"/>
-      <handler event="drop" action="nsDragAndDrop.drop(event, this);"/>
-      <handler event="dragover" action="nsDragAndDrop.dragOver(event, this);"/>
-      <handler event="dragexit" action="nsDragAndDrop.dragExit(event, this);"/>
+
+      <handler event="dragstart"><![CDATA[
+        if (!event.target.node)
+          return;
+
+        let draggedNode = event.target.node;
+
+        // Force a copy action if parent node is a query or we are dragging a
+        // not-removable node.
+        if (!PlacesControllerDragHelper.canMoveNode(draggedNode))
+          event.dataTransfer.effectAllowed = "copyLink";
+
+        // Activate the view and cache the dragged node.
+        this._rootView._draggedNode = draggedNode;
+        this._rootView.focus();
+
+        this._rootView._controller.setDataTransfer(event);
+
+        this.setAttribute("dragstart", "true");
+        event.stopPropagation();
+      ]]></handler>
+
+      <handler event="drop"><![CDATA[
+        PlacesControllerDragHelper.currentDropTarget = event.target;
+
+        let dropPoint = this._getDropPoint(event);
+        if (dropPoint && dropPoint.ip) {
+          PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer);
+          event.preventDefault();
+        }
+
+        this._cleanupDragDetails();
+        event.stopPropagation();
+      ]]></handler>
+
+      <handler event="dragover"><![CDATA[
+        PlacesControllerDragHelper.currentDropTarget = event.target;
+        let dt = event.dataTransfer;
+
+        let dropPoint = this._getDropPoint(event);
+        if (!dropPoint || !dropPoint.ip ||
+            !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
+          this._indicatorBar.hidden = true;
+          event.stopPropagation();
+          return;
+        }
+
+        // Mark this popup as being dragged over.
+        this.setAttribute("dragover", "true");
+
+        if (dropPoint.folderNode) {
+          // We are dragging over a folder.
+          // _overFolder should take the care of opening it on a timer.
+          if (this._overFolder.node &&
+              this._overFolder.node != dropPoint.folderNode) {
+            // We are dragging over a new folder, let's clear old values
+            this._overFolder.clear();
+          }
+          if (!this._overFolder.node) {
+            this._overFolder.node = dropPoint.folderNode;
+            // Create the timer to open this folder.
+            this._overFolder.openTimer = this._overFolder
+                                             .setTimer(this._overFolder.hoverTime);
+          }
+          // Since we are dropping into a folder set the corresponding style.
+          dropPoint.folderNode.setAttribute("_moz-menuactive", true);
+        }
+        else {
+          // We are not dragging over a folder.
+          // Clear out old _overFolder information.
+          this._overFolder.clear();
+        }
+
+        // Autoscroll the popup strip if we drag over the scroll buttons.
+        let anonid = event.originalTarget.getAttribute('anonid');
+        let scrollDir = anonid == "scrollbutton-up" ? -1 :
+                        anonid == "scrollbutton-down" ? 1 : 0;
+        if (scrollDir != 0) {
+          this._scrollBox.scrollByIndex(scrollDir, false);
+        }
+
+        // Check if we should hide the drop indicator for this target.
+        if (dropPoint.folderNode || this._hideDropIndicator(event)) {
+          this._indicatorBar.hidden = true;
+          event.preventDefault();
+          event.stopPropagation();
+          return;
+        }
+
+        // We should display the drop indicator relative to the arrowscrollbox.
+        let sbo = this._scrollBox.scrollBoxObject;
+        let newMarginTop = 0;
+        if (scrollDir == 0) {
+          let node = this.firstChild;
+          while (node && event.screenY > node.boxObject.screenY +
+                                         node.boxObject.height / 2)
+            node = node.nextSibling;
+          newMarginTop = node ? node.boxObject.screenY - sbo.screenY :
+                                sbo.height;
+        }
+        else if (scrollDir == 1)
+          newMarginTop = sbo.height;
+
+        // Set the new marginTop based on arrowscrollbox.
+        newMarginTop += sbo.y - this._scrollBox.boxObject.y;
+        this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
+        this._indicatorBar.hidden = false;
+
+        event.preventDefault();
+        event.stopPropagation();
+      ]]></handler>
+
+      <handler event="dragleave"><![CDATA[
+        PlacesControllerDragHelper.currentDropTarget = null;
+        this.removeAttribute("dragover");
+
+        // If we have not moved to a valid new target clear the drop indicator
+        // this happens when moving out of the popup.
+        let target = event.relatedTarget;
+        if (!target)
+          this._indicatorBar.hidden = true;
+
+        // Close any folder being hovered over
+        if (this._overFolder.node) {
+          this._overFolder.closeTimer = this._overFolder
+                                            .setTimer(this._overFolder.hoverTime);
+        }
+
+        // The autoopened attribute is set when this folder was automatically
+        // opened after the user dragged over it.  If this attribute is set,
+        // auto-close the folder on drag exit.
+        // We should also try to close this popup if the drag has started
+        // from here, the timer will check if we are dragging over a child.
+        if (this.hasAttribute("autoopened") ||
+            this.hasAttribute("dragstart")) {
+          this._overFolder.closeMenuTimer = this._overFolder
+                                                .setTimer(this._overFolder.hoverTime);
+        }
+
+        event.stopPropagation();
+      ]]></handler>
+
+      <handler event="dragend"><![CDATA[
+        this._cleanupDragDetails();
+      ]]></handler>
+
     </handlers>
   </binding>
 
 
   <binding id="places-menupopup"
            extends="chrome://browser/content/places/menu.xml#places-popup-base">
     <implementation>
       <destructor><![CDATA[
@@ -1015,18 +995,18 @@
               // If a static menuitem is selected the insertion point
               // is inside the folder, at the end.
               container = selectedNode;
               orientation = Ci.nsITreeView.DROP_ON;
             }
             else {
               // In all other cases the insertion point is before that node.
               container = selectedNode.parent;
-              index = PlacesUtils.getIndexOfNode(selectedNode);
-              isTag = PlacesUtils.nodeIsTagQuery(selectedNode.parent);
+              index = container.getChildIndex(selectedNode);
+              isTag = PlacesUtils.nodeIsTagQuery(container);
             }
           }
 
           if (PlacesControllerDragHelper.disallowInsertion(container))
             return null;
 
           return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
                                     index, orientation, isTag);
--- a/browser/components/places/content/placesOverlay.xul
+++ b/browser/components/places/content/placesOverlay.xul
@@ -52,18 +52,16 @@
   <script type="application/javascript"
           src="chrome://browser/content/utilityOverlay.js"/>
   <script type="application/javascript" 
           src="chrome://browser/content/places/utils.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/places/controller.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/places/treeView.js"/>
-  <script type="application/javascript"
-          src="chrome://global/content/nsDragAndDrop.js"/>
 
   <!-- Bookmarks and history tooltip -->
   <tooltip id="bhTooltip" noautohide="true"
            onpopupshowing="return window.top.BookmarksEventHandler.fillInBHTooltip(document, event)">
     <vbox id="bhTooltipTextBox" flex="1">
       <label id="bhtTitleText" class="tooltip-label" />
       <label id="bhtUrlText" crop="center" class="tooltip-label" />
     </vbox>
--- a/browser/components/places/content/toolbar.xml
+++ b/browser/components/places/content/toolbar.xml
@@ -478,18 +478,18 @@
               // If a static menuitem is selected the insertion point
               // is inside the folder, at the end.
               container = selectedNode;
               orientation = Ci.nsITreeView.DROP_ON;
             }
             else {
               // In all other cases the insertion point is before that node.
               container = selectedNode.parent;
-              index = PlacesUtils.getIndexOfNode(selectedNode);
-              isTag = PlacesUtils.nodeIsTagQuery(selectedNode.parent);
+              index = container.getChildIndex(selectedNode);
+              isTag = PlacesUtils.nodeIsTagQuery(container);
             }
           }
 
           if (PlacesControllerDragHelper.disallowInsertion(container))
             return null;
 
           return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
                                     index, orientation, isTag);
@@ -1038,106 +1038,114 @@
             if (inHierarchy)
               this._overFolder.node = null;
 
             // Clear out the folder and all associated timers.
             this._clearOverFolder();
           }
         ]]></body>
       </method>
+
+      <method name="_cleanupDragDetails">
+        <body><![CDATA[
+          // Called on dragend and drop.
+          PlacesControllerDragHelper.currentDropTarget = null;
+          this._draggedNode = null;
+          if (this._ibTimer)
+            this._ibTimer.cancel();
+          this._dropIndicator.collapsed = true;
+        ]]></body>
+      </method>
+
     </implementation>
 
     <handlers>
       <handler event="mouseover"><![CDATA[
         var button = event.target;
         if (button.parentNode == this && button.node &&
             PlacesUtils.nodeIsURI(button.node))
           window.XULBrowserWindow.setOverLink(event.target.node.uri, null);
       ]]></handler>
 
       <handler event="mouseout"><![CDATA[
         window.XULBrowserWindow.setOverLink("", null);
       ]]></handler>
 
       <handler event="dragstart"><![CDATA[
-        // sub menus have their own d&d handlers
-        var draggedDOMNode = event.target;
+        // Sub menus have their own d&d handlers.
+        let draggedDOMNode = event.target;
         if (draggedDOMNode.parentNode != this || !draggedDOMNode.node)
           return;
 
         if (draggedDOMNode.localName == "toolbarbutton" &&
             draggedDOMNode.getAttribute("type") == "menu") {
           // If the drag gesture on a container is toward down we open instead
           // of dragging.
           if (this._mouseDownTimer) {
             this._mouseDownTimer.cancel();
             this._mouseDownTimer = null;
           }
-          var translateY = this._cachedMouseMoveEvent.clientY - event.clientY;
-          var translateX = this._cachedMouseMoveEvent.clientX - event.clientX;
+          let translateY = this._cachedMouseMoveEvent.clientY - event.clientY;
+          let translateX = this._cachedMouseMoveEvent.clientX - event.clientX;
           if ((translateY) >= Math.abs(translateX/2)) {
-            // Don't start the drag
+            // Don't start the drag.
             event.preventDefault();
             // Open the menu
             draggedDOMNode.open = true;
             return;
           }
-          // if the menu is open, close it
+          // If the menu is open, close it.
           if (draggedDOMNode.open) {
             draggedDOMNode.firstChild.hidePopup();
             draggedDOMNode.open = false;
           }
         }
 
-        // activate the view and cache the dragged node
+        // Activate the view and cache the dragged node.
         this._draggedNode = draggedDOMNode.node;
         this.focus();
 
         this._controller.setDataTransfer(event);
         event.stopPropagation();
       ]]></handler>
 
       <handler event="dragover"><![CDATA[
-        // Cache the dataTransfer
-        var dt = PlacesControllerDragHelper.currentDataTransfer =
-                 event.dataTransfer;
+        PlacesControllerDragHelper.currentDropTarget = event.target;
+        let dt = event.dataTransfer;
 
-        var ip = this.insertionPoint;
-        if (!ip || !PlacesControllerDragHelper.canDrop(ip)) {
+        let dropPoint = this._getDropPoint(event);
+        if (!dropPoint || !dropPoint.ip ||
+            !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
           this._dropIndicator.collapsed = true;
-          PlacesControllerDragHelper.currentDataTransfer = null;
+          event.stopPropagation();
           return;
         }
 
-        PlacesControllerDragHelper.currentDropTarget = event.target;
-        var dropPoint = this._getDropPoint(event);
-
         if (this._ibTimer) {
           this._ibTimer.cancel();
           this._ibTimer = null;
         }
 
-        if (dropPoint.folderNode ||
-            event.originalTarget == this._chevron) {
-          // Dropping over a menubutton or chevron button
-          // set styles and timer to open relative menupopup
-          var overNode = dropPoint.folderNode || this._chevron;
+        if (dropPoint.folderNode || event.originalTarget == this._chevron) {
+          // Dropping over a menubutton or chevron button.
+          // Set styles and timer to open relative menupopup.
+          let overNode = dropPoint.folderNode || this._chevron;
           if (this._overFolder.node != overNode) {
             this._clearOverFolder();
             this._overFolder.node = overNode;
             this._overFolder.openTimer = this._setTimer(this._overFolder.hoverTime);
           }
           if (!this._overFolder.node.hasAttribute("dragover"))
             this._overFolder.node.setAttribute("dragover", "true");
 
           this._dropIndicator.collapsed = true;
         }
         else {
-          // Dragging over a normal toolbarbutton,
-          // show indicator bar and move it to the appropriate drop point.
+          // Dragging over a normal toolbarbutton.
+          // Show indicator bar and move it to the appropriate drop point.
           let ind = this._dropIndicator;
           let halfInd = ind.clientWidth / 2;
           let translateX;
           if (this._isRTL) {
             halfInd = Math.ceil(halfInd);
             translateX = 0 - this._scrollbox.getBoundingClientRect().right -
                              halfInd;
             if (this.firstChild) {
@@ -1162,59 +1170,54 @@
               }
             }
           }
 
           ind.style.MozTransform = "translate(" + Math.round(translateX) + "px)";
           ind.style.MozMarginStart = (-ind.clientWidth) + "px";
           ind.collapsed = false;
 
-          // Clear out old folder information
+          // Clear out old folder information.
           this._clearOverFolder();
         }
 
-        dt.effectAllowed = "all";
         event.preventDefault();
         event.stopPropagation();
       ]]></handler>
 
       <handler event="drop"><![CDATA[
-        PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer;
         PlacesControllerDragHelper.currentDropTarget = event.target;
 
-        var ip = this.insertionPoint;
-        if (!ip || !PlacesControllerDragHelper.canDrop(ip))
-          return;
+        let dropPoint = this._getDropPoint(event);
+        if (dropPoint && dropPoint.ip) {
+          PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer);
+          event.preventDefault();
+        }
 
-        var dropPoint = this._getDropPoint(event);
-        if (!dropPoint)
-          return;
-
-        PlacesControllerDragHelper.onDrop(dropPoint.ip);
+        this._cleanupDragDetails();
         event.stopPropagation();
       ]]></handler>
 
       <handler event="dragleave"><![CDATA[
         PlacesControllerDragHelper.currentDropTarget = null;
-        PlacesControllerDragHelper.currentDataTransfer = null;
 
         // Set timer to turn off indicator bar (if we turn it off
         // here, dragenter might be called immediately after, creating
-        // flicker.)
+        // flicker).
         if (this._ibTimer)
           this._ibTimer.cancel();
         this._ibTimer = this._setTimer(10);
 
-        // If we hovered over a folder, close it now
+        // If we hovered over a folder, close it now.
         if (this._overFolder.node)
             this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime);
       ]]></handler>
 
       <handler event="dragend"><![CDATA[
-        this._draggedNode = null;
+        this._cleanupDragDetails();
       ]]></handler>
 
       <handler event="popupshowing" phase="capturing"><![CDATA[
         if (!this._allowPopupShowing) {
           this._allowPopupShowing = true;
           event.preventDefault();
           return;
         }
--- a/browser/components/places/content/tree.xml
+++ b/browser/components/places/content/tree.xml
@@ -321,25 +321,25 @@
         <getter><![CDATA[
           return this.view.selection.count >= 1;
         ]]></getter>
       </property>
       
       <!-- nsIPlacesView -->
       <method name="getSelectionNodes">
         <body><![CDATA[ 
-          var selection = this.view.selection;
-          var rc = selection.getRangeCount();
-          var nodes = [];
-          var resultview = this.view;
-          for (var i = 0; i < rc; ++i) {
-            var min = { }, max = { };
+          let selection = this.view.selection;
+          let rc = selection.getRangeCount();
+          let nodes = [];
+          let resultview = this.view;
+          for (let i = 0; i < rc; ++i) {
+            let min = { }, max = { };
             selection.getRangeAt(i, min, max);
 
-            for (var j = min.value; j <= max.value; ++j)
+            for (let j = min.value; j <= max.value; ++j)
               nodes.push(resultview.nodeForTreeIndex(j));
           }
           return nodes;
         ]]></body>
       </method>
 
       <!-- nsIPlacesView -->
       <method name="getRemovableSelectionRanges">
@@ -444,17 +444,17 @@
           // * The default orientation is to drop _before_ the selected item.
           // * If the selected item is a container, the default orientation
           //   is to drop _into_ that container.
           //
           // Warning: It may be tempting to use tree indexes in this code, but
           //          you must not, since the tree is nested and as your tree 
           //          index may change when folders before you are opened and
           //          closed. You must convert your tree index to a node, and
-          //          then use getIndexOfNode to find your absolute index in
+          //          then use getChildIndex to find your absolute index in
           //          the parent container instead. 
           //
           var resultView = this.view;
           var selection = resultView.selection;
           var rc = selection.getRangeCount();
           var min = { }, max = { };
           selection.getRangeAt(rc - 1, min, max);
           
@@ -476,17 +476,17 @@
             this._getInsertionPoint(max.value, orientation);
           return this._cachedInsertionPoint;
         ]]></getter>
       </property>
 
       <method name="_getInsertionPoint">
         <parameter name="index"/>
         <parameter name="orientation"/>
-        <body><![CDATA[ 
+        <body><![CDATA[
           var result = this.getResult();
           var resultview = this.view;
           var container = result.root;
           var dropNearItemId = -1;
           NS_ASSERT(container, "null container");
           // When there's no selection, assume the container is the container
           // the view is populated from (i.e. the result's itemId).
           if (index != -1) {
@@ -502,22 +502,24 @@
                      lastSelected.hasChildren) {
              // If the last selected item is an open container and the user is
              // trying to drag into it as a first item, really insert into it.
              container = lastSelected;
              orientation = Ci.nsITreeView.DROP_ON;
              index = 0;
             }
             else {
-              // Use the last-selected node's container unless the root node
-              // is selected, in which case we use the root node itself as the
-              // insertion point.
-              container = lastSelected.parent || container;
+              // Use the last-selected node's container.
+              container = lastSelected.parent;
 
-              // avoid the potentially expensive call to getIndexOfNode() 
+              // See comment in the treeView.js's copy of this method
+              if (!container || !container.containerOpen)
+                return null;
+
+              // Avoid the potentially expensive call to getChildIndex
               // if we know this container doesn't allow insertion
               if (PlacesControllerDragHelper.disallowInsertion(container))
                 return null;
 
               var queryOptions = asQuery(result.root).queryOptions;
               if (queryOptions.sortingMode !=
                     Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
                 // If we are within a sorted view, insert at the end
@@ -528,17 +530,17 @@
                        queryOptions.excludeReadOnlyFolders) {
                 // Some item may be invisible, insert near last selected one.
                 // We don't replace index here to avoid requests to the db,
                 // instead it will be calculated later by the controller.
                 index = -1;
                 dropNearItemId = lastSelected.itemId;
               }
               else {
-                var lsi = PlacesUtils.getIndexOfNode(lastSelected);
+                var lsi = container.getChildIndex(lastSelected);
                 index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
               }
             }
           }
 
           if (PlacesControllerDragHelper.disallowInsertion(container))
             return null;
 
@@ -658,109 +660,16 @@
           for (var i = 0; i < nodes.length; i++) {
             var index = resultview.treeIndexForNode(nodes[i]);
             selection.rangedSelect(index, index, true);
           }
           selection.selectEventsSuppressed = false;
         ]]></body>
       </method>
 
-      <!-- nsDragAndDrop -->
-      <method name="onDragStart">
-        <parameter name="aEvent"/>
-        <parameter name="aXferData"/>
-        <parameter name="aDragAction"/>
-        <body><![CDATA[ 
-          var nodes = this.getSelectionNodes();
-          for (var i = 0; i < nodes.length; ++i) {
-            var node = nodes[i];
-
-            // Disallow dragging the root node of a tree
-            var parent = node.parent;
-            if (!parent)
-              return;
-
-            // If this node is child of a readonly container (e.g. a livemark)
-            // or cannot be moved, we must force a copy.
-            if (!PlacesControllerDragHelper.canMoveNode(node)) {
-              aEvent.dataTransfer.effectAllowed = "copyLink";
-              break;
-            }
-          }
-
-          // Stuff the encoded selection into the transferable data object
-          this._controller.setDataTransfer(aEvent);
-        ]]></body>
-      </method>
-      
-      <!-- nsDragAndDrop -->
-      <method name="canDrop">
-        <parameter name="aEvent"/>
-        <parameter name="aDragSession"/>
-        <body><![CDATA[
-          // Cache the dataTransfer for the view
-          PlacesControllerDragHelper.currentDataTransfer = aEvent.dataTransfer;
-
-          var row = { }, col = { }, child = { };
-          this.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY,
-                                       row, col, child);
-          var node = row.value != -1 ?
-                     this.view.nodeForTreeIndex(row.value) :
-                     this.getResultNode();
-          // cache the dropTarget for the view
-          PlacesControllerDragHelper.currentDropTarget = node;
-
-          // We have to calculate the orientation since view.canDrop will use
-          // it and we want to be consistent with the dropfeedback
-          var tbo = this.treeBoxObject;
-          var rowHeight = tbo.rowHeight;
-          var eventY = aEvent.clientY - tbo.treeBody.boxObject.y -
-                       rowHeight * (row.value - tbo.getFirstVisibleRow());
-
-          var orientation = Ci.nsITreeView.DROP_BEFORE;
-
-          if (row.value == -1) {
-            // If the row is not valid we try to insert inside the resultNode.
-            orientation = Ci.nsITreeView.DROP_ON;
-          }
-          else if (PlacesUtils.nodeIsContainer(node) &&
-                   eventY > rowHeight * 0.75) {
-            // If we are below the 75% of a container the treeview we try
-            // to drop after the node.
-            orientation = Ci.nsITreeView.DROP_AFTER;
-          }
-          else if (PlacesUtils.nodeIsContainer(node) &&
-                   eventY > rowHeight * 0.25) {
-            // If we are below the 25% of a container the treeview we try
-            // to drop inside the node.
-            orientation = Ci.nsITreeView.DROP_ON;
-          }
-
-          return this.view.canDrop(row.value, orientation, aEvent.dataTransfer);
-        ]]></body>
-      </method>
-      
-      <!-- nsDragAndDrop -->
-      <method name="onDragOver">
-        <parameter name="aEvent"/>
-        <parameter name="aFlavour"/>
-        <parameter name="aDragSession"/>
-        <body><![CDATA[
-          if (!this.canDrop(aEvent, aDragSession, aEvent.dataTransfer))
-            aEvent.dataTransfer.effectAllowed = "none";
-        ]]></body>
-      </method>
-
-      <!-- nsDragAndDrop -->
-      <method name="getSupportedFlavours">
-        <body><![CDATA[
-          return PlacesControllerDragHelper.flavourSet;
-        ]]></body>
-      </method>
-
       <method name="buildContextMenu">
         <parameter name="aPopup"/>
         <body><![CDATA[
           return this.controller.buildContextMenu(aPopup);
         ]]></body>
       </method>
 
       <method name="destroyContextMenu">
@@ -784,28 +693,91 @@
         while (true) {
           win.document.commandDispatcher.updateCommands("focus");
           if (win == window.top)
             break;
 
           win = win.parent;
         }
       ]]></handler>
-      <handler event="draggesture"><![CDATA[
-        if (event.target.localName == "treechildren")
-          nsDragAndDrop.startDrag(event, this);
+
+      <handler event="dragstart"><![CDATA[
+        if (event.target.localName != "treechildren")
+          return;
+
+        let nodes = this.getSelectionNodes();
+        for (let i = 0; i < nodes.length; i++) {
+          let node = nodes[i];
+
+          // Disallow dragging the root node of a tree.
+          if (!node.parent) {
+            event.preventDefault();
+            event.stopPropagation();
+            return;
+          }
+
+          // If this node is child of a readonly container (e.g. a livemark)
+          // or cannot be moved, we must force a copy.
+          if (!PlacesControllerDragHelper.canMoveNode(node)) {
+            event.dataTransfer.effectAllowed = "copyLink";
+            break;
+          }
+        }
+
+        this._controller.setDataTransfer(event);
+        event.stopPropagation();
       ]]></handler>
+
       <handler event="dragover"><![CDATA[
-        if (event.target.localName == "treechildren")
-          nsDragAndDrop.dragOver(event, this);
+        if (event.target.localName != "treechildren")
+          return;
+
+        let row = { }, col = { }, child = { };
+        this.treeBoxObject.getCellAt(event.clientX, event.clientY,
+                                     row, col, child);
+        let node = row.value != -1 ?
+                   this.view.nodeForTreeIndex(row.value) :
+                   this.getResultNode();
+        // cache the dropTarget for the view
+        PlacesControllerDragHelper.currentDropTarget = node;
+
+        // We have to calculate the orientation since view.canDrop will use
+        // it and we want to be consistent with the dropfeedback.
+        let tbo = this.treeBoxObject;
+        let rowHeight = tbo.rowHeight;
+        let eventY = event.clientY - tbo.treeBody.boxObject.y -
+                     rowHeight * (row.value - tbo.getFirstVisibleRow());
+
+        let orientation = Ci.nsITreeView.DROP_BEFORE;
+
+        if (row.value == -1) {
+          // If the row is not valid we try to insert inside the resultNode.
+          orientation = Ci.nsITreeView.DROP_ON;
+        }
+        else if (PlacesUtils.nodeIsContainer(node) &&
+                 eventY > rowHeight * 0.75) {
+          // If we are below the 75% of a container the treeview we try
+          // to drop after the node.
+          orientation = Ci.nsITreeView.DROP_AFTER;
+        }
+        else if (PlacesUtils.nodeIsContainer(node) &&
+                 eventY > rowHeight * 0.25) {
+          // If we are below the 25% of a container the treeview we try
+          // to drop inside the node.
+          orientation = Ci.nsITreeView.DROP_ON;
+        }
+
+        if (!this.view.canDrop(row.value, orientation, event.dataTransfer))
+          return;
+
+        event.preventDefault();
+        event.stopPropagation();
       ]]></handler>
-      <handler event="drop"><![CDATA[
-          PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer;
-      ]]></handler>
-      <handler event="dragexit"><![CDATA[
-        PlacesControllerDragHelper.currentDataTransfer = null;
+
+      <handler event="dragend"><![CDATA[
         PlacesControllerDragHelper.currentDropTarget = null;
       ]]></handler>
+
     </handlers>
   </binding>
 
 </bindings>
 
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -15,18 +15,18 @@
  * The Original Code is Mozilla History System
  *
  * The Initial Developer of the Original Code is
  * Google Inc.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Brett Wilson <brettw@gmail.com> (original author)
- *   Asaf Romano <mano@mozilla.com> (Javascript version)
+ *   Brett Wilson <brettw@gmail.com> (Original author)
+ *   Asaf Romano <mano@mozilla.com> (JavaScript version)
  *
  * 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
@@ -47,21 +47,16 @@ PlacesTreeView.prototype = {
   _atoms: [],
   _getAtomFor: function PTV__getAtomFor(aName) {
     if (!this._atoms[aName])
       this._atoms[aName] = this._makeAtom(aName);
 
     return this._atoms[aName];
   },
 
-  _ensureValidRow: function PTV__ensureValidRow(aRow) {
-    if (aRow < 0 || aRow >= this._visibleElements.length)
-      throw Cr.NS_ERROR_INVALID_ARG;
-  },
-
   __dateService: null,
   get _dateService() {
     if (!this.__dateService) {
       this.__dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
                            getService(Ci.nsIScriptableDateFormat);
     }
     return this.__dateService;
   },
@@ -75,268 +70,422 @@ PlacesTreeView.prototype = {
 
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
   /**
    * This is called once both the result and the tree are set.
    */
   _finishInit: function PTV__finishInit() {
-    var selection = this.selection;
+    let selection = this.selection;
     if (selection)
       selection.selectEventsSuppressed = true;
 
-    this._rootNode._viewIndex = -1;
     if (!this._rootNode.containerOpen) {
       // This triggers containerOpened which then builds the visible section.
       this._rootNode.containerOpen = true;
     }
     else
       this.invalidateContainer(this._rootNode);
 
     // "Activate" the sorting column and update commands.
     this.sortingChanged(this._result.sortingMode);
 
     if (selection)
       selection.selectEventsSuppressed = false;
   },
 
+  /**
+   * Plain Container: container result nodes which may never include sub
+   * hierarchies.
+   *
+   * When the rows array is constructed, we don't set the children of plain
+   * containers.  Instead, we keep placeholders for these children.  We then
+   * build these children lazily as the tree asks us for information about each
+   * row.  Luckily, the tree doesn't ask about rows outside the visible area.
+   *
+   * @see _getNodeForRow and _getRowForNode for the actual magic.
+   *
+   * @note It's guaranteed that all containers are listed in the rows
+   * elements array.  It's also guaranteed that separators (if they're not
+   * filtered, see below) are listed in the visible elements array, because
+   * bookmark folders are never built lazily, as described above.
+   *
+   * @param aContainer
+   *        A container result node.
+   *
+   * @return true if aContainer is a plain container, false otherwise.
+   */
+  _isPlainContainer: function PTV__isPlainContainer(aContainer) {
+    if (aContainer._plainContainer !== undefined)
+      return aContainer._plainContainer;
+
+    // We don't know enough about non-query containers.
+    if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode))
+      return aContainer._plainContainer = false;
+
+    let resultType = aContainer.queryOptions.resultType;
+    switch (resultType) {
+      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
+      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
+      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
+      case Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY:
+        return aContainer._plainContainer = false;
+    }
+
+    // If it's a folder, it's not a plain container.
+    let nodeType = aContainer.type;
+    return aContainer._plainContainer =
+           (nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER &&
+            nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT);
+  },
+
+  /**
+   * Gets the row number for a given node.  Assumes that the given node is
+   * visible (i.e. it's not an obsolete node).
+   *
+   * @param aNode
+   *        A result node.  Do not pass an obsolete node, or any
+   *        node which isn't supposed to be in the tree (e.g. separators in
+   *        sorted trees).
+   * @param [optional] aForceBuild
+   *        @see isPlainContainer.
+   *        If true, the row will be computed even if the node still isn't set
+   *        in our rows array.
+   * @param [optional] aParentRow
+   *        The row of aNode's parent.
+   *        DO NOT compute this yourself for the purpose of calling this
+   *        function.  However, do pass it if you have it handy.
+   *        Ignored for the root node.
+   * @param [optional] aNodeIndex
+   *        The index of aNode in its parent.  Only used if aParentRow is
+   *        set too.
+   *
+   * @throws if aNode is invisible.
+   * @return aNode's row if it's in the rows list or if aForceBuild is set, -1
+   *         otherwise.
+   */
+  _getRowForNode:
+  function PTV__getRowForNode(aNode, aForceBuild, aParentRow, aNodeIndex) {
+    if (aNode == this._rootNode)
+      throw "The root node is never visible";
+
+    let parent = aNode.parent;
+    if (!parent || !parent.containerOpen)
+      throw "Invisible node passed to _getRowForNode";
+
+    // Non-plain containers are initially built with their contents.
+    let parentIsPlain = this._isPlainContainer(parent);
+    if (!parentIsPlain) {
+      if (parent == this._rootNode)
+        return this._rows.indexOf(aNode);
+
+      return this._rows.indexOf(aNode, aParentRow);
+    }
+
+    let row = -1;
+    let useNodeIndex = typeof(aNodeIndex) == "number";
+    if (parent == this._rootNode)
+      row = useNodeIndex ? aNodeIndex : this._rootNode.getChildIndex(aNode);
+    else if (useNodeIndex && typeof(aParentRow) == "number") {
+      // If we have both the row of the parent node, and the node's index, we
+      // can avoid searching the rows array if the parent is a plain container.
+      row = aParentRow + aNodeIndex + 1;
+    }
+    else {
+      // Look for the node in the nodes array.  Start the search at the parent
+      // row.  If the parent row isn't passed, we'll pass undefined to indexOf,
+      // which is fine.
+      row = this._rows.indexOf(aNode, aParentRow);
+      if (row == -1 && aForceBuild) {
+        let parentRow = typeof(aParentRow) == "number" ? aParentRow
+                                                       : this._getRowForNode(parent);
+        row = parentRow + parent.getChildIndex(aNode) + 1;
+      }
+    }
+
+    if (row != -1)
+      this._rows[row] = aNode;
+
+    return row;
+  },
+
+  /**
+   * Given a row, finds and returns the parent details of the associated node.
+   *
+   * @param aChildRow
+   *        Row number.
+   * @return [parentNode, parentRow]
+   */
+  _getParentByChildRow: function PTV__getParentByChildRow(aChildRow) {
+    let parent = this._getNodeForRow(aChildRow).parent;
+
+    // The root node is never visible
+    if (parent == this._rootNode)
+      return [this._rootNode, -1];
+
+    let parentRow = this._rows.lastIndexOf(parent, aChildRow - 1);
+    return [parent, parentRow];
+  },
+
+  /**
+   * Gets the node at a given row.
+   */
+  _getNodeForRow: function PTV__getNodeForRow(aRow) {
+    let node = this._rows[aRow];
+    if (node !== undefined)
+      return node;
+
+    // Find the nearest node.
+    let rowNode, row;
+    for (let i = aRow - 1; i >= 0 && rowNode === undefined; i--) {
+      rowNode = this._rows[i];
+      row = i;
+    }
+
+    // If there's no container prior to the given row, it's a child of
+    // the root node (remember: all containers are listed in the rows array).
+    if (!rowNode)
+      return this._rows[aRow] = this._rootNode.getChild(aRow);
+
+    // Unset elements may exist only in plain containers.  Thus, if the nearest
+    // node is a container, it's the row's parent, otherwise, it's a sibling.
+    if (rowNode instanceof Ci.nsINavHistoryContainerResultNode)
+      return this._rows[aRow] = rowNode.getChild(aRow - row - 1);
+
+    let [parent, parentRow] = this._getParentByChildRow(row);
+    return this._rows[aRow] = parent.getChild(aRow - parentRow - 1);
+  },
+
   _rootNode: null,
 
   /**
-   * This takes a container and recursively appends visible elements to the
-   * given array. This is used to build the visible element list (with
-   * this._visibleElements passed as the array), or portions thereof (with
-   * a separate array that is merged with the main list later).
+   * This takes a container and recursively appends our rows array per its
+   * contents.  Assumes that the rows arrays has no rows for the given
+   * container.
    *
-   * aVisibleStartIndex is the visible index of the beginning of the 'aVisible'
-   * array. When aVisible is this._visibleElements, this is 0. This is non-zero
-   * when we are building up a sub-region for insertion. Then, this is the
-   * index where the new array will be inserted into this._visibleElements.
-   * It is used to compute each node's viewIndex.
+   * @param [in] aContainer
+   *        A container result node.
+   * @param [in] aFirstChildRow
+   *        The first row at which nodes may be inserted to the row array.
+   *        In other words, that's aContainer's row + 1.
+   * @param [out] aToOpen
+   *        An array of containers to open once the build is done.
+   *
+   * @return the number of rows which were inserted.
    */
   _buildVisibleSection:
-  function PTV__buildVisibleSection(aContainer, aVisible, aToOpen, aVisibleStartIndex)
+  function PTV__buildVisibleSection(aContainer, aFirstChildRow, aToOpen)
   {
+    // There's nothing to do if the container is closed.
     if (!aContainer.containerOpen)
-      return;  // nothing to do
+      return;
+
+    // Inserting the new elements into the rows array in one shot (by
+    // Array.concat) is faster than resizing the array (by splice) on each loop
+    // iteration.
+    let cc = aContainer.childCount;
+    let newElements = new Array(cc);
+    this._rows =
+      this._rows.slice(0, aFirstChildRow).concat(newElements)
+          .concat(this._rows.slice(aFirstChildRow, this._rows.length));
+
+    if (this._isPlainContainer(aContainer))
+      return cc;
 
     const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
     const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
+    let sortingMode = this._result.sortingMode;
 
-    var cc = aContainer.childCount;
-    var sortingMode = this._result.sortingMode;
-    for (var i=0; i < cc; i++) {
-      var curChild = aContainer.getChild(i);
-      var curChildType = curChild.type;
+    let rowsInsertedCounter = 0;
+    for (let i = 0; i < cc; i++) {
+      let curChild = aContainer.getChild(i);
+      let curChildType = curChild.type;
+
+      let row = aFirstChildRow + rowsInsertedCounter;
 
       // Don't display separators when sorted.
       if (curChildType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
         if (sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
-          curChild._viewIndex = -1;
+          // Remove the element for the filtered separator.
+          // Notice that the rows array was initially resized to include all
+          // children.
+          this._rows.splice(row, 1);
           continue;
         }
       }
 
-      // Add the node to the visible-nodes array and set its viewIndex.
-      curChild._viewIndex = aVisibleStartIndex + aVisible.length;
-      aVisible.push(curChild);
+      this._rows[row] = curChild;
+      rowsInsertedCounter++;
 
       // Recursively do containers.
       if (!this._flatList &&
           curChild instanceof Ci.nsINavHistoryContainerResultNode) {
-        var resource = this._getResourceForNode(curChild);
-        var isopen = resource != null &&
-                     PlacesUIUtils.localStore.HasAssertion(resource, openLiteral,
+        let resource = this._getResourceForNode(curChild);
+        let isopen = resource != null &&
+                     PlacesUIUtils.localStore.HasAssertion(resource,
+                                                           openLiteral,
                                                            trueLiteral, true);
         if (isopen != curChild.containerOpen)
           aToOpen.push(curChild);
         else if (curChild.containerOpen && curChild.childCount > 0)
-          this._buildVisibleSection(curChild, aVisible, aToOpen, aVisibleStartIndex);
+          rowsAddedCounter += this._buildVisibleSection(curChild, aToOpen,
+                                                        row + 1);
       }
     }
+
+    return rowsInsertedCounter;
   },
 
   /**
    * This counts how many rows a node takes in the tree.  For containers it
    * will count the node itself plus any child node following it.
    */
-  _countVisibleRowsForNode: function PTV__countVisibleRowsForNode(aNode) {
-    if (aNode == this._rootNode)
-      return this._visibleElements.length;
+  _countVisibleRowsForNodeAtRow:
+  function PTV__countVisibleRowsForNodeAtRow(aNodeRow) {
+    let node = this._rows[aNodeRow];
+
+    // If it's not listed yet, we know that it's a leaf node.
+    if (node === undefined ||
+        !(node instanceof Ci.nsINavHistoryContainerResultNode))
+      return 1;
 
-    var viewIndex = aNode._viewIndex;
-    NS_ASSERT(viewIndex >= 0, "Node is not visible, no rows to count");
-    var outerLevel = aNode.indentLevel;
-    for (var i = viewIndex + 1; i < this._visibleElements.length; i++) {
-      if (this._visibleElements[i].indentLevel <= outerLevel)
-        return i - viewIndex;
+    let outerLevel = node.indentLevel;
+    for (let i = aNodeRow + 1; i < this._rows.length; i++) {
+      let rowNode = this._rows[i];
+      if (rowNode && rowNode.indentLevel <= outerLevel)
+        return i - aNodeRow;
     }
-    // this node plus its children occupy the bottom of the list
-    return this._visibleElements.length - viewIndex;
+
+    // This node plus its children take up the bottom of the list.
+    return this._rows.length - aNodeRow;
   },
 
-  /**
-   * This is called by containers when they change and we need to update
-   * everything about the container. We build a new visible section with
-   * the container as a separate object so we first know how the list
-   * changes.
-   *
-   * We also try to be smart here about redrawing the screen.
-   */
-  _refreshVisibleSection: function PTV__refreshVisibleSection(aContainer) {
-    NS_ASSERT(this._result, "Need to have a result to update");
-    if (!this._tree)
-      return;
-
-    // The root node is invisible.
-    if (aContainer != this._rootNode) {
-      if (aContainer._viewIndex < 0 ||
-          aContainer._viewIndex > this._visibleElements.length)
-        throw "Trying to expand a node that is not visible";
+  _getSelectedNodesInRange:
+  function PTV__getSelectedNodesInRange(aFirstRow, aLastRow) {
+    let selection = this.selection;
+    let rc = selection.getRangeCount();
+    if (rc == 0)
+      return [];
 
-      NS_ASSERT(this._visibleElements[aContainer._viewIndex] == aContainer,
-                "Visible index is out of sync!");
-    }
-
-    var startReplacement = aContainer._viewIndex + 1;
-    var replaceCount = this._countVisibleRowsForNode(aContainer);
+    // The visible-area borders are needed for checking whether a
+    // selected row is also visible.
+    let firstVisibleRow = this._tree.getFirstVisibleRow();
+    let lastVisibleRow = this._tree.getLastVisibleRow();
 
-    // We don't replace the container node itself so we should decrease the
-    // replaceCount by 1, unless the container is our root node, which isn't
-    // visible.
-    if (aContainer != this._rootNode)
-      replaceCount -= 1;
+    let nodesInfo = [];
+    for (let rangeIndex = 0; rangeIndex < rc; rangeIndex++) {
+      let min = { }, max = { };
+      selection.getRangeAt(rangeIndex, min, max);
 
-    // Persist selection state.
-    var previouslySelectedNodes = [];
-    var selection = this.selection;
-    var rc = selection.getRangeCount();
-    for (var rangeIndex = 0; rangeIndex < rc; rangeIndex++) {
-      var min = { }, max = { };
-      selection.getRangeAt(rangeIndex, min, max);
-      var lastIndex = Math.min(max.value, startReplacement + replaceCount -1);
       // If this range does not overlap the replaced chunk, we don't need to
       // persist the selection.
-      if (max.value < startReplacement || min.value > lastIndex)
+      if (max.value < aFirstRow || min.value > aLastRow)
         continue;
 
-      // If this range starts before the replaced chunk, we should persist from
-      // startReplacement to lastIndex.
-      var firstIndex = Math.max(min.value, startReplacement);
-      for (var nodeIndex = firstIndex; nodeIndex <= lastIndex; nodeIndex++) {
-        // Mark the node invisible if we're about to remove it,
-        // otherwise we'll try to select it later.
-        var node = this._visibleElements[nodeIndex];
-        if (nodeIndex >= startReplacement &&
-            nodeIndex < startReplacement + replaceCount)
-          node._viewIndex = -1;
-
-        previouslySelectedNodes.push(
-          { node: node, oldIndex: nodeIndex });
+      let firstRow = Math.max(min.value, aFirstRow);
+      let lastRow = Math.min(max.value, aLastRow);
+      for (let i = firstRow; i <= lastRow; i++) {
+        nodesInfo.push({
+          node: this._rows[i],
+          oldRow: i,
+          wasVisbile: i >= firstVisibleRow && i <= lastVisibleRow
+        });
       }
     }
 
-    // Building the new list will set the new elements' visible indices.
-    var newElements = [];
-    var toOpenElements = [];
-    this._buildVisibleSection(aContainer,
-                              newElements, toOpenElements, startReplacement);
+    return nodesInfo;
+  },
+
+  /**
+   * Tries to find an equivalent node for a node which was removed.  We first
+   * look for the original node, in case it was just relocated.  Then, if we
+   * that node was not found, we look for a node that has the same itemId, uri
+   * and time values.
+   *
+   * @param aUpdatedContainer
+   *        An ancestor of the node which was removed.  It does not have to be
+   *        its direct parent.
+   * @param aOldNode
+   *        The node which was removed.
+   *
+   * @return the row number of an equivalent node for aOldOne, if one was
+   *         found, -1 otherwise.
+   */
+  _getNewRowForRemovedNode:
+  function PTV__getNewRowForRemovedNode(aUpdatedContainer, aOldNode) {
+    let parent = aOldNode.parent;
+    if (parent) {
+      // If the node's parent is still set, the node is not obsolete
+      // and we should just find out its new position.  However, if the node's
+      // parent is closed, the node is invisible.
+      if (parent.containerOpen)
+        return this._getRowForNode(aOldNode, true);
+
+      return -1;
+    }
 
-    // Actually update the visible list.
-    // XXX: We can probably make this more efficient using splice through
-    // Function.apply.
-    this._visibleElements =
-      this._visibleElements.slice(0, startReplacement).concat(newElements)
-          .concat(this._visibleElements.slice(startReplacement + replaceCount,
-                                              this._visibleElements.length));
+    // There's a broken edge case here.
+    // If a visit appears in two queries, and the second one was
+    // the old node, we'll select the first one after refresh.  There's
+    // nothing we could do about that, because aOldNode.parent is
+    // gone by the time invalidateContainer is called.
+    let newNode = aUpdatedContainer.findNodeByDetails(aOldNode.uri,
+                                                      aOldNode.time,
+                                                      aOldNode.itemId,
+                                                      true);
+    if (!newNode)
+      return -1;
+
+    return this._getRowForNode(newNode, true);
+  },
 
-    // If the new area has a different size, we'll have to renumber the
-    // elements following the area.
-    if (replaceCount != newElements.length) {
-      for (var i = startReplacement + newElements.length;
-           i < this._visibleElements.length; i++) {
-        this._visibleElements[i]._viewIndex = i;
+  /**
+   * Restores a given selection state as near as possible to the original
+   * selection state.
+   *
+   * @param aNodesInfo
+   *        The persisted selection state as returned by
+   *        _getSelectedNodesInRange.
+   * @param aUpdatedContainer
+   *        The container which was updated.
+   */
+  _restoreSelection:
+  function PTV__restoreSelection(aNodesInfo, aUpdatedContainer) {
+    if (aNodesInfo.length == 0)
+      return;
+
+    let selection = this.selection;
+
+    // Attempt to ensure that previously-visible selection will be visible
+    // if it's re-selected.  However, we can only ensure that for one row.
+    let scrollToRow = -1;
+    for (let i = 0; i < aNodesInfo.length; i++) {
+      let nodeInfo = aNodesInfo[i];
+      let row = this._getNewRowForRemovedNode(aUpdatedContainer,
+                                              aNodesInfo[i].node);
+      // Select the found node, if any.
+      if (row != -1) {
+        selection.rangedSelect(row, row, true);
+        if (nodeInfo.wasVisible && scrollToRow == -1)
+          scrollToRow = row;
       }
     }
 
-    // now update the number of elements
-    selection.selectEventsSuppressed = true;
-    this._tree.beginUpdateBatch();
-
-    if (replaceCount)
-      this._tree.rowCountChanged(startReplacement, -replaceCount);
-    if (newElements.length)
-      this._tree.rowCountChanged(startReplacement, newElements.length);
-
-    if (!this._flatList) {
-      // now, open any containers that were persisted
-      for (var i = 0; i < toOpenElements.length; i++) {
-        var item = toOpenElements[i];
-        var parent = item.parent;
-        // avoid recursively opening containers
-        while (parent) {
-          if (parent.uri == item.uri)
-            break;
-          parent = parent.parent;
-        }
-        // if we don't have a parent, we made it all the way to the root
-        // and didn't find a match, so we can open our item
-        if (!parent && !item.containerOpen)
-          item.containerOpen = true;
-      }
+    // If only one node was previously selected and there's no selection now,
+    // select the node at its old row, if any.
+    if (aNodesInfo.length == 1 && selection.getRangeCount() == 0) {
+      let row = Math.min(aNodesInfo[0].oldRow, this._rows.length - 1);
+      selection.rangedSelect(row, row, true);
+      if (aNodesInfo[0].wasVisible && scrollToRow == -1)
+        scrollToRow = aNodesInfo[0].oldRow;
     }
 
-    this._tree.endUpdateBatch();
-
-    // restore selection
-    if (previouslySelectedNodes.length > 0) {
-      for (var i = 0; i < previouslySelectedNodes.length; i++) {
-        var nodeInfo = previouslySelectedNodes[i];
-        var index = nodeInfo.node._viewIndex;
-
-        // If the nodes under the invalidated container were preserved, we can
-        // just use viewIndex.
-        if (index == -1) {
-          // Otherwise, try to find an equal node.
-          var itemId = PlacesUtils.getConcreteItemId(nodeInfo.node);
-          if (itemId != 1) {
-            // Search by itemId.
-            for (var j = 0; j < newElements.length && index == -1; j++) {
-              if (PlacesUtils.getConcreteItemId(newElements[j]) == itemId)
-                index = newElements[j]._viewIndex;
-            }
-          }
-          else {
-            // Search by uri.
-            var uri = nodeInfo.node.uri;
-            if (uri) {
-              for (var j = 0; j < newElements.length && index == -1; j++) {
-                if (newElements[j].uri == uri)
-                  index = newElements[j]._viewIndex;
-              }
-            }
-          }
-        }
-
-        // Select the found node, if any.
-        if (index != -1)
-          selection.rangedSelect(index, index, true);
-      }
-
-      // If only one node was previously selected and there's no selection now,
-      // select the node at its old viewIndex, if any.
-      if (previouslySelectedNodes.length == 1 &&
-          selection.getRangeCount() == 0 &&
-          this._visibleElements.length > previouslySelectedNodes[0].oldIndex) {
-        selection.rangedSelect(previouslySelectedNodes[0].oldIndex,
-                               previouslySelectedNodes[0].oldIndex, true);
-      }
-    }
-    selection.selectEventsSuppressed = false;
+    if (scrollToRow != -1)
+      this._tree.ensureRowIsVisible(scrollToRow);
   },
 
   _convertPRTimeToString: function PTV__convertPRTimeToString(aTime) {
     const MS_PER_MINUTE = 60000;
     const MS_PER_DAY = 86400000;
     let timeMs = aTime / 1000; // PRTime is in microseconds
 
     // Date is calculated starting from midnight, so the modulo with a day are
@@ -367,17 +516,17 @@ PlacesTreeView.prototype = {
   COLUMN_TYPE_VISITCOUNT: 4,
   COLUMN_TYPE_KEYWORD: 5,
   COLUMN_TYPE_DESCRIPTION: 6,
   COLUMN_TYPE_DATEADDED: 7,
   COLUMN_TYPE_LASTMODIFIED: 8,
   COLUMN_TYPE_TAGS: 9,
 
   _getColumnType: function PTV__getColumnType(aColumn) {
-    var columnType = aColumn.element.getAttribute("anonid") || aColumn.id;
+    let columnType = aColumn.element.getAttribute("anonid") || aColumn.id;
 
     switch (columnType) {
       case "title":
         return this.COLUMN_TYPE_TITLE;
       case "url":
         return this.COLUMN_TYPE_URI;
       case "date":
         return this.COLUMN_TYPE_DATE;
@@ -439,237 +588,218 @@ PlacesTreeView.prototype = {
       case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_DESCENDING:
         return [this.COLUMN_TYPE_TAGS, true];
     }
     return [this.COLUMN_TYPE_UNKNOWN, false];
   },
 
   // nsINavHistoryResultViewer
   nodeInserted: function PTV_nodeInserted(aParentNode, aNode, aNewIndex) {
-    if (!this._tree)
+    NS_ASSERT(this._result, "Got a notification but have no result!");
+    if (!this._tree || !this._result)
       return;
-    if (!this._result)
-      throw Cr.NS_ERROR_UNEXPECTED;
 
+    // Bail out for hidden separators.
     if (PlacesUtils.nodeIsSeparator(aNode) &&
-        this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
-      aNode._viewIndex = -1;
+        this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
       return;
+
+    let parentRow;
+    if (aParentNode != this._rootNode) {
+      parentRow = this._getRowForNode(aParentNode);
+
+      // Update parent when inserting the first item, since twisty has changed.
+      if (aParentNode.childCount == 1)
+        this._tree.invalidateRow(parentRow);
     }
 
-    // Update parent when inserting the first item, since twisty may
-    // have changed.
-    if (aParentNode.childCount == 1)
-      this._tree.invalidateRow(aParentNode._viewIndex);
-
-    // compute the new view index of the item
-    var newViewIndex = -1;
-    if (aNewIndex == 0) {
-      // item is the first thing in our child list, it takes our index +1. Note
-      // that this computation still works if the parent is an invisible root
-      // node, because root_index + 1 = -1 + 1 = 0
-      newViewIndex = aParentNode._viewIndex + 1;
+    // Compute the new row number of the node.
+    let row = -1;
+    if (aNewIndex == 0 || this._isPlainContainer(aParentNode)) {
+      // We don't need to worry about sub hierarchies of the parent node
+      // if it's a plain container, or if the new node is its first child.
+      if (aParentNode == this._rootNode)
+        row = aNewIndex;
+      else
+        row = parentRow + aNewIndex + 1;
     }
     else {
       // Here, we try to find the next visible element in the child list so we
-      // can set the new visible index to be right before that. Note that we
-      // have to search DOWN instead of up, because some siblings could have
+      // can set the new visible index to be right before that.  Note that we
+      // have to search down instead of up, because some siblings could have
       // children themselves that would be in the way.
-      for (var i = aNewIndex + 1; i < aParentNode.childCount; i++) {
-        var viewIndex = aParentNode.getChild(i)._viewIndex;
-        if (viewIndex >= 0) {
-          // the view indices of subsequent children have not been shifted so
-          // the next item will have what should be our index
-          newViewIndex = viewIndex;
+      let cc = aParentNode.childCount;
+      let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) &&
+        this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
+      for (let i = aNewIndex + 1; i < cc; i++) {
+        let node = aParentNode.getChild(i);
+        if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) {
+          // The children have not been shifted so the next item will have what
+          // should be our index.
+          row = this._getRowForNode(node, false, parentRow, i);
           break;
         }
       }
-      if (newViewIndex < 0) {
-        // At the end of the child list without finding a visible sibling: This
+      if (row < 0) {
+        // At the end of the child list without finding a visible sibling. This
         // is a little harder because we don't know how many rows the last item
         // in our list takes up (it could be a container with many children).
-        var prevChild = aParentNode.getChild(aNewIndex - 1);
-        newViewIndex = prevChild._viewIndex + this._countVisibleRowsForNode(prevChild);
+        let prevChild = aParentNode.getChild(aNewIndex - 1);
+        let prevIndex = this._getRowForNode(prevChild, false, parentRow,
+                                            aNewIndex - 1);
+        row = prevIndex + this._countVisibleRowsForNodeAtRow(prevIndex);
       }
     }
 
-    aNode._viewIndex = newViewIndex;
-    this._visibleElements.splice(newViewIndex, 0, aNode);
-    for (var i = newViewIndex + 1;
-         i < this._visibleElements.length; i++) {
-      this._visibleElements[i]._viewIndex = i;
-    }
-    this._tree.rowCountChanged(newViewIndex, 1);
+    this._rows.splice(row, 0, aNode);
+    this._tree.rowCountChanged(row, 1);
 
     if (PlacesUtils.nodeIsContainer(aNode) && asContainer(aNode).containerOpen)
-      this._refreshVisibleSection(aNode);
-  },
-
-  // This is used in nodeRemoved and nodeMoved to fix viewIndex values.
-  // Throws if the node has an invalid viewIndex.
-  _fixViewIndexOnRemove: function PTV_fixViewIndexOnRemove(aNode,
-                                                           aParentNode) {
-    var oldViewIndex = aNode._viewIndex;
-    // this may have been a container, in which case it has a lot of rows
-    var count = this._countVisibleRowsForNode(aNode);
-
-    if (oldViewIndex > this._visibleElements.length)
-      throw("Trying to remove a node with an invalid viewIndex");
-
-    this._visibleElements.splice(oldViewIndex, count);
-    for (var i = oldViewIndex; i < this._visibleElements.length; i++)
-      this._visibleElements[i]._viewIndex = i;
-
-    this._tree.rowCountChanged(oldViewIndex, -count);
-
-    // redraw parent because twisty may have changed
-    if (!aParentNode.hasChildren)
-      this._tree.invalidateRow(aParentNode._viewIndex);
+      this.invalidateContainer(aNode);
   },
 
   /**
    * THIS FUNCTION DOES NOT HANDLE cases where a collapsed node is being
    * removed but the node it is collapsed with is not being removed (this then
    * just swap out the removee with its collapsing partner). The only time
    * when we really remove things is when deleting URIs, which will apply to
    * all collapsees. This function is called sometimes when resorting items.
    * However, we won't do this when sorted by date because dates will never
    * change for visits, and date sorting is the only time things are collapsed.
    */
   nodeRemoved: function PTV_nodeRemoved(aParentNode, aNode, aOldIndex) {
     NS_ASSERT(this._result, "Got a notification but have no result!");
-    if (!this._tree)
-      return; // nothing to do
+    if (!this._tree || !this._result)
+      return;
+
+    // XXX bug 517701: We don't know what to do when the root node is removed.
+    if (aNode == this._rootNode)
+      throw Cr.NS_ERROR_NOT_IMPLEMENTED;
 
-    var oldViewIndex = aNode._viewIndex;
-    if (oldViewIndex < 0) {
-      // There's nothing to do if the node was already invisible.
+    // Bail out for hidden separators.
+    if (PlacesUtils.nodeIsSeparator(aNode) &&
+        this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
       return;
-    }
+
+    let oldRow = this._getRowForNode(aNode, true);
+    if (oldRow < 0)
+      throw Cr.NS_ERROR_UNEXPECTED;
 
     // If the node was exclusively selected, the node next to it will be
     // selected.
-    var selectNext = false;
-    var selection = this.selection;
+    let selectNext = false;
+    let selection = this.selection;
     if (selection.getRangeCount() == 1) {
-      var min = { }, max = { };
+      let min = { }, max = { };
       selection.getRangeAt(0, min, max);
       if (min.value == max.value &&
           this.nodeForTreeIndex(min.value) == aNode)
         selectNext = true;
     }
 
-    // Remove the node and fix viewIndex values.
-    this._fixViewIndexOnRemove(aNode, aParentNode);
+    // Remove the node and its children, if any.
+    let count = this._countVisibleRowsForNodeAtRow(oldRow);
+    this._rows.splice(oldRow, count);
+    this._tree.rowCountChanged(oldRow, -count);
+
+    // Redraw the parent if its twisty state has changed.
+    if (aParentNode != this._rootNode && !aParentNode.hasChildren) {
+      let parentRow = oldRow - 1;
+      this._tree.invalidateRow(parentRow);
+    }
 
     // Restore selection if the node was exclusively selected.
     if (!selectNext)
       return;
 
-    // Restore selection
-    if (this._visibleElements.length > oldViewIndex)
-      selection.rangedSelect(oldViewIndex, oldViewIndex, true);    
-    else if (this._visibleElements.length > 0) {
-      // if we removed the last child, we select the new last child if exists
-      selection.rangedSelect(this._visibleElements.length - 1,
-                             this._visibleElements.length - 1, true);
-    }
+    // Restore selection.
+    let rowToSelect = Math.min(oldRow,  this._rows.length - 1);
+    this.selection.rangedSelect(rowToSelect, rowToSelect, true);
   },
 
-  /**
-   * Be careful, aOldIndex and aNewIndex specify the index in the
-   * corresponding parent nodes, not the visible indexes.
-   */
   nodeMoved:
   function PTV_nodeMoved(aNode, aOldParent, aOldIndex, aNewParent, aNewIndex) {
     NS_ASSERT(this._result, "Got a notification but have no result!");
-    if (!this._tree)
-      return; // nothing to do
+    if (!this._tree || !this._result)
+      return;
 
-    var oldViewIndex = aNode._viewIndex;
-    if (oldViewIndex < 0) {
-      // There's nothing to do if the node was already invisible.
+    // Bail out for hidden separators.
+    if (PlacesUtils.nodeIsSeparator(aNode) &&
+        this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
       return;
-    }
+
+    let oldRow = this._getRowForNode(aNode, true);
 
-    // This may have been a container, in which case it has a lot of rows.
-    var count = this._countVisibleRowsForNode(aNode);
+    // If this node is a container it could take up more than one row.
+    let count = this._countVisibleRowsForNodeAtRow(oldRow);
 
     // Persist selection state.
-    var nodesToSelect = [];
-    var selection = this.selection;
-    var rc = selection.getRangeCount();
-    for (var rangeIndex = 0; rangeIndex < rc; rangeIndex++) {
-      var min = { }, max = { };
-      selection.getRangeAt(rangeIndex, min, max);
-      var lastIndex = Math.min(max.value, oldViewIndex + count -1);
-      if (min.value < oldViewIndex || min.value > lastIndex)
-        continue;
+    let nodesToReselect =
+      this._getSelectedNodesInRange(oldRow, oldRow + count);
+    if (nodesToReselect.length > 0)
+      this.selection.selectEventsSuppressed = true;
 
-      for (var nodeIndex = min.value; nodeIndex <= lastIndex; nodeIndex++)
-        nodesToSelect.push(this._visibleElements[nodeIndex]);
+    // Redraw the parent if its twisty state has changed.
+    if (aOldParent != this._rootNode && !aOldParent.hasChildren) {
+      let parentRow = oldRow - 1;
+      this._tree.invalidateRow(parentRow);
     }
-    if (nodesToSelect.length > 0)
-      selection.selectEventsSuppressed = true;
 
-    // Remove node from the old position.
-    this._fixViewIndexOnRemove(aNode, aOldParent);
+    // Remove node and its children, if any, from the old position.
+    this._rows.splice(oldRow, count);
+    this._tree.rowCountChanged(oldRow, -count);
 
     // Insert the node into the new position.
     this.nodeInserted(aNewParent, aNode, aNewIndex);
 
     // Restore selection.
-    if (nodesToSelect.length > 0) {
-      for (var i = 0; i < nodesToSelect.length; i++) {
-        var node = nodesToSelect[i];
-        var index = node._viewIndex;
-        selection.rangedSelect(index, index, true);
-      }
-      selection.selectEventsSuppressed = false;
+    if (nodesToReselect.length > 0) {
+      this._restoreSelection(nodesToReselect, aNewParent);
+      this.selection.selectEventsSuppressed = false;
     }
   },
 
   /**
    * Be careful, the parameter 'aIndex' here specifies the node's index in the
    * parent node, not the visible index.
    */
   nodeReplaced:
   function PTV_nodeReplaced(aParentNode, aOldNode, aNewNode, aIndexDoNotUse) {
-    if (!this._tree)
+    NS_ASSERT(this._result, "Got a notification but have no result!");
+    if (!this._tree || !this._result)
       return;
 
-    var viewIndex = aOldNode._viewIndex;
-    aNewNode._viewIndex = viewIndex;
-    if (viewIndex >= 0 &&
-        viewIndex < this._visibleElements.length) {
-      this._visibleElements[viewIndex] = aNewNode;
+    // Nothing to do if the replaced node was not set.
+    let row = this._getRowForNode(aOldNode);
+    if (row != -1) {
+      this._rows[row] = aNewNode;
+      this._tree.invalidateRow(row);
     }
-    aOldNode._viewIndex = -1;
-    this._tree.invalidateRow(viewIndex);
   },
 
   _invalidateCellValue: function PTV__invalidateCellValue(aNode,
                                                           aColumnType) {
     NS_ASSERT(this._result, "Got a notification but have no result!");
-    let viewIndex = aNode._viewIndex;
-    if (viewIndex == -1) // invisible
+    if (!this._tree || !this._result)
+      return;
+
+    let row = this._getRowForNode(aNode);
+    if (row == -1)
       return;
 
-    if (this._tree) {
-      let column = this._findColumnByType(aColumnType);
-      if (column && !column.element.hidden)
-        this._tree.invalidateCell(viewIndex, column);
+    let column = this._findColumnByType(aColumnType);
+    if (column && !column.element.hidden)
+      this._tree.invalidateCell(row, column);
 
-      // Last modified time is altered for almost all node changes.
-      if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) {
-        let lastModifiedColumn =
-          this._findColumnByType(this.COLUMN_TYPE_LASTMODIFIED);
-        if (lastModifiedColumn && !lastModifiedColumn.hidden)
-          this._tree.invalidateCell(viewIndex, lastModifiedColumn);
-      }
+    // Last modified time is altered for almost all node changes.
+    if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) {
+      let lastModifiedColumn =
+        this._findColumnByType(this.COLUMN_TYPE_LASTMODIFIED);
+      if (lastModifiedColumn && !lastModifiedColumn.hidden)
+        this._tree.invalidateCell(row, lastModifiedColumn);
     }
   },
 
   nodeTitleChanged: function PTV_nodeTitleChanged(aNode, aNewTitle) {
     this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
   },
 
   nodeURIChanged: function PTV_nodeURIChanged(aNode, aNewURI) {
@@ -712,80 +842,163 @@ PlacesTreeView.prototype = {
   containerOpened: function PTV_containerOpened(aNode) {
     this.invalidateContainer(aNode);
   },
 
   containerClosed: function PTV_containerClosed(aNode) {
     this.invalidateContainer(aNode);
   },
 
-  invalidateContainer: function PTV_invalidateContainer(aNode) {
-    NS_ASSERT(this._result, "Got a notification but have no result!");
+  invalidateContainer: function PTV_invalidateContainer(aContainer) {
+    NS_ASSERT(this._result, "Need to have a result to update");
     if (!this._tree)
-      return; // nothing to do, container is not visible
+      return;
+
+    let startReplacement, replaceCount;
+    if (aContainer == this._rootNode) {
+      startReplacement = 0;
+      replaceCount = this._rows.length;
+
+      // If the root node is now closed, the tree is empty.
+      if (!this._rootNode.containerOpen) {
+        this._rows = [];
+        if (replaceCount)
+          this._tree.rowCountChanged(startReplacement, -replaceCount);
+
+        return;
+      }
+    }
+    else {
+      // Update the twisty state.
+      let row = this._getRowForNode(aContainer);
+      this._tree.invalidateRow(row);
+
+      // We don't replace the container node itself, so we should decrease the
+      // replaceCount by 1.
+      startReplacement = row + 1;
+      replaceCount = this._countVisibleRowsForNodeAtRow(row) - 1;
+    }
+
+    // Persist selection state.
+    let nodesToReselect =
+      this._getSelectedNodesInRange(startReplacement,
+                                    startReplacement + replaceCount);
+
+    // Now update the number of elements.
+    this.selection.selectEventsSuppressed = true;
+
+    // First remove the old elements
+    this._rows.splice(startReplacement, replaceCount);
+
+    // If the container is now closed, we're done.
+    if (!aContainer.containerOpen) {
+      let oldSelectionCount = this.selection.count;
+      if (replaceCount)
+        this._tree.rowCountChanged(startReplacement, -replaceCount);
 
-    if (aNode._viewIndex >= this._visibleElements.length) {
-      // be paranoid about visible indices since others can change it
-      throw Cr.NS_ERROR_UNEXPECTED;
+      // Select the row next to the closed container if any of its
+      // children were selected, and nothing else is selected.
+      if (nodesToReselect.length > 0 &&
+          nodesToReselect.length == oldSelectionCount) {
+        this.selection.rangedSelect(startReplacement, startReplacement, true);
+        this._tree.ensureRowIsVisible(startReplacement);
+      }
+
+      this.selection.selectEventsSuppressed = false;
+      return;
     }
-    this._refreshVisibleSection(aNode);
+
+    // Otherwise, start a batch first.
+    this._tree.beginUpdateBatch();
+    if (replaceCount)
+      this._tree.rowCountChanged(startReplacement, -replaceCount);
+
+    let toOpenElements = [];
+    let elementsAddedCount = this._buildVisibleSection(aContainer,
+                                                       startReplacement,
+                                                       toOpenElements);
+    if (elementsAddedCount)
+      this._tree.rowCountChanged(startReplacement, elementsAddedCount);
+
+    if (!this._flatList) {
+      // Now, open any containers that were persisted.
+      for (let i = 0; i < toOpenElements.length; i++) {
+        let item = toOpenElements[i];
+        let parent = item.parent;
+
+        // Avoid recursively opening containers.
+        while (parent) {
+          if (parent.uri == item.uri)
+            break;
+          parent = parent.parent;
+        }
+
+        // If we don't have a parent, we made it all the way to the root
+        // and didn't find a match, so we can open our item.
+        if (!parent && !item.containerOpen)
+          item.containerOpen = true;
+      }
+    }
+
+    this._tree.endUpdateBatch();
+
+    // Restore selection.
+    this._restoreSelection(nodesToReselect, aContainer);
+    this.selection.selectEventsSuppressed = false;
   },
 
   _columns: [],
   _findColumnByType: function PTV__findColumnByType(aColumnType) {
     if (this._columns[aColumnType])
       return this._columns[aColumnType];
 
-    var columns = this._tree.columns;
-    var colCount = columns.count;
-    for (var i = 0; i < colCount; i++) {
+    let columns = this._tree.columns;
+    let colCount = columns.count;
+    for (let i = 0; i < colCount; i++) {
       let column = columns.getColumnAt(i);
       let columnType = this._getColumnType(column);
       this._columns[columnType] = column;
       if (columnType == aColumnType)
         return column;
     }
 
     // That's completely valid.  Most of our trees actually include just the
     // title column.
     return null;
   },
 
   sortingChanged: function PTV__sortingChanged(aSortingMode) {
     if (!this._tree || !this._result)
       return;
 
-    // depending on the sort mode, certain commands may be disabled
+    // Depending on the sort mode, certain commands may be disabled.
     window.updateCommands("sort");
 
-    var columns = this._tree.columns;
+    let columns = this._tree.columns;
 
-    // clear old sorting indicator
-    var sortedColumn = columns.getSortedColumn();
+    // Clear old sorting indicator.
+    let sortedColumn = columns.getSortedColumn();
     if (sortedColumn)
       sortedColumn.element.removeAttribute("sortDirection");
 
-    // set new sorting indicator by looking through all columns for ours
+    // Set new sorting indicator by looking through all columns for ours.
     if (aSortingMode == Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
       return;
 
-    var [desiredColumn, desiredIsDescending] =
+    let [desiredColumn, desiredIsDescending] =
       this._sortTypeToColumnType(aSortingMode);
-    var colCount = columns.count;
-    var column = this._findColumnByType(desiredColumn);
+    let colCount = columns.count;
+    let column = this._findColumnByType(desiredColumn);
     if (column) {
       let sortDir = desiredIsDescending ? "descending" : "ascending";
       column.element.setAttribute("sortDirection", sortDir);
     }
   },
 
-  get result() {
-    return this._result;
-  },
-
+  get result() this._result,
   set result(val) {
     // Some methods (e.g. getURLsFromContainer) temporarily null out the
     // viewer when they do temporary changes to the view, this does _not_
     // call setResult(null), but then, we're called again with the result
     // object which is already set for this viewer. At that point,
     // we should do nothing.
     if (this._result != val) {
       if (this._result)
@@ -797,76 +1010,67 @@ PlacesTreeView.prototype = {
       // If the tree is not set yet, setTree will call finishInit.
       if (this._tree && val)
         this._finishInit();
     }
     return val;
   },
 
   nodeForTreeIndex: function PTV_nodeForTreeIndex(aIndex) {
-    if (aIndex > this._visibleElements.length)
+    if (aIndex > this._rows.length)
       throw Cr.NS_ERROR_INVALID_ARG;
 
-    return this._visibleElements[aIndex];
+    return this._getNodeForRow(aIndex);
   },
 
   treeIndexForNode: function PTV_treeNodeForIndex(aNode) {
-    var viewIndex = aNode._viewIndex;
-    if (viewIndex < 0)
-      return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE;
+    // The API allows passing invisible nodes.
+    try {
+      return this._getRowForNode(aNode, true);
+    }
+    catch(ex) { }
 
-    NS_ASSERT(this._visibleElements[viewIndex] == aNode,
-              "Node's visible index and array out of sync");
-    return viewIndex;
+    return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE;
   },
 
   _getResourceForNode: function PTV_getResourceForNode(aNode)
   {
-    var uri = aNode.uri;
+    let uri = aNode.uri;
     NS_ASSERT(uri, "if there is no uri, we can't persist the open state");
     return uri ? PlacesUIUtils.RDF.GetResource(uri) : null;
   },
 
   // nsITreeView
-  get rowCount() {
-    return this._visibleElements.length;
-  },
-
-  get selection() {
-    return this._selection;
-  },
+  get rowCount() this._rows.length,
+  get selection() this._selection,
+  set selection(val) this._selection = val,
 
-  set selection(val) {
-    return this._selection = val;
-  },
-
-  getRowProperties: function PTV_getRowProperties(aRow, aProperties) { },
+  getRowProperties: function() { },
 
-  getCellProperties: function PTV_getCellProperties(aRow, aColumn, aProperties) {
-    this._ensureValidRow(aRow);
-
+  getCellProperties:
+  function PTV_getCellProperties(aRow, aColumn, aProperties) {
     // for anonid-trees, we need to add the column-type manually
-    var columnType = aColumn.element.getAttribute("anonid");
+    let columnType = aColumn.element.getAttribute("anonid");
     if (columnType)
       aProperties.AppendElement(this._getAtomFor(columnType));
     else
-      var columnType = aColumn.id;
+      columnType = aColumn.id;
 
     // Set the "ltr" property on url cells
     if (columnType == "url")
       aProperties.AppendElement(this._getAtomFor("ltr"));
 
     if (columnType != "title")
       return;
 
-    var node = this._visibleElements[aRow];
+    let node = this._getNodeForRow(aRow);
     if (!node._cellProperties) {
       let properties = new Array();
-      var itemId = node.itemId;
-      var nodeType = node.type;
+      let itemId = node.itemId;
+      let nodeType = node.type;
       if (PlacesUtils.containerTypes.indexOf(nodeType) != -1) {
         if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
           properties.push(this._getAtomFor("query"));
           if (PlacesUtils.nodeIsTagQuery(node))
             properties.push(this._getAtomFor("tagContainer"));
           else if (PlacesUtils.nodeIsDay(node))
             properties.push(this._getAtomFor("dayContainer"));
           else if (PlacesUtils.nodeIsHost(node))
@@ -874,226 +1078,234 @@ PlacesTreeView.prototype = {
         }
         else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER ||
                  nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) {
           if (PlacesUtils.nodeIsLivemarkContainer(node))
             properties.push(this._getAtomFor("livemark"));
         }
 
         if (itemId != -1) {
-          var queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId);
+          let queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId);
           if (queryName)
             properties.push(this._getAtomFor("OrganizerQuery_" + queryName));
         }
       }
       else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
         properties.push(this._getAtomFor("separator"));
       else if (PlacesUtils.nodeIsURI(node)) {
         properties.push(this._getAtomFor(PlacesUIUtils.guessUrlSchemeForUI(node.uri)));
         if (itemId != -1) {
           if (PlacesUtils.nodeIsLivemarkContainer(node.parent))
             properties.push(this._getAtomFor("livemarkItem"));
         }
       }
 
       node._cellProperties = properties;
     }
-    for (var i = 0; i < node._cellProperties.length; i++)
+    for (let i = 0; i < node._cellProperties.length; i++)
       aProperties.AppendElement(node._cellProperties[i]);
   },
 
   getColumnProperties: function(aColumn, aProperties) { },
 
   isContainer: function PTV_isContainer(aRow) {
-    this._ensureValidRow(aRow);
+    // Only leaf nodes aren't listed in the rows array.
+    let node = this._rows[aRow];
+    if (!node)
+      return false;
 
-    var node = this._visibleElements[aRow];
     if (PlacesUtils.nodeIsContainer(node)) {
       // Flat-lists may ignore expandQueries and other query options when
       // they are asked to open a container.
       if (this._flatList)
         return true;
 
       // treat non-expandable childless queries as non-containers
       if (PlacesUtils.nodeIsQuery(node)) {
-        var parent = node.parent;
+        let parent = node.parent;
         if ((PlacesUtils.nodeIsQuery(parent) ||
              PlacesUtils.nodeIsFolder(parent)) &&
             !node.hasChildren)
           return asQuery(parent).queryOptions.expandQueries;
       }
       return true;
     }
     return false;
   },
 
   isContainerOpen: function PTV_isContainerOpen(aRow) {
     if (this._flatList)
       return false;
 
-    this._ensureValidRow(aRow);
-    return this._visibleElements[aRow].containerOpen;
+    // All containers are listed in the rows array.
+    return this._rows[aRow].containerOpen;
   },
 
   isContainerEmpty: function PTV_isContainerEmpty(aRow) {
     if (this._flatList)
       return true;
 
-    this._ensureValidRow(aRow);
-    return !this._visibleElements[aRow].hasChildren;
+    // All containers are listed in the rows array.
+    return !this._rows[aRow].hasChildren;
   },
 
   isSeparator: function PTV_isSeparator(aRow) {
-    this._ensureValidRow(aRow);
-    return PlacesUtils.nodeIsSeparator(this._visibleElements[aRow]);
+    // All separators are listed in the rows array.
+    let node = this._rows[aRow];
+    return node && PlacesUtils.nodeIsSeparator(node);
   },
 
   isSorted: function PTV_isSorted() {
     return this._result.sortingMode !=
            Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
   },
 
-  canDrop: function PTV_canDrop(aRow, aOrientation) {
+  canDrop: function PTV_canDrop(aRow, aOrientation, aDataTransfer) {
     if (!this._result)
       throw Cr.NS_ERROR_UNEXPECTED;
 
-    // drop position into a sorted treeview would be wrong
+    // Drop position into a sorted treeview would be wrong.
     if (this.isSorted())
       return false;
 
-    var ip = this._getInsertionPoint(aRow, aOrientation);
-    return ip && PlacesControllerDragHelper.canDrop(ip);
+    let ip = this._getInsertionPoint(aRow, aOrientation);
+    return ip && PlacesControllerDragHelper.canDrop(ip, aDataTransfer);
   },
 
   _getInsertionPoint: function PTV__getInsertionPoint(index, orientation) {
-    var container = this._result.root;
-    var dropNearItemId = -1;
+    let container = this._result.root;
+    let dropNearItemId = -1;
     // When there's no selection, assume the container is the container
     // the view is populated from (i.e. the result's itemId).
     if (index != -1) {
-      var lastSelected = this.nodeForTreeIndex(index);
+      let lastSelected = this.nodeForTreeIndex(index);
       if (this.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
         // If the last selected item is an open container, append _into_
-        // it, rather than insert adjacent to it. 
+        // it, rather than insert adjacent to it.
         container = lastSelected;
         index = -1;
       }
       else if (lastSelected.containerOpen &&
                orientation == Ci.nsITreeView.DROP_AFTER &&
                lastSelected.hasChildren) {
         // If the last selected node is an open container and the user is
         // trying to drag into it as a first node, really insert into it.
         container = lastSelected;
         orientation = Ci.nsITreeView.DROP_ON;
         index = 0;
       }
       else {
-        // Use the last-selected node's container unless the root node
-        // is selected, in which case we use the root node itself as the
-        // insertion point.
-        container = lastSelected.parent || container;
+        // Use the last-selected node's container.
+        container = lastSelected.parent;
+        
+        // During its Drag & Drop operation, the tree code closes-and-opens
+        // containers very often (part of the XUL "spring-loaded folders"
+        // implementation).  And in certain cases, we may reach a closed
+        // container here.  However, we can simply bail out when this happens,
+        // because we would then be back here in less than a millisecond, when
+        // the container had been reopened.
+        if (!container || !container.containerOpen)
+          return null;
 
-        // avoid the potentially expensive call to getIndexOfNode() 
-        // if we know this container doesn't allow insertion
+        // Avoid the potentially expensive call to getChildIndex
+        // if we know this container doesn't allow insertion.
         if (PlacesControllerDragHelper.disallowInsertion(container))
           return null;
 
-        var queryOptions = asQuery(this._result.root).queryOptions;
+        let queryOptions = asQuery(this._result.root).queryOptions;
         if (queryOptions.sortingMode !=
               Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
-          // If we are within a sorted view, insert at the ends
+          // If we are within a sorted view, insert at the end.
           index = -1;
         }
         else if (queryOptions.excludeItems ||
                  queryOptions.excludeQueries ||
                  queryOptions.excludeReadOnlyFolders) {
           // Some item may be invisible, insert near last selected one.
           // We don't replace index here to avoid requests to the db,
           // instead it will be calculated later by the controller.
           index = -1;
           dropNearItemId = lastSelected.itemId;
         }
         else {
-          var lsi = PlacesUtils.getIndexOfNode(lastSelected);
+          let lsi = container.getChildIndex(lastSelected);
           index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
         }
       }
     }
 
     if (PlacesControllerDragHelper.disallowInsertion(container))
       return null;
 
     return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
                               index, orientation,
                               PlacesUtils.nodeIsTagQuery(container),
                               dropNearItemId);
   },
 
-  drop: function PTV_drop(aRow, aOrientation) {
-    // We are responsible for translating the |index| and |orientation| 
-    // parameters into a container id and index within the container, 
+  drop: function PTV_drop(aRow, aOrientation, aDataTransfer) {
+    // We are responsible for translating the |index| and |orientation|
+    // parameters into a container id and index within the container,
     // since this information is specific to the tree view.
-    var ip = this._getInsertionPoint(aRow, aOrientation);
-    if (!ip)
-      return;
-    PlacesControllerDragHelper.onDrop(ip);
+    let ip = this._getInsertionPoint(aRow, aOrientation);
+    if (ip)
+      PlacesControllerDragHelper.onDrop(ip, aDataTransfer);
+
+    PlacesControllerDragHelper.currentDropTarget = null;
   },
 
   getParentIndex: function PTV_getParentIndex(aRow) {
-    this._ensureValidRow(aRow);
-    var parent = this._visibleElements[aRow].parent;
-    if (!parent || parent._viewIndex < 0)
-      return -1;
-
-    return parent._viewIndex;
+    let [parentNode, parentRow] = this._getParentByChildRow(aRow);
+    return parentRow;
   },
 
   hasNextSibling: function PTV_hasNextSibling(aRow, aAfterIndex) {
-    this._ensureValidRow(aRow);
-    if (aRow == this._visibleElements.length -1) {
-      // this is the last thing in the list -> no next sibling
+    if (aRow == this._rows.length - 1) {
+      // The last row has no sibling.
       return false;
     }
 
-    var thisLevel = this._visibleElements[aRow].indentLevel;
-    for (var i = aAfterIndex + 1; i < this._visibleElements.length; ++i) {
-      var nextLevel = this._visibleElements[i].indentLevel;
+    let node = this._rows[aRow];
+    if (node === undefined || this._isPlainContainer(node.parent)) {
+      // The node is a child of a plain container.
+      // If the next row is either unset or has the same parent,
+      // it's a sibling.
+      let nextNode = this._rows[aRow + 1];
+      return (nextNode == undefined || nextNode.parent == node.parent);
+    }
+
+    let thisLevel = node.indentLevel;
+    for (let i = aAfterIndex + 1; i < this._rows.length; ++i) {
+      let rowNode = this._getNodeForRow(i);
+      let nextLevel = rowNode.indentLevel;
       if (nextLevel == thisLevel)
         return true;
       if (nextLevel < thisLevel)
         break;
     }
+
     return false;
   },
 
-  getLevel: function PTV_getLevel(aRow) {
-    this._ensureValidRow(aRow);
-
-    // Level is 0 for nodes at the root level, 1 for its children and so on.
-    return this._visibleElements[aRow].indentLevel;
-  },
+  getLevel: function(aRow) this._getNodeForRow(aRow).indentLevel,
 
   getImageSrc: function PTV_getImageSrc(aRow, aColumn) {
-    this._ensureValidRow(aRow);
-
-    // only the title column has an image
+    // Only the title column has an image.
     if (this._getColumnType(aColumn) != this.COLUMN_TYPE_TITLE)
       return "";
 
-    return this._visibleElements[aRow].icon;
+    return this._getNodeForRow(aRow).icon;
   },
 
   getProgressMode: function(aRow, aColumn) { },
   getCellValue: function(aRow, aColumn) { },
 
   getCellText: function PTV_getCellText(aRow, aColumn) {
-    this._ensureValidRow(aRow);
-
-    var node = this._visibleElements[aRow];
-    var columnType = this._getColumnType(aColumn);
+    let node = this._getNodeForRow(aRow);
+    let columnType = this._getColumnType(aColumn);
     switch (columnType) {
       case this.COLUMN_TYPE_TITLE:
         // normally, this is just the title, but we don't want empty items in
         // the tree view so return a special string if the title is empty.
         // Do it here so that callers can still get at the 0 length title
         // if they go through the "result" API.
         if (PlacesUtils.nodeIsSeparator(node))
           return "";
@@ -1139,17 +1351,17 @@ PlacesTreeView.prototype = {
         if (node.lastModified)
           return this._convertPRTimeToString(node.lastModified);
         return "";
     }
     return "";
   },
 
   setTree: function PTV_setTree(aTree) {
-    var hasOldTree = this._tree != null;
+    let hasOldTree = this._tree != null;
     this._tree = aTree;
 
     if (this._result) {
       if (hasOldTree) {
         // detach from result when we are detaching from the tree.
         // This breaks the reference cycle between us and the result.
         if (!aTree)
           this._result.viewer = null;
@@ -1157,25 +1369,24 @@ PlacesTreeView.prototype = {
       if (aTree)
         this._finishInit();
     }
   },
 
   toggleOpenState: function PTV_toggleOpenState(aRow) {
     if (!this._result)
       throw Cr.NS_ERROR_UNEXPECTED;
-    this._ensureValidRow(aRow);
 
-    var node = this._visibleElements[aRow];
+    let node = this._rows[aRow];
     if (this._flatList && this._openContainerCallback) {
       this._openContainerCallback(node);
       return;
     }
 
-    var resource = this._getResourceForNode(node);
+    let resource = this._getResourceForNode(node);
     if (resource) {
       const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
       const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
 
       if (node.containerOpen)
         PlacesUIUtils.localStore.Unassert(resource, openLiteral, trueLiteral);
       else
         PlacesUIUtils.localStore.Assert(resource, openLiteral, trueLiteral, true);
@@ -1194,24 +1405,24 @@ PlacesTreeView.prototype = {
     // the third state to reset the sorting to the natural bookmark order. When
     // you are looking at history, that third state has no meaning so we try
     // to disallow it.
     //
     // The problem occurs when you have a query that results in bookmark
     // folders. One example of this is the subscriptions view. In these cases,
     // this rule doesn't allow you to sort those sub-folders by their natural
     // order.
-    var allowTriState = PlacesUtils.nodeIsFolder(this._result.root);
+    let allowTriState = PlacesUtils.nodeIsFolder(this._result.root);
 
-    var oldSort = this._result.sortingMode;
-    var oldSortingAnnotation = this._result.sortingAnnotation;
-    var newSort;
-    var newSortingAnnotation = "";
+    let oldSort = this._result.sortingMode;
+    let oldSortingAnnotation = this._result.sortingAnnotation;
+    let newSort;
+    let newSortingAnnotation = "";
     const NHQO = Ci.nsINavHistoryQueryOptions;
-    var columnType = this._getColumnType(aColumn);
+    let columnType = this._getColumnType(aColumn);
     switch (columnType) {
       case this.COLUMN_TYPE_TITLE:
         if (oldSort == NHQO.SORT_BY_TITLE_ASCENDING)
           newSort = NHQO.SORT_BY_TITLE_DESCENDING;
         else if (allowTriState && oldSort == NHQO.SORT_BY_TITLE_DESCENDING)
           newSort = NHQO.SORT_BY_NONE;
         else
           newSort = NHQO.SORT_BY_TITLE_ASCENDING;
@@ -1309,43 +1520,57 @@ PlacesTreeView.prototype = {
     this._result.sortingMode = newSort;
   },
 
   isEditable: function PTV_isEditable(aRow, aColumn) {
     // At this point we only support editing the title field.
     if (aColumn.index != 0)
       return false;
 
-    var node = this.nodeForTreeIndex(aRow);
-    if (!PlacesUtils.nodeIsReadOnly(node) &&
-        (PlacesUtils.nodeIsFolder(node) ||
-         (PlacesUtils.nodeIsBookmark(node) &&
-          !PlacesUtils.nodeIsLivemarkItem(node))))
-      return true;
+    // Only bookmark-nodes are editable, and those are never built lazily
+    let node = this._rows[aRow];
+    if (!node || node.itemId == -1)
+      return false;
 
-    return false;
+    // The following items are never editable:
+    // * Read-only items.
+    // * places-roots
+    // * livemark items
+    // * separators
+    if (PlacesUtils.nodeIsReadOnly(node) ||
+        PlacesUtils.nodeIsLivemarkItem(node) ||
+        PlacesUtils.nodeIsSeparator(node))
+      return false;
+
+    if (PlacesUtils.nodeIsFolder(node)) {
+      let itemId = PlacesUtils.getConcreteItemId(node);
+      if (PlacesUtils.isRootItem(itemId))
+        return false;
+    }
+
+    return true;
   },
 
   setCellText: function PTV_setCellText(aRow, aColumn, aText) {
-    // we may only get here if the cell is editable
-    var node = this.nodeForTreeIndex(aRow);
+    // We may only get here if the cell is editable.
+    let node = this._rows[aRow];
     if (node.title != aText) {
-      var txn = PlacesUIUtils.ptm.editItemTitle(node.itemId, aText);
+      let txn = PlacesUIUtils.ptm.editItemTitle(node.itemId, aText);
       PlacesUIUtils.ptm.doTransaction(txn);
     }
   },
 
   selectionChanged: function() { },
-  cycleCell: function PTV_cycleCell(aRow, aColumn) { },
+  cycleCell: function(aRow, aColumn) { },
   isSelectable: function(aRow, aColumn) { return false; },
   performAction: function(aAction) { },
   performActionOnRow: function(aAction, aRow) { },
   performActionOnCell: function(aAction, aRow, aColumn) { }
 };
 
 function PlacesTreeView(aFlatList, aOnOpenFlatContainer) {
   this._tree = null;
   this._result = null;
   this._selection = null;
-  this._visibleElements = [];
+  this._rows = [];
   this._flatList = aFlatList;
   this._openContainerCallback = aOnOpenFlatContainer;
 }
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-3.7a2pre
+3.7a3pre
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -12,17 +12,16 @@
 
 #ifdef XP_MACOSX
 ; Mac bundle stuff
 @APPNAME@/Contents/Info.plist
 @APPNAME@/Contents/PkgInfo
 @APPNAME@/Contents/Plug-Ins/
 @APPNAME@/Contents/Resources/
 #endif
-@BINPATH@/.autoreg
 
 [@AB_CD@]
 @BINPATH@/chrome/@AB_CD@.jar
 @BINPATH@/chrome/@AB_CD@.manifest
 @BINPATH@/@PREF_DIR@/firefox-l10n.js
 @BINPATH@/browserconfig.properties
 @BINPATH@/searchplugins/*
 @BINPATH@/defaults/profile/bookmarks.html
@@ -69,20 +68,18 @@
 #endif
 
 [browser]
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
-#ifndef XP_MACOSX
 @BINPATH@/@MOZ_APP_NAME@
 #endif
-#endif
 @BINPATH@/application.ini
 @BINPATH@/platform.ini
 #ifdef XP_MACOSX
 @BINPATH@/plugins/DefaultPlugin.plugin/
 #elifdef XP_UNIX
 @BINPATH@/plugins/libnullplugin.so
 #elifdef XP_WIN32
 #ifndef WINCE
@@ -95,19 +92,19 @@
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
 #else
 @BINPATH@/mozsqlt3@DLL_SUFFIX@
 #endif
 @BINPATH@/README.txt
 @BINPATH@/LICENSE
 @BINPATH@/blocklist.xml
 #ifdef XP_UNIX
+@BINPATH@/run-mozilla.sh
 #ifndef XP_MACOSX
 @BINPATH@/mozilla-xremote-client
-@BINPATH@/run-mozilla.sh
 #endif
 #endif
 #ifdef MOZ_SPLASHSCREEN
 @BINPATH@/splash.bmp
 #endif
 
 ; [Components]
 @BINPATH@/components/components.list
@@ -135,17 +132,16 @@
 @BINPATH@/components/content_base.xpt
 @BINPATH@/components/content_events.xpt
 @BINPATH@/components/content_canvas.xpt
 @BINPATH@/components/content_htmldoc.xpt
 @BINPATH@/components/content_html.xpt
 @BINPATH@/components/content_xmldoc.xpt
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/content_xtf.xpt
-@BINPATH@/components/contentprefs.xpt
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_base.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
@@ -214,16 +210,17 @@
 @BINPATH@/components/necko_http.xpt
 @BINPATH@/components/necko_res.xpt
 @BINPATH@/components/necko_socket.xpt
 @BINPATH@/components/necko_strconv.xpt
 @BINPATH@/components/necko_viewsource.xpt
 @BINPATH@/components/necko_wifi.xpt
 @BINPATH@/components/necko.xpt
 @BINPATH@/components/loginmgr.xpt
+@BINPATH@/components/parentalcontrols.xpt
 @BINPATH@/components/places.xpt
 @BINPATH@/components/plugin.xpt
 @BINPATH@/components/pref.xpt
 @BINPATH@/components/prefetch.xpt
 @BINPATH@/components/profile.xpt
 @BINPATH@/components/proxyObject.xpt
 @BINPATH@/components/rdf.xpt
 @BINPATH@/components/satchel.xpt
@@ -247,16 +244,19 @@
 @BINPATH@/components/unicharutil.xpt
 @BINPATH@/components/update.xpt
 @BINPATH@/components/uriloader.xpt
 @BINPATH@/components/urlformatter.xpt
 @BINPATH@/components/webBrowser_core.xpt
 @BINPATH@/components/webbrowserpersist.xpt
 @BINPATH@/components/webshell_idls.xpt
 @BINPATH@/components/widget.xpt
+#ifdef XP_MACOSX
+@BINPATH@/components/widget_cocoa.xpt
+#endif
 @BINPATH@/components/windowds.xpt
 @BINPATH@/components/windowwatcher.xpt
 @BINPATH@/components/xpcom_base.xpt
 @BINPATH@/components/xpcom_system.xpt
 @BINPATH@/components/xpcom_components.xpt
 @BINPATH@/components/xpcom_ds.xpt
 @BINPATH@/components/xpcom_io.xpt
 @BINPATH@/components/xpcom_threads.xpt
@@ -307,20 +307,18 @@
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.js
 @BINPATH@/components/pluginGlue.js
 @BINPATH@/components/nsSessionStartup.js
 @BINPATH@/components/nsSessionStore.js
 @BINPATH@/components/nsURLFormatter.js
 #ifndef XP_OS2
-@BINPATH@/components/@DLL_PREFIX@browserdirprovider@DLL_SUFFIX@
 @BINPATH@/components/@DLL_PREFIX@browsercomps@DLL_SUFFIX@
 #else
-@BINPATH@/components/brwsrdir@DLL_SUFFIX@
 @BINPATH@/components/brwsrcmp@DLL_SUFFIX@
 #endif
 @BINPATH@/components/txEXSLTRegExFunctions.js
 @BINPATH@/components/nsLivemarkService.js
 @BINPATH@/components/nsTaggingService.js
 @BINPATH@/components/nsPlacesDBFlush.js
 @BINPATH@/components/nsPlacesAutoComplete.js
 @BINPATH@/components/nsPlacesExpiration.js
@@ -370,19 +368,21 @@
 @BINPATH@/chrome/icons/default/default32.png
 @BINPATH@/chrome/icons/default/default48.png
 #endif
 #endif
 
 
 ; shell icons
 #ifdef XP_UNIX
+#ifndef XP_MACOSX
 @BINPATH@/icons/*.xpm
 @BINPATH@/icons/*.png
 #endif
+#endif
 
 ; [Default Preferences]
 ; All the pref files must be part of base to prevent migration bugs
 @BINPATH@/@PREF_DIR@/firefox.js
 @BINPATH@/@PREF_DIR@/firefox-branding.js
 @BINPATH@/@PREF_DIR@/channel-prefs.js
 @BINPATH@/greprefs.js
 @BINPATH@/defaults/autoconfig/platform.js
@@ -406,19 +406,17 @@
 @BINPATH@/res/table-add-row-before-hover.gif
 @BINPATH@/res/table-add-row-before.gif
 @BINPATH@/res/table-remove-column-active.gif
 @BINPATH@/res/table-remove-column-hover.gif
 @BINPATH@/res/table-remove-column.gif
 @BINPATH@/res/table-remove-row-active.gif
 @BINPATH@/res/table-remove-row-hover.gif
 @BINPATH@/res/table-remove-row.gif
-@BINPATH@/res/arrowd.gif
 @BINPATH@/res/grabber.gif
-@BINPATH@/res/arrow.gif
 #ifdef XP_MACOSX
 @BINPATH@/res/cursors/*
 #endif
 @BINPATH@/res/fonts/*
 @BINPATH@/res/dtd/*
 @BINPATH@/res/html/*
 @BINPATH@/res/langGroups.properties
 @BINPATH@/res/language.properties
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -797,18 +797,16 @@ components/xpinstall.xpt
 components/xulapp.xpt
 components/xuldoc.xpt
 components/xultmpl.xpt
 components/zipwriter.xpt
 components/firefox.xpt
 greprefs/all.js
 greprefs/security-prefs.js
 greprefs/xpinstall.js
-run-mozilla.sh
-firefox
 components/nsProgressDialog.js
 libwidget.rsrc
 #endif
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
 readme.txt
 chrome/icons/default/default.xpm
 dictionaries/PL.dic
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -74,18 +74,16 @@
 
 .tabbrowser-tab:-moz-lwtheme {
   text-shadow: none;
 }
 
 /* Places toolbar */
 toolbarbutton.bookmark-item {
   margin: 0;
-  min-width: 0;
-  max-width: 13em;
   padding: 2px 3px;
 }
 
 toolbarbutton.bookmark-item:hover:active,
 toolbarbutton.bookmark-item[open="true"] {
   padding-top: 3px;
   padding-bottom: 1px;
   -moz-padding-start: 4px;
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -70,18 +70,16 @@
 #print-preview-toolbar:not(:-moz-lwtheme) {
   -moz-appearance: toolbox;
 }
 
 /* ::::: bookmark buttons ::::: */
 
 toolbarbutton.bookmark-item {
   margin: 0;
-  min-width: 0;
-  max-width: 13em;
   padding: 2px 3px;
 }
 
 toolbarbutton.bookmark-item:hover:active:not([disabled="true"]),
 toolbarbutton.bookmark-item[open="true"] {
   padding-top: 3px;
   padding-bottom: 1px;
   -moz-padding-start: 4px;
@@ -181,52 +179,62 @@ menuitem.bookmark-item {
 }
 
 /* ::::: primary toolbar buttons ::::: */
 
 .toolbarbutton-menubutton-button,
 .toolbarbutton-menubutton-dropmarker,
 .toolbarbutton-1 {
   -moz-appearance: none;
-  padding: 1px 2px;
+  padding: 1px 5px;
   background: rgba(85%,85%,85%,.1)
-              -moz-linear-gradient(top, rgba(255,255,255,.7), rgba(255,255,255,.39) 48%,
-                                        rgba(95%,95%,95%,.33) 52%, rgba(95%,95%,95%,.15));
+              -moz-linear-gradient(top, rgba(255,255,255,.9), rgba(255,255,255,.45) 48%,
+                                        rgba(90%,90%,90%,.4) 52%, rgba(90%,90%,90%,.2));
   -moz-background-clip: padding;
   -moz-border-radius: 5px;
   border: 1px solid;
-  border-color: rgba(0,0,0,.1) rgba(0,0,0,.15) rgba(0,0,0,.25);
+  border-color: rgba(0,0,0,.12) rgba(0,0,0,.19) rgba(0,0,0,.38);
   -moz-box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
-                   0 1px 0 rgba(0,0,0,.08);
+                   0 0 0 2px rgba(255,255,255,.1) inset,
+                   0 1px 0 rgba(0,0,0,.1);
   color: black;
   text-shadow: 0 0 3px white;
 }
 
+.toolbarbutton-menubutton-dropmarker,
+toolbar[iconsize="small"][mode="icons"] .toolbarbutton-menubutton-button,
+toolbar[iconsize="small"][mode="icons"] .toolbarbutton-1 {
+  padding-left: 3px;
+  padding-right: 3px;
+}
+
 .toolbarbutton-1 {
   -moz-box-orient: vertical;
   list-style-image: url("chrome://browser/skin/Toolbar.png");
 }
 
-.toolbarbutton-1[disabled="true"] > .toolbarbutton-icon {
-  opacity: .5;
-}
-
 toolbarbutton[type="menu-button"] {
   -moz-appearance: none;
   padding: 0;
   background: none !important;
   border: none !important;
   -moz-box-shadow: none !important;
 }
 
 .toolbarbutton-1,
 toolbarbutton[type="menu-button"] {
   margin: 1px 3px;
 }
 
+toolbar[iconsize="small"][mode="icons"] .toolbarbutton-1,
+toolbar[iconsize="small"][mode="icons"] toolbarbutton[type="menu-button"] {
+  margin-left: 2px;
+  margin-right: 2px;
+}
+
 .toolbarbutton-menubutton-dropmarker {
   -moz-border-start-style: none;
 }
 
 .toolbarbutton-menubutton-button:-moz-locale-dir(ltr),
 .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(rtl) {
   -moz-border-radius-topright: 0;
   -moz-border-radius-bottomright: 0;
@@ -236,33 +244,34 @@ toolbarbutton[type="menu-button"] {
 .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(ltr) {
   -moz-border-radius-topleft: 0;
   -moz-border-radius-bottomleft: 0;
 }
 
 .toolbarbutton-menubutton-button[disabled="true"],
 .toolbarbutton-menubutton-dropmarker[disabled="true"],
 .toolbarbutton-1[disabled="true"] {
-  background-color: rgba(0,0,0,.15);
-  -moz-box-shadow: 0 1px 0 rgba(0,0,0,.08);
+  opacity: .8;
+}
+
+.toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
+.toolbarbutton-1[disabled="true"] > .toolbarbutton-icon {
+  opacity: .6;
 }
 
 .toolbarbutton-menubutton-button:not([disabled="true"]):not(:active):hover,
 :not([open="true"]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
 .toolbarbutton-1:not([disabled="true"]):not([checked="true"]):not(:active):hover {
-  background-color: rgba(255,255,255,.6);
-  border-color: rgba(60%,60%,60%,.3) rgba(60%,60%,60%,.45) rgba(60%,60%,60%,.75);
-  -moz-box-shadow: 0 1px 0 rgba(0,0,0,.04),
-                   0 0 6px -1px white,
-                   0 0 6px -2px highlight,
-                   0 0 5px -2px highlight,
-                   0 4px 4px -3px white inset,
-                   0 -4px 4px -3px white inset,
-                   0 -3px 12px -5px highlight inset;
-  -moz-transition: background-color .6s;
+  background-color: hsla(190,60%,70%,.5);
+  border-color: hsla(190,50%,65%,.8) hsla(190,50%,50%,.8) hsla(190,50%,40%,.8);
+  -moz-box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
+                   0 0 0 2px rgba(255,255,255,.1) inset,
+                   0 0 5px hsl(190,90%,80%),
+                   0 1px 0 rgba(0,0,0,.1);
+  -moz-transition: background-color .5s ease-in;
 }
 
 .toolbarbutton-menubutton-button:not([disabled="true"]):hover:active,
 :hover:active > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
 [open="true"] > .toolbarbutton-menubutton-dropmarker,
 .toolbarbutton-1:not([disabled="true"]):hover:active,
 .toolbarbutton-1[checked="true"],
 .toolbarbutton-1[open="true"] {
@@ -342,17 +351,23 @@ toolbar:not([iconsize="small"])[mode="ic
   padding-top: 3px;
   padding-bottom: 3px;
 }
 
 toolbar:not([iconsize="small"])[mode="icons"] #forward-button {
   /*mask: url(keyhole-forward-mask.svg#mask); XXX: this regresses twinopen */
   mask: url(chrome://browser/content/browser.xul#winstripe-keyhole-forward-mask);
   -moz-margin-start: -6px;
-  padding-left: 6px;
+  padding-left: 7px;
+  padding-right: 3px;
+}
+
+toolbar:not([iconsize="small"])[mode="icons"] #forward-button:not([disabled="true"]):hover {
+  /*mask: url(keyhole-forward-mask.svg#mask-hover);*/
+  mask: url(chrome://browser/content/browser.xul#winstripe-keyhole-forward-mask-hover);
 }
 
 #back-forward-dropmarker {
   -moz-appearance: none;
   padding: 0;
   -moz-padding-end: 2px;
   -moz-margin-start: -3px;
   border: none;
--- a/browser/themes/winstripe/browser/keyhole-forward-mask.svg
+++ b/browser/themes/winstripe/browser/keyhole-forward-mask.svg
@@ -1,7 +1,11 @@
 <?xml version="1.0"?>
 <svg xmlns="http://www.w3.org/2000/svg">
   <mask id="mask" maskContentUnits="objectBoundingBox">
     <rect x="0" y="0" width="1" height="1" fill="white"/>
-    <circle cx="-0.42" cy="0.5" r="0.63" id="circle"/>
+    <circle cx="-0.46" cy="0.5" r="0.63" id="circle"/>
+  </mask>
+  <mask id="mask-hover" maskContentUnits="objectBoundingBox">
+    <rect x="0" y="0" width="1" height="1" fill="white"/>
+    <circle cx="-0.35" cy="0.5" r="0.58" id="circle"/>
   </mask>
 </svg>
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -560,18 +560,18 @@ user_pref("camino.use_system_proxy_setti
     def killPid(self, pid):
       os.kill(pid, signal.SIGKILL)
 
   def triggerBreakpad(self, proc, utilityPath):
     """Attempt to kill this process in a way that triggers Breakpad crash
     reporting, if we know how for this platform. Otherwise just .kill() it."""
     if self.CRASHREPORTER:
       if self.UNIXISH:
-        # SEGV will get picked up by Breakpad's signal handler
-        os.kill(proc.pid, signal.SIGSEGV)
+        # ABRT will get picked up by Breakpad's signal handler
+        os.kill(proc.pid, signal.SIGABRT)
         return
       elif self.IS_WIN32:
         # We should have a "crashinject" program in our utility path
         crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
         if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(proc.pid)]).wait() == 0:
           return
     #TODO: kill the process such that it triggers Breakpad on OS X (bug 525296)
     self.log.info("Can't trigger Breakpad, just killing process")
new file mode 100644
--- /dev/null
+++ b/build/devicemanager.py
@@ -0,0 +1,591 @@
+# ***** 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 Test Automation Framework.
+#
+# The Initial Developer of the Original Code is Joel Maher.
+#
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Joel Maher <joel.maher@gmail.com> (Original Developer)
+#
+# 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 *****
+
+import socket
+import time, datetime
+import os
+import re
+import hashlib
+import subprocess
+from threading import Thread
+import traceback
+import sys
+
+
+class myProc(Thread):
+  def __init__(self, hostip, hostport, cmd, new_line = True, sleeptime = 0):
+    self.cmdline = cmd
+    self.newline = new_line
+    self.sleep = sleeptime
+    self.host = hostip
+    self.port = hostport
+    Thread.__init__(self)
+
+  def run(self):
+    promptre =re.compile('.*\$\>.$')
+    data = ""
+    try:
+      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    except:
+      return None
+      
+    try:
+      s.connect((self.host, int(self.port)))
+    except:
+      s.close()
+      return None
+      
+    try:
+      s.recv(1024)
+    except:
+      s.close()
+      return None
+      
+    for cmd in self.cmdline:
+      if (cmd == 'quit'): break
+      if self.newline: cmd += '\r\n'
+      try:
+        s.send(cmd)
+      except:
+        s.close()
+        return None
+        
+      time.sleep(int(self.sleep))
+
+      found = False
+      while (found == False):
+        try:
+          temp = s.recv(1024)
+        except:
+          s.close()
+          return None
+          
+        lines = temp.split('\n')
+        for line in lines:
+          if (promptre.match(line)):
+            found = True
+          data += temp
+
+    try:
+      s.send('quit\r\n')
+    except:
+      s.close()
+      return None
+    try:
+      s.close()
+    except:
+      return None
+    return data
+
+class DeviceManager:
+  host = ''
+  port = 0
+  debug = 3
+  _redo = False
+  deviceRoot = '/tests'
+  tempRoot = os.getcwd()
+
+  def __init__(self, host, port = 27020):
+    self.host = host
+    self.port = port
+    self._sock = None
+
+  def sendCMD(self, cmdline, newline = True, sleep = 0):
+    promptre = re.compile('.*\$\>.$')
+
+    # TODO: any commands that don't output anything and quit need to match this RE
+    pushre = re.compile('^push .*$')
+    data = ""
+    noQuit = False
+
+    if (self._sock == None):
+      try:
+        if (self.debug >= 1):
+          print "reconnecting socket"
+        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+      except:
+        self._redo = True
+        self._sock = None
+        if (self.debug >= 2):
+          print "unable to create socket"
+        return None
+      
+      try:
+        self._sock.connect((self.host, int(self.port)))
+        self._sock.recv(1024)
+      except:
+        self._redo = True
+        self._sock.close()
+        self._sock = None
+        if (self.debug >= 2):
+          print "unable to connect socket"
+        return None
+      
+    for cmd in cmdline:
+      if (cmd == 'quit'): break
+      if newline: cmd += '\r\n'
+      
+      try:
+        self._sock.send(cmd)
+      except:
+        self._redo = True
+        self._sock.close()
+        self._sock = None
+        return None
+      
+      if (pushre.match(cmd) or cmd == 'rebt'):
+        noQuit = True
+      elif noQuit == False:
+        time.sleep(int(sleep))
+        found = False
+        while (found == False):
+          if (self.debug >= 3): print "recv'ing..."
+          
+          try:
+            temp = self._sock.recv(1024)
+          except:
+            self._redo = True
+            self._sock.close()
+            self._sock = None
+            return None
+          lines = temp.split('\n')
+          for line in lines:
+            if (promptre.match(line)):
+              found = True
+          data += temp
+
+    time.sleep(int(sleep))
+    if (noQuit == True):
+      try:
+        self._sock.close()
+        self._sock = None
+      except:
+        self._redo = True
+        self._sock = None
+        return None
+
+    return data
+  
+  
+  # take a data blob and strip instances of the prompt '$>\x00'
+  def stripPrompt(self, data):
+    promptre = re.compile('.*\$\>.*')
+    retVal = []
+    lines = data.split('\n')
+    for line in lines:
+      try:
+        while (promptre.match(line)):
+          pieces = line.split('\x00')
+          index = pieces.index("$>")
+          pieces.pop(index)
+          line = '\x00'.join(pieces)
+      except(ValueError):
+        pass
+      retVal.append(line)
+
+    return '\n'.join(retVal)
+  
+
+  def pushFile(self, localname, destname):
+    if (self.debug >= 2):
+      print "in push file with: " + localname + ", and: " + destname
+    if (self.validateFile(destname, localname) == True):
+      if (self.debug >= 2):
+        print "files are validated"
+      return ''
+
+    self.mkDirs(destname)
+    if (self.debug >= 2):
+      print "sending: push " + destname
+    
+    # sleep 5 seconds / MB
+    filesize = os.path.getsize(localname)
+    sleepsize = 1024 * 1024
+    sleepTime = (int(filesize / sleepsize) * 5) + 2
+    f = open(localname, 'rb')
+    data = f.read()
+    f.close()
+    retVal = self.sendCMD(['push ' + destname + '\r\n', data], newline = False, sleep = sleepTime)
+    if (retVal == None):
+      if (self.debug >= 2):
+        print "Error in sendCMD, not validating push"
+      return None
+
+    if (self.validateFile(destname, localname) == False):
+      if (self.debug >= 2):
+        print "file did not copy as expected"
+      return None
+
+    return retVal
+  
+  def mkDir(self, name):
+    return self.sendCMD(['mkdr ' + name, 'quit'])
+  
+  
+  # make directory structure on the device
+  def mkDirs(self, filename):
+    parts = filename.split('/')
+    name = ""
+    for part in parts:
+      if (part == parts[-1]): break
+      if (part != ""):
+        name += '/' + part
+        if (self.mkDir(name) == None):
+          return None
+
+
+  # push localDir from host to remoteDir on the device
+  def pushDir(self, localDir, remoteDir):
+    if (self.debug >= 2): print "pushing directory: " + localDir + " to " + remoteDir
+    for root, dirs, files in os.walk(localDir):
+      parts = root.split(localDir)
+      for file in files:
+        print "examining file: " + file
+        remoteRoot = remoteDir + '/' + parts[1]
+        remoteName = remoteRoot + '/' + file
+        if (parts[1] == ""): remoteRoot = remoteDir
+        if (self.pushFile(os.path.join(root, file), remoteName) == None):
+          time.sleep(5)
+          self.removeFile(remoteName)
+          time.sleep(5)
+          if (self.pushFile(os.path.join(root, file), remoteName) == None):
+            return None
+    return True
+
+
+  def dirExists(self, dirname):
+    match = ".*" + dirname + "$"
+    dirre = re.compile(match)
+    data = self.sendCMD(['cd ' + dirname, 'cwd', 'quit'], sleep = 1)
+    if (data == None):
+      return None
+    retVal = self.stripPrompt(data)
+    data = retVal.split('\n')
+    found = False
+    for d in data:
+      if (dirre.match(d)): 
+        found = True
+
+    return found
+
+  # Because we always have / style paths we make this a lot easier with some
+  # assumptions
+  def fileExists(self, filepath):
+    s = filepath.split('/')
+    containingpath = '/'.join(s[:-1])
+    listfiles = self.listFiles(containingpath)
+    for f in listfiles:
+      if (f == s[-1]):
+        return True
+    return False
+
+  # list files on the device, requires cd to directory first
+  def listFiles(self, rootdir):
+    if (self.dirExists(rootdir) == False):
+      return []  
+    data = self.sendCMD(['cd ' + rootdir, 'ls', 'quit'], sleep=1)
+    if (data == None):
+      return None
+    retVal = self.stripPrompt(data)
+    return retVal.split('\n')
+
+  def removeFile(self, filename):
+    if (self.debug>= 2): print "removing file: " + filename
+    return self.sendCMD(['rm ' + filename, 'quit'])
+  
+  
+  # does a recursive delete of directory on the device: rm -Rf remoteDir
+  def removeDir(self, remoteDir):
+    self.sendCMD(['rmdr ' + remoteDir], sleep = 5)
+
+
+  def getProcessList(self):
+    data = self.sendCMD(['ps', 'quit'], sleep = 3)
+    if (data == None):
+      return None
+      
+    retVal = self.stripPrompt(data)
+    lines = retVal.split('\n')
+    files = []
+    for line in lines:
+      if (line.strip() != ''):
+        pidproc = line.strip().split(' ')
+        if (len(pidproc) == 2):
+          files += [[pidproc[0], pidproc[1]]]
+      
+    return files
+
+
+  def getMemInfo(self):
+    data = self.sendCMD(['mems', 'quit'])
+    if (data == None):
+      return None
+    retVal = self.stripPrompt(data)
+    # TODO: this is hardcoded for now
+    fhandle = open("memlog.txt", 'a')
+    fhandle.write("\n")
+    fhandle.write(retVal)
+    fhandle.close()
+
+  def fireProcess(self, appname):
+    if (self.debug >= 2): print "FIRE PROC: '" + appname + "'"
+    self.process = myProc(self.host, self.port, ['exec ' + appname, 'quit'])
+    self.process.start()  
+
+  def launchProcess(self, cmd, outputFile = "process.txt", cwd = ''):
+    if (outputFile == "process.txt"):
+      outputFile = self.getDeviceRoot() + '/' + "process.txt"
+      
+    cmdline = subprocess.list2cmdline(cmd)
+    self.fireProcess(cmdline + " > " + outputFile)
+    handle = outputFile
+        
+    return handle
+  
+  def communicate(self, process, timeout = 600):
+    timed_out = True
+    if (timeout > 0):
+      total_time = 0
+      while total_time < timeout:
+        time.sleep(1)
+        if (not self.poll(process)):
+          timed_out = False
+          break
+        total_time += 1
+
+    if (timed_out == True):
+      return None
+
+    return [self.getFile(process, "temp.txt"), None]
+
+
+  def poll(self, process):
+    try:
+      if (not self.process.isAlive()):
+        return None
+      return 1
+    except:
+      return None
+    return 1
+  
+
+
+  # iterates process list and returns pid if exists, otherwise ''
+  def processExist(self, appname):
+    pid = ''
+  
+    pieces = appname.split('/')
+    app = pieces[-1]
+    procre = re.compile('.*' + app + '.*')
+  
+    procList = self.getProcessList()
+    if (procList == None):
+      return None
+      
+    for proc in procList:
+      if (procre.match(proc[1])):
+        pid = proc[0]
+        break
+    return pid
+
+
+  def killProcess(self, appname):
+    if (self.sendCMD(['kill ' + appname]) == None):
+      return None
+
+    return True
+
+  def getTempDir(self):
+    promptre = re.compile('.*\$\>\x00.*')
+    retVal = ''
+    data = self.sendCMD(['tmpd', 'quit'])
+    if (data == None):
+      return None
+    return self.stripPrompt(data).strip('\n')
+
+  
+  # copy file from device (remoteFile) to host (localFile)
+  def getFile(self, remoteFile, localFile = ''):
+    if localFile == '':
+        localFile = os.path.join(self.tempRoot, "temp.txt")
+  
+    promptre = re.compile('.*\$\>\x00.*')
+    data = self.sendCMD(['cat ' + remoteFile, 'quit'], sleep = 5)
+    if (data == None):
+      return None
+    retVal = self.stripPrompt(data)
+    fhandle = open(localFile, 'wb')
+    fhandle.write(retVal)
+    fhandle.close()
+    return retVal
+  
+  
+  # copy directory structure from device (remoteDir) to host (localDir)
+  def getDirectory(self, remoteDir, localDir):
+    if (self.debug >= 2): print "getting files in '" + remoteDir + "'"
+    filelist = self.listFiles(remoteDir)
+    if (filelist == None):
+      return None
+    if (self.debug >= 3): print filelist
+    if not os.path.exists(localDir):
+      os.makedirs(localDir)
+  
+    # TODO: is this a comprehensive file regex?
+    isFile = re.compile('^([a-zA-Z0-9_\-\. ]+)\.([a-zA-Z0-9]+)$')
+    for f in filelist:
+      if (isFile.match(f)):
+        if (self.getFile(remoteDir + '/' + f, os.path.join(localDir, f)) == None):
+          return None
+      else:
+        if (self.getDirectory(remoteDir + '/' + f, os.path.join(localDir, f)) == None):
+          return None
+
+
+  # true/false check if the two files have the same md5 sum
+  def validateFile(self, remoteFile, localFile):
+    remoteHash = self.getRemoteHash(remoteFile)
+    localHash = self.getLocalHash(localFile)
+
+    if (remoteHash == localHash):
+        return True
+
+    return False
+
+  
+  # return the md5 sum of a remote file
+  def getRemoteHash(self, filename):
+      data = self.sendCMD(['hash ' + filename, 'quit'], sleep = 1)
+      if (data == None):
+          return ''
+      retVal = self.stripPrompt(data)
+      if (retVal != None):
+        retVal = retVal.strip('\n')
+      if (self.debug >= 3): 
+        print "remote hash: '" + retVal + "'"
+      return retVal
+    
+
+  # return the md5 sum of a file on the host
+  def getLocalHash(self, filename):
+      file = open(filename, 'rb')
+      if (file == None):
+          return None
+
+      try:
+        mdsum = hashlib.md5()
+      except:
+        return None
+
+      while 1:
+          data = file.read(1024)
+          if not data:
+              break
+          mdsum.update(data)
+
+      file.close()
+      hexval = mdsum.hexdigest()
+      if (self.debug >= 3):
+        print "local hash: '" + hexval + "'"
+      return hexval
+
+  # Gets the device root for the testing area on the device
+  # For all devices we will use / type slashes and depend on the device-agent
+  # to sort those out.
+  # Structure on the device is as follows:
+  # /tests
+  #       /<fennec>|<firefox>  --> approot
+  #       /profile
+  #       /xpcshell
+  #       /reftest
+  #       /mochitest
+  def getDeviceRoot(self):
+    if (not self.deviceRoot):
+      if (self.dirExists('/tests')):
+        self.deviceRoot = '/tests'
+      else:
+        self.mkDir('/tests')
+        self.deviceRoot = '/tests'
+    return self.deviceRoot
+
+  # Either we will have /tests/fennec or /tests/firefox but we will never have
+  # both.  Return the one that exists
+  def getAppRoot(self):
+    if (self.dirExists(self.getDeviceRoot() + '/fennec')):
+      return self.getDeviceRoot() + '/fennec'
+    else:
+      return self.getDeviceRoot() + '/firefox'
+
+  # Gets the directory location on the device for a specific test type
+  # Type is one of: xpcshell|reftest|mochitest
+  def getTestRoot(self, type):
+    if (re.search('xpcshell', type, re.I)):
+      self.testRoot = self.getDeviceRoot() + '/xpcshell'
+    elif (re.search('?(i)reftest', type)):
+      self.testRoot = self.getDeviceRoot() + '/reftest'
+    elif (re.search('?(i)mochitest', type)):
+      self.testRoot = self.getDeviceRoot() + '/mochitest'
+    return self.testRoot
+
+  # Sends a specific process ID a signal code and action.
+  # For Example: SIGINT and SIGDFL to process x
+  def signal(self, processID, signalType, signalAction):
+    # currently not implemented in device agent - todo
+    pass
+
+  # Get a return code from process ending -- needs support on device-agent
+  # this is a todo
+  def getReturnCode(self, processID):
+    # todo make this real
+    return 0
+
+  def unpackFile(self, filename):
+    self.sendCMD(['cd /tests', 'unzp ' + filename])
+    
+    
+  # validate localDir from host to remoteDir on the device
+  def validateDir(self, localDir, remoteDir):
+    if (self.debug >= 2): print "validating directory: " + localDir + " to " + remoteDir
+    for root, dirs, files in os.walk(localDir):
+      parts = root.split(localDir)
+      for file in files:
+        remoteRoot = remoteDir + '/' + parts[1]
+        remoteRoot = remoteRoot.replace('/', '/')
+        if (parts[1] == ""): remoteRoot = remoteDir
+        remoteName = remoteRoot + '/' + file
+        if (self.validateFile(remoteName, os.path.join(root, file)) <> True):
+            return None
+    return True
--- a/build/wince/shunt/time.cpp
+++ b/build/wince/shunt/time.cpp
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
  
 #include "include/mozce_shunt.h"
 #include "time_conversions.h"
 #include <stdlib.h>
 #include <Windows.h>
+#include <altcecrt.h>
 
 ////////////////////////////////////////////////////////
 //  Time Stuff
 ////////////////////////////////////////////////////////
 
 // This is the kind of crap that makes me hate microsoft.  defined in their system headers, but not implemented anywhere.
 #define strftime __not_supported_on_device_strftime
 #define localtime __not_supported_on_device_localtime
@@ -72,22 +73,32 @@ extern "C" {
 
 static const int sDaysOfYear[12] = {
   0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
 };
 static struct tm tmStorage;
 
 size_t strftime(char *out, size_t maxsize, const char *pat, const struct tm *time)
 {
-  WCHAR* tmpBuf = (WCHAR*)malloc(sizeof(WCHAR) * maxsize);
-  if (!tmpBuf)
-    return 0;
-  wcsftime(tmpBuf, maxsize, pat, time);
-  size_t ret = ::WideCharToMultiByte(CP_ACP, 0, tmpBuf, -1, out, maxsize, 0, 0);
-  free(tmpBuf);
+  size_t ret = 0;
+  size_t wpatlen = MultiByteToWideChar(CP_ACP, 0, pat, -1, 0, 0);
+  if (wpatlen) {
+    wchar_t* wpat = (wchar_t*)calloc(wpatlen, sizeof(wchar_t));
+    if (wpat) {
+      wchar_t* tmpBuf = (wchar_t*)calloc(maxsize, sizeof(wchar_t));
+      if (tmpBuf) {
+        if (MultiByteToWideChar(CP_ACP, 0, pat, -1, wpat, wpatlen)) {
+          if (wcsftime(tmpBuf, maxsize, wpat, time))
+            ret = ::WideCharToMultiByte(CP_ACP, 0, tmpBuf, -1, out, maxsize, 0, 0);
+        }
+        free(tmpBuf);
+      }
+      free(wpat);
+    }
+  }
   return ret;
 }
 
 struct tm* gmtime_r(const time_t* inTimeT, struct tm* outRetval)
 {
   struct tm* retval = NULL;
   
   if(NULL != inTimeT) {
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -84,18 +84,16 @@ MOZ_CHROME_FILE_FORMAT	= @MOZ_CHROME_FIL
 MOZ_WIDGET_TOOLKIT	= @MOZ_WIDGET_TOOLKIT@
 MOZ_GFX_OPTIMIZE_MOBILE = @MOZ_GFX_OPTIMIZE_MOBILE@
 
 MOZ_DFB			= @MOZ_DFB@
 MOZ_X11			= @MOZ_X11@
 
 MOZ_PANGO = @MOZ_PANGO@
 
-MOZ_CORETEXT = @MOZ_CORETEXT@
-
 MOZ_JS_LIBS		   = @MOZ_JS_LIBS@
 
 MOZ_DEBUG	= @MOZ_DEBUG@
 MOZ_DEBUG_MODULES = @MOZ_DEBUG_MODULES@
 MOZ_DEBUG_ENABLE_DEFS		= @MOZ_DEBUG_ENABLE_DEFS@
 MOZ_DEBUG_DISABLE_DEFS	= @MOZ_DEBUG_DISABLE_DEFS@
 MOZ_DEBUG_FLAGS	= @MOZ_DEBUG_FLAGS@
 MOZ_DEBUG_LDFLAGS=@MOZ_DEBUG_LDFLAGS@
@@ -130,17 +128,16 @@ IBMBIDI = @IBMBIDI@
 MOZ_UNIVERSALCHARDET = @MOZ_UNIVERSALCHARDET@
 ACCESSIBILITY = @ACCESSIBILITY@
 MOZ_VIEW_SOURCE = @MOZ_VIEW_SOURCE@
 MOZ_XPINSTALL = @MOZ_XPINSTALL@
 MOZ_JSLOADER  = @MOZ_JSLOADER@
 MOZ_USE_NATIVE_UCONV = @MOZ_USE_NATIVE_UCONV@
 MOZ_BRANDING_DIRECTORY = @MOZ_BRANDING_DIRECTORY@
 XPCOM_USE_LEA = @XPCOM_USE_LEA@
-JS_ULTRASPARC_OPTS = @JS_ULTRASPARC_OPTS@
 MOZ_ENABLE_POSTSCRIPT = @MOZ_ENABLE_POSTSCRIPT@
 MOZ_INSTALLER	= @MOZ_INSTALLER@
 MOZ_UPDATER	= @MOZ_UPDATER@
 MOZ_UPDATE_PACKAGING	= @MOZ_UPDATE_PACKAGING@
 MOZ_NO_ACTIVEX_SUPPORT = @MOZ_NO_ACTIVEX_SUPPORT@
 MOZ_ACTIVEX_SCRIPTING_SUPPORT = @MOZ_ACTIVEX_SCRIPTING_SUPPORT@
 MOZ_DISABLE_VISTA_SDK_REQUIREMENTS = @MOZ_DISABLE_VISTA_SDK_REQUIREMENTS@
 MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@
@@ -639,16 +636,17 @@ HAS_XCODE_2_1	= @HAS_XCODE_2_1@
 UNIVERSAL_BINARY= @UNIVERSAL_BINARY@
 HAVE_DTRACE= @HAVE_DTRACE@
 
 VISIBILITY_FLAGS = @VISIBILITY_FLAGS@
 WRAP_SYSTEM_INCLUDES = @WRAP_SYSTEM_INCLUDES@
 
 HAVE_ARM_SIMD = @HAVE_ARM_SIMD@
 HAVE_ARM_NEON = @HAVE_ARM_NEON@
+HAVE_GCC_ALIGN_ARG_POINTER = @HAVE_GCC_ALIGN_ARG_POINTER@
 
 MOZ_SPLASHSCREEN = @MOZ_SPLASHSCREEN@
 
 MOZ_THEME_FASTSTRIPE = @MOZ_THEME_FASTSTRIPE@
 
 MOZ_OFFICIAL_BRANDING = @MOZ_OFFICIAL_BRANDING@
 
 HAVE_CLOCK_MONOTONIC = @HAVE_CLOCK_MONOTONIC@
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-1.9.3a2pre
+1.9.3a3pre
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -771,16 +771,17 @@ tools_tier_%:
 	@$(MAKE_TIER_SUBMAKEFILES)
 	$(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,tools,$(dir)))
 
 $(foreach tier,$(TIERS),tier_$(tier))::
 	@$(ECHO) "$@: $($@_staticdirs) $($@_dirs)"
 	$(foreach dir,$($@_staticdirs),$(call SUBMAKE,,$(dir)))
 	$(MAKE) export_$@
 	$(MAKE) libs_$@
+	$(MAKE) tools_$@
 
 # Do everything from scratch
 everything::
 	$(MAKE) clean
 	$(MAKE) alldep
 
 # Add dummy depend target for tinderboxes
 depend::
--- a/configure.in
+++ b/configure.in
@@ -3123,16 +3123,36 @@ fi         # GNU_CC
 # visibility hidden flag for Sun Studio on Solaris
 if test "$SOLARIS_SUNPRO_CC"; then
 VISIBILITY_FLAGS='-xldscope=hidden'
 fi         # Sun Studio on Solaris
 
 AC_SUBST(WRAP_SYSTEM_INCLUDES)
 AC_SUBST(VISIBILITY_FLAGS)
 
+dnl Check for __force_align_arg_pointer__ for SSE2 on gcc
+dnl ========================================================
+if test "$GNU_CC"; then
+  CFLAGS_save="${CFLAGS}"
+  CFLAGS="${CFLAGS} -Werror"
+  AC_CACHE_CHECK(for __force_align_arg_pointer__ attribute,
+                 ac_cv_force_align_arg_pointer,
+                 [AC_TRY_COMPILE([__attribute__ ((__force_align_arg_pointer__)) void test() {}],
+                                 [],
+                                 ac_cv_force_align_arg_pointer="yes",
+                                 ac_cv_force_align_arg_pointer="no")])
+  CFLAGS="${CFLAGS_save}"
+  if test "$ac_cv_force_align_arg_pointer" = "yes"; then
+    HAVE_GCC_ALIGN_ARG_POINTER=1
+  else
+    HAVE_GCC_ALIGN_ARG_POINTER=
+  fi
+fi
+AC_SUBST(HAVE_GCC_ALIGN_ARG_POINTER)
+
 dnl Checks for header files.
 dnl ========================================================
 AC_HEADER_DIRENT
 case "$target_os" in
 freebsd*)
 # for stuff like -lXshm
     CPPFLAGS="${CPPFLAGS} ${X_CFLAGS}"
     ;;
@@ -3238,45 +3258,33 @@ case $target in
 *-os2*)
     ;;
 *)
     AC_CHECK_LIB(socket, socket)
 esac
 
 dnl ========================================================
 dnl Check whether we can compile code for Core Text
-dnl (Mac OS X 10.5 or later)
+dnl (available on Mac OS X 10.5 or later)
 dnl ========================================================
 case "$target" in
 *-darwin*)
   AC_MSG_CHECKING([for Core Text])
   AC_TRY_COMPILE([#include <ApplicationServices/ApplicationServices.h>],
                  [CTLineRef lineRef;],
                   ac_cv_have_core_text="yes",
                   ac_cv_have_core_text="no")
   AC_MSG_RESULT([$ac_cv_have_core_text])
 
-  MOZ_CORETEXT=1
-
-  MOZ_ARG_DISABLE_BOOL(coretext,
-[  --disable-coretext      Use ATSUI instead of Core Text for text rendering],
-    MOZ_CORETEXT=,
-    MOZ_CORETEXT=1)
-
-  if test -n "$MOZ_CORETEXT"; then
-    if test "$ac_cv_have_core_text" = "no"; then
-      AC_MSG_ERROR([--enable-coretext requires MacOS SDK 10.5 or newer])
-     fi
-     AC_DEFINE(MOZ_CORETEXT)
+  if test "$ac_cv_have_core_text" = "no"; then
+    AC_MSG_ERROR([Core Text is required (available on Mac OS X 10.5 or later).])
   fi
   ;;
 esac
 
-AC_SUBST(MOZ_CORETEXT)
-
 XLDFLAGS="$X_LIBS"
 XLIBS="$X_EXTRA_LIBS"
 
 dnl ========================================================
 dnl Checks for X libraries.
 dnl Ordering is important.
 dnl Xt is dependent upon SM as of X11R6
 dnl ========================================================
@@ -4966,17 +4974,17 @@ AC_SUBST(MOZ_ENABLE_STARTUP_NOTIFICATION
 AC_SUBST(MOZ_STARTUP_NOTIFICATION_CFLAGS)
 AC_SUBST(MOZ_STARTUP_NOTIFICATION_LIBS)
 
 dnl ========================================================
 dnl = QT support
 dnl ========================================================
 if test "$MOZ_ENABLE_QT"
 then
-    PKG_CHECK_MODULES(MOZ_QT, QtGui QtNetwork QtUiTools QtCore)
+    PKG_CHECK_MODULES(MOZ_QT, QtGui QtNetwork QtCore)
     AC_SUBST(MOZ_QT_CFLAGS)
     AC_SUBST(MOZ_QT_LIBS)
 
     MOZ_ARG_WITH_STRING(qtdir,
     [  --with-qtdir=\$dir       Specify Qt directory ],
     [ QTDIR=$withval])
 
     if test -z "$QTDIR"; then
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -92,16 +92,17 @@ XPIDLSRCS	= \
 		nsIContentPolicy.idl        \
 		nsIDocumentEncoder.idl      \
 		nsIDOMFile.idl \
 		nsIDOMFileReader.idl \
 		nsIDOMFileInternal.idl \
 		nsIDOMFileList.idl \
 		nsIDOMFileException.idl \
 		nsIDOMFileError.idl \
+		nsIDOMFormData.idl \
 		nsIDOMParser.idl \
 		nsIDOMSerializer.idl \
 		nsISelection2.idl \
 		nsISelectionController.idl  \
 		nsISelectionDisplay.idl  \
 		nsISelectionListener.idl  \
 		nsISelectionPrivate.idl  \
 		nsIScriptLoaderObserver.idl  \
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -112,22 +112,27 @@ class nsIXPConnectJSObjectHolder;
 class nsPrefOldCallback;
 #ifdef MOZ_XTF
 class nsIXTFService;
 #endif
 #ifdef IBMBIDI
 class nsIBidiKeyboard;
 #endif
 class nsIMIMEHeaderParam;
+class nsIObserver;
 
 #ifndef have_PrefChangedFunc_typedef
 typedef int (*PR_CALLBACK PrefChangedFunc)(const char *, void *);
 #define have_PrefChangedFunc_typedef
 #endif
 
+namespace mozilla {
+  class IHistory;
+}
+
 extern const char kLoadAsData[];
 
 enum EventNameType {
   EventNameType_None = 0x0000,
   EventNameType_HTML = 0x0001,
   EventNameType_XUL = 0x0002,
   EventNameType_SVGGraphic = 0x0004, // svg graphic elements
   EventNameType_SVGSVG = 0x0008, // the svg element
@@ -461,16 +466,21 @@ public:
     return sIOService;
   }
 
   static imgILoader* GetImgLoader()
   {
     return sImgLoader;
   }
 
+  static mozilla::IHistory* GetHistory()
+  {
+    return sHistory;
+  }
+
 #ifdef MOZ_XTF
   static nsIXTFService* GetXTFService();
 #endif
 
 #ifdef IBMBIDI
   static nsIBidiKeyboard* GetBidiKeyboard();
 #endif
   
@@ -585,16 +595,23 @@ public:
   }
 
   static nsIUGenCategory* GetGenCat()
   {
     return sGenCat;
   }
 
   /**
+   * Regster aObserver as a shutdown observer. A strong reference is held
+   * to aObserver until UnregisterShutdownObserver is called.
+   */
+  static void RegisterShutdownObserver(nsIObserver* aObserver);
+  static void UnregisterShutdownObserver(nsIObserver* aObserver);
+
+  /**
    * @return PR_TRUE if aContent has an attribute aName in namespace aNameSpaceID,
    * and the attribute value is non-empty.
    */
   static PRBool HasNonEmptyAttr(nsIContent* aContent, PRInt32 aNameSpaceID,
                                 nsIAtom* aName);
 
   /**
    * Method that gets the primary presContext for the node.
@@ -1428,17 +1445,26 @@ public:
   static nsresult ProcessViewportInfo(nsIDocument *aDocument,
                                       const nsAString &viewportInfo);
 
   static nsIScriptContext* GetContextForEventHandlers(nsINode* aNode,
                                                       nsresult* aRv);
 
   static JSContext *GetCurrentJSContext();
 
-                                             
+  /**
+   * Convert ASCII A-Z to a-z.
+   */
+  static void ASCIIToLower(const nsAString& aSource, nsAString& aDest);
+
+  /**
+   * Convert ASCII a-z to A-Z.
+   */
+  static void ASCIIToUpper(nsAString& aStr);
+
   static nsIInterfaceRequestor* GetSameOriginChecker();
 
   static nsIThreadJSContextStack* ThreadJSContextStack()
   {
     return sThreadJSContextStack;
   }
   
 
@@ -1544,16 +1570,18 @@ private:
 
   static nsIPrefBranch2 *sPrefBranch;
   // For old compatibility of RegisterPrefCallback
   static nsCOMArray<nsPrefOldCallback> *sPrefCallbackList;
 
   static imgILoader* sImgLoader;
   static imgICache* sImgCache;
 
+  static mozilla::IHistory* sHistory;
+
   static nsIConsoleService* sConsoleService;
 
   static nsDataHashtable<nsISupportsHashKey, EventNameMapping>* sEventTable;
 
   static nsIStringBundleService* sStringBundleService;
   static nsIStringBundle* sStringBundles[PropertiesFile_COUNT];
 
   static nsIContentPolicy* sContentPolicyService;
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -67,18 +67,18 @@ enum nsLinkState {
   eLinkState_Unknown    = 0,
   eLinkState_Unvisited  = 1,
   eLinkState_Visited    = 2,
   eLinkState_NotLink    = 3
 };
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID       \
-{ 0xe88a767e, 0x1ca1, 0x4855, \
- { 0xa7, 0xa4, 0x37, 0x9f, 0x07, 0x89, 0x45, 0xef } }
+{ 0xc19d6f16, 0xab13, 0x4dde, \
+ { 0x99, 0x7a, 0x51, 0x04, 0xc3, 0x64, 0xd2, 0x51 } }
 
 /**
  * A node of content in a document's content model. This interface
  * is supported by all content objects.
  */
 class nsIContent : public nsINode {
 public:
 #ifdef MOZILLA_INTERNAL_API
@@ -663,50 +663,28 @@ public:
    *
    * @note The out param, aURI, is guaranteed to be set to a non-null pointer
    *   when the return value is PR_TRUE.
    *
    * XXXjwatt: IMO IsInteractiveLink would be a better name.
    */
   virtual PRBool IsLink(nsIURI** aURI) const = 0;
 
-   /**
-   * If the implementing element is a link, calling this method forces it to
-   * clear its cached href, if it has one.
-   *
-   * This function does not notify the document that it may need to restyle the
-   * link.
-   */
-  virtual void DropCachedHref()
-  {
-  }
-
   /**
    * Get the cached state of the link.  If the state is unknown, 
    * return eLinkState_Unknown.
    *
    * @return The cached link state of the link.
    */
   virtual nsLinkState GetLinkState() const
   {
     return eLinkState_NotLink;
   }
 
   /**
-   * Set the cached state of the link.
-   *
-   * @param aState The cached link state of the link.
-   */
-  virtual void SetLinkState(nsLinkState aState)
-  {
-    NS_ASSERTION(aState == eLinkState_NotLink,
-                 "Need to override SetLinkState?");
-  }
-
-  /**
     * Get a pointer to the full href URI (fully resolved and canonicalized,
     * since it's an nsIURI object) for link elements.
     *
     * @return A pointer to the URI or null if the element is not a link or it
     *         has no HREF attribute.
     */
   virtual already_AddRefed<nsIURI> GetHrefURI() const
   {
new file mode 100644
--- /dev/null
+++ b/content/base/public/nsIDOMFormData.idl
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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
+ * Mozilla Foundation.
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(256c9139-5a29-41e1-8698-f9f9aae7d6cf)]
+interface nsIDOMFormData : nsISupports 
+{
+  void append(in DOMString name, in nsIVariant value);
+};
+
+%{ C++
+#define NS_FORMDATA_CID \
+{ 0x6b192618, 0x26f2, 0x426e, \
+  { 0xa7, 0xac, 0x1e, 0x13, 0xa6, 0xa4, 0x52, 0x2b } }
+
+#define NS_FORMDATA_CONTRACTID "@mozilla.org/files/formdata;1"
+%}
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -99,20 +99,26 @@ template<class E> class nsCOMArray;
 class nsIDocumentObserver;
 class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
 class nsFrameLoader;
 class nsIBoxObject;
 
+namespace mozilla {
+namespace dom {
+class Link;
+} // namespace dom
+} // namespace mozilla
+
 // IID for the nsIDocument interface
 #define NS_IDOCUMENT_IID      \
-{ 0x6b2f1996, 0x95d4, 0x48db, \
-  {0xaf, 0xd1, 0xfd, 0xaa, 0x75, 0x4c, 0x79, 0x92 } }
+  { 0xd7978655, 0x9b7d, 0x41e6, \
+    { 0xad, 0x48, 0xdf, 0x32, 0x0b, 0x06, 0xb4, 0xda } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
@@ -902,32 +908,26 @@ public:
   virtual void OnPageHide(PRBool aPersisted,
                           nsIDOMEventTarget* aDispatchStartTarget) = 0;
   
   /*
    * We record the set of links in the document that are relevant to
    * style.
    */
   /**
-   * Notification that an element is a link with a given URI that is
-   * relevant to style.
+   * Notification that an element is a link that is relevant to style.
    */
-  virtual void AddStyleRelevantLink(nsIContent* aContent, nsIURI* aURI) = 0;
+  virtual void AddStyleRelevantLink(mozilla::dom::Link* aLink) = 0;
   /**
    * Notification that an element is a link and its URI might have been
    * changed or the element removed. If the element is still a link relevant
    * to style, then someone must ensure that AddStyleRelevantLink is
    * (eventually) called on it again.
    */
-  virtual void ForgetLink(nsIContent* aContent) = 0;
-  /**
-   * Notification that the visitedness state of a URI has been changed
-   * and style related to elements linking to that URI should be updated.
-   */
-  virtual void NotifyURIVisitednessChanged(nsIURI* aURI) = 0;
+  virtual void ForgetLink(mozilla::dom::Link* aLink) = 0;
 
   /**
    * Resets and removes a box object from the document's box object cache
    *
    * @param aElement canonical nsIContent pointer of the box object's element
    */
   virtual void ClearBoxObjectFor(nsIContent *aContent) = 0;
 
--- a/content/base/public/nsIMutationObserver.h
+++ b/content/base/public/nsIMutationObserver.h
@@ -134,17 +134,21 @@ public:
    * @param aContent  The piece of content that changed. Is never null.
    * @param aInfo     The structure with information details about the change.
    */
   virtual void CharacterDataChanged(nsIDocument *aDocument,
                                     nsIContent* aContent,
                                     CharacterDataChangeInfo* aInfo) = 0;
 
   /**
-   * Notification that an attribute of an element will change.
+   * Notification that an attribute of an element will change.  This
+   * can happen before the BeginUpdate for the change and may not
+   * always be followed by an AttributeChanged (in particular, if the
+   * attribute doesn't actually change there will be no corresponding
+   * AttributeChanged).
    *
    * @param aDocument    The owner-document of aContent. Can be null.
    * @param aContent     The element whose attribute will change
    * @param aNameSpaceID The namespace id of the changing attribute
    * @param aAttribute   The name of the changing attribute
    * @param aModType     Whether or not the attribute will be added, changed, or
    *                     removed. The constants are defined in
    *                     nsIDOMMutationEvent.h.
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -40,16 +40,17 @@
 interface nsIChannel;
 interface nsIDOMDocument;
 interface nsIDOMEventListener;
 interface nsIPrincipal;
 interface nsIScriptContext;
 interface nsIURI;
 interface nsIVariant;
 interface nsPIDOMWindow;
+interface nsIInputStream;
 
 [scriptable, uuid(6ce0a193-b033-4c3d-b748-f851b09261f5)]
 interface nsIXMLHttpRequestEventTarget : nsIDOMEventTarget {
   // event handler attributes
   attribute nsIDOMEventListener onabort;
   attribute nsIDOMEventListener onerror;
   attribute nsIDOMEventListener onload;
   attribute nsIDOMEventListener onloadstart;
@@ -366,16 +367,23 @@ interface nsIXMLHttpRequest : nsISupport
    * After the initial response, all event listeners will be cleared.
    * // XXXbz what does that mean, exactly?   
    *
    * Call open() before setting an onreadystatechange listener.
    */
   attribute nsIDOMEventListener onreadystatechange;
 };
 
+[scriptable, uuid(840d0d00-e83e-4a29-b3c7-67e96e90a499)]
+interface nsIXHRSendable : nsISupports {
+  void getSendInfo(out nsIInputStream body,
+                   out ACString contentType,
+                   out ACString charset);
+};
+
 /**
  * DEPRECATED.
  */
 [scriptable, uuid(423fdd3d-41c9-4149-8fe5-b14a1d3912a0)]
 interface nsIJSXMLHttpRequest : nsISupports {
   /**
    * Meant to be a script-only mechanism for setting an upload progress event
    * listener.
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -34,43 +34,528 @@
  * 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 "Link.h"
 
+#include "nsIEventStateManager.h"
+#include "nsIURL.h"
+
+#include "nsContentUtils.h"
+#include "nsEscape.h"
+#include "nsGkAtoms.h"
+#include "nsString.h"
+#include "mozAutoDocUpdate.h"
+
+#include "mozilla/IHistory.h"
+
 namespace mozilla {
 namespace dom {
 
 Link::Link()
   : mLinkState(defaultState)
+  , mRegistered(false)
+  , mContent(NULL)
 {
 }
 
+Link::~Link()
+{
+  UnregisterFromHistory();
+}
+
 nsLinkState
 Link::GetLinkState() const
 {
+  NS_ASSERTION(mRegistered,
+               "Getting the link state of an unregistered Link!");
+  NS_ASSERTION(mLinkState != eLinkState_Unknown,
+               "Getting the link state with an unknown value!");
   return mLinkState;
 }
 
 void
 Link::SetLinkState(nsLinkState aState)
 {
+  NS_ASSERTION(mRegistered,
+               "Setting the link state of an unregistered Link!");
+  NS_ASSERTION(mLinkState != aState,
+               "Setting state to the currently set state!");
+
+  // Remember our old link state for when we notify.
+  PRInt32 oldLinkState = LinkState();
+
+  // Set our current state as appropriate.
   mLinkState = aState;
+
+  // Per IHistory interface documentation, we are no longer registered.
+  mRegistered = false;
+
+  // Notify the document that our visited state has changed.
+  nsIContent *content = Content();
+  nsIDocument *doc = content->GetCurrentDoc();
+  NS_ASSERTION(doc, "Registered but we have no document?!");
+  PRInt32 newLinkState = LinkState();
+  NS_ASSERTION(newLinkState == NS_EVENT_STATE_VISITED ||
+               newLinkState == NS_EVENT_STATE_UNVISITED,
+               "Unexpected state obtained from LinkState()!");
+  mozAutoDocUpdate update(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+  doc->ContentStatesChanged(content, nsnull, oldLinkState ^ newLinkState);
+}
+
+PRInt32
+Link::LinkState() const
+{
+  // We are a constant method, but we are just lazily doing things and have to
+  // track that state.  Cast away that constness!
+  Link *self = const_cast<Link *>(this);
+
+  // If we are not in the document, default to not visited.
+  nsIContent *content = self->Content();
+  if (!content->IsInDoc()) {
+    self->mLinkState = eLinkState_Unvisited;
+  }
+
+  // If we have not yet registered for notifications and are in an unknown
+  // state, register now!
+  if (!mRegistered && mLinkState == eLinkState_Unknown) {
+    // First, make sure the href attribute has a valid link (bug 23209).
+    nsCOMPtr<nsIURI> hrefURI(GetURI());
+    if (!hrefURI) {
+      self->mLinkState = eLinkState_NotLink;
+      return 0;
+    }
+
+    // We have a good href, so register with History.
+    IHistory *history = nsContentUtils::GetHistory();
+    nsresult rv = history->RegisterVisitedCallback(hrefURI, self);
+    if (NS_SUCCEEDED(rv)) {
+      self->mRegistered = true;
+
+      // Assume that we are not visited until we are told otherwise.
+      self->mLinkState = eLinkState_Unvisited;
+
+      // And make sure we are in the document's link map.
+      nsIDocument *doc = content->GetCurrentDoc();
+      if (doc) {
+        doc->AddStyleRelevantLink(self);
+      }
+    }
+  }
+
+  // Otherwise, return our known state.
+  if (mLinkState == eLinkState_Visited) {
+    return NS_EVENT_STATE_VISITED;
+  }
+
+  if (mLinkState == eLinkState_Unvisited) {
+    return NS_EVENT_STATE_UNVISITED;
+  }
+
+  return 0;
+}
+
+already_AddRefed<nsIURI>
+Link::GetURI() const
+{
+  nsCOMPtr<nsIURI> uri(mCachedURI);
+
+  // If we have this URI cached, use it.
+  if (uri) {
+    return uri.forget();
+  }
+
+  // Otherwise obtain it.
+  Link *self = const_cast<Link *>(this);
+  nsIContent *content = self->Content();
+  uri = content->GetHrefURI();
+
+  // We want to cache the URI if the node is in the document.
+  if (uri && content->IsInDoc()) {
+    mCachedURI = uri;
+  }
+
+  return uri.forget();
+}
+
+nsresult
+Link::SetProtocol(const nsAString &aProtocol)
+{
+  nsCOMPtr<nsIURI> uri(GetURIToMutate());
+  if (!uri) {
+    // Ignore failures to be compatible with NS4.
+    return NS_OK;
+  }
+
+  nsAString::const_iterator start, end;
+  aProtocol.BeginReading(start);
+  aProtocol.EndReading(end);
+  nsAString::const_iterator iter(start);
+  (void)FindCharInReadable(':', iter, end);
+  (void)uri->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
+
+  SetHrefAttribute(uri);
+  return NS_OK;
+}
+
+nsresult
+Link::SetHost(const nsAString &aHost)
+{
+  nsCOMPtr<nsIURI> uri(GetURIToMutate());
+  if (!uri) {
+    // Ignore failures to be compatible with NS4.
+    return NS_OK;
+  }
+
+  // We cannot simply call nsIURI::SetHost because that would treat the name as
+  // an IPv6 address (like http:://[server:443]/).  We also cannot call
+  // nsIURI::SetHostPort because that isn't implemented.  Sadfaces.
+
+  // First set the hostname.
+  nsAString::const_iterator start, end;
+  aHost.BeginReading(start);
+  aHost.EndReading(end);
+  nsAString::const_iterator iter(start);
+  (void)FindCharInReadable(':', iter, end);
+  NS_ConvertUTF16toUTF8 host(Substring(start, iter));
+  (void)uri->SetHost(host);
+
+  // Also set the port if needed.
+  if (iter != end) {
+    iter++;
+    if (iter != end) {
+      nsAutoString portStr(Substring(iter, end));
+      nsresult rv;
+      PRInt32 port = portStr.ToInteger((PRInt32 *)&rv);
+      if (NS_SUCCEEDED(rv)) {
+        (void)uri->SetPort(port);
+      }
+    }
+  };
+
+  SetHrefAttribute(uri);
+  return NS_OK;
+}
+
+nsresult
+Link::SetHostname(const nsAString &aHostname)
+{
+  nsCOMPtr<nsIURI> uri(GetURIToMutate());
+  if (!uri) {
+    // Ignore failures to be compatible with NS4.
+    return NS_OK;
+  }
+
+  (void)uri->SetHost(NS_ConvertUTF16toUTF8(aHostname));
+  SetHrefAttribute(uri);
+  return NS_OK;
+}
+
+nsresult
+Link::SetPathname(const nsAString &aPathname)
+{
+  nsCOMPtr<nsIURI> uri(GetURIToMutate());
+  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+  if (!url) {
+    // Ignore failures to be compatible with NS4.
+    return NS_OK;
+  }
+
+  (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
+  SetHrefAttribute(uri);
+  return NS_OK;
+}
+
+nsresult
+Link::SetSearch(const nsAString &aSearch)
+{
+  nsCOMPtr<nsIURI> uri(GetURIToMutate());
+  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+  if (!url) {
+    // Ignore failures to be compatible with NS4.
+    return NS_OK;
+  }
+
+  (void)url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
+  SetHrefAttribute(uri);
+  return NS_OK;
+}
+
+nsresult
+Link::SetPort(const nsAString &aPort)
+{
+  nsCOMPtr<nsIURI> uri(GetURIToMutate());
+  if (!uri) {
+    // Ignore failures to be compatible with NS4.
+    return NS_OK;
+  }
+
+  nsresult rv;
+  nsAutoString portStr(aPort);
+  PRInt32 port = portStr.ToInteger((PRInt32 *)&rv);
+  if (NS_FAILED(rv)) {
+    return NS_OK;
+  }
+
+  (void)uri->SetPort(port);
+  SetHrefAttribute(uri);
+  return NS_OK;
+}
+
+nsresult
+Link::SetHash(const nsAString &aHash)
+{
+  nsCOMPtr<nsIURI> uri(GetURIToMutate());
+  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+  if (!url) {
+    // Ignore failures to be compatible with NS4.
+    return NS_OK;
+  }
+
+  (void)url->SetRef(NS_ConvertUTF16toUTF8(aHash));
+  SetHrefAttribute(uri);
+  return NS_OK;
+}
+
+nsresult
+Link::GetProtocol(nsAString &_protocol)
+{
+  nsCOMPtr<nsIURI> uri(GetURI());
+  if (!uri) {
+    _protocol.AssignLiteral("http");
+  }
+  else {
+    nsCAutoString scheme;
+    (void)uri->GetScheme(scheme);
+    CopyASCIItoUTF16(scheme, _protocol);
+  }
+  _protocol.Append(PRUnichar(':'));
+  return NS_OK;
+}
+
+nsresult
+Link::GetHost(nsAString &_host)
+{
+  _host.Truncate();
+
+  nsCOMPtr<nsIURI> uri(GetURI());
+  if (!uri) {
+    // Do not throw!  Not having a valid URI should result in an empty string.
+    return NS_OK;
+  }
+
+  nsCAutoString hostport;
+  nsresult rv = uri->GetHostPort(hostport);
+  if (NS_SUCCEEDED(rv)) {
+    CopyUTF8toUTF16(hostport, _host);
+  }
+  return NS_OK;
+}
+
+nsresult
+Link::GetHostname(nsAString &_hostname)
+{
+  _hostname.Truncate();
+
+  nsCOMPtr<nsIURI> uri(GetURI());
+  if (!uri) {
+    // Do not throw!  Not having a valid URI should result in an empty string.
+    return NS_OK;
+  }
+
+  nsCAutoString host;
+  nsresult rv = uri->GetHost(host);
+  // Note that failure to get the host from the URI is not necessarily a bad
+  // thing.  Some URIs do not have a host.
+  if (NS_SUCCEEDED(rv)) {
+    CopyUTF8toUTF16(host, _hostname);
+  }
+  return NS_OK;
+}
+
+nsresult
+Link::GetPathname(nsAString &_pathname)
+{
+  _pathname.Truncate();
+
+  nsCOMPtr<nsIURI> uri(GetURI());
+  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+  if (!url) {
+    // Do not throw!  Not having a valid URI or URL should result in an empty
+    // string.
+    return NS_OK;
+  }
+
+  nsCAutoString file;
+  nsresult rv = url->GetFilePath(file);
+  NS_ENSURE_SUCCESS(rv, rv);
+  CopyUTF8toUTF16(file, _pathname);
+  return NS_OK;
+}
+
+nsresult
+Link::GetSearch(nsAString &_search)
+{
+  _search.Truncate();
+
+  nsCOMPtr<nsIURI> uri(GetURI());
+  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+  if (!url) {
+    // Do not throw!  Not having a valid URI or URL should result in an empty
+    // string.
+    return NS_OK;
+  }
+
+  nsCAutoString search;
+  nsresult rv = url->GetQuery(search);
+  if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
+    CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, _search);
+  }
+  return NS_OK;
+}
+
+nsresult
+Link::GetPort(nsAString &_port)
+{
+  _port.Truncate();
+
+  nsCOMPtr<nsIURI> uri(GetURI());
+  if (!uri) {
+    // Do not throw!  Not having a valid URI should result in an empty string.
+    return NS_OK;
+  }
+
+  PRInt32 port;
+  nsresult rv = uri->GetPort(&port);
+  // Note that failure to get the port from the URI is not necessarily a bad
+  // thing.  Some URIs do not have a port.
+  if (NS_SUCCEEDED(rv) && port != -1) {
+    nsAutoString portStr;
+    portStr.AppendInt(port, 10);
+    _port.Assign(portStr);
+  }
+  return NS_OK;
+}
+
+nsresult
+Link::GetHash(nsAString &_hash)
+{
+  _hash.Truncate();
+
+  nsCOMPtr<nsIURI> uri(GetURI());
+  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+  if (!url) {
+    // Do not throw!  Not having a valid URI or URL should result in an empty
+    // string.
+    return NS_OK;
+  }
+
+  nsCAutoString ref;
+  nsresult rv = url->GetRef(ref);
+  if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
+    NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
+    _hash.Assign(PRUnichar('#'));
+    AppendUTF8toUTF16(ref, _hash);
+  }
+  return NS_OK;
 }
 
 void
-Link::ResetLinkState()
+Link::ResetLinkState(bool aNotify)
 {
-  nsCOMPtr<nsIContent> content(do_QueryInterface(this));
-  NS_ASSERTION(content, "Why isn't this an nsIContent node?!");
+  // If we are in our default state, bail early.
+  if (mLinkState == defaultState) {
+    return;
+  }
 
+  // If we are not a link, revert to the default state and do no more work.
+  if (mLinkState == eLinkState_NotLink) {
+    mLinkState = defaultState;
+    return;
+  }
+
+  nsIContent *content = Content();
+
+  // Tell the document to forget about this link.
   nsIDocument *doc = content->GetCurrentDoc();
   if (doc) {
-    doc->ForgetLink(content);
+    doc->ForgetLink(this);
+  }
+
+  UnregisterFromHistory();
+
+  // Update our state back to the default.
+  mLinkState = defaultState;
+
+  // Get rid of our cached URI.
+  mCachedURI = nsnull;
+
+  // If aNotify is true, notify both of the visited-related states.  We have
+  // to do that, because we might be racing with a response from history and
+  // hence need to make sure that we get restyled whether we were visited or
+  // not before.  In particular, we need to make sure that our LinkState() is
+  // called so that we'll start a new history query as needed.
+  if (aNotify && doc) {
+    PRUint32 changedState = NS_EVENT_STATE_VISITED ^ NS_EVENT_STATE_UNVISITED;
+    MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, aNotify);
+    doc->ContentStatesChanged(content, nsnull, changedState);
+  }
+}
+
+void
+Link::UnregisterFromHistory()
+{
+  // If we are not registered, we have nothing to do.
+  if (!mRegistered) {
+    return;
   }
-  mLinkState = defaultState;
+
+  NS_ASSERTION(mCachedURI, "mRegistered is true, but we have no cached URI?!");
+
+  // And tell History to stop tracking us.
+  IHistory *history = nsContentUtils::GetHistory();
+  nsresult rv = history->UnregisterVisitedCallback(mCachedURI, this);
+  NS_ASSERTION(NS_SUCCEEDED(rv), "This should only fail if we misuse the API!");
+  if (NS_SUCCEEDED(rv)) {
+    mRegistered = false;
+  }
+}
+
+already_AddRefed<nsIURI>
+Link::GetURIToMutate()
+{
+  nsCOMPtr<nsIURI> uri(GetURI());
+  if (!uri) {
+    return nsnull;
+  }
+  nsCOMPtr<nsIURI> clone;
+  (void)uri->Clone(getter_AddRefs(clone));
+  return clone.forget();
+}
+
+void
+Link::SetHrefAttribute(nsIURI *aURI)
+{
+  NS_ASSERTION(aURI, "Null URI is illegal!");
+
+  nsCAutoString href;
+  (void)aURI->GetSpec(href);
+  (void)Content()->SetAttr(kNameSpaceID_None, nsGkAtoms::href,
+                           NS_ConvertUTF8toUTF16(href), PR_TRUE);
+}
+
+nsIContent *
+Link::Content()
+{
+  if (NS_LIKELY(mContent)) {
+    return mContent;
+  }
+
+  nsCOMPtr<nsIContent> content(do_QueryInterface(this));
+  NS_ABORT_IF_FALSE(content, "This must be able to QI to nsIContent!");
+  return mContent = content;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -44,29 +44,94 @@
 #ifndef mozilla_dom_Link_h__
 #define mozilla_dom_Link_h__
 
 #include "nsIContent.h"
 
 namespace mozilla {
 namespace dom {
 
+#define MOZILLA_DOM_LINK_IMPLEMENTATION_IID \
+  { 0x0c032581, 0x1c8a, 0x4fff, \
+    { 0xbe, 0x9a, 0xfb, 0x6e, 0x2e, 0xe4, 0x22, 0x2e } }
+
 class Link : public nsISupports
 {
 public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
+
   static const nsLinkState defaultState = eLinkState_Unknown;
   Link();
-  virtual nsLinkState GetLinkState() const;
+  nsLinkState GetLinkState() const;
   virtual void SetLinkState(nsLinkState aState);
 
+  /**
+   * @return NS_EVENT_STATE_VISITED if this link is visited,
+   *         NS_EVENT_STATE_UNVISTED if this link is not visited, or 0 if this
+   *         link is not actually a link.
+   */
+  PRInt32 LinkState() const;
+
+  /**
+   * @return the URI this link is for, if available.
+   */
+  already_AddRefed<nsIURI> GetURI() const;
+
+  /**
+   * Helper methods for modifying and obtaining parts of the URI of the Link.
+   */
+  nsresult SetProtocol(const nsAString &aProtocol);
+  nsresult SetHost(const nsAString &aHost);
+  nsresult SetHostname(const nsAString &aHostname);
+  nsresult SetPathname(const nsAString &aPathname);
+  nsresult SetSearch(const nsAString &aSearch);
+  nsresult SetPort(const nsAString &aPort);
+  nsresult SetHash(const nsAString &aHash);
+  nsresult GetProtocol(nsAString &_protocol);
+  nsresult GetHost(nsAString &_host);
+  nsresult GetHostname(nsAString &_hostname);
+  nsresult GetPathname(nsAString &_pathname);
+  nsresult GetSearch(nsAString &_search);
+  nsresult GetPort(nsAString &_port);
+  nsresult GetHash(nsAString &_hash);
+
+  /**
+   * Invalidates any link caching, and resets the state to the default.
+   *
+   * @param aNotify
+   *        true if ResetLinkState should notify the owning document about style
+   *        changes or false if it should not.
+   */
+  void ResetLinkState(bool aNotify);
+
 protected:
+  virtual ~Link();
+
+private:
   /**
-   * Invalidates any link caching, and resets the state to the default.
+   * Unregisters from History so this node no longer gets notifications about
+   * changes to visitedness.
    */
-  virtual void ResetLinkState();
+  void UnregisterFromHistory();
+
+  already_AddRefed<nsIURI> GetURIToMutate();
+  void SetHrefAttribute(nsIURI *aURI);
 
   nsLinkState mLinkState;
+
+  mutable nsCOMPtr<nsIURI> mCachedURI;
+
+  bool mRegistered;
+
+  /**
+   * Obtains a pointer to the nsIContent interface that classes inheriting from
+   * this should also inherit from.
+   */
+  nsIContent *Content();
+  nsIContent *mContent;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(Link, MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Link_h__
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -95,16 +95,17 @@ CPPSRCS		= \
 		nsDOMLists.cpp \
 		nsDOMParser.cpp \
 		nsDOMSerializer.cpp \
 		nsDOMTokenList.cpp \
 		nsDocument.cpp \
 		nsDocumentEncoder.cpp \
 		nsDocumentFragment.cpp \
 		nsFrameLoader.cpp \
+		nsFormData.cpp \
 		nsGenConImageContent.cpp \
 		nsGenericDOMDataNode.cpp \
 		nsGenericElement.cpp \
 		nsGkAtoms.cpp \
 		nsHTMLContentSerializer.cpp \
 		nsImageLoadingContent.cpp \
 		nsLineBreaker.cpp \
 		nsLoadListenerProxy.cpp \
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -46,17 +46,16 @@
 #include "nsUnicharUtils.h"
 #include "nsICSSStyleRule.h"
 #include "nsCSSDeclaration.h"
 #include "nsIHTMLDocument.h"
 #include "nsIDocument.h"
 #include "nsTPtrArray.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
-#include "nsIURI.h"
 #include "prprf.h"
 #ifdef MOZ_SVG
 #include "nsISVGValue.h"
 #endif
 
 #define MISC_STR_PTR(_cont) \
   reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
 
@@ -255,21 +254,16 @@ nsAttrValue::SetTo(const nsAttrValue& aO
       break;
     }
 #endif
     case eFloatValue:
     {
       cont->mFloatValue = otherCont->mFloatValue;
       break;
     }
-    case eLazyURIValue:
-    {
-      NS_IF_ADDREF(cont->mURI = otherCont->mURI);
-      break;
-    }
     default:
     {
       NS_NOTREACHED("unknown type stored in MiscContainer");
       break;
     }
   }
 
   void* otherPtr = MISC_STR_PTR(otherCont);
@@ -442,26 +436,16 @@ nsAttrValue::ToString(nsAString& aResult
 #endif
     case eFloatValue:
     {
       nsAutoString str;
       str.AppendFloat(GetFloatValue());
       aResult = str;
       break;
     }
-    // No need to do for eLazyURIValue, since that always stores the
-    // original string.
-#ifdef DEBUG
-    case eLazyURIValue:
-    {
-      NS_NOTREACHED("Shouldn't get here");
-      aResult.Truncate();
-      break;
-    }
-#endif
     default:
     {
       aResult.Truncate();
       break;
     }
   }
 }
 
@@ -587,27 +571,16 @@ nsAttrValue::HashValue() const
       return NS_PTR_TO_INT32(cont->mSVGValue);
     }
 #endif
     case eFloatValue:
     {
       // XXX this is crappy, but oh well
       return cont->mFloatValue;
     }
-    case eLazyURIValue:
-    {
-      NS_ASSERTION(static_cast<ValueBaseType>(cont->mStringBits &
-                                              NS_ATTRVALUE_BASETYPE_MASK) ==
-                   eStringBase,
-                   "Unexpected type");
-      nsStringBuffer* str = static_cast<nsStringBuffer*>(MISC_STR_PTR(cont));
-      NS_ASSERTION(str, "How did that happen?");
-      PRUint32 len = str->StorageSize()/sizeof(PRUnichar) - 1;
-      return nsCRT::BufferHashCode(static_cast<PRUnichar*>(str->Data()), len);
-    }
     default:
     {
       NS_NOTREACHED("unknown type stored in MiscContainer");
       return 0;
     }
   }
 }
 
@@ -700,21 +673,16 @@ nsAttrValue::Equals(const nsAttrValue& a
     {
       return thisCont->mSVGValue == otherCont->mSVGValue;
     }
 #endif
     case eFloatValue:
     {
       return thisCont->mFloatValue == otherCont->mFloatValue;
     }
-    case eLazyURIValue:
-    {
-      needsStringComparison = PR_TRUE;
-      break;
-    }
     default:
     {
       NS_NOTREACHED("unknown type stored in MiscContainer");
       return PR_FALSE;
     }
   }
   if (needsStringComparison) {
     if (thisCont->mStringBits == otherCont->mStringBits) {
@@ -1166,68 +1134,16 @@ PRBool nsAttrValue::ParseFloatValue(cons
     serializedFloat.AppendFloat(val);
     SetMiscAtomOrString(serializedFloat.Equals(aString) ? nsnull : &aString);
     return PR_TRUE;
   }
 
   return PR_FALSE;
 }
 
-PRBool nsAttrValue::ParseLazyURIValue(const nsAString& aString)
-{
-  ResetIfSet();
-
-  if (EnsureEmptyMiscContainer()) {
-    MiscContainer* cont = GetMiscContainer();
-    cont->mURI = nsnull;
-    cont->mType = eLazyURIValue;
-
-    // Don't use SetMiscAtomOrString because atomizing URIs is not
-    // likely to do us much good.
-    nsStringBuffer* buf = GetStringBuffer(aString);
-    if (!buf) {
-      return PR_FALSE;
-    }
-    cont->mStringBits = reinterpret_cast<PtrBits>(buf) | eStringBase;
-    
-    return PR_TRUE;
-  }
-
-  return PR_FALSE;
-}
-
-void
-nsAttrValue::CacheURIValue(nsIURI* aURI)
-{
-  NS_PRECONDITION(Type() == eLazyURIValue, "wrong type");
-  NS_PRECONDITION(!GetMiscContainer()->mURI, "Why are we being called?");
-  NS_IF_ADDREF(GetMiscContainer()->mURI = aURI);
-}
-
-void
-nsAttrValue::DropCachedURI()
-{
-  NS_PRECONDITION(Type() == eLazyURIValue, "wrong type");
-  NS_IF_RELEASE(GetMiscContainer()->mURI);
-}
-
-const nsCheapString
-nsAttrValue::GetURIStringValue() const
-{
-  NS_PRECONDITION(Type() == eLazyURIValue, "wrong type");
-  NS_PRECONDITION(static_cast<ValueBaseType>(GetMiscContainer()->mStringBits &
-                                             NS_ATTRVALUE_BASETYPE_MASK) ==
-                  eStringBase,
-                  "Unexpected type");
-  NS_PRECONDITION(MISC_STR_PTR(GetMiscContainer()),
-                  "Should have a string buffer here!");
-  return nsCheapString(static_cast<nsStringBuffer*>
-                                  (MISC_STR_PTR(GetMiscContainer())));
-}
-
 void
 nsAttrValue::SetMiscAtomOrString(const nsAString* aValue)
 {
   NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
   NS_ASSERTION(!GetMiscContainer()->mStringBits,
                "Trying to re-set atom or string!");
   if (aValue) {
     PRUint32 len = aValue->Length();
@@ -1283,21 +1199,16 @@ nsAttrValue::EnsureEmptyMiscContainer()
       }
 #ifdef MOZ_SVG
       case eSVGValue:
       {
         NS_RELEASE(cont->mSVGValue);
         break;
       }
 #endif
-      case eLazyURIValue:
-      {
-        NS_IF_RELEASE(cont->mURI);
-        break;
-      }
       default:
       {
         break;
       }
     }
   }
   else {
     ResetIfSet();
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -49,17 +49,16 @@
 #include "nsStringBuffer.h"
 #include "nsColor.h"
 #include "nsCaseTreatment.h"
 
 typedef PRUptrdiff PtrBits;
 class nsAString;
 class nsIAtom;
 class nsICSSStyleRule;
-class nsIURI;
 class nsISVGValue;
 class nsIDocument;
 template<class E> class nsCOMArray;
 template<class E> class nsTPtrArray;
 
 #define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12
 
 #define NS_ATTRVALUE_BASETYPE_MASK (PtrBits(3))
@@ -116,17 +115,16 @@ public:
     // Values below here won't matter, they'll be always stored in the 'misc'
     // struct.
     eCSSStyleRule = 0x10,
     eAtomArray =    0x11 
 #ifdef MOZ_SVG
     ,eSVGValue =    0x12
 #endif
     ,eFloatValue  = 0x13
-    ,eLazyURIValue = 0x14
   };
 
   ValueType Type() const;
 
   void Reset();
 
   void SetTo(const nsAttrValue& aOther);
   void SetTo(const nsAString& aValue);
@@ -150,20 +148,16 @@ public:
   inline PRInt16 GetEnumValue() const;
   inline float GetPercentValue() const;
   inline nsCOMArray<nsIAtom>* GetAtomArrayValue() const;
   inline nsICSSStyleRule* GetCSSStyleRuleValue() const;
 #ifdef MOZ_SVG
   inline nsISVGValue* GetSVGValue() const;
 #endif
   inline float GetFloatValue() const;
-  inline nsIURI* GetURIValue() const;
-  const nsCheapString GetURIStringValue() const;
-  void CacheURIValue(nsIURI* aURI);
-  void DropCachedURI();
 
   // Methods to get access to atoms we may have
   // Returns the number of atoms we have; 0 if we have none.  It's OK
   // to call this without checking the type first; it handles that.
   PRInt32 GetAtomCount() const;
   // Returns the atom at aIndex (0-based).  Do not call this with
   // aIndex >= GetAtomCount().
   nsIAtom* AtomAt(PRInt32 aIndex) const;
@@ -292,17 +286,16 @@ private:
       PRUint32 mEnumValue;
       PRInt32 mPercent;
       nsICSSStyleRule* mCSSStyleRule;
       nsCOMArray<nsIAtom>* mAtomArray;
 #ifdef MOZ_SVG
       nsISVGValue* mSVGValue;
 #endif
       float mFloatValue;
-      nsIURI* mURI;
     };
   };
 
   inline ValueBaseType BaseType() const;
 
   inline void SetPtrValueAndType(void* aValue, ValueBaseType aType);
   void SetIntValueAndType(PRInt32 aValue, ValueType aType,
                           const nsAString* aStringValue);
@@ -399,23 +392,16 @@ nsAttrValue::GetSVGValue() const
 
 inline float
 nsAttrValue::GetFloatValue() const
 {
   NS_PRECONDITION(Type() == eFloatValue, "wrong type");
   return GetMiscContainer()->mFloatValue;
 }
 
-inline nsIURI*
-nsAttrValue::GetURIValue() const
-{
-  NS_PRECONDITION(Type() == eLazyURIValue, "wrong type");
-  return GetMiscContainer()->mURI;
-}
-
 inline nsAttrValue::ValueBaseType
 nsAttrValue::BaseType() const
 {
   return static_cast<ValueBaseType>(mBits & NS_ATTRVALUE_BASETYPE_MASK);
 }
 
 inline void
 nsAttrValue::SetPtrValueAndType(void* aValue, ValueBaseType aType)
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -93,20 +93,24 @@
 #include "nsIForm.h"
 #include "nsIFormControl.h"
 #include "nsGkAtoms.h"
 #include "nsISupportsPrimitives.h"
 #include "imgIDecoderObserver.h"
 #include "imgIRequest.h"
 #include "imgIContainer.h"
 #include "imgILoader.h"
+#include "mozilla/IHistory.h"
+#include "nsDocShellCID.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadGroup.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
 #include "nsContentPolicyUtils.h"
 #include "nsNodeInfoManager.h"
 #include "nsIXBLService.h"
 #include "nsCRT.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsIDOMDocumentEvent.h"
@@ -196,16 +200,17 @@ nsIParserService *nsContentUtils::sParse
 nsINameSpaceManager *nsContentUtils::sNameSpaceManager;
 nsIIOService *nsContentUtils::sIOService;
 #ifdef MOZ_XTF
 nsIXTFService *nsContentUtils::sXTFService = nsnull;
 #endif
 nsIPrefBranch2 *nsContentUtils::sPrefBranch = nsnull;
 imgILoader *nsContentUtils::sImgLoader;
 imgICache *nsContentUtils::sImgCache;
+mozilla::IHistory *nsContentUtils::sHistory;
 nsIConsoleService *nsContentUtils::sConsoleService;
 nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sEventTable = nsnull;
 nsIStringBundleService *nsContentUtils::sStringBundleService;
 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
 nsIContentPolicy *nsContentUtils::sContentPolicyService;
 PRBool nsContentUtils::sTriedToGetContentPolicy = PR_FALSE;
 nsILineBreaker *nsContentUtils::sLineBreaker;
 nsIWordBreaker *nsContentUtils::sWordBreaker;
@@ -369,16 +374,19 @@ nsContentUtils::Init()
     // no image loading for us.  Oh, well.
     sImgLoader = nsnull;
     sImgCache = nsnull;
   } else {
     if (NS_FAILED(CallGetService("@mozilla.org/image/cache;1", &sImgCache )))
       sImgCache = nsnull;
   }
 
+  rv = CallGetService(NS_IHISTORY_CONTRACTID, &sHistory);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   sPtrsToPtrsToRelease = new nsTArray<nsISupports**>();
   if (!sPtrsToPtrsToRelease) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (!InitializeEventTable())
     return NS_ERROR_FAILURE;
 
@@ -949,16 +957,17 @@ nsContentUtils::Shutdown()
   NS_IF_RELEASE(sWordBreaker);
   NS_IF_RELEASE(sCaseConv);
   NS_IF_RELEASE(sGenCat);
 #ifdef MOZ_XTF
   NS_IF_RELEASE(sXTFService);
 #endif
   NS_IF_RELEASE(sImgLoader);
   NS_IF_RELEASE(sImgCache);
+  NS_IF_RELEASE(sHistory);
   NS_IF_RELEASE(sPrefBranch);
 #ifdef IBMBIDI
   NS_IF_RELEASE(sBidiKeyboard);
 #endif
 
   delete sEventTable;
   sEventTable = nsnull;
 
@@ -3447,16 +3456,40 @@ nsIContent*
 nsContentUtils::GetReferencedElement(nsIURI* aURI, nsIContent *aFromContent)
 {
   nsReferencedElement ref;
   ref.Reset(aFromContent, aURI);
   return ref.get();
 }
 
 /* static */
+void
+nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver)
+{
+  nsCOMPtr<nsIObserverService> observerService =
+    do_GetService("@mozilla.org/observer-service;1");
+  if (observerService) {
+    observerService->AddObserver(aObserver, 
+                                 NS_XPCOM_SHUTDOWN_OBSERVER_ID, 
+                                 PR_FALSE);
+  }
+}
+
+/* static */
+void
+nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver)
+{
+  nsCOMPtr<nsIObserverService> observerService =
+    do_GetService("@mozilla.org/observer-service;1");
+  if (observerService) {
+    observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+  }
+}
+
+/* static */
 PRBool
 nsContentUtils::HasNonEmptyAttr(nsIContent* aContent, PRInt32 aNameSpaceID,
                                 nsIAtom* aName)
 {
   static nsIContent::AttrValuesArray strings[] = {&nsGkAtoms::_empty, nsnull};
   return aContent->FindAttrValueIn(aNameSpaceID, aName, strings, eCaseMatters)
     == nsIContent::ATTR_VALUE_NO_MATCH;
 }
@@ -4857,16 +4890,51 @@ nsContentUtils::GetCurrentJSContext()
 
   sThreadJSContextStack->Peek(&cx);
 
   return cx;
 }
 
 /* static */
 void
+nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest)
+{
+  PRUint32 len = aSource.Length();
+  aDest.SetLength(len);
+  if (aDest.Length() == len) {
+    PRUnichar* dest = aDest.BeginWriting();
+    const PRUnichar* iter = aSource.BeginReading();
+    const PRUnichar* end = aSource.EndReading();
+    while (iter != end) {
+      PRUnichar c = *iter;
+      *dest = (c >= 'A' && c <= 'Z') ?
+         c + ('a' - 'A') : c;
+      ++iter;
+      ++dest;
+    }
+  }
+}
+
+/* static */
+void
+nsContentUtils::ASCIIToUpper(nsAString& aStr)
+{
+  PRUnichar* iter = aStr.BeginWriting();
+  PRUnichar* end = aStr.EndWriting();
+  while (iter != end) {
+    PRUnichar c = *iter;
+    if (c >= 'a' && c <= 'z') {
+      *iter = c + ('A' - 'a');
+    }
+    ++iter;
+  }
+}
+
+/* static */
+void
 nsAutoGCRoot::Shutdown()
 {
   NS_IF_RELEASE(sJSRuntimeService);
 }
 
 nsIAtom*
 nsContentUtils::IsNamedItem(nsIContent* aContent)
 {
@@ -5214,34 +5282,52 @@ nsContentUtils::WrapNative(JSContext *cx
 
     *vp = JSVAL_NULL;
 
     return NS_OK;
   }
 
   NS_ENSURE_TRUE(sXPConnect && sThreadJSContextStack, NS_ERROR_UNEXPECTED);
 
-  // Keep sXPConnect and sThreadJSContextStack alive.
-  nsLayoutStaticsRef layoutStaticsRef;
+  // Keep sXPConnect and sThreadJSContextStack alive. If we're on the main
+  // thread then this can be done simply and cheaply by adding a reference to
+  // nsLayoutStatics. If we're not on the main thread then we need to add a
+  // more expensive reference sXPConnect directly. We have to use manual
+  // AddRef and Release calls so don't early-exit from this function after we've
+  // added the reference!
+  PRBool isMainThread = NS_IsMainThread();
+
+  if (isMainThread) {
+    nsLayoutStatics::AddRef();
+  }
+  else {
+    sXPConnect->AddRef();
+  }
 
   JSContext *topJSContext;
   nsresult rv = sThreadJSContextStack->Peek(&topJSContext);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRBool push = topJSContext != cx;
-  if (push) {
-    rv = sThreadJSContextStack->Push(cx);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, aIID, aAllowWrapping,
-                                     vp, aHolder);
-
-  if (push) {
-    sThreadJSContextStack->Pop(nsnull);
+  if (NS_SUCCEEDED(rv)) {
+    PRBool push = topJSContext != cx;
+    if (push) {
+      rv = sThreadJSContextStack->Push(cx);
+    }
+    if (NS_SUCCEEDED(rv)) {
+      rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, aIID,
+                                         aAllowWrapping, vp, aHolder);
+      if (push) {
+        sThreadJSContextStack->Pop(nsnull);
+      }
+    }
+  }
+
+  if (isMainThread) {
+    nsLayoutStatics::Release();
+  }
+  else {
+    sXPConnect->Release();
   }
 
   return rv;
 }
 
 #ifdef DEBUG
 class DebugWrapperTraversalCallback : public nsCycleCollectionTraversalCallback
 {
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -184,140 +184,28 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #include "nsSMILAnimationController.h"
 #include "imgIContainer.h"
 #include "nsSVGUtils.h"
 #endif // MOZ_SMIL
 
 // FOR CSP (autogenerated by xpidl)
 #include "nsIContentSecurityPolicy.h"
 
+#include "mozilla/dom/Link.h"
+using namespace mozilla::dom;
+
+
 /* Keeps track of whether or not CSP is enabled */
 static PRBool gCSPEnabled = PR_TRUE;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
-void
-nsUint32ToContentHashEntry::Destroy()
-{
-  HashSet* set = GetHashSet();
-  if (set) {
-    delete set;
-  } else {
-    nsIContent* content = GetContent();
-    NS_IF_RELEASE(content);
-  }
-}
-
-nsresult
-nsUint32ToContentHashEntry::PutContent(nsIContent* aVal)
-{
-  // Add the value to the hash if it is there
-  HashSet* set = GetHashSet();
-  if (set) {
-    nsISupportsHashKey* entry = set->PutEntry(aVal);
-    return entry ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  // If an element is already there, create a hashtable and both of these to it
-  nsIContent* oldVal = GetContent();
-  if (oldVal) {
-    nsresult rv = InitHashSet(&set);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsISupportsHashKey* entry = set->PutEntry(oldVal);
-    if (!entry) {
-      // OOM - we can't insert aVal, but we can at least put oldVal back (even
-      // if we didn't, we'd still have to release oldVal so that we don't leak)
-      delete set;
-      SetContent(oldVal);
-      // SetContent adds another reference, so release the one we had
-      NS_RELEASE(oldVal);
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    // The hashset adds its own reference, so release the one we had
-    NS_RELEASE(oldVal);
-
-    entry = set->PutEntry(aVal);
-    return entry ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  // Nothing exists in the hash right now, so just set the single pointer
-  return SetContent(aVal);
-}
-
-void
-nsUint32ToContentHashEntry::RemoveContent(nsIContent* aVal)
-{
-  // Remove from the hash if the hash is there
-  HashSet* set = GetHashSet();
-  if (set) {
-    set->RemoveEntry(aVal);
-    if (set->Count() == 0) {
-      delete set;
-      mValOrHash = nsnull;
-    }
-    return;
-  }
-
-  // Remove the ptr if there is just a ptr
-  nsIContent* v = GetContent();
-  if (v == aVal) {
-    NS_IF_RELEASE(v);
-    mValOrHash = nsnull;
-  }
-}
-
-nsresult
-nsUint32ToContentHashEntry::InitHashSet(HashSet** aSet)
-{
-  HashSet* newSet = new HashSet();
-  if (!newSet) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  nsresult rv = newSet->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mValOrHash = newSet;
-  *aSet = newSet;
-  return NS_OK;
-}
-
-static PLDHashOperator
-nsUint32ToContentHashEntryVisitorCallback(nsISupportsHashKey* aEntry,
-                                          void* aClosure)
-{
-  nsUint32ToContentHashEntry::Visitor* visitor =
-    static_cast<nsUint32ToContentHashEntry::Visitor*>(aClosure);
-  visitor->Visit(static_cast<nsIContent*>(aEntry->GetKey()));
-  return PL_DHASH_NEXT;
-}
-
-void
-nsUint32ToContentHashEntry::VisitContent(Visitor* aVisitor)
-{
-  HashSet* set = GetHashSet();
-  if (set) {
-    set->EnumerateEntries(nsUint32ToContentHashEntryVisitorCallback, aVisitor);
-    if (set->Count() == 0) {
-      delete set;
-      mValOrHash = nsnull;
-    }
-    return;
-  }
-
-  nsIContent* v = GetContent();
-  if (v) {
-    aVisitor->Visit(v);
-  }
-}
-
 #define NAME_NOT_VALID ((nsBaseContentList*)1)
 
 nsIdentifierMapEntry::~nsIdentifierMapEntry()
 {
   if (mNameContentList && mNameContentList != NAME_NOT_VALID) {
     NS_RELEASE(mNameContentList);
   }
 
@@ -1711,36 +1599,16 @@ BoxObjectTraverser(const void* key, nsPI
     static_cast<nsCycleCollectionTraversalCallback*>(userArg);
  
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry");
   cb->NoteXPCOMChild(boxObject);
 
   return PL_DHASH_NEXT;
 }
 
-class LinkMapTraversalVisitor : public nsUint32ToContentHashEntry::Visitor
-{
-public:
-  nsCycleCollectionTraversalCallback *mCb;
-  virtual void Visit(nsIContent* aContent)
-  {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*mCb, "mLinkMap entry");
-    mCb->NoteXPCOMChild(aContent);
-  }
-};
-
-static PLDHashOperator
-LinkMapTraverser(nsUint32ToContentHashEntry* aEntry, void* userArg)
-{
-  LinkMapTraversalVisitor visitor;
-  visitor.mCb = static_cast<nsCycleCollectionTraversalCallback*>(userArg);
-  aEntry->VisitContent(&visitor);
-  return PL_DHASH_NEXT;
-}
-
 static PLDHashOperator
 IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
 {
   nsCycleCollectionTraversalCallback *cb =
     static_cast<nsCycleCollectionTraversalCallback*>(aArg);
   aEntry->Traverse(cb);
   return PL_DHASH_NEXT;
 }
@@ -1795,26 +1663,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptEventManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXPathEvaluatorTearoff)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLayoutHistoryState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnloadBlocker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMImplementation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginalDocument)
 
-  // An element will only be in the linkmap as long as it's in the
-  // document, so we'll traverse the table here instead of from the element.
-  if (tmp->mLinkMap.IsInitialized()) {
-    tmp->mLinkMap.EnumerateEntries(LinkMapTraverser, &cb);
-  }
-
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mVisitednessChangedURIs)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPreloadingImages)
 
 #ifdef MOZ_SMIL
   // Traverse animation components
   if (tmp->mAnimationController) {
     tmp->mAnimationController->Traverse(&cb);
   }
 #endif // MOZ_SMIL
@@ -1826,21 +1687,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
-  // Tear down linkmap. This is a performance optimization so that we
-  // don't waste time removing links one by one as they are removed
-  // from the doc.
-  tmp->DestroyLinkMap();
-
   // Clear out our external resources
   tmp->mExternalResourceMap.Shutdown();
 
   nsAutoScriptBlocker scriptBlocker;
 
   // Unlink the mChildren nsAttrAndChildArray.
   for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()) - 1; 
        indx >= 0; --indx) {
@@ -1872,17 +1728,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 nsresult
 nsDocument::Init()
 {
   if (mCSSLoader || mNodeInfoManager || mScriptLoader) {
     return NS_ERROR_ALREADY_INITIALIZED;
   }
 
   mIdentifierMap.Init();
-  mLinkMap.Init();
+  (void)mStyledLinks.Init();
   mRadioGroups.Init();
 
   // Force initialization.
   nsINode::nsSlots* slots = GetSlots();
   NS_ENSURE_TRUE(slots,NS_ERROR_OUT_OF_MEMORY);
 
   // Prepend self as mutation-observer whether we need it or not (some
   // subclasses currently do, other don't). This is because the code in
@@ -5063,30 +4919,21 @@ nsDocument::GetTitleContent(PRUint32 aNa
   // <title> element has been bound to this document. So if it's false,
   // we know there is nothing to do here. This avoids us having to search
   // the whole DOM if someone calls document.title on a large document
   // without a title.
   if (!mMayHaveTitleElement)
     return nsnull;
 
   nsRefPtr<nsContentList> list =
-    NS_GetContentList(this, nsGkAtoms::title, kNameSpaceID_Unknown);
+    NS_GetContentList(this, nsGkAtoms::title, aNamespace);
   if (!list)
     return nsnull;
 
-  for (PRUint32 i = 0; ; ++i) {
-    // Avoid calling list->Length --- by calling Item one at a time,
-    // we can avoid scanning the whole document to build the list of all
-    // matches
-    nsIContent* elem = list->Item(i, PR_FALSE);
-    if (!elem)
-      return nsnull;
-    if (elem->IsInNamespace(aNamespace))
-      return elem;
-  }
+  return list->Item(0, PR_FALSE);
 }
 
 void
 nsDocument::GetTitleFromElement(PRUint32 aNamespace, nsAString& aTitle)
 {
   nsIContent* title = GetTitleContent(aNamespace);
   if (!title)
     return;
@@ -7315,18 +7162,16 @@ void
 nsDocument::OnPageShow(PRBool aPersisted,
                        nsIDOMEventTarget* aDispatchStartTarget)
 {
   mVisible = PR_TRUE;
 
   EnumerateFreezableElements(NotifyActivityChanged, nsnull);
   EnumerateExternalResources(NotifyPageShow, &aPersisted);
 
-  UpdateLinkMap();
-  
   nsIContent* root = GetRootContent();
   if (aPersisted && root) {
     // Send out notifications that our <link> elements are attached.
     nsRefPtr<nsContentList> links = NS_GetContentList(root,
                                                       nsGkAtoms::link,
                                                       kNameSpaceID_Unknown);
 
     if (links) {
@@ -7486,185 +7331,70 @@ nsDocument::MutationEventDispatched(nsIN
 static PRUint32 GetURIHash(nsIURI* aURI)
 {
   nsCAutoString str;
   aURI->GetSpec(str);
   return HashString(str);
 }
 
 void
-nsDocument::AddStyleRelevantLink(nsIContent* aContent, nsIURI* aURI)
-{
-  nsUint32ToContentHashEntry* entry = mLinkMap.PutEntry(GetURIHash(aURI));
-  if (!entry) // out of memory?
-    return;
-  entry->PutContent(aContent);
+nsDocument::AddStyleRelevantLink(Link* aLink)
+{
+  NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
+#ifdef DEBUG
+  nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
+  NS_ASSERTION(!entry, "Document already knows about this Link!");
+  mStyledLinksCleared = false;
+#endif
+  (void)mStyledLinks.PutEntry(aLink);
 }
 
 void
-nsDocument::ForgetLink(nsIContent* aContent)
-{
-  // Important optimization! If the link map is empty (as it will be
-  // during teardown because we destroy the map early), then stop
-  // now before we waste time constructing a URI object.
-  if (mLinkMap.Count() == 0)
-    return;
-
-  nsCOMPtr<nsIURI> uri;
-  if (!aContent->IsLink(getter_AddRefs(uri)))
-    return;
-  PRUint32 hash = GetURIHash(uri);
-  nsUint32ToContentHashEntry* entry = mLinkMap.GetEntry(hash);
-  if (!entry)
-    return;
-
-  entry->RemoveContent(aContent);
-  if (entry->IsEmpty()) {
-    // Remove the entry and allow the table to resize, in case
-    // a lot of links are being removed from the document or modified
-    mLinkMap.RemoveEntry(hash);
-  }
-}
-
-class URIVisitNotifier : public nsUint32ToContentHashEntry::Visitor
-{
-public:
-  nsCAutoString matchURISpec;
-  nsCOMArray<nsIContent> contentVisited;
-  
-  virtual void Visit(nsIContent* aContent) {
-    // Ensure that the URIs really match before we try to do anything
-    nsCOMPtr<nsIURI> uri;
-    if (!aContent->IsLink(getter_AddRefs(uri))) {
-      NS_ERROR("Should have found a URI for content in the link map");
-      return;
-    }
-    nsCAutoString spec;
-    uri->GetSpec(spec);
-    // We use nsCString::Equals here instead of nsIURI::Equals because
-    // history matching is all based on spec equality
-    if (!spec.Equals(matchURISpec))
-      return;
-
-    // Throw away the cached link state so it gets refetched by the style
-    // system.  We can't call ContentStatesChanged here, because that might
-    // modify the hashtable.  Instead, we'll just insert into this array and
-    // leave it to our caller to call ContentStatesChanged.
-    aContent->SetLinkState(eLinkState_Unknown);
-    contentVisited.AppendObject(aContent);
-  }
-};
-
-void
-nsDocument::NotifyURIVisitednessChanged(nsIURI* aURI)
-{
-  if (!mVisible) {
-    mVisitednessChangedURIs.AppendObject(aURI);
-    return;
-  }
-
-  nsUint32ToContentHashEntry* entry = mLinkMap.GetEntry(GetURIHash(aURI));
-  if (!entry)
-    return;
-
-  URIVisitNotifier visitor;
-  aURI->GetSpec(visitor.matchURISpec);
-  entry->VisitContent(&visitor);
-
-  MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_STATE, PR_TRUE);
-  for (PRUint32 count = visitor.contentVisited.Count(), i = 0; i < count; ++i) {
-    ContentStatesChanged(visitor.contentVisited[i],
-                         nsnull, NS_EVENT_STATE_VISITED);
-  }
+nsDocument::ForgetLink(Link* aLink)
+{
+  NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
+#ifdef DEBUG
+  nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
+  NS_ASSERTION(entry || mStyledLinksCleared,
+               "Document knows nothing about this Link!");
+#endif
+  (void)mStyledLinks.RemoveEntry(aLink);
 }
 
 void
 nsDocument::DestroyLinkMap()
 {
-  mVisitednessChangedURIs.Clear();
-  mLinkMap.Clear();
-}
-
-void
-nsDocument::UpdateLinkMap()
-{
-  NS_ASSERTION(mVisible,
-               "Should only be updating the link map in visible documents");
-  if (!mVisible)
-    return;
-
-  PRInt32 count = mVisitednessChangedURIs.Count();
-  for (PRInt32 i = 0; i < count; ++i) {
-    NotifyURIVisitednessChanged(mVisitednessChangedURIs[i]);
-  }
-  mVisitednessChangedURIs.Clear();
-}
-
-class RefreshLinkStateVisitor : public nsUint32ToContentHashEntry::Visitor
-{
-public:
-  nsCOMArray<nsIContent> contentVisited;
-
-  virtual void Visit(nsIContent* aContent) {
-    // We can't call ContentStatesChanged here, because that may modify the link
-    // map.  Instead, we just add to an array and call ContentStatesChanged
-    // later.
-    aContent->SetLinkState(eLinkState_Unknown);
-    contentVisited.AppendObject(aContent);
-  }
-};
-
-static PLDHashOperator
-RefreshLinkStateTraverser(nsUint32ToContentHashEntry* aEntry,
-                               void* userArg)
-{
-  RefreshLinkStateVisitor *visitor =
-    static_cast<RefreshLinkStateVisitor*>(userArg);
-
-  aEntry->VisitContent(visitor);
+#ifdef DEBUG
+  mStyledLinksCleared = true;
+#endif
+  mStyledLinks.Clear();
+}
+
+static
+PLDHashOperator
+EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray)
+{
+  nsTArray<Link*>* array = static_cast<nsTArray<Link*>*>(aArray);
+  (void)array->AppendElement(aEntry->GetKey());
   return PL_DHASH_NEXT;
 }
 
-
-// Helper function for nsDocument::RefreshLinkHrefs
-static void
-DropCachedHrefsRecursive(nsIContent * const elem)
-{
-  // Drop the element's cached href, if it has one.  (If it doesn't have
-  // one, this call does nothing.)  We could check first that elem is an <a>
-  // tag to avoid making a virtual call, but it turns out not to make a
-  // substantial perf difference either way.  This doesn't restyle the link,
-  // but we do that later.
-  elem->DropCachedHref();
-
-  PRUint32 childCount;
-  nsIContent * const * child = elem->GetChildArray(&childCount);
-  nsIContent * const * end = child + childCount;
-  for ( ; child != end; ++child) {
-    DropCachedHrefsRecursive(*child);
-  }
-}
-
 void
 nsDocument::RefreshLinkHrefs()
 {
-  if (!GetRootContent())
-    return;
-
-  // First, walk the DOM and clear the cached hrefs of all the <a> tags.
-  DropCachedHrefsRecursive(GetRootContent());
-
-  // Now update the styles of everything in the linkmap.
-  RefreshLinkStateVisitor visitor;
-  mLinkMap.EnumerateEntries(RefreshLinkStateTraverser, &visitor);
-
+  // Get a list of all links we know about.  We will reset them, which will
+  // remove them from the document, so we need a copy of what is in the
+  // hashtable.
+  nsTArray<Link*> linksToNotify(mStyledLinks.Count());
+  (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify);
+
+  // Reset all of our styled links.
   MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_STATE, PR_TRUE);
-  for (PRUint32 count = visitor.contentVisited.Count(), i = 0; i < count; i++) {
-    ContentStatesChanged(visitor.contentVisited[i],
-                         nsnull, NS_EVENT_STATE_VISITED);
+  for (nsTArray_base::size_type i = 0; i < linksToNotify.Length(); i++) {
+    linksToNotify[i]->ResetLinkState(true);
   }
 }
 
 nsIContent*
 nsDocument::GetFirstBaseNodeWithHref()
 {
   return mFirstBaseNodeWithHref;
 }
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -135,93 +135,16 @@ class nsIFormControl;
 struct nsRadioGroupStruct;
 class nsOnloadBlocker;
 class nsUnblockOnloadEvent;
 class nsChildContentList;
 #ifdef MOZ_SMIL
 class nsSMILAnimationController;
 #endif // MOZ_SMIL
 
-/**
- * Hashentry using a PRUint32 key and a cheap set of nsIContent* owning
- * pointers for the value.
- *
- * @see nsTHashtable::EntryType for specification
- */
-class nsUint32ToContentHashEntry : public PLDHashEntryHdr
-{
-  public:
-    typedef const PRUint32& KeyType;
-    typedef const PRUint32* KeyTypePointer;
-
-    nsUint32ToContentHashEntry(const KeyTypePointer key) :
-      mValue(*key), mValOrHash(nsnull) { }
-    nsUint32ToContentHashEntry(const nsUint32ToContentHashEntry& toCopy) :
-      mValue(toCopy.mValue), mValOrHash(toCopy.mValOrHash)
-    {
-      // Pathetic attempt to not die: clear out the other mValOrHash so we're
-      // effectively stealing it. If toCopy is destroyed right after this,
-      // we'll be OK.
-      const_cast<nsUint32ToContentHashEntry&>(toCopy).mValOrHash = nsnull;
-      NS_ERROR("Copying not supported. Fasten your seat belt.");
-    }
-    ~nsUint32ToContentHashEntry() { Destroy(); }
-
-    KeyType GetKey() const { return mValue; }
-
-    PRBool KeyEquals(KeyTypePointer aKey) const { return mValue == *aKey; }
-
-    static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
-    static PLDHashNumber HashKey(KeyTypePointer aKey) { return *aKey; }
-    enum { ALLOW_MEMMOVE = PR_TRUE };
-
-    // Content set methods
-    nsresult PutContent(nsIContent* aContent);
-
-    void RemoveContent(nsIContent* aContent);
-
-    struct Visitor {
-      virtual void Visit(nsIContent* aContent) = 0;
-    };
-    void VisitContent(Visitor* aVisitor);
-
-    PRBool IsEmpty() { return mValOrHash == nsnull; }
-
-  private:
-    typedef PRUptrdiff PtrBits;
-    typedef nsTHashtable<nsISupportsHashKey> HashSet;
-    /** Get the hash pointer (or null if we're not a hash) */
-    HashSet* GetHashSet()
-    {
-      return (PtrBits(mValOrHash) & 0x1) ? nsnull : (HashSet*)mValOrHash;
-    }
-    /** Find out whether it is an nsIContent (returns weak) */
-    nsIContent* GetContent()
-    {
-      return (PtrBits(mValOrHash) & 0x1)
-             ? (nsIContent*)(PtrBits(mValOrHash) & ~0x1)
-             : nsnull;
-    }
-    /** Set the single element, adding a reference */
-    nsresult SetContent(nsIContent* aVal)
-    {
-      NS_IF_ADDREF(aVal);
-      mValOrHash = (void*)(PtrBits(aVal) | 0x1);
-      return NS_OK;
-    }
-    /** Initialize the hash */
-    nsresult InitHashSet(HashSet** aSet);
-
-    void Destroy();
-
-  private:
-    const PRUint32 mValue;
-    /** A hash or nsIContent ptr, depending on the lower bit (0=hash, 1=ptr) */
-    void* mValOrHash;
-};
 
 /**
  * Right now our identifier map entries contain information for 'name'
  * and 'id' mappings of a given string. This is so that
  * nsHTMLDocument::ResolveName only has to do one hash lookup instead
  * of two. It's not clear whether this still matters for performance.
  * 
  * We also store the document.all result list here. This is mainly so that
@@ -938,19 +861,18 @@ public:
   virtual NS_HIDDEN_(PRBool) CanSavePresentation(nsIRequest *aNewRequest);
   virtual NS_HIDDEN_(void) Destroy();
   virtual NS_HIDDEN_(void) RemovedFromDocShell();
   virtual NS_HIDDEN_(already_AddRefed<nsILayoutHistoryState>) GetLayoutHistoryState() const;
 
   virtual NS_HIDDEN_(void) BlockOnload();
   virtual NS_HIDDEN_(void) UnblockOnload(PRBool aFireSync);
 
-  virtual NS_HIDDEN_(void) AddStyleRelevantLink(nsIContent* aContent, nsIURI* aURI);
-  virtual NS_HIDDEN_(void) ForgetLink(nsIContent* aContent);
-  virtual NS_HIDDEN_(void) NotifyURIVisitednessChanged(nsIURI* aURI);
+  virtual NS_HIDDEN_(void) AddStyleRelevantLink(mozilla::dom::Link* aLink);
+  virtual NS_HIDDEN_(void) ForgetLink(mozilla::dom::Link* aLink);
 
   NS_HIDDEN_(void) ClearBoxObjectFor(nsIContent* aContent);
   NS_IMETHOD GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult);
 
   virtual NS_HIDDEN_(nsresult) GetXBLChildNodesFor(nsIContent* aContent,
                                                    nsIDOMNodeList** aResult);
   virtual NS_HIDDEN_(nsresult) GetContentListFor(nsIContent* aContent,
                                                  nsIDOMNodeList** aResult);
@@ -1033,17 +955,16 @@ protected:
   void DispatchContentLoadedEvents();
 
   void RetrieveRelevantHeaders(nsIChannel *aChannel);
 
   static PRBool TryChannelCharset(nsIChannel *aChannel,
                                   PRInt32& aCharsetSource,
                                   nsACString& aCharset);
 
-  void UpdateLinkMap();
   // Call this before the document does something that will unbind all content.
   // That will stop us from resolving URIs for all links as they are removed.
   void DestroyLinkMap();
 
   // Refreshes the hrefs of all the links in the document.
   void RefreshLinkHrefs();
 
   nsIContent* GetFirstBaseNodeWithHref();
@@ -1238,21 +1159,25 @@ private:
   // document.  We only actually store a pointer to it when:
   // 1)  We have no script global object.
   // 2)  We haven't had Destroy() called on us yet.
   nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
 
   PRUint32 mOnloadBlockCount;
   nsCOMPtr<nsIRequest> mOnloadBlocker;
   ReadyState mReadyState;
-  
-  // A map from unvisited URI hashes to content elements
-  nsTHashtable<nsUint32ToContentHashEntry> mLinkMap;
-  // URIs whose visitedness has changed while we were hidden
-  nsCOMArray<nsIURI> mVisitednessChangedURIs;
+
+  // A hashtable of styled links keyed by address pointer.
+  nsTHashtable<nsPtrHashKey<mozilla::dom::Link> > mStyledLinks;
+#ifdef DEBUG
+  // Indicates whether mStyledLinks was cleared or not.  This is used to track
+  // state so we can provide useful assertions to consumers of ForgetLink and
+  // AddStyleRelevantLink.
+  bool mStyledLinksCleared;
+#endif
 
   // Member to store out last-selected stylesheet set.
   nsString mLastStyleSheetSet;
 
   nsTArray<nsRefPtr<nsFrameLoader> > mInitializableFrameLoaders;
   nsTArray<nsRefPtr<nsFrameLoader> > mFinalizableFrameLoaders;
   nsRefPtr<nsRunnableMethod<nsDocument> > mFrameLoaderRunner;
 
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsFormData.cpp
@@ -0,0 +1,158 @@
+/* ***** 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
+ * Mozilla Foundation.
+ * 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 ***** */
+
+#include "nsFormData.h"
+#include "nsIVariant.h"
+#include "nsIDOMFileInternal.h"
+#include "nsIInputStream.h"
+#include "nsIFile.h"
+#include "nsContentUtils.h"
+
+nsFormData::nsFormData()
+  : nsFormSubmission(NS_LITERAL_CSTRING("UTF-8"))
+{
+}
+
+// -------------------------------------------------------------------------
+// nsISupports
+
+NS_IMPL_ADDREF(nsFormData)
+NS_IMPL_RELEASE(nsFormData)
+NS_INTERFACE_MAP_BEGIN(nsFormData)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMFormData)
+  NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
+  NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(FormData)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormData)
+NS_INTERFACE_MAP_END
+
+// -------------------------------------------------------------------------
+// nsFormSubmission
+nsresult
+nsFormData::GetEncodedSubmission(nsIURI* aURI,
+                                 nsIInputStream** aPostDataStream)
+{
+  NS_NOTREACHED("Shouldn't call nsFormData::GetEncodedSubmission");
+  return NS_OK;
+}
+
+nsresult
+nsFormData::AddNameValuePair(const nsAString& aName,
+                             const nsAString& aValue)
+{
+  FormDataTuple* data = mFormData.AppendElement();
+  data->name = aName;
+  data->stringValue = aValue;
+  data->valueIsFile = PR_FALSE;
+
+  return NS_OK;
+}
+
+nsresult
+nsFormData::AddNameFilePair(const nsAString& aName,
+                            nsIFile* aFile)
+{
+  FormDataTuple* data = mFormData.AppendElement();
+  data->name = aName;
+  data->fileValue = aFile;
+  data->valueIsFile = PR_TRUE;
+
+  return NS_OK;
+}
+
+// -------------------------------------------------------------------------
+// nsIDOMFormData
+
+NS_IMETHODIMP
+nsFormData::Append(const nsAString& aName, nsIVariant* aValue)
+{
+  PRUint16 dataType;
+  nsresult rv = aValue->GetDataType(&dataType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (dataType == nsIDataType::VTYPE_INTERFACE ||
+      dataType == nsIDataType::VTYPE_INTERFACE_IS) {
+    nsCOMPtr<nsISupports> supports;
+    nsID *iid;
+    rv = aValue->GetAsInterface(&iid, getter_AddRefs(supports));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsMemory::Free(iid);
+
+    nsCOMPtr<nsIDOMFileInternal> domFile = do_QueryInterface(supports);
+    if (domFile) {
+      nsCOMPtr<nsIFile> file;
+      rv = domFile->GetInternalFile(getter_AddRefs(file));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      return AddNameFilePair(aName, file);
+    }
+  }
+
+  PRUnichar* stringData = nsnull;
+  PRUint32 stringLen = 0;
+  rv = aValue->GetAsWStringWithSize(&stringLen, &stringData);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString valAsString;
+  valAsString.Adopt(stringData, stringLen);
+
+  return AddNameValuePair(aName, valAsString);
+}
+
+// -------------------------------------------------------------------------
+// nsIDXHRSendable
+
+NS_IMETHODIMP
+nsFormData::GetSendInfo(nsIInputStream** aBody, nsACString& aContentType,
+                        nsACString& aCharset)
+{
+  nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"));
+  
+  for (PRUint32 i = 0; i < mFormData.Length(); ++i) {
+    if (mFormData[i].valueIsFile) {
+      fs.AddNameFilePair(mFormData[i].name, mFormData[i].fileValue);
+    }
+    else {
+      fs.AddNameValuePair(mFormData[i].name, mFormData[i].stringValue);
+    }
+  }
+
+  fs.GetContentType(aContentType);
+  aCharset.Truncate();
+  NS_ADDREF(*aBody = fs.GetSubmissionBody());
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsFormData.h
@@ -0,0 +1,78 @@
+/* ***** 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
+ * Mozilla Foundation.
+ * 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 ***** */
+
+#ifndef nsFormData_h__
+#define nsFormData_h__
+
+#include "nsIDOMFormData.h"
+#include "nsIXMLHttpRequest.h"
+#include "nsIFormSubmission.h"
+#include "nsTArray.h"
+
+class nsFormData : public nsIDOMFormData,
+                   public nsIXHRSendable,
+                   public nsFormSubmission
+{
+public:
+  nsFormData();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMFORMDATA
+  NS_DECL_NSIXHRSENDABLE
+
+  // nsFormSubmission
+  virtual nsresult GetEncodedSubmission(nsIURI* aURI,
+                                        nsIInputStream** aPostDataStream);
+  virtual nsresult AddNameValuePair(const nsAString& aName,
+                                    const nsAString& aValue);
+  virtual nsresult AddNameFilePair(const nsAString& aName,
+                                   nsIFile* aFile);
+
+  
+
+private:
+  struct FormDataTuple
+  {
+    nsString name;
+    nsString stringValue;
+    nsCOMPtr<nsIFile> fileValue;
+    PRBool valueIsFile;
+  };
+  
+  nsTArray<FormDataTuple> mFormData;
+};
+
+#endif // nsFormData_h__
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -78,17 +78,20 @@
 #include "nsIFrame.h"
 #include "nsIFrameFrame.h"
 #include "nsDOMError.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsISHistory.h"
 #include "nsISHistoryInternal.h"
 #include "nsIDOMNSHTMLDocument.h"
+
 #include "nsLayoutUtils.h"
+#include "nsIView.h"
+#include "nsPLDOMEvent.h"
 
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 
 #include "nsGkAtoms.h"
 #include "nsINameSpaceManager.h"
 
@@ -206,18 +209,38 @@ nsFrameLoader::LoadFrame()
   nsresult rv = NS_NewURI(getter_AddRefs(uri), src, charset, base_uri);
 
   // If the URI was malformed, try to recover by loading about:blank.
   if (rv == NS_ERROR_MALFORMED_URI) {
     rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("about:blank"),
                    charset, base_uri);
   }
 
-  NS_ENSURE_SUCCESS(rv, rv);
-  return LoadURI(uri);
+  if (NS_SUCCEEDED(rv)) {
+    rv = LoadURI(uri);
+  }
+  
+  if (NS_FAILED(rv)) {
+    FireErrorEvent();
+
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+nsFrameLoader::FireErrorEvent()
+{
+  if (mOwnerContent) {
+    nsRefPtr<nsPLDOMEvent> event =
+      new nsLoadBlockingPLDOMEvent(mOwnerContent, NS_LITERAL_STRING("error"),
+                                   PR_FALSE, PR_FALSE);
+    event->PostDOMEvent();
+  }
 }
 
 NS_IMETHODIMP
 nsFrameLoader::LoadURI(nsIURI* aURI)
 {
   if (!aURI)
     return NS_ERROR_INVALID_POINTER;
   NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
@@ -236,16 +259,27 @@ nsFrameLoader::LoadURI(nsIURI* aURI)
     mURIToLoad = nsnull;
   }
   return rv;
 }
 
 nsresult
 nsFrameLoader::ReallyStartLoading()
 {
+  nsresult rv = ReallyStartLoadingInternal();
+  if (NS_FAILED(rv)) {
+    FireErrorEvent();
+  }
+  
+  return rv;
+}
+
+nsresult
+nsFrameLoader::ReallyStartLoadingInternal()
+{
   NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInDoc());
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
 #ifdef MOZ_IPC
@@ -289,21 +323,18 @@ nsFrameLoader::ReallyStartLoading()
 
   // Kick off the load...
   PRBool tmpState = mNeedsAsyncDestroy;
   mNeedsAsyncDestroy = PR_TRUE;
   rv = mDocShell->LoadURI(mURIToLoad, loadInfo,
                           nsIWebNavigation::LOAD_FLAGS_NONE, PR_FALSE);
   mNeedsAsyncDestroy = tmpState;
   mURIToLoad = nsnull;
-#ifdef DEBUG
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed to load the URL");
-  }
-#endif
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return NS_OK;
 }
 
 nsresult
 nsFrameLoader::CheckURILoad(nsIURI* aURI)
 {
   // Check for security.  The fun part is trying to figure out what principals
   // to use.  The way I figure it, if we're doing a LoadFrame() accidentally
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -159,16 +159,18 @@ private:
 
   // Properly retrieves documentSize of any subdocument type.
   NS_HIDDEN_(nsIntSize) GetSubDocumentSize(const nsIFrame *aIFrame);
 
   // Updates the subdocument position and size. This gets called only
   // when we have our own in-process DocShell.
   NS_HIDDEN_(nsresult) UpdateBaseWindowPositionAndSize(nsIFrame *aIFrame);
   nsresult CheckURILoad(nsIURI* aURI);
+  void FireErrorEvent();
+  nsresult ReallyStartLoadingInternal();
 
 #ifdef MOZ_IPC
   // True means new process started; nothing else to do
   bool TryNewProcess();
 
   // Do the hookup necessary to actually show a remote frame once the view and
   // widget are available.
   bool ShowRemoteFrame(nsIFrameFrame* frame, nsIView* view);
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -2641,20 +2641,16 @@ nsGenericElement::UnbindFromTree(PRBool 
 
   mParentPtrBits = aNullParent ? 0 : mParentPtrBits & ~PARENT_BIT_INDOCUMENT;
 
   if (document) {
     // Notify XBL- & nsIAnonymousContentCreator-generated
     // anonymous content that the document is changing.
     document->BindingManager()->ChangeDocumentFor(this, document, nsnull);
 
-    if (HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)) {
-      document->ForgetLink(this);
-    }
-
     document->ClearBoxObjectFor(this);
   }
 
   // Ensure that CSS transitions don't continue on an element at a
   // different place in the tree (even if reinserted before next
   // animation refresh).
   // FIXME (Bug 522599): Need a test for this.
   if (HasFlag(NODE_HAS_PROPERTIES)) {
@@ -4294,30 +4290,16 @@ nsresult
 nsGenericElement::SetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                           nsIAtom* aPrefix, const nsAString& aValue,
                           PRBool aNotify)
 {
   NS_ENSURE_ARG_POINTER(aName);
   NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
                "Don't call SetAttr with unknown namespace");
 
-  nsIDocument* doc = GetCurrentDoc();
-  if (kNameSpaceID_XLink == aNamespaceID && nsGkAtoms::href == aName) {
-    // XLink URI(s) might be changing. Drop the link from the map. If it
-    // is still style relevant it will be re-added by
-    // nsStyleUtil::IsLink. Make sure to keep the style system
-    // consistent so this remains true! In particular if the style system
-    // were to get smarter and not restyling an XLink element if the href
-    // doesn't change in a "significant" way, we'd need to do the same
-    // significance check here.
-    if (doc) {
-      doc->ForgetLink(this);
-    }
-  }
-
   nsAutoString oldValue;
   PRBool modification = PR_FALSE;
   PRBool hasListeners = aNotify &&
     nsContentUtils::HasMutationListeners(this,
                                          NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
                                          this);
   
   // If we have no listeners and aNotify is false, we are almost certainly
@@ -4340,55 +4322,58 @@ nsGenericElement::SetAttr(PRInt32 aNames
       }
       if (valueMatches && aPrefix == info.mName->GetPrefix()) {
         return NS_OK;
       }
       modification = PR_TRUE;
     }
   }
 
+
   nsresult rv = BeforeSetAttr(aNamespaceID, aName, &aValue, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
   
+  PRUint8 modType = modification ?
+    static_cast<PRUint8>(nsIDOMMutationEvent::MODIFICATION) :
+    static_cast<PRUint8>(nsIDOMMutationEvent::ADDITION);
+  if (aNotify) {
+    nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
+  }
+
   nsAttrValue attrValue;
   if (!ParseAttribute(aNamespaceID, aName, aValue, attrValue)) {
     attrValue.SetTo(aValue);
   }
 
   return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
-                          attrValue, modification, hasListeners, aNotify,
+                          attrValue, modType, hasListeners, aNotify,
                           &aValue);
 }
   
 nsresult
 nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID,
                                    nsIAtom* aName,
                                    nsIAtom* aPrefix,
                                    const nsAString& aOldValue,
                                    nsAttrValue& aParsedValue,
-                                   PRBool aModification,
+                                   PRUint8 aModType,
                                    PRBool aFireMutation,
                                    PRBool aNotify,
                                    const nsAString* aValueForAfterSetAttr)
 {
   nsresult rv;
-  PRUint8 modType = aModification ?
-    static_cast<PRUint8>(nsIDOMMutationEvent::MODIFICATION) :
-    static_cast<PRUint8>(nsIDOMMutationEvent::ADDITION);
 
   nsIDocument* document = GetCurrentDoc();
   mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
 
   // When notifying, make sure to keep track of states whose value
   // depends solely on the value of an attribute.
   PRUint32 stateMask;
   if (aNotify) {
     stateMask = PRUint32(IntrinsicState());
-    
-    nsNodeUtils::AttributeWillChange(this, aNamespaceID, aName, modType);
   }
 
   if (aNamespaceID == kNameSpaceID_None) {
     // XXXbz Perhaps we should push up the attribute mapping function
     // stuff to nsGenericElement?
     if (!IsAttributeMapped(aName) ||
         !SetMappedAttribute(document, aName, aParsedValue, &rv)) {
       rv = mAttrsAndChildren.SetAndTakeAttr(aName, aParsedValue);
@@ -4416,17 +4401,17 @@ nsGenericElement::SetAttrAndNotify(PRInt
   }
 
   if (aNotify) {
     stateMask = stateMask ^ PRUint32(IntrinsicState());
     if (stateMask && document) {
       MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, aNotify);
       document->ContentStatesChanged(this, nsnull, stateMask);
     }
-    nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, modType);
+    nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType);
   }
 
   if (aNamespaceID == kNameSpaceID_XMLEvents && 
       aName == nsGkAtoms::event && mNodeInfo->GetDocument()) {
     mNodeInfo->GetDocument()->AddXMLEventsContent(this);
   }
   if (aValueForAfterSetAttr) {
     rv = AfterSetAttr(aNamespaceID, aName, aValueForAfterSetAttr, aNotify);
@@ -4450,17 +4435,17 @@ nsGenericElement::SetAttrAndNotify(PRInt
     nsAutoString newValue;
     GetAttr(aNamespaceID, aName, newValue);
     if (!newValue.IsEmpty()) {
       mutation.mNewAttrValue = do_GetAtom(newValue);
     }
     if (!aOldValue.IsEmpty()) {
       mutation.mPrevAttrValue = do_GetAtom(aOldValue);
     }
-    mutation.mAttrChange = modType;
+    mutation.mAttrChange = aModType;
 
     mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
     nsEventDispatcher::Dispatch(this, nsnull, &mutation);
   }
 
   return NS_OK;
 }
 
@@ -4621,22 +4606,16 @@ nsGenericElement::UnsetAttr(PRInt32 aNam
   nsIDocument *document = GetCurrentDoc();    
   mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
 
   if (aNotify) {
     nsNodeUtils::AttributeWillChange(this, aNameSpaceID, aName,
                                      nsIDOMMutationEvent::REMOVAL);
   }
 
-  if (document && kNameSpaceID_XLink == aNameSpaceID &&
-      nsGkAtoms::href == aName) {
-    // XLink URI might be changing.
-    document->ForgetLink(this);
-  }
-
   // When notifying, make sure to keep track of states whose value
   // depends solely on the value of an attribute.
   PRUint32 stateMask;
   if (aNotify) {
     stateMask = PRUint32(IntrinsicState());
   }    
 
   PRBool hasMutationListeners = aNotify &&
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -749,37 +749,40 @@ public:
   {
   }
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericElement)
 
 protected:
   /**
    * Set attribute and (if needed) notify documentobservers and fire off
-   * mutation events.
+   * mutation events.  This will send the AttributeChanged notification.
+   * Callers of this method are responsible for calling AttributeWillChange,
+   * since that needs to happen before the new attr value has been set, and
+   * in particular before it has been parsed.
    *
    * @param aNamespaceID  namespace of attribute
    * @param aAttribute    local-name of attribute
    * @param aPrefix       aPrefix of attribute
    * @param aOldValue     previous value of attribute. Only needed if
    *                      aFireMutation is true.
    * @param aParsedValue  parsed new value of attribute
-   * @param aModification is this a attribute-modification or addition. Only
+   * @param aModType      nsIDOMMutationEvent::MODIFICATION or ADDITION.  Only
    *                      needed if aFireMutation or aNotify is true.
    * @param aFireMutation should mutation-events be fired?
    * @param aNotify       should we notify document-observers?
    * @param aValueForAfterSetAttr If not null, AfterSetAttr will be called
    *                      with the value pointed by this parameter.
    */
   nsresult SetAttrAndNotify(PRInt32 aNamespaceID,
                             nsIAtom* aName,
                             nsIAtom* aPrefix,
                             const nsAString& aOldValue,
                             nsAttrValue& aParsedValue,
-                            PRBool aModification,
+                            PRUint8 aModType,
                             PRBool aFireMutation,
                             PRBool aNotify,
                             const nsAString* aValueForAfterSetAttr);
 
   /**
    * Convert an attribute string value to attribute type based on the type of
    * attribute.  Called by SetAttr().  Note that at the moment we only do this
    * for attributes in the null namespace (kNameSpaceID_None).
@@ -811,18 +814,19 @@ protected:
   virtual PRBool SetMappedAttribute(nsIDocument* aDocument,
                                     nsIAtom* aName,
                                     nsAttrValue& aValue,
                                     nsresult* aRetval);
 
   /**
    * Hook that is called by nsGenericElement::SetAttr to allow subclasses to
    * deal with attribute sets.  This will only be called after we verify that
-   * we're actually doing an attr set and will be called before ParseAttribute
-   * and hence before we've set the new value.
+   * we're actually doing an attr set and will be called before
+   * AttributeWillChange and before ParseAttribute and hence before we've set
+   * the new value.
    *
    * @param aNamespaceID the namespace of the attr being set
    * @param aName the localname of the attribute being set
    * @param aValue the value it's being set to.  If null, the attr is being
    *        removed.
    * @param aNotify Whether we plan to notify document observers.
    */
   // Note that this is inlined so that when subclasses call it it gets
@@ -831,17 +835,18 @@ protected:
                                  const nsAString* aValue, PRBool aNotify)
   {
     return NS_OK;
   }
 
   /**
    * Hook that is called by nsGenericElement::SetAttr to allow subclasses to
    * deal with attribute sets.  This will only be called after we have called
-   * SetAndTakeAttr (that is, after we have actually set the attr).
+   * SetAndTakeAttr and AttributeChanged (that is, after we have actually set
+   * the attr).
    *
    * @param aNamespaceID the namespace of the attr being set
    * @param aName the localname of the attribute being set
    * @param aValue the value it's being set to.  If null, the attr is being
    *        removed.
    * @param aNotify Whether we plan to notify document observers.
    */
   // Note that this is inlined so that when subclasses call it it gets
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -58,22 +58,24 @@
 #endif 
 
 //---------------------------------------------------------------------------
 // Generic atoms
 //---------------------------------------------------------------------------
 
 GK_ATOM(_empty, "")
 GK_ATOM(moz, "_moz")
+GK_ATOM(moztype, "_moz-type")
 GK_ATOM(mozdirty, "_moz_dirty")
 GK_ATOM(mozeditorbogusnode, "_moz_editor_bogus_node")
 GK_ATOM(mozgeneratedcontentbefore, "_moz_generated_content_before")
 GK_ATOM(mozgeneratedcontentafter, "_moz_generated_content_after")
 GK_ATOM(mozgeneratedcontentimage, "_moz_generated_content_image")
 GK_ATOM(_moz_target, "_moz_target")
+GK_ATOM(_moz_type, "_moz-type")
 GK_ATOM(menuactive, "_moz-menuactive")
 GK_ATOM(_poundDefault, "#default")
 GK_ATOM(_asterix, "*")
 GK_ATOM(a, "a")
 GK_ATOM(abbr, "abbr")
 GK_ATOM(abort, "abort")
 GK_ATOM(above, "above")
 GK_ATOM(absoluteList, "Absolute-list")
@@ -716,16 +718,17 @@ GK_ATOM(patternSeparator, "pattern-separ
 GK_ATOM(perMille, "per-mille")
 GK_ATOM(percent, "percent")
 GK_ATOM(persist, "persist")
 GK_ATOM(phase, "phase")
 GK_ATOM(ping, "ping")
 #ifdef MOZ_MEDIA
 GK_ATOM(pixelratio, "pixelratio")
 #endif
+GK_ATOM(placeholder, "placeholder")
 GK_ATOM(plaintext, "plaintext")
 #ifdef MOZ_MEDIA
 GK_ATOM(playbackrate, "playbackrate")
 GK_ATOM(playcount, "playcount")
 #endif
 GK_ATOM(pointSize, "point-size")
 GK_ATOM(poly, "poly")
 GK_ATOM(polygon, "polygon")
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -53,16 +53,17 @@
 #include "nsServiceManagerUtils.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIURI.h"
 #include "nsILoadGroup.h"
 #include "imgIContainer.h"
 #include "imgILoader.h"
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
+#include "nsPLDOMEvent.h"
 
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIEventStateManager.h"
 #include "nsGUIEvent.h"
 
 #include "nsIChannel.h"
 #include "nsIStreamListener.h"
@@ -879,96 +880,30 @@ nsImageLoadingContent::StringToURI(const
   // (3) Construct the silly thing
   return NS_NewURI(aURI,
                    aSpec,
                    charset.IsEmpty() ? nsnull : charset.get(),
                    baseURL,
                    nsContentUtils::GetIOService());
 }
 
-
-/**
- * Class used to dispatch events
- */
-
-class nsImageLoadingContent::Event : public nsRunnable
-{
-public:
-  Event(nsPresContext* aPresContext, nsImageLoadingContent* aContent,