Merge mozilla-inbound to m-c to catch the 8 train.
authorKyle Huey <khuey@kylehuey.com>
Tue, 16 Aug 2011 06:55:39 -0400
changeset 75364 35657230a209b1d8b709d7da1b2cc1f702332115
parent 75324 630e28e90986596a4855f8f67cd8072f2c1bdf6b (current diff)
parent 75363 85b5d609a73cddbf7f09e4b708266975c24e7731 (diff)
child 75365 1248e3a8a8c8a21a4afbcb75634bfc6b1439452f
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone8.0a1
Merge mozilla-inbound to m-c to catch the 8 train.
new file mode 100644
--- /dev/null
+++ b/accessible/src/base/Statistics.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Trevor Saunders <trev.saunders@gmail.com> (original author)
+ *
+ * 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 A11Y_STATISTICS_H_
+#define A11Y_STATISTICS_H_
+
+#include "mozilla/Telemetry.h"
+
+namespace mozilla {
+namespace a11y {
+namespace statistics {
+
+  inline void A11yInitialized()
+    { Telemetry::Accumulate(Telemetry::A11Y_INSTANTIATED, true); }
+
+} // namespace statistics
+} // namespace a11y
+} // namespace mozilla
+
+#endif
+
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -51,16 +51,17 @@
 #include "nsHTMLLinkAccessible.h"
 #include "nsHTMLSelectAccessible.h"
 #include "nsHTMLTableAccessibleWrap.h"
 #include "nsHTMLTextAccessible.h"
 #include "nsHyperTextAccessibleWrap.h"
 #include "nsIAccessibilityService.h"
 #include "nsIAccessibleProvider.h"
 #include "States.h"
+#include "Statistics.h"
 
 #include "nsIDOMDocument.h"
 #include "nsIDOMHTMLAreaElement.h"
 #include "nsIDOMHTMLLegendElement.h"
 #include "nsIDOMHTMLObjectElement.h"
 #include "nsIDOMHTMLOptGroupElement.h"
 #include "nsIDOMHTMLOptionElement.h"
 #include "nsIDOMXULElement.h"
@@ -1767,16 +1768,18 @@ NS_GetAccessibilityService(nsIAccessibil
   nsRefPtr<nsAccessibilityService> service = new nsAccessibilityService();
   NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY);
 
   if (!service->Init()) {
     service->Shutdown();
     return NS_ERROR_FAILURE;
   }
 
+  statistics::A11yInitialized();
+
   nsAccessibilityService::gAccessibilityService = service;
   NS_ADDREF(*aResult = service);
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService private (DON'T put methods here)
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -778,22 +778,22 @@ pref("browser.sessionstore.privacy_level
 // how many tabs can be reopened (per window)
 pref("browser.sessionstore.max_tabs_undo", 10);
 // how many windows can be reopened (per session) - on non-OS X platforms this
 // pref may be ignored when dealing with pop-up windows to ensure proper startup
 pref("browser.sessionstore.max_windows_undo", 3);
 // number of crashes that can occur before the about:sessionrestore page is displayed
 // (this pref has no effect if more than 6 hours have passed since the last crash)
 pref("browser.sessionstore.max_resumed_crashes", 1);
-// The number of tabs that can restore concurrently:
-// < 0 = All tabs can restore at the same time
-//   0 = Only the selected tab in each window will be restored
-//       Other tabs won't be restored until they are selected
-//   N = The number of tabs to restore at the same time
-pref("browser.sessionstore.max_concurrent_tabs", 3);
+// restore_on_demand overrides MAX_CONCURRENT_TAB_RESTORES (sessionstore constant)
+// and restore_hidden_tabs. When true, tabs will not be restored until they are
+// focused (also applies to tabs that aren't visible). When false, the values
+// for MAX_CONCURRENT_TAB_RESTORES and restore_hidden_tabs are respected.
+// Selected tabs are always restored regardless of this pref.
+pref("browser.sessionstore.restore_on_demand", false);
 // Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not
 pref("browser.sessionstore.restore_hidden_tabs", false);
 
 // allow META refresh by default
 pref("accessibility.blockautorefresh", false);
 
 // Whether history is enabled or not.
 pref("places.history.enabled", true);
--- a/browser/base/content/test/tabview/browser_tabview_bug595601.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595601.js
@@ -30,24 +30,21 @@ function test() {
 
   Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false);
 
   TabsProgressListener.init();
 
   registerCleanupFunction(function () {
     TabsProgressListener.uninit();
 
-    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
     Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
 
     ss.setBrowserState(stateBackup);
   });
 
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 3);
-
   TabView._initFrame(function () {
     executeSoon(testRestoreWithHiddenTabs);
   });
 }
 
 function testRestoreWithHiddenTabs() {
   let checked = false;
   let ssReady = false;
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ b/browser/components/migration/src/nsIEProfileMigrator.cpp
@@ -2094,17 +2094,16 @@ nsIEProfileMigrator::CopySecurityPrefs(n
       "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
   if (regKey && 
       NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
                                 key, nsIWindowsRegKey::ACCESS_READ))) {
     
     PRUint32 value;
     if (NS_SUCCEEDED(regKey->ReadIntValue(NS_LITERAL_STRING("SecureProtocols"),
                                           &value))) { 
-      aPrefs->SetBoolPref("security.enable_ssl2", (value >> 3) & PR_TRUE);
       aPrefs->SetBoolPref("security.enable_ssl3", (value >> 5) & PR_TRUE);
       aPrefs->SetBoolPref("security.enable_tls",  (value >> 7) & PR_TRUE);
     }
   }
 
   return NS_OK;
 }
 
--- a/browser/components/migration/src/nsOperaProfileMigrator.cpp
+++ b/browser/components/migration/src/nsOperaProfileMigrator.cpp
@@ -315,17 +315,16 @@ nsOperaProfileMigrator::PrefTransform gT
   { nsnull, "Allow script to change status", _OPM(BOOL), "dom.disable_window_status_change", _OPM(SetBool), PR_FALSE, { -1 } },
   { nsnull, "Ignore Unrequested Popups", _OPM(BOOL), "dom.disable_open_during_load", _OPM(SetBool), PR_FALSE, { -1 } },
   { nsnull, "Load Figures", _OPM(BOOL), "permissions.default.image", _OPM(SetImageBehavior), PR_FALSE, { -1 } },
 
   { "Visited link", nsnull, _OPM(COLOR), "browser.visited_color", _OPM(SetString), PR_FALSE, { -1 } },
   { "Link", nsnull, _OPM(COLOR), "browser.anchor_color", _OPM(SetString), PR_FALSE, { -1 } },
   { nsnull, "Underline", _OPM(BOOL), "browser.underline_anchors", _OPM(SetBool), PR_FALSE, { -1 } },
 
-  { "Security Prefs", "Enable SSL v2", _OPM(BOOL), "security.enable_ssl2", _OPM(SetBool), PR_FALSE, { -1 } },
   { nsnull, "Enable SSL v3", _OPM(BOOL), "security.enable_ssl3", _OPM(SetBool), PR_FALSE, { -1 } },
   { nsnull, "Enable TLS v1.0", _OPM(BOOL), "security.enable_tls", _OPM(SetBool), PR_FALSE, { -1 } },
 
   { "Extensions", "Scripting", _OPM(BOOL), "javascript.enabled", _OPM(SetBool), PR_FALSE, { -1 } }
 };
 
 nsresult 
 nsOperaProfileMigrator::SetFile(void* aTransform, nsIPrefBranch* aBranch)
--- a/browser/components/migration/src/nsSeamonkeyProfileMigrator.cpp
+++ b/browser/components/migration/src/nsSeamonkeyProfileMigrator.cpp
@@ -342,17 +342,16 @@ nsSeamonkeyProfileMigrator::PrefTransfor
   MAKESAMETYPEPREFTRANSFORM("browser.tabs.autoHide",                    Bool),
   MAKESAMETYPEPREFTRANSFORM("browser.tabs.loadInBackground",            Bool),
   MAKESAMETYPEPREFTRANSFORM("browser.enable_automatic_image_resizing",  Bool),
   MAKESAMETYPEPREFTRANSFORM("network.cookie.warnAboutCookies",          Bool),
   MAKESAMETYPEPREFTRANSFORM("network.cookie.lifetime.enabled",          Bool),
   MAKESAMETYPEPREFTRANSFORM("network.cookie.lifetime.behavior",         Int),
   MAKESAMETYPEPREFTRANSFORM("dom.disable_open_during_load",             Bool),
   MAKESAMETYPEPREFTRANSFORM("signon.rememberSignons",                   Bool),
