Merge m-c to b-s.
authorKyle Huey <khuey@kylehuey.com>
Wed, 17 Aug 2011 07:32:25 -0400
changeset 75464 5d1198b8ba0ac9d6789f4276b397803af9a18399
parent 75463 15efac661d6a3ae92cc63edf6e0683ac30e3e969 (current diff)
parent 75394 3d615a56ad46e4de2494d0f987fc12be7f45d0e9 (diff)
child 75465 c5882ebe849983442f77ea1992a82638307c05fd
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone9.0a1
Merge m-c to b-s.
config/config.mk
js/src/config/config.mk
toolkit/themes/pinstripe/global/tree/item.png
toolkit/themes/winstripe/mozapps/places/defaultFavicon-aero.png
--- a/.hgtags
+++ b/.hgtags
@@ -62,8 +62,9 @@ b70744835d94e54eec97b8fd186c96da5708a506
 a71bd564ebf5bf4f93d13e84114f759c263130b0 MOBILE_MERGE_DONE
 a71bd564ebf5bf4f93d13e84114f759c263130b0 MOBILE_MERGE_DONE_20110406
 a95d426422816513477e5863add1b00ac7041dcb AURORA_BASE_20110412
 138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
 9eae975b3d6fb7748fe5a3c0113d449b1c7cc0b2 AURORA_BASE_20110524
 138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
+41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
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/Makefile.in
+++ b/browser/Makefile.in
@@ -40,23 +40,23 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(topsrcdir)/config/config.mk
 
 PARALLEL_DIRS = \
   base \
   components \
-  devtools \
   fuel \
   locales \
   themes \
   $(NULL)
 
 DIRS = \
+  devtools \
   app \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 ifdef MOZ_INSTALLER
 DIRS += installer/windows
 endif
 endif
--- 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/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -510,17 +510,17 @@ GroupItem.prototype = Utils.extend(new I
 
     // Make the computed bounds' "padding" and expand button margin actually be
     // themeable --OR-- compute this from actual bounds. Bug 586546
     box.inset(6, 6);
 
     // make some room for the expand button if we're stacked
     let isStacked = (options && options.forceStacked) || this.isStacked();
     if (isStacked)
-      box.height -= 33; // 33px room for the expand button
+      box.height -= this.$expander.height() + 9; // the button height plus padding
 
     return box;
   },
 
   // ----------
   // Function: setBounds
   // Sets the bounds with the given <Rect>, animating unless "immediately" is false.
   //
@@ -1232,17 +1232,16 @@ GroupItem.prototype = Utils.extend(new I
   },
 
   // ----------
   // Function: showExpandControl
   // Show the control which expands a stacked groupItem into a quick-look view.
   showExpandControl: function GroupItem_showExpandControl() {
     let parentBB = this.getBounds();
     let childBB = this.getChild(0).getBounds();
-    let padding = 7;
     this.$expander
         .show()
         .css({
           left: parentBB.width/2 - this.$expander.width()/2
         });
   },
 
   // ----------
@@ -1358,25 +1357,27 @@ GroupItem.prototype = Utils.extend(new I
 
     if (GroupItems._arrangePaused) {
       GroupItems.pushArrange(this, options);
       return false;
     }
     
     let shouldStack = this.shouldStack(childrenToArrange.length + (options.addTab ? 1 : 0));
     let shouldStackArrange = (shouldStack && !this.expanded);
-    let box = this.getContentBounds({forceStacked: shouldStackArrange});
-    
+    let box;
+
     // if we should stack and we're not expanded
     if (shouldStackArrange) {
       this.showExpandControl();
+      box = this.getContentBounds({forceStacked: true});
       this._stackArrange(childrenToArrange, box, options);
       return false;
     } else {
       this.hideExpandControl();
+      box = this.getContentBounds({forceStacked: false});
       // a dropIndex is returned
       return this._gridArrange(childrenToArrange, box, options);
     }
   },
 
   // ----------
   // Function: _stackArrange
   // Arranges the children in a stack.
--- a/browser/base/content/tabview/storage.js
+++ b/browser/base/content/tabview/storage.js
@@ -180,16 +180,33 @@ let Storage = {
     } catch (e) {
       // getWindowValue will fail if the property doesn't exist
       Utils.log("Error in readGroupItemData: "+e, data);
     }
     return existingData;
   },
 
   // ----------
