merged m-c to fx-team, browser_595601-restore_hidden.js conflicted
authorRob Campbell <rcampbell@mozilla.com>
Tue, 16 Aug 2011 12:21:00 -0300
changeset 75386 e765c8f565c63da5550aa59bf5d6358a4392fee0
parent 75385 b44e9053d17d40417911a7b0ac1f209117b1814e (current diff)
parent 75367 55c3a125c9bb94345ee529fc87cbbe20b22a7ddf (diff)
child 75387 9967f28c64e183bd85ae5f761c3ab2d8cb5adbc3
child 75468 a2b79dba671030d52bf6ee42227d0e1c6d3c7207
push id21011
push userrcampbell@mozilla.com
push dateTue, 16 Aug 2011 15:35:10 +0000
treeherdermozilla-central@e765c8f565c6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone8.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merged m-c to fx-team, browser_595601-restore_hidden.js conflicted
browser/components/sessionstore/src/nsSessionStore.js
browser/components/sessionstore/test/browser/Makefile.in
browser/components/sessionstore/test/browser/browser_595601-restore_hidden.js
browser/themes/winstripe/browser/browser.css
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
@@ -2901,17 +2924,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);
@@ -3037,18 +3060,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) {
@@ -3967,26 +3990,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;
   },
 
   /**
    * Set the given window's busy state
    * @param aWindow the window
    * @param aValue the window's busy state
    */
   _setWindowStateBusyValue:
--- 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 \
 	browser_662812.js \
 	$(NULL)
 
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	browser_625016.js \
--- 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_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/devtools/sourceeditor/test/Makefile.in
+++ b/browser/devtools/sourceeditor/test/Makefile.in
@@ -43,9 +43,9 @@ relativesrcdir = browser/devtools/source
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
 		browser_sourceeditor_initialization.js \
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+#	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- 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 "l">
+
 <!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/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -963,16 +963,24 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 
 /* tabview button */
 
 #tabview-button,
 #menu_tabview {
   list-style-image: url(chrome://browser/skin/tabview/tabview.png);
 }
 
+%ifdef WINSTRIPE_AERO
+#TabsToolbar > #tabview-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+#TabsToolbar > toolbarpaletteitem > #tabview-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+%endif
+#tabview-button:-moz-lwtheme-brighttext {
+  list-style-image: url(chrome://browser/skin/tabview/tabview-inverted.png);
+}
+
 #tabview-button {
   -moz-image-region: rect(0, 90px, 18px, 72px);
 }
 
 #tabview-button[groups="0"] {
   -moz-image-region: rect(0, 18px, 18px, 0);
 }
 
@@ -1880,33 +1888,36 @@ richlistitem[type~="action"][actiontype=
   .tab-content {
     min-height: -moz-calc(6.8mozmm - 7px); /* subtract borders from the desired height */
   }
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
-  -moz-image-region: rect(0, 15px, 17px, 0);
   margin: 0;
   padding-right: 2px;
   border-right: 2px solid transparent;
   background-origin: border-box;
 }
 
+%ifdef WINSTRIPE_AERO
+.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+.tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+%endif
+.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-lwtheme-brighttext,
+.tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-lwtheme-brighttext {
+  list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png);
+}
+
 .tabbrowser-arrowscrollbox > .scrollbutton-up[disabled],
 .tabbrowser-arrowscrollbox > .scrollbutton-down[disabled] {
   opacity: .4;
 }
 
-.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover:active,
-.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover:active {
-  -moz-image-region: rect(0, 30px, 17px, 15px);
-}
-
 .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl),
 .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr) {
   -moz-transform: scaleX(-1);
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-transition: 1s background-color ease-out;
 }
@@ -1925,59 +1936,63 @@ richlistitem[type~="action"][actiontype=
   margin-top: -1px;
   margin-bottom: -1px;
 }
 
 .tabs-newtab-button,
 #TabsToolbar > #new-tab-button,
 #TabsToolbar > toolbarpaletteitem > #new-tab-button {
   list-style-image: url(chrome://browser/skin/tabbrowser/newtab.png);
-  -moz-image-region: rect(0, 16px, 18px, 0);
+  -moz-image-region: auto;
+}
+
+%ifdef WINSTRIPE_AERO
+#TabsToolbar > #new-tab-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+#TabsToolbar > toolbarpaletteitem > #new-tab-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+%endif
+.tabs-newtab-button:-moz-lwtheme-brighttext,
+#TabsToolbar > #new-tab-button:-moz-lwtheme-brighttext,
+#TabsToolbar > toolbarpaletteitem > #new-tab-button:-moz-lwtheme-brighttext {
+  list-style-image: url(chrome://browser/skin/tabbrowser/newtab-inverted.png);
 }
 
 .tabs-newtab-button {
   width: 28px;
 }
 
 #TabsToolbar > #new-tab-button {
   width: 26px;
 }
 
-.tabs-newtab-button:hover:active,
-#TabsToolbar > #new-tab-button:hover:active {
-  -moz-image-region: rect(0, 32px, 18px, 16px);
-}
-
 #alltabs-button {
   list-style-image: url("chrome://browser/skin/tabbrowser/alltabs.png");
   -moz-image-region: rect(0, 14px, 16px, 0);
 }
 
-#alltabs-button:hover:active {
-  -moz-image-region: rect(0, 28px, 16px, 14px);
-}
-
 #alltabs-button[type="menu"] {
   list-style-image: url("chrome://browser/skin/mainwindow-dropdown-arrow.png");
   -moz-image-region: rect(0, 13px, 11px, 0);
 }
 
+%ifdef WINSTRIPE_AERO
+#TabsToolbar > #alltabs-button[type="menu"]:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+#TabsToolbar > toolbarpaletteitem > #alltabs-button[type="menu"]:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+%endif
+#alltabs-button[type="menu"]:-moz-lwtheme-brighttext {
+  list-style-image: url(chrome://browser/skin/mainwindow-dropdown-arrow-inverted.png);
+}
+
 #alltabs-button[type="menu"] > .toolbarbutton-icon {
   margin: 3px 0;
 }
 
 #alltabs-button[type="menu"] > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
-#alltabs-button[type="menu"]:hover:active,
-#alltabs-button[type="menu"][open="true"] {
-  -moz-image-region: rect(0, 26px, 11px, 13px);
-}
-
 /* All tabs menupopup */
 .alltabs-item > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .alltabs-item[selected="true"] {
   font-weight: bold;
 }
--- a/browser/themes/winstripe/browser/jar.mn
+++ b/browser/themes/winstripe/browser/jar.mn
@@ -20,16 +20,17 @@ browser.jar:
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png                                (Info.png)
         skin/classic/browser/identity.png                            (identity.png)
         skin/classic/browser/keyhole-forward-mask.svg
         skin/classic/browser/KUI-background.png
         skin/classic/browser/KUI-close.png
         skin/classic/browser/mainwindow-dropdown-arrow.png
+        skin/classic/browser/mainwindow-dropdown-arrow-inverted.png
         skin/classic/browser/pageInfo.css
         skin/classic/browser/pageInfo.png                            (pageInfo.png)
         skin/classic/browser/page-livemarks.png                      (feeds/feedIcon16.png)
         skin/classic/browser/livemark-item.png                       (livemark-item.png)
         skin/classic/browser/livemark-folder.png                     (livemark-folder.png)
         skin/classic/browser/Privacy-16.png
         skin/classic/browser/Privacy-48.png
         skin/classic/browser/reload-stop-go.png
@@ -84,28 +85,31 @@ browser.jar:
         skin/classic/browser/preferences/Options-sync.png            (preferences/Options-sync.png)
 #endif
         skin/classic/browser/preferences/saveFile.png                (preferences/saveFile.png)
 *       skin/classic/browser/preferences/preferences.css             (preferences/preferences.css)
         skin/classic/browser/preferences/applications.css            (preferences/applications.css)
         skin/classic/browser/preferences/aboutPermissions.css        (preferences/aboutPermissions.css)
         skin/classic/browser/tabbrowser/alltabs.png                  (tabbrowser/alltabs.png)
         skin/classic/browser/tabbrowser/newtab.png                   (tabbrowser/newtab.png)
+        skin/classic/browser/tabbrowser/newtab-inverted.png          (tabbrowser/newtab-inverted.png)
         skin/classic/browser/tabbrowser/connecting.png               (tabbrowser/connecting.png)
         skin/classic/browser/tabbrowser/loading.png                  (tabbrowser/loading.png)
         skin/classic/browser/tabbrowser/tab.png                      (tabbrowser/tab.png)
         skin/classic/browser/tabbrowser/tab-arrow-left.png           (tabbrowser/tab-arrow-left.png)