-  MAKESAMETYPEPREFTRANSFORM("security.enable_ssl2",                     Bool),
   MAKESAMETYPEPREFTRANSFORM("security.enable_ssl3",                     Bool),
   MAKESAMETYPEPREFTRANSFORM("security.enable_tls",                      Bool),
   MAKESAMETYPEPREFTRANSFORM("security.warn_entering_secure",            Bool),
   MAKESAMETYPEPREFTRANSFORM("security.warn_entering_weak",              Bool),
   MAKESAMETYPEPREFTRANSFORM("security.warn_leaving_secure",             Bool),
   MAKESAMETYPEPREFTRANSFORM("security.warn_submit_insecure",            Bool),
   MAKESAMETYPEPREFTRANSFORM("security.warn_viewing_mixed",              Bool),
   MAKESAMETYPEPREFTRANSFORM("security.default_personal_cert",           String),
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -48,16 +48,17 @@ var gMainPane = {
   {
     this._pane = document.getElementById("paneMain");
 
     // set up the "use current page" label-changing listener
     this._updateUseCurrentButton();
     window.addEventListener("focus", this._updateUseCurrentButton, false);
 
     this.updateBrowserStartupLastSession();
+    this.startupPagePrefChanged();
 
     // Notify observers that the UI is now ready
     Components.classes["@mozilla.org/observer-service;1"]
               .getService(Components.interfaces.nsIObserverService)
               .notifyObservers(window, "main-pane-loaded", null);
   },
 
   // HOME PAGE
@@ -76,16 +77,26 @@ var gMainPane = {
    *     2: the last page the user visited (DEPRECATED)
    *     3: windows and tabs from the last session (a.k.a. session restore)
    *
    *   The deprecated option is not exposed in UI; however, if the user has it
    *   selected and doesn't change the UI for this preference, the deprecated
    *   option is preserved.
    */
 
+  /**
+   * Enables/Disables the restore on demand checkbox.
+   */
+  startupPagePrefChanged: function ()
+  {
+    let startupPref = document.getElementById("browser.startup.page");
+    let restoreOnDemandPref = document.getElementById("browser.sessionstore.restore_on_demand");
+    restoreOnDemandPref.disabled = startupPref.value != 3;
+  },
+
   syncFromHomePref: function ()
   {
     let homePref = document.getElementById("browser.startup.homepage");
 
     // If the pref is set to about:home, set the value to "" to show the
     // placeholder text (about:home title).
     if (homePref.value.toLowerCase() == "about:home")
       return "";
--- a/browser/components/preferences/main.xul
+++ b/browser/components/preferences/main.xul
@@ -59,17 +59,21 @@
     <script type="application/javascript" src="chrome://browser/content/preferences/main.js"/>
 
     <preferences id="mainPreferences">
       <!-- XXX Button preferences -->
 
       <!-- Startup -->
       <preference id="browser.startup.page"
                   name="browser.startup.page"
-                  type="int"/>
+                  type="int"
+                  onchange="gMainPane.startupPagePrefChanged();"/>
+      <preference id="browser.sessionstore.restore_on_demand"
+                  name="browser.sessionstore.restore_on_demand"
+                  type="bool"/>
       <preference id="browser.startup.homepage"
                   name="browser.startup.homepage"
                   type="wstring"/>
 
       <preference id="pref.browser.homepage.disable_button.current_page"
                   name="pref.browser.homepage.disable_button.current_page"
                   type="bool"/>
       <preference id="pref.browser.homepage.disable_button.bookmark_page"
@@ -115,16 +119,23 @@
         <menulist id="browserStartupPage" preference="browser.startup.page">
           <menupopup>
             <menuitem label="&startupHomePage.label;"     value="1" id="browserStartupHomePage"/>
             <menuitem label="&startupBlankPage.label;"    value="0" id="browserStartupBlank"/>
             <menuitem label="&startupLastSession.label;"  value="3" id="browserStartupLastSession"/>
           </menupopup>
         </menulist>
       </hbox>
+      <hbox align="center">
+        <checkbox id="restoreOnDemand"
+                  label="&restoreOnDemand.label;"
+                  accesskey="&restoreOnDemand.accesskey;"
+                  class="indent"
+                  preference="browser.sessionstore.restore_on_demand"/>
+      </hbox>
       <separator class="thin"/>
       <hbox align="center">
         <label value="&homepage.label;" accesskey="&homepage.accesskey;" control="browserHomePage"/>
         <textbox id="browserHomePage" class="padded uri-element" flex="1"
                  type="autocomplete" autocompletesearch="history"
                  onsyncfrompreference="return gMainPane.syncFromHomePref();"
                  onsynctopreference="return gMainPane.syncToHomePref(this.value);"
                  placeholder="&abouthome.pageTitle;"
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -70,16 +70,20 @@ const TAB_STATE_RESTORING = 2;
 
 const PRIVACY_NONE = 0;
 const PRIVACY_ENCRYPTED = 1;
 const PRIVACY_FULL = 2;
 
 const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
 const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored";
 
+// Maximum number of tabs to restore simultaneously. Previously controlled by
+// the browser.sessionstore.max_concurrent_tabs pref.
+const MAX_CONCURRENT_TAB_RESTORES = 3;
+
 // global notifications observed
 const OBSERVING = [
   "domwindowopened", "domwindowclosed",
   "quit-application-requested", "quit-application-granted",
   "browser-lastwindow-close-granted",
   "quit-application", "browser:purge-session-history",
   "private-browsing", "browser:purge-domain-data",
   "private-browsing-change-granted"
@@ -187,17 +191,17 @@ SessionStoreService.prototype = {
   // the favicon is always saved for the about:sessionrestore page
   xulAttributes: {"image": true},
 
   // set default load state
   _loadState: STATE_STOPPED,
 
   // During the initial restore and setBrowserState calls tracks the number of
   // windows yet to be restored
-  _restoreCount: 0,
+  _restoreCount: -1,
 
   // whether a setBrowserState call is in progress
   _browserSetState: false,
 
   // time in milliseconds (Date.now()) when the session was last written to file
   _lastSaveTime: 0,
 
   // time in milliseconds when the session was started (saved across sessions),
@@ -224,19 +228,19 @@ SessionStoreService.prototype = {
 
   // whether the last window was closed and should be restored
   _restoreLastWindow: false,
 
   // tabs to restore in order
   _tabsToRestore: { visible: [], hidden: [] },
   _tabsRestoringCount: 0,
 
-  // number of tabs to restore concurrently, pref controlled.
-  _maxConcurrentTabRestores: null,
-  
+  // overrides MAX_CONCURRENT_TAB_RESTORES and _restoreHiddenTabs when true
+  _restoreOnDemand: false,
+
   // whether to restore hidden tabs or not, pref controlled.
   _restoreHiddenTabs: null,
 
   // The state from the previous session (after restoring pinned tabs). This
   // state is persisted and passed through to the next session during an app
   // restart to make the third party add-on warning not trash the deferred
   // session
   _lastSessionState: null,
@@ -276,28 +280,31 @@ SessionStoreService.prototype = {
   initService: function() {
     OBSERVING.forEach(function(aTopic) {
       Services.obs.addObserver(this, aTopic, true);
     }, this);
 
     var pbs = Cc["@mozilla.org/privatebrowsing;1"].
               getService(Ci.nsIPrivateBrowsingService);
     this._inPrivateBrowsing = pbs.privateBrowsingEnabled;
-    
+
+    // Do pref migration before we store any values and start observing changes
+    this._migratePrefs();
+
     // observe prefs changes so we can modify stored data to match
     this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
     this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
     
     // this pref is only read at startup, so no need to observe it
     this._sessionhistory_max_entries =
       this._prefBranch.getIntPref("sessionhistory.max_entries");
 
-    this._maxConcurrentTabRestores =
-      this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
-    this._prefBranch.addObserver("sessionstore.max_concurrent_tabs", this, true);
+    this._restoreOnDemand =
+      this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
+    this._prefBranch.addObserver("sessionstore.restore_on_demand", this, true);
 
     this._restoreHiddenTabs =
       this._prefBranch.getBoolPref("sessionstore.restore_hidden_tabs");
     this._prefBranch.addObserver("sessionstore.restore_hidden_tabs", this, true);
 
     // Make sure gRestoreTabsProgressListener has a reference to sessionstore
     // so that it can make calls back in
     gRestoreTabsProgressListener.ss = this;
@@ -359,16 +366,19 @@ SessionStoreService.prototype = {
           this._sessionStartTime = this._initialState.session &&
                                    this._initialState.session.startTime ||
                                    this._sessionStartTime;
 
           // make sure that at least the first window doesn't have anything hidden
           delete this._initialState.windows[0].hidden;
           // Since nothing is hidden in the first window, it cannot be a popup
           delete this._initialState.windows[0].isPopup;
+          // We don't want to minimize and then open a window at startup.
+          if (this._initialState.windows[0].sizemode == "minimized")
+            this._initialState.windows[0].sizemode = "normal";
         }
       }
       catch (ex) { debug("The session file is invalid: " + ex); }
     }
 
     if (this._resume_from_crash) {
       // create a backup if the session data file exists
       try {
@@ -435,16 +445,29 @@ SessionStoreService.prototype = {
 
     // Make sure to break our cycle with the save timer
     if (this._saveTimer) {
       this._saveTimer.cancel();
       this._saveTimer = null;
     }
   },
 
+  _migratePrefs: function sss__migratePrefs() {
+    // Added For Firefox 8
+    // max_concurrent_tabs is going away. We're going to hard code a max value
+    // (MAX_CONCURRENT_TAB_RESTORES) and start using a boolean pref restore_on_demand.
+    if (this._prefBranch.prefHasUserValue("sessionstore.max_concurrent_tabs") &&
+        !this._prefBranch.prefHasUserValue("sessionstore.restore_on_demand")) {
+      let maxConcurrentTabs =
+        this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
+      this._prefBranch.setBoolPref("sessionstore.restore_on_demand", maxConcurrentTabs == 0);
+      this._prefBranch.clearUserPref("sessionstore.max_concurrent_tabs");
+    }
+  },
+
   /**
    * Handle notifications
    */
   observe: function sss_observe(aSubject, aTopic, aData) {
     // for event listeners
     var _this = this;
 
     switch (aTopic) {
@@ -624,19 +647,19 @@ SessionStoreService.prototype = {
           this._resume_session_once_on_shutdown = null;
         }
         // either create the file with crash recovery information or remove it
         // (when _loadState is not STATE_RUNNING, that file is used for session resuming instead)
         if (!this._resume_from_crash)
           this._clearDisk();
         this.saveState(true);
         break;
-      case "sessionstore.max_concurrent_tabs":
-        this._maxConcurrentTabRestores =
-          this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
+      case "sessionstore.restore_on_demand":
+        this._restoreOnDemand =
+          this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
         break;
       case "sessionstore.restore_hidden_tabs":
         this._restoreHiddenTabs =
           this._prefBranch.getBoolPref("sessionstore.restore_hidden_tabs");
         break;
       }
       break;
     case "timer-callback": // timer call back for delayed saving
@@ -2898,17 +2921,17 @@ SessionStoreService.prototype = {
     event.initEvent("SSTabRestoring", true, false);
     tab.dispatchEvent(event);
 
     // Restore the history in the next tab
     aWindow.setTimeout(function(){
       _this.restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap);
     }, 0);
 
-    // This could cause us to ignore the max_concurrent_tabs pref a bit, but
+    // This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but
     // it ensures each window will have its selected tab loaded.
     if (aWindow.gBrowser.selectedBrowser == browser) {
       this.restoreTab(tab);
     }
     else {
       // Put the tab into the right bucket
       if (tabData.hidden)
         this._tabsToRestore.hidden.push(tab);
@@ -3034,18 +3057,18 @@ SessionStoreService.prototype = {
    *   if we have already reached the limit for number of tabs to restore
    */
   restoreNextTab: function sss_restoreNextTab() {
     // If we call in here while quitting, we don't actually want to do anything
     if (this._loadState == STATE_QUITTING)
       return;
 
     // If it's not possible to restore anything, then just bail out.
-    if (this._maxConcurrentTabRestores >= 0 &&
-        this._tabsRestoringCount >= this._maxConcurrentTabRestores)
+    if (this._restoreOnDemand ||
+        this._tabsRestoringCount >= MAX_CONCURRENT_TAB_RESTORES)
       return;
 
     // Look in visible, then hidden
     let nextTabArray;
     if (this._tabsToRestore.visible.length) {
       nextTabArray = this._tabsToRestore.visible;
     }
     else if (this._restoreHiddenTabs && this._tabsToRestore.hidden.length) {
@@ -3964,26 +3987,33 @@ SessionStoreService.prototype = {
     function exclude(key, value) {
       // returning undefined results in the exclusion of that key
       return internalKeys.indexOf(key) == -1 ? value : undefined;
     }
     return JSON.stringify(aJSObject, exclude);
   },
 
   _sendRestoreCompletedNotifications: function sss_sendRestoreCompletedNotifications() {
-    if (this._restoreCount) {
+    // not all windows restored, yet
+    if (this._restoreCount > 1) {
       this._restoreCount--;
-      if (this._restoreCount == 0) {
-        // This was the last window restored at startup, notify observers.
-        Services.obs.notifyObservers(null,
-          this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED,
-          "");
-        this._browserSetState = false;
-      }
+      return;
     }
+
+    // observers were already notified
+    if (this._restoreCount == -1)
+      return;
+
+    // This was the last window restored at startup, notify observers.
+    Services.obs.notifyObservers(null,
+      this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED,
+      "");
+
+    this._browserSetState = false;
+    this._restoreCount = -1;
   },
 
   /**
    * Dispatch an SSWindowState_____ event for the given window.
    * @param aWindow the window
    * @param aType the type of event, SSWindowState will be prepended to this string
    */
   _sendWindowStateEvent: function sss__sendWindowStateEvent(aWindow, aType) {
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -144,16 +144,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_615394-SSWindowState_events.js \
 	browser_618151.js \
 	browser_623779.js \
 	browser_624727.js \
 	browser_625257.js \
 	browser_628270.js \
 	browser_635418.js \
 	browser_636279.js \
+	browser_645428.js \
 	browser_659591.js \
 	$(NULL)
 
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	browser_625016.js \
 	$(NULL)
--- a/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js
+++ b/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js
@@ -55,17 +55,17 @@ function test() {
 let tests = [test_cascade, test_select, test_multiWindowState,
              test_setWindowStateNoOverwrite, test_setWindowStateOverwrite,
              test_setBrowserStateInterrupted, test_reload,
              /* test_reloadReload, */ test_reloadCascadeSetup,
              /* test_reloadCascade */];
 function runNextTest() {
   // Reset the pref
   try {
-    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+    Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
   } catch (e) {}
 
   // set an empty state & run the next test, or finish
   if (tests.length) {
     // Enumerate windows and close everything but our primary window. We can't
     // use waitForFocus() because apparently it's buggy. See bug 599253.
     var windowsEnum = Services.wm.getEnumerator("navigator:browser");
     while (windowsEnum.hasMoreElements()) {
@@ -83,19 +83,16 @@ function runNextTest() {
   else {
     ss.setBrowserState(stateBackup);
     executeSoon(finish);
   }
 }
 
 
 function test_cascade() {
-  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
-
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       dump("\n\nload: " + aBrowser.currentURI.spec + "\n" + JSON.stringify(countTabs()) + "\n\n");
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -113,21 +110,21 @@ function test_cascade() {
   ] }] };
 
   let loadCount = 0;
   // Since our progress listener is fired before the one in sessionstore, our
   // expected counts look a little weird. This is because we inspect the state
   // before sessionstore has marked the tab as finished restoring and before it
   // starts restoring the next tab
   let expectedCounts = [
-    [5, 1, 0],
-    [4, 1, 1],
-    [3, 1, 2],
-    [2, 1, 3],
-    [1, 1, 4],
+    [3, 3, 0],
+    [2, 3, 1],
+    [1, 3, 2],
+    [0, 3, 3],
+    [0, 2, 4],
     [0, 1, 5]
   ];
 
   function test_cascade_progressCallback() {
     loadCount++;
     let counts = countTabs();
     let expected = expectedCounts[loadCount - 1];
 
@@ -144,19 +141,19 @@ function test_cascade() {
 
   // This progress listener will get attached before the listener in session store.
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 function test_select() {
-  // Set the pref to 0 so we know exactly how many tabs should be restoring at
+  // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -293,19 +290,16 @@ function test_multiWindowState() {
   Services.ww.registerNotification(windowObserver);
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 function test_setWindowStateNoOverwrite() {
-  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
-
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -365,19 +359,16 @@ function test_setWindowStateNoOverwrite(
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setWindowState(window, JSON.stringify(state1), true);
 }
 
 
 function test_setWindowStateOverwrite() {
-  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
-
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -437,19 +428,16 @@ function test_setWindowStateOverwrite() 
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setWindowState(window, JSON.stringify(state1), true);
 }
 
 
 function test_setBrowserStateInterrupted() {
-  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
-
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -556,19 +544,19 @@ function test_setBrowserStateInterrupted
   Services.ww.registerNotification(windowObserver);
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state1));
 }
 
 
 function test_reload() {
-  // Set the pref to 0 so we know exactly how many tabs should be restoring at
+  // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
--- a/browser/components/sessionstore/test/browser/browser_595601-restore_hidden.js
+++ b/browser/components/sessionstore/test/browser/browser_595601-restore_hidden.js
@@ -16,28 +16,25 @@ let state = {windows:[{tabs:[
   {entries:[{url:"http://example.com#7"}], hidden: true},
   {entries:[{url:"http://example.com#8"}], hidden: true}
 ]}]};
 
 function test() {
   waitForExplicitFinish();
 
   registerCleanupFunction(function () {
-    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
     Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
 
     TabsProgressListener.uninit();
 
     ss.setBrowserState(stateBackup);
   });
 
   TabsProgressListener.init();
 
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 3);
-
   // First stage: restoreHiddenTabs = true
   // Second stage: restoreHiddenTabs = false
   test_loadTabs(true, function () {
     test_loadTabs(false, function () {
       waitForFocus(finish);
     });
   });
 }
--- a/browser/components/sessionstore/test/browser/browser_599909.js
+++ b/browser/components/sessionstore/test/browser/browser_599909.js
@@ -38,29 +38,29 @@
 const TAB_STATE_NEEDS_RESTORE = 1;
 const TAB_STATE_RESTORING = 2;
 
 let stateBackup = ss.getBrowserState();
 
 function cleanup() {
   // Reset the pref
   try {
-    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+    Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
   } catch (e) {}
   ss.setBrowserState(stateBackup);
   executeSoon(finish);
 }
 
 function test() {
   /** Bug 599909 - to-be-reloaded tabs don't show up in switch-to-tab **/
   waitForExplicitFinish();
 
-  // Set the pref to 0 so we know exactly how many tabs should be restoring at
+  // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.org/#1" }] },
     { entries: [{ url: "http://example.org/#2" }] },
     { entries: [{ url: "http://example.org/#3" }] },
     { entries: [{ url: "http://example.org/#4" }] }
   ], selected: 1 }] };
 
--- a/browser/components/sessionstore/test/browser/browser_607016.js
+++ b/browser/components/sessionstore/test/browser/browser_607016.js
@@ -38,29 +38,29 @@
 const TAB_STATE_NEEDS_RESTORE = 1;
 const TAB_STATE_RESTORING = 2;
 
 let stateBackup = ss.getBrowserState();
 
 function cleanup() {
   // Reset the pref
   try {
-    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+    Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
   } catch (e) {}
   ss.setBrowserState(stateBackup);
   executeSoon(finish);
 }
 
 function test() {
   /** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/
   waitForExplicitFinish();
 
-  // Set the pref to 0 so we know exactly how many tabs should be restoring at
+  // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
--- a/browser/components/sessionstore/test/browser/browser_636279.js
+++ b/browser/components/sessionstore/test/browser/browser_636279.js
@@ -8,43 +8,42 @@ let stateBackup = ss.getBrowserState();
 
 let statePinned = {windows:[{tabs:[
   {entries:[{url:"http://example.com#1"}], pinned: true}
 ]}]};
 
 let state = {windows:[{tabs:[
   {entries:[{url:"http://example.com#1"}]},
   {entries:[{url:"http://example.com#2"}]},
-  {entries:[{url:"http://example.com#3"}]}
+  {entries:[{url:"http://example.com#3"}]},
+  {entries:[{url:"http://example.com#4"}]},
 ]}]};
 
 function test() {
   waitForExplicitFinish();
 
   registerCleanupFunction(function () {
-    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
     TabsProgressListener.uninit();
     ss.setBrowserState(stateBackup);
   });
 
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 2);
 
   TabsProgressListener.init();
 
   window.addEventListener("SSWindowStateReady", function onReady() {
     window.removeEventListener("SSWindowStateReady", onReady, false);
 
     let firstProgress = true;
 
     TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
       if (firstProgress) {
         firstProgress = false;
-        is(isRestoring, 2, "restoring 2 tabs concurrently");
+        is(isRestoring, 3, "restoring 3 tabs concurrently");
       } else {
-        ok(isRestoring < 3, "restoring max. 2 tabs concurrently");
+        ok(isRestoring <= 3, "restoring max. 2 tabs concurrently");
       }
 
       if (0 == needsRestore) {
         TabsProgressListener.unsetCallback();
         waitForFocus(finish);
       }
     });
 
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_645428.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const NOTIFICATION = "sessionstore-browser-state-restored";
+
+function test() {
+  waitForExplicitFinish();
+
+  function observe(subject, topic, data) {
+    if (NOTIFICATION == topic) {
+      finish();
+      ok(true, "TOPIC received");
+    }
+  }
+
+  Services.obs.addObserver(observe, NOTIFICATION, false);
+  registerCleanupFunction(function () {
+    Services.obs.removeObserver(observe, NOTIFICATION, false);
+  });
+
+  ss.setBrowserState(JSON.stringify({ windows: [] }));
+}
--- a/browser/locales/en-US/chrome/browser/preferences/main.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/main.dtd
@@ -1,15 +1,18 @@
 <!ENTITY startup.label             "Startup">
 
 <!ENTITY startupPage.label         "When &brandShortName; starts:">
 <!ENTITY startupPage.accesskey     "s">
 <!ENTITY startupHomePage.label     "Show my home page">
 <!ENTITY startupBlankPage.label    "Show a blank page">
 <!ENTITY startupLastSession.label  "Show my windows and tabs from last time">
+<!ENTITY restoreOnDemand.label     "Don’t load tabs until selected">
+<!ENTITY restoreOnDemand.accesskey "d">
+
 <!ENTITY homepage.label            "Home Page:">
 <!ENTITY homepage.accesskey        "P">
 <!ENTITY useCurrentPage.label      "Use Current Page">
 <!ENTITY useCurrentPage.accesskey  "C">
 <!ENTITY useMultiple.label         "Use Current Pages">
 <!ENTITY useBookmark.label         "Use Bookmark">
 <!ENTITY useBookmark.accesskey     "B">
 <!ENTITY restoreDefault.label      "Restore to Default">
--- a/config/config.mk
+++ b/config/config.mk
@@ -83,16 +83,19 @@ check-variable = $(if $(filter-out 0 1,$
 
 core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)))
 
 nullstr :=
 space :=$(nullstr) # EOL
 
 core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1)))))))
 
+# LIBXUL_DIST is not defined under js/src, thus we make it mean DIST there.
+LIBXUL_DIST ?= $(DIST)
+
 # FINAL_TARGET specifies the location into which we copy end-user-shipped
 # build products (typelibs, components, chrome).
 #
 # It will usually be the well-loved $(DIST)/bin, today, but can also be an
 # XPI-contents staging directory for ambitious and right-thinking extensions.
 FINAL_TARGET = $(if $(XPI_NAME),$(DIST)/xpi-stage/$(XPI_NAME),$(DIST)/bin)
 
 ifdef XPI_NAME
@@ -738,20 +741,20 @@ MERGE_FILE = $(firstword \
   $(wildcard $(LOCALE_SRCDIR)/$(1)) \
   $(srcdir)/en-US/$(1) )
 else
 MERGE_FILE = $(LOCALE_SRCDIR)/$(1)
 endif
 MERGE_FILES = $(foreach f,$(1),$(call MERGE_FILE,$(f)))
 
 ifeq (OS2,$(OS_ARCH))
-RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(DIST)"
+RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(LIBXUL_DIST)"
 else
 ifneq (WINNT,$(OS_ARCH))
-RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh
+RUN_TEST_PROGRAM = $(LIBXUL_DIST)/bin/run-mozilla.sh
 endif # ! WINNT
 endif # ! OS2
 
 #
 # Java macros
 #
 
 # Make sure any compiled classes work with at least JVM 1.4
--- a/configure.in
+++ b/configure.in
@@ -9317,17 +9317,17 @@ xpcom/xpcom-private.h
 )
 
 # Hack around an Apple bug that effects the egrep that comes with OS X 10.7.
 # "arch -arch i386 egrep" always uses the 32-bit Intel part of the egrep fat
 # binary, even on 64-bit systems.  It should work on OS X 10.4.5 and up.  We
 # (apparently) only need this hack when egrep's "pattern" is particularly
 # long (as in the following code).  See bug 655339.
 case "$host" in
-x86_64-apple-darwin*)
+*-apple-darwin*)
     FIXED_EGREP="arch -arch i386 egrep"
     ;;
 *)
     FIXED_EGREP="egrep"
     ;;
 esac
 
 # Save the defines header file before autoconf removes it.
@@ -9361,16 +9361,26 @@ fi
 
   cat >> $_CONFIG_TMP <<\EOF
 
 /* The c99 defining the limit macros (UINT32_MAX for example), says:
  * C++ implementations should define these macros only when __STDC_LIMIT_MACROS
  * is defined before <stdint.h> is included. */
 #define __STDC_LIMIT_MACROS
 
+/* Force-include hunspell_alloc_hooks.h for hunspell, so that we don't need to
+ * modify it directly.
+ *
+ * HUNSPELL_STATIC is defined in extensions/spellcheck/hunspell/src/Makefile.in,
+ * unless --enable-system-hunspell is defined.
+ */
+#if defined(HUNSPELL_STATIC)
+#include "hunspell_alloc_hooks.h"
+#endif
+
 #endif /* _MOZILLA_CONFIG_H_ */
 
 EOF
 
   # Only write mozilla-config.h when something changes (or it doesn't exist)
   if cmp -s $_CONFIG_TMP $_CONFIG_DEFS_H; then
     rm $_CONFIG_TMP
   else
--- a/content/base/public/nsIMutationObserver.h
+++ b/content/base/public/nsIMutationObserver.h
@@ -88,16 +88,33 @@ struct CharacterDataChangeInfo
 
   /**
    * The net result is that mChangeStart characters at the beginning of the
    * text remained as they were.  The next mChangeEnd - mChangeStart characters
    * were removed, and mReplaceLength characters were inserted in their place.
    * The text that used to begin at mChangeEnd now begins at
    * mChangeStart + mReplaceLength.
    */
+
+  struct Details {
+    enum {
+      eMerge,  // two text nodes are merged as a result of normalize()
+      eSplit   // a text node is split as a result of splitText()
+    } mType;
+    /**
+     * For eMerge it's the text node that will be removed, for eSplit it's the
+     * new text node.
+     */
+    nsIContent* mNextSibling;
+  };
+
+  /**
+   * Used for splitText() and normalize(), otherwise null.
+   */
+  Details* mDetails;
 };
 
 /**
  * Mutation observer interface
  *
  * See nsINode::AddMutationObserver, nsINode::RemoveMutationObserver for how to
  * attach or remove your observers.
  *
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -143,31 +143,38 @@ NS_INTERFACE_MAP_BEGIN(nsDOMFileBase)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
   NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
   NS_INTERFACE_MAP_ENTRY(nsIMutable)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_ADDREF(nsDOMFileBase)
-NS_IMPL_RELEASE(nsDOMFileBase)
+// Threadsafe when GetMutable() == PR_FALSE
+NS_IMPL_THREADSAFE_ADDREF(nsDOMFileBase)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMFileBase)
 
 NS_IMETHODIMP
 nsDOMFileBase::GetName(nsAString &aFileName)
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
   aFileName = mName;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMFileBase::GetMozFullPath(nsAString &aFileName)
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
+
+  // It is unsafe to call IsCallerTrustedForCapability on a non-main thread. If
+  // you hit the following assertion you need to figure out some other way to
+  // determine privileges and call GetMozFullPathInternal.
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
   if (nsContentUtils::IsCallerTrustedForCapability("UniversalFileRead")) {
     return GetMozFullPathInternal(aFileName);
   }
   aFileName.Truncate();
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -282,17 +282,18 @@ nsGenericDOMDataNode::ReplaceData(PRUint
 {
   return SetTextInternal(aOffset, aCount, aData.BeginReading(),
                          aData.Length(), PR_TRUE);
 }
 
 nsresult
 nsGenericDOMDataNode::SetTextInternal(PRUint32 aOffset, PRUint32 aCount,
                                       const PRUnichar* aBuffer,
-                                      PRUint32 aLength, PRBool aNotify)
+                                      PRUint32 aLength, PRBool aNotify,
+                                      CharacterDataChangeInfo::Details* aDetails)
 {
   NS_PRECONDITION(aBuffer || !aLength,
                   "Null buffer passed to SetTextInternal!");
 
   // sanitize arguments
   PRUint32 textLength = mText.GetLength();
   if (aOffset > textLength) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
@@ -325,17 +326,18 @@ nsGenericDOMDataNode::SetTextInternal(PR
     oldValue = GetCurrentValueAtom();
   }
     
   if (aNotify) {
     CharacterDataChangeInfo info = {
       aOffset == textLength,
       aOffset,
       endOffset,
-      aLength
+      aLength,
+      aDetails
     };
     nsNodeUtils::CharacterDataWillChange(this, &info);
   }
 
   if (aOffset == 0 && endOffset == textLength) {
     // Replacing whole text or old text was empty
     mText.SetTo(aBuffer, aLength);
   }
@@ -371,17 +373,18 @@ nsGenericDOMDataNode::SetTextInternal(PR
   UpdateBidiStatus(aBuffer, aLength);
 
   // Notify observers
   if (aNotify) {
     CharacterDataChangeInfo info = {
       aOffset == textLength,
       aOffset,
       endOffset,
-      aLength
+      aLength,
+      aDetails
     };
     nsNodeUtils::CharacterDataChanged(this, &info);
 
     if (haveMutationListeners) {
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_CHARACTERDATAMODIFIED);
 
       mutation.mPrevAttrValue = oldValue;
       if (aLength > 0) {
@@ -739,35 +742,33 @@ nsGenericDOMDataNode::SplitData(PRUint32
 
   PRUint32 cutStartOffset = aCloneAfterOriginal ? aOffset : 0;
   PRUint32 cutLength = aCloneAfterOriginal ? length - aOffset : aOffset;
   rv = SubstringData(cutStartOffset, cutLength, cutText);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  rv = DeleteData(cutStartOffset, cutLength);
+  // Use Clone for creating the new node so that the new node is of same class
+  // as this node!
+  nsCOMPtr<nsIContent> newContent = CloneDataNode(mNodeInfo, PR_FALSE);
+  if (!newContent) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  newContent->SetText(cutText, PR_TRUE); // XXX should be PR_FALSE?
+
+  CharacterDataChangeInfo::Details details = {
+    CharacterDataChangeInfo::Details::eSplit, newContent
+  };
+  rv = SetTextInternal(cutStartOffset, cutLength, nsnull, 0, PR_TRUE, &details);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  /*
-   * Use Clone for creating the new node so that the new node is of same class
-   * as this node!
-   */
-
-  nsCOMPtr<nsIContent> newContent = CloneDataNode(mNodeInfo, PR_FALSE);
-  if (!newContent) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  newContent->SetText(cutText, PR_TRUE);
-
   nsCOMPtr<nsINode> parent = GetNodeParent();
-
   if (parent) {
     PRInt32 insertionIndex = parent->IndexOf(this);
     if (aCloneAfterOriginal) {
       ++insertionIndex;
     }
     parent->InsertChildAt(newContent, insertionIndex, PR_TRUE);
   }
 
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -342,17 +342,18 @@ protected:
                                                 PRInt32 aIndex);
 
   static PRInt32 LastLogicallyAdjacentTextNode(nsIContent* aParent,
                                                PRInt32 aIndex,
                                                PRUint32 aCount);
 
   nsresult SetTextInternal(PRUint32 aOffset, PRUint32 aCount,
                            const PRUnichar* aBuffer, PRUint32 aLength,
-                           PRBool aNotify);
+                           PRBool aNotify,
+                           CharacterDataChangeInfo::Details* aDetails = nsnull);
 
   /**
    * Method to clone this node. This needs to be overriden by all derived
    * classes. If aCloneText is true the text content will be cloned too.
    *
    * @param aOwnerDocument the ownerDocument of the clone
    * @param aCloneText if true the text content will be cloned too
    * @return the clone
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -125,16 +125,17 @@
 #include "nsIView.h"
 #include "nsIViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsXBLInsertionPoint.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
 #include "nsPLDOMEvent.h"
+#include "nsTextNode.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif /* MOZ_XUL */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
@@ -601,23 +602,24 @@ nsINode::Normalize()
     if (text->GetLength()) {
       nsIContent* target = node->GetPreviousSibling();
       NS_ASSERTION((target && target->NodeType() == nsIDOMNode::TEXT_NODE) ||
                    hasRemoveListeners,
                    "Should always have a previous text sibling unless "
                    "mutation events messed us up");
       if (!hasRemoveListeners ||
           (target && target->NodeType() == nsIDOMNode::TEXT_NODE)) {
+        nsTextNode* t = static_cast<nsTextNode*>(target);
         if (text->Is2b()) {
-          target->AppendText(text->Get2b(), text->GetLength(), PR_TRUE);
+          t->AppendTextForNormalize(text->Get2b(), text->GetLength(), PR_TRUE, node);
         }
         else {
           tmpStr.Truncate();
           text->AppendTo(tmpStr);
-          target->AppendText(tmpStr.get(), tmpStr.Length(), PR_TRUE);
+          t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), PR_TRUE, node);
         }
       }
     }
 
     // Remove node
     nsINode* parent = node->GetNodeParent();
     NS_ASSERTION(parent || hasRemoveListeners,
                  "Should always have a parent unless "
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -269,31 +269,68 @@ nsRange::CharacterDataChanged(nsIDocumen
                               nsIContent* aContent,
                               CharacterDataChangeInfo* aInfo)
 {
   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
 
   // If the changed node contains our start boundary and the change starts
   // before the boundary we'll need to adjust the offset.
   if (aContent == mStartParent &&
-      aInfo->mChangeStart < (PRUint32)mStartOffset) {
-    // If boundary is inside changed text, position it before change
-    // else adjust start offset for the change in length
-    mStartOffset = (PRUint32)mStartOffset <= aInfo->mChangeEnd ?
-       aInfo->mChangeStart :
-       mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
-         aInfo->mReplaceLength;
+      aInfo->mChangeStart < static_cast<PRUint32>(mStartOffset)) {
+    if (aInfo->mDetails) {
+      // splitText(), aInfo->mDetails->mNextSibling is the new text node
+      NS_ASSERTION(aInfo->mDetails->mType ==
+                   CharacterDataChangeInfo::Details::eSplit,
+                   "only a split can start before the end");
+      NS_ASSERTION(static_cast<PRUint32>(mStartOffset) <= aInfo->mChangeEnd,
+                   "mStartOffset is beyond the end of this node");
+      mStartOffset = static_cast<PRUint32>(mStartOffset) - aInfo->mChangeStart;
+      mStartParent = aInfo->mDetails->mNextSibling;
+    } else {
+      // If boundary is inside changed text, position it before change
+      // else adjust start offset for the change in length.
+      mStartOffset = static_cast<PRUint32>(mStartOffset) <= aInfo->mChangeEnd ?
+        aInfo->mChangeStart :
+        mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
+          aInfo->mReplaceLength;
+    }
   }
 
   // Do the same thing for the end boundary.
-  if (aContent == mEndParent && aInfo->mChangeStart < (PRUint32)mEndOffset) {
-    mEndOffset = (PRUint32)mEndOffset <= aInfo->mChangeEnd ?
-       aInfo->mChangeStart :
-       mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
-         aInfo->mReplaceLength;
+  if (aContent == mEndParent && aInfo->mChangeStart < static_cast<PRUint32>(mEndOffset)) {
+    if (aInfo->mDetails) {
+      // splitText(), aInfo->mDetails->mNextSibling is the new text node
+      NS_ASSERTION(aInfo->mDetails->mType ==
+                   CharacterDataChangeInfo::Details::eSplit,
+                   "only a split can start before the end");
+      NS_ASSERTION(static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd,
+                   "mEndOffset is beyond the end of this node");
+      mEndOffset = static_cast<PRUint32>(mEndOffset) - aInfo->mChangeStart;
+      mEndParent = aInfo->mDetails->mNextSibling;
+    } else {
+      mEndOffset = static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd ?
+        aInfo->mChangeStart :
+        mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
+          aInfo->mReplaceLength;
+    }
+  }
+
+  if (aInfo->mDetails &&
+      aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eMerge) {
+    // normalize(), aInfo->mDetails->mNextSibling is the merged text node
+    // that will be removed
+    nsIContent* removed = aInfo->mDetails->mNextSibling;
+    if (removed == mStartParent) {
+      mStartOffset = static_cast<PRUint32>(mStartOffset) + aInfo->mChangeStart;
+      mStartParent = aContent;
+    }
+    if (removed == mEndParent) {
+      mEndOffset = static_cast<PRUint32>(mEndOffset) + aInfo->mChangeStart;
+      mEndParent = aContent;
+    }
   }
 }
 
 void
 nsRange::ContentInserted(nsIDocument* aDocument,
                          nsIContent* aContainer,
                          nsIContent* aChild,
                          PRInt32 aIndexInContainer)
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -200,16 +200,26 @@ nsTextNode::UnbindFromAttribute()
   NS_ASSERTION(GetNodeParent(), "Bind before unbinding!");
   NS_ASSERTION(GetNodeParent() &&
                GetNodeParent()->IsNodeOfType(nsINode::eATTRIBUTE),
                "Use this method only to unbind from an attribute!");
   mParent = nsnull;
   return NS_OK;
 }
 
+nsresult
+nsTextNode::AppendTextForNormalize(const PRUnichar* aBuffer, PRUint32 aLength,
+                                   PRBool aNotify, nsIContent* aNextSibling)
+{
+  CharacterDataChangeInfo::Details details = {
+    CharacterDataChangeInfo::Details::eMerge, aNextSibling
+  };
+  return SetTextInternal(mText.GetLength(), 0, aBuffer, aLength, aNotify, &details);
+}
+
 #ifdef DEBUG
 void
 nsTextNode::List(FILE* out, PRInt32 aIndent) const
 {
   PRInt32 index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
   fprintf(out, "Text@%p", static_cast<const void*>(this));
--- a/content/base/src/nsTextNode.h
+++ b/content/base/src/nsTextNode.h
@@ -78,13 +78,16 @@ public:
   virtual nsGenericDOMDataNode* CloneDataNode(nsINodeInfo *aNodeInfo,
                                               PRBool aCloneText) const;
 
   nsresult BindToAttribute(nsIAttribute* aAttr);
   nsresult UnbindFromAttribute();
 
   virtual nsXPCClassInfo* GetClassInfo();
 
+  nsresult AppendTextForNormalize(const PRUnichar* aBuffer, PRUint32 aLength,
+                                  PRBool aNotify, nsIContent* aNextSibling);
+
 #ifdef DEBUG
   virtual void List(FILE* out, PRInt32 aIndent) const;
   virtual void DumpContent(FILE* out, PRInt32 aIndent, PRBool aDumpAll) const;
 #endif
 };
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -488,22 +488,21 @@ PRBool nsBuiltinDecoderStateMachine::IsP
 }
 
 void nsBuiltinDecoderStateMachine::AudioLoop()
 {
   NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
   LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder.get()));
   PRInt64 audioDuration = 0;
   PRInt64 audioStartTime = -1;
+  PRInt64 samplesWritten = 0;
   PRUint32 channels, rate;
   double volume = -1;
   PRBool setVolume;
   PRInt32 minWriteSamples = -1;
-  PRInt64 samplesAtLastSleep = 0;
-
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mAudioCompleted = PR_FALSE;
     audioStartTime = mAudioStartTime;
     channels = mInfo.mAudioChannels;
     rate = mInfo.mAudioRate;
     NS_ASSERTION(audioStartTime != -1, "Should have audio start time by now");
   }
@@ -543,17 +542,16 @@ void nsBuiltinDecoderStateMachine::Audio
              (!IsPlaying() ||
               mState == DECODER_STATE_BUFFERING ||
               (mReader->mAudioQueue.GetSize() == 0 &&
                !mReader->mAudioQueue.AtEndOfStream())))
       {
         if (!IsPlaying() && !mAudioStream->IsPaused()) {
           mAudioStream->Pause();
         }
-        samplesAtLastSleep = audioDuration;
         mon.Wait();
       }
 
       // If we're shutting down, break out and exit the audio thread.
       if (mState == DECODER_STATE_SHUTDOWN ||
           mStopAudioThread ||
           mReader->mAudioQueue.AtEndOfStream())
       {
@@ -611,38 +609,38 @@ void nsBuiltinDecoderStateMachine::Audio
     }
 
     if (missingSamples > 0) {
       // The next sound chunk begins some time after the end of the last chunk
       // we pushed to the sound hardware. We must push silence into the audio
       // hardware so that the next sound chunk begins playback at the correct
       // time.
       missingSamples = NS_MIN(static_cast<PRInt64>(PR_UINT32_MAX), missingSamples);
-      audioDuration += PlaySilence(static_cast<PRUint32>(missingSamples),
+      samplesWritten = PlaySilence(static_cast<PRUint32>(missingSamples),
                                    channels, playedSamples);
     } else {
-      audioDuration += PlayFromAudioQueue(sampleTime, channels);
+      samplesWritten = PlayFromAudioQueue(sampleTime, channels);
     }
+    audioDuration += samplesWritten;
     {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
       PRInt64 playedUsecs;
       if (!SamplesToUsecs(audioDuration, rate, playedUsecs)) {
         NS_WARNING("Int overflow calculating playedUsecs");
         break;
       }
       if (!AddOverflow(audioStartTime, playedUsecs, mAudioEndTime)) {
         NS_WARNING("Int overflow calculating audio end time");
         break;
       }
 
       PRInt64 audioAhead = mAudioEndTime - GetMediaTime();
       if (audioAhead > AMPLE_AUDIO_USECS &&
-          audioDuration - samplesAtLastSleep > minWriteSamples)
+          samplesWritten > minWriteSamples)
       {
-        samplesAtLastSleep = audioDuration;
         // We've pushed enough audio onto the hardware that we've queued up a
         // significant amount ahead of the playback position. The decode
         // thread will be going to sleep, so we won't get any new samples
         // anyway, so sleep until we need to push to the hardware again.
         Wait(AMPLE_AUDIO_USECS / 2);
         // Kick the decode thread; since above we only do a NotifyAll when
         // we pop an audio chunk of the queue, the decoder won't wake up if
         // we've got no more decoded chunks to push to the hardware. We can
@@ -657,16 +655,31 @@ void nsBuiltinDecoderStateMachine::Audio
       mState != DECODER_STATE_SHUTDOWN &&
       !mStopAudioThread)
   {
     // Last sample pushed to audio hardware, wait for the audio to finish,
     // before the audio thread terminates.
     PRBool seeking = PR_FALSE;
     {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+      if (samplesWritten < minWriteSamples) {
+        // We've not written minWriteSamples in the last write, the audio
+        // may not start playing. Write silence to ensure we've got enough
+        // samples written to start playback.
+        PRInt64 samples = minWriteSamples - samplesWritten;
+        if (samples < PR_UINT32_MAX / channels) {
+          // Write silence manually rather than using PlaySilence(), so that
+          // the AudioAPI doesn't get a copy of the samples.
+          PRUint32 numValues = samples * channels;
+          nsAutoArrayPtr<SoundDataValue> buf(new SoundDataValue[numValues]);
+          memset(buf.get(), 0, sizeof(SoundDataValue) * numValues);
+          mAudioStream->Write(buf, numValues, PR_TRUE);
+        }
+      }
+
       PRInt64 oldPosition = -1;
       PRInt64 position = GetMediaTime();
       while (oldPosition != position &&
              mAudioEndTime - position > 0 &&
              mState != DECODER_STATE_SEEKING &&
              mState != DECODER_STATE_SHUTDOWN)
       {
         const PRInt64 DRAIN_BLOCK_USECS = 100000;
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/678822-1.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+  <animate repeatCount="2" dur="1s" accumulate="1" />
+</svg>
--- a/content/smil/crashtests/crashtests.list
+++ b/content/smil/crashtests/crashtests.list
@@ -33,8 +33,9 @@ load 608549-1.svg
 load 608295-1.html
 load 611927-1.svg
 load 615002-1.svg
 load 615872-1.svg
 load 665334-1.svg
 load 669225-1.svg
 load 670313-1.svg
 load 669225-2.svg
+load 678822-1.svg
--- a/content/smil/nsSMILAnimationFunction.cpp
+++ b/content/smil/nsSMILAnimationFunction.cpp
@@ -182,17 +182,19 @@ nsSMILAnimationFunction::SampleAt(nsSMIL
   mHasChanged |= mLastValue;
 
   // Are we sampling at a new point in simple duration? And does that matter?
   mHasChanged |=
     (mSampleTime != aSampleTime || mSimpleDuration != aSimpleDuration) &&
     !IsValueFixedForSimpleDuration();
 
   // Are we on a new repeat and accumulating across repeats?
-  mHasChanged |= (mRepeatIteration != aRepeatIteration) && GetAccumulate();
+  if (!mErrorFlags) { // (can't call GetAccumulate() if we've had parse errors)
+    mHasChanged |= (mRepeatIteration != aRepeatIteration) && GetAccumulate();
+  }
 
   mSampleTime       = aSampleTime;
   mSimpleDuration   = aSimpleDuration;
   mRepeatIteration  = aRepeatIteration;
   mLastValue        = PR_FALSE;
 }
 
 void
--- a/docshell/base/nsDownloadHistory.cpp
+++ b/docshell/base/nsDownloadHistory.cpp
@@ -53,17 +53,18 @@
 NS_IMPL_ISUPPORTS1(nsDownloadHistory, nsIDownloadHistory)
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIDownloadHistory
 
 NS_IMETHODIMP
 nsDownloadHistory::AddDownload(nsIURI *aSource,
                                nsIURI *aReferrer,
-                               PRTime aStartTime)
+                               PRTime aStartTime,
+                               nsIURI *aDestination)
 {
   NS_ENSURE_ARG_POINTER(aSource);
 
   nsCOMPtr<nsIGlobalHistory2> history =
     do_GetService("@mozilla.org/browser/global-history;2");
   if (!history)
     return NS_ERROR_NOT_AVAILABLE;
 
--- a/docshell/base/nsIDownloadHistory.idl
+++ b/docshell/base/nsIDownloadHistory.idl
@@ -41,32 +41,36 @@
 
 interface nsIURI;
 
 /**
  * This interface can be used to add a download to history.  There is a separate
  * interface specifically for downloads in case embedders choose to track
  * downloads differently from other types of history.
  */
-[scriptable, uuid(202533cd-a8f1-4ee4-8d20-3a6a0d2c6c51)]
+[scriptable, uuid(a7a3358c-9af2-41e3-adfe-3bf0b7ac2c38)]
 interface nsIDownloadHistory : nsISupports {
   /**
    * Adds a download to history.  This will also notify observers that the
    * URI aSource is visited with the topic NS_LINK_VISITED_EVENT_TOPIC if
    * aSource has not yet been visited.
    *
    * @param aSource
    *        The source of the download we are adding to history.  This cannot be
    *        null.
    * @param aReferrer
    *        [optional] The referrer of source URI.
    * @param aStartTime
    *        [optional] The time the download was started.  If the start time
    *        is not given, the current time is used.
+   * @param aDestination
+   *        [optional] The target where the download is to be saved on the local
+   *        filesystem.
    * @throws NS_ERROR_NOT_AVAILABLE
    *         In a situation where a history implementation is not available,
    *         where 'history implementation' refers to something like
    *         nsIGlobalHistory and friends.
    */
   void addDownload(in nsIURI aSource, [optional] in nsIURI aReferrer,
-                   [optional] in PRTime aStartTime);
+                   [optional] in PRTime aStartTime,
+                   [optional] in nsIURI aDestination);
 };
 
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2958,35 +2958,16 @@ NS_JProfStopProfiling()
 static JSFunctionSpec JProfFunctions[] = {
     {"JProfStartProfiling",        JProfStartProfilingJS,      0, 0},
     {"JProfStopProfiling",         JProfStopProfilingJS,       0, 0},
     {nsnull,                       nsnull,                     0, 0}
 };
 
 #endif /* defined(MOZ_JPROF) */
 
-#ifdef MOZ_CALLGRIND
-static JSFunctionSpec CallgrindFunctions[] = {
-    {"startCallgrind",             js_StartCallgrind,          0, 0},
-    {"stopCallgrind",              js_StopCallgrind,           0, 0},
-    {"dumpCallgrind",              js_DumpCallgrind,           1, 0},
-    {nsnull,                       nsnull,                     0, 0}
-};
-#endif
-
-#ifdef MOZ_VTUNE
-static JSFunctionSpec VtuneFunctions[] = {
-    {"startVtune",                 js_StartVtune,              1, 0},
-    {"stopVtune",                  js_StopVtune,               0, 0},
-    {"pauseVtune",                 js_PauseVtune,              0, 0},
-    {"resumeVtune",                js_ResumeVtune,             0, 0},
-    {nsnull,                       nsnull,                     0, 0}
-};
-#endif
-
 #ifdef MOZ_TRACEVIS
 static JSFunctionSpec EthogramFunctions[] = {
     {"initEthogram",               js_InitEthogram,            0, 0},
     {"shutdownEthogram",           js_ShutdownEthogram,        0, 0},
     {nsnull,                       nsnull,                     0, 0}
 };
 #endif
 
@@ -3012,26 +2993,16 @@ nsJSContext::InitClasses(void *aGlobalOb
   ::JS_DefineFunctions(mContext, globalObj, TraceMallocFunctions);
 #endif
 
 #ifdef MOZ_JPROF
   // Attempt to initialize JProf functions
   ::JS_DefineFunctions(mContext, globalObj, JProfFunctions);
 #endif
 
-#ifdef MOZ_CALLGRIND
-  // Attempt to initialize Callgrind functions
-  ::JS_DefineFunctions(mContext, globalObj, CallgrindFunctions);
-#endif
-
-#ifdef MOZ_VTUNE
-  // Attempt to initialize Vtune functions
-  ::JS_DefineFunctions(mContext, globalObj, VtuneFunctions);
-#endif
-
 #ifdef MOZ_TRACEVIS
   // Attempt to initialize Ethogram functions
   ::JS_DefineFunctions(mContext, globalObj, EthogramFunctions);
 #endif
 
   JSOptionChangedCallback(js_options_dot_str, this);
 
   return rv;
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -40,16 +41,17 @@
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsobj.h"
 
 #include "nsTraceRefcnt.h"
 
 #include "WorkerInlines.h"
+#include "WorkerPrivate.h"
 
 #define PROPERTY_FLAGS \
   JSPROP_ENUMERATE | JSPROP_SHARED
 
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
 #define CONSTANT_FLAGS \
@@ -410,16 +412,18 @@ class MessageEvent : public Event
   static JSClass sMainRuntimeClass;
 
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
 
 protected:
   uint64* mData;
   size_t mDataByteCount;
+  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+  bool mMainRuntime;
 
 public:
   static bool
   IsThisClass(JSClass* aClass)
   {
     return aClass == &sClass || aClass == &sMainRuntimeClass;
   }
 
@@ -430,46 +434,48 @@ public:
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     return JS_InitClass(aCx, aObj, aParentProto, clasp, Construct, 0,
                         sProperties, sFunctions, NULL, NULL);
   }
 
   static JSObject*
   Create(JSContext* aCx, JSObject* aParent, JSAutoStructuredCloneBuffer& aData,
-         bool aMainRuntime)
+         nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime)
   {
     JSString* type = JS_InternString(aCx, "message");
     if (!type) {
       return NULL;
     }
 
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     JSObject* obj = JS_NewObject(aCx, clasp, NULL, aParent);
     if (!obj) {
       return NULL;
     }
 
-    MessageEvent* priv = new MessageEvent();
+    MessageEvent* priv = new MessageEvent(aMainRuntime);
     if (!SetJSPrivateSafeish(aCx, obj, priv) ||
         !InitMessageEventCommon(aCx, obj, priv, type, false, false, NULL, NULL,
                                 NULL, true)) {
       SetJSPrivateSafeish(aCx, obj, NULL);
       delete priv;
       return NULL;
     }
 
     aData.steal(&priv->mData, &priv->mDataByteCount);
+    priv->mClonedObjects.SwapElements(aClonedObjects);
+
     return obj;
   }
 
 protected:
-  MessageEvent()
-  : mData(NULL), mDataByteCount(0)
+  MessageEvent(bool aMainRuntime)
+  : mData(NULL), mDataByteCount(0), mMainRuntime(aMainRuntime)
   {
     MOZ_COUNT_CTOR(mozilla::dom::workers::MessageEvent);
   }
 
   virtual ~MessageEvent()
   {
     MOZ_COUNT_DTOR(mozilla::dom::workers::MessageEvent);
     JS_ASSERT(!mData);
@@ -565,18 +571,24 @@ private:
     // Deserialize and save the data value if we can.
     if (slot == SLOT_data && event->mData) {
       JSAutoStructuredCloneBuffer buffer;
       buffer.adopt(event->mData, event->mDataByteCount);
 
       event->mData = NULL;
       event->mDataByteCount = 0;
 
+      // Release reference to objects that were AddRef'd for
+      // cloning into worker when array goes out of scope.
+      nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+      clonedObjects.SwapElements(event->mClonedObjects);
+
       jsval data;
-      if (!buffer.read(aCx, &data) ||
+      if (!buffer.read(aCx, &data,
+                       WorkerStructuredCloneCallbacks(event->mMainRuntime)) ||
           !JS_SetReservedSlot(aCx, aObj, slot, data)) {
         return false;
       }
 
       *aVp = data;
       return true;
     }
 
@@ -1052,20 +1064,21 @@ CreateGenericEvent(JSContext* aCx, JSStr
                    bool aCancelable, bool aMainRuntime)
 {
   JSObject* global = JS_GetGlobalForScopeChain(aCx);
   return Event::Create(aCx, global, aType, aBubbles, aCancelable, aMainRuntime);
 }
 
 JSObject*
 CreateMessageEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aData,
+                   nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                    bool aMainRuntime)
 {
   JSObject* global = JS_GetGlobalForScopeChain(aCx);
-  return MessageEvent::Create(aCx, global, aData, aMainRuntime);
+  return MessageEvent::Create(aCx, global, aData, aClonedObjects, aMainRuntime);
 }
 
 JSObject*
 CreateErrorEvent(JSContext* aCx, JSString* aMessage, JSString* aFilename,
                  uint32 aLineNumber, bool aMainRuntime)
 {
   JSObject* global = JS_GetGlobalForScopeChain(aCx);
   return ErrorEvent::Create(aCx, global, aMessage, aFilename, aLineNumber,
--- a/dom/workers/Events.h
+++ b/dom/workers/Events.h
@@ -37,32 +37,35 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_workers_events_h__
 #define mozilla_dom_workers_events_h__
 
 #include "Workers.h"
 
 #include "jspubtd.h"
+#include "nsTArray.h"
+#include "nsCOMPtr.h"
 
 class JSAutoStructuredCloneBuffer;
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace events {
 
 bool
 InitClasses(JSContext* aCx, JSObject* aGlobal, bool aMainRuntime);
 
 JSObject*
 CreateGenericEvent(JSContext* aCx, JSString* aType, bool aBubbles,
                    bool aCancelable, bool aMainRuntime);
 
 JSObject*
 CreateMessageEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aData,
+                   nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                    bool aMainRuntime);
 
 JSObject*
 CreateErrorEvent(JSContext* aCx, JSString* aMessage, JSString* aFilename,
                  uint32 aLineNumber, bool aMainRuntime);
 
 JSObject*
 CreateProgressEvent(JSContext* aCx, JSString* aType, bool aLengthComputable,
--- a/dom/workers/Exceptions.cpp
+++ b/dom/workers/Exceptions.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -184,17 +185,16 @@ private:
 JSClass DOMException::sClass = {
   "DOMException",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
   JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
-
 JSPropertySpec DOMException::sProperties[] = {
   { "code", SLOT_code, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub },
   { "name", SLOT_name, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub },
   { 0, 0, 0, NULL, NULL }
 };
 
 JSFunctionSpec DOMException::sFunctions[] = {
   JS_FN("toString", ToString, 0, 0),
@@ -272,32 +272,192 @@ DOMException::Create(JSContext* aCx, int
   if (!SetJSPrivateSafeish(aCx, obj, priv)) {
     delete priv;
     return NULL;
   }
 
   return obj;
 }
 
+class FileException : public PrivatizableBase
+{
+  static JSClass sClass;
+  static JSPropertySpec sProperties[];
+  static JSPropertySpec sStaticProperties[];
+
+  enum SLOT {
+    SLOT_code = 0,
+    SLOT_name,
+
+    SLOT_COUNT
+  };
+
+public:
+  static JSObject*
+  InitClass(JSContext* aCx, JSObject* aObj)
+  {
+    return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties,
+                        NULL, sStaticProperties, NULL);
+  }
+
+  static JSObject*
+  Create(JSContext* aCx, intN aCode);
+
+private:
+  FileException()
+  {
+    MOZ_COUNT_CTOR(mozilla::dom::workers::exceptions::FileException);
+  }
+
+  ~FileException()
+  {
+    MOZ_COUNT_DTOR(mozilla::dom::workers::exceptions::FileException);
+  }
+
+  static JSBool
+  Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
+                         sClass.name);
+    return false;
+  }
+
+  static void
+  Finalize(JSContext* aCx, JSObject* aObj)
+  {
+    JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
+    delete GetJSPrivateSafeish<FileException>(aCx, aObj);
+  }
+
+  static JSBool
+  GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    JS_ASSERT(JSID_IS_INT(aIdval));
+
+    int32 slot = JSID_TO_INT(aIdval);
+
+    JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+
+    if (classPtr != &sClass ||
+        !GetJSPrivateSafeish<FileException>(aCx, aObj)) {
+      JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+                           JSMSG_INCOMPATIBLE_PROTO, sClass.name,
+                           sProperties[slot].name,
+                           classPtr ? classPtr->name : "object");
+      return false;
+    }
+
+    return JS_GetReservedSlot(aCx, aObj, slot, aVp);
+  }
+
+  static JSBool
+  GetConstant(JSContext* aCx, JSObject* aObj, jsid idval, jsval* aVp)
+  {
+    JS_ASSERT(JSID_IS_INT(idval));
+    *aVp = INT_TO_JSVAL(JSID_TO_INT(idval));
+    return true;
+  }
+};
+
+JSClass FileException::sClass = {
+  "FileException",
+  JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSPropertySpec FileException::sProperties[] = {
+  { "code", SLOT_code, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub },
+  { "name", SLOT_name, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub },
+  { 0, 0, 0, NULL, NULL }
+};
+
+JSPropertySpec FileException::sStaticProperties[] = {
+
+#define EXCEPTION_ENTRY(_name) \
+  { #_name, FILE_##_name, CONSTANT_FLAGS, GetConstant, NULL },
+
+  EXCEPTION_ENTRY(NOT_FOUND_ERR)
+  EXCEPTION_ENTRY(SECURITY_ERR)
+  EXCEPTION_ENTRY(ABORT_ERR)
+  EXCEPTION_ENTRY(NOT_READABLE_ERR)
+  EXCEPTION_ENTRY(ENCODING_ERR)
+
+#undef EXCEPTION_ENTRY
+
+  { 0, 0, 0, NULL, NULL }
+};
+
+// static
+JSObject*
+FileException::Create(JSContext* aCx, intN aCode)
+{
+  JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+  if (!obj) {
+    return NULL;
+  }
+
+  size_t foundIndex = size_t(-1);
+  for (size_t index = 0;
+       index < JS_ARRAY_LENGTH(sStaticProperties) - 1;
+       index++) {
+    if (sStaticProperties[index].tinyid == aCode) {
+      foundIndex = index;
+      break;
+    }
+  }
+
+  JS_ASSERT(foundIndex != size_t(-1));
+
+  JSString* name = JS_NewStringCopyZ(aCx, sStaticProperties[foundIndex].name);
+  if (!name) {
+    return NULL;
+  }
+
+  if (!JS_SetReservedSlot(aCx, obj, SLOT_code, INT_TO_JSVAL(aCode)) ||
+      !JS_SetReservedSlot(aCx, obj, SLOT_name, STRING_TO_JSVAL(name))) {
+    return NULL;
+  }
+
+  FileException* priv = new FileException();
+  if (!SetJSPrivateSafeish(aCx, obj, priv)) {
+    delete priv;
+    return NULL;
+  }
+
+  return obj;
+}
+
 } // anonymous namespace
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace exceptions {
 
 bool
 InitClasses(JSContext* aCx, JSObject* aGlobal)
 {
-  return !!DOMException::InitClass(aCx, aGlobal);
+  return DOMException::InitClass(aCx, aGlobal) &&
+         FileException::InitClass(aCx, aGlobal);
 }
 
 void
 ThrowDOMExceptionForCode(JSContext* aCx, intN aCode)
 {
   JSObject* exception = DOMException::Create(aCx, aCode);
   JS_ASSERT(exception);
 
   JS_SetPendingException(aCx, OBJECT_TO_JSVAL(exception));
 }
 
+void
+ThrowFileExceptionForCode(JSContext* aCx, intN aCode)
+{
+  JSObject* exception = FileException::Create(aCx, aCode);
+  JS_ASSERT(exception);
+
+  JS_SetPendingException(aCx, OBJECT_TO_JSVAL(exception));
+}
+
 } // namespace exceptions
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/Exceptions.h
+++ b/dom/workers/Exceptions.h
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -38,16 +39,17 @@
 
 #ifndef mozilla_dom_workers_exceptions_h__
 #define mozilla_dom_workers_exceptions_h__
 
 #include "Workers.h"
 
 #include "jspubtd.h"
 
+// DOMException Codes.
 #define INDEX_SIZE_ERR 1
 #define DOMSTRING_SIZE_ERR 2
 #define HIERARCHY_REQUEST_ERR 3
 #define WRONG_DOCUMENT_ERR 4
 #define INVALID_CHARACTER_ERR 5
 #define NO_DATA_ALLOWED_ERR 6
 #define NO_MODIFICATION_ALLOWED_ERR 7
 #define NOT_FOUND_ERR 8
@@ -64,23 +66,33 @@
 #define NETWORK_ERR 19
 #define ABORT_ERR 20
 #define URL_MISMATCH_ERR 21
 #define QUOTA_EXCEEDED_ERR 22
 #define TIMEOUT_ERR 23
 #define INVALID_NODE_TYPE_ERR 24
 #define DATA_CLONE_ERR 25
 
+// FileException Codes
+#define FILE_NOT_FOUND_ERR 1
+#define FILE_SECURITY_ERR 2
+#define FILE_ABORT_ERR 3
+#define FILE_NOT_READABLE_ERR 4
+#define FILE_ENCODING_ERR 5
+
 BEGIN_WORKERS_NAMESPACE
 
 namespace exceptions {
 
 bool
 InitClasses(JSContext* aCx, JSObject* aGlobal);
 
 void
 ThrowDOMExceptionForCode(JSContext* aCx, intN aCode);
 
+void
+ThrowFileExceptionForCode(JSContext* aCx, intN aCode);
+
 } // namespace exceptions
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_exceptions_h__
new file mode 100644
--- /dev/null
+++ b/dom/workers/File.cpp
@@ -0,0 +1,452 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 "File.h"
+
+#include "nsIDOMFile.h"
+
+#include "jsapi.h"
+#include "jsatom.h"
+#include "jscntxt.h"
+#include "nsCOMPtr.h"
+#include "nsJSUtils.h"
+#include "nsStringGlue.h"
+#include "xpcprivate.h"
+#include "xpcquickstubs.h"
+
+#include "Exceptions.h"
+#include "WorkerInlines.h"
+#include "WorkerPrivate.h"
+
+#define PROPERTY_FLAGS \
+  JSPROP_ENUMERATE | JSPROP_SHARED
+
+USING_WORKERS_NAMESPACE
+
+using mozilla::dom::workers::exceptions::ThrowFileExceptionForCode;
+
+namespace {
+
+class Blob
+{
+  // Blob should never be instantiated.
+  Blob();
+  ~Blob();
+
+  static JSClass sClass;
+  static JSPropertySpec sProperties[];
+  static JSFunctionSpec sFunctions[];
+
+public:
+  static JSObject*
+  InitClass(JSContext* aCx, JSObject* aObj)
+  {
+    return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties,
+                        sFunctions, NULL, NULL);
+  }
+
+  static JSObject*
+  Create(JSContext* aCx, nsIDOMBlob* aBlob)
+  {
+    JS_ASSERT(SameCOMIdentity(static_cast<nsISupports*>(aBlob), aBlob));
+
+    JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+    if (obj) {
+      if (!JS_SetPrivate(aCx, obj, aBlob)) {
+        return NULL;
+      }
+      NS_ADDREF(aBlob);
+    }
+    return obj;
+  }
+
+  static nsIDOMBlob*
+  GetPrivate(JSContext* aCx, JSObject* aObj);
+
+private:
+  static nsIDOMBlob*
+  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  {
+    JSClass* classPtr = NULL;
+
+    if (aObj) {
+      nsIDOMBlob* blob = GetPrivate(aCx, aObj);
+      if (blob) {
+        return blob;
+      }
+
+      classPtr = JS_GET_CLASS(aCx, aObj);
+    }
+
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+                         JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
+                         classPtr ? classPtr->name : "Object");
+    return NULL;
+  }
+
+  static JSBool
+  Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
+                         sClass.name);
+    return false;
+  }
+
+  static void
+  Finalize(JSContext* aCx, JSObject* aObj)
+  {
+    JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
+
+    nsIDOMBlob* blob = GetPrivate(aCx, aObj);
+    NS_IF_RELEASE(blob);
+  }
+
+  static JSBool
+  GetSize(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "size");
+    if (!blob) {
+      return false;
+    }
+
+    PRUint64 size;
+    if (NS_FAILED(blob->GetSize(&size))) {
+      ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR);
+    }
+
+    if (!JS_NewNumberValue(aCx, jsdouble(size), aVp)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static JSBool
+  GetType(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "type");
+    if (!blob) {
+      return false;
+    }
+
+    nsString type;
+    if (NS_FAILED(blob->GetType(type))) {
+      ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR);
+    }
+
+    JSString* jsType = JS_NewUCStringCopyN(aCx, type.get(), type.Length());
+    if (!jsType) {
+      return false;
+    }
+
+    *aVp = STRING_TO_JSVAL(jsType);
+
+    return true;
+  }
+
+  static JSBool
+  MozSlice(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "mozSlice");
+    if (!blob) {
+      return false;
+    }
+
+    jsdouble start = 0, end = 0;
+    JSString* jsContentType = JS_GetEmptyString(JS_GetRuntime(aCx));
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "/IIS", &start,
+                             &end, &jsContentType)) {
+      return false;
+    }
+
+    nsDependentJSString contentType;
+    if (!contentType.init(aCx, jsContentType)) {
+      return false;
+    }
+
+    PRUint8 optionalArgc = aArgc;
+    nsCOMPtr<nsIDOMBlob> rtnBlob;
+    if (NS_FAILED(blob->MozSlice(xpc_qsDoubleToUint64(start),
+                                 xpc_qsDoubleToUint64(end),
+                                 contentType, optionalArgc,
+                                 getter_AddRefs(rtnBlob)))) {
+      ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR);
+      return false;
+    }
+
+    JSObject* rtnObj = file::CreateBlob(aCx, rtnBlob);
+    if (!rtnObj) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(rtnObj));
+    return true;
+  }
+};
+
+JSClass Blob::sClass = {
+  "Blob",
+  JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSPropertySpec Blob::sProperties[] = {
+  { "size", 0, PROPERTY_FLAGS, GetSize, js_GetterOnlyPropertyStub },
+  { "type", 0, PROPERTY_FLAGS, GetType, js_GetterOnlyPropertyStub },
+  { 0, 0, 0, NULL, NULL }
+};
+
+JSFunctionSpec Blob::sFunctions[] = {
+  JS_FN("mozSlice", MozSlice, 1, JSPROP_ENUMERATE),
+  JS_FS_END
+};
+
+class File : public Blob
+{
+  // File should never be instantiated.
+  File();
+  ~File();
+
+  static JSClass sClass;
+  static JSPropertySpec sProperties[];
+
+public:
+  static JSObject*
+  InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
+  {
+    return JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct, 0,
+                        sProperties, NULL, NULL, NULL);
+  }
+
+  static JSObject*
+  Create(JSContext* aCx, nsIDOMFile* aFile)
+  {
+    JS_ASSERT(SameCOMIdentity(static_cast<nsISupports*>(aFile), aFile));
+
+    JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+    if (obj) {
+      if (!JS_SetPrivate(aCx, obj, aFile)) {
+        return NULL;
+      }
+      NS_ADDREF(aFile);
+    }
+    return obj;
+  }
+
+  static nsIDOMFile*
+  GetPrivate(JSContext* aCx, JSObject* aObj)
+  {
+    if (aObj) {
+      JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+      if (classPtr == &sClass) {
+        nsISupports* priv = static_cast<nsISupports*>(JS_GetPrivate(aCx, aObj));
+        nsCOMPtr<nsIDOMFile> file = do_QueryInterface(priv);
+        JS_ASSERT_IF(priv, file);
+        return file;
+      }
+    }
+    return NULL;
+  }
+
+  static JSClass*
+  Class()
+  {
+    return &sClass;
+  }
+
+private:
+  static nsIDOMFile*
+  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  {
+    JSClass* classPtr = NULL;
+
+    if (aObj) {
+      nsIDOMFile* file = GetPrivate(aCx, aObj);
+      if (file) {
+        return file;
+      }
+      classPtr = JS_GET_CLASS(aCx, aObj);
+    }
+
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+                         JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
+                         classPtr ? classPtr->name : "Object");
+    return NULL;
+  }
+
+  static JSBool
+  Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
+                         sClass.name);
+    return false;
+  }
+
+  static void
+  Finalize(JSContext* aCx, JSObject* aObj)
+  {
+    JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
+
+    nsIDOMFile* file = GetPrivate(aCx, aObj);
+    NS_IF_RELEASE(file);
+  }
+
+  static JSBool
+  GetMozFullPath(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "mozFullPath");
+    if (!file) {
+      return false;
+    }
+
+    nsString fullPath;
+
+    if (GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal() &&
+        NS_FAILED(file->GetMozFullPathInternal(fullPath))) {
+      ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR);
+      return false;
+    }
+
+    JSString* jsFullPath = JS_NewUCStringCopyN(aCx, fullPath.get(),
+                                               fullPath.Length());
+    if (!jsFullPath) {
+      return false;
+    }
+
+    *aVp = STRING_TO_JSVAL(jsFullPath);
+    return true;
+  }
+
+  static JSBool
+  GetName(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "name");
+    if (!file) {
+      return false;
+    }
+
+    nsString name;
+    if (NS_FAILED(file->GetName(name))) {
+      name.Truncate();
+    }
+
+    JSString* jsName = JS_NewUCStringCopyN(aCx, name.get(), name.Length());
+    if (!jsName) {
+      return false;
+    }
+
+    *aVp = STRING_TO_JSVAL(jsName);
+    return true;
+  }
+};
+
+JSClass File::sClass = {
+  "File",
+  JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSPropertySpec File::sProperties[] = {
+  { "name", 0, PROPERTY_FLAGS, GetName, js_GetterOnlyPropertyStub },
+  { "mozFullPath", 0, PROPERTY_FLAGS, GetMozFullPath,
+    js_GetterOnlyPropertyStub },
+  { 0, 0, 0, NULL, NULL }
+};
+
+nsIDOMBlob*
+Blob::GetPrivate(JSContext* aCx, JSObject* aObj)
+{
+  if (aObj) {
+    JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+    if (classPtr == &sClass || classPtr == File::Class()) {
+      nsISupports* priv = static_cast<nsISupports*>(JS_GetPrivate(aCx, aObj));
+      nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(priv);
+      JS_ASSERT_IF(priv, blob);
+      return blob;
+    }
+  }
+  return NULL;
+}
+
+} // anonymous namespace
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace file {
+
+JSObject*
+CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob)
+{
+  return Blob::Create(aCx, aBlob);
+}
+
+bool
+InitClasses(JSContext* aCx, JSObject* aGlobal)
+{
+  JSObject* blobProto = Blob::InitClass(aCx, aGlobal);
+  return blobProto && File::InitClass(aCx, aGlobal, blobProto);
+}
+
+nsIDOMBlob*
+GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj)
+{
+  return Blob::GetPrivate(aCx, aObj);
+}
+
+JSObject*
+CreateFile(JSContext* aCx, nsIDOMFile* aFile)
+{
+  return File::Create(aCx, aFile);
+}
+
+nsIDOMFile*
+GetDOMFileFromJSObject(JSContext* aCx, JSObject* aObj)
+{
+  return File::GetPrivate(aCx, aObj);
+}
+
+} // namespace file
+
+END_WORKERS_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/workers/File.h
@@ -0,0 +1,73 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 mozilla_dom_workers_file_h__
+#define mozilla_dom_workers_file_h__
+
+#include "Workers.h"
+
+#include "jspubtd.h"
+
+class nsIDOMFile;
+class nsIDOMBlob;
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace file {
+
+bool
+InitClasses(JSContext* aCx, JSObject* aGlobal);
+
+JSObject*
+CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob);
+
+nsIDOMBlob*
+GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj);
+
+JSObject*
+CreateFile(JSContext* aCx, nsIDOMFile* aFile);
+
+nsIDOMFile*
+GetDOMFileFromJSObject(JSContext* aCx, JSObject* aObj);
+
+} // namespace file
+
+END_WORKERS_NAMESPACE
+
+#endif /* mozilla_dom_workers_file_h__ */
new file mode 100644
--- /dev/null
+++ b/dom/workers/FileReaderSync.cpp
@@ -0,0 +1,372 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 "FileReaderSync.h"
+
+#include "nsIDOMFile.h"
+
+#include "jsapi.h"
+#include "jsatom.h"
+#include "jscntxt.h"
+#include "jstypedarray.h"
+#include "nsJSUtils.h"
+#include "xpcprivate.h"
+
+#include "Exceptions.h"
+#include "File.h"
+#include "FileReaderSyncPrivate.h"
+#include "WorkerInlines.h"
+
+#define FUNCTION_FLAGS \
+  JSPROP_ENUMERATE
+
+USING_WORKERS_NAMESPACE
+
+using mozilla::dom::workers::exceptions::ThrowFileExceptionForCode;
+using js::ArrayBuffer;
+
+namespace {
+
+inline bool
+EnsureSucceededOrThrow(JSContext* aCx, nsresult rv)
+{
+  if (NS_SUCCEEDED(rv)) {
+    return true;
+  }
+
+  intN code = rv == NS_ERROR_FILE_NOT_FOUND ?
+              FILE_NOT_FOUND_ERR :
+              FILE_NOT_READABLE_ERR;
+  ThrowFileExceptionForCode(aCx, code);
+  return false;
+}
+
+inline nsIDOMBlob*
+GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj) {
+  JSClass* classPtr = NULL;
+
+  if (aObj) {
+    nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aCx, aObj);
+    if (blob) {
+      return blob;
+    }
+
+    classPtr = JS_GET_CLASS(aCx, aObj);
+  }
+
+  JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
+                       classPtr ? classPtr->name : "Object", "not a Blob.");
+  return NULL;
+}
+
+class FileReaderSync
+{
+  // FileReaderSync should not be instantiated.
+  FileReaderSync();
+  ~FileReaderSync();
+
+  static JSClass sClass;
+  static JSFunctionSpec sFunctions[];
+
+public:
+  static JSObject*
+  InitClass(JSContext* aCx, JSObject* aObj)
+  {
+    return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0,
+                        NULL, sFunctions, NULL, NULL);
+  }
+
+  static FileReaderSyncPrivate*
+  GetPrivate(JSContext* aCx, JSObject* aObj)
+  {
+    if (aObj) {
+      JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+      if (classPtr == &sClass) {
+        FileReaderSyncPrivate* fileReader =
+          GetJSPrivateSafeish<FileReaderSyncPrivate>(aCx, aObj);
+        return fileReader;
+      }
+    }
+    return NULL;
+  }
+
+private:
+  static FileReaderSyncPrivate*
+  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  {
+    JSClass* classPtr = NULL;
+
+    if (aObj) {
+      FileReaderSyncPrivate* fileReader = GetPrivate(aCx, aObj);
+      if (fileReader) {
+        return fileReader;
+      }
+
+      classPtr = JS_GET_CLASS(aCx, aObj);
+    }
+
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+                         JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
+                         classPtr ? classPtr->name : "Object");
+    return NULL;
+  }
+
+  static JSBool
+  Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+    if (!obj) {
+      return false;
+    }
+
+    FileReaderSyncPrivate* fileReader = new FileReaderSyncPrivate();
+    NS_ADDREF(fileReader);
+
+    if (!SetJSPrivateSafeish(aCx, obj, fileReader)) {
+      NS_RELEASE(fileReader);
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
+    return true;
+  }
+
+  static void
+  Finalize(JSContext* aCx, JSObject* aObj)
+  {
+    JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
+    FileReaderSyncPrivate* fileReader =
+      GetJSPrivateSafeish<FileReaderSyncPrivate>(aCx, aObj);
+    NS_IF_RELEASE(fileReader);
+  }
+
+  static JSBool
+  ReadAsArrayBuffer(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    FileReaderSyncPrivate* fileReader =
+      GetInstancePrivate(aCx, obj, "readAsArrayBuffer");
+    if (!fileReader) {
+      return false;
+    }
+
+    JSObject* jsBlob;
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
+      return false;
+    }
+
+    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
+    if (!blob) {
+      return false;
+    }
+
+    PRUint64 blobSize;
+    nsresult rv = blob->GetSize(&blobSize);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JSObject* jsArrayBuffer = js_CreateArrayBuffer(aCx, blobSize);
+    if (!jsArrayBuffer) {
+      return false;
+    }
+
+    JSUint32 bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer);
+    uint8* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer);
+
+    rv = fileReader->ReadAsArrayBuffer(blob, bufferLength, arrayBuffer);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(jsArrayBuffer));
+    return true;
+  }
+
+  static JSBool
+  ReadAsDataURL(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    FileReaderSyncPrivate* fileReader =
+      GetInstancePrivate(aCx, obj, "readAsDataURL");
+    if (!fileReader) {
+      return false;
+    }
+
+    JSObject* jsBlob;
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
+      return false;
+    }
+
+    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
+    if (!blob) {
+      return false;
+    }
+
+    nsString blobText;
+    nsresult rv = fileReader->ReadAsDataURL(blob, blobText);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
+                                               blobText.Length());
+    if (!jsBlobText) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
+    return true;
+  }
+
+  static JSBool
+  ReadAsBinaryString(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    FileReaderSyncPrivate* fileReader =
+      GetInstancePrivate(aCx, obj, "readAsBinaryString");
+    if (!fileReader) {
+      return false;
+    }
+
+    JSObject* jsBlob;
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
+      return false;
+    }
+
+    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
+    if (!blob) {
+      return false;
+    }
+
+    nsString blobText;
+    nsresult rv = fileReader->ReadAsBinaryString(blob, blobText);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
+                                               blobText.Length());
+    if (!jsBlobText) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
+    return true;
+  }
+
+  static JSBool
+  ReadAsText(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    FileReaderSyncPrivate* fileReader =
+      GetInstancePrivate(aCx, obj, "readAsText");
+    if (!fileReader) {
+      return false;
+    }
+
+    JSObject* jsBlob;
+    JSString* jsEncoding = JS_GetEmptyString(JS_GetRuntime(aCx));
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o/S", &jsBlob,
+                             &jsEncoding)) {
+      return false;
+    }
+
+    nsDependentJSString encoding;
+    if (!encoding.init(aCx, jsEncoding)) {
+      return false;
+    }
+
+    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
+    if (!blob) {
+      return false;
+    }
+
+    nsString blobText;
+    nsresult rv = fileReader->ReadAsText(blob, encoding, blobText);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
+                                               blobText.Length());
+    if (!jsBlobText) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
+    return true;
+  }
+};
+
+JSClass FileReaderSync::sClass = {
+  "FileReaderSync",
+  JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSFunctionSpec FileReaderSync::sFunctions[] = {
+  JS_FN("readAsArrayBuffer", ReadAsArrayBuffer, 1, FUNCTION_FLAGS),
+  JS_FN("readAsBinaryString", ReadAsBinaryString, 1, FUNCTION_FLAGS),
+  JS_FN("readAsText", ReadAsText, 1, FUNCTION_FLAGS),
+  JS_FN("readAsDataURL", ReadAsDataURL, 1, FUNCTION_FLAGS),
+  JS_FS_END
+};
+
+} // anonymous namespace
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace filereadersync {
+
+bool
+InitClass(JSContext* aCx, JSObject* aGlobal)
+{
+  return !!FileReaderSync::InitClass(aCx, aGlobal);
+}
+
+} // namespace filereadersync
+
+END_WORKERS_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/workers/FileReaderSync.h
@@ -0,0 +1,58 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 Web Workers.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 mozilla_dom_workers_filereadersync_h__
+#define mozilla_dom_workers_filereadersync_h__
+
+#include "Workers.h"
+
+#include "jspubtd.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace filereadersync {
+
+bool
+InitClass(JSContext* aCx, JSObject* aGlobal);
+
+} // namespace filereadersync
+
+END_WORKERS_NAMESPACE
+
+#endif // mozilla_dom_workers_filereadersync_h__
new file mode 100644
--- /dev/null
+++ b/dom/workers/FileReaderSyncPrivate.cpp
@@ -0,0 +1,318 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 "FileReaderSyncPrivate.h"
+
+#include "nsCExternalHandlerService.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMError.h"
+#include "nsIDOMFile.h"
+#include "nsICharsetAlias.h"
+#include "nsICharsetDetector.h"
+#include "nsIConverterInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIPlatformCharset.h"
+#include "nsISeekableStream.h"
+#include "nsISupportsImpl.h"
+#include "nsISupportsImpl.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "RuntimeService.h"
+
+#include "mozilla/Base64.h"
+
+USING_WORKERS_NAMESPACE
+
+NS_IMPL_ISUPPORTS1(FileReaderSyncPrivate, nsICharsetDetectionObserver)
+
+FileReaderSyncPrivate::FileReaderSyncPrivate()
+{
+  MOZ_COUNT_CTOR(mozilla::dom::workers::FileReaderSyncPrivate);
+}
+
+FileReaderSyncPrivate::~FileReaderSyncPrivate()
+{
+  MOZ_COUNT_DTOR(mozilla::dom::workers::FileReaderSyncPrivate);
+}
+
+nsresult
+FileReaderSyncPrivate::ReadAsArrayBuffer(nsIDOMBlob* aBlob, PRUint32 aLength,
+                                         uint8* aBuffer)
+{
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 numRead;
+  rv = stream->Read((char*)aBuffer, aLength, &numRead);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ASSERTION(numRead == aLength, "failed to read data");
+
+  return NS_OK;
+}
+
+nsresult
+FileReaderSyncPrivate::ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult)
+{
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 numRead;
+  do {
+    char readBuf[4096];
+    rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 oldLength = aResult.Length();
+    AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
+    if (aResult.Length() - oldLength != numRead) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  } while (numRead > 0);
+
+  return NS_OK;
+}
+
+nsresult
+FileReaderSyncPrivate::ReadAsText(nsIDOMBlob* aBlob,
+                                  const nsAString& aEncoding, nsAString& aResult)
+{
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString charsetGuess;
+  if (aEncoding.IsEmpty()) {
+    rv = GuessCharset(stream, charsetGuess);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
+    NS_ENSURE_TRUE(seekable, NS_ERROR_FAILURE);
+
+    // Seek to 0 because guessing the charset advances the stream.
+    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    CopyUTF16toUTF8(aEncoding, charsetGuess);
+  }
+
+  nsCOMPtr<nsICharsetAlias> alias =
+    do_GetService(NS_CHARSETALIAS_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString charset;
+  rv = alias->GetPreferred(charsetGuess, charset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return ConvertStream(stream, charset.get(), aResult);
+}
+
+nsresult
+FileReaderSyncPrivate::ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult)
+{
+  nsAutoString scratchResult;
+  scratchResult.AssignLiteral("data:");
+
+  nsString contentType;
+  aBlob->GetType(contentType);
+
+  if (contentType.IsEmpty()) {
+    scratchResult.AppendLiteral("application/octet-stream");
+  } else {
+    scratchResult.Append(contentType);
+  }
+  scratchResult.AppendLiteral(";base64,");
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint64 size;
+  rv = aBlob->GetSize(&size);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIInputStream> bufferedStream;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString encodedData;
+  rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  scratchResult.Append(encodedData);
+
+  aResult = scratchResult;
+  return NS_OK;
+}
+
+nsresult
+FileReaderSyncPrivate::ConvertStream(nsIInputStream *aStream,
+                                     const char *aCharset,
+                                     nsAString &aResult)
+{
+  nsCOMPtr<nsIConverterInputStream> converterStream =
+    do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
+  NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
+
+  nsresult rv = converterStream->Init(aStream, aCharset, 8192,
+                  nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIUnicharInputStream> unicharStream =
+    do_QueryInterface(converterStream);
+  NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
+
+  PRUint32 numChars;
+  nsString result;
+  while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
+         numChars > 0) {
+    PRUint32 oldLength = aResult.Length();
+    aResult.Append(result);
+    if (aResult.Length() - oldLength != result.Length()) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  return rv;
+}
+
+nsresult
+FileReaderSyncPrivate::GuessCharset(nsIInputStream *aStream,
+                                    nsACString &aCharset)
+{
+  // First try the universal charset detector
+  nsCOMPtr<nsICharsetDetector> detector
+    = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
+                        "universal_charset_detector");
+  if (!detector) {
+    RuntimeService* runtime = RuntimeService::GetService();
+    NS_ASSERTION(runtime, "This should never be null!");
+
+    // No universal charset detector, try the default charset detector
+    const nsACString& detectorName = runtime->GetDetectorName();
+
+    if (!detectorName.IsEmpty()) {
+      nsCAutoString detectorContractID;
+      detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
+      detectorContractID += detectorName;
+      detector = do_CreateInstance(detectorContractID.get());
+    }
+  }
+
+  nsresult rv;
+  if (detector) {
+    detector->Init(this);
+
+    PRBool done;
+    PRUint32 numRead;
+    do {
+      char readBuf[4096];
+      rv = aStream->Read(readBuf, sizeof(readBuf), &numRead);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (numRead <= 0) {
+        break;
+      }
+      rv = detector->DoIt(readBuf, numRead, &done);
+      NS_ENSURE_SUCCESS(rv, rv);
+    } while (!done);
+
+    rv = detector->Done();
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    // no charset detector available, check the BOM
+    unsigned char sniffBuf[4];
+    PRUint32 numRead;
+    rv = aStream->Read(reinterpret_cast<char*>(sniffBuf),
+                       sizeof(sniffBuf), &numRead);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (numRead >= 4 &&
+        sniffBuf[0] == 0x00 &&
+        sniffBuf[1] == 0x00 &&
+        sniffBuf[2] == 0xfe &&
+        sniffBuf[3] == 0xff) {
+      mCharset = "UTF-32BE";
+    } else if (numRead >= 4 &&
+               sniffBuf[0] == 0xff &&
+               sniffBuf[1] == 0xfe &&
+               sniffBuf[2] == 0x00 &&
+               sniffBuf[3] == 0x00) {
+      mCharset = "UTF-32LE";
+    } else if (numRead >= 2 &&
+               sniffBuf[0] == 0xfe &&
+               sniffBuf[1] == 0xff) {
+      mCharset = "UTF-16BE";
+    } else if (numRead >= 2 &&
+               sniffBuf[0] == 0xff &&
+               sniffBuf[1] == 0xfe) {
+      mCharset = "UTF-16LE";
+    } else if (numRead >= 3 &&
+               sniffBuf[0] == 0xef &&
+               sniffBuf[1] == 0xbb &&
+               sniffBuf[2] == 0xbf) {
+      mCharset = "UTF-8";
+    }
+  }
+
+  if (mCharset.IsEmpty()) {
+    RuntimeService* runtime = RuntimeService::GetService();
+    mCharset = runtime->GetSystemCharset();
+  }
+
+  if (mCharset.IsEmpty()) {
+    // no sniffed or default charset, try UTF-8
+    mCharset.AssignLiteral("UTF-8");
+  }
+
+  aCharset = mCharset;
+  mCharset.Truncate();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileReaderSyncPrivate::Notify(const char* aCharset, nsDetectionConfident aConf)
+{
+  mCharset.Assign(aCharset);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/FileReaderSyncPrivate.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 nsDOMFileReaderSyncPrivate_h
+#define nsDOMFileReaderSyncPrivate_h
+
+#include "Workers.h"
+
+#include "nsICharsetDetectionObserver.h"
+#include "nsStringGlue.h"
+
+class nsIInputStream;
+class nsIDOMBlob;
+
+BEGIN_WORKERS_NAMESPACE
+
+class FileReaderSyncPrivate : public PrivatizableBase,
+                              public nsICharsetDetectionObserver
+{
+  nsCString mCharset;
+  nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset,
+                         nsAString &aResult);
+  nsresult GuessCharset(nsIInputStream *aStream, nsACString &aCharset);
+
+public:
+  NS_DECL_ISUPPORTS
+
+  FileReaderSyncPrivate();
+  ~FileReaderSyncPrivate();
+
+  nsresult ReadAsArrayBuffer(nsIDOMBlob* aBlob, PRUint32 aLength,
+                             uint8* aBuffer);
+  nsresult ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult);
+  nsresult ReadAsText(nsIDOMBlob* aBlob, const nsAString& aEncoding,
+                      nsAString& aResult);
+  nsresult ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult);
+
+  // From nsICharsetDetectionObserver
+  NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf);
+};
+
+END_WORKERS_NAMESPACE
+
+#endif
--- a/dom/workers/Makefile.in
+++ b/dom/workers/Makefile.in
@@ -52,16 +52,19 @@ EXPORTS_NAMESPACES = mozilla/dom/workers
 
 EXPORTS_mozilla/dom/workers = Workers.h
 
 CPPSRCS = \
   ChromeWorkerScope.cpp \
   Events.cpp \
   EventTarget.cpp \
   Exceptions.cpp \