+  // Function: readWindowBusyState
+  // Returns the current busyState for the given window.
+  readWindowBusyState: function Storage_readWindowBusyState(win) {
+    let state;
+
+    try {
+      let data = this._sessionStore.getWindowState(win);
+      if (data)
+        state = JSON.parse(data);
+    } catch (e) {
+      Utils.log("Error while parsing window state");
+    }
+
+    return (state && state.windows[0].busy);
+  },
+
+  // ----------
   // Function: saveGroupItemsData
   // Saves the global data for the <GroupItems> singleton for the given window.
   saveGroupItemsData: function Storage_saveGroupItemsData(win, data) {
     this.saveData(win, this.GROUPS_DATA_IDENTIFIER, data);
   },
 
   // ----------
   // Function: readGroupItemsData
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -118,19 +118,19 @@ let UI = {
   // Keeps track of info related to private browsing, including: 
   //   transitionMode - whether we're entering or exiting PB
   //   wasInTabView - whether TabView was visible before we went into PB
   _privateBrowsing: {
     transitionMode: "",
     wasInTabView: false 
   },
   
-  // Variable: _storageBusyCount
-  // Used to keep track of how many calls to storageBusy vs storageReady.
-  _storageBusyCount: 0,
+  // Variable: _storageBusy
+  // Tells whether the storage is currently busy or not.
+  _storageBusy: false,
 
   // Variable: isDOMWindowClosing
   // Tells wether the parent window is about to close
   isDOMWindowClosing: false,
 
   // Variable: _browserKeys
   // Used to keep track of allowed browser keys.
   _browserKeys: null,
@@ -164,16 +164,20 @@ let UI = {
       // initialize the direction of the page
       this._initPageDirection();
 
       // ___ thumbnail storage
       ThumbnailStorage.init();
 
       // ___ storage
       Storage.init();
+
+      if (Storage.readWindowBusyState(gWindow))
+        this.storageBusy();
+
       let data = Storage.readUIData(gWindow);
       this._storageSanity(data);
       this._pageBounds = data.pageBounds;
 
       // ___ currentTab
       this._currentTab = gBrowser.selectedTab;
 
       // ___ exit button
@@ -607,39 +611,42 @@ let UI = {
   },
 #endif
 
   // ----------
   // Function: storageBusy
   // Pauses the storage activity that conflicts with sessionstore updates and 
   // private browsing mode switches. Calls can be nested. 
   storageBusy: function UI_storageBusy() {
-    if (!this._storageBusyCount) {
-      TabItems.pauseReconnecting();
-      GroupItems.pauseAutoclose();
-    }
-    
-    this._storageBusyCount++;
+    if (this._storageBusy)
+      return;
+
+    this._storageBusy = true;
+
+    TabItems.pauseReconnecting();
+    GroupItems.pauseAutoclose();
   },
   
   // ----------
   // Function: storageReady
   // Resumes the activity paused by storageBusy, and updates for any new group
   // information in sessionstore. Calls can be nested. 
   storageReady: function UI_storageReady() {
-    this._storageBusyCount--;
-    if (!this._storageBusyCount) {
-      let hasGroupItemsData = GroupItems.load();
-      if (!hasGroupItemsData)
-        this.reset();
-  
-      TabItems.resumeReconnecting();
-      GroupItems._updateTabBar();
-      GroupItems.resumeAutoclose();
-    }
+    if (!this._storageBusy)
+      return;
+
+    this._storageBusy = false;
+
+    let hasGroupItemsData = GroupItems.load();
+    if (!hasGroupItemsData)
+      this.reset();
+
+    TabItems.resumeReconnecting();
+    GroupItems._updateTabBar();
+    GroupItems.resumeAutoclose();
   },
 
   // ----------
   // Function: _addTabActionHandlers
   // Adds handlers to handle tab actions.
   _addTabActionHandlers: function UI__addTabActionHandlers() {
     var self = this;
 
@@ -725,17 +732,17 @@ let UI = {
         
       if (self.isTabViewVisible()) {
         // just closed the selected tab in the TabView interface.
         if (self._currentTab == tab)
           self._closedSelectedTabInTabView = true;
       } else {
         // If we're currently in the process of entering private browsing,
         // we don't want to go to the Tab View UI. 
-        if (self._storageBusyCount)
+        if (self._storageBusy)
           return;
 
         // if not closing the last tab
         if (gBrowser.tabs.length > 1) {
           // Don't return to TabView if there are any app tabs
           for (let a = 0; a < gBrowser._numPinnedTabs; a++) {
             if (!gBrowser.tabs[a].closing)
               return;
--- 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/base/content/test/tabview/browser_tabview_bug597248.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug597248.js
@@ -32,36 +32,30 @@ function setupTwo(win) {
 
   let numTabsToSave = tabItems.length;
 
   // force all canvases to update, and hook in imageData save detection
   tabItems.forEach(function(tabItem) {
     contentWindow.TabItems.update(tabItem.tab);
     tabItem.addSubscriber("savedCachedImageData", function onSaved(item) {
       item.removeSubscriber("savedCachedImageData", onSaved);
-      --numTabsToSave;
+
+      if (!--numTabsToSave)
+        restoreWindow();
     });
   });
 
   // after the window is closed, restore it.
-  let xulWindowDestory = function() {
-    Services.obs.removeObserver(
-       xulWindowDestory, "xul-window-destroyed", false);
-
-    // "xul-window-destroyed" is just fired just before a XUL window is
-    // destroyed so restore window and test it after a delay
+  let restoreWindow = function() {
     executeSoon(function() {
       restoredWin = undoCloseWindow();
       restoredWin.addEventListener("load", function onLoad(event) {
         restoredWin.removeEventListener("load", onLoad, false);
 
         registerCleanupFunction(function() restoredWin.close());
-
-        // ensure that closed tabs have been saved
-        is(numTabsToSave, 0, "All tabs were saved when window was closed.");
         is(restoredWin.gBrowser.tabs.length, 3, "The total number of tabs is 3");
 
         // setup tab variables and listen to the tabs load progress
         newTabOne = restoredWin.gBrowser.tabs[0];
         newTabTwo = restoredWin.gBrowser.tabs[1];
         newTabThree = restoredWin.gBrowser.tabs[2];
         restoredWin.gBrowser.addTabsProgressListener(gTabsProgressListener);
 
@@ -98,17 +92,16 @@ function setupTwo(win) {
           });
         }
 
         restoredWin.addEventListener(
           "tabviewframeinitialized", onTabViewFrameInitialized, false);
       }, false);
     });
   };
-  Services.obs.addObserver(xulWindowDestory, "xul-window-destroyed", false);
 
   win.close();
 }
 
 let gTabsProgressListener = {
   onStateChange: function(browser, webProgress, request, stateFlags, status) {
     // ensure about:blank doesn't trigger the code
     if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
--- a/browser/base/content/test/tabview/browser_tabview_privatebrowsing.js
+++ b/browser/base/content/test/tabview/browser_tabview_privatebrowsing.js
@@ -44,21 +44,25 @@ function onTabViewLoadedAndShown() {
 
   // collect the group titles
   let count = contentWindow.GroupItems.groupItems.length;
   for (let a = 0; a < count; a++) {
     let gi = contentWindow.GroupItems.groupItems[a];
     groupTitles[a] = gi.getTitle();
   }
 
+  contentWindow.gPrefBranch.setBoolPref("animate_zoom", false);
+
   // Create a second tab
   gBrowser.addTab("about:robots");
   is(gBrowser.tabs.length, 2, "we now have 2 tabs");
+
   registerCleanupFunction(function() {
     gBrowser.removeTab(gBrowser.tabs[1]);
+    contentWindow.gPrefBranch.clearUserPref("animate_zoom");
   });
 
   afterAllTabsLoaded(function() {
     // Get normal tab urls
     for (let a = 0; a < gBrowser.tabs.length; a++)
       normalURLs.push(gBrowser.tabs[a].linkedBrowser.currentURI.spec);
 
     // verify that we're all set up for our test
--- a/browser/base/content/test/tabview/head.js
+++ b/browser/base/content/test/tabview/head.js
@@ -356,16 +356,19 @@ function restoreTab(callback, index, win
 }
 
 // ----------
 function togglePrivateBrowsing(callback) {
   let topic = "private-browsing-transition-complete";
 
   Services.obs.addObserver(function observe() {
     Services.obs.removeObserver(observe, topic);
-    afterAllTabsLoaded(callback);
+
+    // use executeSoon() to let Panorama load its group data from the session
+    // before we call afterAllTabsLoaded()
+    executeSoon(function () afterAllTabsLoaded(callback));
   }, topic, false);
 
   let pb = Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
 
   pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
 }
--- 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
@@ -757,17 +780,17 @@ SessionStoreService.prototype = {
     if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" ||
         this._loadState == STATE_QUITTING)
       return;
 
     // assign it a unique identifier (timestamp)
     aWindow.__SSi = "window" + Date.now();
 
     // and create its data object
-    this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [] };
+    this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
     if (!this._isWindowLoaded(aWindow))
       this._windows[aWindow.__SSi]._restoring = true;
     if (!aWindow.toolbar.visible)
       this._windows[aWindow.__SSi].isPopup = true;
     
     // perform additional initialization when the first window is loading
     if (this._loadState == STATE_STOPPED) {
       this._loadState = STATE_RUNNING;
@@ -941,16 +964,19 @@ SessionStoreService.prototype = {
       // Until we decide otherwise elsewhere, this window is part of a series
       // of closing windows to quit.
       winData._shouldRestore = true;
 #endif
 
       // save the window if it has multiple tabs or a single saveable tab
       if (winData.tabs.length > 1 ||
           (winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0]))) {
+        // we don't want to save the busy state
+        delete winData.busy;
+
         this._closedWindows.unshift(winData);
         this._capClosedWindows();
       }
       
       // clear this window from the list
       delete this._windows[aWindow.__SSi];
       
       // save the state without this window to disk
@@ -1237,33 +1263,33 @@ SessionStoreService.prototype = {
   },
 
   setTabState: function sss_setTabState(aTab, aState) {
     var tabState = JSON.parse(aState);
     if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
     
     var window = aTab.ownerDocument.defaultView;
-    this._sendWindowStateEvent(window, "Busy");
+    this._setWindowStateBusy(window);
     this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
   },
 
   duplicateTab: function sss_duplicateTab(aWindow, aTab, aDelta) {
     if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi ||
         !aWindow.getBrowser)
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
 
     var tabState = this._collectTabData(aTab, true);
     var sourceWindow = aTab.ownerDocument.defaultView;
     this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true);
     tabState.index += aDelta;
     tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
     tabState.pinned = false;
 
-    this._sendWindowStateEvent(aWindow, "Busy");
+    this._setWindowStateBusy(aWindow);
     let newTab = aTab == aWindow.gBrowser.selectedTab ?
       aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab}) :
       aWindow.gBrowser.addTab();
     this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0);
 
     return newTab;
   },
 
@@ -1296,17 +1322,17 @@ SessionStoreService.prototype = {
     aIndex = aIndex || 0;
     if (!(aIndex in closedTabs))
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
     
     // fetch the data of closed tab, while removing it from the array
     let closedTab = closedTabs.splice(aIndex, 1).shift();
     let closedTabState = closedTab.state;
 
-    this._sendWindowStateEvent(aWindow, "Busy");
+    this._setWindowStateBusy(aWindow);
     // create a new tab
     let browser = aWindow.gBrowser;
     let tab = browser.addTab();
 
     // restore tab content
     this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1, 0, 0);
       
     // restore the tab's position
@@ -2506,17 +2532,17 @@ SessionStoreService.prototype = {
     catch (ex) { // invalid state object - don't restore anything 
       debug(ex);
       this._sendRestoreCompletedNotifications();
       return;
     }
 
     // We're not returning from this before we end up calling restoreHistoryPrecursor
     // for this window, so make sure we send the SSWindowStateBusy event.
-    this._sendWindowStateEvent(aWindow, "Busy");
+    this._setWindowStateBusy(aWindow);
 
     if (root._closedWindows)
       this._closedWindows = root._closedWindows;
 
     var winData;
     if (!aState.selectedWindow) {
       aState.selectedWindow = 0;
     }
@@ -2693,17 +2719,17 @@ SessionStoreService.prototype = {
       delete this._statesToRestore[aWindow.__SS_restoreID];
       delete aWindow.__SS_restoreID;
       delete this._windows[aWindow.__SSi]._restoring;
     }
 
     if (aTabs.length == 0) {
       // this is normally done in restoreHistory() but as we're returning early
       // here we need to take care of it.
-      this._sendWindowStateEvent(aWindow, "Ready");
+      this._setWindowStateReady(aWindow);
       return;
     }
 
     let unhiddenTabs = aTabData.filter(function (aData) !aData.hidden).length;
 
     // if all tabs to be restored are hidden, make the first one visible
     if (unhiddenTabs == 0) {
       aTabData[0].hidden = false;
@@ -2838,17 +2864,17 @@ SessionStoreService.prototype = {
     var _this = this;
     while (aTabs.length > 0 && (!aTabData[0]._tabStillLoading || !aTabs[0].parentNode)) {
       aTabs.shift(); // this tab got removed before being completely restored
       aTabData.shift();
     }
     if (aTabs.length == 0) {
       // At this point we're essentially ready for consumers to read/write data
       // via the sessionstore API so we'll send the SSWindowStateReady event.
-      this._sendWindowStateEvent(aWindow, "Ready");
+      this._setWindowStateReady(aWindow);
       return; // no more tabs to restore
     }
     
     var tab = aTabs.shift();
     var tabData = aTabData.shift();
 
     var browser = aWindow.gBrowser.getBrowserForTab(tab);
     var history = browser.webNavigation.sessionHistory;
@@ -2898,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);
@@ -3034,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) {
@@ -3964,26 +3990,69 @@ 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:
+    function sss__changeWindowStateBusyValue(aWindow, aValue) {
+
+    this._windows[aWindow.__SSi].busy = aValue;
+
+    // Keep the to-be-restored state in sync because that is returned by
+    // getWindowState() as long as the window isn't loaded, yet.
+    if (!this._isWindowLoaded(aWindow)) {
+      let stateToRestore = this._statesToRestore[aWindow.__SS_restoreID].windows[0];
+      stateToRestore.busy = aValue;
+    }
+  },
+
+  /**
+   * Set the given window's state to 'not busy'.
+   * @param aWindow the window
+   */
+  _setWindowStateReady: function sss__setWindowStateReady(aWindow) {
+    this._setWindowStateBusyValue(aWindow, false);
+    this._sendWindowStateEvent(aWindow, "Ready");
+  },
+
+  /**
+   * Set the given window's state to 'busy'.
+   * @param aWindow the window
+   */
+  _setWindowStateBusy: function sss__setWindowStateBusy(aWindow) {
+    this._setWindowStateBusyValue(aWindow, true);
+    this._sendWindowStateEvent(aWindow, "Busy");
   },
 
   /**
    * Dispatch an SSWindowState_____ event for the given window.
    * @param aWindow the window
    * @param aType the type of event, SSWindowState will be prepended to this string
    */
   _sendWindowStateEvent: function sss__sendWindowStateEvent(aWindow, aType) {
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -144,17 +144,19 @@ 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 \
 	$(NULL)
 endif
--- a/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js
+++ b/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js
@@ -55,17 +55,17 @@ function test() {
 let tests = [test_cascade, test_select, test_multiWindowState,
              test_setWindowStateNoOverwrite, test_setWindowStateOverwrite,
              test_setBrowserStateInterrupted, test_reload,
              /* test_reloadReload, */ test_reloadCascadeSetup,
              /* test_reloadCascade */];
 function runNextTest() {
   // Reset the pref
   try {
-    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+    Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
   } catch (e) {}
 
   // set an empty state & run the next test, or finish
   if (tests.length) {
     // Enumerate windows and close everything but our primary window. We can't
     // use waitForFocus() because apparently it's buggy. See bug 599253.
     var windowsEnum = Services.wm.getEnumerator("navigator:browser");
     while (windowsEnum.hasMoreElements()) {
@@ -83,19 +83,16 @@ function runNextTest() {
   else {
     ss.setBrowserState(stateBackup);
     executeSoon(finish);
   }
 }
 
 
 function test_cascade() {
-  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
-
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       dump("\n\nload: " + aBrowser.currentURI.spec + "\n" + JSON.stringify(countTabs()) + "\n\n");
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -113,21 +110,21 @@ function test_cascade() {
   ] }] };
 
   let loadCount = 0;
   // Since our progress listener is fired before the one in sessionstore, our
   // expected counts look a little weird. This is because we inspect the state
   // before sessionstore has marked the tab as finished restoring and before it
   // starts restoring the next tab
   let expectedCounts = [
-    [5, 1, 0],
-    [4, 1, 1],
-    [3, 1, 2],
-    [2, 1, 3],
-    [1, 1, 4],
+    [3, 3, 0],
+    [2, 3, 1],
+    [1, 3, 2],
+    [0, 3, 3],
+    [0, 2, 4],
     [0, 1, 5]
   ];
 
   function test_cascade_progressCallback() {
     loadCount++;
     let counts = countTabs();
     let expected = expectedCounts[loadCount - 1];
 
@@ -144,19 +141,19 @@ function test_cascade() {
 
   // This progress listener will get attached before the listener in session store.
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 function test_select() {
-  // Set the pref to 0 so we know exactly how many tabs should be restoring at
+  // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
@@ -293,19 +290,16 @@ function test_multiWindowState() {
   Services.ww.registerNotification(windowObserver);
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 function test_setWindowStateNoOverwrite() {
-  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
-
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -365,19 +359,16 @@ function test_setWindowStateNoOverwrite(
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setWindowState(window, JSON.stringify(state1), true);
 }
 
 
 function test_setWindowStateOverwrite() {
-  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
-
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -437,19 +428,16 @@ function test_setWindowStateOverwrite() 
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setWindowState(window, JSON.stringify(state1), true);
 }
 
 
 function test_setBrowserStateInterrupted() {
-  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
-
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       // We only care about load events when the tab still has
       // __SS_restoreState == TAB_STATE_RESTORING on it.
       // Since our listener is attached before the sessionstore one, this works out.
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
@@ -556,19 +544,19 @@ function test_setBrowserStateInterrupted
   Services.ww.registerNotification(windowObserver);
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state1));
 }
 
 
 function test_reload() {
-  // Set the pref to 0 so we know exactly how many tabs should be restoring at
+  // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+  Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
       if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
--- a/browser/components/sessionstore/test/browser/browser_595601-restore_hidden.js
+++ b/browser/components/sessionstore/test/browser/browser_595601-restore_hidden.js
@@ -1,116 +1,117 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TAB_STATE_NEEDS_RESTORE = 1;
 const TAB_STATE_RESTORING = 2;
 
-let stateBackup = ss.getBrowserState();
-
 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#4"}]},
   {entries:[{url:"http://example.com#5"}], hidden: true},
   {entries:[{url:"http://example.com#6"}], hidden: true},
   {entries:[{url:"http://example.com#7"}], hidden: true},
   {entries:[{url:"http://example.com#8"}], hidden: true}
 ]}]};
 
 function test() {
   waitForExplicitFinish();
 
   registerCleanupFunction(function () {
-    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
     Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
-
-    TabsProgressListener.uninit();
-
-    ss.setBrowserState(stateBackup);
   });
 
-  TabsProgressListener.init();
-
-  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 3);
-
   // First stage: restoreHiddenTabs = true
   // Second stage: restoreHiddenTabs = false
   test_loadTabs(true, function () {
-    test_loadTabs(false, function () {
-      waitForFocus(finish);
-    });
+    test_loadTabs(false, finish);
   });
 }
 
 function test_loadTabs(restoreHiddenTabs, callback) {
   Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", restoreHiddenTabs);
 
   let expectedTabs = restoreHiddenTabs ? 8 : 4;
-
   let firstProgress = true;
 
-  TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
+  newWindowWithState(state, function (win, needsRestore, isRestoring) {
     if (firstProgress) {
       firstProgress = false;
       is(isRestoring, 3, "restoring 3 tabs concurrently");
     } else {
       ok(isRestoring < 4, "restoring max. 3 tabs concurrently");
     }
 
-    if (gBrowser.tabs.length - needsRestore == expectedTabs) {
-      TabsProgressListener.unsetCallback();
-      is(gBrowser.visibleTabs.length, 4, "only 4 visible tabs");
+    if (win.gBrowser.tabs.length - needsRestore == expectedTabs) {
+      is(win.gBrowser.visibleTabs.length, 4, "only 4 visible tabs");
+
+      TabsProgressListener.uninit();
       callback();
     }
   });
-
-  ss.setBrowserState(JSON.stringify(state));
-}
-
-function countTabs() {
-  let needsRestore = 0, isRestoring = 0;
-  let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-
-  while (windowsEnum.hasMoreElements()) {
-    let window = windowsEnum.getNext();
-    if (window.closed)
-      continue;
-
-    for (let i = 0; i < window.gBrowser.tabs.length; i++) {
-      let browser = window.gBrowser.tabs[i].linkedBrowser;
-      if (browser.__SS_restoreState == TAB_STATE_RESTORING)
-        isRestoring++;
-      else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
-        needsRestore++;
-    }
-  }
-
-  return [needsRestore, isRestoring];
 }
 
 let TabsProgressListener = {
-  init: function () {
-    gBrowser.addTabsProgressListener(this);
+  init: function (win) {
+    this.window = win;
+
+    this.window.gBrowser.addTabsProgressListener(this);
   },
 
   uninit: function () {
-    this.unsetCallback();
-    gBrowser.removeTabsProgressListener(this);
+    this.window.gBrowser.removeTabsProgressListener(this);
+
+    delete this.window;
+    delete this.callback;
   },
 
   setCallback: function (callback) {
     this.callback = callback;
   },
 
-  unsetCallback: function () {
-    delete this.callback;
-  },
-
   onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
     if (this.callback && aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
-      this.callback.apply(null, countTabs());
+      this.callback.apply(null, [this.window].concat(this.countTabs()));
+  },
+
+  countTabs: function () {
+    let needsRestore = 0, isRestoring = 0;
+
+    for (let i = 0; i < this.window.gBrowser.tabs.length; i++) {
+      let browser = this.window.gBrowser.tabs[i].linkedBrowser;
+      if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+        isRestoring++;
+      else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+        needsRestore++;
+    }
+
+    return [needsRestore, isRestoring];
   }
 }
+
+// ----------
+function whenWindowLoaded(win, callback) {
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    executeSoon(callback);
+  }, false);
+}
+
+// ----------
+function newWindowWithState(state, callback) {
+  let opts = "chrome,all,dialog=no,height=800,width=800";
+  let win = window.openDialog(getBrowserURL(), "_blank", opts);
+
+  registerCleanupFunction(function () win.close());
+
+  whenWindowLoaded(win, function () {
+    TabsProgressListener.init(win);
+    TabsProgressListener.setCallback(callback);
+
+    ss.setWindowState(win, JSON.stringify(state), true);
+  });
+}
--- 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: [] }));
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_662812.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  window.addEventListener("SSWindowStateBusy", function onBusy() {
+    window.removeEventListener("SSWindowStateBusy", onBusy, false);
+
+    let state = JSON.parse(ss.getWindowState(window));
+    ok(state.windows[0].busy, "window is busy");
+
+    window.addEventListener("SSWindowStateReady", function onReady() {
+      window.removeEventListener("SSWindowStateReady", onReady, false);
+
+      let state = JSON.parse(ss.getWindowState(window));
+      ok(!state.windows[0].busy, "window is not busy");
+
+      gBrowser.removeTab(gBrowser.tabs[1]);
+      executeSoon(finish);
+    }, false);
+  }, false);
+
+  // create a new tab
+  let tab = gBrowser.addTab("about:mozilla");
+  let browser = tab.linkedBrowser;
+
+  // close and restore it
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    gBrowser.removeTab(tab);
+    ss.undoCloseTab(window, 0);
+  }, true);
+}
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-8.0a1
+9.0a1
--- 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/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
+++ b/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
@@ -14,19 +14,26 @@ function test()
 {
   waitForExplicitFinish();
 
   const windowUrl = "data:text/xml,<?xml version='1.0'?>" +
     "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
     " title='test for bug 660784' width='600' height='500'><hbox flex='1'/></window>";
   const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
 
-  testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onTabLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onTabLoad, true);
 
-  testWin.addEventListener("load", initEditor, false);
+    testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
+    testWin.addEventListener("load", initEditor, false);
+  }, true);
+
+  content.location = "data:text/html,<p>bug 660784 - test the SourceEditor";
+
 }
 
 function initEditor()
 {
   testWin.removeEventListener("load", initEditor, false);
   testDoc = testWin.document;
 
   let hbox = testDoc.querySelector("hbox");
@@ -314,17 +321,21 @@ function editorLoaded()
   // Done.
 
   editor.destroy();
   ok(!editor.parentElement && !editor.editorElement, "destroy() works");
 
   testWin.close();
 
   testWin = testDoc = editor = null;
-  finish();
+
+  waitForFocus(function() {
+    gBrowser.removeCurrentTab();
+    finish();
+  }, content);
 }
 
 function testBackspaceKey()
 {
   editor.setText("       a\n  b\n c");
   editor.setCaretOffset(7);
   EventUtils.synthesizeKey("VK_BACK_SPACE", {}, testWin);
   is(editor.getText(), "a\n  b\n c", "line outdented (Backspace)");
--- 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/gnomestripe/browser/aboutSessionRestore.css
+++ b/browser/themes/gnomestripe/browser/aboutSessionRestore.css
@@ -76,17 +76,17 @@ treechildren::-moz-tree-image(icon),
 treechildren::-moz-tree-image(noicon) {
   padding-right: 2px;
   margin: 0px 2px;
   width: 16px;
   height: 16px;
 }
 
 treechildren::-moz-tree-image(noicon) {
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 treechildren::-moz-tree-image(container, noicon) {
   list-style-image: url("chrome://browser/skin/aboutSessionRestore-window-icon.png");
 }
 treechildren::-moz-tree-image(checked) {
   list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
 }
 treechildren::-moz-tree-image(partial) {
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -190,17 +190,17 @@ menuitem.bookmark-item {
 
 /* Bookmarks toolbar */
 #PlacesToolbarDropIndicator {
   list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
 }
 
 /* Bookmark items */
 .bookmark-item:not([container])  {
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .bookmark-item[container] {
   list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
 }
 
 .bookmark-item[container][livemark] { 
   list-style-image: url("chrome://browser/skin/feeds/feedIcon16.png");
@@ -977,17 +977,17 @@ toolbar[iconsize="small"] #feed-button {
 
 #page-proxy-stack {
   width: 24px;
   height: 20px;
   padding: 2px 4px;
 }
 
 #page-proxy-favicon:not([src]) {
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 #page-proxy-favicon[pageproxystate="invalid"] {
   opacity: 0.3;
 }
 
 #urlbar-throbber {
   list-style-image: url("chrome://browser/skin/places/searching_16.png");
@@ -1557,17 +1557,17 @@ richlistitem[type~="action"][actiontype=
   outline: 1px dotted;
 }
 
 .tab-throbber,
 .tab-icon-image {
   width: 16px;
   height: 16px;
   -moz-margin-end: 3px;
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .tab-throbber {
   list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
 }
 
 .tab-throbber[progress] {
   list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
@@ -1695,17 +1695,17 @@ richlistitem[type~="action"][actiontype=
 }
 
 #alltabs-button[type="menu"] > .toolbarbutton-icon {
   display: none;
 }
 
 /* All tabs menupopup */
 .alltabs-item > .menu-iconic-left > .menu-iconic-icon {
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .alltabs-item[selected="true"] {
   font-weight: bold;
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
--- a/browser/themes/gnomestripe/browser/places/places.css
+++ b/browser/themes/gnomestripe/browser/places/places.css
@@ -18,17 +18,17 @@
 }
 
 /* Trees */
 treechildren::-moz-tree-image(title) {
   padding-right: 2px;
   margin: 0px 2px;
   width: 16px;
   height: 16px;
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 treechildren::-moz-tree-image(title, livemarkItem) {
   list-style-image: url("chrome://browser/skin/places/livemark-item.png");
 }
 
 treechildren::-moz-tree-image(title, separator) {
   list-style-image: none;
--- a/browser/themes/gnomestripe/browser/preferences/aboutPermissions.css
+++ b/browser/themes/gnomestripe/browser/preferences/aboutPermissions.css
@@ -20,17 +20,17 @@
   padding: 4px;
   border-bottom: 1px solid ThreeDLightShadow;
 }
 
 .site-favicon {
   height: 16px;
   width: 16px;
   -moz-margin-end: 4px;
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 #all-sites-item > .site-container > .site-favicon {
   list-style-image: none;
 }
 
 /* permissions box */
 
--- a/browser/themes/gnomestripe/browser/preferences/preferences.css
+++ b/browser/themes/gnomestripe/browser/preferences/preferences.css
@@ -114,17 +114,17 @@ radio[pane=paneSync] {
   margin: 0px 3px 6px 3px !important;
 }
 
 /* Cookies Manager */
 #cookiesChildren::-moz-tree-image(domainCol) {
   width: 16px;
   height: 16px;
   margin: 0px 2px;
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 #paneApplications {
   margin-left: 4px;
   margin-right: 4px; 
   padding-left: 0;
   padding-right: 0; 
 }
--- a/browser/themes/gnomestripe/browser/searchbar.css
+++ b/browser/themes/gnomestripe/browser/searchbar.css
@@ -16,17 +16,17 @@
 .searchbar-engine-menuitem[selected="true"] > .menu-iconic-text {
   font-weight: bold;
 }
 
 /* Engine button */
 .searchbar-engine-image {
   height: 16px;
   width: 16px;
-  list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
   -moz-margin-start: 2px;
 }
 
 .searchbar-engine-button {
   -moz-appearance: none;
   min-width: 0;
   margin: 0;
   border: 0;
--- a/browser/themes/gnomestripe/browser/tabview/tabview.css
+++ b/browser/themes/gnomestripe/browser/tabview/tabview.css
@@ -213,16 +213,27 @@ html[dir=rtl] .groupItem {
 
 html[dir=rtl] .groupItem.activeGroupItem {
   box-shadow:
     rgba(0,0,0,0.8) -2px 2px 8px;
 }
 
 .groupItem .close {
   z-index: 10;
+  top: 0px;
+  right: 0px;
+  width: 22px;
+  height: 22px;
+  background-position: bottom left;
+}
+
+html[dir=rtl] .groupItem .close {
+  right: auto;
+  left: 0px;
+  background-position: bottom right;
 }
 
 .phantom {
   border: 1px solid rgba(190,190,190,1);
 }
 
 .dragRegion {
   background: rgba(248,248,248,0.4);
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -1524,17 +1524,17 @@ toolbarbutton.chevron > .toolbarbutton-m
 .tabbrowser-tabbox {
   margin: 0;
 }
 
 .tab-throbber,
 .tab-icon-image {
   width: 16px;
   height: 16px;
-  list-style-image: url("chrome://global/skin/tree/item.png");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .tab-throbber {
   list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
 }
 
 .tab-throbber[progress] {
   list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
@@ -2022,17 +2022,17 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 #alltabs-button > .toolbarbutton-icon {
   -moz-margin-end: 2px;
 }
 
 /* All Tabs Menupopup */
 .alltabs-item > .menu-iconic-left > .menu-iconic-icon {
-  list-style-image: url("chrome://global/skin/tree/item.png");
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading_16.png") !important;
 }
 
 .alltabs-item[tabIsVisible] {
   /* box-shadow instead of background-color to work around native styling */
--- a/browser/themes/pinstripe/browser/tabview/tabview.css
+++ b/browser/themes/pinstripe/browser/tabview/tabview.css
@@ -205,16 +205,27 @@ html[dir=rtl] .stack-trayed .tab-title {
 
 html[dir=rtl] .groupItem.activeGroupItem {
   box-shadow:
     rgba(0,0,0,0.8) -2px 2px 8px;
 }
 
 .groupItem .close {
   z-index: 10;
+  top: 0px;
+  right: 0px;
+  width: 22px;
+  height: 22px;
+  background-position: bottom left;
+}
+
+html[dir=rtl] .groupItem .close {
+  right: auto;
+  left: 0px;
+  background-position: bottom right;
 }
 
 .phantom {
   border: 1px solid rgba(255, 255, 255, 0.5);
 }
 
 .dragRegion {
   background: rgba(235, 235, 235, 0.4);
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -583,21 +583,21 @@ menuitem.bookmark-item {
 
 .bookmark-item > .menu-iconic-left > .menu-iconic-icon {
   -moz-padding-start: 0px;
 }
 
 /* ::::: bookmark items ::::: */
 
 .bookmark-item  {
-  list-style-image: url("chrome://global/skin/icons/folder-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .bookmark-item[container] {
+  list-style-image: url("chrome://global/skin/icons/folder-item.png");
   -moz-image-region: rect(0px, 32px, 16px, 16px);
 }
 
 .bookmark-item[container][open] {
   -moz-image-region: rect(16px, 32px, 32px, 16px);
 }
 
 .bookmark-item[container][livemark] { 
@@ -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);
 }
 
@@ -1371,22 +1379,21 @@ html|*.urlbar-input:-moz-lwtheme:-moz-pl
 
 #page-proxy-stack {
   width: 24px;
   height: 18px;
   padding: 1px 4px;
 }
 
 #page-proxy-favicon:not([src]) {
-  list-style-image: url("chrome://global/skin/icons/folder-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px)
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 #page-proxy-favicon[pageproxystate="invalid"] {
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
+  opacity: 0.5;
 }
 
 #urlbar-throbber {
   list-style-image: url("chrome://browser/skin/places/searching_16.png");
 }
 
 /* autocomplete */
 
@@ -1777,18 +1784,17 @@ richlistitem[type~="action"][actiontype=
   background-image: -moz-linear-gradient(hsla(0,0%,100%,.4), hsla(0,0%,75%,.4) 80%),
                     -moz-radial-gradient(center 3px, circle cover, rgba(255,255,255,1) 3%, rgba(186,221,251,.75) 40%, rgba(127,179,255,.5) 80%, rgba(127,179,255,.25));
 }
 
 .tab-throbber,
 .tab-icon-image {
   width: 16px;
   height: 16px;
-  list-style-image: url("chrome://global/skin/icons/folder-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
   -moz-margin-start: 2px;
   -moz-margin-end: 3px;
 }
 
 .tab-throbber {
   list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
 }
 
@@ -1882,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;
 }
@@ -1927,63 +1936,66 @@ 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://global/skin/icons/folder-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .alltabs-item[selected="true"] {
   font-weight: bold;
 }
 
 .alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
--- 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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
--- a/browser/themes/winstripe/browser/preferences/preferences.css
+++ b/browser/themes/winstripe/browser/preferences/preferences.css
@@ -113,21 +113,21 @@ radio[pane=paneSync] {
   margin: 0 3px 6px !important;
 }
 
 /* Cookies Manager */
 #cookiesChildren::-moz-tree-image(domainCol) {
   width: 16px;
   height: 16px;
   margin: 0 2px;
-  list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;
-  -moz-image-region: rect(0, 16px, 16px, 0);
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png") !important;
 }
 
 #cookiesChildren::-moz-tree-image(domainCol, container) {
+  list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
 #cookiesChildren::-moz-tree-image(domainCol, container, open) {
   -moz-image-region: rect(16px, 32px, 32px, 16px);
 }
 
 #cookieInfoBox {
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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/browser/themes/winstripe/browser/tabview/tabview.css
+++ b/browser/themes/winstripe/browser/tabview/tabview.css
@@ -77,16 +77,17 @@ html[dir=rtl] .favicon {
 }
 
 .close {
   top: 6px;
   right: 6px;
   width: 16px;
   height: 16px;
   background-image: -moz-image-rect(url("chrome://browser/skin/tabview/close.png"), 0, 16, 16, 0);
+  background-repeat: no-repeat;
 }
 
 .close:hover {
   background-image: -moz-image-rect(url("chrome://browser/skin/tabview/close.png"), 0, 32, 16, 16);
 }
 
 .close:hover:active {
   background-image: -moz-image-rect(url("chrome://browser/skin/tabview/close.png"), 0, 48, 16, 32);
@@ -231,16 +232,27 @@ html[dir=rtl] .groupItem {
 
 html[dir=rtl] .groupItem.activeGroupItem {
   box-shadow:
     rgba(0,0,0,0.8) -2px 2px 8px;
 }
 
 .groupItem .close {
   z-index: 10;
+  top: 0px;
+  right: 0px;
+  width: 22px;
+  height: 22px;
+  background-position: bottom left;
+}
+
+html[dir=rtl] .groupItem .close {
+  right: auto;
+  left: 0px;
+  background-position: bottom right;
 }
 
 .dragRegion {
   background: rgba(224, 234, 245, 0.4);
 }
 
 .overlay {
   background-color: rgba(0,0,0,.7) !important;
--- 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/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-8.0a1
+9.0a1
--- 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/config/milestone.txt
+++ b/js/src/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-8.0a1
+9.0a1
--- 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