+        skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png  (tabbrowser/tab-arrow-left-inverted.png)
         skin/classic/browser/tabbrowser/tab-overflow-border.png      (tabbrowser/tab-overflow-border.png)
         skin/classic/browser/tabbrowser/tabDragIndicator.png         (tabbrowser/tabDragIndicator.png)
         skin/classic/browser/tabview/close.png                      (tabview/close.png)
         skin/classic/browser/tabview/edit-light.png                 (tabview/edit-light.png)
         skin/classic/browser/tabview/grain.png                      (tabview/grain.png)
         skin/classic/browser/tabview/search.png                     (tabview/search.png)
         skin/classic/browser/tabview/stack-expander.png             (tabview/stack-expander.png)
         skin/classic/browser/tabview/tabview.png                    (tabview/tabview.png)
+        skin/classic/browser/tabview/tabview-inverted.png           (tabview/tabview-inverted.png)
         skin/classic/browser/tabview/tabview.css                    (tabview/tabview.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/sync-throbber.png
         skin/classic/browser/sync-16.png
         skin/classic/browser/sync-32.png
         skin/classic/browser/sync-bg.png
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-mobileIcon.png
@@ -135,16 +139,17 @@ browser.jar:
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/keyhole-forward-mask.svg
         skin/classic/aero/browser/KUI-background.png
         skin/classic/aero/browser/KUI-close.png
         skin/classic/aero/browser/mainwindow-dropdown-arrow.png      (mainwindow-dropdown-arrow-aero.png)
+        skin/classic/aero/browser/mainwindow-dropdown-arrow-inverted.png
         skin/classic/aero/browser/pageInfo.css
         skin/classic/aero/browser/pageInfo.png                       (pageInfo-aero.png)
         skin/classic/aero/browser/page-livemarks.png                 (feeds/feedIcon16-aero.png)
         skin/classic/aero/browser/livemark-item.png                  (livemark-item-aero.png)
         skin/classic/aero/browser/livemark-folder.png                (livemark-folder-aero.png)
         skin/classic/aero/browser/Privacy-16.png                     (Privacy-16-aero.png)
         skin/classic/aero/browser/Privacy-48.png                     (Privacy-48-aero.png)
         skin/classic/aero/browser/reload-stop-go.png
@@ -200,28 +205,31 @@ browser.jar:
 #endif
         skin/classic/aero/browser/preferences/plugin.png             (preferences/plugin-aero.png)
         skin/classic/aero/browser/preferences/saveFile.png           (preferences/saveFile-aero.png)
 *       skin/classic/aero/browser/preferences/preferences.css        (preferences/preferences.css)
         skin/classic/aero/browser/preferences/applications.css       (preferences/applications.css)
         skin/classic/aero/browser/preferences/aboutPermissions.css   (preferences/aboutPermissions.css)
         skin/classic/aero/browser/tabbrowser/alltabs.png             (tabbrowser/alltabs.png)
         skin/classic/aero/browser/tabbrowser/newtab.png              (tabbrowser/newtab.png)
+        skin/classic/aero/browser/tabbrowser/newtab-inverted.png     (tabbrowser/newtab-inverted.png)
         skin/classic/aero/browser/tabbrowser/connecting.png          (tabbrowser/connecting.png)
         skin/classic/aero/browser/tabbrowser/loading.png             (tabbrowser/loading.png)
         skin/classic/aero/browser/tabbrowser/tab.png                 (tabbrowser/tab.png)
         skin/classic/aero/browser/tabbrowser/tab-arrow-left.png      (tabbrowser/tab-arrow-left.png)
+        skin/classic/aero/browser/tabbrowser/tab-arrow-left-inverted.png (tabbrowser/tab-arrow-left-inverted.png)
         skin/classic/aero/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
         skin/classic/aero/browser/tabbrowser/tabDragIndicator.png    (tabbrowser/tabDragIndicator.png)
         skin/classic/aero/browser/tabview/close.png                  (tabview/close.png)
         skin/classic/aero/browser/tabview/edit-light.png             (tabview/edit-light.png)
         skin/classic/aero/browser/tabview/grain.png                  (tabview/grain.png)
         skin/classic/aero/browser/tabview/search.png                 (tabview/search.png)
         skin/classic/aero/browser/tabview/stack-expander.png         (tabview/stack-expander.png)
         skin/classic/aero/browser/tabview/tabview.png                (tabview/tabview.png)
+        skin/classic/aero/browser/tabview/tabview-inverted.png       (tabview/tabview-inverted.png)
         skin/classic/aero/browser/tabview/tabview.css                (tabview/tabview.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/sync-throbber.png
         skin/classic/aero/browser/sync-16.png
         skin/classic/aero/browser/sync-32.png
         skin/classic/aero/browser/sync-bg.png
         skin/classic/aero/browser/sync-desktopIcon.png
         skin/classic/aero/browser/sync-mobileIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ed1791641307105edbe69325356ffc00396dc87b
GIT binary patch
literal 230
zc%17D@N?(olHy`uVBq!ia0vp@K+MI#1|;*}T{!}zBuiW)N`mv#O3D+9QW+dm@{>{(
zJaZG%Q-e|yQz{EjrrH1%b$GfshE&{2`t$$4J@d+j&PJXCOvwgDM!T3b6>YNaGsbQe
zKEq_m|LVxS-{0Tw|I^RfaK!Ob!B&r1HySH`&9yGSr;yF?(eaT&WzMV_jS&JJ&+qQ5
z{jKZ4+`;y7;iEG*OcFC(t~ERoHSs+mm9CKOaP0`kWd<JGlHLUq{xTn_pJ>{6b&~K-
c21Yi9T|yICd*k;#0=kL8)78&qol`;+0N3GKFaQ7m
new file mode 100644
index 0000000000000000000000000000000000000000..4ac1ebaaf0d7c9d8d1efe4b7b6545b07ed3f121d
GIT binary patch
literal 247
zc%17D@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqPlw^r(L`iUdT1k0gQ7VIDN`6wR
zf@f}GdTLN=VoGJ<$y6JlqM4p9jv*DdlK%YvZ_ljSQ20UOz=;zbdgA;?!UDb-D#Uf@
zaBi14a^PXt%>N4+j<`*4d6}HFKp-H8ft$}h`KyqSw4j<tBExrMMTSuCxdsm34rzW_
z!E$Q?TkoUDj9-3<F6;sZ%!c9upPdY=Ob#7!%$kw-YeMV?@s9lu3*V;Ca#yx;;F}!$
u<OyrQj1A5=?H9%!Il;|5e=Z{v4};&~ZU?t*muCVU$>8bg=d#Wzp$Pz4=2^`E
index 404f3af1cafe487d668a64e062d9e4c79d1b2882..c25c98053b045908ecb130bac2bebab64dbef7bb
GIT binary patch
literal 445
zc$@*f0Yd(XP)<h;3K|Lk000e1NJLTq000mG000sQ1^@s6QafI100001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iph)
z1~44N^^DX200BKoL_t(I%gvI_O2bePMo-JNY0?IXU1(}46!Hi@f#9OjMVEr$w$J2N
z5PSkRYWJ1+28DtYS{Egnq<;`o7v7fJm|%B0%b9P!nK=ynb>z~L{gEdC1JGq^4$K_e
zijrE9VQc^mr5*tTz)uSE83sBa+O4MBZZ%zLq^Ak9Hpl}U*BieM6Dc?6feJvOpsCtQ
zf8+tgDie`$^Ect#7Gn*lCYj{q28TfH{Or^%l}!LjrpZyQ4#0T$$}|W7n1%uO58V@B
z=-AfV)<oSf*tZS<C=`pVR&u?vi4c1L#PMe;#5oXkpB_7E@#6C88sP5sW-IT7i%9lt
z3}(Q<3m0PrqUL|RuNz)~B`{GTZVcpy`RhlKw5!X-OdULNY-_aZAH)D*;Qz?&YA^?)
nUhlb+ym?mxl?m$2|1Z7(W9M+*n^KJE00000NkvXXu0mjftlhTH
new file mode 100644
index 0000000000000000000000000000000000000000..16cd7a2775ae1f56274035fcde4ed5e8c211a52c
GIT binary patch
literal 250
zc%17D@N?(olHy`uVBq!ia0vp^{6H+o!3HFmxV|j}Qj#UE5hcO-X(i=}MX3yqDfvmM
z3ZA)%>8U}fi7AzZCsS>JispE_IEGZ*O8WEvzdiHLhR#N&15IZVQ&NsBJI2|O+xWvk
z;JtI1rsC|sjW?bjY-YD~xFwk-z|`Q=l&B;1``+H_@1h#t<ueXsE9{qZwdFgjA?0Y&
z!M|zdgtQo`zlJeg%OuykInAoRVDe2l+vU2C#+IE85e&<9?9%_YbDUGTc2k5^;%K02
wp^~E^!>Wy<chlTbe3zv0xOsgtc4%N^kb38u%%>m}4Rj}ir>mdKI;Vst0OD<0yZ`_I
index 0ef2b1ae61e2d4396ac70fab94dab6a7b49ef1aa..e0fb348d66f4001a50ca9488b9daddcf37bbc3ac
GIT binary patch
literal 368
zc%17D@N?(olHy`uVBq!ia0vp^{6H+o!3-o#udq!9QjEnx?oJHr&dIz4a+U`8gt*>$
z{+>wm^265`@4u*NXsXrEQfr!{)-q4q*h1gJ)_wl=!0;GTD?8Kv<(3mySWQ}K<Lu_-
z>Ejm?5ioyqP()17{LLZD_l7Rq8kvw1o0J}#oSu-Lm6Vmw*|v;ZNL-+Eg<#)0A!#`=
zIYqIVdn6`qkW<rES#Utd#7f`X#=y$n$ja_^()*tv*OvtO1v5B2yO9RsBze2LFm$lW
zdH^|`1s;*b3=G`DAk4@xYmNj^kiEpy*OmP?2Me2lnY^DX6Hv(9)5S4F;&O7r0wDnb
zho@Pd3kph~2JEQf(ui>u6*+5^_Gm@h>RBC&xK_+w#F-({y?|@Q$w^On88&`r?tb+}
Q0%R(Kr>mdKI;Vst0Co0^sQ>@~
new file mode 100644
index 0000000000000000000000000000000000000000..0211d8b650b3db4a8d73e42367aeee6a0b7873fa
GIT binary patch
literal 524
zc$@(Y0`vWeP)<h;3K|Lk000e1NJLTq003G5000sQ1^@s6+oQ<?0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzu1Q2eRCwC#n7>ZLFc8LLRH;+D(+x2<
zTLMyHNGAp)-Xcsq5IhVFKtz)vQZh#jROuR}17a$7(z!C0o$@C^QFWG1=X1XPQaOqJ
z$zrqFINZ8#2RsG;(+095ggAi|Kpa`LuMp1=GtyxF0AIvH6*0%m87RUaj$_C3yp@TP
zB#Gnu{v0$PsG%BfywIdUUDsEm(dg8~S+CbG<MDXd76&vJsG-{Fl9OfG&2&1Qm^gW!
zm#*s$K@*~e>McjeH5#7SE%aASuRmxGRSnhGng%)H4$fVRoKUNK$>BAcH^cp%=pu@u
zcbkx|Ev1HXVUQM8RXy1_bgeax)KKn}lcwpLjYHR#QbT#mQP=2mNOOqEHy=_7aSy+L
z)Yd8H7&z)1Jwqpen-m80(Vmo;<(L|%B`c%1-f_@T;3kCuefPH<bQF>U{Y&IvmSbw5
zR^>Fh4&7x~T!?9R?i(j;ay@_KggaaayAF08Jki`*w|Baov)DbO4eA?5wRgI`{0T?3
zce<Uk*gd1Y^XSmzSb`z&Pn{`aUv5l?CW*EIFvq|Vd>+kf^j@`30R{lJMYCZ;;phnf
O0000<MNUMnLSTa0Wan!D
--- a/caps/src/nsNullPrincipalURI.cpp
+++ b/caps/src/nsNullPrincipalURI.cpp
@@ -197,16 +197,30 @@ nsNullPrincipalURI::SetScheme(const nsAC
 
 NS_IMETHODIMP
 nsNullPrincipalURI::GetSpec(nsACString &_spec)
 {
   _spec = mScheme + NS_LITERAL_CSTRING(":") + mPath;
   return NS_OK;
 }
 
+// result may contain unescaped UTF-8 characters
+NS_IMETHODIMP
+nsNullPrincipalURI::GetSpecIgnoringRef(nsACString &result)
+{
+  return GetSpec(result);
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetHasRef(PRBool *result)
+{
+  *result = PR_FALSE;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsNullPrincipalURI::SetSpec(const nsACString &aSpec)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNullPrincipalURI::GetUsername(nsACString &_username)
--- 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/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -932,28 +932,35 @@ WebGLContext::MozGetUnderlyingParamStrin
 
     default:
         return NS_ERROR_INVALID_ARG;
     }
 
     return NS_OK;
 }
 
+bool WebGLContext::IsExtensionSupported(WebGLExtensionID ei)
+{
+    if (ei == WebGL_OES_texture_float) {
+        MakeContextCurrent();
+        return gl->IsExtensionSupported(gl->IsGLES2() ? GLContext::OES_texture_float
+                                                      : GLContext::ARB_texture_float);
+    }
+    return false;
+}
+
 NS_IMETHODIMP
 WebGLContext::GetExtension(const nsAString& aName, nsIWebGLExtension **retval)
 {
     *retval = nsnull;
 
     // handle simple extensions that don't need custom objects first
     WebGLExtensionID ei = WebGLExtensionID_Max;
     if (aName.EqualsLiteral("OES_texture_float")) {
-        MakeContextCurrent();
-
-        PRBool avail = gl->IsExtensionSupported(gl->IsGLES2() ? "GL_OES_texture_float" : "GL_ARB_texture_float");
-        if (avail)
+        if (IsExtensionSupported(WebGL_OES_texture_float))
             ei = WebGL_OES_texture_float;
     }
 
     // create a WebGLExtension object for extensions that don't
     // have any additional tokens or methods
     if (ei != WebGLExtensionID_Max) {
         if (!IsExtensionEnabled(ei)) {
             mEnabledExtensions[ei] = new WebGLExtension(this);
@@ -1240,17 +1247,18 @@ WebGLActiveInfo::GetName(nsAString & aNa
 NS_IMETHODIMP
 WebGLContext::GetSupportedExtensions(nsIVariant **retval)
 {
     nsCOMPtr<nsIWritableVariant> wrval = do_CreateInstance("@mozilla.org/variant;1");
     NS_ENSURE_TRUE(wrval, NS_ERROR_FAILURE);
 
     nsTArray<const char *> extList;
 
-    /* no extensions to add to extList */
+    if (IsExtensionSupported(WebGL_OES_texture_float))
+        extList.InsertElementAt(extList.Length(), "OES_texture_float");
 
     nsresult rv;
     if (extList.Length() > 0) {
         rv = wrval->SetAsArray(nsIDataType::VTYPE_CHAR_STR, nsnull,
                                extList.Length(), &extList[0]);
     } else {
         rv = wrval->SetAsEmptyArray();
     }
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -467,16 +467,17 @@ protected:
         WebGL_OES_texture_float,
         WebGLExtensionID_Max
     };
     nsCOMPtr<nsIWebGLExtension> mEnabledExtensions[WebGLExtensionID_Max];
     PRBool IsExtensionEnabled(WebGLExtensionID ext) const {
         NS_ABORT_IF_FALSE(ext >= 0 && ext < WebGLExtensionID_Max, "bogus index!");
         return mEnabledExtensions[ext] != nsnull;
     }
+    bool IsExtensionSupported(WebGLExtensionID ei);
 
     PRBool InitAndValidateGL();
     PRBool ValidateBuffers(PRInt32* maxAllowedCount, const char *info);
     PRBool ValidateCapabilityEnum(WebGLenum cap, const char *info);
     PRBool ValidateBlendEquationEnum(WebGLenum cap, const char *info);
     PRBool ValidateBlendFuncDstEnum(WebGLenum mode, const char *info);
     PRBool ValidateBlendFuncSrcEnum(WebGLenum mode, const char *info);
     PRBool ValidateBlendFuncEnumsCompatibility(WebGLenum sfactor, WebGLenum dfactor, const char *info);
--- 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/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -594,16 +594,29 @@ ContentParent::RecvGetIconForExtension(c
 
     bits->AppendElements(aIconSize * aIconSize * 4);
 
     AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize, bits->Elements());
 #endif
     return true;
 }
 
+bool
+ContentParent::RecvGetShowPasswordSetting(PRBool* showPassword)
+{
+    // default behavior is to show the last password character
+    *showPassword = PR_TRUE;
+#ifdef ANDROID
+    NS_ASSERTION(AndroidBridge::Bridge() != nsnull, "AndroidBridge is not available");
+    if (AndroidBridge::Bridge() != nsnull)
+        *showPassword = AndroidBridge::Bridge()->GetShowPasswordSetting();
+#endif
+    return true;
+}
+
 NS_IMPL_THREADSAFE_ISUPPORTS4(ContentParent,
                               nsIObserver,
                               nsIThreadObserver,
                               nsIDOMGeoPositionCallback,
                               nsIDeviceMotionListener)
 
 NS_IMETHODIMP
 ContentParent::Observe(nsISupports* aSubject,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -162,16 +162,17 @@ private:
 
     virtual bool RecvSetClipboardText(const nsString& text, const PRInt32& whichClipboard);
     virtual bool RecvGetClipboardText(const PRInt32& whichClipboard, nsString* text);
     virtual bool RecvEmptyClipboard();
     virtual bool RecvClipboardHasText(PRBool* hasText);
 
     virtual bool RecvGetSystemColors(const PRUint32& colorsCount, InfallibleTArray<PRUint32>* colors);
     virtual bool RecvGetIconForExtension(const nsCString& aFileExt, const PRUint32& aIconSize, InfallibleTArray<PRUint8>* bits);
+    virtual bool RecvGetShowPasswordSetting(PRBool* showPassword);
 
     virtual bool RecvStartVisitedQuery(const IPC::URI& uri);
 
     virtual bool RecvVisitURI(const IPC::URI& uri,
                               const IPC::URI& referrer,
                               const PRUint32& flags);
 
     virtual bool RecvSetURITitle(const IPC::URI& uri,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -200,15 +200,18 @@ parent:
         returns (PRBool hasText);
 
     sync GetSystemColors(PRUint32 colorsCount)
         returns (PRUint32[] colors);
 
     sync GetIconForExtension(nsCString aFileExt, PRUint32 aIconSize)
         returns (PRUint8[] bits);
 
+    sync GetShowPasswordSetting()
+        returns (PRBool showPassword);
+
 both:
      AsyncMessage(nsString aMessage, nsString aJSON);
 
 };
 
 }
 }
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -348,18 +348,18 @@ private:
     nsIntRect          mSharedSize;
     HWND               mPluginHWND;
     WNDPROC            mPluginWndProc;
     bool               mNestedEventState;
 #endif // defined(XP_WIN)
 #if defined(MOZ_WIDGET_COCOA)
 private:
     Shmem                  mShSurface; 
-    size_t                 mShWidth;
-    size_t                 mShHeight;
+    uint16_t               mShWidth;
+    uint16_t               mShHeight;
     CGColorSpaceRef        mShColorSpace;
     int16_t                mDrawingModel;
     nsRefPtr<nsIOSurface> mIOSurface;
     nsRefPtr<nsIOSurface> mFrontIOSurface;
 #endif // definied(MOZ_WIDGET_COCOA)
 
     // ObjectFrame layer wrapper
     nsRefPtr<gfxASurface>    mFrontSurface;
--- 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/editor/composer/src/nsEditorSpellCheck.cpp
+++ b/editor/composer/src/nsEditorSpellCheck.cpp
@@ -59,32 +59,50 @@
 #include "nsReadableUtils.h"
 #include "nsITextServicesFilter.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
+class UpdateDictionnaryHolder {
+  private:
+    nsEditorSpellCheck* mSpellCheck;
+  public:
+    UpdateDictionnaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) {
+      if (mSpellCheck) {
+        mSpellCheck->BeginUpdateDictionary();
+      }
+    }
+    ~UpdateDictionnaryHolder() {
+      if (mSpellCheck) {
+        mSpellCheck->EndUpdateDictionary();
+      }
+    }
+};
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditorSpellCheck)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditorSpellCheck)
 
 NS_INTERFACE_MAP_BEGIN(nsEditorSpellCheck)
   NS_INTERFACE_MAP_ENTRY(nsIEditorSpellCheck)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditorSpellCheck)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsEditorSpellCheck)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_2(nsEditorSpellCheck,
                            mSpellChecker,
                            mTxtSrvFilter)
 
 nsEditorSpellCheck::nsEditorSpellCheck()
   : mSuggestedWordIndex(0)
   , mDictionaryIndex(0)
+  , mUpdateDictionaryRunning(PR_FALSE)
+  , mDictWasSetManually(PR_FALSE)
 {
 }
 
 nsEditorSpellCheck::~nsEditorSpellCheck()
 {
   // Make sure we blow the spellchecker away, just in
   // case it hasn't been destroyed already.
   mSpellChecker = nsnull;
@@ -355,72 +373,60 @@ nsEditorSpellCheck::GetDictionaryList(PR
   {
     tmpPtr[i] = ToNewUnicode(dictList[i]);
   }
 
   return rv;
 }
 
 NS_IMETHODIMP    
-nsEditorSpellCheck::GetCurrentDictionary(PRUnichar **aDictionary)
+nsEditorSpellCheck::GetCurrentDictionary(nsAString& aDictionary)
 {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
-  NS_ENSURE_TRUE(aDictionary, NS_ERROR_NULL_POINTER);
-
-  *aDictionary = 0;
-
-  nsAutoString dictStr;
-  nsresult rv = mSpellChecker->GetCurrentDictionary(dictStr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *aDictionary = ToNewUnicode(dictStr);
-
-  return rv;
+  return mSpellChecker->GetCurrentDictionary(aDictionary);
 }
 
 NS_IMETHODIMP    
-nsEditorSpellCheck::SetCurrentDictionary(const PRUnichar *aDictionary)
+nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
 {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
-  NS_ENSURE_TRUE(aDictionary, NS_ERROR_NULL_POINTER);
-
-  return mSpellChecker->SetCurrentDictionary(nsDependentString(aDictionary));
+  if (!mUpdateDictionaryRunning) {
+    mDictWasSetManually = PR_TRUE;
+  }
+  return mSpellChecker->SetCurrentDictionary(aDictionary);
 }
 
 NS_IMETHODIMP    
 nsEditorSpellCheck::UninitSpellChecker()
 {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
   // Cleanup - kill the spell checker
   DeleteSuggestedWordList();
   mDictionaryList.Clear();
   mDictionaryIndex = 0;
   mSpellChecker = 0;
   return NS_OK;
 }
 
-// Save the last used dictionary to the user's preferences.
+// Save the last set dictionary to the user's preferences.
 NS_IMETHODIMP
 nsEditorSpellCheck::SaveDefaultDictionary()
 {
-  PRUnichar *dictName = nsnull;
-  nsresult rv = GetCurrentDictionary(&dictName);
-
-  if (NS_SUCCEEDED(rv) && dictName && *dictName) {
-    rv = Preferences::SetString("spellchecker.dictionary", dictName);
+  if (!mDictWasSetManually) {
+    return NS_OK;
   }
 
-  if (dictName) {
-    nsMemory::Free(dictName);
-  }
+  nsAutoString dictName;
+  nsresult rv = GetCurrentDictionary(dictName);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return rv;
+  return Preferences::SetString("spellchecker.dictionary", dictName);
 }
 
 
 /* void setFilter (in nsITextServicesFilter filter); */
 NS_IMETHODIMP 
 nsEditorSpellCheck::SetFilter(nsITextServicesFilter *filter)
 {
   mTxtSrvFilter = filter;
@@ -433,18 +439,24 @@ nsEditorSpellCheck::DeleteSuggestedWordL
   mSuggestedWordList.Clear();
   mSuggestedWordIndex = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditor* aEditor)
 {
+  if (mDictWasSetManually) { // user has set dictionary manually; we better not change it.
+    return NS_OK;
+  }
+
   nsresult rv;
 
+  UpdateDictionnaryHolder holder(this);
+
   // Tell the spellchecker what dictionary to use:
   nsAutoString dictName;
 
   // First, try to get language with html5 algorithm
   nsAutoString editorLang;
 
   nsCOMPtr<nsIContent> rootContent;
 
@@ -487,65 +499,65 @@ nsEditorSpellCheck::UpdateCurrentDiction
     if (packageRegistry) {
       nsCAutoString utf8DictName;
       rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"),
                                               utf8DictName);
       AppendUTF8toUTF16(utf8DictName, dictName);
     }
   }
 
-  SetCurrentDictionary(NS_LITERAL_STRING("").get());
+  SetCurrentDictionary(EmptyString());
 
   if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) {
-    rv = SetCurrentDictionary(dictName.get());
+    rv = SetCurrentDictionary(dictName);
     if (NS_FAILED(rv)) {
       // required dictionary was not available. Try to get a dictionary
       // matching at least language part of dictName: If required dictionary is
       // "aa-bb", we try "aa", then we try any available dictionary aa-XX
       nsAutoString langCode;
       PRInt32 dashIdx = dictName.FindChar('-');
       if (dashIdx != -1) {
         langCode.Assign(Substring(dictName, 0, dashIdx));
         // try to use langCode
-        rv = SetCurrentDictionary(langCode.get());
+        rv = SetCurrentDictionary(langCode);
       } else {
         langCode.Assign(dictName);
       }
       if (NS_FAILED(rv)) {
         // loop over avaible dictionaries; if we find one with required
         // language, use it
         nsTArray<nsString> dictList;
         rv = mSpellChecker->GetDictionaryList(&dictList);
         NS_ENSURE_SUCCESS(rv, rv);
         nsDefaultStringComparator comparator;
         PRInt32 i, count = dictList.Length();
         for (i = 0; i < count; i++) {
           nsAutoString dictStr(dictList.ElementAt(i));
           if (nsStyleUtil::DashMatchCompare(dictStr, langCode, comparator) &&
-              NS_SUCCEEDED(SetCurrentDictionary(dictStr.get()))) {
+              NS_SUCCEEDED(SetCurrentDictionary(dictStr))) {
               break;
           }
         }
       }
     }
   }
 
   // If we have not set dictionary, and the editable element doesn't have a
   // lang attribute, we try to get a dictionary. First try, en-US. If it does
   // not work, pick the first one.
   if (editorLang.IsEmpty()) {
-    nsAutoString currentDictonary;
-    rv = mSpellChecker->GetCurrentDictionary(currentDictonary);
-    if (NS_FAILED(rv) || currentDictonary.IsEmpty()) {
-      rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US").get());
+    nsAutoString currentDictionary;
+    rv = GetCurrentDictionary(currentDictionary);
+    if (NS_FAILED(rv) || currentDictionary.IsEmpty()) {
+      rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US"));
       if (NS_FAILED(rv)) {
         nsTArray<nsString> dictList;
         rv = mSpellChecker->GetDictionaryList(&dictList);
         if (NS_SUCCEEDED(rv) && dictList.Length() > 0) {
-          SetCurrentDictionary(dictList[0].get());
+          SetCurrentDictionary(dictList[0]);
         }
       }
     }
   }
 
   // If an error was thrown while setting the dictionary, just
   // fail silently so that the spellchecker dialog is allowed to come
   // up. The user can manually reset the language to their choice on
--- a/editor/composer/src/nsEditorSpellCheck.h
+++ b/editor/composer/src/nsEditorSpellCheck.h
@@ -73,13 +73,20 @@ protected:
   // these are the words in the current personal dictionary,
   // GetPersonalDictionary must be called to load them.
   nsTArray<nsString>  mDictionaryList;
   PRInt32        mDictionaryIndex;
 
   nsresult       DeleteSuggestedWordList();
 
   nsCOMPtr<nsITextServicesFilter> mTxtSrvFilter;
+
+  PRPackedBool mUpdateDictionaryRunning;
+  PRPackedBool mDictWasSetManually;
+
+public:
+  void BeginUpdateDictionary() { mUpdateDictionaryRunning = PR_TRUE ;}
+  void EndUpdateDictionary() { mUpdateDictionaryRunning = PR_FALSE ;}
 };
 
 #endif // nsEditorSpellCheck_h___
 
 
--- a/editor/composer/test/Makefile.in
+++ b/editor/composer/test/Makefile.in
@@ -48,15 +48,16 @@ include $(topsrcdir)/config/rules.mk
 		test_bug348497.html \
 		test_bug384147.html \
 		test_bug389350.html \
 		test_bug519928.html \
 		$(NULL)
 
 _CHROME_TEST_FILES = \
 		test_bug434998.xul \
+		test_bug338427.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/editor/composer/test/test_bug338427.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=338427
+-->
+<head>
+  <title>Test for Bug 338427</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=338427">Mozilla Bug 338427</a>
+<p id="display"></p>
+<div id="content">
+<textarea id="editor" lang="testing-XX"></textarea>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 338427 **/
+function init() {
+    var textarea = document.getElementById("editor");
+    var editor = textarea.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor;
+    var spellchecker = editor.getInlineSpellChecker(true);
+    spellchecker.enableRealTimeSpell = true;
+
+    var list = {}, count = {};
+    spellchecker.spellChecker.GetDictionaryList(list, count);
+    if (count.value === 0) {
+        return; // no dictionary, no test possible
+    }
+    var lang = list.value[0];
+    spellchecker.spellChecker.SetCurrentDictionary(lang);
+
+    textarea.addEventListener("focus", function() {
+        var dictionary = "";
+        try {
+            dictionary = spellchecker.spellChecker.GetCurrentDictionary();
+        } catch(e) {}
+        is(dictionary, lang, "Unexpected spell check dictionary");
+        SimpleTest.finish();
+    }, false);
+    textarea.focus();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(init);
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/editor/idl/nsIEditorSpellCheck.idl
+++ b/editor/idl/nsIEditorSpellCheck.idl
@@ -36,17 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
  
 #include "nsISupports.idl"
 
 interface nsIEditor;
 interface nsITextServicesFilter;
 
-[scriptable, uuid(803ff0dd-07f2-4438-b3a6-ab9c2fe4e1dd)]
+[scriptable, uuid(3da0ce96-7d3a-48d0-80b7-2d90dab09747)]
 interface nsIEditorSpellCheck : nsISupports
 {
 
  /**
    * Returns true if we can enable spellchecking. If there are no available
    * dictionaries, this will return false.
    */
   boolean       canSpellCheck();
@@ -138,22 +138,22 @@ interface nsIEditorSpellCheck : nsISuppo
    *
    * @see mozISpellCheckingEngine::GetDictionaryList
    */
   void          GetDictionaryList([array, size_is(count)] out wstring dictionaryList, out PRUint32 count);
 
   /**
    * @see nsISpellChecker::GetCurrentDictionary
    */
-  wstring       GetCurrentDictionary();
+  AString       GetCurrentDictionary();
 
   /**
    * @see nsISpellChecker::SetCurrentDictionary
    */
-  void          SetCurrentDictionary(in wstring dictionary);
+  void          SetCurrentDictionary(in AString dictionary);
 
   /**
    * Call to save the currently selected dictionary as the default. The
    * function UninitSpellChecker will also do this, but that function may not
    * be called in some situations. This function allows the caller to force the
    * default right now.
    */
   void          saveDefaultDictionary();
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -57,16 +57,17 @@ import android.content.pm.*;
 import android.graphics.*;
 import android.widget.*;
 import android.hardware.*;
 import android.location.*;
 import android.telephony.*;
 import android.webkit.MimeTypeMap;
 import android.media.MediaScannerConnection;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
+import android.provider.Settings;
 
 import android.util.*;
 import android.net.Uri;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 
 import android.graphics.drawable.*;
 import android.graphics.Bitmap;
@@ -1361,9 +1362,21 @@ public class GeckoAppShell
 
         if (resolveInfo == null)
             return null;
 
         ActivityInfo activityInfo = resolveInfo.activityInfo;
 
         return activityInfo.loadIcon(pm);
     }
+
+    public static boolean getShowPasswordSetting() {
+        try {
+            int showPassword =
+                Settings.System.getInt(GeckoApp.mAppContext.getContentResolver(),
+                                       Settings.System.TEXT_SHOW_PASSWORD);
+            return (showPassword > 0);
+        }
+        catch (Exception e) {
+            return false;
+        }
+    }
 }
--- 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/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -1440,18 +1440,18 @@ mozInlineSpellChecker::ResumeCheck(mozIn
   nsresult rv = wordUtil.Init(mEditor);
   if (NS_FAILED(rv))
     return NS_OK; // editor doesn't like us, don't assert
 
   nsCOMPtr<nsISelection> spellCheckSelection;
   rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRUnichar *currentDictionary = nsnull;
-  rv = mSpellCheck->GetCurrentDictionary(&currentDictionary);
+  nsAutoString currentDictionary;
+  rv = mSpellCheck->GetCurrentDictionary(currentDictionary);
   if (NS_FAILED(rv)) {
     // no active dictionary
     PRInt32 count;
     spellCheckSelection->GetRangeCount(&count);
     for (PRInt32 index = count - 1; index >= 0; index--) {
       nsCOMPtr<nsIDOMRange> checkRange;
       spellCheckSelection->GetRangeAt(index, getter_AddRefs(checkRange));
       if (checkRange) {
@@ -1755,36 +1755,27 @@ nsresult mozInlineSpellChecker::KeyPress
 }
 
 NS_IMETHODIMP mozInlineSpellChecker::UpdateCurrentDictionary()
 {
   if (!mSpellCheck) {
     return NS_OK;
   }
 
-  PRUnichar *previousDictionary = nsnull;
-  nsDependentString previousDictionaryStr;
-  if (NS_SUCCEEDED(mSpellCheck->GetCurrentDictionary(&previousDictionary))) {
-    previousDictionaryStr.Assign(previousDictionary);
+  nsAutoString previousDictionary;
+  if (NS_FAILED(mSpellCheck->GetCurrentDictionary(previousDictionary))) {
+    previousDictionary.Truncate();
   }
 
   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
   nsresult rv = mSpellCheck->UpdateCurrentDictionary(editor);
 
-  PRUnichar *currentDictionary = nsnull;
-  nsDependentString currentDictionaryStr;
-  if (NS_SUCCEEDED(mSpellCheck->GetCurrentDictionary(&currentDictionary))) {
-    currentDictionaryStr.Assign(currentDictionary);
+  nsAutoString currentDictionary;
+  if (NS_FAILED(mSpellCheck->GetCurrentDictionary(currentDictionary))) {
+    currentDictionary.Truncate();
   }
 
-  if (!previousDictionary || !currentDictionary || !previousDictionaryStr.Equals(currentDictionaryStr)) {
+  if (!previousDictionary.Equals(currentDictionary)) {
       rv = SpellCheckRange(nsnull);
   }
 
-  if (previousDictionary) {
-     nsMemory::Free(previousDictionary);
-  }
-  if (currentDictionary) {
-     nsMemory::Free(currentDictionary);
-  }
-
   return rv;
 }
--- 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/angle/Makefile.in
+++ b/gfx/angle/Makefile.in
@@ -57,16 +57,17 @@ EXPORTS_angle = \
 LOCAL_INCLUDES += -I$(srcdir)/include -I$(srcdir)/src
 
 VPATH += $(srcdir)/src
 VPATH += $(srcdir)/src/compiler
 VPATH += $(srcdir)/src/compiler/preprocessor
 
 CPPSRCS = \
 	Compiler.cpp \
+        DetectRecursion.cpp \
         InfoSink.cpp \
         Initialize.cpp \
         InitializeDll.cpp \
         Intermediate.cpp \
         intermOut.cpp \
         IntermTraverse.cpp \
         MozAngleLink.cpp \
         parseConst.cpp \
--- a/gfx/angle/README.mozilla
+++ b/gfx/angle/README.mozilla
@@ -1,24 +1,26 @@
 This is the ANGLE project, from http://code.google.com/p/angleproject/.
 
-Current revision: r653
+Current revision: r686
 
 == Applied local patches ==
 
 In this order:
   angle-nspr-misc.patch - don't bother with ANGLE_OS detection with NSPR
   angle-renaming.patch - rename debug.h to compilerdebug.h to avoid conflict in our makefiles
   angle-intrinsic-msvc2005.patch - work around a MSVC 2005 compile error
   angle-amap-arev-fix.patch - plain bug fix, this is ANGLE r699
   angle-r702.patch - this is ANGLE r702
   angle-limit-identifiers-to-250-chars.patch - see bug 675625
   angle-r712.patch - this is ANGLE r712
   angle-win64.patch - Win64 support. This is ANGLE r697
   angle-r707.patch - this is ANGLE r707 for Win64 bug fix
+  angle-r719.patch - this is ANGLE r719
+  angle-r711.patch - this is ANGLE r711
 
 In addition to these patches, the Makefile.in files are ours, they're not present in upsteam ANGLE.
 
 == How to update this ANGLE copy ==
 
 1. Unapply patches
 2. Apply diff with new ANGLE version
 3. Reapply patches.
--- a/gfx/angle/src/compiler/Compiler.cpp
+++ b/gfx/angle/src/compiler/Compiler.cpp
@@ -1,14 +1,15 @@
 //
 // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
 
+#include "compiler/DetectRecursion.h"
 #include "compiler/Initialize.h"
 #include "compiler/ParseHelper.h"
 #include "compiler/ShHandle.h"
 #include "compiler/ValidateLimitations.h"
 #include "compiler/MapLongVariableNames.h"
 
 namespace {
 bool InitializeSymbolTable(
@@ -140,23 +141,26 @@ bool TCompiler::compile(const char* cons
     // Parse shader.
     bool success =
         (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
         (parseContext.treeRoot != NULL);
     if (success) {
         TIntermNode* root = parseContext.treeRoot;
         success = intermediate.postProcess(root);
 
+        if (success)
+            success = detectRecursion(root);
+
         if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
             success = validateLimitations(root);
 
         // Call mapLongVariableNames() before collectAttribsUniforms() so in
         // collectAttribsUniforms() we already have the mapped symbol names and
         // we could composite mapped and original variable names.
-        if (compileOptions & SH_MAP_LONG_VARIABLE_NAMES)
+        if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES))
             mapLongVariableNames(root);
 
         if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS))
             collectAttribsUniforms(root);
 
         if (success && (compileOptions & SH_INTERMEDIATE_TREE))
             intermediate.outputTree(root);
 
@@ -188,16 +192,35 @@ void TCompiler::clearResults()
     infoSink.info.erase();
     infoSink.obj.erase();
     infoSink.debug.erase();
 
     attribs.clear();
     uniforms.clear();
 }
 
+bool TCompiler::detectRecursion(TIntermNode* root)
+{
+    DetectRecursion detect;
+    root->traverse(&detect);
+    switch (detect.detectRecursion()) {
+        case DetectRecursion::kErrorNone:
+            return true;
+        case DetectRecursion::kErrorMissingMain:
+            infoSink.info.message(EPrefixError, "Missing main()");
+            return false;
+        case DetectRecursion::kErrorRecursion:
+            infoSink.info.message(EPrefixError, "Function recursion detected");
+            return false;
+        default:
+            UNREACHABLE();
+            return false;
+    }
+}
+
 bool TCompiler::validateLimitations(TIntermNode* root) {
     ValidateLimitations validate(shaderType, infoSink.info);
     root->traverse(&validate);
     return validate.numErrors() == 0;
 }
 
 void TCompiler::collectAttribsUniforms(TIntermNode* root)
 {
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/DetectRecursion.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/DetectRecursion.h"
+
+DetectRecursion::FunctionNode::FunctionNode(const TString& fname)
+    : name(fname),
+      visit(PreVisit)
+{
+}
+
+const TString& DetectRecursion::FunctionNode::getName() const
+{
+    return name;
+}
+
+void DetectRecursion::FunctionNode::addCallee(
+    DetectRecursion::FunctionNode* callee)
+{
+    for (size_t i = 0; i < callees.size(); ++i) {
+        if (callees[i] == callee)
+            return;
+    }
+    callees.push_back(callee);
+}
+
+bool DetectRecursion::FunctionNode::detectRecursion()
+{
+    ASSERT(visit == PreVisit);
+    visit = InVisit;
+    for (size_t i = 0; i < callees.size(); ++i) {
+        switch (callees[i]->visit) {
+            case InVisit:
+                // cycle detected, i.e., recursion detected.
+                return true;
+            case PostVisit:
+                break;
+            case PreVisit: {
+                bool recursion = callees[i]->detectRecursion();
+                if (recursion)
+                    return true;
+                break;
+            }
+            default:
+                UNREACHABLE();
+                break;
+        }
+    }
+    visit = PostVisit;
+    return false;
+}
+
+DetectRecursion::DetectRecursion()
+    : currentFunction(NULL)
+{
+}
+
+DetectRecursion::~DetectRecursion()
+{
+    for (int i = 0; i < functions.size(); ++i)
+        delete functions[i];
+}
+
+void DetectRecursion::visitSymbol(TIntermSymbol*)
+{
+}
+
+void DetectRecursion::visitConstantUnion(TIntermConstantUnion*)
+{
+}
+
+bool DetectRecursion::visitBinary(Visit, TIntermBinary*)
+{
+    return true;
+}
+
+bool DetectRecursion::visitUnary(Visit, TIntermUnary*)
+{
+    return true;
+}
+
+bool DetectRecursion::visitSelection(Visit, TIntermSelection*)
+{
+    return true;
+}
+
+bool DetectRecursion::visitAggregate(Visit visit, TIntermAggregate* node)
+{
+    switch (node->getOp())
+    {
+        case EOpPrototype:
+            // Function declaration.
+            // Don't add FunctionNode here because node->getName() is the
+            // unmangled function name.
+            break;
+        case EOpFunction: {
+            // Function definition.
+            if (visit == PreVisit) {
+                currentFunction = findFunctionByName(node->getName());
+                if (currentFunction == NULL) {
+                    currentFunction = new FunctionNode(node->getName());
+                    functions.push_back(currentFunction);
+                }
+            }
+            break;
+        }
+        case EOpFunctionCall: {
+            // Function call.
+            if (visit == PreVisit) {
+                ASSERT(currentFunction != NULL);
+                FunctionNode* func = findFunctionByName(node->getName());
+                if (func == NULL) {
+                    func = new FunctionNode(node->getName());
+                    functions.push_back(func);
+                }
+                currentFunction->addCallee(func);
+            }
+            break;
+        }
+        default:
+            break;
+    }
+    return true;
+}
+
+bool DetectRecursion::visitLoop(Visit, TIntermLoop*)
+{
+    return true;
+}
+
+bool DetectRecursion::visitBranch(Visit, TIntermBranch*)
+{
+    return true;
+}
+
+DetectRecursion::ErrorCode DetectRecursion::detectRecursion()
+{
+    FunctionNode* main = findFunctionByName("main(");
+    if (main == NULL)
+        return kErrorMissingMain;
+    if (main->detectRecursion())
+        return kErrorRecursion;
+    return kErrorNone;
+}
+
+DetectRecursion::FunctionNode* DetectRecursion::findFunctionByName(
+    const TString& name)
+{
+    for (size_t i = 0; i < functions.size(); ++i) {
+        if (functions[i]->getName() == name)
+            return functions[i];
+    }
+    return NULL;
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/compiler/DetectRecursion.h
@@ -0,0 +1,67 @@
+//
+// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_DETECT_RECURSION_H_
+#define COMPILER_DETECT_RECURSION_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+#include "compiler/intermediate.h"
+#include "compiler/VariableInfo.h"
+
+// Traverses intermediate tree to detect function recursion.
+class DetectRecursion : public TIntermTraverser {
+public:
+    enum ErrorCode {
+        kErrorMissingMain,
+        kErrorRecursion,
+        kErrorNone
+    };
+
+    DetectRecursion();
+    ~DetectRecursion();
+
+    virtual void visitSymbol(TIntermSymbol*);
+    virtual void visitConstantUnion(TIntermConstantUnion*);
+    virtual bool visitBinary(Visit, TIntermBinary*);
+    virtual bool visitUnary(Visit, TIntermUnary*);
+    virtual bool visitSelection(Visit, TIntermSelection*);
+    virtual bool visitAggregate(Visit, TIntermAggregate*);
+    virtual bool visitLoop(Visit, TIntermLoop*);
+    virtual bool visitBranch(Visit, TIntermBranch*);
+
+    ErrorCode detectRecursion();
+
+private:
+    class FunctionNode {
+    public:
+        FunctionNode(const TString& fname);
+
+        const TString& getName() const;
+
+        // If a function is already in the callee list, this becomes a no-op.
+        void addCallee(FunctionNode* callee);
+
+        // Return true if recursive function calls are detected.
+        bool detectRecursion();
+
+    private:
+        // mangled function name is unique.
+        TString name;
+
+        // functions that are directly called by this function.
+        TVector<FunctionNode*> callees;
+
+        Visit visit;
+    };
+
+    FunctionNode* findFunctionByName(const TString& name);
+
+    TVector<FunctionNode*> functions;
+    FunctionNode* currentFunction;
+};
+
+#endif  // COMPILER_DETECT_RECURSION_H_
--- a/gfx/angle/src/compiler/ShHandle.h
+++ b/gfx/angle/src/compiler/ShHandle.h
@@ -61,16 +61,18 @@ public:
 
 protected:
     ShShaderType getShaderType() const { return shaderType; }
     ShShaderSpec getShaderSpec() const { return shaderSpec; }
     // Initialize symbol-table with built-in symbols.
     bool InitBuiltInSymbolTable(const ShBuiltInResources& resources);
     // Clears the results from the previous compilation.
     void clearResults();
+    // Return true if function recursion is detected.
+    bool detectRecursion(TIntermNode* root);
     // Returns true if the given shader does not exceed the minimum
     // functionality mandated in GLSL 1.0 spec Appendix A.
     bool validateLimitations(TIntermNode* root);
     // Collect info for all attribs and uniforms.
     void collectAttribsUniforms(TIntermNode* root);
     // Map long variable names into shorter ones.
     void mapLongVariableNames(TIntermNode* root);
     // Translate to object code.
--- a/gfx/angle/src/libEGL/Makefile.in
+++ b/gfx/angle/src/libEGL/Makefile.in
@@ -71,16 +71,17 @@ VPATH += $(srcdir)/.. \
   $(srcdir)/../compiler/preprocessor \
   $(srcdir)/../common \
   $(NULL)
 
 # Translator/compiler first
 
 CPPSRCS = \
   Compiler.cpp \
+  DetectRecursion.cpp \
   InfoSink.cpp \
   Initialize.cpp \
   InitializeDll.cpp \
   Intermediate.cpp \
   intermOut.cpp \
   IntermTraverse.cpp \
   MozAngleLink.cpp \
   parseConst.cpp \
--- a/gfx/angle/src/libGLESv2/Context.cpp
+++ b/gfx/angle/src/libGLESv2/Context.cpp
@@ -2104,26 +2104,26 @@ void Context::readPixels(GLint x, GLint 
     IDirect3DDevice9 *device = getDevice();
 
     D3DSURFACE_DESC desc;
     renderTarget->GetDesc(&desc);
 
     IDirect3DSurface9 *systemSurface;
     HRESULT result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, NULL);
 
-    if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
+    if (FAILED(result))
     {
+        ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
         return error(GL_OUT_OF_MEMORY);
     }
 
-    ASSERT(SUCCEEDED(result));
-
     if (desc.MultiSampleType != D3DMULTISAMPLE_NONE)
     {
         UNIMPLEMENTED();   // FIXME: Requires resolve using StretchRect into non-multisampled render target
+        return error(GL_OUT_OF_MEMORY);
     }
 
     result = device->GetRenderTargetData(renderTarget, systemSurface);
 
     if (FAILED(result))
     {
         systemSurface->Release();
 
--- a/gfx/angle/src/libGLESv2/Makefile.in
+++ b/gfx/angle/src/libGLESv2/Makefile.in
@@ -71,16 +71,17 @@ VPATH += $(srcdir)/..
 VPATH += $(srcdir)/../compiler
 VPATH += $(srcdir)/../compiler/preprocessor
 VPATH += $(srcdir)/../common
 
 # Translator/compiler first
 
 CPPSRCS = \
 	Compiler.cpp \
+        DetectRecursion.cpp \
         InfoSink.cpp \
         Initialize.cpp \
         InitializeDll.cpp \
         Intermediate.cpp \
         intermOut.cpp \
         IntermTraverse.cpp \
         MozAngleLink.cpp \
         parseConst.cpp \
--- a/gfx/thebes/GLContext.cpp
+++ b/gfx/thebes/GLContext.cpp
@@ -429,16 +429,18 @@ static const char *sExtensionNames[] = {
     "GL_OES_depth_texture",
     "GL_OES_packed_depth_stencil",
     "GL_IMG_read_format",
     "GL_EXT_read_format_bgra",
     "GL_APPLE_client_storage",
     "GL_ARB_texture_non_power_of_two",
     "GL_ARB_pixel_buffer_object",
     "GL_ARB_ES2_compatibility",
+    "GL_OES_texture_float",
+    "GL_ARB_texture_float",
     NULL
 };
 
 void
 GLContext::InitExtensions()
 {
     MakeCurrent();
     const GLubyte *extensions = fGetString(LOCAL_GL_EXTENSIONS);
--- a/gfx/thebes/GLContext.h
+++ b/gfx/thebes/GLContext.h
@@ -954,16 +954,18 @@ public:
         OES_depth_texture,
         OES_packed_depth_stencil,
         IMG_read_format,
         EXT_read_format_bgra,
         APPLE_client_storage,
         ARB_texture_non_power_of_two,
         ARB_pixel_buffer_object,
         ARB_ES2_compatibility,
+        OES_texture_float,
+        ARB_texture_float,
         Extensions_Max
     };
 
     PRBool IsExtensionSupported(GLExtensions aKnownExtension) {
         return mAvailableExtensions[aKnownExtension];
     }
 
     // for unknown extensions
--- 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/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2605,35 +2605,16 @@ gfxFontGroup::FindFontForChar(PRUint32 a
 
     // 1. check fonts in the font group
     for (PRUint32 i = 0; i < FontListLength(); i++) {
         nsRefPtr<gfxFont> font = GetFontAt(i);
         if (font->HasCharacter(aCh)) {
             *aMatchType = gfxTextRange::kFontGroup;
             return font.forget();
         }
-        // check other faces of the family
-        // XXX optimization point: give the family a charmap that is the union
-        // of the char maps of all its faces, so we can quickly test whether
-        // it's worth doing this search
-        gfxFontFamily *family = font->GetFontEntry()->Family();
-        if (family) {
-            FontSearch matchData(aCh, font);
-            family->FindFontForChar(&matchData);
-            gfxFontEntry *fe = matchData.mBestMatch;
-            if (fe) {
-                PRBool needsBold =
-                    font->GetStyle()->weight >= 600 && !fe->IsBold();
-                selectedFont =
-                    fe->FindOrMakeFont(font->GetStyle(), needsBold);
-                if (selectedFont) {
-                    return selectedFont.forget();
-                }
-            }
-        }
     }
 
     // if character is in Private Use Area, don't do matching against pref or system fonts
     if ((aCh >= 0xE000  && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
         return nsnull;
 
     // 2. search pref fonts
     if ((selectedFont = WhichPrefFontSupportsChar(aCh))) {
--- 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/jsapi-tests/testIndexToString.cpp
+++ b/js/src/jsapi-tests/testIndexToString.cpp
@@ -38,20 +38,20 @@ static const struct TestPair {
     { 1000, "1000" },
     { 4095, "4095" },
     { 4096, "4096" },
     { 9999, "9999" },
     { 1073741823, "1073741823" },
     { 1073741824, "1073741824" },
     { 1073741825, "1073741825" },
     { 2147483647, "2147483647" },
-    { 2147483648, "2147483648" },
-    { 2147483649, "2147483649" },
-    { 4294967294, "4294967294" },
-    { 4294967295, "4294967295" },
+    { 2147483648u, "2147483648" },
+    { 2147483649u, "2147483649" },
+    { 4294967294u, "4294967294" },
+    { 4294967295u, "4294967295" },
 };
 
 BEGIN_TEST(testIndexToString)
 {
     for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) {
         uint32 u = tests[i].num;
         JSString *str = js::IndexToString(cx, u);
         CHECK(str);
--- 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/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1119,17 +1119,17 @@ Compiler::compileScript(JSContext *cx, J
 
     script = JSScript::NewScriptFromCG(cx, &cg);
     if (!script)
         goto out;
 
     JS_ASSERT(script->savedCallerFun == savedCallerFun);
 
     {
-        AutoShapeRooter shapeRoot(cx, script->bindings.lastShape());
+        AutoScriptRooter root(cx, script);
         if (!defineGlobals(cx, globalScope, script))
             goto late_error;
     }
 
   out:
     JS_FinishArenaPool(&codePool);
     JS_FinishArenaPool(&notePool);
     Probes::compileScriptEnd(cx, script, filename, lineno);
--- 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
@@ -1354,17 +1343,17 @@ mozJSComponentLoader::ImportInto(const n
             if (i == 0) {
                 logBuffer.AssignLiteral("Installing symbols [ ");
             }
             JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
             if (!!bytes)
                 logBuffer.Append(bytes.ptr());
             logBuffer.AppendLiteral(" ");
             if (i == symbolCount - 1) {
-                LOG(("%s] from %s\n", PromiseFlatCString(logBuffer).get(),
+                LOG(("%s] from %s\n", logBuffer.get(),
                                       PromiseFlatCString(aLocation).get()));
             }
 #endif
         }
     }
 
     // Cache this module for later
     if (newEntry) {
--- 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/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1054,37 +1054,40 @@ nsPresContext::SetShell(nsIPresShell* aS
       }
 
       if (mLangService) {
         doc->AddCharSetObserver(this);
         UpdateCharSet(doc->GetDocumentCharacterSet());
       }
     }
   } else {
-    // Destroy image loaders now that the presshell is going away.
-    // This is important since imageloaders can have pointers to frames and
-    // we don't want those pointers to outlive the destruction of the frame
-    // arena.
-    for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) {
-      mImageLoaders[i].Enumerate(destroy_loads, nsnull);
-      mImageLoaders[i].Clear();
-    }
-
     if (mTransitionManager) {
       mTransitionManager->Disconnect();
       mTransitionManager = nsnull;
     }
     if (mAnimationManager) {
       mAnimationManager->Disconnect();
       mAnimationManager = nsnull;
     }
   }
 }
 
 void
+nsPresContext::DestroyImageLoaders()
+{
+  // Destroy image loaders. This is important to do when frames are being
+  // destroyed because imageloaders can have pointers to frames and we don't
+  // want those pointers to outlive the destruction of the frame arena.
+  for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) {
+    mImageLoaders[i].Enumerate(destroy_loads, nsnull);
+    mImageLoaders[i].Clear();
+  }
+}
+
+void
 nsPresContext::UpdateCharSet(const nsAFlatCString& aCharSet)
 {
   if (mLangService) {
     NS_IF_RELEASE(mLanguage);
     mLanguage = mLangService->LookupCharSet(aCharSet.get()).get();  // addrefs
     // this will be a language group (or script) code rather than a true language code
 
     // bug 39570: moved from nsLanguageAtomService::LookupCharSet()
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -967,16 +967,18 @@ public:
   }
 
   void NotifyDestroyingFrame(nsIFrame* aFrame)
   {
     PropertyTable()->DeleteAllFor(aFrame);
   }
   inline void ForgetUpdatePluginGeometryFrame(nsIFrame* aFrame);
 
+  void DestroyImageLoaders();
+
   PRBool GetContainsUpdatePluginGeometryFrame()
   {
     return mContainsUpdatePluginGeometryFrame;
   }
 
   void SetContainsUpdatePluginGeometryFrame(PRBool aValue)
   {
     mContainsUpdatePluginGeometryFrame = aValue;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -2981,16 +2981,22 @@ PresShell::FireResizeEvent()
     nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status);
     mInResize = PR_FALSE;
   }
 }
 
 void
 PresShell::SetIgnoreFrameDestruction(PRBool aIgnore)
 {
+  if (mPresContext) {
+    // We need to destroy the image loaders first, as they won't be
+    // notified when frames are destroyed once this setting takes effect.
+    // (See bug 673984)
+    mPresContext->DestroyImageLoaders();
+  }
   mIgnoreFrameDestruction = aIgnore;
 }
 
 void
 PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
 {
   NS_TIME_FUNCTION_MIN(1.0);
 
@@ -6657,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;
+}
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filter-scaled-01.svg
@@ -0,0 +1,18 @@
+<svg width="100%" height="100%" preserveAspectRatio="none" 
+     xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+ 
+  <title>Testcase for scaled filter with non-integer x and y and objectBoundingBox</title> 
+  <defs>
+    <filter id="filter" filterUnits="objectBoundingBox" x="0.5" y="0.5" width="0.25" height="0.25">
+      <feFlood flood-color="red" width="1" height="1" />
+    </filter>
+  </defs>
+
+  <rect width="100%" height="100%" fill="lime"/>
+  <g transform="scale(400)">
+    <rect width="1" height="1" filter="url(#filter)" />
+  </g>
+  <rect x="200" y="200" width="100" height="100" fill="lime" />
+
+</svg>
--- a/layout/reftests/svg/filters/filter-nested-filtering-01.svg
+++ b/layout/reftests/svg/filters/filter-nested-filtering-01.svg
@@ -1,16 +1,16 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" version="1.0">
   <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=461863 -->
   <desc>
-    This test checks that transforms and filtering a decedent of a
+    This test checks that transforms and filtering a descendant of a
     filtered element interact correctly.
   </desc>
   <filter id="a"><feGaussianBlur stdDeviation="0.001"/></filter>
   <filter id="b"><feGaussianBlur stdDeviation="0.001"/></filter>
   <rect width="100%" height="100%" fill="lime"/>
   <circle fill="red" cx="200" cy="200" r="98"/>
   <g id="g" filter="url(#a)" transform="translate(100, 0)">
     <circle fill="lime" filter="url(#b)" cx="100" cy="200" r="100"/>
--- a/layout/reftests/svg/objectBoundingBox-and-fePointLight-01-ref.svg
+++ b/layout/reftests/svg/objectBoundingBox-and-fePointLight-01-ref.svg
@@ -1,24 +1,23 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
-<svg width="100%" height="100%"
-  viewBox="0 0 480 360" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
   <title>Reference for objectBoundingBox with fePointLight</title>