+  File.cpp \
+  FileReaderSync.cpp \
+  FileReaderSyncPrivate.cpp \
   ListenerManager.cpp \
   Location.cpp \
   Navigator.cpp \
   Principal.cpp \
   RuntimeService.cpp \
   ScriptLoader.cpp \
   Worker.cpp \
   WorkerPrivate.cpp \
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -37,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "RuntimeService.h"
 
 #include "nsIDOMChromeWindow.h"
 #include "nsIDocument.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIObserverService.h"
+#include "nsIPlatformCharset.h"
 #include "nsIPrincipal.h"
 #include "nsIJSContextStack.h"
 #include "nsIMemoryReporter.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISupportsPriority.h"
 #include "nsITimer.h"
 #include "nsPIDOMWindow.h"
 
@@ -72,18 +74,23 @@ USING_WORKERS_NAMESPACE
 using mozilla::MutexAutoLock;
 using mozilla::MutexAutoUnlock;
 using mozilla::Preferences;
 using namespace mozilla::xpconnect::memory;
 
 // The size of the worker runtime heaps in bytes.
 #define WORKER_RUNTIME_HEAPSIZE 32 * 1024 * 1024
 
-// The size of the C stack in bytes.
-#define WORKER_CONTEXT_NATIVE_STACK_LIMIT sizeof(size_t) * 32 * 1024
+// The C stack size. We use the same stack size on all platforms for
+// consistency.
+#define WORKER_STACK_SIZE 256 * sizeof(size_t) * 1024
+
+// The stack limit the JS engine will check. Half the size of the
+// actual C stack, to be safe.
+#define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
 
 // The maximum number of threads to use for workers, overridable via pref.
 #define MAX_WORKERS_PER_DOMAIN 10
 
 // The default number of seconds that close handlers will be allowed to run.
 #define MAX_SCRIPT_RUN_TIME_SEC 10
 
 // The number of seconds that idle threads can hang around before being killed.
@@ -238,25 +245,16 @@ CreateJSContextForWorker(WorkerPrivate* 
   NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
 
   JSRuntime* runtime = JS_NewRuntime(WORKER_RUNTIME_HEAPSIZE);
   if (!runtime) {
     NS_WARNING("Could not create new runtime!");
     return nsnull;
   }
 
-  // ChromeWorker has extra clone callbacks for passing threadsafe XPCOM
-  // components.
-  JSStructuredCloneCallbacks* callbacks =
-    aWorkerPrivate->IsChromeWorker() ?
-    ChromeWorkerStructuredCloneCallbacks() :
-    WorkerStructuredCloneCallbacks();
-
-  JS_SetStructuredCloneCallbacks(runtime, callbacks);
-
   JSContext* workerCx = JS_NewContext(runtime, 0);
   if (!workerCx) {
     JS_DestroyRuntime(runtime);
     NS_WARNING("Could not create new context!");
     return nsnull;
   }
 
   JS_SetContextPrivate(workerCx, aWorkerPrivate);
@@ -793,17 +791,18 @@ RuntimeService::ScheduleWorker(JSContext
     if (!mIdleThreadArray.IsEmpty()) {
       PRUint32 index = mIdleThreadArray.Length() - 1;
       mIdleThreadArray[index].mThread.swap(thread);
       mIdleThreadArray.RemoveElementAt(index);
     }
   }
 
   if (!thread) {
-    if (NS_FAILED(NS_NewThread(getter_AddRefs(thread), nsnull))) {
+    if (NS_FAILED(NS_NewThread(getter_AddRefs(thread), nsnull,
+                               WORKER_STACK_SIZE))) {
       UnregisterWorker(aCx, aWorkerPrivate);
       JS_ReportError(aCx, "Could not create new thread!");
       return false;
     }
 
     nsCOMPtr<nsISupportsPriority> priority = do_QueryInterface(thread);
     if (!priority ||
         NS_FAILED(priority->SetPriority(nsISupportsPriority::PRIORITY_LOW))) {
@@ -925,16 +924,25 @@ RuntimeService::Init()
                                             MAX_SCRIPT_RUN_TIME_SEC))) {
       NS_WARNING("Failed to register timeout cache?!");
   }
 
   PRInt32 maxPerDomain = Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN,
                                              MAX_WORKERS_PER_DOMAIN);
   gMaxWorkersPerDomain = NS_MAX(0, maxPerDomain);
 
+  mDetectorName = Preferences::GetLocalizedCString("intl.charset.detector");
+
+  nsCOMPtr<nsIPlatformCharset> platformCharset =
+    do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
+  if (NS_SUCCEEDED(rv)) {
+    rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
+                                     mSystemCharset);
+  }
+
   return NS_OK;
 }
 
 // This spins the event loop until all workers are finished and their threads
 // have been joined.
 void
 RuntimeService::Cleanup()
 {
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -94,16 +95,19 @@ class RuntimeService : public nsIObserve
   nsTArray<IdleThreadInfo> mIdleThreadArray;
 
   // *Not* protected by mMutex.
   nsClassHashtable<nsVoidPtrHashKey, nsTArray<WorkerPrivate*> > mWindowMap;
 
   // Only used on the main thread.
   nsCOMPtr<nsITimer> mIdleThreadTimer;
 
+  nsCString mDetectorName;
+  nsCString mSystemCharset;
+
   static PRUint32 sDefaultJSContextOptions;
   static PRInt32 sCloseHandlerTimeoutSeconds;
 
 #ifdef JS_GC_ZEAL
   static PRUint8 sDefaultGCZeal;
 #endif
 
 public:
@@ -143,16 +147,28 @@ public:
   CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
 
   void
   SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
 
   void
   ResumeWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
 
+  const nsACString&
+  GetDetectorName() const
+  {
+    return mDetectorName;
+  }
+
+  const nsACString&
+  GetSystemCharset() const
+  {
+    return mSystemCharset;
+  }
+
   const NavigatorStrings&
   GetNavigatorStrings() const
   {
     return mNavigatorStrings;
   }
 
   void
   NoteIdleThread(nsIThread* aThread);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -35,16 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "WorkerPrivate.h"
 
 #include "nsIClassInfo.h"
 #include "nsIConsoleService.h"
+#include "nsIDOMFile.h"
 #include "nsIDocument.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIJSContextStack.h"
 #include "nsIScriptError.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsPIDOMWindow.h"
 #include "nsITextToSubURI.h"
@@ -63,16 +65,17 @@
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "xpcpublic.h"
 
 #include "Events.h"
 #include "Exceptions.h"
+#include "File.h"
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "WorkerFeature.h"
 #include "WorkerScope.h"
 
 #include "WorkerInlines.h"
 
@@ -138,24 +141,107 @@ SwapToISupportsArray(SmartPtr<T>& aSrc,
 }
 
 struct WorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32 aTag,
        uint32 aData, void* aClosure)
   {
+    // See if object is a nsIDOMFile pointer.
+    if (aTag == DOMWORKER_SCTAG_FILE) {
+      JS_ASSERT(!aData);
+
+      nsIDOMFile* file;
+      if (JS_ReadBytes(aReader, &file, sizeof(file))) {
+        JS_ASSERT(file);
+
+#ifdef DEBUG
+        {
+          // File should not be mutable.
+          nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
+          PRBool isMutable;
+          NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
+                       !isMutable,
+                       "Only immutable file should be passed to worker");
+        }
+#endif
+
+        // nsIDOMFiles should be threadsafe, thus we will use the same instance
+        // in the worker.
+        JSObject* jsFile = file::CreateFile(aCx, file);
+        return jsFile;
+      }
+    }
+    // See if object is a nsIDOMBlob pointer.
+    else if (aTag == DOMWORKER_SCTAG_BLOB) {
+      JS_ASSERT(!aData);
+
+      nsIDOMBlob* blob;
+      if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
+        JS_ASSERT(blob);
+
+#ifdef DEBUG
+        {
+          // Blob should not be mutable.
+          nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+          PRBool isMutable;
+          NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
+                       !isMutable,
+                       "Only immutable blob should be passed to worker");
+        }
+#endif
+
+        // nsIDOMBlob should be threadsafe, thus we will use the same instance
+        // in the worker.
+        JSObject* jsBlob = file::CreateBlob(aCx, blob);
+        return jsBlob;
+      }
+    }
+
     Error(aCx, 0);
     return nsnull;
   }
 
   static JSBool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
         void* aClosure)
   {
+    NS_ASSERTION(aClosure, "Null pointer!");
+
+    // We'll stash any nsISupports pointers that need to be AddRef'd here.
+    nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
+      static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
+
+    // See if this is a File object.
+    {
+      nsIDOMFile* file = file::GetDOMFileFromJSObject(aCx, aObj);
+      if (file) {
+        if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
+            JS_WriteBytes(aWriter, &file, sizeof(file))) {
+          clonedObjects->AppendElement(file);
+          return true;
+        }
+      }
+    }
+
+    // See if this is a Blob object.
+    {
+      nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aCx, aObj);
+      if (blob) {
+        nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+        if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(PR_FALSE)) &&
+            JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
+            JS_WriteBytes(aWriter, &blob, sizeof(blob))) {
+          clonedObjects->AppendElement(blob);
+          return true;
+        }
+      }
+    }
+
     Error(aCx, 0);
     return false;
   }
 
   static void
   Error(JSContext* aCx, uint32 /* aErrorId */)
   {
     ThrowDOMExceptionForCode(aCx, DATA_CLONE_ERR);
@@ -171,32 +257,144 @@ JSStructuredCloneCallbacks gWorkerStruct
 struct MainThreadWorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32 aTag,
        uint32 aData, void* aClosure)
   {
     AssertIsOnMainThread();
 
+    // See if object is a nsIDOMFile pointer.
+    if (aTag == DOMWORKER_SCTAG_FILE) {
+      JS_ASSERT(!aData);
+
+      nsIDOMFile* file;
+      if (JS_ReadBytes(aReader, &file, sizeof(file))) {
+        JS_ASSERT(file);
+
+#ifdef DEBUG
+        {
+          // File should not be mutable.
+          nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
+          PRBool isMutable;
+          NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
+                       !isMutable,
+                       "Only immutable file should be passed to worker");
+        }
+#endif
+
+        // nsIDOMFiles should be threadsafe, thus we will use the same instance
+        // on the main thread.
+        jsval wrappedFile;
+        nsresult rv =
+          nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
+                                     &NS_GET_IID(nsIDOMFile), &wrappedFile);
+        if (NS_FAILED(rv)) {
+          Error(aCx, DATA_CLONE_ERR);
+          return nsnull;
+        }
+
+        return JSVAL_TO_OBJECT(wrappedFile);
+      }
+    }
+    // See if object is a nsIDOMBlob pointer.
+    else if (aTag == DOMWORKER_SCTAG_BLOB) {
+      JS_ASSERT(!aData);
+
+      nsIDOMBlob* blob;
+      if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
+        JS_ASSERT(blob);
+
+#ifdef DEBUG
+        {
+          // Blob should not be mutable.
+          nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+          PRBool isMutable;
+          NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
+                       !isMutable,
+                       "Only immutable blob should be passed to worker");
+        }
+#endif
+
+        // nsIDOMBlobs should be threadsafe, thus we will use the same instance
+        // on the main thread.
+        jsval wrappedBlob;
+        nsresult rv =
+          nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
+                                     &NS_GET_IID(nsIDOMBlob), &wrappedBlob);
+        if (NS_FAILED(rv)) {
+          Error(aCx, DATA_CLONE_ERR);
+          return nsnull;
+        }
+
+        return JSVAL_TO_OBJECT(wrappedBlob);
+      }
+    }
+
     JSObject* clone =
       WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, aClosure);
     if (clone) {
       return clone;
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nsnull);
   }
 
   static JSBool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
         void* aClosure)
   {
     AssertIsOnMainThread();
 
+    NS_ASSERTION(aClosure, "Null pointer!");
+
+    // We'll stash any nsISupports pointers that need to be AddRef'd here.
+    nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
+      static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
+
+    // See if this is a wrapped native.
+    nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+    nsContentUtils::XPConnect()->
+      GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
+
+    if (wrappedNative) {
+      // Get the raw nsISupports out of it.
+      nsISupports* wrappedObject = wrappedNative->Native();
+      NS_ASSERTION(wrappedObject, "Null pointer?!");
+
+      // See if the wrapped native is a nsIDOMFile.
+      nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject);
+      if (file) {
+        nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
+        if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(PR_FALSE))) {
+          nsIDOMFile* filePtr = file;
+          if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
+              JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) {
+            clonedObjects->AppendElement(file);
+            return true;
+          }
+        }
+      }
+
+      // See if the wrapped native is a nsIDOMBlob.
+      nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject);
+      if (blob) {
+        nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+        if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(PR_FALSE))) {
+          nsIDOMBlob* blobPtr = blob;
+          if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
+              JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) {
+            clonedObjects->AppendElement(blob);
+            return true;
+          }
+        }
+      }
+    }
+
     JSBool ok =
       WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
     if (ok) {
       return ok;
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull);
@@ -514,25 +712,31 @@ public:
     aWorkerPrivate->CloseHandlerFinished();
   }
 };
 
 class MessageEventRunnable : public WorkerRunnable
 {
   uint64* mData;
   size_t mDataByteCount;
+  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
-                       JSAutoStructuredCloneBuffer& aData)
+                       JSAutoStructuredCloneBuffer& aData,
+                       nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects)
   : WorkerRunnable(aWorkerPrivate, aTarget, aTarget == WorkerThread ?
                                                        ModifyBusyCount :
                                                        UnchangedBusyCount)
   {
     aData.steal(&mData, &mDataByteCount);
+
+    if (!mClonedObjects.SwapElements(aClonedObjects)) {
+      NS_ERROR("This should never fail!");
+    }
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     JSAutoStructuredCloneBuffer buffer;
     buffer.adopt(mData, mDataByteCount);
 
@@ -564,17 +768,18 @@ public:
       NS_ASSERTION(aWorkerPrivate == GetWorkerPrivateFromContext(aCx),
                    "Badness!");
       mainRuntime = false;
       target = JS_GetGlobalObject(aCx);
     }
 
     NS_ASSERTION(target, "This should never be null!");
 
-    JSObject* event = events::CreateMessageEvent(aCx, buffer, mainRuntime);
+    JSObject* event = events::CreateMessageEvent(aCx, buffer, mClonedObjects,
+                                                 mainRuntime);
     if (!event) {
       return false;
     }
 
     bool dummy;
     return events::DispatchEventToTarget(aCx, target, event, &dummy);
   }
 };
@@ -1385,17 +1590,17 @@ WorkerPrivateParent<Derived>::WorkerPriv
                                      nsCOMPtr<nsIURI>& aBaseURI,
                                      nsCOMPtr<nsIPrincipal>& aPrincipal,
                                      nsCOMPtr<nsIDocument>& aDocument)
 : mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mJSObject(aObject), mParent(aParent), mParentJSContext(aParentJSContext),
   mScriptURL(aScriptURL), mDomain(aDomain), mBusyCount(0),
   mParentStatus(Pending), mJSObjectRooted(false), mParentSuspended(false),
-  mIsChromeWorker(aIsChromeWorker)
+  mIsChromeWorker(aIsChromeWorker), mPrincipalIsSystem(false)
 {
   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent);
 
   if (aWindow) {
     NS_ASSERTION(aWindow->IsInnerWindow(), "Should have inner window here!");
   }
 
   mWindow.swap(aWindow);
@@ -1688,37 +1893,45 @@ WorkerPrivateParent<Derived>::ForgetMain
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::PostMessage(JSContext* aCx, jsval aMessage)
 {
   AssertIsOnParentThread();
 
   JSStructuredCloneCallbacks* callbacks;
   if (GetParent()) {
-    callbacks = nsnull;
+    if (IsChromeWorker()) {
+      callbacks = &gChromeWorkerStructuredCloneCallbacks;
+    }
+    else {
+      callbacks = &gWorkerStructuredCloneCallbacks;
+    }
   }
   else {
     AssertIsOnMainThread();
 
     if (IsChromeWorker()) {
       callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks;
     }
     else {
       callbacks = &gMainThreadWorkerStructuredCloneCallbacks;
     }
   }
 
+  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aMessage, callbacks, nsnull)) {
+  if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) {
     return false;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(ParentAsWorkerPrivate(),
-                             WorkerRunnable::WorkerThread, buffer);
+                             WorkerRunnable::WorkerThread, buffer,
+                             clonedObjects);
   return runnable->Dispatch(aCx);
 }
 
 template <class Derived>
 PRUint64
 WorkerPrivateParent<Derived>::GetOuterWindowId()
 {
   AssertIsOnMainThread();
@@ -1835,16 +2048,26 @@ WorkerPrivateParent<Derived>::SetBaseURI
   else {
     mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
   }
 
   return NS_OK;
 }
 
 template <class Derived>
+void
+WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal)
+{
+  AssertIsOnMainThread();
+
+  mPrincipal = aPrincipal;
+  mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
+}
+
+template <class Derived>
 JSContext*
 WorkerPrivateParent<Derived>::ParentJSContext() const
 {
   AssertIsOnParentThread();
 
   if (!mParent) {
     AssertIsOnMainThread();
 
@@ -2624,23 +2847,31 @@ WorkerPrivate::StopSyncLoop(PRUint32 aSy
   syncQueue->mComplete = true;
 }
 
 bool
 WorkerPrivate::PostMessageToParent(JSContext* aCx, jsval aMessage)
 {
   AssertIsOnWorkerThread();
 
+  JSStructuredCloneCallbacks* callbacks =
+    IsChromeWorker() ?
+    &gChromeWorkerStructuredCloneCallbacks :
+    &gWorkerStructuredCloneCallbacks;
+
+  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aMessage, nsnull, nsnull)) {
+  if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) {
     return false;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
-    new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer);
+    new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer,
+                             clonedObjects);
   return runnable->Dispatch(aCx);
 }
 
 bool
 WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
 {
   AssertIsOnWorkerThread();
 
@@ -3205,20 +3436,24 @@ template class WorkerPrivateParent<Worke
 WorkerPrivate*
 GetWorkerPrivateFromContext(JSContext* aCx)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   return static_cast<WorkerPrivate*>(JS_GetContextPrivate(aCx));
 }
 
 JSStructuredCloneCallbacks*
-WorkerStructuredCloneCallbacks()
+WorkerStructuredCloneCallbacks(bool aMainRuntime)
 {
-  return &gWorkerStructuredCloneCallbacks;
+  return aMainRuntime ?
+         &gMainThreadWorkerStructuredCloneCallbacks :
+         &gWorkerStructuredCloneCallbacks;
 }
 
 JSStructuredCloneCallbacks*
-ChromeWorkerStructuredCloneCallbacks()
+ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime)
 {
-  return &gChromeWorkerStructuredCloneCallbacks;
+  return aMainRuntime ?
+         &gMainThreadChromeWorkerStructuredCloneCallbacks :
+         &gChromeWorkerStructuredCloneCallbacks;
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -214,16 +214,17 @@ private:
 
   PRUint64 mBusyCount;
   Status mParentStatus;
   PRUint32 mJSContextOptions;
   PRUint8 mGCZeal;
   bool mJSObjectRooted;
   bool mParentSuspended;
   bool mIsChromeWorker;
+  bool mPrincipalIsSystem;
 
 protected:
   WorkerPrivateParent(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent,
                       JSContext* aParentJSContext, const nsAString& aScriptURL,
                       bool aIsChromeWorker, const nsACString& aDomain,
                       nsCOMPtr<nsPIDOMWindow>& aWindow,
                       nsCOMPtr<nsIScriptContext>& aScriptContext,
                       nsCOMPtr<nsIURI>& aBaseURI,
@@ -393,20 +394,22 @@ public:
   nsIPrincipal*
   GetPrincipal() const
   {
     AssertIsOnMainThread();
     return mPrincipal;
   }
 
   void
-  SetPrincipal(nsIPrincipal* aPrincipal)
+  SetPrincipal(nsIPrincipal* aPrincipal);
+
+  bool
+  UsesSystemPrincipal() const
   {
-    AssertIsOnMainThread();
-    mPrincipal = aPrincipal;
+    return mPrincipalIsSystem;
   }
 
   nsIDocument*
   GetDocument() const
   {
     AssertIsOnMainThread();
     return mDocument;
   }
@@ -740,17 +743,25 @@ private:
     ClearQueue(&mQueue);
     ClearQueue(&mControlQueue);
   }
 };
 
 WorkerPrivate*
 GetWorkerPrivateFromContext(JSContext* aCx);
 
-JSStructuredCloneCallbacks*
-WorkerStructuredCloneCallbacks();
+enum WorkerStructuredDataType
+{
+  DOMWORKER_SCTAG_FILE = JS_SCTAG_USER_MIN + 0x1000,
+  DOMWORKER_SCTAG_BLOB,
+
+  DOMWORKER_SCTAG_END
+};
 
 JSStructuredCloneCallbacks*
-ChromeWorkerStructuredCloneCallbacks();
+WorkerStructuredCloneCallbacks(bool aMainRuntime);
+
+JSStructuredCloneCallbacks*
+ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime);
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_workerprivate_h__ */
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -44,16 +45,18 @@
 
 #include "nsTraceRefcnt.h"
 #include "xpcprivate.h"
 
 #include "ChromeWorkerScope.h"
 #include "Events.h"
 #include "EventTarget.h"
 #include "Exceptions.h"
+#include "File.h"
+#include "FileReaderSync.h"
 #include "ListenerManager.h"
 #include "Location.h"
 #include "Navigator.h"
 #include "Principal.h"
 #include "ScriptLoader.h"
 #include "Worker.h"
 #include "WorkerPrivate.h"
 #include "XMLHttpRequest.h"
@@ -870,16 +873,18 @@ CreateDedicatedWorkerGlobalScope(JSConte
     if (!chromeworker::InitClass(aCx, global, workerProto, false) ||
         !chromeworker::DefineChromeWorkerFunctions(aCx, global)) {
       return NULL;
     }
   }
 
   // Init other classes we care about.
   if (!events::InitClasses(aCx, global, false) ||
+      !file::InitClasses(aCx, global) ||
+      !filereadersync::InitClass(aCx, global) ||
       !exceptions::InitClasses(aCx, global) ||
       !xhr::InitClasses(aCx, global, eventTargetProto) ||
       !location::InitClass(aCx, global) ||
       !navigator::InitClass(aCx, global)) {
     return NULL;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, global)) {
--- a/dom/workers/test/Makefile.in
+++ b/dom/workers/test/Makefile.in
@@ -115,16 +115,34 @@ include $(topsrcdir)/config/rules.mk
   relativeLoad_sub_worker.js \
   relativeLoad_sub_worker2.js \
   relativeLoad_sub_import.js \
   $(NULL)
 
 _CHROME_TEST_FILES = \
   test_chromeWorker.xul \
   test_chromeWorkerJSM.xul \
+  test_file.xul \
+  test_fileMozSlice.xul \
+  test_fileBlobPosting.xul \
+  test_filePosting.xul \
+  test_fileReaderSync.xul \
+  test_fileReaderSyncErrors.xul \
+  test_fileReadMozSlice.xul \
+  test_fileSubWorker.xul \
+  test_fileBlobSubWorker.xul \
+  file_worker.js \
+  fileBlob_worker.js \
+  fileMozSlice_worker.js \
+  filePosting_worker.js \
+  fileReaderSync_worker.js \
+  fileReaderSyncErrors_worker.js \
+  fileReadMozSlice_worker.js \
+  fileSubWorker_worker.js \
+  fileBlobSubWorker_worker.js \
   WorkerTest.jsm \
   WorkerTest_worker.js \
   WorkerTest_subworker.js \
   chromeWorker_worker.js \
   chromeWorker_subworker.js \
   test_workersDisabled.xul \
   workersDisabled_worker.js \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileBlobSubWorker_worker.js
@@ -0,0 +1,17 @@
+/**
+ * Expects a blob. Returns an object containing the size, type.
+ *  Used to test posting of blob from worker to worker.
+ */
+onmessage = function(event) {
+  var worker = new Worker("fileBlob_worker.js");
+
+  worker.postMessage(event.data);
+
+  worker.onmessage = function(event) {
+    postMessage(event.data);
+  }
+
+  worker.onerror = function(event) {
+    postMessage(undefined);
+  }
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileBlob_worker.js
@@ -0,0 +1,13 @@
+/**
+ * Expects a blob. Returns an object containing the size, type.
+ */
+onmessage = function(event) {
+  var file = event.data;
+
+  var rtnObj = new Object();
+
+  rtnObj.size = file.size;
+  rtnObj.type = file.type;
+
+  postMessage(rtnObj);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileMozSlice_worker.js
@@ -0,0 +1,27 @@
+/**
+ * Expects an object containing a blob, a start offset, an end offset
+ * and an optional content type to slice the blob. Returns an object
+ * containing the size and type of the sliced blob.
+ */
+onmessage = function(event) {
+  var blob = event.data.blob;
+  var start = event.data.start;
+  var end = event.data.end;
+  var contentType = event.data.contentType;
+
+  var slicedBlob;
+  if (contentType == undefined && end == undefined) {
+    slicedBlob = blob.mozSlice(start);
+  } else if (contentType == undefined) {
+    slicedBlob = blob.mozSlice(start, end);
+  } else {
+    slicedBlob = blob.mozSlice(start, end, contentType);
+  }
+
+  var rtnObj = new Object();
+
+  rtnObj.size = slicedBlob.size;
+  rtnObj.type = slicedBlob.type;
+
+  postMessage(rtnObj);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/filePosting_worker.js
@@ -0,0 +1,3 @@
+onmessage = function(event) {
+  postMessage(event.data);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileReadMozSlice_worker.js
@@ -0,0 +1,16 @@
+/**
+ * Expects an object containing a blob, a start index and an end index
+ * for slicing. Returns the contents of the blob read as text.
+ */
+onmessage = function(event) {
+  var blob = event.data.blob;
+  var start = event.data.start;
+  var end = event.data.end;
+
+  var slicedBlob = blob.mozSlice(start, end);
+
+  var fileReader = new FileReaderSync();
+  var text = fileReader.readAsText(slicedBlob);
+
+  postMessage(text);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileReaderSyncErrors_worker.js
@@ -0,0 +1,79 @@
+/**
+ * Delegates "is" evaluation back to main thread.
+ */
+function is(actual, expected, message) {
+  var rtnObj = new Object();
+  rtnObj.actual = actual;
+  rtnObj.expected = expected;
+  rtnObj.message = message;
+  postMessage(rtnObj);
+}
+
+/**
+ * Tries to write to property.
+ */
+function writeProperty(file, property) {
+  try {
+    var oldValue = file[property];
+    file[property] = -1;
+    is(false, true, "Should have thrown an exception setting a read only property.");
+  } catch (ex) {
+    is(true, true, "Should have thrown an exception setting a read only property.");
+  }
+}
+
+/**
+ * Passes junk arguments to FileReaderSync methods and expects an exception to
+ * be thrown.
+ */
+function fileReaderJunkArgument(blob) {
+  var fileReader = new FileReaderSync();
+
+  try {
+    fileReader.readAsBinaryString(blob);
+    is(false, true, "Should have thrown an exception calling readAsBinaryString.");
+  } catch(ex) {
+    is(true, true, "Should have thrown an exception.");
+  }
+
+  try {
+    fileReader.readAsDataURL(blob);
+    is(false, true, "Should have thrown an exception calling readAsDataURL.");
+  } catch(ex) {
+    is(true, true, "Should have thrown an exception.");
+  }
+
+  try {
+    fileReader.readAsArrayBuffer(blob);
+    is(false, true, "Should have thrown an exception calling readAsArrayBuffer.");
+  } catch(ex) {
+    is(true, true, "Should have thrown an exception.");
+  }
+
+  try {
+    fileReader.readAsText(blob);
+    is(false, true, "Should have thrown an exception calling readAsText.");
+  } catch(ex) {
+    is(true, true, "Should have thrown an exception.");
+  }
+}
+
+onmessage = function(event) {
+  var file = event.data;
+
+  // Test read only properties.
+  writeProperty(file, "size");
+  writeProperty(file, "type");
+  writeProperty(file, "name");
+  writeProperty(file, "mozFullPath");
+
+  // Bad types.
+  fileReaderJunkArgument(undefined);
+  fileReaderJunkArgument(-1);
+  fileReaderJunkArgument(1);
+  fileReaderJunkArgument(new Object());
+  fileReaderJunkArgument("hello");
+
+    // Post undefined to indicate that testing has finished.
+  postMessage(undefined);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileReaderSync_worker.js
@@ -0,0 +1,25 @@
+var reader = new FileReaderSync();
+
+/**
+ * Expects an object containing a file and an encoding then uses a
+ * FileReaderSync to read the file. Returns an object containing the
+ * file read a binary string, text, url and ArrayBuffer.
+ */
+onmessage = function(event) {
+  var file = event.data.file;
+  var encoding = event.data.encoding;
+
+  var rtnObj = new Object();
+
+  if (encoding != undefined) {
+    rtnObj.text = reader.readAsText(file, encoding);
+  } else {
+    rtnObj.text = reader.readAsText(file);
+  }
+
+  rtnObj.bin = reader.readAsBinaryString(file);
+  rtnObj.url = reader.readAsDataURL(file);
+  rtnObj.arrayBuffer = reader.readAsArrayBuffer(file);
+
+  postMessage(rtnObj);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileSubWorker_worker.js
@@ -0,0 +1,17 @@
+/**
+ * Expects a file. Returns an object containing the size, type, name and path
+ * using another worker. Used to test posting of file from worker to worker.
+ */
+onmessage = function(event) {
+  var worker = new Worker("file_worker.js");
+
+  worker.postMessage(event.data);
+
+  worker.onmessage = function(event) {
+    postMessage(event.data);
+  }
+
+  worker.onerror = function(event) {
+    postMessage(undefined);
+  }
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/file_worker.js
@@ -0,0 +1,15 @@
+/**
+ * Expects a file. Returns an object containing the size, type, name and path.
+ */
+onmessage = function(event) {
+  var file = event.data;
+
+  var rtnObj = new Object();
+
+  rtnObj.size = file.size;
+  rtnObj.type = file.type;
+  rtnObj.name = file.name;
+  rtnObj.mozFullPath = file.mozFullPath;
+
+  postMessage(rtnObj);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_file.xul
@@ -0,0 +1,97 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=123456
+-->
+<window title="Mozilla Bug 123456"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=123456"
+     target="_blank">Mozilla Bug 123456</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 123456 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerFile" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker to access file properties.
+   */
+  function accessFileProperties(file, expectedSize, expectedType) {
+    var worker = new Worker("file_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+      is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect.");
+      is(event.data.name, file.name, "name proproperty accessed from worker is incorrect.");
+      is(event.data.mozFullPath, file.mozFullPath, "mozFullPath proproperty accessed from worker is not the same as on main thread.");
+      SimpleTest.finish();
+    };
+
+    worker.postMessage(file);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  accessFileProperties(createFileWithData(""), 0, "");
+
+  // Typical use case.
+  accessFileProperties(createFileWithData("Hello"), 5, "");
+
+  // Longish file.
+  var text = "";
+  for (var i = 0; i < 10000; ++i) {
+    text += "long";
+  }
+  accessFileProperties(createFileWithData(text), 40000, "");
+
+  // Type detection based on extension.
+  accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain");
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileBlobPosting.xul
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data.
+   */
+  function createFileWithData(fileData) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    testFile.append("workerBlobPosting" + fileNum++);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker which posts the same blob given. Used to test cloning of blobs.
+   * Checks the size, type, name and path of the file posted from the worker to ensure it
+   * is the same as the original.
+   */
+  function postBlob(file) {
+    var worker = new Worker("filePosting_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      console.log(event.data);
+      is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker.");
+      SimpleTest.finish();
+    };
+
+    var blob = file.mozSlice();
+    worker.postMessage(blob);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  postBlob(createFileWithData(""));
+
+  // Typical use case.
+  postBlob(createFileWithData("Hello"));
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileBlobSubWorker.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerBlobSubWorker" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker to access blob properties.
+   */
+  function accessFileProperties(file, expectedSize) {
+    var worker = new Worker("fileBlobSubWorker_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      if (event.data == undefined) {
+        ok(false, "Worker had an error.");
+      } else {
+        is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+      }
+      SimpleTest.finish();
+    };
+
+    var blob = file.mozSlice();
+    worker.postMessage(blob);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  accessFileProperties(createFileWithData(""), 0);
+
+  // Typical use case.
+  accessFileProperties(createFileWithData("Hello"), 5);
+
+  // Longish file.
+  var text = "";
+  for (var i = 0; i < 10000; ++i) {
+    text += "long";
+  }
+  accessFileProperties(createFileWithData(text), 40000);
+
+  // Type detection based on extension.
+  accessFileProperties(createFileWithData("text", "txt"), 4);
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileMozSlice.xul
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerMozSlice" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Starts a worker which slices the blob to the given start offset and optional end offset and
+   * content type. It then verifies that the size and type of the sliced blob is correct.
+   */
+  function createMozSlice(blob, start, expectedLength, /** optional */ end, /** optional */ contentType) {
+    var worker = new Worker("fileMozSlice_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      is(event.data.size, expectedLength, "size property of slice is incorrect.");
+      is(event.data.type, contentType ? contentType : blob.type, "type property of slice is incorrect.");
+      SimpleTest.finish();
+    };
+
+    var params = {blob: blob, start: start, end: end, contentType: contentType};
+    worker.postMessage(params);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  createMozSlice(createFileWithData(""), 0, 0, 0);
+
+  // Typical use case.
+  createMozSlice(createFileWithData("Hello"), 1, 1, 2);
+
+  // Longish file.
+  var text = "";
+  for (var i = 0; i < 10000; ++i) {
+    text += "long";
+  }
+  createMozSlice(createFileWithData(text), 2000, 2000, 4000);
+
+  // Slice to different type.
+  createMozSlice(createFileWithData("text", "txt"), 0, 2, 2, "image/png");
+
+  // Length longer than blob.
+  createMozSlice(createFileWithData("text"), 0, 4, 20);
+
+  // Start longer than blob.
+  createMozSlice(createFileWithData("text"), 20, 0, 4);
+
+  // No optional arguments
+  createMozSlice(createFileWithData("text"), 0, 4);
+  createMozSlice(createFileWithData("text"), 2, 2);
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_filePosting.xul
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data.
+   */
+  function createFileWithData(fileData) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    testFile.append("workerFilePosting" + fileNum++);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker which posts the same file given. Used to test cloning of files.
+   * Checks the size, type, name and path of the file posted from the worker to ensure it
+   * is the same as the original.
+   */
+  function postFile(file) {
+    var worker = new Worker("file_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker.");
+      is(event.data.type, file.type, "type of file posted from worker does not match file posted to worker.");
+      is(event.data.name, file.name, "name of file posted from worker does not match file posted to worker.");
+      is(event.data.mozFullPath, file.mozFullPath, "mozFullPath of file posted from worker does not match file posted to worker.");
+      SimpleTest.finish();
+    };
+
+    worker.postMessage(file);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  postFile(createFileWithData(""));
+
+  // Typical use case.
+  postFile(createFileWithData("Hello"));
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileReadMozSlice.xul
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data.
+   */
+  function createFileWithData(fileData) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    testFile.append("workerReadMozSlice" + fileNum++);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Creates a worker which slices a blob to the given start and end offset and
+   * reads the content as text.
+   */
+  function readMozSlice(blob, start, end, expectedText) {
+    var worker = new Worker("fileReadMozSlice_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      is(event.data, expectedText, "Text from sliced blob in worker is incorrect.");
+      SimpleTest.finish();
+    };
+
+    var params = {blob: blob, start: start, end: end};
+    worker.postMessage(params);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  readMozSlice(createFileWithData(""), 0, 0, "");
+
+  // Typical use case.
+  readMozSlice(createFileWithData("HelloBye"), 5, 8, "Bye");
+
+  // End offset too large.
+  readMozSlice(createFileWithData("HelloBye"), 5, 9, "Bye");
+
+  // Start of file.
+  readMozSlice(createFileWithData("HelloBye"), 0, 5, "Hello");
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileReaderSync.xul
@@ -0,0 +1,200 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerFileReaderSync" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  function convertToUTF16(s) {
+    res = "";
+    for (var i = 0; i < s.length; ++i) {
+      c = s.charCodeAt(i);
+      res += String.fromCharCode(c >>> 8, c & 255);
+    }
+    return res;
+  }
+
+  /**
+   * Converts the given string to a data URL of the specified mime type.
+   */
+  function convertToDataURL(mime, s) {
+    return "data:" + mime + ";base64," + btoa(s);
+  }
+
+  /**
+   * Create a worker to read a file containing fileData using FileReaderSync and
+   * checks the return type against the expected type. Optionally set an encoding
+   * for reading the file as text.
+   */
+  function readFileData(fileData, expectedText, /** optional */ encoding) {
+    var worker = new Worker("fileReaderSync_worker.js");
+
+    worker.onmessage = function(event) {
+      is(event.data.text, expectedText, "readAsText in worker returned incorrect result.");
+      is(event.data.bin, fileData, "readAsBinaryString in worker returned incorrect result.");
+      is(event.data.url, convertToDataURL("application/octet-stream", fileData), "readAsDataURL in worker returned incorrect result.");
+      is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length.");
+      SimpleTest.finish();
+    };
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    var params = {file: createFileWithData(fileData), encoding: encoding};
+
+    worker.postMessage(params);
+
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  /**
+   * Create a worker which reuses a FileReaderSync to read multiple files as DataURLs.
+   */
+  function reuseReaderForURL(files, expected) {
+    var worker = new Worker("fileReaderSync_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    var k = 0;
+    worker.onmessage = function(event) {
+      is(event.data.url, expected[k], "readAsDataURL in worker returned incorrect result when reusing FileReaderSync.");
+      k++;
+      SimpleTest.finish();
+    };
+
+    for (var i = 0; i < files.length; ++i) {
+      var params = {file: files[i], encoding: undefined};
+      worker.postMessage(params);
+      SimpleTest.waitForExplicitFinish();
+    }
+  }
+
+  /**
+   * Create a worker which reuses a FileReaderSync to read multiple files as text.
+   */
+  function reuseReaderForText(fileData, expected) {
+    var worker = new Worker("fileReaderSync_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    var k = 0;
+    worker.onmessage = function(event) {
+      is(event.data.text, expected[k++], "readAsText in worker returned incorrect result when reusing FileReaderSync.");
+      SimpleTest.finish();
+    };
+
+    for (var i = 0; i < fileData.length; ++i) {
+      var params = {file: createFileWithData(fileData[i]), encoding: undefined};
+      worker.postMessage(params);
+      SimpleTest.waitForExplicitFinish();
+    }
+  }
+
+
+  /**
+   * Creates a a worker which reads a file containing fileData as an ArrayBuffer.
+   * Verifies that the ArrayBuffer when interpreted as a string matches the original data.
+   */
+  function readArrayBuffer(fileData) {
+    var worker = new Worker("fileReaderSync_worker.js");
+
+    worker.onmessage = function(event) {
+      var view = new Uint8Array(event.data.arrayBuffer);
+      is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length.");
+      is(String.fromCharCode.apply(String, view), fileData, "readAsArrayBuffer returned buffer containing incorrect data.");
+      SimpleTest.finish();
+    };
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    var params = {file: createFileWithData(fileData), encoding: undefined};
+
+    worker.postMessage(params);
+
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  readFileData("", "");
+
+  // Typical use case.
+  readFileData("text", "text");
+
+  // Test reading UTF-16 characters.
+  readFileData(convertToUTF16("text"), "text", "UTF-16");
+
+  // First read a file of type "text/plain", then read a file of type "application/octet-stream".
+  reuseReaderForURL([createFileWithData("text", "txt"), createFileWithData("text")],
+                    [convertToDataURL("text/plain", "text"),
+                     convertToDataURL("application/octet-stream", "text")]);
+
+  // First read UTF-16 characters marked using BOM, then read UTF-8 characters.
+  reuseReaderForText([convertToUTF16("\ufefftext"), "text"],
+                     ["text", "text"]);
+
+  // Reading data as ArrayBuffer.
+  readArrayBuffer("");
+  readArrayBuffer("text");
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileReaderSyncErrors.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data.
+   */
+  function createFileWithData(fileData) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    testFile.append("workerFileReaderSyncErrors" + fileNum++);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Creates a worker which runs errors cases.
+   */
+  function runWorkerErrors(file) {
+    var worker = new Worker("fileReaderSyncErrors_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      if(event.data == undefined) {
+        // Worker returns undefined when tests have finished running.
+        SimpleTest.finish();
+      } else {
+        // Otherwise worker will return results of tests to be evaluated.
+        is(event.data.actual, event.data.expected, event.data.message);
+      }
+    };
+
+    worker.postMessage(file);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Run worker which creates exceptions.
+  runWorkerErrors(createFileWithData("text"));
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileSubWorker.xul
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerSubWorker" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker to access file properties.
+   */
+  function accessFileProperties(file, expectedSize, expectedType) {
+    var worker = new Worker("fileSubWorker_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      if (event.data == undefined) {
+        ok(false, "Worker had an error.");
+      } else {
+        is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+        is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect.");
+        is(event.data.name, file.name, "name proproperty accessed from worker is incorrect.");
+        is(event.data.mozFullPath, file.mozFullPath, "mozFullPath proproperty accessed from worker is not the same as on main thread.");
+      }
+      SimpleTest.finish();
+    };
+
+    worker.postMessage(file);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  accessFileProperties(createFileWithData(""), 0, "");
+
+  // Typical use case.
+  accessFileProperties(createFileWithData("Hello"), 5, "");
+
+  // Longish file.
+  var text = "";
+  for (var i = 0; i < 10000; ++i) {
+    text += "long";
+  }
+  accessFileProperties(createFileWithData(text), 40000, "");
+
+  // Type detection based on extension.
+  accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain");
+
+  ]]>
+  </script>
+</window>
--- a/extensions/spellcheck/hunspell/src/Makefile.in
+++ b/extensions/spellcheck/hunspell/src/Makefile.in
@@ -59,16 +59,18 @@ CPPSRCS         += affentry.cpp \
                    hashmgr.cpp \
                    hunspell.cpp \
                    hunzip.cpp \
                    phonet.cpp \
                    replist.cpp \
                    suggestmgr.cpp \
                    $(NULL)
 
+# This variable is referenced in configure.in.  Make sure to change that file
+# too if you need to change this variable.
 DEFINES = -DHUNSPELL_STATIC
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 ifdef MOZ_NATIVE_HUNSPELL
 CXXFLAGS += $(MOZ_HUNSPELL_CFLAGS)
 endif
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/hunspell_alloc_hooks.h
@@ -0,0 +1,108 @@
+/******* 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 Initial Developers of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developers
+ * are Copyright (C) 2011 the Initial Developers. 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 alloc_hooks_h__
+#define alloc_hooks_h__
+
+/**
+ * This file is force-included in hunspell code.  Its purpose is to add memory
+ * reporting to hunspell without modifying its code, in order to ease future
+ * upgrades.
+ *
+ * This file is force-included through mozilla-config.h which is generated
+ * during the configure step.
+ *
+ * Currently, the memory allocated using operator new/new[] is not being
+ * tracked, but that's OK, since almost all of the memory used by Hunspell is
+ * allocated using C memory allocation functions.
+ */
+
+// Prevent the standard macros from being redefined
+#define mozilla_mozalloc_macro_wrappers_h
+
+#include "mozilla/mozalloc.h"
+
+extern void HunspellReportMemoryAllocation(void*);
+extern void HunspellReportMemoryDeallocation(void*);
+
+inline void* hunspell_malloc(size_t size)
+{
+  void* result = moz_malloc(size);
+  HunspellReportMemoryAllocation(result);
+  return result;
+}
+#define malloc(size) hunspell_malloc(size)
+
+inline void* hunspell_calloc(size_t count, size_t size)
+{
+  void* result = moz_calloc(count, size);
+  HunspellReportMemoryAllocation(result);
+  return result;
+}
+#define calloc(count, size) hunspell_calloc(count, size)
+
+inline void hunspell_free(void* ptr)
+{
+  HunspellReportMemoryDeallocation(ptr);
+  moz_free(ptr);
+}
+#define free(ptr) hunspell_free(ptr)
+
+inline void* hunspell_realloc(void* ptr, size_t size)
+{
+  HunspellReportMemoryDeallocation(ptr);
+  void* result = moz_realloc(ptr, size);
+  HunspellReportMemoryAllocation(result);
+  return result;
+}
+#define realloc(ptr, size) hunspell_realloc(ptr, size)
+
+inline char* hunspell_strdup(const char* str)
+{
+  char* result = moz_strdup(str);
+  HunspellReportMemoryAllocation(result);
+  return result;
+}
+#define strdup(str) hunspell_strdup(str)
+
+#if defined(HAVE_STRNDUP)
+inline char* hunspell_strndup(const char* str, size_t size)
+{
+  char* result = moz_strndup(str, size);
+  HunspellReportMemoryAllocation(result);
+  return result;
+}
+#define strndup(str, size) hunspell_strndup(str, size)
+#endif
+
+#endif
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp
@@ -66,16 +66,17 @@
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "mozISpellI18NManager.h"
 #include "nsICharsetConverterManager.h"
 #include "nsUnicharUtilCIID.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include <stdlib.h>
+#include "nsIMemoryReporter.h"
 
 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
 static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID);
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell)
 
 NS_INTERFACE_MAP_BEGIN(mozHunspell)
@@ -86,37 +87,64 @@ NS_INTERFACE_MAP_BEGIN(mozHunspell)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_3(mozHunspell,
                            mPersonalDictionary,
                            mEncoder,
                            mDecoder)
 
+// Memory reporting stuff
+static PRInt64 gHunspellAllocatedSize = 0;
+
+void HunspellReportMemoryAllocation(void* ptr) {
+  gHunspellAllocatedSize += moz_malloc_usable_size(ptr);
+}
+void HunspellReportMemoryDeallocation(void* ptr) {
+  gHunspellAllocatedSize -= moz_malloc_usable_size(ptr);
+}
+static PRInt64 HunspellGetCurrentAllocatedSize() {
+  return gHunspellAllocatedSize;
+}
+
+NS_MEMORY_REPORTER_IMPLEMENT(Hunspell,
+    "explicit/spell-check",
+    KIND_HEAP,
+    UNITS_BYTES,
+    HunspellGetCurrentAllocatedSize,
+    "Memory used by the Hunspell spell checking engine.  This number accounts "
+    "for the memory in use by Hunspell's internal data structures."
+)
+
 nsresult
 mozHunspell::Init()
 {
   if (!mDictionaries.Init())
     return NS_ERROR_OUT_OF_MEMORY;
 
   LoadDictionaryList();
 
   nsCOMPtr<nsIObserverService> obs =
     do_GetService("@mozilla.org/observer-service;1");
   if (obs) {
     obs->AddObserver(this, "profile-do-change", PR_TRUE);
   }
 
+  mHunspellReporter = new NS_MEMORY_REPORTER_NAME(Hunspell);
+  NS_RegisterMemoryReporter(mHunspellReporter);
+
   return NS_OK;
 }
 
 mozHunspell::~mozHunspell()
 {
   mPersonalDictionary = nsnull;
   delete mHunspell;
+
+  NS_UnregisterMemoryReporter(mHunspellReporter);
 }
 
 /* attribute wstring dictionary; */
 NS_IMETHODIMP mozHunspell::GetDictionary(PRUnichar **aDictionary)
 {
   NS_ENSURE_ARG_POINTER(aDictionary);
 
   if (mDictionary.IsEmpty())
--- a/extensions/spellcheck/hunspell/src/mozHunspell.h
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.h
@@ -72,27 +72,29 @@
 #include "nsCycleCollectionParticipant.h"
 
 #define MOZ_HUNSPELL_CONTRACTID "@mozilla.org/spellchecker/engine;1"
 #define MOZ_HUNSPELL_CID         \
 /* 56c778e4-1bee-45f3-a689-886692a97fe7 */   \
 { 0x56c778e4, 0x1bee, 0x45f3, \
   { 0xa6, 0x89, 0x88, 0x66, 0x92, 0xa9, 0x7f, 0xe7 } }
 
+class nsIMemoryReporter;
+
 class mozHunspell : public mozISpellCheckingEngine,
                    public nsIObserver,
                    public nsSupportsWeakReference
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_MOZISPELLCHECKINGENGINE
   NS_DECL_NSIOBSERVER
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozHunspell, mozISpellCheckingEngine)
 
-  mozHunspell() : mHunspell(nsnull) { }
+  mozHunspell() : mHunspell(nsnull), mHunspellReporter(nsnull) { }
   virtual ~mozHunspell();
 
   nsresult Init();
 
   void LoadDictionaryList();
 
   // helper method for converting a word to the charset of the dictionary
   nsresult ConvertCharset(const PRUnichar* aStr, char ** aDst);
@@ -104,11 +106,13 @@ protected:
   nsCOMPtr<nsIUnicodeDecoder>      mDecoder; 
 
   // Hashtable matches dictionary name to .aff file
   nsInterfaceHashtable<nsStringHashKey, nsIFile> mDictionaries;
   nsString  mDictionary;
   nsString  mLanguage;
 
   Hunspell  *mHunspell;
+
+  nsIMemoryReporter* mHunspellReporter;
 };
 
 #endif
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -905,19 +905,26 @@ DrawTargetD2D::PushClip(const Path *aPat
   mPushedClips.push_back(clip);
 
   // The transform of clips is relative to the world matrix, since we use the total
   // transform for the clips, make the world matrix identity.
   mRT->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
   if (mClipsArePushed) {
+    D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
+
+    if (mFormat == FORMAT_B8G8R8X8) {
+      options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
+    }
+
     mRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), pathD2D->mGeometry,
                                          D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
-                                         clip.mTransform), layer);
+                                         clip.mTransform, 1.0f, NULL,
+                                         options), layer);
   }
 }
 
 void
 DrawTargetD2D::PopClip()
 {
   if (mClipsArePushed) {
     mRT->PopLayer();
@@ -1186,32 +1193,43 @@ DrawTargetD2D::InitD2DRenderTarget()
   if (!mRT) {
     return false;
   }
 
   mRT->BeginDraw();
 
   mRT->Clear(D2D1::ColorF(0, 0));
 
+  if (mFormat == FORMAT_B8G8R8X8) {
+    mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
+  }
+
   return InitD3D10Data();
 }
 
 void
 DrawTargetD2D::PrepareForDrawing(ID2D1RenderTarget *aRT)
 {
   if (!mClipsArePushed || aRT == mTempRT) {
     if (mPushedClips.size()) {
       // The transform of clips is relative to the world matrix, since we use the total
       // transform for the clips, make the world matrix identity.
       mRT->SetTransform(D2D1::IdentityMatrix());
       for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
            iter != mPushedClips.end(); iter++) {
+        D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
+
+        if (mFormat == FORMAT_B8G8R8X8) {
+          options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
+        }
+
         aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), iter->mPath->mGeometry,
                                              D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
-                                             iter->mTransform), iter->mLayer);
+                                             iter->mTransform, 1.0f, NULL,
+                                             options), iter->mLayer);
       }
       if (aRT == mRT) {
         mClipsArePushed = true;
       }
     }
   }
   mRT->SetTransform(D2DMatrix(mTransform));
   MarkChanged();
@@ -1470,18 +1488,24 @@ DrawTargetD2D::CreateRTForTexture(ID3D10
   if (FAILED(hr)) {
     gfxWarning() << "Failed to QI texture to surface.";
     return NULL;
   }
 
   D3D10_TEXTURE2D_DESC desc;
   aTexture->GetDesc(&desc);
 
+  D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
+
+  if (mFormat == FORMAT_B8G8R8X8) {
+    alphaMode = D2D1_ALPHA_MODE_IGNORE;
+  }
+
   D2D1_RENDER_TARGET_PROPERTIES props =
-    D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, D2D1_ALPHA_MODE_PREMULTIPLIED));
+    D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode));
   hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt));
 
   if (FAILED(hr)) {
     gfxWarning() << "Failed to create D2D render target for texture.";
     return NULL;
   }
 
   return rt;
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -525,17 +525,16 @@ gfxASurface::BytePerPixelFromFormat(gfxI
     return 0;
 }
 
 void
 gfxASurface::FastMovePixels(const nsIntRect& aSourceRect,
                             const nsIntPoint& aDestTopLeft)
 {
     // Used when the backend can internally handle self copies.
-    gfxIntSize size = GetSize();
     nsIntRect dest(aDestTopLeft, aSourceRect.Size());
     
     nsRefPtr<gfxContext> ctx = new gfxContext(this);
     ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
     nsIntPoint srcOrigin = dest.TopLeft() - aSourceRect.TopLeft();
     ctx->SetSource(this, gfxPoint(srcOrigin.x, srcOrigin.y));
     ctx->Rectangle(gfxRect(dest.x, dest.y, dest.width, dest.height));
     ctx->Fill();
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -554,21 +554,16 @@ JSFunctionSpec gGlobalFunctions[] =
     {"gc",              GC,             0,0},
 #ifdef JS_GC_ZEAL
     {"gczeal",          GCZeal,         1,0},
 #endif
     {"clear",           Clear,          1,0},
 #ifdef DEBUG
     {"dumpHeap",        DumpHeap,       5,0},
 #endif
-#ifdef MOZ_CALLGRIND
-    {"startCallgrind",  js_StartCallgrind,  0,0},
-    {"stopCallgrind",   js_StopCallgrind,   0,0},
-    {"dumpCallgrind",   js_DumpCallgrind,   1,0},
-#endif
     {nsnull,nsnull,0,0}
 };
 
 typedef enum JSShellErrNum
 {
 #define MSG_DEF(name, number, count, exception, format) \
     name = number,
 #include "jsshell.msg"
--- a/js/src/config/config.mk
+++ b/js/src/config/config.mk
@@ -83,16 +83,19 @@ check-variable = $(if $(filter-out 0 1,$
 
 core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)))
 
 nullstr :=
 space :=$(nullstr) # EOL
 
 core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1)))))))
 
+# LIBXUL_DIST is not defined under js/src, thus we make it mean DIST there.
+LIBXUL_DIST ?= $(DIST)
+
 # FINAL_TARGET specifies the location into which we copy end-user-shipped
 # build products (typelibs, components, chrome).
 #
 # It will usually be the well-loved $(DIST)/bin, today, but can also be an
 # XPI-contents staging directory for ambitious and right-thinking extensions.
 FINAL_TARGET = $(if $(XPI_NAME),$(DIST)/xpi-stage/$(XPI_NAME),$(DIST)/bin)
 
 ifdef XPI_NAME
@@ -738,20 +741,20 @@ MERGE_FILE = $(firstword \
   $(wildcard $(LOCALE_SRCDIR)/$(1)) \
   $(srcdir)/en-US/$(1) )
 else
 MERGE_FILE = $(LOCALE_SRCDIR)/$(1)
 endif
 MERGE_FILES = $(foreach f,$(1),$(call MERGE_FILE,$(f)))
 
 ifeq (OS2,$(OS_ARCH))
-RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(DIST)"
+RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(LIBXUL_DIST)"
 else
 ifneq (WINNT,$(OS_ARCH))
-RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh
+RUN_TEST_PROGRAM = $(LIBXUL_DIST)/bin/run-mozilla.sh
 endif # ! WINNT
 endif # ! OS2
 
 #
 # Java macros
 #
 
 # Make sure any compiled classes work with at least JVM 1.4
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -769,19 +769,17 @@ js_InternNonIntElementIdSlow(JSContext *
                              jsid *idp)
 {
     JS_ASSERT(idval.isObject());
     if (obj->isXML()) {
         *idp = OBJECT_TO_JSID(&idval.toObject());
         return true;
     }
 
-    if (!js_IsFunctionQName(cx, &idval.toObject(), idp))
-        return JS_FALSE;
-    if (!JSID_IS_VOID(*idp))
+    if (js_GetLocalNameFromFunctionQName(&idval.toObject(), idp, cx))
         return true;
 
     return js_ValueToStringId(cx, idval, idp);
 }
 
 bool
 js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval,
                              jsid *idp, Value *vp)
@@ -789,19 +787,17 @@ js_InternNonIntElementIdSlow(JSContext *
     JS_ASSERT(idval.isObject());
     if (obj->isXML()) {
         JSObject &idobj = idval.toObject();
         *idp = OBJECT_TO_JSID(&idobj);
         vp->setObject(idobj);
         return true;
     }
 
-    if (!js_IsFunctionQName(cx, &idval.toObject(), idp))
-        return JS_FALSE;
-    if (!JSID_IS_VOID(*idp)) {
+    if (js_GetLocalNameFromFunctionQName(&idval.toObject(), idp, cx)) {
         *vp = IdToValue(*idp);
         return true;
     }
 
     if (js_ValueToStringId(cx, idval, idp)) {
         vp->setString(JSID_TO_STRING(*idp));
         return true;
     }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -530,17 +530,17 @@ struct JSRuntime
     JSTraceDataOp       gcExtraRootsTraceOp;
     void                *gcExtraRootsData;
 
     /* Well-known numbers held for use by this runtime's contexts. */
     js::Value           NaNValue;
     js::Value           negativeInfinityValue;
     js::Value           positiveInfinityValue;
 
-    JSFlatString        *emptyString;
+    JSAtom              *emptyString;
 
     /* List of active contexts sharing this runtime; protected by gcLock. */
     JSCList             contextList;
 
     /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */
     JSDebugHooks        globalDebugHooks;
 
     /* If true, new compartments are initially in debug mode. */
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -38,16 +38,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * JS debugging API.
  */
 #include <string.h>
+#include <stdarg.h>
 #include "jsprvtd.h"
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jshashtable.h"
 #include "jsapi.h"
 #include "jscntxt.h"
@@ -78,16 +79,20 @@
 
 #include "vm/Stack-inl.h"
 
 #include "jsautooplen.h"
 
 #include "methodjit/MethodJIT.h"
 #include "methodjit/Retcon.h"
 
+#ifdef __APPLE__
+#include "sharkctl.h"
+#endif
+
 using namespace js;
 using namespace js::gc;
 
 JS_PUBLIC_API(JSBool)
 JS_GetDebugMode(JSContext *cx)
 {
     return cx->compartment->debugMode();
 }
@@ -1208,66 +1213,364 @@ JS_SetContextDebugHooks(JSContext *cx, c
 }
 
 JS_PUBLIC_API(JSDebugHooks *)
 JS_ClearContextDebugHooks(JSContext *cx)
 {
     return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
 }
 
+/************************************************************************/
+
+/* Profiling-related API */
+
+/* Thread-unsafe error management */
+
+static char gLastError[2000];
+
+static void
+#ifdef _GNU_SOURCE
+__attribute__((unused,format(printf,1,2)))
+#endif
+UnsafeError(const char *format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    (void) vsnprintf(gLastError, sizeof(gLastError), format, args);
+    va_end(args);
+
+    gLastError[sizeof(gLastError) - 1] = '\0';
+}
+
+JS_PUBLIC_API(const char *)
+JS_UnsafeGetLastProfilingError()
+{
+    return gLastError;
+}
+
 JS_PUBLIC_API(JSBool)
-JS_StartProfiling()
+JS_StartProfiling(const char *profileName)
 {
-    return Probes::startProfiling();
+    JSBool ok = JS_TRUE;
+#if defined(MOZ_SHARK) && defined(__APPLE__)
+    if (!Shark::Start()) {
+        UnsafeError("Failed to start Shark for %s", profileName);
+        ok = JS_FALSE;
+    }
+#endif
+#ifdef MOZ_VTUNE
+    if (!js_StartVtune(profileName))
+        ok = JS_FALSE;
+#endif
+    return ok;
+}
+
+JS_PUBLIC_API(JSBool)
+JS_StopProfiling(const char *profileName)
+{
+    JSBool ok = JS_TRUE;
+#if defined(MOZ_SHARK) && defined(__APPLE__)
+    Shark::Stop();
+#endif
+#ifdef MOZ_VTUNE
+    if (!js_StopVtune())
+        ok = JS_FALSE;
+#endif
+    return ok;
 }
 
-JS_PUBLIC_API(void)
-JS_StopProfiling()
+/*
+ * Start or stop whatever platform- and configuration-specific profiling
+ * backends are available.
+ */
+static JSBool
+ControlProfilers(bool toState)
 {
-    Probes::stopProfiling();
+    JSBool ok = JS_TRUE;
+
+    if (! Probes::ProfilingActive && toState) {
+#if defined(MOZ_SHARK) && defined(__APPLE__)
+        if (!Shark::Start()) {
+            UnsafeError("Failed to start Shark");
+            ok = JS_FALSE;
+        }
+#endif
+#ifdef MOZ_CALLGRIND
+        if (! js_StartCallgrind()) {
+            UnsafeError("Failed to start Callgrind");
+            ok = JS_FALSE;
+        }
+#endif
+#ifdef MOZ_VTUNE
+        if (! js_ResumeVtune())
+            ok = JS_FALSE;
+#endif
+    } else if (Probes::ProfilingActive && ! toState) {
+#if defined(MOZ_SHARK) && defined(__APPLE__)
+        Shark::Stop();
+#endif
+#ifdef MOZ_CALLGRIND
+        if (! js_StopCallgrind()) {
+            UnsafeError("failed to stop Callgrind");
+            ok = JS_FALSE;
+        }
+#endif
+#ifdef MOZ_VTUNE
+        if (! js_PauseVtune())
+            ok = JS_FALSE;
+#endif
+    }
+
+    Probes::ProfilingActive = toState;
+
+    return ok;
+}
+
+/*
+ * Pause/resume whatever profiling mechanism is currently compiled
+ * in, if applicable. This will not affect things like dtrace.
+ *
+ * Do not mix calls to these APIs with calls to the individual
+ * profilers' pause/resume functions, because only overall state is
+ * tracked, not the state of each profiler.
+ */
+JS_PUBLIC_API(JSBool)
+JS_PauseProfilers(const char *profileName)
+{
+    return ControlProfilers(false);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_ResumeProfilers(const char *profileName)
+{
+    return ControlProfilers(true);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_DumpProfile(const char *outfile, const char *profileName)
+{
+    JSBool ok = JS_TRUE;
+#ifdef MOZ_CALLGRIND
+    js_DumpCallgrind(outfile);
+#endif
+    return ok;
 }
 
 #ifdef MOZ_PROFILING
 
+struct RequiredStringArg {
+    JSContext *mCx;
+    char *mBytes;
+    RequiredStringArg(JSContext *cx, uintN argc, jsval *vp, size_t argi, const char *caller)
+        : mCx(cx), mBytes(NULL)
+    {
+        if (argc <= argi) {
+            JS_ReportError(cx, "%s: not enough arguments", caller);
+        } else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) {
+            JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
+        } else {
+            mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi]));
+        }
+    }
+    operator void*() {
+        return (void*) mBytes;
+    }
+    ~RequiredStringArg() {
+        if (mBytes)
+            mCx->free_(mBytes);
+    }
+};
+
 static JSBool
 StartProfiling(JSContext *cx, uintN argc, jsval *vp)
 {
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling()));
-    return true;
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes)));
+    return JS_TRUE;
 }
 
 static JSBool
 StopProfiling(JSContext *cx, uintN argc, jsval *vp)
 {
-    JS_StopProfiling();
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes)));
+    return JS_TRUE;
+}
+
+static JSBool
+PauseProfilers(JSContext *cx, uintN argc, jsval *vp)
+{
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes)));
+    return JS_TRUE;
+}
+
+static JSBool
+ResumeProfilers(JSContext *cx, uintN argc, jsval *vp)
+{
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes)));
+    return JS_TRUE;
+}
+
+/* Usage: DumpProfile([filename[, profileName]]) */
+static JSBool
+DumpProfile(JSContext *cx, uintN argc, jsval *vp)
+{
+    bool ret;
+    if (argc == 0) {
+        ret = JS_DumpProfile(NULL, NULL);
+    } else {
+        RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile");
+        if (!filename)
+            return JS_FALSE;
+
+        if (argc == 1) {
+            ret = JS_DumpProfile(filename.mBytes, NULL);
+        } else {
+            RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile");
+            if (!profileName)
+                return JS_FALSE;
+
+            ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
+        }
+    }
+
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret));
     return true;
 }
 
 #ifdef MOZ_SHARK
 
 static JSBool
 IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp)
 {
     JS_SET_RVAL(cx, vp, JSVAL_TRUE);
     return true;
 }
 
 #endif
 
+#ifdef MOZ_CALLGRIND
+static JSBool
+StartCallgrind(JSContext *cx, uintN argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind()));
+    return JS_TRUE;
+}
+
+static JSBool
+StopCallgrind(JSContext *cx, uintN argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind()));
+    return JS_TRUE;
+}
+
+static JSBool
+DumpCallgrind(JSContext *cx, uintN argc, jsval *vp)
+{
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind");
+    if (!outFile)
+        return JS_FALSE;
+
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes)));
+    return JS_TRUE;
+}    
+#endif
+
+#ifdef MOZ_VTUNE
+static JSBool
+StartVtune(JSContext *cx, uintN argc, jsval *vp)
+{
+    RequiredStringArg profileName(cx, argc, vp, 0, "startVtune");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartVtune(profileName.mBytes)));
+    return JS_TRUE;
+}
+
+static JSBool
+StopVtune(JSContext *cx, uintN argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopVtune()));
+    return JS_TRUE;
+}
+
+static JSBool
+PauseVtune(JSContext *cx, uintN argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_PauseVtune()));
+    return JS_TRUE;
+}
+
+static JSBool
+ResumeVtune(JSContext *cx, uintN argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_ResumeVtune()));
+    return JS_TRUE;
+}
+#endif
+
 static JSFunctionSpec profiling_functions[] = {
-    JS_FN("startProfiling",  StartProfiling,      0,0),
-    JS_FN("stopProfiling",   StopProfiling,       0,0),
+    JS_FN("startProfiling",  StartProfiling,      1,0),
+    JS_FN("stopProfiling",   StopProfiling,       1,0),
+    JS_FN("pauseProfilers",  PauseProfilers,      1,0),
+    JS_FN("resumeProfilers", ResumeProfilers,     1,0),
+    JS_FN("dumpProfile",     DumpProfile,         2,0),
 #ifdef MOZ_SHARK
     /* Keep users of the old shark API happy. */
     JS_FN("connectShark",    IgnoreAndReturnTrue, 0,0),
     JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
     JS_FN("startShark",      StartProfiling,      0,0),
     JS_FN("stopShark",       StopProfiling,       0,0),
 #endif
+#ifdef MOZ_CALLGRIND
+    JS_FN("startCallgrind", StartCallgrind,       0,0),
+    JS_FN("stopCallgrind",  StopCallgrind,        0,0),
+    JS_FN("dumpCallgrind",  DumpCallgrind,        1,0),
+#endif
+#ifdef MOZ_VTUNE
+    JS_FN("startVtune",     js_StartVtune,        1,0),
+    JS_FN("stopVtune",      js_StopVtune,         0,0),
+    JS_FN("pauseVtune",     js_PauseVtune,        0,0),
+    JS_FN("resumeVtune",    js_ResumeVtune,       0,0),
+#endif
     JS_FS_END
 };
 
 #endif
 
 JS_PUBLIC_API(JSBool)
 JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj)
 {
@@ -1279,50 +1582,40 @@ JS_DefineProfilingFunctions(JSContext *c
 #endif
 }
 
 #ifdef MOZ_CALLGRIND
 
 #include <valgrind/callgrind.h>
 
 JS_FRIEND_API(JSBool)
-js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp)
+js_StartCallgrind()
 {
     CALLGRIND_START_INSTRUMENTATION;
     CALLGRIND_ZERO_STATS;
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
-}
-
-JS_FRIEND_API(JSBool)
-js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp)
-{
-    CALLGRIND_STOP_INSTRUMENTATION;
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
+    return true;
 }
 
 JS_FRIEND_API(JSBool)
-js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp)
+js_StopCallgrind()
 {
-    JSString *str;
+    CALLGRIND_STOP_INSTRUMENTATION;
+    return true;
+}
 
-    jsval *argv = JS_ARGV(cx, vp);
-    if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
-        str = JSVAL_TO_STRING(argv[0]);
-        JSAutoByteString bytes(cx, str);
-        if (!!bytes) {
-            CALLGRIND_DUMP_STATS_AT(bytes.ptr());
-            return JS_TRUE;
-        }
+JS_FRIEND_API(JSBool)
+js_DumpCallgrind(const char *outfile)
+{
+    if (outfile) {
+        CALLGRIND_DUMP_STATS_AT(outfile);
+    } else {
+        CALLGRIND_DUMP_STATS;
     }
-    CALLGRIND_DUMP_STATS;
 
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
+    return true;
 }
 
 #endif /* MOZ_CALLGRIND */
 
 #ifdef MOZ_VTUNE
 #include <VTuneApi.h>
 
 static const char *vtuneErrorMessages[] = {
@@ -1347,18 +1640,18 @@ static const char *vtuneErrorMessages[] 
   "invalid 'event size' field",
   "sampling file already bound",
   "invalid event path",
   "invalid license",
   "invalid 'global options' field",
 
 };
 
-JS_FRIEND_API(JSBool)
-js_StartVtune(JSContext *cx, uintN argc, jsval *vp)
+bool
+js_StartVtune(const char *profileName)
 {
     VTUNE_EVENT events[] = {
         { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
         { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
     };
 
     U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
     char *default_filename = "mozilla-vtune.tb5";
@@ -1375,72 +1668,64 @@ js_StartVtune(JSContext *cx, uintN argc,
         0.1,  /* Sampling interval in ms */
         1,    /* 1 for event-based sampling, 0 for time-based */
 
         n_events,
         events,
         default_filename,
     };
 
-    jsval *argv = JS_ARGV(cx, vp);
-    if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
-        str = JSVAL_TO_STRING(argv[0]);
-        params.tb5Filename = DeflateString(cx, str->chars(), str->length());
+    if (profileName) {
+        char filename[strlen(profileName) + strlen("-vtune.tb5") + 1];
+        snprintf(filename, sizeof(filename), "%s-vtune.tb5", profileName);
+        params.tb5Filename = filename;
     }
 
     status = VTStartSampling(&params);
 
     if (params.tb5Filename != default_filename)
-        cx->free_(params.tb5Filename);
+        Foreground::free_(params.tb5Filename);
 
     if (status != 0) {
         if (status == VTAPI_MULTIPLE_RUNS)
             VTStopSampling(0);
         if (status < sizeof(vtuneErrorMessages))
-            JS_ReportError(cx, "Vtune setup error: %s",
-                           vtuneErrorMessages[status]);
+            UnsafeError("Vtune setup error: %s", vtuneErrorMessages[status]);
         else
-            JS_ReportError(cx, "Vtune setup error: %d",
-                           status);
+            UnsafeError("Vtune setup error: %d", status);
         return false;
     }
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
-JS_FRIEND_API(JSBool)
-js_StopVtune(JSContext *cx, uintN argc, jsval *vp)
+bool
+js_StopVtune()
 {
     U32 status = VTStopSampling(1);
     if (status) {
         if (status < sizeof(vtuneErrorMessages))
-            JS_ReportError(cx, "Vtune shutdown error: %s",
-                           vtuneErrorMessages[status]);
+            UnsafeError("Vtune shutdown error: %s", vtuneErrorMessages[status]);
         else
-            JS_ReportError(cx, "Vtune shutdown error: %d",
-                           status);
+            UnsafeError("Vtune shutdown error: %d", status);
         return false;
     }
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
-JS_FRIEND_API(JSBool)
-js_PauseVtune(JSContext *cx, uintN argc, jsval *vp)
+bool
+js_PauseVtune()
 {
     VTPause();
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
-JS_FRIEND_API(JSBool)
-js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp)
+bool
+js_ResumeVtune()
 {
     VTResume();
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
 #endif /* MOZ_VTUNE */
 
 #ifdef MOZ_TRACEVIS
 /*
  * Ethogram - Javascript wrapper for TraceVis state
@@ -1901,37 +2186,34 @@ JS_PUBLIC_API(JSFunctionCallback)
 JS_GetFunctionCallback(JSContext *cx)
 {
     return cx->functionCallback;
 }
 
 #endif /* MOZ_TRACE_JSCALLS */
 
 JS_PUBLIC_API(void)
-JS_DumpProfile(JSContext *cx, JSScript *script)
+JS_DumpBytecode(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(!cx->runtime->gcRunning);
 
 #if defined(DEBUG)
-    if (script->pcCounters) {
-        // Display hit counts for every JS code line
-        AutoArenaAllocator mark(&cx->tempPool);
-        Sprinter sprinter;
-        INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
+    AutoArenaAllocator mark(&cx->tempPool);
+    Sprinter sprinter;
+    INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
 
-        fprintf(stdout, "--- PC COUNTS %s:%d ---\n", script->filename, script->lineno);
-        js_Disassemble(cx, script, true, &sprinter);
-        fprintf(stdout, "%s\n", sprinter.base);
-        fprintf(stdout, "--- END PC COUNTS %s:%d ---\n", script->filename, script->lineno);
-    }
+    fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno);
+    js_Disassemble(cx, script, true, &sprinter);
+    fprintf(stdout, "%s\n", sprinter.base);
+    fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno);
 #endif
 }
 
 JS_PUBLIC_API(void)
-JS_DumpAllProfiles(JSContext *cx)
+JS_DumpCompartmentBytecode(JSContext *cx)
 {
     for (JSScript *script = (JSScript *) JS_LIST_HEAD(&cx->compartment->scripts);
          script != (JSScript *) &cx->compartment->scripts;
          script = (JSScript *) JS_NEXT_LINK((JSCList *)script))
     {
-        JS_DumpProfile(cx, script);
+        JS_DumpBytecode(cx, script);
     }
 }
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -492,55 +492,102 @@ JS_GetGlobalDebugHooks(JSRuntime *rt);
 
 extern JS_PUBLIC_API(JSDebugHooks *)
 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks);
 
 /* Disable debug hooks for this context. */
 extern JS_PUBLIC_API(JSDebugHooks *)
 JS_ClearContextDebugHooks(JSContext *cx);
 
+/**
+ * Start any profilers that are available and have been configured on for this
+ * platform. This is NOT thread safe.
+ *
+ * The profileName is used by some profilers to describe the current profiling
+ * run. It may be used for part of the filename of the output, but the
+ * specifics depend on the profiler. Many profilers will ignore it. Passing in
+ * NULL is legal; some profilers may use it to output to stdout or similar.
+ *
+ * Returns true if no profilers fail to start.
+ */
 extern JS_PUBLIC_API(JSBool)
-JS_StartProfiling();
+JS_StartProfiling(const char *profileName);
+
+/**
+ * Stop any profilers that were previously started with JS_StartProfiling.
+ * Returns true if no profilers fail to stop.
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_StopProfiling(const char *profileName);
 
-extern JS_PUBLIC_API(void)
-JS_StopProfiling();
+/**
+ * Write the current profile data to the given file, if applicable to whatever
+ * profiler is being used.
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_DumpProfile(const char *outfile, const char *profileName);
 
+/**
+ * Pause currently active profilers (only supported by some profilers). Returns
+ * whether any profilers failed to pause. (Profilers that do not support
+ * pause/resume do not count.)
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_PauseProfilers(const char *profileName);
+
+/**
+ * Resume suspended profilers
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_ResumeProfilers(const char *profileName);
+
+/**
+ * Add various profiling-related functions as properties of the given object.
+ */
 extern JS_PUBLIC_API(JSBool)
 JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj);
 
 /* Defined in vm/Debugger.cpp. */
 extern JS_PUBLIC_API(JSBool)
 JS_DefineDebuggerObject(JSContext *cx, JSObject *obj);
 
+/**
+ * The profiling API calls are not able to report errors, so they use a
+ * thread-unsafe global memory buffer to hold the last error encountered. This
+ * should only be called after something returns false.
+ */
+JS_PUBLIC_API(const char *)
+JS_UnsafeGetLastProfilingError();
+
 #ifdef MOZ_CALLGRIND
 
 extern JS_FRIEND_API(JSBool)
-js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp);
+js_StopCallgrind();
 
 extern JS_FRIEND_API(JSBool)
-js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp);
+js_StartCallgrind();
 
 extern JS_FRIEND_API(JSBool)
-js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp);
+js_DumpCallgrind(const char *outfile);
 
 #endif /* MOZ_CALLGRIND */
 
 #ifdef MOZ_VTUNE
 
-extern JS_FRIEND_API(JSBool)
-js_StartVtune(JSContext *cx, uintN argc, jsval *vp);
+extern JS_FRIEND_API(bool)
+js_StartVtune(const char *profileName);
 
-extern JS_FRIEND_API(JSBool)
-js_StopVtune(JSContext *cx, uintN argc, jsval *vp);
+extern JS_FRIEND_API(bool)
+js_StopVtune();
 
-extern JS_FRIEND_API(JSBool)
-js_PauseVtune(JSContext *cx, uintN argc, jsval *vp);
+extern JS_FRIEND_API(bool)
+js_PauseVtune();
 
-extern JS_FRIEND_API(JSBool)
-js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp);
+extern JS_FRIEND_API(bool)
+js_ResumeVtune();
 
 #endif /* MOZ_VTUNE */
 
 #ifdef MOZ_TRACEVIS
 extern JS_FRIEND_API(JSBool)
 js_InitEthogram(JSContext *cx, uintN argc, jsval *vp);
 extern JS_FRIEND_API(JSBool)
 js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp);
@@ -564,16 +611,16 @@ typedef void (*JSFunctionCallback)(const
 extern JS_PUBLIC_API(void)
 JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb);
 
 extern JS_PUBLIC_API(JSFunctionCallback)
 JS_GetFunctionCallback(JSContext *cx);
 #endif /* MOZ_TRACE_JSCALLS */
 
 extern JS_PUBLIC_API(void)
-JS_DumpProfile(JSContext *cx, JSScript *script);
+JS_DumpBytecode(JSContext *cx, JSScript *script);
 
 extern JS_PUBLIC_API(void)
-JS_DumpAllProfiles(JSContext *cx);
+JS_DumpCompartmentBytecode(JSContext *cx);
 
 JS_END_EXTERN_C
 
 #endif /* jsdbgapi_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2197,17 +2197,16 @@ SweepCompartments(JSContext *cx, JSGCInv
 
     while (read < end) {
         JSCompartment *compartment = *read++;
 
         if (!compartment->hold &&
             (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT))
         {
             compartment->freeLists.checkEmpty();
-            Probes::GCEndSweepPhase(compartment);
             if (callback)
                 JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY));
             if (compartment->principals)
                 JSPRINCIPALS_DROP(cx, compartment->principals);
             cx->delete_(compartment);
             continue;
         }
         *write++ = compartment;
@@ -2333,23 +2332,25 @@ MarkAndSweep(JSContext *cx, JSCompartmen
     /*
      * We finalize objects before other GC things to ensure that object's finalizer
      * can access them even if they will be freed. Sweep the runtime's property trees
      * after finalizing objects, in case any had watchpoints referencing tree nodes.
      * Do this before sweeping compartments, so that we sweep all shapes in
      * unreachable compartments.
      */
     if (comp) {
+        Probes::GCStartSweepPhase(comp);
         comp->sweep(cx, 0);
         comp->finalizeObjectArenaLists(cx);
         GCTIMESTAMP(sweepObjectEnd);
         comp->finalizeStringArenaLists(cx);
         GCTIMESTAMP(sweepStringEnd);
         comp->finalizeShapeArenaLists(cx);
         GCTIMESTAMP(sweepShapeEnd);
+        Probes::GCEndSweepPhase(comp);
     } else {
         /*
          * Some sweeping is not compartment-specific. Start a NULL-compartment
          * phase to demarcate all of that. (The compartment sweeps will nest
          * within.)
          */
         Probes::GCStartSweepPhase(NULL);
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -533,19 +533,17 @@ js_OnUnknownMethod(JSContext *cx, Value 
         return false;
     if (tvr.value().isPrimitive()) {
         vp[0] = tvr.value();
     } else {
 #if JS_HAS_XML_SUPPORT
         /* Extract the function name from function::name qname. */
         if (vp[0].isObject()) {
             obj = &vp[0].toObject();
-            if (!js_IsFunctionQName(cx, obj, &id))
-                return false;
-            if (!JSID_IS_VOID(id))
+            if (js_GetLocalNameFromFunctionQName(obj, &id, cx))
                 vp[0] = IdToValue(id);
         }
 #endif
         obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
         if (!obj)
             return false;
 
         obj->init(cx, &js_NoSuchMethodClass, NULL, NULL, NULL, false);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1078,19 +1078,19 @@ struct JSObject : js::gc::Cell {
 
     inline JSLinearString *getNameURI() const;
     inline jsval getNameURIVal() const;
     inline void setNameURI(JSLinearString *uri);
 
     inline jsval getNamespaceDeclared() const;
     inline void setNamespaceDeclared(jsval decl);
 
-    inline JSLinearString *getQNameLocalName() const;
+    inline JSAtom *getQNameLocalName() const;
     inline jsval getQNameLocalNameVal() const;
-    inline void setQNameLocalName(JSLinearString *name);
+    inline void setQNameLocalName(JSAtom *name);
 
     /*
      * Proxy-specific getters and setters.
      */
 
     inline js::JSProxyHandler *getProxyHandler() const;
     inline const js::Value &getProxyPrivate() const;
     inline void setProxyPrivate(const js::Value &priv);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -640,33 +640,33 @@ JSObject::getNamespaceDeclared() const
 
 inline void
 JSObject::setNamespaceDeclared(jsval decl)
 {
     JS_ASSERT(isNamespace());
     setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl));
 }
 
-inline JSLinearString *
+inline JSAtom *
 JSObject::getQNameLocalName() const
 {
     JS_ASSERT(isQName());
     const js::Value &v = getSlot(JSSLOT_QNAME_LOCAL_NAME);
-    return !v.isUndefined() ? &v.toString()->asLinear() : NULL;
+    return !v.isUndefined() ? &v.toString()->asAtom() : NULL;
 }
 
 inline jsval
 JSObject::getQNameLocalNameVal() const
 {
     JS_ASSERT(isQName());
     return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME));
 }
 
 inline void
-JSObject::setQNameLocalName(JSLinearString *name)
+JSObject::setQNameLocalName(JSAtom *name)
 {
     JS_ASSERT(isQName());
     setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue());
 }
 
 inline JSObject *
 JSObject::getWithThis() const
 {
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -544,17 +544,17 @@ extern size_t
 GetBytecodeLength(JSContext *cx, JSScript *script, jsbytecode *pc);
 
 extern bool
 IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset);
 
 inline bool
 FlowsIntoNext(JSOp op)
 {
-    // JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL.
+    /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */
     return op != JSOP_STOP && op != JSOP_RETURN && op != JSOP_RETRVAL && op != JSOP_THROW &&
            op != JSOP_GOTO && op != JSOP_GOTOX && op != JSOP_RETSUB;
 }
 
 }
 #endif
 
 #if defined(DEBUG) && defined(__cplusplus)
--- a/js/src/jsprobes.cpp
+++ b/js/src/jsprobes.cpp
@@ -48,154 +48,98 @@
 #include "jsdbgapi.h"
 #include "jsfun.h"
 #include "jsinterp.h"
 #include "jsobj.h"
 #include "jsscript.h"
 #include "jsstaticcheck.h"
 #include "jsstr.h"
 
-#ifdef __APPLE__
-#include "sharkctl.h"
-#endif
-
 #include "jsprobes.h"
 #include <sys/types.h>
 
 #define TYPEOF(cx,v)    (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
 
 using namespace js;
 
 const char Probes::nullName[] = "(null)";
 const char Probes::anonymousName[] = "(anonymous)";
 
 bool Probes::ProfilingActive = true;
 
-bool
-Probes::controlProfilers(JSContext *cx, bool toState)
+#ifdef INCLUDE_MOZILLA_DTRACE
+static const char *
+ScriptFilename(const JSScript *script)
 {
-    JSBool ok = JS_TRUE;
-#if defined(MOZ_CALLGRIND) || defined(MOZ_VTUNE)
-    jsval dummy;
-#endif
+    if (!script)
+        return Probes::nullName;
+    if (!script->filename)
+        return Probes::anonymousName;
+    return script->filename;
+}
+
+static const char *
+FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes)
+{
+    if (!fun)
+        return Probes::nullName;
+    JSAtom *atom = const_cast<JSAtom*>(fun->atom);
+    if (!atom)
+        return Probes::anonymousName;
+    return bytes->encode(cx, atom) ? bytes->ptr() : Probes::nullName;
+}
 
-    if (! ProfilingActive && toState) {
-#if defined(MOZ_SHARK) && defined(__APPLE__)
-        if (!Shark::Start())
-            ok = JS_FALSE;
-#endif
-#ifdef MOZ_CALLGRIND
-        if (! js_StartCallgrind(cx, 0, &dummy))
-            ok = JS_FALSE;
-#endif
-#ifdef MOZ_VTUNE
-        if (! js_ResumeVtune(cx, 0, &dummy))
-            ok = JS_FALSE;
-#endif
-    } else if (ProfilingActive && ! toState) {
-#if defined(MOZ_SHARK) && defined(__APPLE__)
-        Shark::Stop();
-#endif
-#ifdef MOZ_CALLGRIND
-        if (! js_StopCallgrind(cx, 0, &dummy))
-            ok = JS_FALSE;
-#endif
-#ifdef MOZ_VTUNE
-        if (! js_PauseVtune(cx, 0, &dummy))
-            ok = JS_FALSE;
-#endif
-    }
+static const char *
+FunctionClassname(const JSFunction *fun)
+{
+    if (!fun || FUN_INTERPRETED(fun))
+        return Probes::nullName;
+    if (!(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun))
+        return (char *)FUN_CLASP(fun)->name;
+    return Probes::nullName;
+}
 
-    ProfilingActive = toState;
-
-    return ok;
+/*
+ * These functions call the DTrace macros for the JavaScript USDT probes.
+ * Originally this code was inlined in the JavaScript code; however since
+ * a number of operations are called, these have been placed into functions
+ * to reduce any negative compiler optimization effect that the addition of
+ * a number of usually unused lines of code would cause.
+ */
+void
+Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
+{
+    JSAutoByteString funNameBytes;
+    JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun),
+                              FunctionName(cx, fun, &funNameBytes));
 }
 
 void
-Probes::current_location(JSContext *cx, int* lineno, char const **filename)
+Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
+{
+    JSAutoByteString funNameBytes;
+    JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun),
+                               FunctionName(cx, fun, &funNameBytes));
+}
+#endif
+
+#ifdef MOZ_ETW
+static void
+current_location(JSContext *cx, int* lineno, char const **filename)
 {
     JSScript *script = js_GetCurrentScript(cx);
     if (! script) {
         *lineno = -1;
         *filename = "(uninitialized)";
         return;
     }
     *lineno = js_PCToLineNumber(cx, script, js_GetCurrentBytecodePC(cx));
     *filename = ScriptFilename(script);
 }
 
-const char *
-Probes::FunctionClassname(const JSFunction *fun)
-{
-    return (fun && !FUN_INTERPRETED(fun) && !(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun))
-           ? (char *)FUN_CLASP(fun)->name
-           : nullName;
-}
-
-const char *
-Probes::ScriptFilename(JSScript *script)
-{
-    return (script && script->filename) ? (char *)script->filename : nullName;
-}
-
-int
-Probes::FunctionLineNumber(JSContext *cx, const JSFunction *fun)
-{
-    if (fun && FUN_INTERPRETED(fun))
-        return (int) JS_GetScriptBaseLineNumber(cx, FUN_SCRIPT(fun));
-
-    return 0;
-}
-
-#ifdef INCLUDE_MOZILLA_DTRACE
-/*
- * These functions call the DTrace macros for the JavaScript USDT probes.
- * Originally this code was inlined in the JavaScript code; however since
- * a number of operations are called, these have been placed into functions
- * to reduce any negative compiler optimization effect that the addition of
- * a number of usually unused lines of code would cause.
- */
-void
-Probes::enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script)
-{
-    JSAutoByteString funNameBytes;
-    JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun),
-                              FunctionName(cx, fun, &funNameBytes));
-}
-
-void
-Probes::handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script)
-{
-    JSAutoByteString funNameBytes;
-    JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun),
-                               FunctionName(cx, fun, &funNameBytes));
-}
-
-#endif
-
-bool
-Probes::startProfiling()
-{
-#if defined(MOZ_SHARK) && defined(__APPLE__)
-    if (Shark::Start())
-        return true;
-#endif
-    return false;
-}
-
-void
-Probes::stopProfiling()
-{
-#if defined(MOZ_SHARK) && defined(__APPLE__)
-    Shark::Stop();
-#endif
-}
-
-
-#ifdef MOZ_ETW
 /*
  * ETW (Event Tracing for Windows)
  *
  * These are here rather than in the .h file to avoid having to include
  * windows.h in a header.
  */
 bool
 Probes::ETWCallTrackingActive(JSContext *cx)
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -9,20 +9,24 @@
  * 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.
  *
- * Copyright (C) 2007  Sun Microsystems, Inc. All Rights Reserved.
+ * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
+ * June 12, 2009.
+ *
+ * The Initial Developer of the Original Code is
+ *   the Mozilla Corporation.
  *
  * Contributor(s):
- *      Brendan Eich <brendan@mozilla.org>
+ *      Steve Fink <sfink@mozilla.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -39,178 +43,214 @@
 #ifdef INCLUDE_MOZILLA_DTRACE
 #include "javascript-trace.h"
 #endif
 #include "jspubtd.h"
 #include "jsprvtd.h"
 
 namespace js {
 
-class Probes {
-    static bool ProfilingActive;
-    static bool controlProfilers(JSContext *cx, bool toState);
-
-    static const char nullName[];
-    static const char anonymousName[];
-
-    static const char *FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes)
-    {
-        if (!fun)
-            return nullName;
-        JSAtom *atom = const_cast<JSAtom*>(fun->atom);
-        if (!atom)
-            return anonymousName;
-        return bytes->encode(cx, atom) ? bytes->ptr() : nullName;
-    }
+namespace Probes {
 
-    static const char *ScriptFilename(const JSScript *script) {
-        if (! script)
-            return "(null)";
-        if (! script->filename)
-            return "(anonymous)";
-        return script->filename;
-    }
+/*
+ * Static probes
+ *
+ * The probe points defined in this file are scattered around the SpiderMonkey
+ * source tree. The presence of Probes::someEvent() means that someEvent is
+ * about to happen or has happened. To the extent possible, probes should be
+ * inserted in all paths associated with a given event, regardless of the
+ * active runmode (interpreter/traceJIT/methodJIT/ionJIT).
+ *
+ * When a probe fires, it is handled by any probe handling backends that have
+ * been compiled in. By default, most probes do nothing or at least do nothing
+ * expensive, so the presence of the probe should have negligible effect on
+ * running time. (Probes in slow paths may do something by default, as long as
+ * there is no noticeable slowdown.)
+ *
+ * For some probes, the mere existence of the probe is too expensive even if it
+ * does nothing when called. For example, just having consistent information
+ * available for a function call entry/exit probe causes the JITs to
+ * de-optimize function calls. In those cases, the JITs may query at compile
+ * time whether a probe is desired, and omit the probe invocation if not. If a
+ * probe is runtime-disabled at compilation time, it is not guaranteed to fire
+ * within a compiled function if it is later enabled.
+ *
+ * Not all backends handle all of the probes listed here.
+ */
 
-    static const char *ObjectClassname(JSObject *obj) {
-        if (! obj)
-            return "(null object)";
-        Class *clasp = obj->getClass();
-        if (! clasp)
-            return "(null)";
-        const char *class_name = clasp->name;
-        if (! class_name)
-            return "(null class name)";
-        return class_name;
-    }
+/*
+ * Internal use only: remember whether "profiling", whatever that means, is
+ * currently active. Used for state management.
+ */
+extern bool ProfilingActive;
+
+extern const char nullName[];
+extern const char anonymousName[];
 
-    static void current_location(JSContext *cx, int* lineno, char const **filename);
+/* JSRuntime created, with currently valid fields */
+bool createRuntime(JSRuntime *rt);
+
+/* JSRuntime about to be destroyed */
+bool destroyRuntime(JSRuntime *rt);
+
+/* Total JS engine shutdown */
+bool shutdown();
 
-    static const char *FunctionClassname(const JSFunction *fun);
-    static const char *ScriptFilename(JSScript *script);
-    static int FunctionLineNumber(JSContext *cx, const JSFunction *fun);
+/*
+ * Test whether we are tracking JS function call enter/exit. The JITs use this
+ * to decide whether they can optimize in a way that would prevent probes from
+ * firing.
+ */
+bool callTrackingActive(JSContext *);
+
+/* Entering a JS function */
+bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1);
 
-    static void enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script);
-    static void handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script);
-    static void finalizeObjectImpl(JSObject *obj);
-  public:
-    static bool createRuntime(JSRuntime *rt);
-    static bool destroyRuntime(JSRuntime *rt);
-    static bool shutdown();
+/* About to leave a JS function */
+bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0);
+
+/* Executing a script */
+bool startExecution(JSContext *cx, JSScript *script);
+
+/* Script has completed execution */
+bool stopExecution(JSContext *cx, JSScript *script);
+
+/* Heap has been resized */
+bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
 
-    /*
-     * Pause/resume whatever profiling mechanism is currently compiled
-     * in, if applicable. This will not affect things like dtrace.
-     *
-     * Do not mix calls to these APIs with calls to the individual
-     * profilers' pase/resume functions, because only overall state is
-     * tracked, not the state of each profiler.
-     *
-     * Return the previous state.
-     */
-    static bool pauseProfilers(JSContext *cx) {
-        bool prevState = ProfilingActive;
-        controlProfilers(cx, false);
-        return prevState;
-    }
-    static bool resumeProfilers(JSContext *cx) {
-        bool prevState = ProfilingActive;
-        controlProfilers(cx, true);
-        return prevState;
-    }
+/*
+ * Object has been created. |obj| must exist (its class and size are read)
+ */
+bool createObject(JSContext *cx, JSObject *obj);
+
+/* Object has been resized */
+bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
+
+/*
+ * Object is about to be finalized. |obj| must still exist (its class is
+ * read)
+ */
+bool finalizeObject(JSObject *obj);
 
-    static bool callTrackingActive(JSContext *);
+/*
+ * String has been created.
+ *
+ * |string|'s content is not (yet) valid. |length| is the length of the string
+ * and does not imply anything about the amount of storage consumed to store
+ * the string. (It may be a short string, an external string, or a rope, and
+ * the encoding is not taken into consideration.)
+ */
+bool createString(JSContext *cx, JSString *string, size_t length);
 
-    static bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1);
-    static bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0);
-
-    static bool startExecution(JSContext *cx, JSScript *script);
-    static bool stopExecution(JSContext *cx, JSScript *script);
+/*
+ * String is about to be finalized
+ *
+ * |string| must still have a valid length.
+ */
+bool finalizeString(JSString *string);
 
-    static bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
+/* Script is about to be compiled */
+bool compileScriptBegin(JSContext *cx, const char *filename, int lineno);
 
-    /* |obj| must exist (its class and size are computed) */
-    static bool createObject(JSContext *cx, JSObject *obj);
+/* Script has just finished compilation */
+bool compileScriptEnd(JSContext *cx, JSScript *script, const char *filename, int lineno);
 
-    static bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
+/* About to make a call from JS into native code */
+bool calloutBegin(JSContext *cx, JSFunction *fun);
 
-    /* |obj| must still exist (its class is accessed) */
-    static bool finalizeObject(JSObject *obj);
+/* Native code called by JS has terminated */
+bool calloutEnd(JSContext *cx, JSFunction *fun);
+
+/* Unimplemented */
+bool acquireMemory(JSContext *cx, void *address, size_t nbytes);
+bool releaseMemory(JSContext *cx, void *address, size_t nbytes);
 
-    /*
-     * |string| does not need to contain any content yet; only its
-     * pointer value is used. |length| is the length of the string and
-     * does not imply anything about the amount of storage consumed to
-     * store the string. (It may be a short string, an external
-     * string, or a rope, and the encoding is not taken into
-     * consideration.)
-     */
-    static bool createString(JSContext *cx, JSString *string, size_t length);
+/*
+ * Garbage collection probes
+ *
+ * GC timing is tricky and at the time of this writing is changing frequently.
+ * GCStart(NULL)/GCEnd(NULL) are intended to bracket the entire garbage
+ * collection (either global or single-compartment), but a separate thread may
+ * continue doing work after GCEnd.
+ *
+ * Multiple compartments' GC will be interleaved during a global collection
+ * (eg, compartment 1 starts, compartment 2 starts, compartment 1 ends, ...)
+ */
+bool GCStart(JSCompartment *compartment);
+bool GCEnd(JSCompartment *compartment);
 
-    /*
-     * |string| must still have a valid length.
-     */
-    static bool finalizeString(JSString *string);
+bool GCStartMarkPhase(JSCompartment *compartment);
+bool GCEndMarkPhase(JSCompartment *compartment);
 
-    static bool compileScriptBegin(JSContext *cx, const char *filename, int lineno);
-    static bool compileScriptEnd(JSContext *cx, JSScript *script, const char *filename, int lineno);
+bool GCStartSweepPhase(JSCompartment *compartment);
+bool GCEndSweepPhase(JSCompartment *compartment);
 
-    static bool calloutBegin(JSContext *cx, JSFunction *fun);
-    static bool calloutEnd(JSContext *cx, JSFunction *fun);
-
-    static bool acquireMemory(JSContext *cx, void *address, size_t nbytes);
-    static bool releaseMemory(JSContext *cx, void *address, size_t nbytes);
-
-    static bool GCStart(JSCompartment *compartment);
-    static bool GCEnd(JSCompartment *compartment);
-    static bool GCStartMarkPhase(JSCompartment *compartment);
+/*
+ * Various APIs for inserting custom probe points. These might be used to mark
+ * when something starts and stops, or for various other purposes the user has
+ * in mind. These are useful to export to JS so that JS code can mark
+ * application-meaningful events and phases of execution.
+ *
+ * Not all backends support these.
+ */
+bool CustomMark(JSString *string);
+bool CustomMark(const char *string);
+bool CustomMark(int marker);
 
-    static bool GCEndMarkPhase(JSCompartment *compartment);
-    static bool GCStartSweepPhase(JSCompartment *compartment);
-    static bool GCEndSweepPhase(JSCompartment *compartment);
+/*
+ * Internal: DTrace-specific functions to be called during Probes::enterJSFun
+ * and Probes::exitJSFun. These will not be inlined, but the argument
+ * marshalling required for these probe points is expensive enough that it
+ * shouldn't really matter.
+ */
+void DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script);
+void DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script);
 
-    static bool CustomMark(JSString *string);
-    static bool CustomMark(const char *string);
-    static bool CustomMark(int marker);
-
-    static bool startProfiling();
-    static void stopProfiling();
-
+/*
+ * Internal: ETW-specific probe functions
+ */
 #ifdef MOZ_ETW
-    // ETW Handlers
-    static bool ETWCreateRuntime(JSRuntime *rt);
-    static bool ETWDestroyRuntime(JSRuntime *rt);
-    static bool ETWShutdown();
-    static bool ETWCallTrackingActive(JSContext *cx);
-    static bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter);
-    static bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter);
-    static bool ETWCreateObject(JSContext *cx, JSObject *obj);
-    static bool ETWFinalizeObject(JSObject *obj);
-    static bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
-    static bool ETWCreateString(JSContext *cx, JSString *string, size_t length);
-    static bool ETWFinalizeString(JSString *string);
-    static bool ETWCompileScriptBegin(const char *filename, int lineno);
-    static bool ETWCompileScriptEnd(const char *filename, int lineno);
-    static bool ETWCalloutBegin(JSContext *cx, JSFunction *fun);
-    static bool ETWCalloutEnd(JSContext *cx, JSFunction *fun);
-    static bool ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes);
-    static bool ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes);
-    static bool ETWGCStart(JSCompartment *compartment);
-    static bool ETWGCEnd(JSCompartment *compartment);
-    static bool ETWGCStartMarkPhase(JSCompartment *compartment);
-    static bool ETWGCEndMarkPhase(JSCompartment *compartment);
-    static bool ETWGCStartSweepPhase(JSCompartment *compartment);
-    static bool ETWGCEndSweepPhase(JSCompartment *compartment);
-    static bool ETWCustomMark(JSString *string);
-    static bool ETWCustomMark(const char *string);
-    static bool ETWCustomMark(int marker);
-    static bool ETWStartExecution(JSContext *cx, JSScript *script);
-    static bool ETWStopExecution(JSContext *cx, JSScript *script);
-    static bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
+// ETW Handlers
+bool ETWCreateRuntime(JSRuntime *rt);
+bool ETWDestroyRuntime(JSRuntime *rt);
+bool ETWShutdown();
+bool ETWCallTrackingActive(JSContext *cx);
+bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter);
+bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter);
+bool ETWCreateObject(JSContext *cx, JSObject *obj);
+bool ETWFinalizeObject(JSObject *obj);
+bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
+bool ETWCreateString(JSContext *cx, JSString *string, size_t length);
+bool ETWFinalizeString(JSString *string);
+bool ETWCompileScriptBegin(const char *filename, int lineno);
+bool ETWCompileScriptEnd(const char *filename, int lineno);
+bool ETWCalloutBegin(JSContext *cx, JSFunction *fun);
+bool ETWCalloutEnd(JSContext *cx, JSFunction *fun);
+bool ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes);
+bool ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes);
+bool ETWGCStart(JSCompartment *compartment);
+bool ETWGCEnd(JSCompartment *compartment);
+bool ETWGCStartMarkPhase(JSCompartment *compartment);
+bool ETWGCEndMarkPhase(JSCompartment *compartment);
+bool ETWGCStartSweepPhase(JSCompartment *compartment);
+bool ETWGCEndSweepPhase(JSCompartment *compartment);
+bool ETWCustomMark(JSString *string);
+bool ETWCustomMark(const char *string);
+bool ETWCustomMark(int marker);
+bool ETWStartExecution(JSContext *cx, JSScript *script);
+bool ETWStopExecution(JSContext *cx, JSScript *script);
+bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
 #endif
-};
+
+} /* namespace Probes */
+
+/*
+ * Probe handlers are implemented inline for minimal performance impact,
+ * especially important when no backends are enabled.
+ */
 
 inline bool
 Probes::createRuntime(JSRuntime *rt)
 {
     bool ok = true;
 #ifdef MOZ_ETW
     if (!ETWCreateRuntime(rt))
         ok = false;
@@ -253,40 +293,23 @@ Probes::callTrackingActive(JSContext *cx
 #endif
 #ifdef MOZ_ETW
     if (ProfilingActive && ETWCallTrackingActive(cx))
         return true;
 #endif
     return false;
 }
 
-extern inline JS_FRIEND_API(JSBool)
-js_PauseProfilers(JSContext *cx, uintN argc, jsval *vp)
-{
-    Probes::pauseProfilers(cx);
-    return JS_TRUE;
-}
-
-extern inline JS_FRIEND_API(JSBool)
-js_ResumeProfilers(JSContext *cx, uintN argc, jsval *vp)
-{
-    Probes::resumeProfilers(cx);
-    return JS_TRUE;
-}
-
-extern JS_FRIEND_API(JSBool)
-js_ResumeProfilers(JSContext *cx, uintN argc, jsval *vp);
-
 inline bool
 Probes::enterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
 {
     bool ok = true;
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
-        enterJSFunImpl(cx, fun, script);
+        DTraceEnterJSFun(cx, fun, script);
 #endif
 #ifdef MOZ_TRACE_JSCALLS
     cx->doFunctionCallback(fun, script, counter);
 #endif
 #ifdef MOZ_ETW
     if (ProfilingActive && !ETWEnterJSFun(cx, fun, script, counter))
         ok = false;
 #endif
@@ -296,17 +319,17 @@ Probes::enterJSFun(JSContext *cx, JSFunc
 
 inline bool
 Probes::exitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
-        handleFunctionReturn(cx, fun, script);
+        DTraceExitJSFun(cx, fun, script);
 #endif
 #ifdef MOZ_TRACE_JSCALLS
     if (counter > 0)
         counter = -counter;
     cx->doFunctionCallback(fun, script, counter);
 #endif
 #ifdef MOZ_ETW
     if (ProfilingActive && !ETWExitJSFun(cx, fun, script, counter))
@@ -324,16 +347,30 @@ Probes::resizeHeap(JSCompartment *compar
 #ifdef MOZ_ETW
     if (ProfilingActive && !ETWResizeHeap(compartment, oldSize, newSize))
         ok = false;
 #endif
 
     return ok;
 }
 
+#ifdef INCLUDE_MOZILLA_DTRACE
+static const char *ObjectClassname(JSObject *obj) {
+    if (! obj)
+        return "(null object)";
+    Class *clasp = obj->getClass();
+    if (! clasp)
+        return "(null)";
+    const char *class_name = clasp->name;
+    if (! class_name)
+        return "(null class name)";
+    return class_name;
+}
+#endif
+
 inline bool
 Probes::createObject(JSContext *cx, JSObject *obj)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_OBJECT_CREATE_ENABLED())
         JAVASCRIPT_OBJECT_CREATE(ObjectClassname(obj), (uintptr_t)obj);
@@ -652,10 +689,15 @@ struct AutoFunctionCallProbe {
     }
 
     ~AutoFunctionCallProbe() {
         Probes::exitJSFun(cx, fun, script);
     }
 };
 
 } /* namespace js */
-    
+
+/*
+ * Internal functions for controlling various profilers. The profiler-specific
+ * implementations of these are mostly in jsdbgapi.cpp.
+ */
+
 #endif /* _JSPROBES_H */
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -466,17 +466,17 @@ qname_toString(JSContext *cx, uintN argc
 static JSFunctionSpec qname_methods[] = {
     JS_FN(js_toString_str,  qname_toString,    0,0),
     JS_FS_END
 };
 
 
 static bool
 InitXMLQName(JSContext *cx, JSObject *obj, JSLinearString *uri, JSLinearString *prefix,
-             JSLinearString *localName)
+             JSAtom *localName)
 {
     JS_ASSERT(obj->isQName());
     JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
     JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
     JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal()));
 
     /* Per ECMA-357, 13.3.5, these properties must be "own". */
     if (!JS_DefineProperties(cx, obj, qname_props))
@@ -488,29 +488,29 @@ InitXMLQName(JSContext *cx, JSObject *ob
         obj->setNamePrefix(prefix);
     if (localName)
         obj->setQNameLocalName(localName);
     return true;
 }
 
 static JSObject *
 NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
-            JSLinearString *localName)
+            JSAtom *localName)
 {
     JSObject *obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
     if (!obj)
         return NULL;
     if (!InitXMLQName(cx, obj, uri, prefix, localName))
         return NULL;
     return obj;
 }
 
 static JSObject *
 NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
-                    JSLinearString *localName)
+                    JSAtom *localName)
 {
     /*
      * AttributeName is an internal anonymous class which instances are not
      * exposed to scripts.
      */
     JSObject *parent = GetGlobalForScopeChain(cx);
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AttributeNameClass, NULL, parent);
     if (!obj)
@@ -714,19 +714,20 @@ Namespace(JSContext *cx, uintN argc, Val
  * if argc is 1 and argv[0] is JSVAL_VOID.
  */
 static JSBool
 QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval)
 {
     jsval nameval, nsval;
     JSBool isQName, isNamespace;
     JSObject *qn;
-    JSLinearString *uri, *prefix, *name;
+    JSLinearString *uri, *prefix;
     JSObject *obj2;
 
+    JSAtom *name;
     if (argc <= 0) {
         nameval = JSVAL_VOID;
         isQName = JS_FALSE;
     } else {
         nameval = argv[argc > 1];
         isQName =
             !JSVAL_IS_PRIMITIVE(nameval) &&
             JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass;
@@ -761,23 +762,18 @@ QNameHelper(JSContext *cx, JSObject *obj
         nameval = qn->getQNameLocalNameVal();
     }
 
     if (argc == 0) {
         name = cx->runtime->emptyString;
     } else if (argc < 0) {
         name = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
     } else {
-        JSString *str = js_ValueToString(cx, Valueify(nameval));
-        if (!str)
-            return JS_FALSE;
-        name = str->ensureLinear(cx);
-        if (!name)
-            return JS_FALSE;
-        argv[argc > 1] = STRING_TO_JSVAL(name);
+        if (!js_ValueToAtom(cx, Valueify(nameval), &name))
+            return false;
     }
 
     if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
         nsval = argv[0];
     } else if (IS_STAR(name)) {
         nsval = JSVAL_NULL;
     } else {
         if (!js_GetDefaultXMLNamespace(cx, &nsval))
@@ -1161,30 +1157,32 @@ HAS_NS_AFTER_XML(const jschar *chars)
 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
 
 static JSObject *
 ParseNodeToQName(Parser *parser, JSParseNode *pn,
                  JSXMLArray *inScopeNSes, JSBool isAttributeName)
 {
     JSContext *cx = parser->context;
-    JSLinearString *str, *uri, *prefix, *localName;
+    JSLinearString *uri, *prefix;
     size_t length, offset;
     const jschar *start, *limit, *colon;
     uint32 n;
     JSObject *ns;
     JSLinearString *nsprefix;
 
     JS_ASSERT(pn->pn_arity == PN_NULLARY);
-    str = pn->pn_atom;
+    JSAtom *str = pn->pn_atom;
     start = str->chars();
     length = str->length();
     JS_ASSERT(length != 0 && *start != '@');
     JS_ASSERT(length != 1 || *start != '*');
 
+    JSAtom *localName;
+
     uri = cx->runtime->emptyString;
     limit = start + length;
     colon = js_strchr_limit(start, ':', limit);
     if (colon) {
         offset = colon - start;
         prefix = js_NewDependentString(cx, str, 0, offset);
         if (!prefix)
             return NULL;
@@ -1220,17 +1218,17 @@ ParseNodeToQName(Parser *parser, JSParse
             JSAutoByteString bytes;
             if (js_ValueToPrintable(parser->context, v, &bytes)) {
                 ReportCompileErrorNumber(parser->context, &parser->tokenStream, pn,
                                          JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
             }
             return NULL;
         }
 
-        localName = js_NewStringCopyN(parser->context, colon + 1, length - (offset + 1));
+        localName = js_AtomizeChars(parser->context, colon + 1, length - (offset + 1));
         if (!localName)
             return NULL;
     } else {
         if (isAttributeName) {
             /*
              * An unprefixed attribute is not in any namespace, so set prefix
              * as well as uri to the empty string.
              */
@@ -2759,24 +2757,24 @@ ToXMLString(JSContext *cx, jsval v, uint
     JS::Anchor<JSObject *> anch(obj);
     JSXML *xml = reinterpret_cast<JSXML *>(obj->getPrivate());
     return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0);
 }
 
 static JSObject *
 ToAttributeName(JSContext *cx, jsval v)
 {
-    JSLinearString *name, *uri, *prefix;
+    JSLinearString *uri, *prefix;
     JSObject *obj;
     Class *clasp;
     JSObject *qn;
 
+    JSAtom *name;
     if (JSVAL_IS_STRING(v)) {
-        name = JSVAL_TO_STRING(v)->ensureLinear(cx);
-        if (!name)
+        if (!js_ValueToAtom(cx, Valueify(v), &name))
             return NULL;
         uri = prefix = cx->runtime->emptyString;
     } else {
         if (JSVAL_IS_PRIMITIVE(v)) {
             js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME,
                                 JSDVG_IGNORE_STACK, Valueify(v), NULL);
             return NULL;
         }
@@ -2790,21 +2788,17 @@ ToAttributeName(JSContext *cx, jsval v)
             qn = obj;
             uri = qn->getNameURI();
             prefix = qn->getNamePrefix();
             name = qn->getQNameLocalName();
         } else {
             if (clasp == &js_AnyNameClass) {
                 name = cx->runtime->atomState.starAtom;
             } else {
-                JSString *str = js_ValueToString(cx, Valueify(v));
-                if (!str)
-                    return NULL;
-                name = str->ensureLinear(cx);
-                if (!name)
+                if (!js_ValueToAtom(cx, Valueify(v), &name))
                     return NULL;
             }
             uri = prefix = cx->runtime->emptyString;
         }
     }
 
     qn = NewXMLAttributeName(cx, uri, prefix, name);
     if (!qn)
@@ -2813,37 +2807,34 @@ ToAttributeName(JSContext *cx, jsval v)
 }
 
 static void
 ReportBadXMLName(JSContext *cx, const Value &idval)
 {
     js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, idval, NULL);
 }
 
-static JSBool
-IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp)
-{
-    JSAtom *atom;
-    JSLinearString *uri;
-
-    atom = cx->runtime->atomState.functionNamespaceURIAtom;
-    uri = qn->getNameURI();
-    if (uri && (uri == atom || EqualStrings(uri, atom)))
-        return JS_ValueToId(cx, STRING_TO_JSVAL(qn->getQNameLocalName()), funidp);
-    *funidp = JSID_VOID;
-    return JS_TRUE;
-}
-
-JSBool
-js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
-{
-    if (obj->getClass() == &js_QNameClass)
-        return IsFunctionQName(cx, obj, funidp);
-    *funidp = JSID_VOID;
-    return JS_TRUE;
+static bool
+GetLocalNameFromFunctionQName(JSObject *qn, jsid *funidp, JSContext *cx)
+{
+    JSAtom *atom = cx->runtime->atomState.functionNamespaceURIAtom;
+    JSLinearString *uri = qn->getNameURI();
+    if (uri && (uri == atom || EqualStrings(uri, atom))) {
+        *funidp = ATOM_TO_JSID(qn->getQNameLocalName());
+        return true;
+    }
+    return false;
+}
+
+bool
+js_GetLocalNameFromFunctionQName(JSObject *obj, jsid *funidp, JSContext *cx)
+{
+    if (!obj->isQName())
+        return false;
+    return GetLocalNameFromFunctionQName(obj, funidp, cx);
 }
 
 static JSObject *
 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
 {
     JSAtom *atomizedName;
     JSString *name;
     JSObject *obj;
@@ -2901,18 +2892,18 @@ ToXMLName(JSContext *cx, jsval v, jsid *
 
 construct:
     v = STRING_TO_JSVAL(name);
     obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&v));
     if (!obj)
         return NULL;
 
 out:
-    if (!IsFunctionQName(cx, obj, funidp))
-        return NULL;
+    if (!GetLocalNameFromFunctionQName(obj, funidp, cx))
+        *funidp = JSID_VOID;
     return obj;
 
 bad:
     JSAutoByteString bytes;
     if (js_ValueToPrintable(cx, StringValue(name), &bytes))
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAME, bytes.ptr());
     return NULL;
 }
@@ -4981,24 +4972,18 @@ again:
 /*
  * 11.2.2.1 Step 3(d) onward.
  */
 JSBool
 js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JS_ASSERT(obj->isXML());
 
-    if (JSID_IS_OBJECT(id)) {
-        jsid funid;
-
-        if (!js_IsFunctionQName(cx, JSID_TO_OBJECT(id), &funid))
-            return JS_FALSE;
-        if (!JSID_IS_VOID(funid))
-            id = funid;
-    }
+    if (JSID_IS_OBJECT(id))
+        js_GetLocalNameFromFunctionQName(JSID_TO_OBJECT(id), &id, cx);
 
     /*
      * As our callers have a bad habit of passing a pointer to an unrooted
      * local value as vp, we use a proper root here.
      */
     AutoValueRooter tvr(cx);
     JSBool ok = GetXMLFunction(cx, obj, id, Jsvalify(tvr.addr()));
     *vp = tvr.value();
@@ -6440,39 +6425,30 @@ xml_setChildren(JSContext *cx, uintN arg
 
     *vp = OBJECT_TO_JSVAL(obj);
     return JS_TRUE;
 }
 
 static JSBool
 xml_setLocalName(JSContext *cx, uintN argc, jsval *vp)
 {
-    jsval name;
-    JSObject *nameqn;
-    JSLinearString *namestr;
-
     NON_LIST_XML_METHOD_PROLOG;
     if (!JSXML_HAS_NAME(xml))
         return JS_TRUE;
 
+    JSAtom *namestr;
     if (argc == 0) {
         namestr = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
     } else {
-        name = vp[2];
-        if (!JSVAL_IS_PRIMITIVE(name) &&
-            JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) {
-            nameqn = JSVAL_TO_OBJECT(name);
-            namestr = nameqn->getQNameLocalName();
+        jsval name = vp[2];
+        if (!JSVAL_IS_PRIMITIVE(name) && JSVAL_TO_OBJECT(name)->isQName()) {
+            namestr = JSVAL_TO_OBJECT(name)->getQNameLocalName();
         } else {
-            if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2]))
-                return JS_FALSE;
-            name = vp[2];
-            namestr = JSVAL_TO_STRING(name)->ensureLinear(cx);
-            if (!namestr)
-                return JS_FALSE;
+            if (!js_ValueToAtom(cx, Valueify(name), &namestr))
+                return false;
         }
     }
 
     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
     if (!xml)
         return JS_FALSE;
     if (namestr)
         xml->name->setQNameLocalName(namestr);
@@ -7147,17 +7123,17 @@ js_InitQNameClass(JSContext *cx, JSObjec
 {
     JS_ASSERT(obj->isNative());
 
     GlobalObject *global = obj->asGlobal();
 
     JSObject *qnameProto = global->createBlankPrototype(cx, &js_QNameClass);
     if (!qnameProto)
         return NULL;
-    JSFlatString *empty = cx->runtime->emptyString;
+    JSAtom *empty = cx->runtime->emptyString;
     if (!InitXMLQName(cx, qnameProto, empty, empty, empty))
         return NULL;
     qnameProto->syncSpecialEquality();
 
     const uintN QNAME_CTOR_LENGTH = 2;
     JSFunction *ctor = global->createConstructor(cx, QName, &js_QNameClass,
                                                  CLASS_ATOM(cx, QName), QNAME_CTOR_LENGTH);
     if (!ctor)
@@ -7458,18 +7434,18 @@ js_FindXMLProperty(JSContext *cx, const 
         if (!nameobj)
             return JS_FALSE;
     } else {
         JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass ||
                   nameobj->getClass() == &js_QNameClass);
     }
 
     qn = nameobj;
-    if (!IsFunctionQName(cx, qn, &funid))
-        return JS_FALSE;
+    if (!GetLocalNameFromFunctionQName(qn, &funid, cx))
+        funid = JSID_VOID;
 
     obj = &js_GetTopStackFrame(cx)->scopeChain();
     do {
         /* Skip any With object that can wrap XML. */
         target = obj;
         while (target->getClass() == &js_WithClass) {
              proto = target->getProto();
              if (!proto)
@@ -7777,20 +7753,20 @@ js_NewXMLSpecialObject(JSContext *cx, JS
         return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
     }
 
     obj = js_NewXMLObject(cx, xml_class);
     if (!obj)
         return NULL;
     xml = (JSXML *) obj->getPrivate();
     if (name) {
-        JSLinearString *linearName = name->ensureLinear(cx);
-        if (!linearName)
+        JSAtom *atomName = js_AtomizeString(cx, name);
+        if (!atomName)
             return NULL;
-        qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, linearName);
+        qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, atomName);
         if (!qn)
             return NULL;
         xml->name = qn;
     }
     xml->xml_value = value;
     return obj;
 }
 
--- a/js/src/jsxml.h
+++ b/js/src/jsxml.h
@@ -275,21 +275,21 @@ js_InitQNameClass(JSContext *cx, JSObjec
 
 extern JSObject *
 js_InitXMLClass(JSContext *cx, JSObject *obj);
 
 extern JSObject *
 js_InitXMLClasses(JSContext *cx, JSObject *obj);
 
 /*
- * If obj is QName corresponding to function::name, set *funidp to name's id,
- * otherwise set *funidp to void.
+ * If obj is a QName corresponding to function::name, set *funidp to name's id
+ * and return true, else return false.
  */
-JSBool
-js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp);
+extern bool
+js_GetLocalNameFromFunctionQName(JSObject *obj, jsid *funidp, JSContext *cx);
 
 extern JSBool
 js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp);
 
 extern JSBool
 js_SetDefaultXMLNamespace(JSContext *cx, const js::Value &v);
 
 /*
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3955,29 +3955,16 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("clone",          Clone,          1,0),
     JS_FN("getpda",         GetPDA,         1,0),
     JS_FN("getslx",         GetSLX,         1,0),
     JS_FN("toint32",        ToInt32,        1,0),
     JS_FN("evalcx",         EvalInContext,  1,0),
     JS_FN("evalInFrame",    EvalInFrame,    2,0),
     JS_FN("shapeOf",        ShapeOf,        1,0),
     JS_FN("resolver",       Resolver,       1,0),
-    JS_FN("pauseProfilers", js_PauseProfilers, 0,0),
-    JS_FN("resumeProfilers", js_ResumeProfilers, 0,0),
-#ifdef MOZ_CALLGRIND
-    JS_FN("startCallgrind", js_StartCallgrind,  0,0),
-    JS_FN("stopCallgrind",  js_StopCallgrind,   0,0),
-    JS_FN("dumpCallgrind",  js_DumpCallgrind,   1,0),
-#endif
-#ifdef MOZ_VTUNE
-    JS_FN("startVtune",     js_StartVtune,    1,0),
-    JS_FN("stopVtune",      js_StopVtune,     0,0),
-    JS_FN("pauseVtune",     js_PauseVtune,    0,0),
-    JS_FN("resumeVtune",    js_ResumeVtune,   0,0),
-#endif
 #ifdef MOZ_TRACEVIS
     JS_FN("startTraceVis",  StartTraceVisNative, 1,0),
     JS_FN("stopTraceVis",   StopTraceVisNative,  0,0),
 #endif
 #ifdef DEBUG
     JS_FN("arrayInfo",      js_ArrayInfo,   1,0),
 #endif
 #ifdef JS_THREADSAFE
@@ -4094,29 +4081,16 @@ static const char *const shell_help_mess
 "  Evaluate s in optional sandbox object o\n"
 "  if (s == '' && !o) return new o with eager standard classes\n"
 "  if (s == 'lazy' && !o) return new o with lazy standard classes",
 "evalInFrame(n,str,save)  Evaluate 'str' in the nth up frame.\n"
 "                         If 'save' (default false), save the frame chain",
 "shapeOf(obj)             Get the shape of obj (an implementation detail)",
 "resolver(src[, proto])   Create object with resolve hook that copies properties\n"
 "                         from src. If proto is omitted, use Object.prototype.",
-"pauseProfilers()         Pause all profilers that can be paused",
-"resumeProfilers()        Resume profilers if they are paused",
-#ifdef MOZ_CALLGRIND
-"startCallgrind()         Start callgrind instrumentation",
-"stopCallgrind()          Stop callgrind instrumentation",
-"dumpCallgrind([name])    Dump callgrind counters",
-#endif
-#ifdef MOZ_VTUNE
-"startVtune([filename])   Start vtune instrumentation",
-"stopVtune()              Stop vtune instrumentation",
-"pauseVtune()             Pause vtune collection",
-"resumeVtune()            Resume vtune collection",
-#endif
 #ifdef MOZ_TRACEVIS
 "startTraceVis(filename)  Start TraceVis recording (stops any current recording)",
 "stopTraceVis()           Stop TraceVis recording",
 #endif
 #ifdef DEBUG
 "arrayInfo(a1, a2, ...)   Report statistics about arrays",
 #endif
 #ifdef JS_THREADSAFE
@@ -4148,53 +4122,86 @@ static const char *const shell_help_mess
 "  Enables or disables a particularly expensive assertion in stack-walking\n"
 "  code.  If your test isn't ridiculously thorough, such that performing this\n"
 "  assertion increases test duration by an order of magnitude, you shouldn't\n"
 "  use this.",
 "getMaxArgs()             Return the maximum number of supported args for a call.",
 
 /* Keep these last: see the static assertion below. */
 #ifdef MOZ_PROFILING
-"startProfiling()         Start a profiling session.\n"
+"startProfiling([profileName])\n"
+"                         Start a profiling session\n"
 "                         Profiler must be running with programatic sampling",
-"stopProfiling()          Stop a running profiling session\n"
+"stopProfiling([profileName])\n"
+"                         Stop a running profiling session",
+"pauseProfilers([profileName])\n"
+"                         Pause a running profiling session",
+"resumeProfilers([profileName])\n"
+"                         Resume a paused profiling session",
+"dumpProfile([outfile[, profileName]])\n"
+"                         Dump out current profile info (only valid for callgrind)",
+# ifdef MOZ_CALLGRIND
+"startCallgrind()         Start Callgrind instrumentation",
+"stopCallgrind()          Stop Callgrind instrumentation",
+"dumpCallgrind([outfile]) Dump current Callgrind counters to file or stdout",
+# endif
+# ifdef MOZ_VTUNE
+"startVtune()             Start Vtune instrumentation",
+"stopVtune()              Stop Vtune instrumentation",
+"pauseVtune()             Pause Vtune collection",
+"resumeVtune()            Resume Vtune collection",
+# endif
 #endif
 };
 
 #ifdef MOZ_PROFILING
-#define PROFILING_FUNCTION_COUNT 2
+# define PROFILING_FUNCTION_COUNT 5
+# ifdef MOZ_CALLGRIND
+#  define CALLGRIND_FUNCTION_COUNT 3
+# else
+#  define CALLGRIND_FUNCTION_COUNT 0
+# endif
+# ifdef MOZ_VTUNE
+#  define VTUNE_FUNCTION_COUNT 4
+# else
+#  define VTUNE_FUNCTION_COUNT 0
+# endif
+# define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
 #else
-#define PROFILING_FUNCTION_COUNT 0
+# define EXTERNAL_FUNCTION_COUNT 0
 #endif
 
 /* Help messages must match shell functions. */
-JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT ==
+JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - EXTERNAL_FUNCTION_COUNT ==
                  JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */);
 
 #ifdef DEBUG
 static void
 CheckHelpMessages()
 {
     const char *const *m;
     const char *lp;
 
     /* Messages begin with "function_name(" prefix and don't end with \n. */
-    for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - PROFILING_FUNCTION_COUNT; ++m) {
+    for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - EXTERNAL_FUNCTION_COUNT; ++m) {
         lp = strchr(*m, '(');
         JS_ASSERT(lp);
         JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
                          *m, lp - *m) == 0);
         JS_ASSERT((*m)[strlen(*m) - 1] != '\n');
     }
 }
 #else
 # define CheckHelpMessages() ((void) 0)
 #endif
 
 #undef PROFILING_FUNCTION_COUNT
+#undef CALLGRIND_FUNCTION_COUNT
+#undef VTUNE_FUNCTION_COUNT
+#undef EXTERNAL_FUNCTION_COUNT
 
 static JSBool
 Help(JSContext *cx, uintN argc, jsval *vp)
 {
     uintN i, j;
     int did_header, did_something;
     JSType type;
     JSFunction *fun;
@@ -5025,17 +5032,24 @@ NewGlobalObject(JSContext *cx, Compartme
 
 static bool
 BindScriptArgs(JSContext *cx, JSObject *obj, OptionParser *op)
 {
     MultiStringRange msr = op->getMultiStringArg("scriptArgs");
     JSObject *scriptArgs = JS_NewArrayObject(cx, 0, NULL);
     if (!scriptArgs)
         return false;
-    if (!JS_DefineProperty(cx, obj, "scriptArgs", OBJECT_TO_JSVAL(scriptArgs), NULL, NULL, 0))
+
+    /* 
+     * Script arguments are bound as a normal |arguments| property on the
+     * global object. It has no special significance, like |arguments| in
+     * function scope does -- this identifier is used de-facto across shell
+     * implementations, see bug 675269.
+     */
+    if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(scriptArgs), NULL, NULL, 0))
         return false;
 
     for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) {
         const char *scriptArg = msr.front();
         JSString *str = JS_NewStringCopyZ(cx, scriptArg);
         if (!str ||
             !JS_DefineElement(cx, scriptArgs, i, STRING_TO_JSVAL(str), NULL, NULL,
                               JSPROP_ENUMERATE)) {
@@ -5209,17 +5223,17 @@ Shell(JSContext *cx, OptionParser *op, c
         if (jsdbc)
             JSDB_TermDebugger(jsdc);
 #endif /* JSDEBUGGER_C_UI */
         JSD_DebuggerOff(jsdc);
     }
 #endif  /* JSDEBUGGER */
 
     if (enableDisassemblyDumps)
-        JS_DumpAllProfiles(cx);
+        JS_DumpCompartmentBytecode(cx);
  
     return result;
 }
 
 static void
 MaybeOverrideOutFileFromEnv(const char* const envVar,
                             FILE* defaultOut,
                             FILE** outFile)
@@ -5345,17 +5359,17 @@ main(int argc, char **argv, char **envp)
                                "N indicates \"zealousness\":\n"
                                "  0: no additional GCs\n"
                                "  1: additional GCs at common danger points\n"
                                "  2: GC every F allocations (default: 100)\n"
                                "If C is 1, compartmental GCs are performed; otherwise, full")
 #endif
         || !op.addOptionalStringArg("script", "A script to execute (after all options)")
         || !op.addOptionalMultiStringArg("scriptArgs",
-                                         "String arguments to bind as |scriptArgs| in the "
+                                         "String arguments to bind as |arguments| in the "
                                          "shell's global")) {
         return EXIT_FAILURE;
     }
 
     switch (op.parseArgs(argc, argv)) {
       case OptionParser::ParseHelp:
         return EXIT_SUCCESS;
       case OptionParser::ParseError:
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -286,27 +286,16 @@ File(JSContext *cx, uintN argc, jsval *v
 }
 
 static JSFunctionSpec gGlobalFun[] = {
     {"dump",    Dump,   1,0},
     {"debug",   Debug,  1,0},
     {"atob",    Atob,   1,0},
     {"btoa",    Btoa,   1,0},
     {"File",    File,   1,JSFUN_CONSTRUCTOR},
-#ifdef MOZ_CALLGRIND
-    {"startCallgrind",  js_StartCallgrind, 0,0},
-    {"stopCallgrind",   js_StopCallgrind,  0,0},
-    {"dumpCallgrind",   js_DumpCallgrind,  1,0},
-#endif
-#ifdef MOZ_VTUNE
-    {"startVtune",      js_StartVtune,     1,0},
-    {"stopVtune",       js_StopVtune,      0,0},
-    {"pauseVtune",      js_PauseVtune,     0,0},
-    {"resumeVtune",     js_ResumeVtune,    0,0},
-#endif
 #ifdef MOZ_TRACEVIS
     {"initEthogram",     js_InitEthogram,      0,0},
     {"shutdownEthogram", js_ShutdownEthogram,  0,0},
 #endif
     {nsnull,nsnull,0,0}
 };
 
 class JSCLContextHelper
--- a/js/src/xpconnect/shell/xpcshell.cpp
+++ b/js/src/xpconnect/shell/xpcshell.cpp
@@ -842,21 +842,16 @@ static JSFunctionSpec glob_functions[] =
     {"clear",           Clear,          1,0},
     {"options",         Options,        0,0},
     JS_FN("parent",     Parent,         1,0),
 #ifdef DEBUG
     {"dumpHeap",        DumpHeap,       5,0},
 #endif
     {"sendCommand",     SendCommand,    1,0},
     {"getChildGlobalObject", GetChildGlobalObject, 0,0},
-#ifdef MOZ_CALLGRIND
-    {"startCallgrind",  js_StartCallgrind,  0,0},
-    {"stopCallgrind",   js_StopCallgrind,   0,0},
-    {"dumpCallgrind",   js_DumpCallgrind,   1,0},
-#endif
     {nsnull,nsnull,0,0}
 };
 
 JSClass global_class = {
     "global", 0,
     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   nsnull
 };
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2786,20 +2786,17 @@ PRBool nsDisplayTransform::UntransformRe
   gfxRect result(NSAppUnitsToFloatPixels(aUntransformedBounds.x, factor),
                  NSAppUnitsToFloatPixels(aUntransformedBounds.y, factor),
                  NSAppUnitsToFloatPixels(aUntransformedBounds.width, factor),
                  NSAppUnitsToFloatPixels(aUntransformedBounds.height, factor));
 
   /* We want to untransform the matrix, so invert the transformation first! */
   result = matrix.Inverse().ProjectRectBounds(result);
 
-  *aOutRect = nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
-                     NSFloatPixelsToAppUnits(float(result.y), factor),
-                     NSFloatPixelsToAppUnits(float(result.width), factor),
-                     NSFloatPixelsToAppUnits(float(result.height), factor));
+  *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor);
 
   return PR_TRUE;
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList)
     : nsDisplayWrapList(aBuilder, aFrame, aList), mEffectsFrame(aFrame),
       mBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6663,17 +6663,17 @@ PresShell::HandleEvent(nsIView         *
       nsPoint eventPoint
           = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
       {
         PRBool ignoreRootScrollFrame = PR_FALSE;
         if (aEvent->eventStructType == NS_MOUSE_EVENT) {
           ignoreRootScrollFrame = static_cast<nsMouseEvent*>(aEvent)->ignoreRootScrollFrame;
         }
         nsIFrame* target = nsLayoutUtils::GetFrameForPoint(frame, eventPoint,
-                                                           PR_TRUE, ignoreRootScrollFrame);
+                                                           PR_FALSE, ignoreRootScrollFrame);
         if (target) {
           frame = target;
         }
       }
     }
 
     // if a node is capturing the mouse, check if the event needs to be
     // retargeted at the capturing content instead. This will be the case when
--- a/layout/base/tests/Makefile.in
+++ b/layout/base/tests/Makefile.in
@@ -148,16 +148,17 @@ DEFINES += -D_IMPL_NS_LAYOUT
 		     bug664087-2-ref.html \
 		test_bug514127.html \
 		test_bug518777.html \
 		test_bug548545.xhtml \
 		test_bug558663.html \
 		test_bug559499.html \
 		test_bug582181-1.html \
 		test_bug582181-2.html \
+		test_bug677878.html \
 		test_flush_on_paint.html \
 		test_mozPaintCount.html \
 		test_scroll_selection_into_view.html \
 		test_bug583889.html \
 		bug583889_inner1.html \
 		bug583889_inner2.html \
 		test_bug582771.html \
 		test_bug603550.html \
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_bug677878.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677878
+-->
+<head>
+  <title>Test for Bug 677878</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+  <style>
+    #test1 {
+      background: green;
+      height: 100px;
+      width: 100px;
+      -moz-transform: scale(20, 20);
+      -moz-transform-origin: 0 0%;
+    }
+  </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677878">Mozilla Bug 677878</a>
+<div id="content">
+  <div id="test1">
+    <div id="test2" onclick="testFinish();">
+      test
+    </div>
+  </div>
+  
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+runtests();
+
+function runtests() {
+  function doClick() {
+    document.getElementById("test2").addEventListener("mousedown", testFinish, true);
+    synthesizeMouseAtCenter(document.getElementById("test2"), { type: "mousedown" })
+  }
+  setTimeout(doClick, 300);
+}
+
+function testFinish(event){
+  ok(true, "We can still interact with the item after it is transformed");
+  SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
--- a/layout/reftests/selection/reftest.list
+++ b/layout/reftests/selection/reftest.list
@@ -25,8 +25,9 @@
 == extend-4a.html extend-4-ref.html
 == extend-4b.html extend-4-ref.html
 fails-if(Android) != pseudo-element-of-native-anonymous.html pseudo-element-of-native-anonymous-ref.html # bug 676641
 # These tests uses Highlight and HighlightText color keywords, they are not same as text selection color on Mac.
 fails-if(cocoaWidget) == non-themed-widget.html non-themed-widget-ref.html
 fails-if(cocoaWidget) == themed-widget.html themed-widget-ref.html
 == addrange-1.html addrange-ref.html
 == addrange-2.html addrange-ref.html
+== splitText-normalize.html splitText-normalize-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/splitText-normalize-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=191864
+-->
+<head>
+  <title>Test for Bug 191864</title>
+  <script type="application/javascript;version=1.7" src="splitText-normalize.js"></script>
+<script type="application/javascript;version=1.7">
+var id;
+function checkFinished() {
+  if (window.frames.length == tests_done) {
+    clearInterval(id);
+    document.documentElement.className = "";
+  }
+}
+
+function runTest() {
+  let col1 = document.getElementById('col1');
+  let col2 = document.getElementById('col2');
+  let col3 = document.getElementById('col3');
+  let col4 = document.getElementById('col4');
+  for (let i=0; i < tests.length; ++i) {
+    let t = tests[i];
+    col1.appendChild(createFrame(test_ref,t));
+    col2.appendChild(createFrame(test_ref,t));
+    col3.appendChild(createFrame(test_ref,t));
+    col4.appendChild(createFrame(test_ref,t));
+  }
+  id = setInterval(checkFinished,500);
+}
+</script>
+</head>
+<body onload="runTest()">
+<span id="col1" style="float:left; height:800px; width:180px;"></span>
+<span id="col2" style="float:left; height:800px; width:180px;"></span>
+<span id="col3" style="float:left; height:800px; width:180px;"></span>
+<span id="col4" style="float:left; height:800px; width:180px;"></span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/splitText-normalize.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=191864
+-->
+<head>
+  <title>Test for Bug 191864</title>
+  <script type="application/javascript;version=1.7" src="splitText-normalize.js"></script>
+<script type="application/javascript;version=1.7">
+var id;
+function checkFinished() {
+  if (window.frames.length == tests_done) {
+    clearInterval(id);
+    document.documentElement.className = "";
+  }
+}
+
+function runTest() {
+  let col1 = document.getElementById('col1');
+  let col2 = document.getElementById('col2');
+  let col3 = document.getElementById('col3');
+  let col4 = document.getElementById('col4');
+  for (let i=0; i < tests.length; ++i) {
+    let t = tests[i];
+    col1.appendChild(createFrame(test_split,t));
+    col2.appendChild(createFrame(test_split_merge,t));
+    col3.appendChild(createFrame(test_merge,t));
+    col4.appendChild(createFrame(test_split_insert,t));
+  }
+  id = setInterval(checkFinished,500);
+}
+</script>
+</head>
+<body onload="runTest()">
+<span id="col1" style="float:left; height:800px; width:180px;"></span>
+<span id="col2" style="float:left; height:800px; width:180px;"></span>
+<span id="col3" style="float:left; height:800px; width:180px;"></span>
+<span id="col4" style="float:left; height:800px; width:180px;"></span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/selection/splitText-normalize.js
@@ -0,0 +1,97 @@
+/** Test for Bug 191864 **/
+var tests_done = 0;
+var tests = [
+ [ {}, [0,4], "012345678" ],
+ [ {}, [0,0], "012345678" ],
+ [ {}, [0,9], "012345678" ],
+ [ {startOffset:4}, [0,4], "012345678" ],
+ [ {startOffset:5}, [0,4], "012345678" ],
+ [ {startOffset:5,endOffset:6}, [0,4], "012345678" ],
+ [ {endOffset:5}, [0,4], "012345678" ],
+ [ {endOffset:4}, [0,4], "012345678" ],
+ [ {endOffset:3}, [0,4], "012345678" ],
+ [ {startOffset:1,endOffset:3}, [0,4], "012345678" ],
+ [ {startOffset:7,endOffset:7}, [0,4], "012345678" ],
+ [ {startOffset:4,endOffset:4}, [0,4], "012345678" ],
+ [ {endNode:1}, [0,4], "012345678", "" ],
+ [ {endNode:1}, [0,4], "01234567", "8" ],
+ [ {endNode:1}, [1,4], "0", "12345678" ],
+ [ {endNode:2}, [1,4], "0", "12345", "678" ],
+]
+
+function runtest(f,t,nosplit) {
+  // create content
+  let doc = f.contentDocument;
+  for (let i = 2; i < t.length; ++i) {
+    c = doc.createTextNode(t[i]);
+    doc.body.appendChild(c);
+  }
+
+  // setup selection
+  let sel = t[0]
+  let startNode = sel.startNode === undefined ? doc.body.firstChild : doc.body.childNodes[sel.startNode];
+  let startOffset = sel.startOffset === undefined ? 0 : sel.startOffset;
+  let endNode = sel.endNode === undefined ? startNode : doc.body.childNodes[sel.endNode];
+  let endOffset = sel.endOffset === undefined ? endNode.length : sel.endOffset;
+  let selection = f.contentWindow.getSelection();
+  let r = doc.createRange();
+  r.setStart(startNode, startOffset);
+  r.setEnd(endNode, endOffset);
+  selection.addRange(r);
+
+  // splitText
+  let split = t[1]
+  if (!nosplit)
+    doc.body.childNodes[split[0]].splitText(split[1])
+}
+function test_ref(f,t,nosplit) {
+  runtest(f,t,true);
+}
+function test_split(f,t) {
+  runtest(f,t);
+}
+function test_split_insert(f,t) {
+  runtest(f,t);
+  let doc = f.contentDocument;
+  doc.body.firstChild;
+  let split = t[1]
+  let text1 = doc.body.childNodes[split[0]]
+  let text2 = doc.body.childNodes[split[0]+1]
+  if (text2.textContent.length==0) return;
+  let c = doc.createTextNode(text2.textContent[0]);
+  doc.body.insertBefore(c,text2);
+  let r = doc.createRange();
+  r.setStart(text2, 0);
+  r.setEnd(text2, 1);
+  r.deleteContents();
+}
+function test_split_merge(f,t) {
+  runtest(f,t);
+  f.contentDocument.body.normalize();
+}
+function test_merge(f,t) {
+  runtest(f,t,true);
+  f.contentDocument.body.normalize();
+}
+
+function repaint_selection(win) {
+  let a = new Array;
+  let sel = win.getSelection();
+  for (let i = 0; i < sel.rangeCount; ++i) {
+    a[i] = sel.getRangeAt(i);
+  }
+  sel.removeAllRanges();
+  for (let i = 0; i < a.length; ++i) {
+    sel.addRange(a[i]);
+  }
+}
+
+function createFrame(run,t) {
+  let f = document.createElement('iframe');
+  f.setAttribute('height','22');
+  f.setAttribute('frameborder','0');
+  f.setAttribute('width','200');
+  f.src = 'data:text/html,<body style="margin:0;padding:0">';
+  f.onload = function () { try { run(f, t); repaint_selection(f.contentWindow);} finally { ++tests_done; } }
+  return f;
+}
--- a/mobile/themes/core/gingerbread/browser.css
+++ b/mobile/themes/core/gingerbread/browser.css
@@ -319,17 +319,17 @@ toolbarbutton.urlbar-button {
 #tool-tabs {
   list-style-image: url("chrome://browser/skin/images/tabs-hdpi.png");
 }
 
 .tool-star {
   list-style-image: url("chrome://browser/skin/images/bookmark-default-hdpi.png");
 }
 
-#tool-star[starred="true"] {
+.tool-star[starred="true"] {
   list-style-image: url("chrome://browser/skin/images/bookmark-starred-hdpi.png");
 }
 
 .tool-back,
 .tool-forward:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/images/back-default-hdpi.png");
 }
 
--- a/netwerk/base/public/security-prefs.js
+++ b/netwerk/base/public/security-prefs.js
@@ -1,26 +1,19 @@
-pref("security.enable_ssl2",             false);
 pref("security.enable_ssl3",             true);
 pref("security.enable_tls",		 true);
 pref("security.enable_tls_session_tickets", true);
 
 pref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", false);
 pref("security.ssl.renego_unrestricted_hosts", "");
 pref("security.ssl.treat_unsafe_negotiation_as_broken", false);
 pref("security.ssl.require_safe_negotiation",  false);
 pref("security.ssl.warn_missing_rfc5746",  1);
 pref("security.ssl.enable_false_start", false);
 
-pref("security.ssl2.rc4_128", false);
-pref("security.ssl2.rc2_128", false);
-pref("security.ssl2.des_ede3_192", false);
-pref("security.ssl2.des_64", false);
-pref("security.ssl2.rc4_40", false);
-pref("security.ssl2.rc2_40", false);
 pref("security.ssl3.rsa_rc4_128_md5", true);
 pref("security.ssl3.rsa_rc4_128_sha", true);
 pref("security.ssl3.rsa_fips_des_ede3_sha", true);
 pref("security.ssl3.rsa_des_ede3_sha", true);
 pref("security.ssl3.rsa_fips_des_sha", false);
 pref("security.ssl3.rsa_des_sha", false);
 pref("security.ssl3.rsa_1024_rc4_56_sha", false);
 pref("security.ssl3.rsa_1024_des_cbc_sha", false);
--- a/security/manager/ssl/src/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/src/nsNSSCertHelper.cpp
@@ -1914,25 +1914,18 @@ ProcessSubjectPublicKeyInfo(CERTSubjectP
           int i_pv = DER_GetInteger(&ecpk.publicValue);
           s_pv.AppendInt(i_pv);
         }
         const PRUnichar *params[] = {s_fsl.get(), s_bpol.get(), s_pv.get()};
         nssComponent->PIPBundleFormatStringFromName("CertDumpECTemplate",
                                                     params, 3, text);
         break;
       }
-      case dhKey:
-      case dsaKey:
-      case fortezzaKey:
-      case keaKey:
-         /* Too many parameters, to rarely used to bother displaying it */
-         break;
-      case nullKey:
       default:
-         /* Algorithm unknown */
+         /* Algorithm unknown, or too rarely used to bother displaying it */
          break;
       }
       SECKEY_DestroyPublicKey (key);
   }
   if (!displayed) {
       // Algorithm unknown, display raw bytes
       // The subjectPublicKey field is encoded as a bit string.
       // ProcessRawBytes expects the length to be in bytes, so 
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -1028,27 +1028,16 @@ nsNSSComponent::RegisterPSMContentListen
 
 /* Table of pref names and SSL cipher ID */
 typedef struct {
   const char* pref;
   long id;
 } CipherPref;
 
 static CipherPref CipherPrefs[] = {
-/* SSL2 cipher suites, all use RSA and an MD5 MAC */
- {"security.ssl2.rc4_128", SSL_EN_RC4_128_WITH_MD5}, // 128-bit RC4 encryption with RSA and an MD5 MAC
- {"security.ssl2.rc2_128", SSL_EN_RC2_128_CBC_WITH_MD5}, // 128-bit RC2 encryption with RSA and an MD5 MAC
- {"security.ssl2.des_ede3_192", SSL_EN_DES_192_EDE3_CBC_WITH_MD5}, // 168-bit Triple DES encryption with RSA and MD5 MAC 
- {"security.ssl2.des_64", SSL_EN_DES_64_CBC_WITH_MD5}, // 56-bit DES encryption with RSA and an MD5 MAC
- {"security.ssl2.rc4_40", SSL_EN_RC4_128_EXPORT40_WITH_MD5}, // 40-bit RC4 encryption with RSA and an MD5 MAC (export)
- {"security.ssl2.rc2_40", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5}, // 40-bit RC2 encryption with RSA and an MD5 MAC (export)
- /* Fortezza SSL3/TLS cipher suites, see bug 133502 */
- {"security.ssl3.fortezza_fortezza_sha", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA},
- {"security.ssl3.fortezza_rc4_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA},
- {"security.ssl3.fortezza_null_sha", SSL_FORTEZZA_DMS_WITH_NULL_SHA},
  /* SSL3/TLS cipher suites*/
  {"security.ssl3.rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5}, // 128-bit RC4 encryption with RSA and an MD5 MAC
  {"security.ssl3.rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA}, // 128-bit RC4 encryption with RSA and a SHA1 MAC
  {"security.ssl3.rsa_fips_des_ede3_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA}, // 168-bit Triple DES with RSA and a SHA1 MAC (FIPS)
  {"security.ssl3.rsa_des_ede3_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA}, // 168-bit Triple DES with RSA and a SHA1 MAC
  {"security.ssl3.rsa_fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA}, // 56-bit DES encryption with RSA and a SHA1 MAC (FIPS)
  {"security.ssl3.rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA}, // 56-bit DES encryption with RSA and a SHA1 MAC
  {"security.ssl3.rsa_1024_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA}, // 56-bit RC4 encryption with RSA and a SHA1 MAC (export)
@@ -1806,20 +1795,19 @@ nsNSSComponent::InitializeNSS(PRBool sho
       //  SSL_EnableCipher(SSL_RSA_WITH_NULL_SHA, SSL_ALLOWED);
 
       PK11_SetPasswordFunc(PK11PasswordPrompt);
 
       // Register an observer so we can inform NSS when these prefs change
       nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefBranch);
       pbi->AddObserver("security.", this, PR_FALSE);
 
+      SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE);
+      SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE);
       PRBool enabled;
-      mPrefBranch->GetBoolPref("security.enable_ssl2", &enabled);
-      SSL_OptionSetDefault(SSL_ENABLE_SSL2, enabled);
-      SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, enabled);
       mPrefBranch->GetBoolPref("security.enable_ssl3", &enabled);
       SSL_OptionSetDefault(SSL_ENABLE_SSL3, enabled);
       mPrefBranch->GetBoolPref("security.enable_tls", &enabled);
       SSL_OptionSetDefault(SSL_ENABLE_TLS, enabled);
 
       // Configure TLS session tickets
       mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
       SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
@@ -2331,22 +2319,17 @@ nsNSSComponent::Observe(nsISupports *aSu
     }
   }
   else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { 
     nsNSSShutDownPreventionLock locker;
     PRBool clearSessionCache = PR_FALSE;
     PRBool enabled;
     NS_ConvertUTF16toUTF8  prefName(someData);
 
-    if (prefName.Equals("security.enable_ssl2")) {
-      mPrefBranch->GetBoolPref("security.enable_ssl2", &enabled);
-      SSL_OptionSetDefault(SSL_ENABLE_SSL2, enabled);
-      SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, enabled);
-      clearSessionCache = PR_TRUE;
-    } else if (prefName.Equals("security.enable_ssl3")) {
+    if (prefName.Equals("security.enable_ssl3")) {
       mPrefBranch->GetBoolPref("security.enable_ssl3", &enabled);
       SSL_OptionSetDefault(SSL_ENABLE_SSL3, enabled);
       clearSessionCache = PR_TRUE;
     } else if (prefName.Equals("security.enable_tls")) {
       mPrefBranch->GetBoolPref("security.enable_tls", &enabled);
       SSL_OptionSetDefault(SSL_ENABLE_TLS, enabled);
       clearSessionCache = PR_TRUE;
     } else if (prefName.Equals("security.enable_tls_session_tickets")) {
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -1771,19 +1771,17 @@ nsSSLIOLayerHelpers::rememberPossibleTLS
     // drop this site from the list of intolerant sites. TLS failure might be 
     // caused only by a traffic congestion while the server is TLS tolerant.
     removeIntolerantSite(key);
     return PR_FALSE;
   }
 
   PRBool enableSSL3 = PR_FALSE;
   SSL_OptionGet(ssl_layer_fd, SSL_ENABLE_SSL3, &enableSSL3);
-  PRBool enableSSL2 = PR_FALSE;
-  SSL_OptionGet(ssl_layer_fd, SSL_ENABLE_SSL2, &enableSSL2);
-  if (enableSSL3 || enableSSL2) {
+  if (enableSSL3) {
     // Add this site to the list of TLS intolerant sites.
     addIntolerantSite(key);
   }
   
   return currentlyUsesTLS;
 }
 
 void
@@ -3749,25 +3747,16 @@ nsSSLIOLayerSetOptions(PRFileDesc *fd, P
   nsNSSShutDownPreventionLock locker;
   if (forSTARTTLS || proxyHost) {
     if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, PR_FALSE)) {
       return NS_ERROR_FAILURE;
     }
     infoObject->SetHasCleartextPhase(PR_TRUE);
   }
 
-  if (forSTARTTLS) {
-    if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_SSL2, PR_FALSE)) {
-      return NS_ERROR_FAILURE;
-    }
-    if (SECSuccess != SSL_OptionSet(fd, SSL_V2_COMPATIBLE_HELLO, PR_FALSE)) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
   // Let's see if we're trying to connect to a site we know is
   // TLS intolerant.
   nsCAutoString key;
   key = nsDependentCString(host) + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port);
 
   if (nsSSLIOLayerHelpers::isKnownAsIntolerantSite(key)) {
     if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_TLS, PR_FALSE))
       return NS_ERROR_FAILURE;
@@ -3775,20 +3764,16 @@ nsSSLIOLayerSetOptions(PRFileDesc *fd, P
     infoObject->SetAllowTLSIntoleranceTimeout(PR_FALSE);
       
     // We assume that protocols that use the STARTTLS mechanism should support
     // modern hellos. For other protocols, if we suspect a site 
     // does not support TLS, let's also use V2 hellos.
     // One advantage of this approach, if a site only supports the older
     // hellos, it is more likely that we will get a reasonable error code
     // on our single retry attempt.
-    
-    if (!forSTARTTLS &&
-        SECSuccess != SSL_OptionSet(fd, SSL_V2_COMPATIBLE_HELLO, PR_TRUE))
-      return NS_ERROR_FAILURE;
   }
 
   if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE)) {
     return NS_ERROR_FAILURE;
   }
   if (SECSuccess != SSL_BadCertHook(fd, (SSLBadCertHandler) nsNSSBadCertHandler,
                                     infoObject)) {
     return NS_ERROR_FAILURE;
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -2382,17 +2382,17 @@ nsDownload::OnProgressChange64(nsIWebPro
       mReferrer = referrer;
 
     // If we have a MIME info, we know that exthandler has already added this to
     // the history, but if we do not, we'll have to add it ourselves.
     if (!mMIMEInfo) {
       nsCOMPtr<nsIDownloadHistory> dh =
         do_GetService(NS_DOWNLOADHISTORY_CONTRACTID);
       if (dh)
-        (void)dh->AddDownload(mSource, mReferrer, mStartTime);
+        (void)dh->AddDownload(mSource, mReferrer, mStartTime, mTarget);
     }
 
     // Fetch the entityID, but if we can't get it, don't panic (non-resumable)
     nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(aRequest));
     if (resumableChannel)
       (void)resumableChannel->GetEntityID(mEntityID);
 
     // Update the state and the database
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -24,16 +24,17 @@
  *   Seth Spitzer <sspitzer@mozilla.com>
  *   Asaf Romano <mano@mozilla.com>
  *   Marco Bonardo <mak77@bonardo.net>
  *   Edward Lee <edward.lee@engineering.uiuc.edu>
  *   Michael Ventnor <m.ventnor@gmail.com>
  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
  *   Drew Willcoxon <adw@mozilla.com>
  *   Philipp von Weitershausen <philipp@weitershausen.de>
+ *   Paolo Amadini <http://www.amadzone.org/>
  *
  * 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
@@ -165,16 +166,24 @@ using namespace mozilla::places;
 static const PRInt64 USECS_PER_DAY = LL_INIT(20, 500654080);
 
 // character-set annotation
 #define CHARSET_ANNO NS_LITERAL_CSTRING("URIProperties/characterSet")
 
 // Sync guid annotation
 #define SYNCGUID_ANNO NS_LITERAL_CSTRING("sync/guid")
 
+// Download destination file URI annotation
+#define DESTINATIONFILEURI_ANNO \
+  NS_LITERAL_CSTRING("downloads/destinationFileURI")
+
+// Download destination file name annotation
+#define DESTINATIONFILENAME_ANNO \
+  NS_LITERAL_CSTRING("downloads/destinationFileName")
+
 // These macros are used when splitting history by date.
 // These are the day containers and catch-all final container.
 #define HISTORY_ADDITIONAL_DATE_CONT_NUM 3
 // We use a guess of the number of months considering all of them 30 days
 // long, but we split only the last 6 months.
 #define HISTORY_DATE_CONT_NUM(_daysFromOldestVisit) \
   (HISTORY_ADDITIONAL_DATE_CONT_NUM + \
    NS_MIN(6, (PRInt32)ceilf((float)_daysFromOldestVisit/30)))
@@ -5211,28 +5220,82 @@ nsNavHistory::OnEndVacuum(PRBool aSuccee
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIDownloadHistory
 
 NS_IMETHODIMP
 nsNavHistory::AddDownload(nsIURI* aSource, nsIURI* aReferrer,
-                          PRTime aStartTime)
+                          PRTime aStartTime, nsIURI* aDestination)
 {
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
   NS_ENSURE_ARG(aSource);
 
   // don't add when history is disabled and silently fail
   if (IsHistoryDisabled())
     return NS_OK;
 
   PRInt64 visitID;
-  return AddVisit(aSource, aStartTime, aReferrer, TRANSITION_DOWNLOAD, PR_FALSE,
-                  0, &visitID);
+  nsresult rv = AddVisit(aSource, aStartTime, aReferrer, TRANSITION_DOWNLOAD,
+                         PR_FALSE, 0, &visitID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!aDestination) {
+    return NS_OK;
+  }
+
+  // Exit silently if the download destination is not a local file.
+  nsCOMPtr<nsIFileURL> destinationFileURL = do_QueryInterface(aDestination);
+  if (!destinationFileURL) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIFile> destinationFile;
+  rv = destinationFileURL->GetFile(getter_AddRefs(destinationFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString destinationFileName;
+  rv = destinationFile->GetLeafName(destinationFileName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString destinationURISpec;
+  rv = destinationFileURL->GetSpec(destinationURISpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Use annotations for storing the additional download metadata.
+  nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
+  NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
+
+  (void)annosvc->SetPageAnnotationString(
+    aSource,
+    DESTINATIONFILEURI_ANNO,
+    NS_ConvertUTF8toUTF16(destinationURISpec),
+    0,
+    nsIAnnotationService::EXPIRE_WITH_HISTORY
+  );
+
+  (void)annosvc->SetPageAnnotationString(
+    aSource,
+    DESTINATIONFILENAME_ANNO,
+    destinationFileName,
+    0,
+    nsIAnnotationService::EXPIRE_WITH_HISTORY
+  );
+
+  // In case we are downloading a file that does not correspond to a web
+  // page for which the title is present, we populate the otherwise empty
+  // history title with the name of the destination file, to allow it to be
+  // visible and searchable in history results.
+  nsAutoString title;
+  if (NS_SUCCEEDED(GetPageTitle(aSource, title)) && title.IsEmpty()) {
+    (void)SetPageTitle(aSource, destinationFileName);
+  }
+
+  return NS_OK;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsPIPlacesDatabase
 
 NS_IMETHODIMP
 nsNavHistory::GetDBConnection(mozIStorageConnection **_DBConnection)
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -60,16 +60,21 @@ XPCOMUtils.defineLazyGetter(this, "Servi
   return Services;
 });
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
+XPCOMUtils.defineLazyGetter(this, "FileUtils", function() {
+  Cu.import("resource://gre/modules/FileUtils.jsm");
+  return FileUtils;
+});
+
 XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
   Cu.import("resource://gre/modules/PlacesUtils.jsm");
   return PlacesUtils;
 });
 
 
 function LOG(aMsg) {
   aMsg = ("*** PLACES TESTS: " + aMsg);
--- a/toolkit/components/places/tests/unit/test_download_history.js
+++ b/toolkit/components/places/tests/unit/test_download_history.js
@@ -1,47 +1,15 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** 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 Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2007
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
- *   Marco Bonardo <mak77@bonardo.net>
- *
- * 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 ***** */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * The first part of this file runs a series of tests for the synchronous
+ * behavior of the nsIDownloadHistory::AddDownload function.
+ */
 
 // Get services
 var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
               getService(Ci.nsINavHistoryService);
 let bh = histsvc.QueryInterface(Ci.nsIBrowserHistory);
 var os = Cc["@mozilla.org/observer-service;1"].
          getService(Ci.nsIObserverService);
 var prefs = Cc["@mozilla.org/preferences-service;1"].
@@ -154,9 +122,98 @@ function run_test() {
     (tests.shift())();
 
     // Cleanup
     bh.removeAllPages();
     observer.topicReceived = false;
   }
 
   os.removeObserver(observer, NS_LINK_VISITED_EVENT_TOPIC);
+
+  // Asynchronous part of the test.
+  test_dh_details();
 }
+
+/**
+ * The second part of this file tests that nsIDownloadHistory::AddDownload saves
+ * the additional download details if the optional destination URL is specified.
+ */
+
+function test_dh_details()
+{
+  do_test_pending();
+
+  const SOURCE_URI = uri("http://example.com/test_download_history_details");
+  const DEST_FILE_NAME = "dest.txt";
+
+  // We must build a real, valid file URI for the destination.
+  let destFileUri = NetUtil.newURI(FileUtils.getFile("TmpD", [DEST_FILE_NAME]));
+
+  let titleSet = false;
+  let destinationFileUriSet = false;
+  let destinationFileNameSet = false;
+
+  function checkFinished()
+  {
+    if (titleSet && destinationFileUriSet && destinationFileNameSet) {
+      PlacesUtils.annotations.removeObserver(annoObserver);
+      PlacesUtils.history.removeObserver(historyObserver);
+
+      // Cleanup.
+      bh.removeAllPages();
+      do_test_finished();
+    }
+  };
+
+  let annoObserver = {
+    onPageAnnotationSet: function AO_onPageAnnotationSet(aPage, aName)
+    {
+      if (aPage.equals(SOURCE_URI)) {
+        let value = PlacesUtils.annotations.getPageAnnotation(aPage, aName);
+        switch (aName)
+        {
+          case "downloads/destinationFileURI":
+            destinationFileUriSet = true;
+            do_check_eq(value, destFileUri.spec);
+            break;
+          case "downloads/destinationFileName":
+            destinationFileNameSet = true;
+            do_check_eq(value, DEST_FILE_NAME);
+            break;
+        }
+        checkFinished();
+      }
+    },
+    onItemAnnotationSet: function() {},
+    onPageAnnotationRemoved: function() {},
+    onItemAnnotationRemoved: function() {}
+  }
+
+  let historyObserver = {
+    onBeginUpdateBatch: function() {},
+    onEndUpdateBatch: function() {},
+    onVisit: function() {},
+    onTitleChanged: function HO_onTitleChanged(aURI, aPageTitle)
+    {
+      if (aURI.equals(SOURCE_URI)) {
+        titleSet = true;
+        do_check_eq(aPageTitle, DEST_FILE_NAME);
+        checkFinished();
+      }
+    },
+    onBeforeDeleteURI: function() {},
+    onDeleteURI: function() {},
+    onClearHistory: function() {},
+    onPageChanged: function() {},
+    onDeleteVisits: function() {}   
+  };
+
+  PlacesUtils.annotations.addObserver(annoObserver, false);
+  PlacesUtils.history.addObserver(historyObserver, false);
+
+  // Both null values and remote URIs should not cause errors.
+  dh.addDownload(SOURCE_URI, null, Date.now() * 1000);
+  dh.addDownload(SOURCE_URI, null, Date.now() * 1000, null);
+  dh.addDownload(SOURCE_URI, null, Date.now() * 1000, uri("http://localhost/"));
+
+  // Valid local file URIs should cause the download details to be saved.
+  dh.addDownload(SOURCE_URI, null, Date.now() * 1000, destFileUri);
+}
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -39,16 +39,21 @@
 /**
  * This file lists Telemetry histograms collected by Firefox.  The format is
  *
  *    HISTOGRAM(id, minimum, maximum, bucket count, histogram kind,
  *              human-readable description for about:telemetry)
  *
  */
 
+/**
+ * a11y telemetry
+ */
+HISTOGRAM(A11Y_INSTANTIATED, 0, 1, 2, BOOLEAN, "has accessibility support been instantiated")
+
 HISTOGRAM(CYCLE_COLLECTOR, 1, 10000, 50, EXPONENTIAL, "Time spent on one cycle collection (ms)")
 HISTOGRAM(CYCLE_COLLECTOR_VISITED_REF_COUNTED, 1, 300000, 50, EXPONENTIAL, "Number of ref counted objects visited by the cycle collector")
 HISTOGRAM(CYCLE_COLLECTOR_VISITED_GCED, 1, 300000, 50, EXPONENTIAL, "Number of JS objects visited by the cycle collector")
 HISTOGRAM(CYCLE_COLLECTOR_COLLECTED, 1, 100000, 50, EXPONENTIAL, "Number of objects collected by the cycle collector")
 HISTOGRAM(TELEMETRY_PING, 1, 3000, 10, EXPONENTIAL, "Time taken to submit telemetry info (ms)")
 HISTOGRAM(TELEMETRY_SUCCESS, 0, 1, 2, BOOLEAN,  "Successful telemetry submission")
 HISTOGRAM(MEMORY_JS_COMPARTMENTS_SYSTEM, 1, 1000, 10, EXPONENTIAL, "Total JavaScript compartments used for add-ons and internals.")
 HISTOGRAM(MEMORY_JS_COMPARTMENTS_USER, 1, 1000, 10, EXPONENTIAL, "Total JavaScript compartments used for web pages")
--- a/toolkit/mozapps/extensions/Makefile.in
+++ b/toolkit/mozapps/extensions/Makefile.in
@@ -34,22 +34,22 @@
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
+include $(DEPTH)/config/autoconf.mk
+
 ifeq (,$(filter aurora beta release,$(MOZ_UPDATE_CHANNEL)))
-DEFINES += -DMOZ_COMPATABILITY_NIGHTLY=1
+DEFINES += -DMOZ_COMPATIBILITY_NIGHTLY=1
 endif
 
-include $(DEPTH)/config/autoconf.mk
-
 MODULE = extensions
 
 XPIDLSRCS = \
   amIInstallTrigger.idl \
   amIWebInstallListener.idl \
   amIWebInstaller.idl \
   $(NULL)
 
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -120,17 +120,17 @@ const PREFIX_NS_EM                    = 
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
 const BRANCH_REGEXP                   = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
 
 const DB_SCHEMA                       = 5;
 const REQ_VERSION                     = 2;
 
-#ifdef MOZ_COMPATABILITY_NIGHTLY
+#ifdef MOZ_COMPATIBILITY_NIGHTLY
 const PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE +
                                     ".nightly";
 #else
 const PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + "." +
                                     Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
 #endif
 
 // Properties that exist in the install manifest
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -57,17 +57,17 @@ const PREF_CHECK_UPDATE_SECURITY = "exte
 const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault";
 const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
 const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled";
 const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden";
 const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
 
 const BRANCH_REGEXP = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
 
-#ifdef MOZ_COMPATABILITY_NIGHTLY
+#ifdef MOZ_COMPATIBILITY_NIGHTLY
 const PREF_CHECK_COMPATIBILITY = PREF_CHECK_COMPATIBILITY_BASE +
                                  ".nightly";
 #else
 const PREF_CHECK_COMPATIBILITY = PREF_CHECK_COMPATIBILITY_BASE + "." +
                                  Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
 #endif
 
 const LOADING_MSG_DELAY = 100;
@@ -1101,17 +1101,17 @@ var gViewController = {
         var files = fp.files;
         var installs = [];
 
         function buildNextInstall() {
           if (!files.hasMoreElements()) {
             if (installs.length > 0) {
               // Display the normal install confirmation for the installs
               AddonManager.installAddonsFromWebpage("application/x-xpinstall",
-                                                    this, null, installs);
+                                                    window, null, installs);
             }
             return;
           }
 
           var file = files.getNext();
           AddonManager.getInstallForFile(file, function(aInstall) {
             installs.push(aInstall);
             buildNextInstall();
--- a/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
@@ -25,33 +25,31 @@ const URI_EXTENSION_UPDATE_DIALOG = "chr
 function test() {
   requestLongerTimeout(2);
   waitForExplicitFinish();
 
   run_next_test();
 }
 
 function end_test() {
-  Services.prefs.clearUserPref("extensions.update.url");
-
   // Test generates a lot of available installs so just cancel them all
   AddonManager.getAllInstalls(function(aInstalls) {
     aInstalls.forEach(function(aInstall) {
       aInstall.cancel();
     });
 
     finish();
   });
 }
 
 function install_test_addons(aCallback) {
   var installs = [];
 
   // Use a blank update URL
-  Services.prefs.setCharPref("extensions.update.url", TESTROOT + "missing.rdf");
+  Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "missing.rdf");
 
   ["browser_bug557956_1",
    "browser_bug557956_2",
    "browser_bug557956_3",
    "browser_bug557956_4",
    "browser_bug557956_5",
    "browser_bug557956_6",
    "browser_bug557956_7",
@@ -64,17 +62,17 @@ function install_test_addons(aCallback) 
 
   var listener = {
     installCount: 0,
 
     onInstallEnded: function() {
       this.installCount++;
       if (this.installCount == installs.length) {
         // Switch to the test update URL
-        Services.prefs.setCharPref("extensions.update.url", TESTROOT + "browser_bug557956.rdf");
+        Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "browser_bug557956.rdf");
 
         aCallback();
       }
     }
   };
 
   installs.forEach(function(aInstall) {
     aInstall.addListener(listener);
--- a/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
@@ -4,16 +4,17 @@
 
 // Tests bug 567127 - Add install button to the add-ons manager
 
 
 var gFilePickerFiles = [];
 var gMockFilePickerFactory;
 var gMockFilePickerFactoryCID;
 var gManagerWindow;
+var gSawInstallNotification = false;
 
 function MockFilePicker() { }
 
 MockFilePicker.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIFilePicker]),
   init: function(aParent, aTitle, aMode) { },
   appendFilters: function(aFilterMask) { },
   appendFilter: function(aTitle, aFilter) { },
@@ -92,16 +93,26 @@ WindowOpenListener.prototype = {
 
     // Let the window close complete
     executeSoon(this.closecallback);
     this.closecallback = null;
   }
 };
 
 
+var gInstallNotificationObserver = {
+  observe: function(aSubject, aTopic, aData) {
+    var installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
+    isnot(installInfo.originatingWindow, null, "Notification should have non-null originatingWindow");
+    gSawInstallNotification = true;
+    Services.obs.removeObserver(this, "addon-install-started");
+  }
+};
+
+
 function test_confirmation(aWindow, aExpectedURLs) {
   var list = aWindow.document.getElementById("itemList");
   is(list.childNodes.length, aExpectedURLs.length, "Should be the right number of installs");
 
   aExpectedURLs.forEach(function(aURL) {
     var node = list.firstChild;
     while (node) {
       if (node.url == aURL) {
@@ -130,30 +141,35 @@ function test() {
 
   open_manager("addons://list/extension", function(aWindow) {
     gManagerWindow = aWindow;
     run_next_test();
   });
 }
 
 function end_test() {
+  is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
+
   var compReg = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
   compReg.unregisterFactory(gMockFilePickerFactoryCID,
                             gMockFilePickerFactory);
   close_manager(gManagerWindow, function() {
     finish();
   });
 }
 
 
 add_test(function() {
   var filePaths = [
                    get_addon_file_url("browser_bug567127_1.xpi"),
                    get_addon_file_url("browser_bug567127_2.xpi")
                   ];
   gFilePickerFiles = filePaths.map(function(aPath) aPath.file);
   
+  Services.obs.addObserver(gInstallNotificationObserver,
+                           "addon-install-started", false);
+
   new WindowOpenListener(INSTALL_URI, function(aWindow) {
     test_confirmation(aWindow, filePaths.map(function(aPath) aPath.spec));
   }, run_next_test);
-  
+
   gManagerWindow.gViewController.doCommand("cmd_installFromFile");
 });
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -15,43 +15,46 @@ pathParts.splice(pathParts.length - 1);
 pathParts.push("browser");
 
 const RELATIVE_DIR = pathParts.slice(4).join("/") + "/";
 
 const TESTROOT = "http://example.com/" + RELATIVE_DIR;
 const TESTROOT2 = "http://example.org/" + RELATIVE_DIR;
 const CHROMEROOT = pathParts.join("/") + "/";
 const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
+const PREF_UPDATEURL = "extensions.update.url";
 
 const MANAGER_URI = "about:addons";
 const INSTALL_URI = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
 const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
 const PREF_SEARCH_MAXRESULTS = "extensions.getAddons.maxResults";
 
 var gPendingTests = [];
 var gTestsRun = 0;
 var gTestStart = null;
 
 var gUseInContentUI = !gTestInWindow && ("switchToTabHavingURI" in window);
 
 var gDiscoveryURL = Services.prefs.getCharPref(PREF_DISCOVERURL);
+var gUpdateURL = Services.prefs.getCharPref(PREF_UPDATEURL);
 
 // Turn logging on for all tests
 Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
 // Turn off remote results in searches
 Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 0);
 registerCleanupFunction(function() {
   Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
   try {
     Services.prefs.clearUserPref(PREF_SEARCH_MAXRESULTS);
   }
   catch (e) {
   }
 
   Services.prefs.setCharPref(PREF_DISCOVERURL, gDiscoveryURL);
+  Services.prefs.setCharPref(PREF_UPDATEURL, gUpdateURL);
 
   // Throw an error if the add-ons manager window is open anywhere
   var windows = Services.wm.getEnumerator("Addons:Manager");
   if (windows.hasMoreElements())
     ok(false, "Found unexpected add-ons manager window still open");
   while (windows.hasMoreElements())
     windows.getNext().QueryInterface(Ci.nsIDOMWindow).close();
 
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -363,16 +363,22 @@ function shutdownManager() {
             getService(AM_Ci.nsIThreadManager).
             mainThread;
 
   // Wait until we observe the shutdown notifications
   while (!repositoryShutdown || !xpiShutdown) {
     if (thr.hasPendingEvents())
       thr.processNextEvent(false);
   }
+
+  // Force the XPIProvider provider to reload since it defines some constants on
+  // load that need to change during tests
+  let scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
+  AddonManagerPrivate.unregisterProvider(scope.XPIProvider);
+  Components.utils.unload("resource://gre/modules/XPIProvider.jsm");
 }
 
 function loadAddonsList() {
   function readDirectories(aSection) {
     var dirs = [];
     var keys = parser.getKeys(aSection);
     while (keys.hasMore()) {
       let descriptor = parser.getString(aSection, keys.getNext());
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
@@ -1080,20 +1080,22 @@ function check_test_23() {
 
       let dir = do_get_addon_root_uri(profileDir, "bootstrap1@tests.mozilla.org");
       do_check_eq(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
 
       AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
         do_check_eq(list.length, 0);
 
         restartManager();
-        b1.uninstall();
-        restartManager();
+        AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+          b1.uninstall();
+          restartManager();
 
-        testserver.stop(run_test_24);
+          testserver.stop(run_test_24);
+        });
       });
     });
   });
 }
 
 // Tests that we recover from a broken preference
 function run_test_24() {
   resetPrefs();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js
@@ -255,20 +255,22 @@ function run_test_1() {
     dbfile.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
 
     // Accessing the add-ons should open and recover the database
     AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                  "addon2@tests.mozilla.org",
                                  "addon3@tests.mozilla.org",
                                  "addon4@tests.mozilla.org",
                                  "addon5@tests.mozilla.org",
+                                 "addon6@tests.mozilla.org",
+                                 "addon7@tests.mozilla.org",
                                  "theme1@tests.mozilla.org",
                                  "theme2@tests.mozilla.org"], function([a1, a2, a3,
-                                                                        a4, a5, t1,
-                                                                        t2]) {
+                                                                        a4, a5, a6,
+                                                                        a7, t1, t2]) {
       // Should be correctly recovered
       do_check_neq(a1, null);
       do_check_true(a1.isActive);
       do_check_false(a1.userDisabled);
       do_check_false(a1.appDisabled);
       do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
 
       // Should be correctly recovered
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -385,22 +385,32 @@ endif
 ifdef MOZ_OMNIJAR
 
 ifdef GENERATE_CACHE
 ifneq (1_,$(if $(CROSS_COMPILE),1,0)_$(UNIVERSAL_BINARY))
 ifdef RUN_TEST_PROGRAM
 _ABS_RUN_TEST_PROGRAM = $(call core_abspath,$(RUN_TEST_PROGRAM))
 endif
 
+ifdef LIBXUL_SDK
+PRECOMPILE_DIR=XCurProcD
+PRECOMPILE_RESOURCE=app
+PRECOMPILE_GRE=$(LIBXUL_DIST)/bin
+else
+PRECOMPILE_DIR=GreD
+PRECOMPILE_RESOURCE=gre
+PRECOMPILE_GRE=$$PWD
+endif
+
 GENERATE_CACHE = \
-  $(_ABS_RUN_TEST_PROGRAM) $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) -g "$$PWD" -a "$$PWD" -f $(MOZILLA_DIR)/toolkit/mozapps/installer/precompile_cache.js -e "populate_startupcache('omni.jar', 'startupCache.zip');" && \
+  $(_ABS_RUN_TEST_PROGRAM) $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) -g "$(PRECOMPILE_GRE)" -a "$$PWD" -f $(MOZILLA_DIR)/toolkit/mozapps/installer/precompile_cache.js -e "populate_startupcache('$(PRECOMPILE_DIR)', 'omni.jar', 'startupCache.zip');" && \
   rm -rf jsloader && \
   $(UNZIP) startupCache.zip && \
   rm startupCache.zip && \
-  $(ZIP) -r9m omni.jar jsloader
+  $(ZIP) -r9m omni.jar jsloader/resource/$(PRECOMPILE_RESOURCE)
 endif
 endif
 
 GENERATE_CACHE ?= true
 
 OMNIJAR_FILES	= \
   chrome \
   chrome.manifest \
--- a/toolkit/mozapps/installer/precompile_cache.js
+++ b/toolkit/mozapps/installer/precompile_cache.js
@@ -64,34 +64,34 @@ function load(url) {
 
 function load_entries(entries, prefix) {
   while (entries.hasMore()) {
     var c = entries.getNext();
     load(prefix + c);
   }
 }
 
-function getGreDir() {
+function getDir(prop) {
   return Cc["@mozilla.org/file/directory_service;1"].
-    getService(Ci.nsIProperties).get("GreD", Ci.nsIFile);
+    getService(Ci.nsIProperties).get(prop, Ci.nsIFile);
 }
 
 function openJar(file) {
   var zipreader = Cc["@mozilla.org/libjar/zip-reader;1"].
     createInstance(Ci.nsIZipReader);
   zipreader.open(file);
   return zipreader;
 }
 
-function populate_startupcache(omnijarName, startupcacheName) {
-  var file = getGreDir();
+function populate_startupcache(prop, omnijarName, startupcacheName) {
+  var file = getDir(prop);
   file.append(omnijarName);
   zipreader = openJar(file);
 
-  var scFile = getGreDir();
+  var scFile = getDir(prop);
   scFile.append(startupcacheName);
   setenv("MOZ_STARTUP_CACHE", scFile.path);
 
   let prefix = "resource:///";
 
   load_entries(zipreader.findEntries("components/*js"), prefix);
   load_entries(zipreader.findEntries("modules/*js"), prefix);
   load_entries(zipreader.findEntries("modules/*jsm"), prefix);
--- a/toolkit/themes/winstripe/mozapps/extensions/newaddon.css
+++ b/toolkit/themes/winstripe/mozapps/extensions/newaddon.css
@@ -129,32 +129,8 @@
 
 #restartSpacer {
   -moz-box-flex: 1;
 }
 
 #later {
   color: GrayText;
 }
-
-.addon-control {
-  -moz-appearance: none;
-  color: black;
-  padding: 0 5px;
-  background: -moz-linear-gradient(rgba(251, 252, 253, 0.95), rgba(246, 247, 248, 0) 49%, 
-                                   rgba(211, 212, 213, 0.45) 51%, rgba(225, 226, 229, 0.3));
-  background-clip: padding-box;
-  border-radius: 3px;
-  border: 1px solid rgba(31, 64, 100, 0.4);
-  border-top-color: rgba(31, 64, 100, 0.3);
-  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.25) inset,
-              0 0 2px 1px rgba(255, 255, 255, 0.25) inset;
-}
-
-.addon-control:active:hover {
-  background-color: rgba(61, 76, 92, 0.2);
-  border-color: rgba(39, 53, 68, 0.5);
-  box-shadow: 0 0 3px 1px rgba(39, 53, 68, 0.2) inset;
-}
-
-.addon-control > .button-box {
-  padding: 1px;
-}
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -1772,17 +1772,21 @@ NS_IMETHODIMP nsExternalAppHandler::OnSt
   }
 
   // Now let's add the download to history
   nsCOMPtr<nsIDownloadHistory> dh(do_GetService(NS_DOWNLOADHISTORY_CONTRACTID));
   if (dh) {
     nsCOMPtr<nsIURI> referrer;
     if (aChannel)
       NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
-    dh->AddDownload(mSourceUrl, referrer, mTimeDownloadStarted);
+
+    nsCOMPtr<nsIURI> target;
+    NS_NewFileURI(getter_AddRefs(target), mFinalFileDestination);
+
+    dh->AddDownload(mSourceUrl, referrer, mTimeDownloadStarted, target);
   }
 
   return NS_OK;
 }
 
 // Convert error info into proper message text and send OnStatusChange notification
 // to the web progress listener.
 void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequest *aRequest, const nsAFlatString &path)
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -8880,19 +8880,22 @@ PRBool nsWindow::UseTrackPointHack()
   return GetInputWorkaroundPref("ui.trackpoint_hack.enabled",
                                 sDefaultTrackPointHack);
 }
 
 static PRBool
 HasRegistryKey(HKEY aRoot, PRUnichar* aName)
 {
   HKEY key;
-  LONG result = ::RegOpenKeyExW(aRoot, aName, 0, KEY_READ, &key);
-  if (result != ERROR_SUCCESS)
-    return PR_FALSE;
+  LONG result = ::RegOpenKeyExW(aRoot, aName, 0, KEY_READ | KEY_WOW64_32KEY, &key);
+  if (result != ERROR_SUCCESS) {
+    result = ::RegOpenKeyExW(aRoot, aName, 0, KEY_READ | KEY_WOW64_64KEY, &key);
+    if (result != ERROR_SUCCESS)
+      return PR_FALSE;
+  }
   ::RegCloseKey(key);
   return PR_TRUE;
 }
 
 /**
  * Gets the value of a string-typed registry value.
  *
  * @param aRoot The registry root to search in.
@@ -8908,19 +8911,22 @@ HasRegistryKey(HKEY aRoot, PRUnichar* aN
 static PRBool
 GetRegistryKey(HKEY aRoot, PRUnichar* aKeyName, PRUnichar* aValueName, PRUnichar* aBuffer, DWORD aBufferLength)
 {
   if (!aKeyName) {
     return PR_FALSE;
   }
 
   HKEY key;
-  LONG result = ::RegOpenKeyExW(aRoot, aKeyName, NULL, KEY_READ, &key);
-  if (result != ERROR_SUCCESS)
-    return PR_FALSE;
+  LONG result = ::RegOpenKeyExW(aRoot, aKeyName, NULL, KEY_READ | KEY_WOW64_32KEY, &key);
+  if (result != ERROR_SUCCESS) {
+    result = ::RegOpenKeyExW(aRoot, aKeyName, NULL, KEY_READ | KEY_WOW64_64KEY, &key);
+    if (result != ERROR_SUCCESS)
+      return PR_FALSE;
+  }
   DWORD type;
   result = ::RegQueryValueExW(key, aValueName, NULL, &type, (BYTE*) aBuffer, &aBufferLength);
   ::RegCloseKey(key);
   if (result != ERROR_SUCCESS || type != REG_SZ)
     return PR_FALSE;
   if (aBuffer)
     aBuffer[aBufferLength / sizeof(*aBuffer) - 1] = 0;
   return PR_TRUE;
@@ -8934,17 +8940,22 @@ IsObsoleteSynapticsDriver()
                                    L"Software\\Synaptics\\SynTP\\Install",
                                    L"DriverVersion",
                                    buf,
                                    sizeof buf);
   if (!foundKey)
     return PR_FALSE;
 
   int majorVersion = wcstol(buf, NULL, 10);
-  return majorVersion < 15;
+  int minorVersion = 0;
+  PRUnichar* p = wcschr(buf, L'.');
+  if (p) {
+    minorVersion = wcstol(p + 1, NULL, 10);
+  }
+  return majorVersion < 15 || majorVersion == 15 && minorVersion == 0;
 }
 
 static PRInt32
 GetElantechDriverMajorVersion()
 {
   PRUnichar buf[40];
   // The driver version is found in one of these two registry keys.
   PRBool foundKey = GetRegistryKey(HKEY_CURRENT_USER,
--- a/xpcom/tests/TestHarness.h
+++ b/xpcom/tests/TestHarness.h
@@ -51,16 +51,17 @@
 #include "nsStringGlue.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsIDirectoryService.h"
 #include "nsIFile.h"
 #include "nsIProperties.h"
 #include "nsXULAppAPI.h"
+#include "jsdbgapi.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
 
 static PRUint32 gFailCount = 0;
 
 /**
  * Prints the given failure message and arguments using printf, prepending
@@ -90,27 +91,16 @@ void passed(const char* test)
 {
   printf("TEST-PASS | %s\n", test);
 }
 
 //-----------------------------------------------------------------------------
 // Code profiling
 //
 static const char* gCurrentProfile;
-static PRBool gProfilerTriedInit = PR_FALSE;
-static PRBool gProfilerInited = PR_FALSE;
-
-// Platform profilers must implement these functions.
-// Init and deinit are guaranteed to only be called once, and
-// StartProfile/StopProfile may assume that they are only called
-// when the profiler has successfully been initialized.
-static PRBool _PlatformInitProfiler();
-static PRBool _PlatformStartProfile(const char* profileName);
-static PRBool _PlatformStopProfile(const char* profileName);
-static PRBool _PlatformDeinitProfiler();
 
 /**
  * If the build has been configured properly, start the best code profiler
  * available on this platform.
  *
  * This is NOT thread safe.
  *
  * @precondition Profiling is not started
@@ -119,117 +109,45 @@ static PRBool _PlatformDeinitProfiler();
  *                    to this name, but check your platform's profiler
  *                    documentation for what this means.
  * @return PR_TRUE if profiling was available and successfully started.
  * @see StopProfiling
  */
 inline PRBool
 StartProfiling(const char* profileName)
 {
-    if (!gProfilerTriedInit) {
-        gProfilerTriedInit = PR_TRUE;
-        gProfilerInited = _PlatformInitProfiler();
-    }
-    if (!gProfilerInited)
-        return PR_FALSE;
-
     NS_ASSERTION(profileName, "need a name for this profile");
     NS_PRECONDITION(!gCurrentProfile, "started a new profile before stopping another");
 
-    PRBool rv = _PlatformStartProfile(profileName);
+    JSBool ok = JS_StartProfiling(profileName);
     gCurrentProfile = profileName;
-    return rv;
+    return ok ? PR_TRUE : PR_FALSE;
 }
 
 /**
  * Stop the platform's profiler.  For what this means, what happens after
  * stopping, and how the profile data can be accessed, check the 
  * documentation of your platform's profiler.
  *
  * This is NOT thread safe.
  *
  * @precondition Profiling was started
  * @return PR_TRUE if profiling was successfully stopped.
  * @see StartProfiling
  */
 inline PRBool
 StopProfiling()
 {
-    NS_ASSERTION(gProfilerTriedInit, "tried to stop profile before starting one");
-    if (!gProfilerInited)
-        return PR_FALSE;
-
     NS_PRECONDITION(gCurrentProfile, "tried to stop profile before starting one");
 
     const char* profileName = gCurrentProfile;
     gCurrentProfile = 0;
-    return _PlatformStopProfile(profileName);
-}
-
-//--------------------------------------------------
-// Shark impl
-#if defined(MOZ_SHARK)
-#include "jsdbgapi.h"
-
-static PRBool
-_PlatformInitProfiler()
-{
-    return PR_TRUE;
-}
-
-static PRBool
-_PlatformStartProfile(const char* profileName)
-{
-    return JS_StartProfiling() ? PR_TRUE : PR_FALSE;
-}
-
-static PRBool
-_PlatformStopProfile(const char* profileName)
-{
-    JS_StopProfiling();
-    return PR_TRUE;
-}
-
-static PRBool
-_PlatformDeinitProfiler()
-{
-    return PR_TRUE;
+    return JS_StopProfiling(profileName) ? PR_TRUE : PR_FALSE;
 }
 
-//--------------------------------------------------
-// Default, no-profiler impl
-#else 
-
-static PRBool
-_PlatformInitProfiler()
-{
-    NS_WARNING("Profiling is not available/configured for your platform.");
-    return PR_FALSE;
-}
-static PRBool
-_PlatformStartProfile(const char* profileName)
-{
-    NS_WARNING("Profiling is not available/configured for your platform.");
-    return PR_FALSE;
-}
-static PRBool
-_PlatformStopProfile(const char* profileName)
-{
-    NS_WARNING("Profiling is not available/configured for your platform.");
-    return PR_FALSE;
-}
-static PRBool
-_PlatformDeinitProfiler()
-{
-    NS_WARNING("Profiling is not available/configured for your platform.");
-    return PR_FALSE;
-}
-
-#endif
-
 //-----------------------------------------------------------------------------
 
 class ScopedLogging
 {
 public:
     ScopedLogging()
     {
         NS_LogInit();
@@ -259,20 +177,16 @@ class ScopedXPCOM : public nsIDirectoryS
         fail("NS_InitXPCOM2 returned failure code 0x%x", rv);
         mServMgr = NULL;
         return;
       }
     }
 
     ~ScopedXPCOM()
     {
-      if (gProfilerInited)
-        if (!_PlatformDeinitProfiler())
-          NS_WARNING("Problem shutting down profiler");
-
       // If we created a profile directory, we need to remove it.
       if (mProfD) {
         if (NS_FAILED(mProfD->Remove(PR_TRUE)))
           NS_WARNING("Problem removing profile direrctory");
 
         mProfD = nsnull;
       }