Merge m-c to b-s.
authorKyle Huey <khuey@kylehuey.com>
Wed, 31 Aug 2011 07:52:43 -0400
changeset 76289 95a55b2253c5a0e176f5c4123ac9a2561db6045d
parent 76288 94b987001b3819f8ac50a13a9814ed1a346b2592 (current diff)
parent 76257 e043bf9fa4c1e8a2b40dd702f78b745d5c223f3e (diff)
child 76290 9e4aea4d70300f4655eee28662ecb5552954cd5c
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
milestone9.0a1
Merge m-c to b-s.
config/rules.mk
gfx/angle/angle-amap-arev-fix.patch
gfx/angle/angle-r702.patch
gfx/angle/angle-r707.patch
gfx/angle/angle-r712.patch
gfx/angle/angle-win64.patch
gfx/angle/fix-angle-surface-assert.patch
js/src/config/autoconf.mk.in
js/src/config/rules.mk
js/src/configure.in
layout/base/nsPresShell.cpp
security/manager/Makefile.in
--- a/accessible/src/xul/nsXULTreeGridAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeGridAccessible.cpp
@@ -1165,18 +1165,17 @@ nsXULTreeGridCellAccessible::GetAttribut
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   // "table-cell-index" attribute
   nsAccessible* grandParent = mParent->Parent();
   if (!grandParent)
     return NS_OK;
 
-  nsCOMPtr<nsIAccessibleTable> tableAccessible =
-    do_QueryInterface(static_cast<nsIAccessible*>(grandParent));
+  nsCOMPtr<nsIAccessibleTable> tableAccessible = do_QueryObject(grandParent);
 
   // XXX - temp fix for crash bug 516047
   if (!tableAccessible)
     return NS_ERROR_FAILURE;
     
   PRInt32 colIdx = GetColumnIndex();
 
   PRInt32 cellIdx = -1;
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -793,22 +793,16 @@ pref("browser.sessionstore.restore_on_de
 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);
 
-// The percentage of system memory that the Places database can use.  Out of the
-// allowed cache size it will at most use the size of the database file.
-// Changes to this value are effective after an application restart.
-// Acceptable values are between 0 and 50.
-pref("places.database.cache_to_memory_percentage", 6);
-
 // the (maximum) number of the recent visits to sample
 // when calculating frecency
 pref("places.frecency.numVisits", 10);
 
 // buckets (in days) for frecency calculation
 pref("places.frecency.firstBucketCutoff", 4);
 pref("places.frecency.secondBucketCutoff", 14);
 pref("places.frecency.thirdBucketCutoff", 31);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2208,18 +2208,22 @@ var gLastOpenDirectory = {
         if (!this._lastDir.exists())
           this._lastDir = null;
       }
       catch(e) {}
     }
     return this._lastDir;
   },
   set path(val) {
-    if (!val || !val.exists() || !val.isDirectory())
+    try {
+      if (!val || !val.isDirectory())
+        return;
+    } catch(e) {
       return;
+    }
     this._lastDir = val.clone();
 
     // Don't save the last open directory pref inside the Private Browsing mode
     if (!gPrivateBrowsingUI.privateBrowsingEnabled)
       gPrefService.setComplexValue("browser.open.lastDir", Ci.nsILocalFile,
                                    this._lastDir);
   },
   reset: function() {
@@ -2234,18 +2238,21 @@ function BrowserOpenFileWindow()
     const nsIFilePicker = Components.interfaces.nsIFilePicker;
     var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
     fp.init(window, gNavigatorBundle.getString("openFile"), nsIFilePicker.modeOpen);
     fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | nsIFilePicker.filterImages |
                      nsIFilePicker.filterXML | nsIFilePicker.filterHTML);
     fp.displayDirectory = gLastOpenDirectory.path;
 
     if (fp.show() == nsIFilePicker.returnOK) {
-      if (fp.file && fp.file.exists())
-        gLastOpenDirectory.path = fp.file.parent.QueryInterface(Ci.nsILocalFile);
+      try {
+        if (fp.file)
+          gLastOpenDirectory.path = fp.file.parent.QueryInterface(Ci.nsILocalFile);
+      } catch(e) {
+      }
       openTopWin(fp.fileURL.spec);
     }
   } catch (ex) {
   }
 }
 
 function BrowserCloseTabOrWindow() {
 #ifdef XP_MACOSX
@@ -7326,17 +7333,18 @@ var FeedHandler = {
     return this._feedMenupopup = document.getElementById("multipleFeedsMenuState");
   },
 
   /**
    * Update the browser UI to show whether or not feeds are available when
    * a page is loaded or the user switches tabs to a page that has feeds.
    */
   updateFeeds: function() {
-    clearTimeout(this._updateFeedTimeout);
+    if (this._updateFeedTimeout)
+      clearTimeout(this._updateFeedTimeout);
 
     var feeds = gBrowser.selectedBrowser.feeds;
     var haveFeeds = feeds && feeds.length > 0;
 
     var feedButton = document.getElementById("feed-button");
     if (feedButton)
       feedButton.disabled = !haveFeeds;
 
--- a/browser/base/content/inspector.js
+++ b/browser/base/content/inspector.js
@@ -535,16 +535,17 @@ Highlighter.prototype = {
  */
 var InspectorUI = {
   browser: null,
   tools: {},
   showTextNodesWithWhitespace: false,
   inspecting: false,
   treeLoaded: false,
   prefEnabledName: "devtools.inspector.enabled",
+  isDirty: false,
 
   /**
    * Toggle the inspector interface elements on or off.
    *
    * @param aEvent
    *        The event that requested the UI change. Toolbar button or menu.
    */
   toggleInspectorUI: function IUI_toggleInspectorUI(aEvent)
@@ -580,17 +581,17 @@ var InspectorUI = {
   },
 
   /**
    * Return the default selection element for the inspected document.
    */
   get defaultSelection()
   {
     let doc = this.win.document;
-    return doc.documentElement.lastElementChild;
+    return doc.documentElement ? doc.documentElement.lastElementChild : null;
   },
 
   initializeTreePanel: function IUI_initializeTreePanel()
   {
     this.treeBrowserDocument = this.treeIFrame.contentDocument;
     this.treePanelDiv = this.treeBrowserDocument.createElement("div");
     this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
     this.treePanelDiv.ownerPanel = this;
@@ -774,16 +775,18 @@ var InspectorUI = {
       Cu.import("resource:///modules/domplate.jsm", this);
       this.domplateUtils.setDOM(window);
     }
 
     this.openTreePanel();
 
     this.toolbar.hidden = false;
     this.inspectCmd.setAttribute("checked", true);
+
+    gBrowser.addProgressListener(InspectorProgressListener);
   },
 
   /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
     this.highlighter = new Highlighter(this.browser);
@@ -833,16 +836,18 @@ var InspectorUI = {
 
     if (this.closing || !this.win || !this.browser) {
       return;
     }
 
     this.closing = true;
     this.toolbar.hidden = true;
 
+    gBrowser.removeProgressListener(InspectorProgressListener);
+
     if (!aKeepStore) {
       InspectorStore.deleteStore(this.winID);
       this.win.removeEventListener("pagehide", this, true);
     } else {
       // Update the store before closing.
       if (this.selection) {
         InspectorStore.setValue(this.winID, "selectedNode",
           this.selection);
@@ -1310,16 +1315,18 @@ var InspectorUI = {
 
     // set the new attribute value on the original target DOM element
     this.editingContext.repObj.setAttribute(this.editingContext.attrName, 
                                               editorInput.value);
 
     // update the HTML tree attribute value
     this.editingContext.attrObj.innerHTML = editorInput.value;
 
+    this.isDirty = true;
+
     // event notification    
     Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, 
                                   null);
     
     this.closeEditor();
   },
 
   /**
@@ -1736,14 +1743,127 @@ var InspectorStore = {
       delete this.store[aID][aKey];
       result = true;
     }
 
     return result;
   }
 };
 
+/**
+ * The InspectorProgressListener object is an nsIWebProgressListener which
+ * handles onStateChange events for the inspected browser. If the user makes
+ * changes to the web page and he tries to navigate away, he is prompted to
+ * confirm page navigation, such that he's given the chance to prevent the loss
+ * of edits.
+ */
+var InspectorProgressListener = {
+  onStateChange:
+  function IPL_onStateChange(aProgress, aRequest, aFlag, aStatus)
+  {
+    // Remove myself if the Inspector is no longer open.
+    if (!InspectorUI.isTreePanelOpen) {
+      gBrowser.removeProgressListener(InspectorProgressListener);
+      return;
+    }
+
+    // Skip non-start states.
+    if (!(aFlag & Ci.nsIWebProgressListener.STATE_START)) {
+      return;
+    }
+
+    // If the request is about to happen in a new window, we are not concerned
+    // about the request.
+    if (aProgress.DOMWindow != InspectorUI.win) {
+      return;
+    }
+
+    if (InspectorUI.isDirty) {
+      this.showNotification(aRequest);
+    } else {
+      InspectorUI.closeInspectorUI();
+    }
+  },
+
+  /**
+   * Show an asynchronous notification which asks the user to confirm or cancel
+   * the page navigation request.
+   *
+   * @param nsIRequest aRequest
+   *        The request initiated by the user or by the page itself.
+   * @returns void
+   */
+  showNotification: function IPL_showNotification(aRequest)
+  {
+    aRequest.suspend();
+
+    let notificationBox = gBrowser.getNotificationBox(InspectorUI.browser);
+    let notification = notificationBox.
+      getNotificationWithValue("inspector-page-navigation");
+
+    if (notification) {
+      notificationBox.removeNotification(notification, true);
+    }
+
+    let cancelRequest = function onCancelRequest() {
+      if (aRequest) {
+        aRequest.cancel(Cr.NS_BINDING_ABORTED);
+        aRequest.resume(); // needed to allow the connection to be cancelled.
+        aRequest = null;
+      }
+    };
+
+    let eventCallback = function onNotificationCallback(aEvent) {
+      if (aEvent == "removed") {
+        cancelRequest();
+      }
+    };
+
+    let buttons = [
+      {
+        id: "inspector.confirmNavigationAway.buttonLeave",
+        label: InspectorUI.strings.
+          GetStringFromName("confirmNavigationAway.buttonLeave"),
+        accessKey: InspectorUI.strings.
+          GetStringFromName("confirmNavigationAway.buttonLeaveAccesskey"),
+        callback: function onButtonLeave() {
+          if (aRequest) {
+            aRequest.resume();
+            aRequest = null;
+            InspectorUI.closeInspectorUI();
+          }
+        },
+      },
+      {
+        id: "inspector.confirmNavigationAway.buttonStay",
+        label: InspectorUI.strings.
+          GetStringFromName("confirmNavigationAway.buttonStay"),
+        accessKey: InspectorUI.strings.
+          GetStringFromName("confirmNavigationAway.buttonStayAccesskey"),
+        callback: cancelRequest
+      },
+    ];
+
+    let message = InspectorUI.strings.
+      GetStringFromName("confirmNavigationAway.message");
+
+    notification = notificationBox.appendNotification(message,
+      "inspector-page-navigation", "chrome://browser/skin/Info.png",
+      notificationBox.PRIORITY_WARNING_HIGH, buttons, eventCallback);
+
+    // Make sure this not a transient notification, to avoid the automatic
+    // transient notification removal.
+    notification.persistence = -1;
+  },
+};
+
 /////////////////////////////////////////////////////////////////////////
-//// Initializors
+//// Initializers
 
 XPCOMUtils.defineLazyGetter(InspectorUI, "inspectCmd", function () {
   return document.getElementById("Tools:Inspect");
 });
+
+XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () {
+  return Services.strings.
+         createBundle("chrome://browser/locale/inspector.properties");
+});
+
--- a/browser/base/content/safeMode.js
+++ b/browser/base/content/safeMode.js
@@ -70,18 +70,21 @@ function restoreDefaultBookmarks() {
 }
 
 function deleteLocalstore() {
   const nsIDirectoryServiceContractID = "@mozilla.org/file/directory_service;1";
   const nsIProperties = Components.interfaces.nsIProperties;
   var directoryService =  Components.classes[nsIDirectoryServiceContractID]
                                     .getService(nsIProperties);
   var localstoreFile = directoryService.get("LStoreS", Components.interfaces.nsIFile);
-  if (localstoreFile.exists())
+  try {
     localstoreFile.remove(false);
+  } catch(e) {
+    Components.utils.reportError(e);
+  }
 }
 
 function disableAddons() {
   AddonManager.getAllAddons(function(aAddons) {
     aAddons.forEach(function(aAddon) {
       if (aAddon.type == "theme") {
         // Setting userDisabled to false on the default theme activates it,
         // disables all other themes and deactivates the applied persona, if
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -11,22 +11,16 @@
   display: none;
 }
 
 .tabbrowser-tabs[closebuttons="activetab"] > * > * > * > .tab-close-button:not([pinned])[selected="true"],
 .tabbrowser-tabs[closebuttons="alltabs"] > * > * > * > .tab-close-button:not([pinned]) {
   display: -moz-box;
 }
 
-.tab-close-button[selected="true"] {
-  /* Make this button focusable so clicking on it will not focus the tab while
-     it's getting closed */
-  -moz-user-focus: normal;
-}
-
 .tab-label[pinned] {
   width: 0;
   margin-left: 0 !important;
   margin-right: 0 !important;
   padding-left: 0 !important;
   padding-right: 0 !important;
 }
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3997,17 +3997,16 @@
                      class="tab-icon-image"
                      role="presentation"/>
           <xul:label flex="1"
                      xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected"
                      class="tab-text tab-label"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
                              xbl:inherits="fadein,pinned,selected"
-                             tabindex="-1"
                              clickthrough="never"
                              class="tab-close-button"/>
         </xul:hbox>
       </xul:stack>
     </content>
 
     <implementation>
       <property name="pinned" readonly="true">
@@ -4035,35 +4034,24 @@
       <handler event="mouseout">
         var anonid = event.originalTarget.getAttribute("anonid");
         if (anonid == "close-button")
           this.mOverCloseButton = false;
       </handler>
       <handler event="dragstart" phase="capturing">
         this.style.MozUserFocus = '';
       </handler>
-      <handler event="mousedown" button="0" phase="capturing">
+      <handler event="mousedown">
       <![CDATA[
-        if (this.mOverCloseButton) {
-          event.stopPropagation();
-        }
-        else if (this.selected) {
+        if (this.selected) {
           this.style.MozUserFocus = 'ignore';
           this.clientTop; // just using this to flush style updates
         }
       ]]>
       </handler>
-      <handler event="mousedown" button="1">
-        this.style.MozUserFocus = 'ignore';
-        this.clientTop;
-      </handler>
-      <handler event="mousedown" button="2">
-        this.style.MozUserFocus = 'ignore';
-        this.clientTop;
-      </handler>
       <handler event="mouseup">
         this.style.MozUserFocus = '';
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-alltabs-popup"
            extends="chrome://global/content/bindings/popup.xml#popup">
--- a/browser/base/content/test/inspector/Makefile.in
+++ b/browser/base/content/test/inspector/Makefile.in
@@ -52,12 +52,13 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_store.js \
 		browser_inspector_tab_switch.js \
 		browser_inspector_treePanel_output.js \
 		browser_inspector_treePanel_input.html \
 		browser_inspector_treePanel_result.html \
 		browser_inspector_registertools.js \
 		browser_inspector_bug_665880.js \
 		browser_inspector_editor.js \
+		browser_inspector_bug_566084_location_changed.js \
 		$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/inspector/browser_inspector_bug_566084_location_changed.js
@@ -0,0 +1,121 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let notificationBox = null;
+
+function startLocationTests() {
+  ok(window.InspectorUI, "InspectorUI variable exists");
+  Services.obs.addObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null);
+  InspectorUI.toggleInspectorUI();
+}
+
+function runInspectorTests() {
+  Services.obs.removeObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null);
+
+  let para = content.document.querySelector("p");
+  ok(para, "found the paragraph element");
+  is(para.textContent, "init", "paragraph content is correct");
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
+
+  InspectorUI.isDirty = true;
+
+  notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+  notificationBox.addEventListener("AlertActive", alertActive1, false);
+
+  gBrowser.selectedBrowser.addEventListener("load", onPageLoad, true);
+
+  content.location = "data:text/html,<div>location change test 1 for " +
+    "inspector</div><p>test1</p>";
+}
+
+function alertActive1() {
+  notificationBox.removeEventListener("AlertActive", alertActive1, false);
+
+  let notification = notificationBox.
+    getNotificationWithValue("inspector-page-navigation");
+  ok(notification, "found the inspector-page-navigation notification");
+
+  // By closing the notification it is expected that page navigation is
+  // canceled.
+  executeSoon(function() {
+    notification.close();
+    locationTest2();
+  });
+}
+
+function onPageLoad() {
+  gBrowser.selectedBrowser.removeEventListener("load", onPageLoad, true);
+
+  isnot(content.location.href.indexOf("test2"), -1,
+        "page navigated to the correct location");
+
+  let para = content.document.querySelector("p");
+  ok(para, "found the paragraph element, third time");
+  is(para.textContent, "test2", "paragraph content is correct");
+
+  ok(!InspectorUI.inspecting, "Inspector is not highlighting");
+  ok(!InspectorUI.isTreePanelOpen, "Inspector Panel is not open");
+
+  testEnd();
+}
+
+function locationTest2() {
+  // Location did not change.
+  let para = content.document.querySelector("p");
+  ok(para, "found the paragraph element, second time");
+  is(para.textContent, "init", "paragraph content is correct");
+
+  ok(InspectorUI.inspecting, "Inspector is highlighting");
+  ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open");
+
+  notificationBox.addEventListener("AlertActive", alertActive2, false);
+
+  content.location = "data:text/html,<div>location change test 2 for " +
+    "inspector</div><p>test2</p>";
+}
+
+function alertActive2() {
+  notificationBox.removeEventListener("AlertActive", alertActive2, false);
+
+  let notification = notificationBox.
+    getNotificationWithValue("inspector-page-navigation");
+  ok(notification, "found the inspector-page-navigation notification");
+
+  let buttons = notification.querySelectorAll("button");
+  let buttonLeave = null;
+  for (let i = 0; i < buttons.length; i++) {
+    if (buttons[i].buttonInfo.id == "inspector.confirmNavigationAway.buttonLeave") {
+      buttonLeave = buttons[i];
+      break;
+    }
+  }
+
+  ok(buttonLeave, "the Leave page button was found");
+
+  // Accept page navigation.
+  executeSoon(function(){
+    buttonLeave.doCommand();
+  });
+}
+
+function testEnd() {
+  notificationBox = null;
+  InspectorUI.isDirty = false;
+  gBrowser.removeCurrentTab();
+  executeSoon(finish);
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
+    waitForFocus(startLocationTests, content);
+  }, true);
+
+  content.location = "data:text/html,<div>location change tests for " +
+    "inspector.</div><p>init</p>";
+}
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -339,19 +339,21 @@ nsWindowsShellService::IsDefaultBrowserV
   
   HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
                                 NULL,
                                 CLSCTX_INPROC,
                                 IID_IApplicationAssociationRegistration,
                                 (void**)&pAAR);
 
   if (SUCCEEDED(hr)) {
+    BOOL res;
     hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
                                     APP_REG_NAME,
-                                    aIsDefaultBrowser);
+                                    &res);
+    *aIsDefaultBrowser = res;
 
     pAAR->Release();
     return PR_TRUE;
   }
 #endif  
   return PR_FALSE;
 }
 
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -275,17 +275,17 @@ var Scratchpad = {
       let contentWindow = this.gBrowser.selectedBrowser.contentWindow;
 
       let scriptError = Cc["@mozilla.org/scripterror;1"].
                         createInstance(Ci.nsIScriptError2);
 
       scriptError.initWithWindowID(ex.message + "\n" + ex.stack, ex.fileName,
                                    "", ex.lineNumber, 0, scriptError.errorFlag,
                                    "content javascript",
-                                   this.getWindowId(contentWindow));
+                                   this.getInnerWindowId(contentWindow));
 
       Services.console.logMessage(scriptError);
     }
 
     return result;
   },
 
   /**
@@ -628,26 +628,26 @@ var Scratchpad = {
     this._chromeSandbox = null;
     this._contentSandbox = null;
     this._previousWindow = null;
     this._previousBrowser = null;
     this._previousLocation = null;
   },
 
   /**
-   * Gets the ID of the outer window of the given DOM window object.
+   * Gets the ID of the inner window of the given DOM window object.
    *
    * @param nsIDOMWindow aWindow
    * @return integer
-   *         the outer window ID
+   *         the inner window ID
    */
-  getWindowId: function SP_getWindowId(aWindow)
+  getInnerWindowId: function SP_getInnerWindowId(aWindow)
   {
     return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-           getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+           getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
   },
 
   /**
    * The Scratchpad window DOMContentLoaded event handler. This method
    * initializes the Scratchpad window and source editor.
    *
    * @param nsIDOMEvent aEvent
    */
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -85,17 +85,17 @@ XPCOMUtils.defineLazyGetter(this, "Prope
     Cu.reportError(err);
   }
   return obj.PropertyPanel;
 });
 
 XPCOMUtils.defineLazyGetter(this, "AutocompletePopup", function () {
   var obj = {};
   try {
-    Cu.import("resource://gre/modules/AutocompletePopup.jsm", obj);
+    Cu.import("resource:///modules/AutocompletePopup.jsm", obj);
   }
   catch (err) {
     Cu.reportError(err);
   }
   return obj.AutocompletePopup;
 });
 
 XPCOMUtils.defineLazyGetter(this, "namesAndValuesOf", function () {
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_651501_document_body_autocomplete.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_651501_document_body_autocomplete.js
@@ -1,17 +1,17 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests that document.body autocompletes in the web console.
 
-Cu.import("resource://gre/modules/PropertyPanel.jsm");
+Cu.import("resource:///modules/PropertyPanel.jsm");
 
 function test() {
   addTab("data:text/html,Web Console autocompletion bug in document.body");
   browser.addEventListener("load", onLoad, true);
 }
 
 var gHUD;
 
--- a/browser/devtools/webconsole/test/browser/head.js
+++ b/browser/devtools/webconsole/test/browser/head.js
@@ -31,17 +31,17 @@
  * 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 ***** */
 
-Cu.import("resource://gre/modules/HUDService.jsm");
+Cu.import("resource:///modules/HUDService.jsm");
 
 function log(aMsg)
 {
   dump("*** WebConsoleTest: " + aMsg + "\n");
 }
 
 function pprint(aObj)
 {
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/inspector.properties
@@ -0,0 +1,9 @@
+# LOCALIZATION NOTE (confirmNavigationAway): Used in the Inspector tool, when
+# the user tries to navigate away from a web page, to confirm the change of
+# page.
+confirmNavigationAway.message=Leaving this page will close the Inspector and the changes you have made will be lost.
+confirmNavigationAway.buttonLeave=Leave Page
+confirmNavigationAway.buttonLeaveAccesskey=L
+confirmNavigationAway.buttonStay=Stay on Page
+confirmNavigationAway.buttonStayAccesskey=S
+
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -11,16 +11,17 @@
 #ifdef MOZ_SERVICES_SYNC
     locale/browser/aboutSyncTabs.dtd               (%chrome/browser/aboutSyncTabs.dtd)
 #endif
 *   locale/browser/browser.dtd                     (%chrome/browser/browser.dtd)
     locale/browser/baseMenuOverlay.dtd             (%chrome/browser/baseMenuOverlay.dtd)
     locale/browser/browser.properties              (%chrome/browser/browser.properties)
     locale/browser/scratchpad.properties           (%chrome/browser/scratchpad.properties)
     locale/browser/scratchpad.dtd                  (%chrome/browser/scratchpad.dtd)
+    locale/browser/inspector.properties            (%chrome/browser/inspector.properties)
     locale/browser/openLocation.dtd                (%chrome/browser/openLocation.dtd)
     locale/browser/openLocation.properties         (%chrome/browser/openLocation.properties)
 *   locale/browser/pageInfo.dtd                    (%chrome/browser/pageInfo.dtd)
     locale/browser/pageInfo.properties             (%chrome/browser/pageInfo.properties)
     locale/browser/quitDialog.properties           (%chrome/browser/quitDialog.properties)
 *   locale/browser/safeMode.dtd                    (%chrome/browser/safeMode.dtd)
     locale/browser/sanitize.dtd                    (%chrome/browser/sanitize.dtd)
     locale/browser/search.properties               (%chrome/browser/search.properties)
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -1863,20 +1863,16 @@ richlistitem[type~="action"][actiontype=
 .tab-close-button:hover:active[selected="true"] {
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 .tab-close-button[selected="true"] {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-.tab-close-button:focus {
-  outline: none !important;
-}
-
 /* Tab scrollbox arrow, tabstrip new tab and all-tabs buttons */
 
 @media all and (-moz-touch-enabled) {
   .tabbrowser-arrowscrollbox > .scrollbutton-up,
   .tabbrowser-arrowscrollbox > .scrollbutton-down,
   #TabsToolbar .toolbarbutton-1 {
     min-width: 8.1mozmm;
   }
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -331,16 +331,17 @@ class Automation(object):
     self.setupPermissionsDatabase(profileDir,
       {'allowXULXBL':[(l.host, 'noxul' not in l.options) for l in locations]});
 
     part = """\
 user_pref("browser.console.showInPanel", true);
 user_pref("browser.dom.window.dump.enabled", true);
 user_pref("browser.firstrun.show.localepicker", false);
 user_pref("browser.firstrun.show.uidiscovery", false);
+user_pref("browser.ui.layout.tablet", 0); // force tablet UI off
 user_pref("dom.allow_scripts_to_close_windows", true);
 user_pref("dom.disable_open_during_load", false);
 user_pref("dom.max_script_run_time", 0); // no slow script dialogs
 user_pref("dom.max_chrome_script_run_time", 0);
 user_pref("dom.popup_maximum", -1);
 user_pref("dom.send_after_paint_to_content", true);
 user_pref("dom.successive_dialog_time_limit", 0);
 user_pref("signed.applets.codebase_principal_support", true);
--- a/build/mobile/sutagent/android/watcher/AndroidManifest.xml
+++ b/build/mobile/sutagent/android/watcher/AndroidManifest.xml
@@ -1,13 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.mozilla.watcher"
       android:versionCode="1"
       android:versionName="1.0">
+    <uses-permission android:name="android.permission.WRITE_SETTINGS"></uses-permission>
     <application android:icon="@drawable/icon" android:label="@string/app_name">
         <activity android:name=".WatcherMain"
                   android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
--- a/build/mobile/sutagent/android/watcher/WatcherService.java
+++ b/build/mobile/sutagent/android/watcher/WatcherService.java
@@ -53,26 +53,30 @@ import java.util.TimerTask;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.BatteryManager;
+import android.os.Debug;
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.Gravity;
 import android.widget.Toast;
 
 public class WatcherService extends Service
 {
     String sErrorPrefix = "##Installer Error## ";
     String currentDir = "/";
@@ -129,21 +133,36 @@ public class WatcherService extends Serv
 
         File dir = getFilesDir();
         File iniFile = new File(dir, "watcher.ini");
         String sIniFile = iniFile.getAbsolutePath();
         String sHold = "";
 
         this.sPingTarget = GetIniData("watcher", "PingTarget", sIniFile, "www.mozilla.org");
         sHold = GetIniData("watcher", "delay", sIniFile, "60000");
-           this.lDelay = Long.parseLong(sHold.trim());
+        this.lDelay = Long.parseLong(sHold.trim());
         sHold = GetIniData("watcher", "period", sIniFile,"300000");
-           this.lPeriod = Long.parseLong(sHold.trim());
+        this.lPeriod = Long.parseLong(sHold.trim());
         sHold = GetIniData("watcher", "strikes", sIniFile,"3");
-           this.nMaxStrikes = Integer.parseInt(sHold.trim());
+        this.nMaxStrikes = Integer.parseInt(sHold.trim());
+
+        sHold = GetIniData("watcher", "stayon", sIniFile,"0");
+        int nStayOn = Integer.parseInt(sHold.trim());
+        
+        try {
+            if (nStayOn != 0) {
+                if (!Settings.System.putInt(getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB)) {
+                    doToast("Screen couldn't be set to Always On [stay on while plugged in]");
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            String sExcept = e.getMessage();
+            doToast("Screen couldn't be set to Always On [exception " + sExcept + "]");
+        }
 
         doToast("WatcherService created");
         }
 
     public String GetIniData(String sSection, String sKey, String sFile, String sDefault)
         {
         String sRet = sDefault;
         String sComp = "";
new file mode 100644
--- /dev/null
+++ b/config/makefiles/target_export.mk
@@ -0,0 +1,68 @@
+# -*- makefile -*-
+# vim:set ts=8 sw=8 sts=8 noet:
+#
+# ***** 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):
+#  Chase Phillips <chase@mozilla.org>
+#  Benjamin Smedberg <benjamin@smedbergs.us>
+#  Jeff Walden <jwalden+code@mit.edu>
+#  Joey Armstrong <joey@mozilla.com>
+#
+# 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
+# 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 *****
+
+PARALLEL_DIRS_export = $(addsuffix _export,$(PARALLEL_DIRS))
+
+.PHONY: export $(PARALLEL_DIRS_export)
+
+###############
+## TIER targets
+###############
+export_tier_%:
+	@$(ECHO) "$@"
+	@$(MAKE_TIER_SUBMAKEFILES)
+	$(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,export,$(dir)))
+
+#################
+## Common targets
+#################
+ifdef PARALLEL_DIRS
+export:: $(PARALLEL_DIRS_export)
+
+$(PARALLEL_DIRS_export): %_export: %/Makefile
+	+@$(call SUBMAKE,export,$*)
+endif
+
+export:: $(SUBMAKEFILES) $(MAKE_DIRS) $(if $(XPIDLSRCS),$(IDL_DIR))
+	$(LOOP_OVER_DIRS)
+	$(LOOP_OVER_TOOL_DIRS)
new file mode 100644
--- /dev/null
+++ b/config/makefiles/target_libs.mk
@@ -0,0 +1,141 @@
+# -*- makefile -*-
+# vim:set ts=8 sw=8 sts=8 noet:
+#
+# ***** 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):
+#  Chase Phillips <chase@mozilla.org>
+#  Benjamin Smedberg <benjamin@smedbergs.us>
+#  Jeff Walden <jwalden+code@mit.edu>
+#  Joey Armstrong <joey@mozilla.com>
+#
+# 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
+# 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 *****
+
+PARALLEL_DIRS_libs = $(addsuffix _libs,$(PARALLEL_DIRS))
+
+.PHONY: libs $(PARALLEL_DIRS_libs)
+
+###############
+## TIER targets
+###############
+libs_tier_%:
+	@$(ECHO) "$@"
+	@$(MAKE_TIER_SUBMAKEFILES)
+	$(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,libs,$(dir)))
+
+#################
+## Common targets
+#################
+ifdef PARALLEL_DIRS
+libs:: $(PARALLEL_DIRS_libs)
+
+$(PARALLEL_DIRS_libs): %_libs: %/Makefile
+	+@$(call SUBMAKE,libs,$*)
+endif
+
+
+####################
+##
+####################
+ifdef EXPORT_LIBRARY
+ifeq ($(EXPORT_LIBRARY),1)
+ifdef IS_COMPONENT
+EXPORT_LIBRARY = $(DEPTH)/staticlib/components
+else
+EXPORT_LIBRARY = $(DEPTH)/staticlib
+endif
+else
+# If EXPORT_LIBRARY has a value, we'll be installing there. We also need to cleanup there
+GARBAGE += $(foreach lib,$(LIBRARY),$(EXPORT_LIBRARY)/$(lib))
+endif
+endif # EXPORT_LIBRARY
+
+libs:: $(SUBMAKEFILES) $(MAKE_DIRS) $(HOST_LIBRARY) $(LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY) $(HOST_PROGRAM) $(PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(SIMPLE_PROGRAMS) $(JAVA_LIBRARY)
+ifndef NO_DIST_INSTALL
+ifdef LIBRARY
+ifdef EXPORT_LIBRARY # Stage libs that will be linked into a static build
+	$(INSTALL) $(IFLAGS1) $(LIBRARY) $(EXPORT_LIBRARY)
+endif # EXPORT_LIBRARY
+ifdef DIST_INSTALL
+ifdef IS_COMPONENT
+	$(error Shipping static component libs makes no sense.)
+else
+	$(INSTALL) $(IFLAGS1) $(LIBRARY) $(DIST)/lib
+endif
+endif # DIST_INSTALL
+endif # LIBRARY
+ifdef SHARED_LIBRARY
+ifdef IS_COMPONENT
+	$(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)/components
+	$(ELF_DYNSTR_GC) $(FINAL_TARGET)/components/$(SHARED_LIBRARY)
+ifndef NO_COMPONENTS_MANIFEST
+	@$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/chrome.manifest "manifest components/components.manifest"
+	@$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/components/components.manifest "binary-component $(SHARED_LIBRARY)"
+endif
+else # ! IS_COMPONENT
+ifneq (,$(filter OS2 WINNT,$(OS_ARCH)))
+ifndef NO_INSTALL_IMPORT_LIBRARY
+	$(INSTALL) $(IFLAGS2) $(IMPORT_LIBRARY) $(DIST)/lib
+endif
+else
+	$(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(DIST)/lib
+endif
+	$(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)
+endif # IS_COMPONENT
+endif # SHARED_LIBRARY
+ifdef PROGRAM
+	$(INSTALL) $(IFLAGS2) $(PROGRAM) $(FINAL_TARGET)
+endif
+ifdef SIMPLE_PROGRAMS
+	$(INSTALL) $(IFLAGS2) $(SIMPLE_PROGRAMS) $(FINAL_TARGET)
+endif
+ifdef HOST_PROGRAM
+	$(INSTALL) $(IFLAGS2) $(HOST_PROGRAM) $(DIST)/host/bin
+endif
+ifdef HOST_SIMPLE_PROGRAMS
+	$(INSTALL) $(IFLAGS2) $(HOST_SIMPLE_PROGRAMS) $(DIST)/host/bin
+endif
+ifdef HOST_LIBRARY
+	$(INSTALL) $(IFLAGS1) $(HOST_LIBRARY) $(DIST)/host/lib
+endif
+ifdef JAVA_LIBRARY
+ifdef IS_COMPONENT
+	$(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET)/components
+else
+	$(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET)
+endif
+endif # JAVA_LIBRARY
+endif # !NO_DIST_INSTALL
+	$(LOOP_OVER_DIRS)
+
+# EOF
new file mode 100644
--- /dev/null
+++ b/config/makefiles/target_tools.mk
@@ -0,0 +1,72 @@
+# -*- makefile -*-
+# vim:set ts=8 sw=8 sts=8 noet:
+#
+# ***** 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):
+#  Chase Phillips <chase@mozilla.org>
+#  Benjamin Smedberg <benjamin@smedbergs.us>
+#  Jeff Walden <jwalden+code@mit.edu>
+#  Joey Armstrong <joey@mozilla.com>
+#
+# 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
+# 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 *****
+
+PARALLEL_DIRS_tools = $(addsuffix _tools,$(PARALLEL_DIRS))
+
+.PHONY: tools $(PARALLEL_DIRS_tools)
+
+###############
+## TIER targets
+###############
+tools_tier_%:
+	@$(ECHO) "$@"
+	@$(MAKE_TIER_SUBMAKEFILES)
+	$(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,tools,$(dir)))
+
+#################
+## Common targets
+#################
+ifdef PARALLEL_DIRS
+tools:: $(PARALLEL_DIRS_tools)
+
+$(PARALLEL_DIRS_tools): %_tools: %/Makefile
+	+@$(call SUBMAKE,tools,$*)
+endif
+
+tools:: $(SUBMAKEFILES) $(MAKE_DIRS)
+	$(LOOP_OVER_DIRS)
+ifneq (,$(strip $(TOOL_DIRS)))
+	$(foreach dir,$(TOOL_DIRS),$(call SUBMAKE,libs,$(dir)))
+endif
+
+# EOF
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1,8 +1,9 @@
+# -*- makefile -*-
 # vim:set ts=8 sw=8 sts=8 noet:
 #
 # ***** 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
@@ -19,16 +20,17 @@
 # Netscape Communications Corporation.
 # Portions created by the Initial Developer are Copyright (C) 1998
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #  Chase Phillips <chase@mozilla.org>
 #  Benjamin Smedberg <benjamin@smedbergs.us>
 #  Jeff Walden <jwalden+code@mit.edu>
+#  Joey Armstrong <joey@mozilla.com>
 #
 # 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
@@ -484,25 +486,16 @@ LOOP_OVER_STATIC_DIRS = \
   $(foreach dir,$(STATIC_DIRS),$(call SUBMAKE,$@,$(dir)))
 endif
 
 ifneq (,$(strip $(TOOL_DIRS)))
 LOOP_OVER_TOOL_DIRS = \
   $(foreach dir,$(TOOL_DIRS),$(call SUBMAKE,$@,$(dir)))
 endif
 
-ifdef PARALLEL_DIRS
-# create a bunch of fake targets for order-only processing
-PARALLEL_DIRS_export = $(addsuffix _export,$(PARALLEL_DIRS))
-PARALLEL_DIRS_libs = $(addsuffix _libs,$(PARALLEL_DIRS))
-PARALLEL_DIRS_tools = $(addsuffix _tools,$(PARALLEL_DIRS))
-
-.PHONY: $(PARALLEL_DIRS_export) $(PARALLEL_DIRS_libs) $(PARALLEL_DIRS_tools)
-endif
-
 #
 # Now we can differentiate between objects used to build a library, and
 # objects used to build an executable in the same directory.
 #
 ifndef PROGOBJS
 PROGOBJS		= $(OBJS)
 endif
 
@@ -731,31 +724,16 @@ ECHO := echo
 QUIET :=
 else
 ECHO := true
 QUIET := -q
 endif
 
 MAKE_TIER_SUBMAKEFILES = +$(if $(tier_$*_dirs),$(MAKE) $(addsuffix /Makefile,$(tier_$*_dirs)))
 
-export_tier_%:
-	@$(ECHO) "$@"
-	@$(MAKE_TIER_SUBMAKEFILES)
-	$(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,export,$(dir)))
-
-libs_tier_%:
-	@$(ECHO) "$@"
-	@$(MAKE_TIER_SUBMAKEFILES)
-	$(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,libs,$(dir)))
-
-tools_tier_%:
-	@$(ECHO) "$@"
-	@$(MAKE_TIER_SUBMAKEFILES)
-	$(foreach dir,$(tier_$*_dirs),$(call SUBMAKE,tools,$(dir)))
-
 $(foreach tier,$(TIERS),tier_$(tier))::
 	@$(ECHO) "$@: $($@_staticdirs) $($@_dirs)"
 	$(foreach dir,$($@_staticdirs),$(call SUBMAKE,,$(dir)))
 	$(MAKE) export_$@
 	$(MAKE) libs_$@
 	$(MAKE) tools_$@
 
 # Do everything from scratch
@@ -769,39 +747,18 @@ depend::
 # Target to only regenerate makefiles
 makefiles: $(SUBMAKEFILES)
 ifneq (,$(DIRS)$(TOOL_DIRS)$(PARALLEL_DIRS))
 	$(LOOP_OVER_PARALLEL_DIRS)
 	$(LOOP_OVER_DIRS)
 	$(LOOP_OVER_TOOL_DIRS)
 endif
 
-ifdef PARALLEL_DIRS
-export:: $(PARALLEL_DIRS_export)
-
-$(PARALLEL_DIRS_export): %_export: %/Makefile
-	+@$(call SUBMAKE,export,$*)
-endif
-
-export:: $(SUBMAKEFILES) $(MAKE_DIRS) $(if $(XPIDLSRCS),$(IDL_DIR))
-	$(LOOP_OVER_DIRS)
-	$(LOOP_OVER_TOOL_DIRS)
-
-ifdef PARALLEL_DIRS
-tools:: $(PARALLEL_DIRS_tools)
-
-$(PARALLEL_DIRS_tools): %_tools: %/Makefile
-	+@$(call SUBMAKE,tools,$*)
-endif
-
-tools:: $(SUBMAKEFILES) $(MAKE_DIRS)
-	$(LOOP_OVER_DIRS)
-ifneq (,$(strip $(TOOL_DIRS)))
-	$(foreach dir,$(TOOL_DIRS),$(call SUBMAKE,libs,$(dir)))
-endif
+include $(topsrcdir)/config/makefiles/target_export.mk
+include $(topsrcdir)/config/makefiles/target_tools.mk
 
 #
 # Rule to create list of libraries for final link
 #
 export::
 ifdef LIBRARY_NAME
 ifdef EXPORT_LIBRARY
 ifdef IS_COMPONENT
@@ -821,96 +778,19 @@ LIBS_DEPS = $(call DO_EXPAND_LIBS,$(filt
 SHARED_LIBRARY_LIBS_DEPS = $(call DO_EXPAND_LIBS,$(SHARED_LIBRARY_LIBS))
 HOST_LIBS_DEPS = $(filter %.$(LIB_SUFFIX),$(HOST_LIBS))
 DSO_LDOPTS_DEPS = $(call DO_EXPAND_LIBS,$(EXTRA_DSO_LIBS) $(filter %.$(LIB_SUFFIX), $(EXTRA_DSO_LDOPTS)))
 
 # Dependencies which, if modified, should cause everything to rebuild
 GLOBAL_DEPS += Makefile Makefile.in $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk
 
 ##############################################
-ifdef PARALLEL_DIRS
-libs:: $(PARALLEL_DIRS_libs)
-
-$(PARALLEL_DIRS_libs): %_libs: %/Makefile
-	+@$(call SUBMAKE,libs,$*)
-endif
-
-ifdef EXPORT_LIBRARY
-ifeq ($(EXPORT_LIBRARY),1)
-ifdef IS_COMPONENT
-EXPORT_LIBRARY = $(DEPTH)/staticlib/components
-else
-EXPORT_LIBRARY = $(DEPTH)/staticlib
-endif
-else
-# If EXPORT_LIBRARY has a value, we'll be installing there. We also need to cleanup there
-GARBAGE += $(foreach lib,$(LIBRARY),$(EXPORT_LIBRARY)/$(lib))
-endif
-endif # EXPORT_LIBRARY
-
-libs:: $(SUBMAKEFILES) $(MAKE_DIRS) $(HOST_LIBRARY) $(LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY) $(HOST_PROGRAM) $(PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(SIMPLE_PROGRAMS) $(JAVA_LIBRARY)
-ifndef NO_DIST_INSTALL
-ifdef LIBRARY
-ifdef EXPORT_LIBRARY # Stage libs that will be linked into a static build
-	$(INSTALL) $(IFLAGS1) $(LIBRARY) $(EXPORT_LIBRARY)
-endif # EXPORT_LIBRARY
-ifdef DIST_INSTALL
-ifdef IS_COMPONENT
-	$(error Shipping static component libs makes no sense.)
-else
-	$(INSTALL) $(IFLAGS1) $(LIBRARY) $(DIST)/lib
-endif
-endif # DIST_INSTALL
-endif # LIBRARY
-ifdef SHARED_LIBRARY
-ifdef IS_COMPONENT
-	$(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)/components
-	$(ELF_DYNSTR_GC) $(FINAL_TARGET)/components/$(SHARED_LIBRARY)
-ifndef NO_COMPONENTS_MANIFEST
-	@$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/chrome.manifest "manifest components/components.manifest"
-	@$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/components/components.manifest "binary-component $(SHARED_LIBRARY)"
-endif
-else # ! IS_COMPONENT
-ifneq (,$(filter OS2 WINNT,$(OS_ARCH)))
-ifndef NO_INSTALL_IMPORT_LIBRARY
-	$(INSTALL) $(IFLAGS2) $(IMPORT_LIBRARY) $(DIST)/lib
-endif
-else
-	$(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(DIST)/lib
-endif
-	$(INSTALL) $(IFLAGS2) $(SHARED_LIBRARY) $(FINAL_TARGET)
-endif # IS_COMPONENT
-endif # SHARED_LIBRARY
-ifdef PROGRAM
-	$(INSTALL) $(IFLAGS2) $(PROGRAM) $(FINAL_TARGET)
-endif
-ifdef SIMPLE_PROGRAMS
-	$(INSTALL) $(IFLAGS2) $(SIMPLE_PROGRAMS) $(FINAL_TARGET)
-endif
-ifdef HOST_PROGRAM
-	$(INSTALL) $(IFLAGS2) $(HOST_PROGRAM) $(DIST)/host/bin
-endif
-ifdef HOST_SIMPLE_PROGRAMS
-	$(INSTALL) $(IFLAGS2) $(HOST_SIMPLE_PROGRAMS) $(DIST)/host/bin
-endif
-ifdef HOST_LIBRARY
-	$(INSTALL) $(IFLAGS1) $(HOST_LIBRARY) $(DIST)/host/lib
-endif
-ifdef JAVA_LIBRARY
-ifdef IS_COMPONENT
-	$(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET)/components
-else
-	$(INSTALL) $(IFLAGS1) $(JAVA_LIBRARY) $(FINAL_TARGET)
-endif
-endif # JAVA_LIBRARY
-endif # !NO_DIST_INSTALL
-	$(LOOP_OVER_DIRS)
+include $(topsrcdir)/config/makefiles/target_libs.mk
 
 ##############################################
-
 ifndef NO_PROFILE_GUIDED_OPTIMIZE
 ifdef MOZ_PROFILE_USE
 ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_)
 # When building with PGO, we have to make sure to re-link
 # in the MOZ_PROFILE_USE phase if we linked in the
 # MOZ_PROFILE_GENERATE phase. We'll touch this pgo.relink
 # file in the link rule in the GENERATE phase to indicate
 # that we need a relink.
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -738,18 +738,18 @@ public:
    *   @param aParamsLength Length of aParams.
    *   @param aURI URI of resource containing error (may be null).
    *   @param aSourceLine The text of the line that contains the error (may be
               empty).
    *   @param aLineNumber Line number within resource containing error.
    *   @param aColumnNumber Column number within resource containing error.
    *   @param aErrorFlags See nsIScriptError.
    *   @param aCategory Name of module reporting error.
-   *   @param [aWindowId=0] (Optional) The window ID of the outer window the
-   *          message originates from.
+   *   @param [aInnerWindowId=0] (Optional) The window ID of the inner window
+   *          the message originates from.
    */
   enum PropertiesFile {
     eCSS_PROPERTIES,
     eXBL_PROPERTIES,
     eXUL_PROPERTIES,
     eLAYOUT_PROPERTIES,
     eFORMS_PROPERTIES,
     ePRINTING_PROPERTIES,
@@ -764,17 +764,17 @@ public:
                                   const PRUnichar **aParams,
                                   PRUint32 aParamsLength,
                                   nsIURI* aURI,
                                   const nsAFlatString& aSourceLine,
                                   PRUint32 aLineNumber,
                                   PRUint32 aColumnNumber,
                                   PRUint32 aErrorFlags,
                                   const char *aCategory,
-                                  PRUint64 aWindowId = 0);
+                                  PRUint64 aInnerWindowId = 0);
 
   /**
    * Report a localized error message to the error console.
    *   @param aFile Properties file containing localized message.
    *   @param aMessageName Name of localized message.
    *   @param aParams Parameters to be substituted into localized message.
    *   @param aParamsLength Length of aParams.
    *   @param aURI URI of resource containing error (may be null).
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -716,16 +716,25 @@ public:
    */
   PRUint64 OuterWindowID() const
   {
     nsPIDOMWindow *window = GetWindow();
     return window ? window->WindowID() : 0;
   }
 
   /**
+   * Return the inner window ID.
+   */
+  PRUint64 InnerWindowID()
+  {
+    nsPIDOMWindow *window = GetInnerWindow();
+    return window ? window->WindowID() : 0;
+  }
+
+  /**
    * Get the script loader for this document
    */ 
   virtual nsScriptLoader* ScriptLoader() = 0;
 
   /**
    * Add/Remove an element to the document's id and name hashes
    */
   virtual void AddToIdTable(Element* aElement, nsIAtom* aId) = 0;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2728,17 +2728,17 @@ nsContentUtils::ReportToConsole(Properti
                                 const PRUnichar **aParams,
                                 PRUint32 aParamsLength,
                                 nsIURI* aURI,
                                 const nsAFlatString& aSourceLine,
                                 PRUint32 aLineNumber,
                                 PRUint32 aColumnNumber,
                                 PRUint32 aErrorFlags,
                                 const char *aCategory,
-                                PRUint64 aWindowId)
+                                PRUint64 aInnerWindowId)
 {
   NS_ASSERTION((aParams && aParamsLength) || (!aParams && !aParamsLength),
                "Supply either both parameters and their number or no"
                "parameters and 0.");
 
   nsresult rv;
   if (!sConsoleService) { // only need to bother null-checking here
     rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
@@ -2762,17 +2762,18 @@ nsContentUtils::ReportToConsole(Properti
   nsCOMPtr<nsIScriptError2> errorObject =
       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = errorObject->InitWithWindowID(errorText.get(),
                                      NS_ConvertUTF8toUTF16(spec).get(), // file name
                                      aSourceLine.get(),
                                      aLineNumber, aColumnNumber,
-                                     aErrorFlags, aCategory, aWindowId);
+                                     aErrorFlags, aCategory,
+                                     aInnerWindowId);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIScriptError> logError = do_QueryInterface(errorObject);
   return sConsoleService->LogMessage(logError);
 }
 
 /* static */ nsresult
 nsContentUtils::ReportToConsole(PropertiesFile aFile,
@@ -2783,27 +2784,27 @@ nsContentUtils::ReportToConsole(Properti
                                 const nsAFlatString& aSourceLine,
                                 PRUint32 aLineNumber,
                                 PRUint32 aColumnNumber,
                                 PRUint32 aErrorFlags,
                                 const char *aCategory,
                                 nsIDocument* aDocument)
 {
   nsIURI* uri = aURI;
-  PRUint64 windowID = 0;
+  PRUint64 innerWindowID = 0;
   if (aDocument) {
     if (!uri) {
       uri = aDocument->GetDocumentURI();
     }
-    windowID = aDocument->OuterWindowID();
+    innerWindowID = aDocument->InnerWindowID();
   }
 
   return ReportToConsole(aFile, aMessageName, aParams, aParamsLength, uri,
                          aSourceLine, aLineNumber, aColumnNumber, aErrorFlags,
-                         aCategory, windowID);
+                         aCategory, innerWindowID);
 }
 
 PRBool
 nsContentUtils::IsChromeDoc(nsIDocument *aDocument)
 {
   if (!aDocument) {
     return PR_FALSE;
   }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2734,18 +2734,17 @@ nsDocument::GetActiveElement(nsIDOMEleme
     // be safe and make sure the element is from this document
     if (focusedContent && focusedContent->GetOwnerDoc() == this) {
       CallQueryInterface(focusedContent, aElement);
       return NS_OK;
     }
   }
 
   // No focused element anywhere in this document.  Try to get the BODY.
-  nsCOMPtr<nsIDOMHTMLDocument> htmlDoc =
-    do_QueryInterface(static_cast<nsIDocument*>(this));
+  nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryObject(this);
   if (htmlDoc) {
     nsCOMPtr<nsIDOMHTMLElement> bodyElement;
     htmlDoc->GetBody(getter_AddRefs(bodyElement));
     if (bodyElement) {
       *aElement = bodyElement;
       NS_ADDREF(*aElement);
     }
     // Because of IE compatibility, return null when html document doesn't have
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -80,17 +80,17 @@ using namespace mozilla;
 nsEventSource::nsEventSource() :
   mStatus(PARSE_STATE_OFF),
   mFrozen(PR_FALSE),
   mErrorLoadOnRedirect(PR_FALSE),
   mGoingToDispatchAllMessages(PR_FALSE),
   mLastConvertionResult(NS_OK),
   mReadyState(nsIEventSource::CONNECTING),
   mScriptLine(0),
-  mWindowID(0)
+  mInnerWindowID(0)
 {
 }
 
 nsEventSource::~nsEventSource()
 {
   Close();
 
   if (mListenerManager) {
@@ -248,17 +248,17 @@ nsEventSource::Init(nsIPrincipal* aPrinc
     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
   JSContext* cx = nsnull;
   if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
     const char *filename;
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mScriptLine)) {
       mScriptFile.AssignASCII(filename);
     }
 
-    mWindowID = nsJSUtils::GetCurrentlyRunningCodeWindowID(cx);
+    mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
   }
 
   // Get the load group for the page. When requesting we'll add ourselves to it.
   // This way any pending requests will be automatically aborted if the user
   // leaves the page.
   if (mScriptContext) {
     nsCOMPtr<nsIDocument> doc =
       nsContentUtils::GetDocumentFromScriptContext(mScriptContext);
@@ -1067,17 +1067,17 @@ nsEventSource::PrintErrorOnConsole(const
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   errObj->InitWithWindowID(message.get(),
                            mScriptFile.get(),
                            nsnull,
                            mScriptLine, 0,
                            nsIScriptError::errorFlag,
-                           "Event Source", mWindowID);
+                           "Event Source", mInnerWindowID);
 
   // print the error message directly to the JS console
   nsCOMPtr<nsIScriptError> logError = do_QueryInterface(errObj);
   rv = console->LogMessage(logError);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/content/base/src/nsEventSource.h
+++ b/content/base/src/nsEventSource.h
@@ -247,21 +247,21 @@ protected:
 
   PRUint32 mRedirectFlags;
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
 
   // Event Source owner information:
   // - the script file name
   // - source code line number where the Event Source object was constructed.
-  // - the window ID of the outer window where the script lives. Note that this
-  // may not be the same as the Event Source owner window.
+  // - the ID of the inner window where the script lives. Note that this may not
+  //   be the same as the Event Source owner window.
   // These attributes are used for error reporting.
   nsString mScriptFile;
   PRUint32 mScriptLine;
-  PRUint64 mWindowID;
+  PRUint64 mInnerWindowID;
 
 private:
   nsEventSource(const nsEventSource& x);   // prevent bad usage
   nsEventSource& operator=(const nsEventSource& x);
 };
 
 #endif // nsEventSource_h__
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -452,18 +452,17 @@ nsFrameMessageManager::ReceiveMessage(ns
         if (JS_ObjectIsCallable(ctx, object)) {
           // If the listener is a JS function:
           funval = OBJECT_TO_JSVAL(object);
 
           // A small hack to get 'this' value right on content side where
           // messageManager is wrapped in TabChildGlobal.
           nsCOMPtr<nsISupports> defaultThisValue;
           if (mChrome) {
-            defaultThisValue =
-              do_QueryInterface(static_cast<nsIContentFrameMessageManager*>(this));
+            defaultThisValue = do_QueryObject(this);
           } else {
             defaultThisValue = aTarget;
           }
           nsContentUtils::WrapNative(ctx,
                                      JS_GetGlobalForObject(ctx, object),
                                      defaultThisValue, &thisValue, nsnull, PR_TRUE);
         } else {
           // If the listener is a JS object which has receiveMessage function:
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -4869,18 +4869,17 @@ nsGenericElement::UnsetAttr(PRInt32 aNam
     nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                   nsIDOMMutationEvent::REMOVAL);
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nsnull, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (hasMutationListeners) {
-    nsCOMPtr<nsIDOMEventTarget> node =
-      do_QueryInterface(static_cast<nsIContent *>(this));
+    nsCOMPtr<nsIDOMEventTarget> node = do_QueryObject(this);
     nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED);
 
     mutation.mRelatedNode = attrNode;
     mutation.mAttrName = aName;
 
     nsAutoString value;
     oldValue.ToString(value);
     if (!value.IsEmpty())
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -352,17 +352,17 @@ nsWebSocketEstablishedConnection::PrintE
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   errorObject->InitWithWindowID
     (message.get(),
      NS_ConvertUTF8toUTF16(mOwner->GetScriptFile()).get(),
      nsnull,
      mOwner->GetScriptLine(), 0, nsIScriptError::errorFlag,
-     "Web Socket", mOwner->WindowID()
+     "Web Socket", mOwner->InnerWindowID()
      );
   
   // print the error message directly to the JS console
   nsCOMPtr<nsIScriptError> logError(do_QueryInterface(errorObject));
   rv = console->LogMessage(logError);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
@@ -626,17 +626,17 @@ nsWebSocketEstablishedConnection::GetInt
 nsWebSocket::nsWebSocket() : mKeepingAlive(PR_FALSE),
                              mCheckMustKeepAlive(PR_TRUE),
                              mTriggeredCloseEvent(PR_FALSE),
                              mClientReasonCode(0),
                              mServerReasonCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
                              mReadyState(nsIMozWebSocket::CONNECTING),
                              mOutgoingBufferedAmount(0),
                              mScriptLine(0),
-                             mWindowID(0)
+                             mInnerWindowID(0)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 }
 
 nsWebSocket::~nsWebSocket()
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (mConnection) {
@@ -1423,17 +1423,17 @@ nsWebSocket::Init(nsIPrincipal* aPrincip
       }
 
       jsbytecode *pc = JS_GetFramePC(cx, fp);
       if (script && pc) {
         mScriptLine = JS_PCToLineNumber(cx, script, pc);
       }
     }
 
-    mWindowID = nsJSUtils::GetCurrentlyRunningCodeWindowID(cx);
+    mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
   }
 
   // parses the url
   rv = ParseURL(PromiseFlatString(aURL));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Don't allow https:// to open ws://
   if (!mSecure && 
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -94,17 +94,17 @@ public:
                               PRUint8 optional_argc);
   NS_IMETHOD RemoveEventListener(const nsAString& aType,
                                  nsIDOMEventListener* aListener,
                                  PRBool aUseCapture);
 
   // Determine if preferences allow WebSocket
   static PRBool PrefEnabled();
 
-  const PRUint64 WindowID() const { return mWindowID; }
+  const PRUint64 InnerWindowID() const { return mInnerWindowID; }
   const nsCString& GetScriptFile() const { return mScriptFile; }
   const PRUint32 GetScriptLine() const { return mScriptLine; }
 
 protected:
   nsresult ParseURL(const nsString& aURL);
   nsresult EstablishConnection();
 
   nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
@@ -159,21 +159,21 @@ protected:
   nsRefPtr<nsWebSocketEstablishedConnection> mConnection;
   PRUint32 mOutgoingBufferedAmount; // actually, we get this value from
                                     // mConnection when we are connected,
                                     // but we need this one after disconnecting.
 
   // Web Socket owner information:
   // - the script file name, UTF8 encoded.
   // - source code line number where the Web Socket object was constructed.
-  // - the window ID of the outer window where the script lives. Note that this 
-  // may not be the same as the Web Socket owner window.
+  // - the ID of the inner window where the script lives. Note that this may not
+  //   be the same as the Web Socket owner window.
   // These attributes are used for error reporting.
   nsCString mScriptFile;
   PRUint32 mScriptLine;
-  PRUint64 mWindowID;
+  PRUint64 mInnerWindowID;
 
 private:
   nsWebSocket(const nsWebSocket& x);   // prevent bad usage
   nsWebSocket& operator=(const nsWebSocket& x);
 };
 
 #endif
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -3964,16 +3964,17 @@ WebGLContext::CompileShader(nsIWebGLShad
         resources.MaxVertexTextureImageUnits = mGLMaxVertexTextureImageUnits;
         resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits;
         resources.MaxTextureImageUnits = mGLMaxTextureImageUnits;
         resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
         resources.MaxDrawBuffers = 1;
 
         compiler = ShConstructCompiler((ShShaderType) shader->ShaderType(),
                                        SH_WEBGL_SPEC,
+                                       gl->IsGLES2() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT,
                                        &resources);
 
         nsPromiseFlatCString src(shader->Source());
         const char *s = src.get();
 
         if (!ShCompile(compiler, &s, 1, SH_OBJECT_CODE)) {
             int len = 0;
             ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &len);
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -334,17 +334,19 @@ public:
     nsCanvasRenderingContext2D();
     virtual ~nsCanvasRenderingContext2D();
 
     nsresult Redraw();
 
     // nsICanvasRenderingContextInternal
     NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas);
     NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
+    void Initialize(nsIDocShell *shell, PRInt32 width, PRInt32 height);
     NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, PRInt32 width, PRInt32 height);
+    PRBool EnsureSurface();
     NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream);
     NS_IMETHOD GetThebesSurface(gfxASurface **surface);
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot()
         { return nsnull; }
 
@@ -375,17 +377,17 @@ public:
         STYLE_SHADOW,
         STYLE_MAX
     };
 
     class PathAutoSaveRestore
     {
     public:
         PathAutoSaveRestore(nsCanvasRenderingContext2D* aCtx) :
-            mContext(aCtx->mThebes)
+          mContext(aCtx->mThebes)
         {
             if (aCtx->mHasPath) {
                 mPath = mContext->CopyPath();
             }
         }
         ~PathAutoSaveRestore()
         {
             mContext->NewPath();
@@ -455,22 +457,26 @@ protected:
     PRPackedBool mIPC;
 
     // the canvas element we're a context of
     nsCOMPtr<nsIDOMHTMLCanvasElement> mCanvasElement;
     nsHTMLCanvasElement *HTMLCanvasElement() {
         return static_cast<nsHTMLCanvasElement*>(mCanvasElement.get());
     }
 
+    // Initialize the Thebes rendering context
+    void CreateThebes();
+
     // If mCanvasElement is not provided, then a docshell is
     nsCOMPtr<nsIDocShell> mDocShell;
 
     // our drawing surfaces, contexts, and layers
     nsRefPtr<gfxContext> mThebes;
     nsRefPtr<gfxASurface> mSurface;
+    PRPackedBool mSurfaceCreated;
 
     PRUint32 mSaveCount;
 
     /**
      * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
      * Redraw is called, reset to false when Render is called.
      */
     PRPackedBool mIsEntireFrameInvalid;
@@ -522,30 +528,40 @@ protected:
     /**
      * Checks the current state to determine if an intermediate surface would
      * be necessary to complete a drawing operation. Does not check the
      * condition pertaining to global alpha and patterns since that does not
      * pertain to all drawing operations.
      */
     PRBool NeedToUseIntermediateSurface()
     {
+        if (!mThebes) {
+            // Haven't created a surface yet, default is OVER.
+            return OperatorAffectsUncoveredAreas(gfxContext::OPERATOR_OVER);
+        }
+     
         // certain operators always need an intermediate surface, except
         // with quartz since quartz does compositing differently than cairo
         return OperatorAffectsUncoveredAreas(mThebes->CurrentOperator());
 
         // XXX there are other unhandled cases but they should be investigated
         // first to ensure we aren't using an intermediate surface unecessarily
     }
 
     /**
      * If the current operator is "source" then clear the destination before we
      * draw into it, to simulate the effect of an unbounded source operator.
      */
     void ClearSurfaceForUnboundedSource()
     {
+        if (!mThebes) {
+            // Haven't created a surface yet, default is OVER.
+            return;
+        }
+
         gfxContext::GraphicsOperator current = mThebes->CurrentOperator();
         if (current != gfxContext::OPERATOR_SOURCE)
             return;
         mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
         // It doesn't really matter what the source is here, since Paint
         // isn't bounded by the source and the mask covers the entire clip
         // region.
         mThebes->Paint();
@@ -592,18 +608,17 @@ protected:
      * Draws a rectangle in the given style; used by FillRect and StrokeRect.
      */
     nsresult DrawRect(const gfxRect& rect, Style style);
 
     /**
      * Gets the pres shell from either the canvas element or the doc shell
      */
     nsIPresShell *GetPresShell() {
-      nsCOMPtr<nsIContent> content =
-        do_QueryInterface(static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement));
+      nsCOMPtr<nsIContent> content = do_QueryObject(mCanvasElement);
       if (content) {
         nsIDocument* ownerDoc = content->GetOwnerDoc();
         return ownerDoc ? ownerDoc->GetShell() : nsnull;
       }
       if (mDocShell) {
         nsCOMPtr<nsIPresShell> shell;
         mDocShell->GetPresShell(getter_AddRefs(shell));
         return shell.get();
@@ -960,16 +975,20 @@ nsCanvasRenderingContext2D::ApplyStyle(S
     if (mLastStyle == aWhichStyle &&
         !mDirtyStyle[aWhichStyle] &&
         aUseGlobalAlpha)
     {
         // nothing to do, this is already the set style
         return;
     }
 
+    if (!EnsureSurface()) {
+      return;
+    }
+
     // if not using global alpha, don't optimize with dirty bit
     if (aUseGlobalAlpha)
         mDirtyStyle[aWhichStyle] = PR_FALSE;
     mLastStyle = aWhichStyle;
 
     nsCanvasPattern* pattern = CurrentState().patternStyles[aWhichStyle];
     if (pattern) {
         if (mCanvasElement)
@@ -1054,135 +1073,169 @@ nsCanvasRenderingContext2D::RedrawUser(c
     }
 
     return Redraw(mThebes->UserToDevice(r));
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
 {
-    nsRefPtr<gfxASurface> surface;
-
-    // Check that the dimensions are sane
-    gfxIntSize size(width, height);
-    if (gfxASurface::CheckSurfaceSize(size, 0xffff)) {
-        // Zero sized surfaces have problems, so just use a 1 by 1.
-        if (height == 0 || width == 0) {
-            mZero = PR_TRUE;
-            height = 1;
-            width = 1;
-        } else {
-            mZero = PR_FALSE;
-        }
-
-        gfxASurface::gfxImageFormat format = GetImageFormat();
-
-        if (!PR_GetEnv("MOZ_CANVAS_IMAGE_SURFACE")) {
-            nsCOMPtr<nsIContent> content =
-                do_QueryInterface(static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement));
-            nsIDocument* ownerDoc = nsnull;
-            if (content)
-                ownerDoc = content->GetOwnerDoc();
-            nsRefPtr<LayerManager> layerManager = nsnull;
-
-            if (ownerDoc)
-              layerManager =
-                nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
-
-            if (layerManager) {
-              surface = layerManager->CreateOptimalSurface(gfxIntSize(width, height), format);
-            } else {
-              surface = gfxPlatform::GetPlatform()->
-                CreateOffscreenSurface(gfxIntSize(width, height), gfxASurface::ContentFromFormat(format));
-            }
-        }
-
-        if (!surface || surface->CairoStatus()) {
-            // If we couldn't create a surface of the type we want, fall back
-            // to an image surface. This lets us handle surface sizes that
-            // the underlying cairo backend might not handle.
-            surface = new gfxImageSurface(gfxIntSize(width, height), format);
-            if (!surface || surface->CairoStatus()) {
-                surface = nsnull;
-            }
-        }
-    }
-    if (surface) {
-        if (gCanvasMemoryReporter == nsnull) {
-            gCanvasMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasMemory);
-            NS_RegisterMemoryReporter(gCanvasMemoryReporter);
-        }
-
-        gCanvasMemoryUsed += width * height * 4;
-        JSContext* context = nsContentUtils::GetCurrentJSContext();
-        if (context) {
-            JS_updateMallocCounter(context, width * height * 4);
-        }
-    }
-
-    return InitializeWithSurface(NULL, surface, width, height);
+    Initialize(NULL, width, height);
+    return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, PRInt32 width, PRInt32 height) {
+void
+nsCanvasRenderingContext2D::Initialize(nsIDocShell *docShell, PRInt32 width, PRInt32 height) 
+{
     Reset();
 
     NS_ASSERTION(!docShell ^ !mCanvasElement, "Cannot set both docshell and canvas element");
     mDocShell = docShell;
 
     mWidth = width;
     mHeight = height;
 
-    mSurface = surface;
-    mThebes = surface ? new gfxContext(mSurface) : nsnull;
     mResetLayer = PR_TRUE;
-
-    /* Create dummy surfaces here */
-    if (mSurface == nsnull || mSurface->CairoStatus() != 0 ||
-        mThebes == nsnull || mThebes->HasError())
-    {
-        mSurface = new gfxImageSurface(gfxIntSize(1,1), gfxASurface::ImageFormatARGB32);
-        mThebes = new gfxContext(mSurface);
-    } else {
-        mValid = PR_TRUE;
-    }
+    mValid = PR_TRUE;
+    mSurfaceCreated = PR_FALSE;
 
     // set up the initial canvas defaults
     mStyleStack.Clear();
     mSaveCount = 0;
 
     ContextState *state = mStyleStack.AppendElement();
     state->globalAlpha = 1.0;
 
     state->colorStyles[STYLE_FILL] = NS_RGB(0,0,0);
     state->colorStyles[STYLE_STROKE] = NS_RGB(0,0,0);
     state->colorStyles[STYLE_SHADOW] = NS_RGBA(0,0,0,0);
     DirtyAllStyles();
 
+    // always force a redraw, because if the surface dimensions were reset
+    // then the surface became cleared, and we need to redraw everything.
+    Redraw();
+
+    return;
+}
+
+void
+nsCanvasRenderingContext2D::CreateThebes()
+{
+    mThebes = new gfxContext(mSurface);
+    mSurfaceCreated = PR_TRUE;
+    
     mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
     mThebes->NewPath();
     mThebes->Rectangle(gfxRect(0, 0, mWidth, mHeight));
     mThebes->Fill();
 
     mThebes->SetLineWidth(1.0);
     mThebes->SetOperator(gfxContext::OPERATOR_OVER);
     mThebes->SetMiterLimit(10.0);
     mThebes->SetLineCap(gfxContext::LINE_CAP_BUTT);
     mThebes->SetLineJoin(gfxContext::LINE_JOIN_MITER);
-    mThebes->SetFillRule(gfxContext::FILL_RULE_WINDING);
 
     mThebes->NewPath();
-
-    // always force a redraw, because if the surface dimensions were reset
-    // then the surface became cleared, and we need to redraw everything.
-    Redraw();
-
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *docShell, 
+                                                  gfxASurface *surface, 
+                                                  PRInt32 width, 
+                                                  PRInt32 height)
+{
+    Initialize(docShell, width, height);
+    
+    mSurface = surface;
+    CreateThebes();
     return mValid ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
+PRBool
+nsCanvasRenderingContext2D::EnsureSurface()
+{
+    if (!mValid) {
+        return PR_FALSE;
+    }
+
+    if (mSurface && mThebes && mSurfaceCreated) {
+        if (mSurface->CairoStatus()) {
+            return PR_FALSE;
+        }
+        return PR_TRUE;
+    }
+    
+    nsRefPtr<gfxASurface> surface;
+
+    // Check that the dimensions are sane
+    if (gfxASurface::CheckSurfaceSize(gfxIntSize(mWidth, mHeight), 0xffff)) {
+        // Zero sized surfaces have problems, so just use a 1 by 1.
+        if (mHeight == 0 || mWidth == 0) {
+            mZero = PR_TRUE;
+            mHeight = 1;
+            mWidth = 1;
+        } else {
+            mZero = PR_FALSE;
+        }
+
+        gfxASurface::gfxImageFormat format = GetImageFormat();
+
+        if (!PR_GetEnv("MOZ_CANVAS_IMAGE_SURFACE")) {
+            nsCOMPtr<nsIContent> content = do_QueryObject(mCanvasElement);
+            nsIDocument* ownerDoc = nsnull;
+            if (content)
+                ownerDoc = content->GetOwnerDoc();
+            nsRefPtr<LayerManager> layerManager = nsnull;
+
+            if (ownerDoc)
+              layerManager =
+                nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
+
+            if (layerManager) {
+              surface = layerManager->CreateOptimalSurface(gfxIntSize(mWidth, mHeight), format);
+            } else {
+              surface = gfxPlatform::GetPlatform()->
+                CreateOffscreenSurface(gfxIntSize(mWidth, mHeight), gfxASurface::ContentFromFormat(format));
+            }
+        }
+
+        if (!surface || surface->CairoStatus()) {
+            // If we couldn't create a surface of the type we want, fall back
+            // to an image surface. This lets us handle surface sizes that
+            // the underlying cairo backend might not handle.
+            surface = new gfxImageSurface(gfxIntSize(mWidth, mHeight), format);
+            if (!surface || surface->CairoStatus()) {
+                surface = nsnull;
+            }
+        }
+    }
+    if (surface) {
+        if (gCanvasMemoryReporter == nsnull) {
+            gCanvasMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasMemory);
+            NS_RegisterMemoryReporter(gCanvasMemoryReporter);
+        }
+
+        gCanvasMemoryUsed += mWidth * mHeight * 4;
+        JSContext* context = nsContentUtils::GetCurrentJSContext();
+        if (context) {
+            JS_updateMallocCounter(context, mWidth * mHeight * 4);
+        }
+    } else {
+        return PR_FALSE;
+    }
+
+    mSurface = surface;
+    CreateThebes();
+
+    if (mSurface->CairoStatus()) {
+        return PR_FALSE;
+    }
+    return PR_TRUE;
+}
+
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetIsOpaque(PRBool isOpaque)
 {
     if (isOpaque == mOpaque)
         return NS_OK;
 
     mOpaque = isOpaque;
 
@@ -1214,19 +1267,17 @@ nsCanvasRenderingContext2D::SetIsIPC(PRB
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter)
 {
     nsresult rv = NS_OK;
 
-    if (!mValid || !mSurface ||
-        mSurface->CairoStatus() ||
-        mThebes->HasError())
+    if (!EnsureSurface())
         return NS_ERROR_FAILURE;
 
     nsRefPtr<gfxPattern> pat = new gfxPattern(mSurface);
 
     pat->SetFilter(aFilter);
     pat->SetExtend(gfxPattern::EXTEND_PAD);
 
     gfxContext::GraphicsOperator op = ctx->CurrentOperator();
@@ -1245,19 +1296,17 @@ nsCanvasRenderingContext2D::Render(gfxCo
     return rv;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetInputStream(const char *aMimeType,
                                            const PRUnichar *aEncoderOptions,
                                            nsIInputStream **aStream)
 {
-    if (!mValid || !mSurface ||
-        mSurface->CairoStatus() ||
-        mThebes->HasError())
+    if (!EnsureSurface())
         return NS_ERROR_FAILURE;
 
     nsresult rv;
     const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
     nsAutoArrayPtr<char> conid(new (std::nothrow) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
 
     if (!conid)
         return NS_ERROR_OUT_OF_MEMORY;
@@ -1332,26 +1381,32 @@ nsCanvasRenderingContext2D::GetCanvas(ns
 
 //
 // state
 //
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Save()
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     ContextState state = CurrentState();
     mStyleStack.AppendElement(state);
     mThebes->Save();
     mSaveCount++;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Restore()
 {
+    if (!EnsureSurface()) 
+        return NS_ERROR_FAILURE;
+
     if (mSaveCount == 0)
         return NS_OK;
 
     mStyleStack.RemoveElementAt(mSaveCount);
     mThebes->Restore();
 
     mLastStyle = STYLE_MAX;
     DirtyAllStyles();
@@ -1362,96 +1417,120 @@ nsCanvasRenderingContext2D::Restore()
 
 //
 // transformations
 //
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Scale(float x, float y)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(x,y))
         return NS_OK;
 
     mThebes->Scale(x, y);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Rotate(float angle)
 {
+    if (!EnsureSurface()) 
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(angle))
         return NS_OK;
 
     mThebes->Rotate(angle);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Translate(float x, float y)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(x,y))
         return NS_OK;
 
     mThebes->Translate(gfxPoint(x, y));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Transform(float m11, float m12, float m21, float m22, float dx, float dy)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(m11,m12,m21,m22,dx,dy))
         return NS_OK;
 
     gfxMatrix matrix(m11, m12, m21, m22, dx, dy);
     mThebes->Multiply(matrix);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetTransform(float m11, float m12, float m21, float m22, float dx, float dy)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(m11,m12,m21,m22,dx,dy))
         return NS_OK;
 
     gfxMatrix matrix(m11, m12, m21, m22, dx, dy);
     mThebes->SetMatrix(matrix);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetMozCurrentTransform(JSContext* cx,
                                                    const jsval& matrix)
 {
     nsresult rv;
     gfxMatrix newCTM;
+    
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
 
     if (!JSValToMatrix(cx, matrix, &newCTM, &rv)) {
         return rv;
     }
 
     mThebes->SetMatrix(newCTM);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetMozCurrentTransform(JSContext* cx,
                                                    jsval* matrix)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     return MatrixToJSVal(mThebes->CurrentMatrix(), cx, matrix);
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* cx,
                                                           const jsval& matrix)
 {
     nsresult rv;
     gfxMatrix newCTMInverse;
+    
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
 
     if (!JSValToMatrix(cx, matrix, &newCTMInverse, &rv)) {
         return rv;
     }
 
     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     if (!newCTMInverse.IsSingular()) {
         mThebes->SetMatrix(newCTMInverse.Invert());
@@ -1643,32 +1722,38 @@ nsCanvasRenderingContext2D::GetFillStyle
 {
     return GetStyleAsStringOrInterface(aStr, aInterface, aType, STYLE_FILL);
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetMozFillRule(const nsAString& aString)
 {
     gfxContext::FillRule rule;
+    
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
 
     if (aString.EqualsLiteral("evenodd"))
         rule = gfxContext::FILL_RULE_EVEN_ODD;
     else if (aString.EqualsLiteral("nonzero"))
         rule = gfxContext::FILL_RULE_WINDING;
     else
         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
         return NS_OK;
 
     mThebes->SetFillRule(rule);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetMozFillRule(nsAString& aString)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     switch (mThebes->CurrentFillRule()) {
     case gfxContext::FILL_RULE_WINDING:
         aString.AssignLiteral("nonzero"); break;
     case gfxContext::FILL_RULE_EVEN_ODD:
         aString.AssignLiteral("evenodd"); break;
     default:
         return NS_ERROR_FAILURE;
     }
@@ -1883,30 +1968,36 @@ nsCanvasRenderingContext2D::ShadowInitia
         return nsnull;
 
     return ctx;
 }
 
 void
 nsCanvasRenderingContext2D::ShadowFinalize(gfxAlphaBoxBlur& blur)
 {
+    if (!EnsureSurface())
+        return;
+
     ApplyStyle(STYLE_SHADOW);
     // canvas matrix was already applied, don't apply it twice, but do
     // apply the shadow offset
     gfxMatrix matrix = mThebes->CurrentMatrix();
     mThebes->IdentityMatrix();
     mThebes->Translate(CurrentState().shadowOffset);
 
     blur.Paint(mThebes);
     mThebes->SetMatrix(matrix);
 }
 
 nsresult
 nsCanvasRenderingContext2D::DrawPath(Style style, gfxRect *dirtyRect)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     PRBool doUseIntermediateSurface = PR_FALSE;
     
     if (mSurface->GetType() == gfxASurface::SurfaceTypeD2D) {
       if (style != STYLE_FILL) {
         // D2D does all operators correctly even if transparent areas of SOURCE
         // affect dest. We need to use an intermediate surface for STROKE because
         // we can't clip to the actual stroke shape easily, but prefer a geometric
         // clip over an intermediate surface for a FILL.
@@ -2024,16 +2115,19 @@ nsCanvasRenderingContext2D::DrawPath(Sty
 
 //
 // rects
 //
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::ClearRect(float x, float y, float w, float h)
 {
+    if (!mSurfaceCreated)
+        return NS_OK;
+
     if (!FloatValidate(x,y,w,h))
         return NS_OK;
 
     PathAutoSaveRestore pathSR(this);
     gfxContextAutoSaveRestore autoSR(mThebes);
 
     mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
     mThebes->NewPath();
@@ -2041,16 +2135,19 @@ nsCanvasRenderingContext2D::ClearRect(fl
     mThebes->Fill();
 
     return RedrawUser(mThebes->GetUserPathExtent());
 }
 
 nsresult
 nsCanvasRenderingContext2D::DrawRect(const gfxRect& rect, Style style)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(rect.X(), rect.Y(), rect.Width(), rect.Height()))
         return NS_OK;
 
     PathAutoSaveRestore pathSR(this);
 
     mThebes->NewPath();
     mThebes->Rectangle(rect);
 
@@ -2079,24 +2176,30 @@ nsCanvasRenderingContext2D::StrokeRect(f
 
 //
 // path bits
 //
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::BeginPath()
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     mHasPath = PR_FALSE;
     mThebes->NewPath();
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::ClosePath()
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     mThebes->ClosePath();
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Fill()
 {
     gfxRect dirty;
@@ -2114,45 +2217,57 @@ nsCanvasRenderingContext2D::Stroke()
     if (NS_FAILED(rv))
         return rv;
     return RedrawUser(dirty);
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Clip()
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     mThebes->Clip();
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::MoveTo(float x, float y)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(x,y))
         return NS_OK;
 
     mHasPath = PR_TRUE;
     mThebes->MoveTo(gfxPoint(x, y));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::LineTo(float x, float y)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(x,y))
         return NS_OK;
 
     mHasPath = PR_TRUE;
     mThebes->LineTo(gfxPoint(x, y));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::QuadraticCurveTo(float cpx, float cpy, float x, float y)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(cpx,cpy,x,y))
         return NS_OK;
 
     // we will always have a current point, since beginPath forces
     // a moveto(0,0)
     gfxPoint c = mThebes->CurrentPoint();
     gfxPoint p(x,y);
     gfxPoint cp(cpx, cpy);
@@ -2163,30 +2278,36 @@ nsCanvasRenderingContext2D::QuadraticCur
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::BezierCurveTo(float cp1x, float cp1y,
                                           float cp2x, float cp2y,
                                           float x, float y)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(cp1x,cp1y,cp2x,cp2y,x,y))
         return NS_OK;
 
     mHasPath = PR_TRUE;
     mThebes->CurveTo(gfxPoint(cp1x, cp1y),
                      gfxPoint(cp2x, cp2y),
                      gfxPoint(x, y));
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::ArcTo(float x1, float y1, float x2, float y2, float radius)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(x1,y1,x2,y2,radius))
         return NS_OK;
 
     if (radius < 0)
         return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
     mHasPath = PR_TRUE;
 
@@ -2236,16 +2357,19 @@ nsCanvasRenderingContext2D::ArcTo(float 
         mThebes->Arc(gfxPoint(cx, cy), radius, angle0, angle1);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Arc(float x, float y, float r, float startAngle, float endAngle, PRBool ccw)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(x,y,r,startAngle,endAngle))
         return NS_OK;
 
     if (r < 0.0)
         return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
     gfxPoint p(x,y);
 
@@ -2255,16 +2379,19 @@ nsCanvasRenderingContext2D::Arc(float x,
     else
         mThebes->Arc(p, r, startAngle, endAngle);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Rect(float x, float y, float w, float h)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(x,y,w,h))
         return NS_OK;
 
     mHasPath = PR_TRUE;
     mThebes->Rectangle(gfxRect(x, y, w, h));
     return NS_OK;
 }
 
@@ -2767,17 +2894,23 @@ nsCanvasRenderingContext2D::DrawOrMeasur
 
     // Clear the surface if we need to simulate unbounded SOURCE operator
     ClearSurfaceForUnboundedSource();
 
     nsCanvasBidiProcessor processor;
 
     GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, NULL);
     processor.mPt = gfxPoint(aX, aY);
-    processor.mThebes = mThebes;
+    nsRefPtr<nsRenderingContext> ctx;
+    if (mThebes) {
+        processor.mThebes = mThebes;
+    } else {
+        ctx = presShell->GetReferenceRenderingContext();
+        processor.mThebes = ctx->ThebesContext(); 
+    }
     processor.mOp = aOp;
     processor.mBoundingBox = gfxRect(0, 0, 0, 0);
     processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
 
     processor.mFontgrp = GetCurrentFontStyle();
     NS_ASSERTION(processor.mFontgrp, "font group is null");
 
     nscoord totalWidthCoord;
@@ -2801,16 +2934,21 @@ nsCanvasRenderingContext2D::DrawOrMeasur
     float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
     if (aWidth)
         *aWidth = totalWidth;
 
     // if only measuring, don't need to do any more work
     if (aOp==TEXT_DRAW_OPERATION_MEASURE)
         return NS_OK;
 
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
+    processor.mThebes = mThebes;
+
     // offset pt.x based on text align
     gfxFloat anchorX;
 
     if (CurrentState().textAlign == TEXT_ALIGN_CENTER)
         anchorX = .5;
     else if (CurrentState().textAlign == TEXT_ALIGN_LEFT ||
              (!isRTL && CurrentState().textAlign == TEXT_ALIGN_START) ||
              (isRTL && CurrentState().textAlign == TEXT_ALIGN_END))
@@ -3021,34 +3159,43 @@ nsCanvasRenderingContext2D::MakeTextRun(
 
 
 //
 // line caps/joins
 //
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetLineWidth(float width)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(width) || width <= 0.0)
         return NS_OK;
 
     mThebes->SetLineWidth(width);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetLineWidth(float *width)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+ 
     gfxFloat d = mThebes->CurrentLineWidth();
     *width = static_cast<float>(d);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetLineCap(const nsAString& capstyle)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     gfxContext::GraphicsLineCap cap;
 
     if (capstyle.EqualsLiteral("butt"))
         cap = gfxContext::LINE_CAP_BUTT;
     else if (capstyle.EqualsLiteral("round"))
         cap = gfxContext::LINE_CAP_ROUND;
     else if (capstyle.EqualsLiteral("square"))
         cap = gfxContext::LINE_CAP_SQUARE;
@@ -3058,16 +3205,19 @@ nsCanvasRenderingContext2D::SetLineCap(c
 
     mThebes->SetLineCap(cap);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetLineCap(nsAString& capstyle)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     gfxContext::GraphicsLineCap cap = mThebes->CurrentLineCap();
 
     if (cap == gfxContext::LINE_CAP_BUTT)
         capstyle.AssignLiteral("butt");
     else if (cap == gfxContext::LINE_CAP_ROUND)
         capstyle.AssignLiteral("round");
     else if (cap == gfxContext::LINE_CAP_SQUARE)
         capstyle.AssignLiteral("square");
@@ -3075,16 +3225,19 @@ nsCanvasRenderingContext2D::GetLineCap(n
         return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetLineJoin(const nsAString& joinstyle)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     gfxContext::GraphicsLineJoin j;
 
     if (joinstyle.EqualsLiteral("round"))
         j = gfxContext::LINE_JOIN_ROUND;
     else if (joinstyle.EqualsLiteral("bevel"))
         j = gfxContext::LINE_JOIN_BEVEL;
     else if (joinstyle.EqualsLiteral("miter"))
         j = gfxContext::LINE_JOIN_MITER;
@@ -3094,16 +3247,19 @@ nsCanvasRenderingContext2D::SetLineJoin(
 
     mThebes->SetLineJoin(j);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetLineJoin(nsAString& joinstyle)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     gfxContext::GraphicsLineJoin j = mThebes->CurrentLineJoin();
 
     if (j == gfxContext::LINE_JOIN_ROUND)
         joinstyle.AssignLiteral("round");
     else if (j == gfxContext::LINE_JOIN_BEVEL)
         joinstyle.AssignLiteral("bevel");
     else if (j == gfxContext::LINE_JOIN_MITER)
         joinstyle.AssignLiteral("miter");
@@ -3111,56 +3267,71 @@ nsCanvasRenderingContext2D::GetLineJoin(
         return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetMiterLimit(float miter)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(miter) || miter <= 0.0)
         return NS_OK;
 
     mThebes->SetMiterLimit(miter);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetMiterLimit(float *miter)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     gfxFloat d = mThebes->CurrentMiterLimit();
     *miter = static_cast<float>(d);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetMozDash(JSContext *cx, const jsval& patternArray)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     AutoFallibleTArray<gfxFloat, 10> dashes;
     nsresult rv = JSValToDashArray(cx, patternArray, dashes);
     if (NS_SUCCEEDED(rv)) {
         mThebes->SetDash(dashes.Elements(), dashes.Length(),
                          mThebes->CurrentDashOffset());
     }
     return rv;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetMozDash(JSContext* cx, jsval* dashArray)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     AutoFallibleTArray<gfxFloat, 10> dashes;
     if (!mThebes->CurrentDash(dashes, nsnull)) {
         dashes.SetLength(0);
     }
     return DashArrayToJSVal(dashes, cx, dashArray);
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetMozDashOffset(float offset)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(offset)) {
         return NS_ERROR_ILLEGAL_VALUE;
     }
 
     AutoFallibleTArray<gfxFloat, 10> dashes;
     if (!mThebes->CurrentDash(dashes, nsnull)) {
         // Either no dash is set or the cairo call failed.  Either
         // way, eat the error.
@@ -3175,23 +3346,29 @@ nsCanvasRenderingContext2D::SetMozDashOf
                      gfxFloat(offset));
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetMozDashOffset(float* offset)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     *offset = float(mThebes->CurrentDashOffset());
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::IsPointInPath(float x, float y, PRBool *retVal)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!FloatValidate(x,y)) {
         *retVal = PR_FALSE;
         return NS_OK;
     }
 
     gfxPoint pt(x, y);
     *retVal = mThebes->PointInFill(mThebes->DeviceToUser(pt));
     return NS_OK;
@@ -3209,16 +3386,19 @@ nsCanvasRenderingContext2D::IsPointInPat
 //   -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1,
                                       float a2, float a3, float a4, float a5,
                                       float a6, float a7, float a8,
                                       PRUint8 optional_argc)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     if (!imgElt) {
         return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
     }
 
     nsCOMPtr<nsIContent> content = do_QueryInterface(imgElt);
     nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(content);
     if (canvas) {
         nsIntSize size = canvas->GetSize();
@@ -3393,16 +3573,19 @@ nsCanvasRenderingContext2D::DrawImage(ns
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString& op)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     gfxContext::GraphicsOperator thebes_op;
 
 #define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \
     if (op.EqualsLiteral(cvsop))   \
         thebes_op = gfxContext::OPERATOR_##thebesop;
 
     CANVAS_OP_TO_THEBES_OP("copy", SOURCE)
     else CANVAS_OP_TO_THEBES_OP("destination-atop", DEST_ATOP)
@@ -3422,16 +3605,19 @@ nsCanvasRenderingContext2D::SetGlobalCom
 
     mThebes->SetOperator(thebes_op);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     gfxContext::GraphicsOperator thebes_op = mThebes->CurrentOperator();
 
 #define CANVAS_OP_TO_THEBES_OP(cvsop,thebesop) \
     if (thebes_op == gfxContext::OPERATOR_##thebesop) \
         op.AssignLiteral(cvsop);
 
     CANVAS_OP_TO_THEBES_OP("copy", SOURCE)
     else CANVAS_OP_TO_THEBES_OP("destination-atop", DEST_ATOP)
@@ -3452,16 +3638,19 @@ nsCanvasRenderingContext2D::GetGlobalCom
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY,
                                        float aW, float aH,
                                        const nsAString& aBGColor,
                                        PRUint32 flags)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     NS_ENSURE_ARG(aWindow != nsnull);
 
     // protect against too-large surfaces that will cause allocation
     // or overflow issues
     if (!gfxASurface::CheckSurfaceSize(gfxIntSize(PRInt32(aW), PRInt32(aH)),
                                        0xffff))
         return NS_ERROR_FAILURE;
 
@@ -3542,16 +3731,19 @@ nsCanvasRenderingContext2D::DrawWindow(n
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::AsyncDrawXULElement(nsIDOMXULElement* aElem, float aX, float aY,
                                                 float aW, float aH,
                                                 const nsAString& aBGColor,
                                                 PRUint32 flags)
 {
+    if (!EnsureSurface())
+        return NS_ERROR_FAILURE;
+
     NS_ENSURE_ARG(aElem != nsnull);
 
     // We can't allow web apps to call this until we fix at least the
     // following potential security issues:
     // -- rendering cross-domain IFRAMEs and then extracting the results
     // -- rendering the user's theme and then extracting the results
     // -- rendering native anonymous content (e.g., file input paths;
     // scrollbars should be allowed)
@@ -3655,17 +3847,17 @@ nsCanvasRenderingContext2D::GetImageData
     /* Should never be called -- GetImageData_explicit is the QS entry point */
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
                                                   PRUint8 *aData, PRUint32 aDataLen)
 {
-    if (!mValid)
+    if (!EnsureSurface())
         return NS_ERROR_FAILURE;
 
     if (!mCanvasElement && !mDocShell) {
         NS_ERROR("No canvas element and no docshell in GetImageData!!!");
         return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     // Check only if we have a canvas element; if we were created with a docshell,
@@ -3787,17 +3979,17 @@ nsCanvasRenderingContext2D::PutImageData
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
                                                   unsigned char *aData, PRUint32 aDataLen,
                                                   PRBool hasDirtyRect, PRInt32 dirtyX, PRInt32 dirtyY,
                                                   PRInt32 dirtyWidth, PRInt32 dirtyHeight)
 {
-    if (!mValid)
+    if (!EnsureSurface())
         return NS_ERROR_FAILURE;
 
     if (w == 0 || h == 0)
         return NS_ERROR_DOM_SYNTAX_ERR;
 
     gfxRect dirtyRect;
     gfxRect imageDataRect(0, 0, w, h);
 
@@ -3893,17 +4085,17 @@ nsCanvasRenderingContext2D::PutImageData
     mThebes->Fill();
 
     return Redraw(dirtyRect);
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
 {
-    if (!mSurface) {
+    if (!EnsureSurface()) {
         *surface = nsnull;
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     *surface = mSurface.get();
     NS_ADDREF(*surface);
 
     return NS_OK;
@@ -3949,17 +4141,17 @@ private:
   nsRefPtr<nsHTMLCanvasElement> mContent;
 };
 
 already_AddRefed<CanvasLayer>
 nsCanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                            CanvasLayer *aOldLayer,
                                            LayerManager *aManager)
 {
-    if (!mValid)
+    if (!EnsureSurface()) 
         return nsnull;
 
     if (!mResetLayer && aOldLayer &&
         aOldLayer->HasUserData(&g2DContextLayerUserData)) {
         NS_ADDREF(aOldLayer);
         return aOldLayer;
     }
 
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -600,18 +600,17 @@ protected:
 
     return CurrentState().op;
   }
 
   /**
     * Gets the pres shell from either the canvas element or the doc shell
     */
   nsIPresShell *GetPresShell() {
-    nsCOMPtr<nsIContent> content =
-      do_QueryInterface(static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement));
+    nsCOMPtr<nsIContent> content = do_QueryObject(mCanvasElement);
     if (content) {
       nsIDocument* ownerDoc = content->GetOwnerDoc();
       return ownerDoc ? ownerDoc->GetShell() : nsnull;
     }
     if (mDocShell) {
       nsCOMPtr<nsIPresShell> shell;
       mDocShell->GetPresShell(getter_AddRefs(shell));
       return shell.get();
@@ -1230,18 +1229,17 @@ nsCanvasRenderingContext2DAzure::SetDime
     mZero = PR_FALSE;
   }
 
   // Check that the dimensions are sane
   IntSize size(width, height);
   if (size.width <= 0xFFFF && size.height <= 0xFFFF &&
       size.width >= 0 && size.height >= 0) {
     SurfaceFormat format = GetSurfaceFormat();
-    nsCOMPtr<nsIContent> content =
-      do_QueryInterface(static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement));
+    nsCOMPtr<nsIContent> content = do_QueryObject(mCanvasElement);
     nsIDocument* ownerDoc = nsnull;
     if (content) {
       ownerDoc = content->GetOwnerDoc();
     }
 
     nsRefPtr<LayerManager> layerManager = nsnull;
 
     if (ownerDoc) {
--- a/content/canvas/test/webgl/conformance/oes-texture-float.html
+++ b/content/canvas/test/webgl/conformance/oes-texture-float.html
@@ -80,17 +80,18 @@ if (!gl) {
   runTextureCreationTest(testProgram, false);
 
   if (!gl.getExtension("OES_texture_float")) {
       testPassed("No OES_texture_float support -- this is legal");
   } else {
       testPassed("Successfully enabled OES_texture_float extension");
       runTextureCreationTest(testProgram, true);
       runRenderTargetTest(testProgram);
-      runUniqueObjectTest();
+      // bug 683216, see the discussion in bug 630672
+      // runUniqueObjectTest();
   }
 }
 
 // Needs to be global for shouldBe to see it.
 var pixels;
 
 function allocateTexture()
 {
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/remove-uniqueObjectTest.patch
@@ -0,0 +1,25 @@
+# HG changeset patch
+# Parent 6c8a909977d32284bbddd60a45e1780e824b5d7c
+diff --git a/content/canvas/test/webgl/conformance/oes-texture-float.html b/content/canvas/test/webgl/conformance/oes-texture-float.html
+--- a/content/canvas/test/webgl/conformance/oes-texture-float.html
++++ b/content/canvas/test/webgl/conformance/oes-texture-float.html
+@@ -80,17 +80,18 @@ if (!gl) {
+   runTextureCreationTest(testProgram, false);
+ 
+   if (!gl.getExtension("OES_texture_float")) {
+       testPassed("No OES_texture_float support -- this is legal");
+   } else {
+       testPassed("Successfully enabled OES_texture_float extension");
+       runTextureCreationTest(testProgram, true);
+       runRenderTargetTest(testProgram);
+-      runUniqueObjectTest();
++      // bug 683216, see the discussion in bug 630672
++      // runUniqueObjectTest();
+   }
+ }
+ 
+ // Needs to be global for shouldBe to see it.
+ var pixels;
+ 
+ function allocateTexture()
+ {
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -1209,18 +1209,17 @@ nsHTMLFormElement::AddElement(nsGenericH
     if (oldDefaultSubmit && oldDefaultSubmit != mDefaultSubmitElement) {
       oldDefaultSubmit->UpdateState(aNotify);
     }
   }
 
   // If the element is subject to constraint validaton and is invalid, we need
   // to update our internal counter.
   if (aUpdateValidity) {
-    nsCOMPtr<nsIConstraintValidation> cvElmt =
-      do_QueryInterface(static_cast<nsGenericHTMLElement*>(aChild));
+    nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aChild);
     if (cvElmt &&
         cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
       UpdateValidity(PR_FALSE);
     }
   }
 
   // Notify the radio button it's been added to a group
   // This has to be done _after_ UpdateValidity() call to prevent the element
@@ -1296,18 +1295,17 @@ nsHTMLFormElement::RemoveElement(nsGener
     // being removed) because it's either being removed from the DOM or
     // changing attributes in a way that makes it responsible for sending its
     // own notifications.
   }
 
   // If the element was subject to constraint validaton and is invalid, we need
   // to update our internal counter.
   if (aUpdateValidity) {
-    nsCOMPtr<nsIConstraintValidation> cvElmt =
-      do_QueryInterface(static_cast<nsGenericHTMLElement*>(aChild));
+    nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aChild);
     if (cvElmt &&
         cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
       UpdateValidity(PR_TRUE);
     }
   }
 
   return rv;
 }
@@ -2345,18 +2343,17 @@ nsFormControlList::AddElementToTable(nsG
 
       // Determine the ordering between the new and old element.
       PRBool newFirst = nsContentUtils::PositionIsBefore(aChild, content);
 
       list->AppendElement(newFirst ? aChild : content);
       list->AppendElement(newFirst ? content : aChild);
 
 
-      nsCOMPtr<nsISupports> listSupports =
-        do_QueryInterface(static_cast<nsIDOMNodeList*>(list));
+      nsCOMPtr<nsISupports> listSupports = do_QueryObject(list);
 
       // Replace the element with the list.
       NS_ENSURE_TRUE(mNameLookupTable.Put(aName, listSupports),
                      NS_ERROR_FAILURE);
     } else {
       // There's already a list in the hash, add the child to the list
       nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(supports));
       NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -2250,18 +2250,17 @@ ImageContainer* nsHTMLMediaElement::GetI
     return mImageContainer;
 
   // If we have a print surface, this is just a static image so
   // no image container is required
   if (mPrintSurface)
     return nsnull;
 
   // Only video frames need an image container.
-  nsCOMPtr<nsIDOMHTMLVideoElement> video =
-    do_QueryInterface(static_cast<nsIContent*>(this));
+  nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
   if (!video)
     return nsnull;
 
   nsRefPtr<LayerManager> manager =
     nsContentUtils::PersistentLayerManagerForDocument(GetOwnerDoc());
   if (!manager)
     return nsnull;
 
@@ -2278,17 +2277,17 @@ nsresult nsHTMLMediaElement::DispatchAud
 {
   // Auto manage the memory for the frame buffer. If we fail and return
   // an error, this ensures we free the memory in the frame buffer. Otherwise
   // we hand off ownership of the frame buffer to the audioavailable event,
   // which frees the memory when it's destroyed.
   nsAutoArrayPtr<float> frameBuffer(aFrameBuffer);
 
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(GetOwnerDoc());
-  nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(static_cast<nsIContent*>(this)));
+  nsCOMPtr<nsIDOMEventTarget> target(do_QueryObject(this));
   NS_ENSURE_TRUE(domDoc && target, NS_ERROR_INVALID_ARG);
 
   nsCOMPtr<nsIDOMEvent> event;
   nsresult rv = domDoc->CreateEvent(NS_LITERAL_STRING("MozAudioAvailableEvent"),
                                     getter_AddRefs(event));
   nsCOMPtr<nsIDOMNotifyAudioAvailableEvent> audioavailableEvent(do_QueryInterface(event));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2491,18 +2490,17 @@ void nsHTMLMediaElement::NotifyAddedSour
   if (mLoadWaitStatus == WAITING_FOR_SOURCE) {
     QueueLoadFromSourceTask();
   }
 }
 
 nsIContent* nsHTMLMediaElement::GetNextSource()
 {
   nsresult rv = NS_OK;
-  nsCOMPtr<nsIDOMNode> thisDomNode =
-    do_QueryInterface(static_cast<nsGenericElement*>(this));
+  nsCOMPtr<nsIDOMNode> thisDomNode = do_QueryObject(this);
 
   mSourceLoadCandidate = nsnull;
 
   if (!mSourcePointer) {
     // First time this has been run, create a selection to cover children.
     mSourcePointer = do_CreateInstance("@mozilla.org/content/range;1");
 
     rv = mSourcePointer->SelectNodeContents(thisDomNode);
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -775,18 +775,18 @@ void nsBuiltinDecoder::SeekingStoppedAtE
 
     // An additional seek was requested while the current seek was
     // in operation.
     if (mRequestedSeekTime >= 0.0) {
       ChangeState(PLAY_STATE_SEEKING);
       seekWasAborted = PR_TRUE;
     } else {
       UnpinForSeek();
-      fireEnded = mNextState != PLAY_STATE_PLAYING;
-      ChangeState(fireEnded ? PLAY_STATE_ENDED : mNextState);
+      fireEnded = PR_TRUE;
+      ChangeState(PLAY_STATE_ENDED);
     }
   }
 
   if (mElement) {
     UpdateReadyStateForData();
     if (!seekWasAborted) {
       mElement->SeekCompleted();
       if (fireEnded) {
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -325,20 +325,23 @@ function MediaTestManager() {
   // to be one of, or have the same fields as, the g*Test arrays of tests. Uses
   // the user supplied 'startTest' function to initialize the test. This 
   // function must accept two arguments, the test entry from the 'tests' array,
   // and a token. Call MediaTestManager.started(token) if you start the test,
   // and MediaTestManager.finished(token) when the test finishes. You don't have
   // to start every test, but if you call started() you *must* call finish()
   // else you'll timeout. 
   this.runTests = function(tests, startTest) {
+    this.startTime = new Date();
+    SimpleTest.info("Started " + this.startTime + " (" + this.startTime.getTime()/1000 + "s)");
     this.testNum = 0;
     this.tests = tests;
     this.startTest = startTest;
     this.tokens = [];
+    this.isShutdown = false;
     // Always wait for explicit finish.
     SimpleTest.waitForExplicitFinish();
     this.nextTest();
   }
   
   // Registers that the test corresponding to 'token' has been started.
   // Don't call more than once per token.
   this.started = function(token) {
@@ -350,57 +353,62 @@ function MediaTestManager() {
   // run, otherwise it may start up the next run. It's ok to call multiple times
   // per token.
   this.finished = function(token) {
     var i = this.tokens.indexOf(token);
     if (i != -1) {
       // Remove the element from the list of running tests.
       this.tokens.splice(i, 1);
     }
-    if (this.tokens.length == 0) {
+    if (this.tokens.length < PARALLEL_TESTS) {
       this.nextTest();
     }
   }
   
   // Starts the next batch of tests, or finishes if they're all done.
   // Don't call this directly, call finished(token) when you're done.
   this.nextTest = function() {
     // Force a GC after every completed testcase. This ensures that any decoders
     // with live threads waiting for the GC are killed promptly, to free up the
     // thread stacks' address space.
     netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
     Components.utils.forceGC();
-    if (this.testNum == this.tests.length && !DEBUG_TEST_LOOP_FOREVER) {
-      if (this.onFinished) {
-        this.onFinished();
-      }
-      mediaTestCleanup();
-      SimpleTest.finish();
-      return;
-    }
+    
     while (this.testNum < this.tests.length && this.tokens.length < PARALLEL_TESTS) {
       var test = this.tests[this.testNum];
       var token = (test.name ? (test.name + "-"): "") + this.testNum;
       this.testNum++;
 
       if (DEBUG_TEST_LOOP_FOREVER && this.testNum == this.tests.length) {
         this.testNum = 0;
       }
       
       // Ensure we can play the resource type.
       if (test.type && !document.createElement('video').canPlayType(test.type))
         continue;
       
       // Do the init. This should start the test.
       this.startTest(test, token);
-      
     }
-    if (this.tokens.length == 0) {
-      // No tests were added, we must have tried everything, exit.
+
+    if (this.testNum == this.tests.length &&
+        !DEBUG_TEST_LOOP_FOREVER &&
+        this.tokens.length == 0 &&
+        !this.isShutdown)
+    {
+      this.isShutdown = true;
+      if (this.onFinished) {
+        this.onFinished();
+      }
+      mediaTestCleanup();
+      var end = new Date();
+      SimpleTest.info("Finished at " + end + " (" + (end.getTime() / 1000) + "s)");
+      SimpleTest.info("Running time: " + (end.getTime() - this.startTime.getTime())/1000 + "s");
       SimpleTest.finish();
+      return;
     }
   }
 }
 
 // Ensures we've got no active video or audio elements in the document, and
 // forces a GC to release the address space reserved by the decoders' threads'
 // stacks.
 function mediaTestCleanup() {
--- a/content/media/test/test_fragment_play.html
+++ b/content/media/test/test_fragment_play.html
@@ -18,23 +18,23 @@ var manager = new MediaTestManager;
 // try playing the video. Tests for other fragment
 // formats are in test_fragment_noplay.html.
 var gFragmentParams = [
   { fragment: "", start: null, end: null },
   { fragment: "#t=,", start: null, end: null },
   { fragment: "#t=3,3", start: null, end: null },
   { fragment: "#t=7,3", start: null, end: null },
   { fragment: "#t=7,15", start: 7, end: null },
-  { fragment: "#t=15,20", start: 9.287981, end: null, todo: "See Bug 679262" },
+  { fragment: "#t=15,20", start: 9.287981, end: null },
   { fragment: "#t=5", start: 5, end: null },
   { fragment: "#t=5.5", start: 5.5, end: null },
   { fragment: "#t=5,", start: null, end: null },
   { fragment: "#t=,5", start: 0, end: 5 },
-  { fragment: "#t=2.5,5.5", start: 2.5, end: 5.5 },
-  { fragment: "#t=1,2.5", start: 1, end: 2.5 },
+  { fragment: "#t=2.5,5.5", start: 2.5, end: 5.5, todo: "See Bug 682141" },
+  { fragment: "#t=1,2.5", start: 1, end: 2.5, todo: "See Bug 682141" },
   { fragment: "#t=,15", start: 0, end: null }
 ];
 
 function createTestArray() {
   var tests = [];
   var tmpVid = document.createElement("video");
 
   for (var testNum=0; testNum<gFragmentTests.length; testNum++) {
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1485,16 +1485,17 @@ void nsSVGElement::LengthAttributesInfo:
 void
 nsSVGElement::SetLength(nsIAtom* aName, const nsSVGLength2 &aLength)
 {
   LengthAttributesInfo lengthInfo = GetLengthInfo();
 
   for (PRUint32 i = 0; i < lengthInfo.mLengthCount; i++) {
     if (aName == *lengthInfo.mLengthInfo[i].mName) {
       lengthInfo.mLengths[i] = aLength;
+      DidChangeLength(i, PR_TRUE);
       return;
     }
   }
   NS_ABORT_IF_FALSE(false, "no length found to set");
 }
 
 void
 nsSVGElement::DidChangeLength(PRUint8 aAttrEnum, PRBool aDoSetAttr)
@@ -2337,35 +2338,32 @@ nsSVGElement::RecompileScriptEventListen
 #ifdef MOZ_SMIL
 nsISMILAttr*
 nsSVGElement::GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     // Transforms:
     nsCOMPtr<nsIDOMSVGAnimatedTransformList> transformList;
     if (aName == nsGkAtoms::transform) {
-      nsCOMPtr<nsIDOMSVGTransformable> transformable(
-              do_QueryInterface(static_cast<nsIContent*>(this)));
+      nsCOMPtr<nsIDOMSVGTransformable> transformable(do_QueryObject(this));
       if (!transformable)
         return nsnull;
       nsresult rv = transformable->GetTransform(getter_AddRefs(transformList));
       NS_ENSURE_SUCCESS(rv, nsnull);
     }
     if (aName == nsGkAtoms::gradientTransform) {
-      nsCOMPtr<nsIDOMSVGGradientElement> gradientElement(
-              do_QueryInterface(static_cast<nsIContent*>(this)));
+      nsCOMPtr<nsIDOMSVGGradientElement> gradientElement(do_QueryObject(this));
       if (!gradientElement)
         return nsnull;
 
       nsresult rv = gradientElement->GetGradientTransform(getter_AddRefs(transformList));
       NS_ENSURE_SUCCESS(rv, nsnull);
     }
     if (aName == nsGkAtoms::patternTransform) {
-      nsCOMPtr<nsIDOMSVGPatternElement> patternElement(
-              do_QueryInterface(static_cast<nsIContent*>(this)));
+      nsCOMPtr<nsIDOMSVGPatternElement> patternElement(do_QueryObject(this));
       if (!patternElement)
         return nsnull;
 
       nsresult rv = patternElement->GetPatternTransform(getter_AddRefs(transformList));
       NS_ENSURE_SUCCESS(rv, nsnull);
     }
     if (transformList) {
       nsSVGAnimatedTransformList* list
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -642,24 +642,38 @@ nsXMLContentSink::CloseElement(nsIConten
                                   &willNotify,
                                   &isAlternate);
       if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mFragmentMode) {
         ++mPendingSheetCount;
         mScriptLoader->AddExecuteBlocker();
       }
     }
     // Look for <link rel="dns-prefetch" href="hostname">
+    // and look for <link rel="next" href="hostname"> like in HTML sink
     if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) {
       nsAutoString relVal;
       aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal);
-      if (relVal.EqualsLiteral("dns-prefetch")) {
-        nsAutoString hrefVal;
-        aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
-        if (!hrefVal.IsEmpty()) {
-          PrefetchDNS(hrefVal);
+      if (!relVal.IsEmpty()) {
+        // XXX seems overkill to generate this string array
+        nsAutoTArray<nsString, 4> linkTypes;
+        nsStyleLinkElement::ParseLinkTypes(relVal, linkTypes);
+        PRBool hasPrefetch = linkTypes.Contains(NS_LITERAL_STRING("prefetch"));
+        if (hasPrefetch || linkTypes.Contains(NS_LITERAL_STRING("next"))) {
+          nsAutoString hrefVal;
+          aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
+          if (!hrefVal.IsEmpty()) {
+            PrefetchHref(hrefVal, aContent, hasPrefetch);
+          }
+        }
+        if (linkTypes.Contains(NS_LITERAL_STRING("dns-prefetch"))) {
+          nsAutoString hrefVal;
+          aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
+          if (!hrefVal.IsEmpty()) {
+            PrefetchDNS(hrefVal);
+          }
         }
       }
     }
   }
 
   return rv;
 }  
 
--- a/content/xml/document/src/nsXMLDocument.cpp
+++ b/content/xml/document/src/nsXMLDocument.cpp
@@ -377,18 +377,18 @@ nsXMLDocument::Load(const nsAString& aUr
       nsCOMPtr<nsIScriptError2> errorObject =
           do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = errorObject->InitWithWindowID(error.get(), NS_ConvertUTF8toUTF16(spec).get(),
                                          nsnull, 0, 0, nsIScriptError::warningFlag,
                                          "DOM",
                                          callingDoc ?
-                                           callingDoc->OuterWindowID() :
-                                           this->OuterWindowID());
+                                           callingDoc->InnerWindowID() :
+                                           this->InnerWindowID());
 
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIConsoleService> consoleService =
         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
       nsCOMPtr<nsIScriptError> logError = do_QueryInterface(errorObject);
       if (consoleService && logError) {
         consoleService->LogMessage(logError);
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -575,18 +575,17 @@ nsXULElement::IsFocusable(PRInt32 *aTabI
   PRBool shouldFocus = PR_FALSE;
 
 #ifdef XP_MACOSX
   // on Mac, mouse interactions only focus the element if it's a list
   if (aWithMouse && IsNonList(mNodeInfo))
     return PR_FALSE;
 #endif
 
-  nsCOMPtr<nsIDOMXULControlElement> xulControl = 
-    do_QueryInterface(static_cast<nsIContent*>(this));
+  nsCOMPtr<nsIDOMXULControlElement> xulControl = do_QueryObject(this);
   if (xulControl) {
     // a disabled element cannot be focused and is not part of the tab order
     PRBool disabled;
     xulControl->GetDisabled(&disabled);
     if (disabled) {
       if (aTabIndex)
         *aTabIndex = -1;
       return PR_FALSE;
@@ -953,17 +952,17 @@ nsXULElement::RemoveChildAt(PRUint32 aIn
     // anything else = index to re-set as current
     PRInt32 newCurrentIndex = -1;
 
     if (oldKid->NodeInfo()->Equals(nsGkAtoms::listitem, kNameSpaceID_XUL)) {
       // This is the nasty case. We have (potentially) a slew of selected items
       // and cells going away.
       // First, retrieve the tree.
       // Check first whether this element IS the tree
-      controlElement = do_QueryInterface(static_cast<nsIContent*>(this));
+      controlElement = do_QueryObject(this);
 
       // If it's not, look at our parent
       if (!controlElement)
         rv = GetParentTree(getter_AddRefs(controlElement));
 
       nsCOMPtr<nsIDOMElement> oldKidElem = do_QueryInterface(oldKid);
       if (controlElement && oldKidElem) {
         // Iterate over all of the items and find out if they are contained inside
@@ -2052,17 +2051,17 @@ nsXULElement::GetParentTree(nsIDOMXULMul
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULElement::Focus()
 {
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
-    nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(static_cast<nsIContent*>(this));
+    nsCOMPtr<nsIDOMElement> elem = do_QueryObject(this);
     return fm ? fm->SetFocus(this, 0) : NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULElement::Blur()
 {
     if (!ShouldBlur(this))
       return NS_OK;
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -374,17 +374,17 @@ bool nsDSURIContentListener::CheckFrameO
             // If the value of the header is DENY, then the document
             // should never be permitted to load as a subdocument.
             NS_ASSERTION(xfoHeaderValue.LowerCaseEqualsLiteral("deny"),
                          "How did we get here with some random header value?");
         }
 
         // cancel the load and display about:blank
         httpChannel->Cancel(NS_BINDING_ABORTED);
-        nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(static_cast<nsIDocShell*>(mDocShell)));
+        nsCOMPtr<nsIWebNavigation> webNav(do_QueryObject(mDocShell));
         if (webNav) {
             webNav->LoadURI(NS_LITERAL_STRING("about:blank").get(),
                             0, nsnull, nsnull, nsnull);
         }
         return false;
     }
 
     return true;
--- a/dom/base/ConsoleAPI.js
+++ b/dom/base/ConsoleAPI.js
@@ -38,59 +38,64 @@
  * ***** END LICENSE BLOCK ***** */
 
 let Cu = Components.utils;
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm");
 
 function ConsoleAPI() {}
 ConsoleAPI.prototype = {
 
   classID: Components.ID("{b49c18f8-3379-4fc0-8c90-d7772c1a9ff3}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
 
   // nsIDOMGlobalPropertyInitializer
   init: function CA_init(aWindow) {
-    let id;
+    let outerID;
+    let innerID;
     try {
-      id = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                  .getInterface(Ci.nsIDOMWindowUtils)
-                  .outerWindowID;
-    } catch (ex) {
+      let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindowUtils);
+
+      outerID = windowUtils.outerWindowID;
+      innerID = windowUtils.currentInnerWindowID;
+    }
+    catch (ex) {
       Cu.reportError(ex);
     }
 
     let self = this;
     let chromeObject = {
       // window.console API
       log: function CA_log() {
-        self.notifyObservers(id, "log", arguments);
+        self.notifyObservers(outerID, innerID, "log", arguments);
       },
       info: function CA_info() {
-        self.notifyObservers(id, "info", arguments);
+        self.notifyObservers(outerID, innerID, "info", arguments);
       },
       warn: function CA_warn() {
-        self.notifyObservers(id, "warn", arguments);
+        self.notifyObservers(outerID, innerID, "warn", arguments);
       },
       error: function CA_error() {
-        self.notifyObservers(id, "error", arguments);
+        self.notifyObservers(outerID, innerID, "error", arguments);
       },
       debug: function CA_debug() {
-        self.notifyObservers(id, "log", arguments);
+        self.notifyObservers(outerID, innerID, "log", arguments);
       },
       trace: function CA_trace() {
-        self.notifyObservers(id, "trace", self.getStackTrace());
+        self.notifyObservers(outerID, innerID, "trace", self.getStackTrace());
       },
       // Displays an interactive listing of all the properties of an object.
       dir: function CA_dir() {
-        self.notifyObservers(id, "dir", arguments);
+        self.notifyObservers(outerID, innerID, "dir", arguments);
       },
       __exposedProps__: {
         log: "r",
         info: "r",
         warn: "r",
         error: "r",
         debug: "r",
         trace: "r",
@@ -120,38 +125,52 @@ ConsoleAPI.prototype = {
 
     Object.defineProperties(contentObj, properties);
     Cu.makeObjectPropsNormal(contentObj);
 
     return contentObj;
   },
 
   /**
-   * Notify all observers of any console API call
+   * Notify all observers of any console API call.
+   *
+   * @param number aOuterWindowID
+   *        The outer window ID from where the message came from.
+   * @param number aInnerWindowID
+   *        The inner window ID from where the message came from.
+   * @param string aLevel
+   *        The message level.
+   * @param mixed aArguments
+   *        The arguments given to the console API call.
    **/
-  notifyObservers: function CA_notifyObservers(aID, aLevel, aArguments) {
-    if (!aID)
+  notifyObservers:
+  function CA_notifyObservers(aOuterWindowID, aInnerWindowID, aLevel, aArguments) {
+    if (!aOuterWindowID) {
       return;
+    }
 
     let stack = this.getStackTrace();
     // Skip the first frame since it contains an internal call.
     let frame = stack[1];
     let consoleEvent = {
-      ID: aID,
+      ID: aOuterWindowID,
+      innerID: aInnerWindowID,
       level: aLevel,
       filename: frame.filename,
       lineNumber: frame.lineNumber,
       functionName: frame.functionName,
       arguments: aArguments
     };
 
     consoleEvent.wrappedJSObject = consoleEvent;
 
+    ConsoleAPIStorage.recordEvent(aInnerWindowID, consoleEvent);
+
     Services.obs.notifyObservers(consoleEvent,
-                                 "console-api-log-event", aID);
+                                 "console-api-log-event", aOuterWindowID);
   },
 
   /**
    * Build the stacktrace array for the console.trace() call.
    *
    * @return array
    *         Each element is a stack frame that holds the following properties:
    *         filename, lineNumber, functionName and language.
new file mode 100644
--- /dev/null
+++ b/dom/base/ConsoleAPIStorage.jsm
@@ -0,0 +1,180 @@
+/* ***** 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 ConsoleStorageService code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  David Dahl <ddahl@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 ***** */
+
+let Cu = Components.utils;
+let Ci = Components.interfaces;
+let Cc = Components.classes;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const STORAGE_MAX_EVENTS = 200;
+
+XPCOMUtils.defineLazyGetter(this, "gPrivBrowsing", function () {
+  // private browsing may not be available in some Gecko Apps
+  try {
+    return Cc["@mozilla.org/privatebrowsing;1"].getService(Ci.nsIPrivateBrowsingService);
+  }
+  catch (ex) {
+    return null;
+  }
+});
+
+var EXPORTED_SYMBOLS = ["ConsoleAPIStorage"];
+
+var _consoleStorage = {};
+
+/**
+ * The ConsoleAPIStorage is meant to cache window.console API calls for later
+ * reuse by other components when needed. For example, the Web Console code can
+ * display the cached messages when it opens for the active tab.
+ *
+ * ConsoleAPI messages are stored as they come from the ConsoleAPI code, with
+ * all their properties. They are kept around until the inner window object that
+ * created the messages is destroyed. Messages are indexed by the inner window
+ * ID.
+ *
+ * Usage:
+ *    Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm");
+ *
+ *    // Get the cached events array for the window you want (use the inner
+ *    // window ID).
+ *    let events = ConsoleAPIStorage.getEvents(innerWindowID);
+ *    events.forEach(function(event) { ... });
+ *
+ *    // Clear the events for the given inner window ID.
+ *    ConsoleAPIStorage.clearEvents(innerWindowID);
+ */
+var ConsoleAPIStorage = {
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  /** @private */
+  observe: function CS_observe(aSubject, aTopic, aData)
+  {
+    if (aTopic == "xpcom-shutdown") {
+      Services.obs.removeObserver(this, "xpcom-shutdown");
+      Services.obs.removeObserver(this, "inner-window-destroyed");
+      Services.obs.removeObserver(this, "memory-pressure");
+      delete _consoleStorage;
+    }
+    else if (aTopic == "inner-window-destroyed") {
+      let innerWindowID = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+      this.clearEvents(innerWindowID);
+    }
+    else if (aTopic == "memory-pressure") {
+      if (aData == "low-memory") {
+        this.clearEvents();
+      }
+    }
+  },
+
+  /** @private */
+  init: function CS_init()
+  {
+    Services.obs.addObserver(this, "xpcom-shutdown", false);
+    Services.obs.addObserver(this, "inner-window-destroyed", false);
+    Services.obs.addObserver(this, "memory-pressure", false);
+  },
+
+  /**
+   * Get the events array by inner window ID.
+   *
+   * @param string aId
+   *        The inner window ID for which you want to get the array of cached
+   *        events.
+   * @returns array
+   *          The array of cached events for the given window.
+   */
+  getEvents: function CS_getEvents(aId)
+  {
+    return _consoleStorage[aId] || [];
+  },
+
+  /**
+   * Record an event associated with the given window ID.
+   *
+   * @param string aWindowID
+   *        The ID of the inner window for which the event occurred.
+   * @param object aEvent
+   *        A JavaScript object you want to store.
+   */
+  recordEvent: function CS_recordEvent(aWindowID, aEvent)
+  {
+    let ID = parseInt(aWindowID);
+    if (isNaN(ID)) {
+      throw new Error("Invalid window ID argument");
+    }
+
+    if (gPrivBrowsing && gPrivBrowsing.privateBrowsingEnabled) {
+      return;
+    }
+
+    if (!_consoleStorage[ID]) {
+      _consoleStorage[ID] = [];
+    }
+    let storage = _consoleStorage[ID];
+    storage.push(aEvent);
+
+    // truncate
+    if (storage.length > STORAGE_MAX_EVENTS) {
+      storage.shift();
+    }
+
+    Services.obs.notifyObservers(aEvent, "console-storage-cache-event", ID);
+  },
+
+  /**
+   * Clear storage data for the given window.
+   *
+   * @param string [aId]
+   *        Optional, the inner window ID for which you want to clear the
+   *        messages. If this is not specified all of the cached messages are
+   *        cleared, from all window objects.
+   */
+  clearEvents: function CS_clearEvents(aId)
+  {
+    if (aId != null) {
+      delete _consoleStorage[aId];
+    }
+    else {
+      _consoleStorage = {};
+      Services.obs.notifyObservers(null, "console-storage-reset", null);
+    }
+  },
+};
+
+ConsoleAPIStorage.init();
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -47,16 +47,19 @@ LIBRARY_NAME	= jsdombase_s
 LIBXUL_LIBRARY	= 1
 FORCE_STATIC_LIB = 1
 
 EXTRA_PP_COMPONENTS = \
 		ConsoleAPI.js \
 		ConsoleAPI.manifest \
 		$(NULL)
 
+EXTRA_JS_MODULES = ConsoleAPIStorage.jsm \
+		$(NULL)
+
 XPIDLSRCS = \
   nsIEntropyCollector.idl \
   nsIScriptChannel.idl \
   $(NULL)
 
 EXPORTS = \
   nsDOMCID.h \
   nsDOMClassInfoClasses.h \
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1697,17 +1697,17 @@ PrintWarningOnConsole(JSContext *cx, con
 
   nsresult rv = scriptError->InitWithWindowID(msg.get(),
                                               sourcefile.get(),
                                               EmptyString().get(),
                                               lineno,
                                               0, // column for error is not available
                                               nsIScriptError::warningFlag,
                                               "DOM:HTML",
-                                              nsJSUtils::GetCurrentlyRunningCodeWindowID(cx));
+                                              nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
 
   if (NS_SUCCEEDED(rv)){
     nsCOMPtr<nsIScriptError> logError = do_QueryInterface(scriptError);
     consoleService->LogMessage(logError);
   }
 }
 
 static inline JSString *
@@ -5078,17 +5078,17 @@ nsWindowSH::InvalidateGlobalScopePollute
       nsIHTMLDocument *doc = (nsIHTMLDocument *)::JS_GetPrivate(cx, proto);
 
       NS_IF_RELEASE(doc);
 
       ::JS_SetPrivate(cx, proto, nsnull);
 
       // Pull the global scope polluter out of the prototype chain so
       // that it can be freed.
-      ::JS_SetPrototype(cx, obj, ::JS_GetPrototype(cx, proto));
+      ::JS_SplicePrototype(cx, obj, ::JS_GetPrototype(cx, proto));
 
       break;
     }
 
     obj = proto;
   }
 }
 
@@ -5100,44 +5100,40 @@ nsWindowSH::InstallGlobalScopePolluter(J
   // If global scope pollution is disabled, or if our document is not
   // a HTML document, do nothing
   if (sDisableGlobalScopePollutionSupport || !doc) {
     return NS_OK;
   }
 
   JSAutoRequest ar(cx);
 
-  JSObject *gsp = ::JS_NewObject(cx, &sGlobalScopePolluterClass, nsnull, obj);
+  JSObject *gsp = ::JS_NewObjectWithUniqueType(cx, &sGlobalScopePolluterClass, nsnull, obj);
   if (!gsp) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   JSObject *o = obj, *proto;
 
   // Find the place in the prototype chain where we want this global
   // scope polluter (right before Object.prototype).
 
   while ((proto = ::JS_GetPrototype(cx, o))) {
     if (JS_GET_CLASS(cx, proto) == sObjectClass) {
       // Set the global scope polluters prototype to Object.prototype
-      if (!::JS_SetPrototype(cx, gsp, proto)) {
-        return NS_ERROR_UNEXPECTED;
-      }
+      ::JS_SplicePrototype(cx, gsp, proto);
 
       break;
     }
 
     o = proto;
   }
 
   // And then set the prototype of the object whose prototype was
   // Object.prototype to be the global scope polluter.
-  if (!::JS_SetPrototype(cx, o, gsp)) {
-    return NS_ERROR_UNEXPECTED;
-  }
+  ::JS_SplicePrototype(cx, o, gsp);
 
   if (!::JS_SetPrivate(cx, gsp, doc)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // The global scope polluter will release doc on destruction (or
   // invalidation).
   NS_ADDREF(doc);
--- a/dom/base/nsDOMMemoryReporter.cpp
+++ b/dom/base/nsDOMMemoryReporter.cpp
@@ -98,13 +98,15 @@ GetWindowsMemoryUsage(const PRUint64& aI
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsDOMMemoryReporter::GetAmount(PRInt64* aAmount) {
   *aAmount = 0;
 
   nsGlobalWindow::WindowByIdTable* windows = nsGlobalWindow::GetWindowsTable();
+  NS_ENSURE_TRUE(windows, NS_OK);
+
   windows->Enumerate(GetWindowsMemoryUsage, aAmount);
 
   return NS_OK;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1096,18 +1096,17 @@ nsGlobalWindow::MaybeForgiveSpamCount()
   }
 }
 
 void
 nsGlobalWindow::CleanUp(PRBool aIgnoreModalDialog)
 {
   if (IsOuterWindow() && !aIgnoreModalDialog) {
     nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
-    nsCOMPtr<nsIDOMModalContentWindow>
-      dlg(do_QueryInterface(static_cast<nsPIDOMWindow*>(inner)));
+    nsCOMPtr<nsIDOMModalContentWindow> dlg(do_QueryObject(inner));
     if (dlg) {
       // The window we're trying to clean up is the outer window of a
       // modal dialog.  Defer cleanup until the window closes, and let
       // ShowModalDialog take care of calling CleanUp.
       mCallCleanUpAfterModalDialogCloses = PR_TRUE;
       return;
     }
   }
@@ -9774,18 +9773,17 @@ nsGlobalWindow::BuildURIfromBase(const c
     *aCXused = nsnull;
 
   // get JSContext
   NS_ASSERTION(scx, "opening window missing its context");
   NS_ASSERTION(mDocument, "opening window missing its document");
   if (!scx || !mDocument)
     return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIDOMChromeWindow> chrome_win =
-    do_QueryInterface(static_cast<nsIDOMWindow *>(this));
+  nsCOMPtr<nsIDOMChromeWindow> chrome_win = do_QueryObject(this);
 
   if (nsContentUtils::IsCallerChrome() && !chrome_win) {
     // If open() is called from chrome on a non-chrome window, we'll
     // use the context from the window on which open() is being called
     // to prevent giving chrome priveleges to new windows opened in
     // such a way. This also makes us get the appropriate base URI for
     // the below URI resolution code.
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -535,16 +535,21 @@ public:
     return mSerial;
   }
 
   static nsGlobalWindow* GetOuterWindowWithId(PRUint64 aWindowID) {
     nsGlobalWindow* outerWindow = sWindowsById->Get(aWindowID);
     return outerWindow && !outerWindow->IsInnerWindow() ? outerWindow : nsnull;
   }
 
+  static nsGlobalWindow* GetInnerWindowWithId(PRUint64 aInnerWindowID) {
+    nsGlobalWindow* innerWindow = sWindowsById->Get(aInnerWindowID);
+    return innerWindow && innerWindow->IsInnerWindow() ? innerWindow : nsnull;
+  }
+
   static bool HasIndexedDBSupport();
 
   static bool HasPerformanceSupport();
 
   static WindowByIdTable* GetWindowsTable() {
     return sWindowsById;
   }
 
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -262,21 +262,21 @@ class ScriptErrorEvent : public nsRunnab
 {
 public:
   ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal,
                    PRUint32 aLineNr, PRUint32 aColumn, PRUint32 aFlags,
                    const nsAString& aErrorMsg,
                    const nsAString& aFileName,
                    const nsAString& aSourceLine,
                    PRBool aDispatchEvent,
-                   PRUint64 aWindowID)
+                   PRUint64 aInnerWindowID)
   : mScriptGlobal(aScriptGlobal), mLineNr(aLineNr), mColumn(aColumn),
     mFlags(aFlags), mErrorMsg(aErrorMsg), mFileName(aFileName),
     mSourceLine(aSourceLine), mDispatchEvent(aDispatchEvent),
-    mWindowID(aWindowID)
+    mInnerWindowID(aInnerWindowID)
   {}
 
   NS_IMETHOD Run()
   {
     nsEventStatus status = nsEventStatus_eIgnore;
     // First, notify the DOM that we have a script error.
     if (mDispatchEvent) {
       nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
@@ -359,17 +359,17 @@ public:
           ? "chrome javascript"
           : "content javascript";
 
         nsCOMPtr<nsIScriptError2> error2(do_QueryInterface(errorObject));
         if (error2) {
           rv = error2->InitWithWindowID(mErrorMsg.get(), mFileName.get(),
                                         mSourceLine.get(),
                                         mLineNr, mColumn, mFlags,
-                                        category, mWindowID);
+                                        category, mInnerWindowID);
         } else {
           rv = errorObject->Init(mErrorMsg.get(), mFileName.get(),
                                  mSourceLine.get(),
                                  mLineNr, mColumn, mFlags,
                                  category);
         }
 
         if (NS_SUCCEEDED(rv)) {
@@ -388,17 +388,17 @@ public:
   nsCOMPtr<nsIScriptGlobalObject> mScriptGlobal;
   PRUint32                        mLineNr;
   PRUint32                        mColumn;
   PRUint32                        mFlags;
   nsString                        mErrorMsg;
   nsString                        mFileName;
   nsString                        mSourceLine;
   PRBool                          mDispatchEvent;
-  PRUint64                        mWindowID;
+  PRUint64                        mInnerWindowID;
 
   static PRBool sHandlingScriptError;
 };
 
 PRBool ScriptErrorEvent::sHandlingScriptError = PR_FALSE;
 
 // NOTE: This function could be refactored to use the above.  The only reason
 // it has not been done is that the code below only fills the error event
@@ -469,23 +469,29 @@ NS_ScriptErrorReporter(JSContext *cx,
        * event because the dom event handler would encounter
        * an OOM exception trying to process the event, and
        * then we'd need to generate a new OOM event for that
        * new OOM instance -- this isn't pretty.
        */
       nsAutoString sourceLine;
       sourceLine.Assign(reinterpret_cast<const PRUnichar*>(report->uclinebuf));
       nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
-      PRUint64 windowID = win ? win->WindowID() : 0;
+      PRUint64 innerWindowID = 0;
+      if (win) {
+        nsCOMPtr<nsPIDOMWindow> innerWin = win->GetCurrentInnerWindow();
+        if (innerWin) {
+          innerWindowID = innerWin->WindowID();
+        }
+      }
       nsContentUtils::AddScriptRunner(
         new ScriptErrorEvent(globalObject, report->lineno,
                              report->uctokenptr - report->uclinebuf,
                              report->flags, msg, fileName, sourceLine,
                              report->errorNumber != JSMSG_OUT_OF_MEMORY,
-                             windowID));
+                             innerWindowID));
     }
   }
 
 #ifdef DEBUG
   // Print it to stderr as well, for the benefit of those invoking
   // mozilla with -console.
   nsCAutoString error;
   error.Assign("JavaScript ");
@@ -904,16 +910,17 @@ static const char js_zeal_compartment_st
 #endif
 static const char js_tracejit_content_str[]   = JS_OPTIONS_DOT_STR "tracejit.content";
 static const char js_tracejit_chrome_str[]    = JS_OPTIONS_DOT_STR "tracejit.chrome";
 static const char js_methodjit_content_str[]  = JS_OPTIONS_DOT_STR "methodjit.content";
 static const char js_methodjit_chrome_str[]   = JS_OPTIONS_DOT_STR "methodjit.chrome";
 static const char js_profiling_content_str[]  = JS_OPTIONS_DOT_STR "jitprofiling.content";
 static const char js_profiling_chrome_str[]   = JS_OPTIONS_DOT_STR "jitprofiling.chrome";
 static const char js_methodjit_always_str[]   = JS_OPTIONS_DOT_STR "methodjit_always";
+static const char js_typeinfer_str[]          = JS_OPTIONS_DOT_STR "typeinference";
 static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
 
 int
 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
 {
   nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
   PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
   PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
@@ -936,24 +943,26 @@ nsJSContext::JSOptionChangedCallback(con
                                               js_tracejit_content_str);
   PRBool useMethodJIT = Preferences::GetBool(chromeWindow ?
                                                js_methodjit_chrome_str :
                                                js_methodjit_content_str);
   PRBool useProfiling = Preferences::GetBool(chromeWindow ?
                                                js_profiling_chrome_str :
                                                js_profiling_content_str);
   PRBool useMethodJITAlways = Preferences::GetBool(js_methodjit_always_str);
+  PRBool useTypeInference = !chromeWindow && Preferences::GetBool(js_typeinfer_str);
   nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
   if (xr) {
     PRBool safeMode = PR_FALSE;
     xr->GetInSafeMode(&safeMode);
     if (safeMode) {
       useTraceJIT = PR_FALSE;
       useMethodJIT = PR_FALSE;
       useProfiling = PR_FALSE;
+      useTypeInference = PR_FALSE;
       useMethodJITAlways = PR_TRUE;
     }
   }    
 
   if (useTraceJIT)
     newDefaultJSOptions |= JSOPTION_JIT;
   else
     newDefaultJSOptions &= ~JSOPTION_JIT;
@@ -968,16 +977,21 @@ nsJSContext::JSOptionChangedCallback(con
   else
     newDefaultJSOptions &= ~JSOPTION_PROFILING;
 
   if (useMethodJITAlways)
     newDefaultJSOptions |= JSOPTION_METHODJIT_ALWAYS;
   else
     newDefaultJSOptions &= ~JSOPTION_METHODJIT_ALWAYS;
 
+  if (useTypeInference)
+    newDefaultJSOptions |= JSOPTION_TYPE_INFERENCE;
+  else
+    newDefaultJSOptions &= ~JSOPTION_TYPE_INFERENCE;
+
 #ifdef DEBUG
   // In debug builds, warnings are enabled in chrome context if
   // javascript.options.strict.debug is true
   PRBool strictDebug = Preferences::GetBool(js_strict_debug_option_str);
   // Note this callback is also called from context's InitClasses thus we don't
   // need to enable this directly from InitContext
   if (strictDebug && (newDefaultJSOptions & JSOPTION_STRICT) == 0) {
     if (chromeWindow)
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -158,29 +158,29 @@ nsJSUtils::GetDynamicScriptGlobal(JSCont
 
 nsIScriptContext *
 nsJSUtils::GetDynamicScriptContext(JSContext *aContext)
 {
   return GetScriptContextFromJSContext(aContext);
 }
 
 PRUint64
-nsJSUtils::GetCurrentlyRunningCodeWindowID(JSContext *aContext)
+nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext)
 {
   if (!aContext)
     return 0;
 
-  PRUint64 windowID = 0;
+  PRUint64 innerWindowID = 0;
 
   JSObject *jsGlobal = JS_GetGlobalForScopeChain(aContext);
   if (jsGlobal) {
     nsIScriptGlobalObject *scriptGlobal = GetStaticScriptGlobal(aContext,
                                                                 jsGlobal);
     if (scriptGlobal) {
       nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptGlobal);
       if (win)
-        windowID = win->GetOuterWindow()->WindowID();
+        innerWindowID = win->WindowID();
     }
   }
 
-  return windowID;
+  return innerWindowID;
 }
 
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -66,24 +66,24 @@ public:
   static nsIScriptContext *GetStaticScriptContext(JSContext* aContext,
                                                   JSObject* aObj);
 
   static nsIScriptGlobalObject *GetDynamicScriptGlobal(JSContext *aContext);
 
   static nsIScriptContext *GetDynamicScriptContext(JSContext *aContext);
 
   /**
-   * Retrieve the outer window ID based on the given JSContext.
+   * Retrieve the inner window ID based on the given JSContext.
    *
    * @param JSContext aContext
-   *        The JSContext from which you want to find the outer window ID.
+   *        The JSContext from which you want to find the inner window ID.
    *
-   * @returns PRUint64 the outer window ID.
+   * @returns PRUint64 the inner window ID.
    */
-  static PRUint64 GetCurrentlyRunningCodeWindowID(JSContext *aContext);
+  static PRUint64 GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext);
 };
 
 
 class nsDependentJSString : public nsDependentString
 {
 public:
   /**
    * In the case of string ids, getting the string's chars is infallible, so
--- a/dom/indexedDB/AsyncConnectionHelper.cpp
+++ b/dom/indexedDB/AsyncConnectionHelper.cpp
@@ -285,18 +285,17 @@ AsyncConnectionHelper::Run()
 
   if (!mStartTime.IsNull()) {
     nsCOMPtr<mozIStorageProgressHandler> handler;
     rv = connection->RemoveProgressHandler(getter_AddRefs(handler));
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "RemoveProgressHandler failed!");
 #ifdef DEBUG
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsISupports> handlerSupports(do_QueryInterface(handler));
-      nsCOMPtr<nsISupports> thisSupports =
-        do_QueryInterface(static_cast<nsIRunnable*>(this));
+      nsCOMPtr<nsISupports> thisSupports = do_QueryObject(this);
       NS_ASSERTION(thisSupports == handlerSupports, "Mismatch!");
     }
 #endif
     mStartTime = TimeStamp();
   }
 
   return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
 }
--- a/dom/interfaces/html/nsIDOMHTMLSourceElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLSourceElement.idl
@@ -34,17 +34,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMHTMLElement.idl"
 
 /**
- * The nsIDOMHTMLVideoElement interface is the interface to a HTML
+ * The nsIDOMHTMLSourceElement interface is the interface to a HTML
  * <source> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#source
  *
  * @status UNDER_DEVELOPMENT
  */
 
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/threads/Makefile.in
@@ -0,0 +1,52 @@
+#
+# ***** 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) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+#   Ben Turner <bent.mozilla@gmail.com>
+#
+# 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
+# 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 *****
+
+DEPTH        = ../../..
+topsrcdir    = @top_srcdir@
+srcdir       = @srcdir@
+VPATH        = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE       = dom
+XPIDL_MODULE = dom_threads
+GRE_MODULE   = 1
+
+XPIDLSRCS    =  nsIDOMWorkers.idl
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/threads/nsIDOMWorkers.idl
@@ -0,0 +1,156 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** 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
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@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 ***** */
+
+/**
+ * From http://www.whatwg.org/specs/web-workers/current-work
+ */
+
+#include "nsIDOMEvent.idl"
+#include "nsIDOMEventTarget.idl"
+
+interface nsIDOMEventListener;
+
+[scriptable, uuid(ab3725b8-3fca-40cc-a42c-92fb154ef01d)]
+interface nsIWorkerMessagePort : nsISupports
+{
+  void postMessage(/* in JSObject aMessage */);
+};
+
+[scriptable, uuid(508f2d49-e9a0-4fe8-bd33-321820173b4a)]
+interface nsIWorkerMessageEvent : nsIDOMEvent
+{
+  readonly attribute DOMString data;
+  readonly attribute DOMString origin;
+
+  readonly attribute nsISupports source;
+
+  void initMessageEvent(in DOMString aTypeArg,
+                        in boolean aCanBubbleArg,
+                        in boolean aCancelableArg,
+                        in DOMString aDataArg,
+                        in DOMString aOriginArg,
+                        in nsISupports aSourceArg);
+};
+
+[scriptable, uuid(73d82c1d-05de-49c9-a23b-7121ff09a67a)]
+interface nsIWorkerErrorEvent : nsIDOMEvent
+{
+  readonly attribute DOMString message;
+  readonly attribute DOMString filename;
+
+  readonly attribute unsigned long lineno;
+
+  void initErrorEvent(in DOMString aTypeArg,
+                      in boolean aCanBubbleArg,
+                      in boolean aCancelableArg,
+                      in DOMString aMessageArg,
+                      in DOMString aFilenameArg,
+                      in unsigned long aLinenoArg);
+};
+
+[scriptable, uuid(17a005c3-4f2f-4bb6-b169-c181fa6873de)]
+interface nsIWorkerLocation : nsISupports
+{
+  readonly attribute AUTF8String href;
+  readonly attribute AUTF8String protocol;
+  readonly attribute AUTF8String host;
+  readonly attribute AUTF8String hostname;
+  readonly attribute AUTF8String port;
+  readonly attribute AUTF8String pathname;
+  readonly attribute AUTF8String search;
+  readonly attribute AUTF8String hash;
+
+  AUTF8String toString();
+};
+
+[scriptable, uuid(74fb665a-e477-4ce2-b3c6-c58b1b28b6c3)]
+interface nsIWorkerNavigator : nsISupports
+{
+  readonly attribute DOMString appName;
+  readonly attribute DOMString appVersion;
+  readonly attribute DOMString platform;
+  readonly attribute DOMString userAgent;
+};
+
+[scriptable, uuid(c111e7d3-8044-4458-aa7b-637696ffb841)]
+interface nsIWorkerGlobalScope : nsISupports
+{
+  readonly attribute nsIWorkerGlobalScope self;
+  readonly attribute nsIWorkerNavigator navigator;
+  readonly attribute nsIWorkerLocation location;
+
+  attribute nsIDOMEventListener onerror;
+};
+
+[scriptable, uuid(5c55ea4b-e4ac-4ceb-bfeb-46bd5e521b8a)]
+interface nsIWorkerScope : nsIWorkerGlobalScope
+{
+  void postMessage(/* in JSObject aMessage */);
+
+  void close();
+
+  attribute nsIDOMEventListener onmessage;
+  attribute nsIDOMEventListener onclose;
+};
+
+[scriptable, builtinclass, uuid(b90b7561-b5e2-4545-84b0-280dbaaa94ea)]
+interface nsIAbstractWorker : nsIDOMEventTarget
+{
+  attribute nsIDOMEventListener onerror;
+};
+
+[scriptable, builtinclass, uuid(daf945c3-8d29-4724-8939-dd383f7d27a7)]
+interface nsIWorker : nsIAbstractWorker
+{
+  void postMessage(/* in JSObject aMessage */);
+
+  attribute nsIDOMEventListener onmessage;
+
+  void terminate();
+};
+
+[scriptable, uuid(cfc4bb32-ca83-4d58-9b6f-66f8054a333a)]
+interface nsIWorkerFactory : nsISupports
+{
+    nsIWorker newChromeWorker(/* in DOMString aScriptURL */);
+};
+
+%{ C++
+#define NS_WORKERFACTORY_CONTRACTID \
+"@mozilla.org/threads/workerfactory;1"
+%}
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -682,18 +682,20 @@ ContentChild::RecvGeolocationUpdate(cons
   gs->Update(position);
   return true;
 }
 
 bool
 ContentChild::RecvAddPermission(const IPC::Permission& permission)
 {
 #if MOZ_PERMISSIONS
-  nsRefPtr<nsPermissionManager> permissionManager =
-    nsPermissionManager::GetSingleton();
+  nsCOMPtr<nsIPermissionManager> permissionManagerIface =
+      do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+  nsPermissionManager* permissionManager =
+      static_cast<nsPermissionManager*>(permissionManagerIface.get());
   NS_ABORT_IF_FALSE(permissionManager, 
                    "We have no permissionManager in the Content process !");
 
   permissionManager->AddInternal(nsCString(permission.host),
                                  nsCString(permission.type),
                                  permission.capability,
                                  0,
                                  permission.expireType,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -264,23 +264,38 @@ ContentParent::OnChannelConnected(int32 
                 setpriority(PRIO_PROCESS, pid, getpriority(PRIO_PROCESS, pid) + nice);
             }
         }
 #endif
     }
 }
 
 namespace {
+
 void
 DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
 {
     XRE_GetIOMessageLoop()
         ->PostTask(FROM_HERE,
                    new DeleteTask<GeckoChildProcessHost>(aSubprocess));
 }
+
+// This runnable only exists to delegate ownership of the
+// ContentParent to this runnable, until it's deleted by the event
+// system.
+struct DelayedDeleteContentParentTask : public nsRunnable
+{
+    DelayedDeleteContentParentTask(ContentParent* aObj) : mObj(aObj) { }
+
+    // No-op
+    NS_IMETHODIMP Run() { return NS_OK; }
+
+    nsRefPtr<ContentParent> mObj;
+};
+
 }
 
 void
 ContentParent::ActorDestroy(ActorDestroyReason why)
 {
     nsCOMPtr<nsIThreadObserver>
         kungFuDeathGrip(static_cast<nsIThreadObserver*>(this));
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@@ -361,16 +376,25 @@ ContentParent::ActorDestroy(ActorDestroy
             obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nsnull);
         }
     }
 
     MessageLoop::current()->
         PostTask(FROM_HERE,
                  NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess));
     mSubprocess = NULL;
+
+    // IPDL rules require actors to live on past ActorDestroy, but it
+    // may be that the kungFuDeathGrip above is the last reference to
+    // |this|.  If so, when we go out of scope here, we're deleted and
+    // all hell breaks loose.
+    //
+    // This runnable ensures that a reference to |this| lives on at
+    // least until after the current task finishes running.
+    NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this));
 }
 
 TabParent*
 ContentParent::CreateTab(PRUint32 aChromeFlags)
 {
   return static_cast<TabParent*>(SendPBrowserConstructor(aChromeFlags));
 }
 
@@ -450,18 +474,20 @@ ContentParent::EnsurePrefService()
                      "We lost prefService in the Chrome process !");
     }
 }
 
 bool
 ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions)
 {
 #ifdef MOZ_PERMISSIONS
-    nsRefPtr<nsPermissionManager> permissionManager =
-        nsPermissionManager::GetSingleton();
+    nsCOMPtr<nsIPermissionManager> permissionManagerIface =
+        do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+    nsPermissionManager* permissionManager =
+        static_cast<nsPermissionManager*>(permissionManagerIface.get());
     NS_ABORT_IF_FALSE(permissionManager,
                  "We have no permissionManager in the Chrome process !");
 
     nsCOMPtr<nsISimpleEnumerator> enumerator;
     nsresult rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
     NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Could not get enumerator!");
     while(1) {
         PRBool hasMore;
--- a/dom/plugins/base/PluginPRLibrary.cpp
+++ b/dom/plugins/base/PluginPRLibrary.cpp
@@ -241,16 +241,27 @@ PluginPRLibrary::NPP_GetSitesWithData(In
 nsresult
 PluginPRLibrary::AsyncSetWindow(NPP instance, NPWindow* window)
 {
   nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
   NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+nsresult
+PluginPRLibrary::HandleGUIEvent(NPP instance, const nsGUIEvent& anEvent,
+                                bool* handled)
+{
+  nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+  NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+#endif
+
 nsresult
 PluginPRLibrary::GetImage(NPP instance, ImageContainer* aContainer, Image** aImage)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 #if defined(XP_MACOSX)
 nsresult
--- a/dom/plugins/base/PluginPRLibrary.h
+++ b/dom/plugins/base/PluginPRLibrary.h
@@ -149,16 +149,20 @@ public:
     NS_OVERRIDE
     virtual nsresult SetBackgroundUnknown(NPP instance);
     NS_OVERRIDE
     virtual nsresult BeginUpdateBackground(NPP instance,
                                            const nsIntRect&, gfxContext** aCtx);
     NS_OVERRIDE
     virtual nsresult EndUpdateBackground(NPP instance,
                                          gfxContext* aCtx, const nsIntRect&);
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+    virtual nsresult HandleGUIEvent(NPP instance,
+                                    const nsGUIEvent& anEvent, bool* handled);
+#endif
 
 private:
     NP_InitializeFunc mNP_Initialize;
     NP_ShutdownFunc mNP_Shutdown;
     NP_GetMIMEDescriptionFunc mNP_GetMIMEDescription;
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
     NP_GetValueFunc mNP_GetValue;
 #endif
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -837,16 +837,33 @@ nsNPAPIPluginInstance::AsyncSetWindow(NP
 
   AutoPluginLibraryCall library(this);
   if (!library)
     return NS_ERROR_FAILURE;
 
   return library->AsyncSetWindow(&mNPP, window);
 }
 
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+nsresult
+nsNPAPIPluginInstance::HandleGUIEvent(const nsGUIEvent& anEvent, bool* handled)
+{
+  if (RUNNING != mRunning) {
+    *handled = false;
+    return NS_OK;
+  }
+
+  AutoPluginLibraryCall library(this);
+  if (!library)
+    return NS_ERROR_FAILURE;
+
+  return library->HandleGUIEvent(&mNPP, anEvent, handled);
+}
+#endif
+
 nsresult
 nsNPAPIPluginInstance::GetImage(ImageContainer* aContainer, Image** aImage)
 {
   *aImage = nsnull;
 
   if (RUNNING != mRunning)
     return NS_OK;
 
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -109,16 +109,19 @@ public:
   nsresult InvalidateRegion(NPRegion invalidRegion);
   nsresult ForceRedraw();
   nsresult GetMIMEType(const char* *result);
   nsresult GetJSContext(JSContext* *outContext);
   nsresult GetOwner(nsIPluginInstanceOwner **aOwner);
   nsresult SetOwner(nsIPluginInstanceOwner *aOwner);
   nsresult ShowStatus(const char* message);
   nsresult InvalidateOwner();
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+  nsresult HandleGUIEvent(const nsGUIEvent& anEvent, bool* handled);
+#endif
 
   nsNPAPIPlugin* GetPlugin();
 
   nsresult GetNPP(NPP * aNPP);
 
   void SetURI(nsIURI* uri);
   nsIURI* GetURI();
 
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1683,16 +1683,39 @@ nsresult nsPluginInstanceOwner::Dispatch
     }
     else NS_ASSERTION(PR_FALSE, "nsPluginInstanceOwner::DispatchFocusToPlugin failed, focusEvent null");   
   }
   else NS_ASSERTION(PR_FALSE, "nsPluginInstanceOwner::DispatchFocusToPlugin failed, privateEvent null");   
   
   return NS_OK;
 }    
 
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+nsresult nsPluginInstanceOwner::Text(nsIDOMEvent* aTextEvent)
+{
+  if (mInstance) {
+    nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aTextEvent));
+    if (privateEvent) {
+      nsEvent *event = privateEvent->GetInternalNSEvent();
+      if (event && event->eventStructType == NS_TEXT_EVENT) {
+        nsEventStatus rv = ProcessEvent(*static_cast<nsGUIEvent*>(event));
+        if (nsEventStatus_eConsumeNoDefault == rv) {
+          aTextEvent->PreventDefault();
+          aTextEvent->StopPropagation();
+        }
+      }
+      else NS_ASSERTION(PR_FALSE, "nsPluginInstanceOwner::DispatchTextToPlugin failed, textEvent null");
+    }
+    else NS_ASSERTION(PR_FALSE, "nsPluginInstanceOwner::DispatchTextToPlugin failed, privateEvent null");
+  }
+
+  return NS_OK;
+}
+#endif
+
 nsresult nsPluginInstanceOwner::KeyPress(nsIDOMEvent* aKeyEvent)
 {
 #ifdef XP_MACOSX
 #ifndef NP_NO_CARBON
   if (GetEventModel() == NPEventModelCarbon) {
     // KeyPress events are really synthesized keyDown events.
     // Here we check the native message of the event so that
     // we won't send the plugin two keyDown events.
@@ -1866,16 +1889,21 @@ nsPluginInstanceOwner::HandleEvent(nsIDO
   }
   if (eventType.EqualsLiteral("keydown") ||
       eventType.EqualsLiteral("keyup")) {
     return DispatchKeyToPlugin(aEvent);
   }
   if (eventType.EqualsLiteral("keypress")) {
     return KeyPress(aEvent);
   }
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+  if (eventType.EqualsLiteral("text")) {
+    return Text(aEvent);
+  }
+#endif
 
   nsCOMPtr<nsIDOMDragEvent> dragEvent(do_QueryInterface(aEvent));
   if (dragEvent && mInstance) {
     nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aEvent));
     if (privateEvent) {
       nsEvent* ievent = privateEvent->GetInternalNSEvent();
       if ((ievent && NS_IS_TRUSTED_EVENT(ievent)) &&
            ievent->message != NS_DRAGDROP_ENTER && ievent->message != NS_DRAGDROP_OVER) {
@@ -2363,20 +2391,39 @@ nsEventStatus nsPluginInstanceOwner::Pro
           event.same_screen = False;
         }
       else
         {
           // If we need to send synthesized key events, then
           // DOMKeyCodeToGdkKeyCode(keyEvent.keyCode) and
           // gdk_keymap_get_entries_for_keyval will be useful, but the
           // mappings will not be unique.
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+          bool handled;
+          if (NS_SUCCEEDED(mInstance->HandleGUIEvent(anEvent, &handled)) &&
+              handled) {
+            rv = nsEventStatus_eConsumeNoDefault;
+          }
+#else
           NS_WARNING("Synthesized key event not sent to plugin");
+#endif
         }
       break;
 
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+   case NS_TEXT_EVENT:
+        {
+          bool handled;
+          if (NS_SUCCEEDED(mInstance->HandleGUIEvent(anEvent, &handled)) &&
+              handled) {
+            rv = nsEventStatus_eConsumeNoDefault;
+          }
+        }
+      break;
+#endif
     default: 
       switch (anEvent.message)
         {
         case NS_FOCUS_CONTENT:
         case NS_BLUR_CONTENT:
           {
             XFocusChangeEvent &event = pluginEvent.xfocus;
             event.type =
@@ -2447,16 +2494,19 @@ nsPluginInstanceOwner::Destroy()
   mContent->RemoveEventListener(NS_LITERAL_STRING("drag"), this, PR_TRUE);
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragenter"), this, PR_TRUE);
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragover"), this, PR_TRUE);
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragleave"), this, PR_TRUE);
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragexit"), this, PR_TRUE);
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragstart"), this, PR_TRUE);
   mContent->RemoveEventListener(NS_LITERAL_STRING("draggesture"), this, PR_TRUE);
   mContent->RemoveEventListener(NS_LITERAL_STRING("dragend"), this, PR_TRUE);
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+  mContent->RemoveEventListener(NS_LITERAL_STRING("text"), this, PR_TRUE);
+#endif
 
   if (mWidget) {
     nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
     if (pluginWidget)
       pluginWidget->SetPluginInstanceOwner(nsnull);
 
     if (mDestroyWidget)
       mWidget->Destroy();
@@ -2907,16 +2957,19 @@ nsresult nsPluginInstanceOwner::Init(nsP
   mContent->AddEventListener(NS_LITERAL_STRING("drag"), this, PR_TRUE);
   mContent->AddEventListener(NS_LITERAL_STRING("dragenter"), this, PR_TRUE);
   mContent->AddEventListener(NS_LITERAL_STRING("dragover"), this, PR_TRUE);
   mContent->AddEventListener(NS_LITERAL_STRING("dragleave"), this, PR_TRUE);
   mContent->AddEventListener(NS_LITERAL_STRING("dragexit"), this, PR_TRUE);
   mContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, PR_TRUE);
   mContent->AddEventListener(NS_LITERAL_STRING("draggesture"), this, PR_TRUE);
   mContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, PR_TRUE);
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+  mContent->AddEventListener(NS_LITERAL_STRING("text"), this, PR_TRUE);
+#endif
   
   // Register scroll position listeners
   // We need to register a scroll position listener on every scrollable
   // frame up to the top
   for (nsIFrame* f = mObjectFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
     nsIScrollableFrame* sf = do_QueryFrame(f);
     if (sf) {
       sf->AddScrollPositionListener(this);
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -64,17 +64,17 @@
 #endif
 
 #ifdef XP_MACOSX
 #include "nsCoreAnimationSupport.h"
 #include <ApplicationServices/ApplicationServices.h>
 #endif
 
 class nsIInputStream;
-class nsIntRect;
+struct nsIntRect;
 class nsPluginDOMContextMenuListener;
 class nsObjectFrame;
 class nsDisplayListBuilder;
 
 #ifdef MOZ_X11
 class gfxXlibSurface;
 #endif
 
@@ -125,16 +125,19 @@ public:
   //nsIPluginTagInfo interface
   NS_DECL_NSIPLUGINTAGINFO
   
   // nsIDOMEventListener interfaces 
   NS_DECL_NSIDOMEVENTLISTENER
   
   nsresult MouseDown(nsIDOMEvent* aKeyEvent);
   nsresult KeyPress(nsIDOMEvent* aKeyEvent);
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+  nsresult Text(nsIDOMEvent* aTextEvent);
+#endif
 
   nsresult Destroy();  
   
   void PrepareToStop(PRBool aDelayedStop);
   
 #ifdef XP_WIN
   void Paint(const RECT& aDirty, HDC aDC);
 #elif defined(XP_MACOSX)
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -41,29 +41,32 @@ include protocol PPluginBackgroundDestro
 include protocol PPluginModule;
 include protocol PPluginScriptableObject;
 include protocol PBrowserStream;
 include protocol PPluginStream;
 include protocol PStreamNotify;
 include protocol PPluginSurface;
 
 include "mozilla/plugins/PluginMessageUtils.h";
+include "IPC/nsGUIEventIPC.h";
 
 using NPError;
 using NPRemoteWindow;
 using NPRemoteEvent;
 using NPRect;
 using NPNURLVariable;
 using NPCoordinateSpace;
 using mozilla::plugins::NativeWindowHandle;
 using mozilla::gfxSurfaceType;
 using gfxIntSize;
 using mozilla::null_t;
 using mozilla::plugins::WindowsSharedMemoryHandle;
 using nsIntRect;
+using nsTextEvent;
+using nsKeyEvent;
 
 namespace mozilla {
 namespace plugins {
 
 struct SurfaceDescriptorX11 {
   int XID;
   int xrenderPictID;
   gfxIntSize size;
@@ -142,16 +145,21 @@ child:
 
   // There is now an opaque background behind this instance (or the
   // background was updated).  The changed area is |rect|.  The
   // browser owns the background surface, and it's read-only from
   // within the plugin process.  |background| is either null_t to
   // refer to the existing background or a fresh descriptor.
   async UpdateBackground(SurfaceDescriptor background, nsIntRect rect);
 
+  rpc HandleTextEvent(nsTextEvent event)
+    returns (bool handled);
+  rpc HandleKeyEvent(nsKeyEvent event)
+    returns (bool handled);
+
   rpc NPP_Destroy()
     returns (NPError rv);
 
 parent:
   rpc NPN_GetValue_NPNVjavascriptEnabledBool()
     returns (bool value, NPError result);
   rpc NPN_GetValue_NPNVisOfflineBool()
     returns (bool value, NPError result);
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -32,16 +32,24 @@
  * 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 ***** */
 
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+#include <QEvent>
+#include <QKeyEvent>
+#include <QApplication>
+#include <QInputMethodEvent>
+#include "nsQtKeyUtils.h"
+#endif
+
 #include "PluginBackgroundDestroyer.h"
 #include "PluginInstanceChild.h"
 #include "PluginModuleChild.h"
 #include "BrowserStreamChild.h"
 #include "PluginStreamChild.h"
 #include "StreamNotifyChild.h"
 #include "PluginProcessChild.h"
 #include "gfxASurface.h"
@@ -69,16 +77,18 @@ using namespace mozilla::plugins;
 
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 #include <gdk/gdk.h>
 #include "gtk2xtbin.h"
 
 #elif defined(MOZ_WIDGET_QT)
 #include <QX11Info>
+#undef KeyPress
+#undef KeyRelease
 #elif defined(OS_WIN)
 #ifndef WM_MOUSEHWHEEL
 #define WM_MOUSEHWHEEL     0x020E
 #endif
 
 #include "nsWindowsDllInterceptor.h"
 
 typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu,
@@ -2312,16 +2322,67 @@ PluginInstanceChild::RecvAsyncSetWindow(
         MessageLoop::current()->PostTask(FROM_HERE, mCurrentAsyncSetWindowTask);
     } else {
         DoAsyncSetWindow(aSurfaceType, aWindow, false);
     }
 
     return true;
 }
 
+bool
+PluginInstanceChild::AnswerHandleKeyEvent(const nsKeyEvent& aKeyEvent,
+                                          bool* handled)
+{
+    AssertPluginThread();
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+    Qt::KeyboardModifiers modifier;
+    if (aKeyEvent.isShift)
+        modifier |= Qt::ShiftModifier;
+    if (aKeyEvent.isControl)
+        modifier |= Qt::ControlModifier;
+    if (aKeyEvent.isAlt)
+        modifier |= Qt::AltModifier;
+    if (aKeyEvent.isMeta)
+        modifier |= Qt::MetaModifier;
+
+    QEvent::Type type;
+    if (aKeyEvent.message == NS_KEY_DOWN) {
+        type = QEvent::KeyPress;
+    } else if (aKeyEvent.message == NS_KEY_UP) {
+        type = QEvent::KeyRelease;
+    } else {
+        *handled = false;
+        return true;
+    }
+    QKeyEvent keyEv(type, DOMKeyCodeToQtKeyCode(aKeyEvent.keyCode), modifier);
+    *handled = QApplication::sendEvent(qApp, &keyEv);
+#else
+    NS_ERROR("Not implemented");
+#endif
+
+    return true;
+}
+
+bool
+PluginInstanceChild::AnswerHandleTextEvent(const nsTextEvent& aEvent,
+                                           bool* handled)
+{
+    AssertPluginThread();
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+    QInputMethodEvent event;
+    event.setCommitString(QString((const QChar*)aEvent.theText.get(),
+                          aEvent.theText.Length()));
+    *handled = QApplication::sendEvent(qApp, &event);
+#else
+    NS_ERROR("Not implemented");
+#endif
+
+    return true;
+}
+
 void
 PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
                                       const NPRemoteWindow& aWindow,
                                       bool aIsAsync)
 {
     PLUGIN_LOG_DEBUG(
         ("[InstanceChild][%p] AsyncSetWindow to <x=%d,y=%d, w=%d,h=%d>",
          this, aWindow.x, aWindow.y, aWindow.width, aWindow.height));
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -115,16 +115,21 @@ protected:
     RecvAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
                        const NPRemoteWindow& aWindow);
 
     virtual void
     DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
                      const NPRemoteWindow& aWindow,
                      bool aIsAsync);
 
+    virtual bool
+    AnswerHandleKeyEvent(const nsKeyEvent& aEvent, bool* handled);
+    virtual bool
+    AnswerHandleTextEvent(const nsTextEvent& aEvent, bool* handled);
+
     virtual PPluginSurfaceChild* AllocPPluginSurface(const WindowsSharedMemoryHandle&,
                                                      const gfxIntSize&, const bool&) {
         return new PPluginSurfaceChild();
     }
 
     virtual bool DeallocPPluginSurface(PPluginSurfaceChild* s) {
         delete s;
         return true;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -613,16 +613,41 @@ PluginInstanceParent::AsyncSetWindow(NPW
     window.type = aWindow->type;
     if (!SendAsyncSetWindow(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType(),
                             window))
         return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+nsresult
+PluginInstanceParent::HandleGUIEvent(const nsGUIEvent& anEvent, bool* handled)
+{
+    switch (anEvent.eventStructType) {
+    case NS_KEY_EVENT:
+        if (!CallHandleKeyEvent(static_cast<const nsKeyEvent&>(anEvent),
+                                handled)) {
+            return NS_ERROR_FAILURE;
+        }
+        break;
+    case NS_TEXT_EVENT:
+        if (!CallHandleTextEvent(static_cast<const nsTextEvent&>(anEvent),
+                                 handled)) {
+            return NS_ERROR_FAILURE;
+        }
+        break;
+    default:
+        NS_ERROR("Not implemented for this event type");
+        return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
+}
+#endif
+
 nsresult
 PluginInstanceParent::GetImage(ImageContainer* aContainer, Image** aImage)
 {
 #ifdef XP_MACOSX
     nsIOSurface* ioSurface = NULL;
   
     if (mFrontIOSurface) {
       ioSurface = mFrontIOSurface;
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -52,16 +52,17 @@
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsRect.h"
 #include "gfxASurface.h"
 #include "ImageLayers.h"
 #ifdef MOZ_X11
 class gfxXlibSurface;
 #endif
+#include "nsGUIEvent.h"
 
 namespace mozilla {
 namespace plugins {
 
 class PBrowserStreamParent;
 class PluginModuleParent;
 
 class PluginInstanceParent : public PPluginInstanceParent
@@ -285,16 +286,19 @@ public:
 #ifdef XP_MACOSX
     nsresult IsRemoteDrawingCoreAnimation(PRBool *aDrawing);
 #endif
     nsresult SetBackgroundUnknown();
     nsresult BeginUpdateBackground(const nsIntRect& aRect,
                                    gfxContext** aCtx);
     nsresult EndUpdateBackground(gfxContext* aCtx,
                                  const nsIntRect& aRect);
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+    nsresult HandleGUIEvent(const nsGUIEvent& anEvent, bool* handled);
+#endif
 
 private:
     // Create an appropriate platform surface for a background of size
     // |aSize|.  Return true if successful.
     bool CreateBackground(const nsIntSize& aSize);
     void DestroyBackground();
     SurfaceDescriptor BackgroundDescriptor() /*const*/;
 
--- a/dom/plugins/ipc/PluginLibrary.h
+++ b/dom/plugins/ipc/PluginLibrary.h
@@ -47,16 +47,17 @@
 #include "nsPluginError.h"
 
 class gfxASurface;
 class gfxContext;
 class nsCString;
 struct nsIntRect;
 struct nsIntSize;
 class nsNPAPIPlugin;
+class nsGUIEvent;
 
 namespace mozilla {
 namespace layers {
 class Image;
 class ImageContainer;
 }
 }
 
@@ -111,14 +112,17 @@ public:
    * PluginInstanceParent.  They approximately follow the ReadbackSink
    * API.
    */
   virtual nsresult SetBackgroundUnknown(NPP instance) = 0;
   virtual nsresult BeginUpdateBackground(NPP instance,
                                          const nsIntRect&, gfxContext**) = 0;
   virtual nsresult EndUpdateBackground(NPP instance,
                                        gfxContext*, const nsIntRect&) = 0;
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+  virtual nsresult HandleGUIEvent(NPP instance, const nsGUIEvent&, bool*) = 0;
+#endif
 };
 
 
 } // namespace mozilla
 
 #endif  // ifndef mozilla_PluginLibrary_h
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -698,16 +698,30 @@ PluginModuleParent::AsyncSetWindow(NPP i
 {
     PluginInstanceParent* i = InstCast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->AsyncSetWindow(window);
 }
 
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+nsresult
+PluginModuleParent::HandleGUIEvent(NPP instance,
+                                   const nsGUIEvent& anEvent,
+                                   bool* handled)
+{
+    PluginInstanceParent* i = InstCast(instance);
+    if (!i)
+        return NS_ERROR_FAILURE;
+
+    return i->HandleGUIEvent(anEvent, handled);
+}
+#endif
+
 nsresult
 PluginModuleParent::GetImage(NPP instance,
                              mozilla::layers::ImageContainer* aContainer,
                              mozilla::layers::Image** aImage)
 {
     PluginInstanceParent* i = InstCast(instance);
     return !i ? NS_ERROR_FAILURE : i->GetImage(aContainer, aImage);
 }
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -291,16 +291,20 @@ private:
                              NPError* error);
     virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
                                        uint64_t maxAge);
     virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result);
 
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, PRBool *aDrawing);
 #endif
+#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
+    virtual nsresult HandleGUIEvent(NPP instance, const nsGUIEvent& anEvent,
+                                    bool* handled);
+#endif
 
 private:
     void WritePluginExtraDataForMinidump(const nsAString& id);
     void WriteExtraDataForHang();
     void CleanupFromTimeout();
     static int TimeoutChanged(const char* aPref, void* aModule);
     void NotifyPluginCrashed();
 
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -0,0 +1,1608 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** 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 worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jscntxt.h"
+
+#include "nsDOMThreadService.h"
+
+// Interfaces
+#include "nsIComponentManager.h"
+#include "nsIConsoleService.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIEventTarget.h"
+#include "nsIJSContextStack.h"
+#include "nsIJSRuntimeService.h"
+#include "nsIObserverService.h"
+#include "nsIScriptError.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIServiceManager.h"
+#include "nsISupportsPriority.h"
+#include "nsIThreadPool.h"
+#include "nsIXPConnect.h"
+#include "nsPIDOMWindow.h"
+
+// Other includes
+#include "nsAutoPtr.h"
+#include "nsContentUtils.h"
+#include "nsDeque.h"
+#include "nsGlobalWindow.h"
+#include "nsIClassInfoImpl.h"
+#include "nsStringBuffer.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMCID.h"
+#include "nsXPCOMCIDInternal.h"
+#include "pratom.h"
+#include "prthread.h"
+#include "mozilla/Preferences.h"
+
+// DOMWorker includes
+#include "nsDOMWorker.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerMacros.h"
+#include "nsDOMWorkerMessageHandler.h"
+#include "nsDOMWorkerPool.h"
+#include "nsDOMWorkerSecurityManager.h"
+#include "nsDOMWorkerTimeout.h"
+
+using namespace mozilla;
+
+#ifdef PR_LOGGING
+PRLogModuleInfo *gDOMThreadsLog = nsnull;
+#endif
+#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
+
+// The maximum number of threads in the internal thread pool
+#define THREADPOOL_MAX_THREADS 3
+
+PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= 1);
+
+// The maximum number of idle threads in the internal thread pool
+#define THREADPOOL_IDLE_THREADS 3
+
+PR_STATIC_ASSERT(THREADPOOL_MAX_THREADS >= THREADPOOL_IDLE_THREADS);
+
+// As we suspend threads for various reasons (navigating away from the page,
+// loading scripts, etc.) we open another slot in the thread pool for another
+// worker to use. We can't do this forever so we set an absolute cap on the
+// number of threads we'll allow to prevent DOS attacks.
+#define THREADPOOL_THREAD_CAP 20
+
+PR_STATIC_ASSERT(THREADPOOL_THREAD_CAP >= THREADPOOL_MAX_THREADS);
+
+// A "bad" value for the NSPR TLS functions.
+#define BAD_TLS_INDEX (PRUintn)-1
+
+// Easy access for static functions. No reference here.
+static nsDOMThreadService* gDOMThreadService = nsnull;
+
+// These pointers actually carry references and must be released.
+static nsIObserverService* gObserverService = nsnull;
+static nsIJSRuntimeService* gJSRuntimeService = nsnull;
+static nsIThreadJSContextStack* gThreadJSContextStack = nsnull;
+static nsIXPCSecurityManager* gWorkerSecurityManager = nsnull;
+
+PRUintn gJSContextIndex = BAD_TLS_INDEX;
+
+static const char* sPrefsToWatch[] = {
+  "dom.max_script_run_time"
+};
+
+// The length of time the close handler is allowed to run in milliseconds.
+static PRUint32 gWorkerCloseHandlerTimeoutMS = 10000;
+
+/**
+ * Simple class to automatically destroy a JSContext to make error handling
+ * easier.
+ */
+class JSAutoContextDestroyer
+{
+public:
+  JSAutoContextDestroyer(JSContext* aCx)
+  : mCx(aCx) { }
+
+  ~JSAutoContextDestroyer() {
+    if (mCx) {
+      nsContentUtils::XPConnect()->ReleaseJSContext(mCx, PR_TRUE);
+    }
+  }
+
+  operator JSContext*() {
+    return mCx;
+  }
+
+  JSContext* forget() {
+    JSContext* cx = mCx;
+    mCx = nsnull;
+    return cx;
+  }
+
+private:
+  JSContext* mCx;
+};
+
+class nsDestroyJSContextRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDestroyJSContextRunnable(JSContext* aCx)
+  : mCx(aCx)
+  {
+    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    NS_ASSERTION(aCx, "Null pointer!");
+    NS_ASSERTION(!JS_GetGlobalObject(aCx), "Should not have a global!");
+
+    // We're removing this context from this thread. Let the JS engine know.
+    JS_ClearContextThread(aCx);
+  }
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    // We're about to use this context on this thread. Let the JS engine know.
+    if (!!JS_SetContextThread(mCx)) {
+      NS_WARNING("JS_SetContextThread failed!");
+    }
+
+    if (nsContentUtils::XPConnect()) {
+      nsContentUtils::XPConnect()->ReleaseJSContext(mCx, PR_TRUE);
+    }
+    else {
+      NS_WARNING("Failed to release JSContext!");
+    }
+
+    return NS_OK;
+  }
+
+private:
+  JSContext* mCx;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDestroyJSContextRunnable, nsIRunnable)
+
+/**
+ * This class is used as to post an error to the worker's outer handler.
+ */
+class nsReportErrorRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsReportErrorRunnable(nsDOMWorker* aWorker,
+                        nsIScriptError* aScriptError)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()),
+    mScriptError(aScriptError) {
+      NS_ASSERTION(aScriptError, "Null pointer!");
+    }
+
+  NS_IMETHOD Run() {
+    if (mWorker->IsCanceled()) {
+      return NS_OK;
+    }
+
+#ifdef DEBUG
+    {
+      nsRefPtr<nsDOMWorker> parent = mWorker->GetParent();
+      if (NS_IsMainThread()) {
+        NS_ASSERTION(!parent, "Shouldn't have a parent on the main thread!");
+      }
+      else {
+        NS_ASSERTION(parent, "Should have a parent!");
+
+        JSContext* cx = nsDOMThreadService::get()->GetCurrentContext();
+        NS_ASSERTION(cx, "No context!");
+
+        nsDOMWorker* currentWorker = (nsDOMWorker*)JS_GetContextPrivate(cx);
+        NS_ASSERTION(currentWorker == parent, "Wrong worker!");
+      }
+    }
+#endif
+
+    NS_NAMED_LITERAL_STRING(errorStr, "error");
+
+    nsresult rv;
+
+    if (mWorker->HasListeners(errorStr)) {
+      // Construct the error event.
+      nsString message;
+      rv = mScriptError->GetErrorMessage(message);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsString filename;
+      rv = mScriptError->GetSourceName(filename);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      PRUint32 lineno;
+      rv = mScriptError->GetLineNumber(&lineno);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsRefPtr<nsDOMWorkerErrorEvent> event(new nsDOMWorkerErrorEvent());
+      NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+      rv = event->InitErrorEvent(errorStr, PR_FALSE, PR_TRUE, message,
+                                 filename, lineno);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      event->SetTarget(static_cast<nsDOMWorkerMessageHandler*>(mWorker));
+
+      PRBool stopPropagation = PR_FALSE;
+      rv = mWorker->DispatchEvent(static_cast<nsDOMWorkerEvent*>(event),
+                                  &stopPropagation);
+      if (NS_SUCCEEDED(rv) && stopPropagation) {
+        return NS_OK;
+      }
+    }
+
+    nsRefPtr<nsDOMWorker> parent = mWorker->GetParent();
+    if (!parent) {
+      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+      nsCOMPtr<nsIConsoleService> consoleService =
+        do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+      if (consoleService) {
+        rv = consoleService->LogMessage(mScriptError);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      return NS_OK;
+    }
+
+    nsRefPtr<nsReportErrorRunnable> runnable =
+      new nsReportErrorRunnable(parent, mScriptError);
+    if (runnable) {
+      nsRefPtr<nsDOMWorker> grandparent = parent->GetParent();
+      rv = grandparent ?
+           nsDOMThreadService::get()->Dispatch(grandparent, runnable) :
+           NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsDOMWorker> mWorker;
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+  nsCOMPtr<nsIScriptError> mScriptError;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsReportErrorRunnable, nsIRunnable)
+
+/**
+ * Used to post an expired timeout to the correct worker.
+ */
+class nsDOMWorkerTimeoutRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerTimeoutRunnable(nsDOMWorkerTimeout* aTimeout)
+  : mTimeout(aTimeout) { }
+
+  NS_IMETHOD Run() {
+    return mTimeout->Run();
+  }
+protected:
+  nsRefPtr<nsDOMWorkerTimeout> mTimeout;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerTimeoutRunnable, nsIRunnable)
+
+class nsDOMWorkerKillRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerKillRunnable(nsDOMWorker* aWorker)
+  : mWorker(aWorker) { }
+
+  NS_IMETHOD Run() {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    mWorker->Kill();
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsDOMWorker> mWorker;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerKillRunnable, nsIRunnable)
+
+/**
+ * This class exists to solve a particular problem: Calling Dispatch on a
+ * thread pool will always create a new thread to service the runnable as long
+ * as the thread limit has not been reached. Since our DOM workers can only be
+ * accessed by one thread at a time we could end up spawning a new thread that
+ * does nothing but wait initially. There is no way to control this behavior
+ * currently so we cheat by using a runnable that emulates a thread. The
+ * nsDOMThreadService's monitor protects the queue of events.
+ */
+class nsDOMWorkerRunnable : public nsIRunnable
+{
+  friend class nsDOMThreadService;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerRunnable(nsDOMWorker* aWorker)
+  : mWorker(aWorker), mCloseTimeoutInterval(0), mKillWorkerWhenDone(PR_FALSE) {
+  }
+
+  virtual ~nsDOMWorkerRunnable() {
+    ClearQueue();
+  }
+
+  void PutRunnable(nsIRunnable* aRunnable,
+                   PRIntervalTime aTimeoutInterval,
+                   PRBool aClearQueue) {
+    NS_ASSERTION(aRunnable, "Null pointer!");
+
+    gDOMThreadService->mReentrantMonitor.AssertCurrentThreadIn();
+
+    if (NS_LIKELY(!aTimeoutInterval)) {
+      NS_ADDREF(aRunnable);
+      mRunnables.Push(aRunnable);
+    }
+    else {
+      NS_ASSERTION(!mCloseRunnable, "More than one close runnable?!");
+      if (aClearQueue) {
+        ClearQueue();
+      }
+      mCloseRunnable = aRunnable;
+      mCloseTimeoutInterval = aTimeoutInterval;
+      mKillWorkerWhenDone = PR_TRUE;
+    }
+  }
+
+  void SetCloseRunnableTimeout(PRIntervalTime aTimeoutInterval) {
+    NS_ASSERTION(aTimeoutInterval, "No timeout specified!");
+    NS_ASSERTION(aTimeoutInterval!= PR_INTERVAL_NO_TIMEOUT, "Bad timeout!");
+
+    // No need to enter the monitor because we should already be in it.
+
+    NS_ASSERTION(mWorker->GetExpirationTime() == PR_INTERVAL_NO_TIMEOUT,
+                 "Asked to set timeout on a runnable with no close handler!");
+
+    // This may actually overflow but we don't care - the worst that could
+    // happen is that the close handler could run for a slightly different
+    // amount of time and the spec leaves the time up to us anyway.
+    mWorker->SetExpirationTime(PR_IntervalNow() + aTimeoutInterval);
+  }
+
+  NS_IMETHOD Run() {
+    NS_ASSERTION(!NS_IsMainThread(),
+                 "This should *never* run on the main thread!");
+
+    // This must have been set up by the thread service
+    NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+    // Make sure we have a JSContext to run everything on.
+    JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+    if (!cx) {
+        NS_ERROR("nsDOMThreadService didn't give us a context! Are we out of memory?");
+        return NS_ERROR_FAILURE;
+    }
+
+    NS_ASSERTION(!JS_GetGlobalObject(cx), "Shouldn't have a global!");
+
+    if (mWorker->IsPrivileged()) {
+      JS_SetVersion(cx, JSVERSION_LATEST);
+    }
+    else {
+      JS_SetVersion(cx, JSVERSION_DEFAULT);
+    }
+
+    JS_SetContextPrivate(cx, mWorker);
+
+    // Go ahead and trigger the operation callback for this context before we
+    // try to run any JS. That way we'll be sure to cancel or suspend as soon as
+    // possible if the compilation takes too long.
+    JS_TriggerOperationCallback(cx);
+
+    PRBool killWorkerWhenDone;
+    {
+      nsLazyAutoRequest ar;
+      JSAutoEnterCompartment ac;
+
+      // Tell the worker which context it will be using
+      if (mWorker->SetGlobalForContext(cx, &ar, &ac)) {
+        NS_ASSERTION(ar.entered(), "SetGlobalForContext must enter request on success");
+        NS_ASSERTION(ac.entered(), "SetGlobalForContext must enter compartment on success");
+
+        RunQueue(cx, &killWorkerWhenDone);
+
+        // Remove the global object from the context so that it might be garbage
+        // collected.
+        JS_SetGlobalObject(cx, NULL);
+        JS_SetContextPrivate(cx, NULL);
+      }
+      else {
+        NS_ASSERTION(!ar.entered(), "SetGlobalForContext must not enter request on failure");
+        NS_ASSERTION(!ac.entered(), "SetGlobalForContext must not enter compartment on failure");
+
+        {
+          // Code in XPConnect assumes that the context's global object won't be
+          // replaced outside of a request.
+          JSAutoRequest ar2(cx);
+
+          // This is usually due to a parse error in the worker script...
+          JS_SetGlobalObject(cx, NULL);
+          JS_SetContextPrivate(cx, NULL);
+        }
+
+        ReentrantMonitorAutoEnter mon(gDOMThreadService->mReentrantMonitor);
+        killWorkerWhenDone = mKillWorkerWhenDone;
+        gDOMThreadService->WorkerComplete(this);
+        mon.NotifyAll();
+      }
+    }
+
+    if (killWorkerWhenDone) {
+      nsCOMPtr<nsIRunnable> runnable = new nsDOMWorkerKillRunnable(mWorker);
+      NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+      nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    return NS_OK;
+  }
+
+protected:
+  void ClearQueue() {
+    nsCOMPtr<nsIRunnable> runnable;
+    while ((runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront()))) {
+      // Loop until all the runnables are dead.
+    }
+  }
+
+  void RunQueue(JSContext* aCx, PRBool* aCloseRunnableSet) {
+    while (1) {
+      nsCOMPtr<nsIRunnable> runnable;
+      {
+        ReentrantMonitorAutoEnter mon(gDOMThreadService->mReentrantMonitor);
+
+        runnable = dont_AddRef((nsIRunnable*)mRunnables.PopFront());
+
+        if (!runnable && mCloseRunnable) {
+          PRIntervalTime expirationTime;
+          if (mCloseTimeoutInterval == PR_INTERVAL_NO_TIMEOUT) {
+            expirationTime = mCloseTimeoutInterval;
+          }
+          else {
+            expirationTime = PR_IntervalNow() + mCloseTimeoutInterval;
+          }
+          mWorker->SetExpirationTime(expirationTime);
+
+          runnable.swap(mCloseRunnable);
+        }
+
+        if (!runnable || mWorker->IsCanceled()) {
+#ifdef PR_LOGGING
+          if (mWorker->IsCanceled()) {
+            LOG(("Bailing out of run loop for canceled worker[0x%p]",
+                 static_cast<void*>(mWorker.get())));
+          }
+#endif
+          *aCloseRunnableSet = mKillWorkerWhenDone;
+          gDOMThreadService->WorkerComplete(this);
+          mon.NotifyAll();
+          return;
+        }
+      }
+
+      // Clear out any old cruft hanging around in the regexp statics.
+      if (JSObject *global = JS_GetGlobalObject(aCx))
+          JS_ClearRegExpStatics(aCx, global);
+
+      runnable->Run();
+    }
+    NS_NOTREACHED("Shouldn't ever get here!");
+  }
+
+  // Set at construction
+  nsRefPtr<nsDOMWorker> mWorker;
+
+  // Protected by mReentrantMonitor
+  nsDeque mRunnables;
+  nsCOMPtr<nsIRunnable> mCloseRunnable;
+  PRIntervalTime mCloseTimeoutInterval;
+  PRBool mKillWorkerWhenDone;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerRunnable, nsIRunnable)
+
+/*******************************************************************************
+ * JS environment function and callbacks
+ */
+
+JSBool
+DOMWorkerOperationCallback(JSContext* aCx)
+{
+  nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+  NS_ASSERTION(worker, "This must never be null!");
+
+  PRBool canceled = worker->IsCanceled();
+  if (!canceled && worker->IsSuspended()) {
+    JSAutoSuspendRequest suspended(aCx);
+
+    // Since we're going to block this thread we should open up a new thread
+    // in the thread pool for other workers. Must check the return value to
+    // make sure we don't decrement when we failed.
+    PRBool extraThreadAllowed =
+      NS_SUCCEEDED(gDOMThreadService->ChangeThreadPoolMaxThreads(1));
+
+    // Flush JIT caches now before suspending to avoid holding memory that we
+    // are not going to use.
+    JS_FlushCaches(aCx);
+
+    for (;;) {
+      ReentrantMonitorAutoEnter mon(worker->Pool()->GetReentrantMonitor());
+
+      // There's a small chance that the worker was canceled after our check
+      // above in which case we shouldn't wait here. We're guaranteed not to
+      // race here because the pool reenters its monitor after canceling each
+      // worker in order to notify its condition variable.
+      canceled = worker->IsCanceled();
+      if (!canceled && worker->IsSuspended()) {
+        mon.Wait();
+      }
+      else {
+        break;
+      }
+    }
+
+    if (extraThreadAllowed) {
+      gDOMThreadService->ChangeThreadPoolMaxThreads(-1);
+    }
+  }
+
+  if (canceled) {
+    LOG(("Forcefully killing JS for worker [0x%p]",
+         static_cast<void*>(worker)));
+    // Kill execution of the currently running JS.
+    JS_ClearPendingException(aCx);
+    return JS_FALSE;
+  }
+  return JS_TRUE;
+}
+
+void
+DOMWorkerErrorReporter(JSContext* aCx,
+                       const char* aMessage,
+                       JSErrorReport* aReport)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Huh?!");
+
+  nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(aCx);
+
+  if (worker->IsCanceled()) {
+    // We don't want to report errors from canceled workers. It's very likely
+    // that we only returned an error in the first place because the worker was
+    // already canceled.
+    return;
+  }
+
+  if (worker->mErrorHandlerRecursionCount == 2) {
+    // We've somehow ended up in a recursive onerror loop. Bail out.
+    return;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIScriptError> scriptError;
+
+  {
+    // CreateInstance will lock, make sure we suspend our request!
+    JSAutoSuspendRequest ar(aCx);
+
+    scriptError = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+  }
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIScriptError2> scriptError2(do_QueryInterface(scriptError));
+
+  nsAutoString message, filename, line;
+  PRUint32 lineNumber, columnNumber, flags, errorNumber;
+
+  if (aReport) {
+    if (aReport->ucmessage) {
+      message.Assign(reinterpret_cast<const PRUnichar*>(aReport->ucmessage));
+    }
+    filename.AssignWithConversion(aReport->filename);
+    line.Assign(reinterpret_cast<const PRUnichar*>(aReport->uclinebuf));
+    lineNumber = aReport->lineno;
+    columnNumber = aReport->uctokenptr - aReport->uclinebuf;
+    flags = aReport->flags;
+    errorNumber = aReport->errorNumber;
+  }
+  else {
+    lineNumber = columnNumber = errorNumber = 0;
+    flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
+  }
+
+  if (message.IsEmpty()) {
+    message.AssignWithConversion(aMessage);
+  }
+
+  rv = scriptError2->InitWithWindowID(message.get(), filename.get(), line.get(),
+                                      lineNumber, columnNumber, flags,
+                                      "DOM Worker javascript",
+                                      worker->Pool()->WindowID());
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  // Don't call the error handler if we're out of stack space.
+  if (errorNumber != JSMSG_OVER_RECURSED) {
+    // Try the onerror handler for the worker's scope.
+    nsRefPtr<nsDOMWorkerScope> scope = worker->GetInnerScope();
+    NS_ASSERTION(scope, "Null scope!");
+
+    PRBool hasListeners = scope->HasListeners(NS_LITERAL_STRING("error"));
+    if (hasListeners) {
+      nsRefPtr<nsDOMWorkerErrorEvent> event(new nsDOMWorkerErrorEvent());
+      if (event) {
+        rv = event->InitErrorEvent(NS_LITERAL_STRING("error"), PR_FALSE,
+                                   PR_TRUE, nsDependentString(message),
+                                   filename, lineNumber);
+        if (NS_SUCCEEDED(rv)) {
+          event->SetTarget(scope);
+
+          NS_ASSERTION(worker->mErrorHandlerRecursionCount >= 0,
+                       "Bad recursion count logic!");
+          worker->mErrorHandlerRecursionCount++;
+
+          PRBool preventDefaultCalled = PR_FALSE;
+          scope->DispatchEvent(static_cast<nsDOMWorkerEvent*>(event),
+                               &preventDefaultCalled);
+
+          worker->mErrorHandlerRecursionCount--;
+
+          if (preventDefaultCalled) {
+            return;
+          }
+        }
+      }
+    }
+  }
+
+  // Still unhandled, fire at the onerror handler on the worker.
+  nsCOMPtr<nsIRunnable> runnable =
+    new nsReportErrorRunnable(worker, scriptError);
+  NS_ENSURE_TRUE(runnable,);
+
+  nsRefPtr<nsDOMWorker> parent = worker->GetParent();
+
+  // If this worker has a parent then we need to send the message through the
+  // thread service to be run on the parent's thread. Otherwise it is a
+  // top-level worker and we send the message to the main thread.
+  rv = parent ? nsDOMThreadService::get()->Dispatch(parent, runnable)
+              : NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+}
+
+/*******************************************************************************
+ * nsDOMThreadService
+ */
+
+nsDOMThreadService::nsDOMThreadService()
+: mReentrantMonitor("nsDOMThreadServer.mReentrantMonitor"),
+  mNavigatorStringsLoaded(PR_FALSE)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+#ifdef PR_LOGGING
+  if (!gDOMThreadsLog) {
+    gDOMThreadsLog = PR_NewLogModule("nsDOMThreads");
+  }
+#endif
+  LOG(("Initializing DOM Thread service"));
+}
+
+nsDOMThreadService::~nsDOMThreadService()
+{
+  LOG(("DOM Thread service destroyed"));
+
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  Cleanup();
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThreadService, nsIEventTarget,
+                                                  nsIObserver,
+                                                  nsIThreadPoolListener)
+
+nsresult
+nsDOMThreadService::Init()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!gDOMThreadService, "Only one instance should ever be created!");
+
+  nsresult rv;
+  nsCOMPtr<nsIObserverService> obs =
+    do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  obs.forget(&gObserverService);
+
+  RegisterPrefCallbacks();
+
+  mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mThreadPool->SetListener(this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mThreadPool->SetThreadLimit(THREADPOOL_MAX_THREADS);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mThreadPool->SetIdleThreadLimit(THREADPOOL_IDLE_THREADS);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRBool success = mWorkersInProgress.Init();
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  success = mPools.Init();
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  success = mThreadsafeContractIDs.Init();
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  success = mJSContexts.SetCapacity(THREADPOOL_THREAD_CAP);
+  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+
+  nsCOMPtr<nsIJSRuntimeService>
+    runtimeSvc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
+  NS_ENSURE_TRUE(runtimeSvc, NS_ERROR_FAILURE);
+  runtimeSvc.forget(&gJSRuntimeService);
+
+  nsCOMPtr<nsIThreadJSContextStack>
+    contextStack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
+  NS_ENSURE_TRUE(contextStack, NS_ERROR_FAILURE);
+  contextStack.forget(&gThreadJSContextStack);
+
+  nsCOMPtr<nsIXPCSecurityManager> secMan(new nsDOMWorkerSecurityManager());
+  NS_ENSURE_TRUE(secMan, NS_ERROR_OUT_OF_MEMORY);
+  secMan.forget(&gWorkerSecurityManager);
+
+  if (gJSContextIndex == BAD_TLS_INDEX &&
+      PR_NewThreadPrivateIndex(&gJSContextIndex, NULL) != PR_SUCCESS) {
+    NS_ERROR("PR_NewThreadPrivateIndex failed!");
+    gJSContextIndex = BAD_TLS_INDEX;
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+/* static */
+already_AddRefed<nsDOMThreadService>
+nsDOMThreadService::GetOrInitService()
+{
+  if (!gDOMThreadService) {
+    nsRefPtr<nsDOMThreadService> service = new nsDOMThreadService();
+    NS_ENSURE_TRUE(service, nsnull);
+
+    nsresult rv = service->Init();
+    NS_ENSURE_SUCCESS(rv, nsnull);
+
+    service.swap(gDOMThreadService);
+  }
+
+  nsRefPtr<nsDOMThreadService> service(gDOMThreadService);
+  return service.forget();
+}
+
+/* static */
+nsDOMThreadService*
+nsDOMThreadService::get()
+{
+  return gDOMThreadService;
+}
+
+/* static */
+JSContext*
+nsDOMThreadService::GetCurrentContext()
+{
+  JSContext* cx;
+
+  if (NS_IsMainThread()) {
+    nsresult rv = ThreadJSContextStack()->GetSafeJSContext(&cx);
+    NS_ENSURE_SUCCESS(rv, nsnull);
+  }
+  else {
+    NS_ENSURE_TRUE(gJSContextIndex, nsnull);
+    cx = static_cast<JSContext*>(PR_GetThreadPrivate(gJSContextIndex));
+  }
+
+  return cx;
+}
+
+/* static */
+void
+nsDOMThreadService::Shutdown()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_IF_RELEASE(gDOMThreadService);
+}
+
+void
+nsDOMThreadService::Cleanup()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // This will either be called at 'xpcom-shutdown' or earlier if the call to
+  // Init fails somehow. We can therefore assume that all services will still
+  // be available here.
+
+  // Cancel all workers that weren't tied to a window.
+  CancelWorkersForGlobal(nsnull);
+
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    NS_ASSERTION(!mPools.Count(), "Live workers left!");
+    mPools.Clear();
+
+    NS_ASSERTION(!mSuspendedWorkers.Length(), "Suspended workers left!");
+    mSuspendedWorkers.Clear();
+  }
+
+  if (gObserverService) {
+    gObserverService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+    NS_RELEASE(gObserverService);
+
+    UnregisterPrefCallbacks();
+  }
+
+  // The thread pool holds a circular reference to this service through its
+  // listener. We must shut down the thread pool manually to break this cycle.
+  if (mThreadPool) {
+    mThreadPool->Shutdown();
+    mThreadPool = nsnull;
+  }
+
+  // Need to force a GC so that all of our workers get cleaned up.
+  if (gThreadJSContextStack) {
+    JSContext* safeContext;
+    if (NS_SUCCEEDED(gThreadJSContextStack->GetSafeJSContext(&safeContext))) {
+      JS_GC(safeContext);
+    }
+    NS_RELEASE(gThreadJSContextStack);
+  }
+
+  // These must be released after the thread pool is shut down.
+  NS_IF_RELEASE(gJSRuntimeService);
+  NS_IF_RELEASE(gWorkerSecurityManager);
+}
+
+nsresult
+nsDOMThreadService::Dispatch(nsDOMWorker* aWorker,
+                             nsIRunnable* aRunnable,
+                             PRIntervalTime aTimeoutInterval,
+                             PRBool aClearQueue)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+  NS_ASSERTION(aRunnable, "Null pointer!");
+
+  if (!mThreadPool) {
+    // This can happen from a nsDOMWorker::Finalize call after the thread pool
+    // has been shutdown. It should never be possible off the main thread.
+    NS_ASSERTION(NS_IsMainThread(),
+                 "This should be impossible on a non-main thread!");
+    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+  }
+
+  // Don't accept the runnable if the worker's close handler has been triggered
+  // (unless, of course, this is the close runnable as indicated by the non-0
+  // timeout value).
+  if (aWorker->IsClosing() && !aTimeoutInterval) {
+    LOG(("Will not dispatch runnable [0x%p] for closing worker [0x%p]",
+         static_cast<void*>(aRunnable), static_cast<void*>(aWorker)));
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsRefPtr<nsDOMWorkerRunnable> workerRunnable;
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    if (mWorkersInProgress.Get(aWorker, getter_AddRefs(workerRunnable))) {
+      workerRunnable->PutRunnable(aRunnable, aTimeoutInterval, aClearQueue);
+      return NS_OK;
+    }
+
+    workerRunnable = new nsDOMWorkerRunnable(aWorker);
+    NS_ENSURE_TRUE(workerRunnable, NS_ERROR_OUT_OF_MEMORY);
+
+    workerRunnable->PutRunnable(aRunnable, aTimeoutInterval, PR_FALSE);
+
+    PRBool success = mWorkersInProgress.Put(aWorker, workerRunnable);
+    NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  nsresult rv = mThreadPool->Dispatch(workerRunnable, NS_DISPATCH_NORMAL);
+
+  // XXX This is a mess and it could probably be removed once we have an
+  // infallible malloc implementation.
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch runnable to thread pool!");
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // We exited the monitor after inserting the runnable into the table so make
+    // sure we're removing the right one!
+    nsRefPtr<nsDOMWorkerRunnable> tableRunnable;
+    if (mWorkersInProgress.Get(aWorker, getter_AddRefs(tableRunnable)) &&
+        workerRunnable == tableRunnable) {
+      mWorkersInProgress.Remove(aWorker);
+
+      // And don't forget to tell anyone who's waiting.
+      mon.NotifyAll();
+    }
+
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+nsDOMThreadService::SetWorkerTimeout(nsDOMWorker* aWorker,
+                                     PRIntervalTime aTimeoutInterval)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+  NS_ASSERTION(aTimeoutInterval, "No timeout specified!");
+
+  NS_ASSERTION(mThreadPool, "Dispatch called after 'xpcom-shutdown'!");
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  nsRefPtr<nsDOMWorkerRunnable> workerRunnable;
+  if (mWorkersInProgress.Get(aWorker, getter_AddRefs(workerRunnable))) {
+    workerRunnable->SetCloseRunnableTimeout(aTimeoutInterval);
+  }
+}
+
+void
+nsDOMThreadService::WorkerComplete(nsDOMWorkerRunnable* aRunnable)
+{
+  mReentrantMonitor.AssertCurrentThreadIn();
+
+#ifdef DEBUG
+  nsRefPtr<nsDOMWorker>& debugWorker = aRunnable->mWorker;
+
+  nsRefPtr<nsDOMWorkerRunnable> runnable;
+  NS_ASSERTION(mWorkersInProgress.Get(debugWorker, getter_AddRefs(runnable)) &&
+               runnable == aRunnable,
+               "Removing a worker that isn't in our hashtable?!");
+#endif
+
+  mWorkersInProgress.Remove(aRunnable->mWorker);
+}
+
+PRBool
+nsDOMThreadService::QueueSuspendedWorker(nsDOMWorkerRunnable* aRunnable)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+    {
+      // Make sure that the runnable is in mWorkersInProgress.
+      nsRefPtr<nsDOMWorkerRunnable> current;
+      mWorkersInProgress.Get(aRunnable->mWorker, getter_AddRefs(current));
+      NS_ASSERTION(current == aRunnable, "Something crazy wrong here!");
+    }
+#endif
+
+  return mSuspendedWorkers.AppendElement(aRunnable) ? PR_TRUE : PR_FALSE;
+}
+
+/* static */
+JSContext*
+nsDOMThreadService::CreateJSContext()
+{
+  JSRuntime* rt;
+  gJSRuntimeService->GetRuntime(&rt);
+  NS_ENSURE_TRUE(rt, nsnull);
+
+  JSAutoContextDestroyer cx(JS_NewContext(rt, 8192));
+  NS_ENSURE_TRUE(cx, nsnull);
+
+  JS_SetErrorReporter(cx, DOMWorkerErrorReporter);
+
+  JS_SetOperationCallback(cx, DOMWorkerOperationCallback);
+
+  static JSSecurityCallbacks securityCallbacks = {
+    nsDOMWorkerSecurityManager::JSCheckAccess,
+    nsDOMWorkerSecurityManager::JSTranscodePrincipals,
+    nsDOMWorkerSecurityManager::JSFindPrincipal
+  };
+  JS_SetContextSecurityCallbacks(cx, &securityCallbacks);
+
+  JS_ClearContextDebugHooks(cx);
+
+  nsresult rv = nsContentUtils::XPConnect()->
+    SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  JS_SetNativeStackQuota(cx, 256*1024);
+
+  JS_SetOptions(cx,
+    JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_JIT | JSOPTION_PROFILING);
+  JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 1 * 1024 * 1024);
+
+  return cx.forget();
+}
+
+already_AddRefed<nsDOMWorkerPool>
+nsDOMThreadService::GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
+                                     PRBool aRemove)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  nsRefPtr<nsDOMWorkerPool> pool;
+  mPools.Get(aGlobalObject, getter_AddRefs(pool));
+
+  if (aRemove) {
+    mPools.Remove(aGlobalObject);
+  }
+
+  return pool.forget();
+}
+
+void
+nsDOMThreadService::TriggerOperationCallbackForPool(nsDOMWorkerPool* aPool)
+{
+  mReentrantMonitor.AssertCurrentThreadIn();
+
+  // See if we need to trigger the operation callback on any currently running
+  // contexts.
+  PRUint32 contextCount = mJSContexts.Length();
+  for (PRUint32 index = 0; index < contextCount; index++) {
+    JSContext*& cx = mJSContexts[index];
+    nsDOMWorker* worker = (nsDOMWorker*)JS_GetContextPrivate(cx);
+    if (worker && worker->Pool() == aPool) {
+      JS_TriggerOperationCallback(cx);
+    }
+  }
+}
+
+void
+nsDOMThreadService::RescheduleSuspendedWorkerForPool(nsDOMWorkerPool* aPool)
+{
+  mReentrantMonitor.AssertCurrentThreadIn();
+
+  PRUint32 count = mSuspendedWorkers.Length();
+  if (!count) {
+    // Nothing to do here.
+    return;
+  }
+
+  nsTArray<nsDOMWorkerRunnable*> others(count);
+
+  for (PRUint32 index = 0; index < count; index++) {
+    nsDOMWorkerRunnable* runnable = mSuspendedWorkers[index];
+
+#ifdef DEBUG
+    {
+      // Make sure that the runnable never left mWorkersInProgress.
+      nsRefPtr<nsDOMWorkerRunnable> current;
+      mWorkersInProgress.Get(runnable->mWorker, getter_AddRefs(current));
+      NS_ASSERTION(current == runnable, "Something crazy wrong here!");
+    }
+#endif
+
+    if (runnable->mWorker->Pool() == aPool) {
+#ifdef DEBUG
+      nsresult rv =
+#endif
+      mThreadPool->Dispatch(runnable, NS_DISPATCH_NORMAL);
+      NS_ASSERTION(NS_SUCCEEDED(rv), "This shouldn't ever fail!");
+    }
+    else {
+      others.AppendElement(runnable);
+    }
+  }
+
+  mSuspendedWorkers.SwapElements(others);
+}
+
+void
+nsDOMThreadService::CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_TRUE);
+  if (pool) {
+    pool->Cancel();
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    TriggerOperationCallbackForPool(pool);
+    RescheduleSuspendedWorkerForPool(pool);
+  }
+}
+
+void
+nsDOMThreadService::SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
+  if (pool) {
+    pool->Suspend();
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    TriggerOperationCallbackForPool(pool);
+  }
+}
+
+void
+nsDOMThreadService::ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject)
+{
+  NS_ASSERTION(aGlobalObject, "Null pointer!");
+
+  nsRefPtr<nsDOMWorkerPool> pool = GetPoolForGlobal(aGlobalObject, PR_FALSE);
+  if (pool) {
+    pool->Resume();
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    TriggerOperationCallbackForPool(pool);
+    RescheduleSuspendedWorkerForPool(pool);
+  }
+}
+
+void
+nsDOMThreadService::NoteEmptyPool(nsDOMWorkerPool* aPool)
+{
+  NS_ASSERTION(aPool, "Null pointer!");
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  mPools.Remove(aPool->ScriptGlobalObject());
+}
+
+void
+nsDOMThreadService::TimeoutReady(nsDOMWorkerTimeout* aTimeout)
+{
+  nsRefPtr<nsDOMWorkerTimeoutRunnable> runnable =
+    new nsDOMWorkerTimeoutRunnable(aTimeout);
+  NS_ENSURE_TRUE(runnable,);
+
+  Dispatch(aTimeout->GetWorker(), runnable);
+}
+
+nsresult
+nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
+{
+  NS_ENSURE_ARG(aDelta == 1 || aDelta == -1);
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  PRUint32 currentThreadCount;
+  nsresult rv = mThreadPool->GetThreadLimit(&currentThreadCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRInt32 newThreadCount = (PRInt32)currentThreadCount + (PRInt32)aDelta;
+  NS_ASSERTION(newThreadCount >= THREADPOOL_MAX_THREADS,
+               "Can't go below initial thread count!");
+
+  if (newThreadCount > THREADPOOL_THREAD_CAP) {
+    NS_WARNING("Thread pool cap reached!");
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = mThreadPool->SetThreadLimit((PRUint32)newThreadCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If we're allowing an extra thread then post a dummy event to the thread
+  // pool so that any pending workers can get started. The thread pool doesn't
+  // do this on its own like it probably should...
+  if (aDelta == 1) {
+    nsCOMPtr<nsIRunnable> dummy(new nsRunnable());
+    if (dummy) {
+      rv = mThreadPool->Dispatch(dummy, NS_DISPATCH_NORMAL);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+nsDOMThreadService::NoteThreadsafeContractId(const nsACString& aContractId,
+                                             PRBool aIsThreadsafe)
+{
+  NS_ASSERTION(!aContractId.IsEmpty(), "Empty contract id!");
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+  {
+    PRBool isThreadsafe;
+    if (mThreadsafeContractIDs.Get(aContractId, &isThreadsafe)) {
+      NS_ASSERTION(aIsThreadsafe == isThreadsafe, "Inconsistent threadsafety!");
+    }
+  }
+#endif
+
+  if (!mThreadsafeContractIDs.Put(aContractId, aIsThreadsafe)) {
+    NS_WARNING("Out of memory!");
+  }
+}
+
+ThreadsafeStatus
+nsDOMThreadService::GetContractIdThreadsafeStatus(const nsACString& aContractId)
+{
+  NS_ASSERTION(!aContractId.IsEmpty(), "Empty contract id!");
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  PRBool isThreadsafe;
+  if (mThreadsafeContractIDs.Get(aContractId, &isThreadsafe)) {
+    return isThreadsafe ? Threadsafe : NotThreadsafe;
+  }
+
+  return Unknown;
+}
+
+// static
+nsIJSRuntimeService*
+nsDOMThreadService::JSRuntimeService()
+{
+  return gJSRuntimeService;
+}
+
+// static
+nsIThreadJSContextStack*
+nsDOMThreadService::ThreadJSContextStack()
+{
+  return gThreadJSContextStack;
+}
+
+// static
+nsIXPCSecurityManager*
+nsDOMThreadService::WorkerSecurityManager()
+{
+  return gWorkerSecurityManager;
+}
+
+/**
+ * See nsIEventTarget
+ */
+NS_IMETHODIMP
+nsDOMThreadService::Dispatch(nsIRunnable* aEvent,
+                             PRUint32 aFlags)
+{
+  NS_ENSURE_ARG_POINTER(aEvent);
+  NS_ENSURE_FALSE(aFlags & NS_DISPATCH_SYNC, NS_ERROR_NOT_IMPLEMENTED);
+
+  // This should only ever be called by the timer code! We run the event right
+  // now, but all that does is queue the real event for the proper worker.
+  aEvent->Run();
+
+  return NS_OK;
+}
+
+/**
+ * See nsIEventTarget
+ */
+NS_IMETHODIMP
+nsDOMThreadService::IsOnCurrentThread(PRBool* _retval)
+{
+  NS_NOTREACHED("No one should call this!");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/**
+ * See nsIObserver
+ */
+NS_IMETHODIMP
+nsDOMThreadService::Observe(nsISupports* aSubject,
+                            const char* aTopic,
+                            const PRUnichar* aData)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    Cleanup();
+    return NS_OK;
+  }
+
+  NS_NOTREACHED("Unknown observer topic!");
+  return NS_OK;
+}
+
+/**
+ * See nsIThreadPoolListener
+ */
+NS_IMETHODIMP
+nsDOMThreadService::OnThreadCreated()
+{
+  LOG(("Thread created"));
+
+  nsIThread* current = NS_GetCurrentThread();
+
+  // We want our worker threads to always have a lower priority than the main
+  // thread. NSPR docs say that this isn't incredibly reliable across all
+  // platforms but we hope for the best.
+  nsCOMPtr<nsISupportsPriority> priority(do_QueryInterface(current));
+  NS_ENSURE_TRUE(priority, NS_ERROR_FAILURE);
+
+  nsresult rv = priority->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+  // Set the context up for the worker.
+  JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+  if (!cx) {
+    cx = nsDOMThreadService::CreateJSContext();
+    NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
+
+    PRStatus status = PR_SetThreadPrivate(gJSContextIndex, cx);
+    if (status != PR_SUCCESS) {
+      NS_WARNING("Failed to set context on thread!");
+      nsContentUtils::XPConnect()->ReleaseJSContext(cx, PR_TRUE);
+      return NS_ERROR_FAILURE;
+    }
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+#ifdef DEBUG
+    JSContext** newContext =
+#endif
+    mJSContexts.AppendElement(cx);
+
+    // We ensure the capacity of this array in Init.
+    NS_ASSERTION(newContext, "Should never fail!");
+  }
+
+  // Make sure that XPConnect knows about this context.
+  gThreadJSContextStack->Push(cx);
+  gThreadJSContextStack->SetSafeJSContext(cx);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMThreadService::OnThreadShuttingDown()
+{
+  LOG(("Thread shutting down"));
+
+  NS_ASSERTION(gJSContextIndex != BAD_TLS_INDEX, "No context index!");
+
+  JSContext* cx = (JSContext*)PR_GetThreadPrivate(gJSContextIndex);
+  NS_WARN_IF_FALSE(cx, "Thread died with no context?");
+  if (cx) {
+    {
+      ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+      mJSContexts.RemoveElement(cx);
+    }
+
+    JSContext* pushedCx;
+    gThreadJSContextStack->Pop(&pushedCx);
+    NS_ASSERTION(pushedCx == cx, "Popped the wrong context!");
+
+    gThreadJSContextStack->SetSafeJSContext(nsnull);
+
+    // The cycle collector may be running on the main thread. If so we cannot
+    // simply destroy this context. Instead we proxy the context destruction to
+    // the main thread. If that fails somehow then we simply leak the context.
+    nsCOMPtr<nsIRunnable> runnable = new nsDestroyJSContextRunnable(cx);
+
+    if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+      NS_WARNING("Failed to dispatch release runnable!");
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMThreadService::RegisterWorker(nsDOMWorker* aWorker,
+                                   nsIScriptGlobalObject* aGlobalObject)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+
+  if (aGlobalObject && NS_IsMainThread()) {
+    nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
+    NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+
+    nsPIDOMWindow* innerWindow = domWindow->IsOuterWindow() ?
+                                 domWindow->GetCurrentInnerWindow() :
+                                 domWindow.get();
+    NS_ENSURE_STATE(innerWindow);
+
+    nsCOMPtr<nsIScriptGlobalObject> newGlobal(do_QueryInterface(innerWindow));
+    NS_ENSURE_TRUE(newGlobal, NS_ERROR_NO_INTERFACE);
+
+    aGlobalObject = newGlobal;
+  }
+
+  nsRefPtr<nsDOMWorkerPool> pool;
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    if (!mThreadPool) {
+      // Shutting down!
+      return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+    }
+
+    mPools.Get(aGlobalObject, getter_AddRefs(pool));
+  }
+
+  nsresult rv;
+
+  if (!pool) {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    if (!mNavigatorStringsLoaded) {
+      rv = NS_GetNavigatorAppName(mAppName);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = NS_GetNavigatorAppVersion(mAppVersion);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = NS_GetNavigatorPlatform(mPlatform);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = NS_GetNavigatorUserAgent(mUserAgent);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      mNavigatorStringsLoaded = PR_TRUE;
+    }
+
+    nsCOMPtr<nsIDocument> document;
+    if (aGlobalObject) {
+      nsCOMPtr<nsPIDOMWindow> domWindow(do_QueryInterface(aGlobalObject));
+      NS_ENSURE_TRUE(domWindow, NS_ERROR_NO_INTERFACE);
+
+      nsIDOMDocument* domDocument = domWindow->GetExtantDocument();
+      NS_ENSURE_STATE(domDocument);
+
+      document = do_QueryInterface(domDocument);
+      NS_ENSURE_STATE(document);
+    }
+
+    pool = new nsDOMWorkerPool(aGlobalObject, document);
+    NS_ENSURE_TRUE(pool, NS_ERROR_OUT_OF_MEMORY);
+
+    rv = pool->Init();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    PRBool success = mPools.Put(aGlobalObject, pool);
+    NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  rv = pool->NoteWorker(aWorker);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aWorker->SetPool(pool);
+  return NS_OK;
+}
+
+void
+nsDOMThreadService::GetAppName(nsAString& aAppName)
+{
+  NS_ASSERTION(mNavigatorStringsLoaded,
+               "Shouldn't call this before we have loaded strings!");
+  aAppName.Assign(mAppName);
+}
+
+void
+nsDOMThreadService::GetAppVersion(nsAString& aAppVersion)
+{
+  NS_ASSERTION(mNavigatorStringsLoaded,
+               "Shouldn't call this before we have loaded strings!");
+  aAppVersion.Assign(mAppVersion);
+}
+
+void
+nsDOMThreadService::GetPlatform(nsAString& aPlatform)
+{
+  NS_ASSERTION(mNavigatorStringsLoaded,
+               "Shouldn't call this before we have loaded strings!");
+  aPlatform.Assign(mPlatform);
+}
+
+void
+nsDOMThreadService::GetUserAgent(nsAString& aUserAgent)
+{
+  NS_ASSERTION(mNavigatorStringsLoaded,
+               "Shouldn't call this before we have loaded strings!");
+  aUserAgent.Assign(mUserAgent);
+}
+
+void
+nsDOMThreadService::RegisterPrefCallbacks()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  for (PRUint32 index = 0; index < NS_ARRAY_LENGTH(sPrefsToWatch); index++) {
+    Preferences::RegisterCallback(PrefCallback, sPrefsToWatch[index]);
+    PrefCallback(sPrefsToWatch[index], nsnull);
+  }
+}
+
+void
+nsDOMThreadService::UnregisterPrefCallbacks()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  for (PRUint32 index = 0; index < NS_ARRAY_LENGTH(sPrefsToWatch); index++) {
+    Preferences::UnregisterCallback(PrefCallback, sPrefsToWatch[index]);
+  }
+}
+
+// static
+int
+nsDOMThreadService::PrefCallback(const char* aPrefName,
+                                 void* aClosure)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  if(!strcmp(aPrefName, "dom.max_script_run_time")) {
+    // We assume atomic 32bit reads/writes. If this assumption doesn't hold on
+    // some wacky platform then the worst that could happen is that the close
+    // handler will run for a slightly different amount of time.
+    PRUint32 timeoutMS =
+      Preferences::GetUint(aPrefName, gWorkerCloseHandlerTimeoutMS);
+
+    // We must have a timeout value, 0 is not ok. If the pref is set to 0 then
+    // fall back to our default.
+    if (timeoutMS) {
+      gWorkerCloseHandlerTimeoutMS = timeoutMS;
+    }
+  }
+  return 0;
+}
+
+// static
+PRUint32
+nsDOMThreadService::GetWorkerCloseHandlerTimeoutMS()
+{
+  return gWorkerCloseHandlerTimeoutMS;
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMThreadService.h
@@ -0,0 +1,212 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** 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 worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com> (Original Author)
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMTHREADSERVICE_H__
+#define __NSDOMTHREADSERVICE_H__
+
+// Interfaces
+#include "nsIEventTarget.h"
+#include "nsIObserver.h"
+#include "nsIThreadPool.h"
+
+// Other includes
+#include "jsapi.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsRefPtrHashtable.h"
+#include "nsStringGlue.h"
+#include "nsTPtrArray.h"
+
+#include "prlog.h"
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gDOMThreadsLog;
+#endif
+
+class nsDOMWorker;
+class nsDOMWorkerPool;
+class nsDOMWorkerRunnable;
+class nsDOMWorkerTimeout;
+class nsIJSRuntimeService;
+class nsIScriptGlobalObject;
+class nsIThreadJSContextStack;
+class nsIXPConnect;
+class nsIXPCSecurityManager;
+
+enum ThreadsafeStatus
+{
+  Threadsafe,
+  NotThreadsafe,
+  Unknown
+};
+
+class nsDOMThreadService : public nsIEventTarget,
+                           public nsIObserver,
+                           public nsIThreadPoolListener
+{
+  friend class nsDOMWorker;
+  friend class nsDOMWorkerNavigator;
+  friend class nsDOMWorkerPool;
+  friend class nsDOMWorkerRunnable;
+  friend class nsDOMWorkerThread;
+  friend class nsDOMWorkerTimeout;
+  friend class nsDOMWorkerXHR;
+  friend class nsDOMWorkerXHRProxy;
+  friend class nsLayoutStatics;
+  friend class nsReportErrorRunnable;
+
+  friend void DOMWorkerErrorReporter(JSContext* aCx,
+                                     const char* aMessage,
+                                     JSErrorReport* aReport);
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIEVENTTARGET
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSITHREADPOOLLISTENER
+
+  // Any DOM consumers that need access to this service should use this method.
+  static already_AddRefed<nsDOMThreadService> GetOrInitService();
+
+  // Simple getter for this service. This does not create the service if it
+  // hasn't been created already, and it never AddRef's!
+  static nsDOMThreadService* get();
+
+  static JSContext* GetCurrentContext();
+
+  // Easy access to the services we care about.
+  static nsIJSRuntimeService* JSRuntimeService();
+  static nsIThreadJSContextStack* ThreadJSContextStack();
+  static nsIXPCSecurityManager* WorkerSecurityManager();
+
+  void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+  void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+  void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
+
+  nsresult ChangeThreadPoolMaxThreads(PRInt16 aDelta);
+
+  void NoteThreadsafeContractId(const nsACString& aContractId,
+                                PRBool aIsThreadsafe);
+
+  ThreadsafeStatus GetContractIdThreadsafeStatus(const nsACString& aContractId);
+
+private:
+  nsDOMThreadService();
+  ~nsDOMThreadService();
+
+  nsresult Init();
+  void Cleanup();
+
+  static void Shutdown();
+
+  nsresult Dispatch(nsDOMWorker* aWorker,
+                    nsIRunnable* aRunnable,
+                    PRIntervalTime aTimeoutInterval = 0,
+                    PRBool aClearQueue = PR_FALSE);
+
+  void SetWorkerTimeout(nsDOMWorker* aWorker,
+                        PRIntervalTime aTimeoutInterval);
+
+  void WorkerComplete(nsDOMWorkerRunnable* aRunnable);
+
+  static JSContext* CreateJSContext();
+
+  already_AddRefed<nsDOMWorkerPool>
+    GetPoolForGlobal(nsIScriptGlobalObject* aGlobalObject,
+                     PRBool aRemove);
+
+  void TriggerOperationCallbackForPool(nsDOMWorkerPool* aPool);
+  void RescheduleSuspendedWorkerForPool(nsDOMWorkerPool* aPool);
+
+  void NoteEmptyPool(nsDOMWorkerPool* aPool);
+
+  void TimeoutReady(nsDOMWorkerTimeout* aTimeout);
+
+  nsresult RegisterWorker(nsDOMWorker* aWorker,
+                          nsIScriptGlobalObject* aGlobalObject);
+
+  void GetAppName(nsAString& aAppName);
+  void GetAppVersion(nsAString& aAppVersion);
+  void GetPlatform(nsAString& aPlatform);
+  void GetUserAgent(nsAString& aUserAgent);
+
+  void RegisterPrefCallbacks();
+  void UnregisterPrefCallbacks();
+
+  static int PrefCallback(const char* aPrefName,
+                          void* aClosure);
+
+  static PRUint32 GetWorkerCloseHandlerTimeoutMS();
+
+  PRBool QueueSuspendedWorker(nsDOMWorkerRunnable* aRunnable);
+
+  // Our internal thread pool.
+  nsCOMPtr<nsIThreadPool> mThreadPool;
+
+  // Maps nsIScriptGlobalObject* to nsDOMWorkerPool.
+  nsRefPtrHashtable<nsVoidPtrHashKey, nsDOMWorkerPool> mPools;
+
+  // mReentrantMonitor protects all access to mWorkersInProgress and
+  // mCreationsInProgress.
+  mozilla::ReentrantMonitor mReentrantMonitor;
+
+  // A map from nsDOMWorkerThread to nsDOMWorkerRunnable.
+  nsRefPtrHashtable<nsVoidPtrHashKey, nsDOMWorkerRunnable> mWorkersInProgress;
+
+  // A list of active JSContexts that we've created. Always protected with
+  // mReentrantMonitor.
+  nsTArray<JSContext*> mJSContexts;
+
+  // A list of worker runnables that were never started because the worker was
+  // suspended. Always protected with mReentrantMonitor.
+  nsTArray<nsDOMWorkerRunnable*> mSuspendedWorkers;
+
+  // Always protected with mReentrantMonitor.
+  nsDataHashtable<nsCStringHashKey, PRBool> mThreadsafeContractIDs;
+
+  nsString mAppName;
+  nsString mAppVersion;
+  nsString mPlatform;
+  nsString mUserAgent;
+
+  PRBool mNavigatorStringsLoaded;
+};
+
+#endif /* __NSDOMTHREADSERVICE_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -0,0 +1,2752 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** 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
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@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 ***** */
+
+#include "nsDOMWorker.h"
+
+#include "nsIDOMEvent.h"
+#include "nsIEventTarget.h"
+#include "nsIJSRuntimeService.h"
+#include "nsIXPConnect.h"
+
+#include "jscntxt.h"
+#ifdef MOZ_SHARK
+#include "jsdbgapi.h"
+#endif
+#include "nsAtomicRefcnt.h"
+#include "nsAXPCNativeCallContext.h"
+#include "nsContentUtils.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMClassInfoID.h"
+#include "nsGlobalWindow.h"
+#include "nsJSON.h"
+#include "nsJSUtils.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+#include "nsNativeCharsetUtils.h"
+#include "xpcprivate.h"
+
+#include "nsDOMThreadService.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerLocation.h"
+#include "nsDOMWorkerNavigator.h"
+#include "nsDOMWorkerPool.h"
+#include "nsDOMWorkerScriptLoader.h"
+#include "nsDOMWorkerTimeout.h"
+#include "nsDOMWorkerXHR.h"
+
+using namespace mozilla;
+
+class TestComponentThreadsafetyRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  TestComponentThreadsafetyRunnable(const nsACString& aContractId,
+                                    PRBool aService)
+  : mContractId(aContractId),
+    mService(aService),
+    mIsThreadsafe(PR_FALSE)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+    nsresult rv;
+    nsCOMPtr<nsISupports> instance;
+    if (mService) {
+      instance = do_GetService(mContractId.get(), &rv);
+    }
+    else {
+      instance = do_CreateInstance(mContractId.get(), &rv);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(instance, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 flags;
+    rv = classInfo->GetFlags(&flags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mIsThreadsafe = !!(flags & nsIClassInfo::THREADSAFE);
+    return NS_OK;
+  }
+
+  PRBool IsThreadsafe()
+  {
+    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+    return mIsThreadsafe;
+  }
+
+private:
+  nsCString mContractId;
+  PRBool mService;
+  PRBool mIsThreadsafe;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(TestComponentThreadsafetyRunnable, nsIRunnable)
+
+class nsDOMWorkerFunctions
+{
+public:
+  typedef nsDOMWorker::WorkerPrivilegeModel WorkerPrivilegeModel;
+
+  // Same as window.dump().
+  static JSBool
+  Dump(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  // Same as window.setTimeout().
+  static JSBool
+  SetTimeout(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return MakeTimeout(aCx, aArgc, aVp, PR_FALSE);
+  }
+
+  // Same as window.setInterval().
+  static JSBool
+  SetInterval(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return MakeTimeout(aCx, aArgc, aVp, PR_TRUE);
+  }
+
+  // Used for both clearTimeout() and clearInterval().
+  static JSBool
+  KillTimeout(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  LoadScripts(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  NewXMLHttpRequest(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  NewWorker(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return MakeNewWorker(aCx, aArgc, aVp, nsDOMWorker::CONTENT);
+  }
+
+  static JSBool
+  AtoB(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  BtoA(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  // Chrome-only functions
+  static JSBool
+  NewChromeWorker(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  XPCOMLazyGetter(JSContext* aCx, JSObject* aObj, jsid aId, jsval* aVp);
+
+  static JSBool
+  CreateInstance(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return GetInstanceCommon(aCx, aArgc, aVp, PR_FALSE);
+  }
+
+  static JSBool
+  GetService(JSContext* aCx, uintN aArgc, jsval* aVp) {
+    return GetInstanceCommon(aCx, aArgc, aVp, PR_TRUE);
+  }
+
+#ifdef BUILD_CTYPES
+  static JSBool
+  CTypesLazyGetter(JSContext* aCx, JSObject* aObj, jsid aId, jsval* aVp);
+#endif
+
+private:
+  // Internal helper for SetTimeout and SetInterval.
+  static JSBool
+  MakeTimeout(JSContext* aCx, uintN aArgc, jsval* aVp, PRBool aIsInterval);
+
+  static JSBool
+  MakeNewWorker(JSContext* aCx, uintN aArgc, jsval* aVp,
+                WorkerPrivilegeModel aPrivilegeModel);
+
+  static JSBool
+  GetInstanceCommon(JSContext* aCx, uintN aArgc, jsval* aVp, PRBool aService);
+};
+
+JSFunctionSpec gDOMWorkerXPCOMFunctions[] = {
+  {"createInstance", nsDOMWorkerFunctions::CreateInstance, 1, JSPROP_ENUMERATE},
+  {"getService",     nsDOMWorkerFunctions::GetService,     1, JSPROP_ENUMERATE},
+  { nsnull,          nsnull,                               0, 0 }
+};
+
+JSBool
+nsDOMWorkerFunctions::Dump(JSContext* aCx,
+                           uintN aArgc,
+                           jsval* aVp)
+{
+  JS_SET_RVAL(cx, aVp, JSVAL_VOID);
+  if (!nsGlobalWindow::DOMWindowDumpEnabled()) {
+    return JS_TRUE;
+  }
+
+  JSString* str;
+  if (aArgc && (str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0])) && str) {
+    nsDependentJSString depStr;
+    if (depStr.init(aCx, str)) {
+      fputs(NS_ConvertUTF16toUTF8(depStr).get(), stderr);
+      fflush(stderr);
+    }
+  }
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::MakeTimeout(JSContext* aCx,
+                                  uintN aArgc,
+                                  jsval* aVp,
+                                  PRBool aIsInterval)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  PRUint32 id = worker->NextTimeoutId();
+
+  if (worker->IsClosing()) {
+    // Timeouts won't run in the close handler, fake success and bail.
+    JS_SET_RVAL(aCx, aVp, INT_TO_JSVAL(id));
+    return JS_TRUE;
+  }
+
+  nsRefPtr<nsDOMWorkerTimeout> timeout = new nsDOMWorkerTimeout(worker, id);
+  if (!timeout) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = timeout->Init(aCx, aArgc, JS_ARGV(aCx, aVp), aIsInterval);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to initialize timeout!");
+    return JS_FALSE;
+  }
+
+  rv = worker->AddFeature(timeout, aCx);
+  if (NS_FAILED(rv)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  rv = timeout->Start();
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to start timeout!");
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, INT_TO_JSVAL(id));
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::KillTimeout(JSContext* aCx,
+                                  uintN aArgc,
+                                  jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  uint32 id;
+  if (!JS_ValueToECMAUint32(aCx, JS_ARGV(aCx, aVp)[0], &id)) {
+    JS_ReportError(aCx, "First argument must be a timeout id");
+    return JS_FALSE;
+  }
+
+  worker->CancelTimeoutWithId(PRUint32(id));
+  JS_SET_RVAL(aCx, aVp, JSVAL_VOID);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::LoadScripts(JSContext* aCx,
+                                  uintN aArgc,
+                                  jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    // No argument is ok according to spec.
+    return JS_TRUE;
+  }
+
+  nsAutoTArray<nsString, 10> urls;
+
+  if (!urls.SetCapacity((PRUint32)aArgc)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  jsval* argv = JS_ARGV(aCx, aVp);
+  for (uintN index = 0; index < aArgc; index++) {
+    jsval val = argv[index];
+
+    if (!JSVAL_IS_STRING(val)) {
+      JS_ReportError(aCx, "Argument %d must be a string", index);
+      return JS_FALSE;
+    }
+
+    JSString* str = JS_ValueToString(aCx, val);
+    if (!str) {
+      JS_ReportError(aCx, "Couldn't convert argument %d to a string", index);
+      return JS_FALSE;
+    }
+
+    nsString* newURL = urls.AppendElement();
+    NS_ASSERTION(newURL, "Shouldn't fail if SetCapacity succeeded above!");
+
+    nsDependentJSString depStr;
+    if (!depStr.init(aCx, str)) {
+      return JS_FALSE;
+    }
+
+    newURL->Assign(depStr);
+  }
+
+  nsRefPtr<nsDOMWorkerScriptLoader> loader =
+    new nsDOMWorkerScriptLoader(worker);
+  if (!loader) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = worker->AddFeature(loader, aCx);
+  if (NS_FAILED(rv)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  rv = loader->LoadScripts(aCx, urls, PR_TRUE);
+  if (NS_FAILED(rv)) {
+    if (!JS_IsExceptionPending(aCx)) {
+      JS_ReportError(aCx, "Failed to load scripts");
+    }
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, JSVAL_VOID);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::NewXMLHttpRequest(JSContext* aCx,
+                                        uintN aArgc,
+                                        jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (aArgc) {
+    JS_ReportError(aCx, "XMLHttpRequest constructor takes no arguments!");
+    return JS_FALSE;
+  }
+
+  nsRefPtr<nsDOMWorkerXHR> xhr = new nsDOMWorkerXHR(worker);
+  if (!xhr) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = xhr->Init();
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to construct XMLHttpRequest!");
+    return JS_FALSE;
+  }
+
+  rv = worker->AddFeature(xhr, aCx);
+  if (NS_FAILED(rv)) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> xhrWrapped;
+  jsval v;
+  rv = nsContentUtils::WrapNative(aCx, JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
+                                  static_cast<nsIXMLHttpRequest*>(xhr), &v,
+                                  getter_AddRefs(xhrWrapped));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to wrap XMLHttpRequest!");
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCs, aVp, v);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::AtoB(JSContext* aCx,
+                           uintN aArgc,
+                           jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  return nsXPConnect::Base64Decode(aCx, JS_ARGV(aCx, aVp)[0],
+                                   &JS_RVAL(aCx, aVp));
+}
+
+JSBool
+nsDOMWorkerFunctions::BtoA(JSContext* aCx,
+                           uintN aArgc,
+                           jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  return nsXPConnect::Base64Encode(aCx, JS_ARGV(aCx, aVp)[0],
+                                   &JS_RVAL(aCx, aVp));
+}
+
+JSBool
+nsDOMWorkerFunctions::NewChromeWorker(JSContext* aCx,
+                                      uintN aArgc,
+                                      jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (!worker->IsPrivileged()) {
+    JS_ReportError(aCx, "Cannot create a priviliged worker!");
+    return JS_FALSE;
+  }
+
+  return MakeNewWorker(aCx, aArgc, aVp, nsDOMWorker::CHROME);
+}
+
+JSBool
+nsDOMWorkerFunctions::XPCOMLazyGetter(JSContext* aCx,
+                                      JSObject* aObj,
+                                      jsid aId,
+                                      jsval* aVp)
+{
+#ifdef DEBUG
+  {
+    NS_ASSERTION(JS_GetGlobalForObject(aCx, aObj) == aObj, "Bad object!");
+    NS_ASSERTION(JSID_IS_STRING(aId), "Not a string!");
+    NS_ASSERTION(nsDependentJSString(aId).EqualsLiteral("XPCOM"), "Bad id!");
+  }
+#endif
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  PRUint16 dummy;
+  nsCOMPtr<nsIXPCSecurityManager> secMan;
+  nsContentUtils::XPConnect()->
+    GetSecurityManagerForJSContext(aCx, getter_AddRefs(secMan), &dummy);
+  if (!secMan) {
+    JS_ReportError(aCx, "Could not get security manager!");
+    return JS_FALSE;
+  }
+
+  nsCID dummyCID;
+  if (NS_FAILED(secMan->CanGetService(aCx, dummyCID))) {
+    JS_ReportError(aCx, "Access to the XPCOM object is denied!");
+    return JS_FALSE;
+  }
+
+  JSObject* xpcom = JS_NewObject(aCx, nsnull, nsnull, nsnull);
+  NS_ENSURE_TRUE(xpcom, JS_FALSE);
+
+  JSBool ok = JS_DefineFunctions(aCx, xpcom, gDOMWorkerXPCOMFunctions);
+  NS_ENSURE_TRUE(ok, JS_FALSE);
+
+  ok = JS_DeletePropertyById(aCx, aObj, aId);
+  NS_ENSURE_TRUE(ok, JS_FALSE);
+
+  jsval xpcomVal = OBJECT_TO_JSVAL(xpcom);
+  ok = JS_SetPropertyById(aCx, aObj, aId, &xpcomVal);
+  NS_ENSURE_TRUE(ok, JS_FALSE);
+
+  JS_SET_RVAL(aCx, aVp, xpcomVal);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::GetInstanceCommon(JSContext* aCx,
+                                        uintN aArgc,
+                                        jsval* aVp,
+                                        PRBool aService)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
+  if (!str) {
+    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
+    return JS_FALSE;
+  }
+
+  JSAutoByteString strBytes(aCx, str);
+  if (!strBytes) {
+    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
+    return JS_FALSE;
+  }
+
+  nsDependentCString contractId(strBytes.ptr(), JS_GetStringLength(str));
+
+  nsDOMThreadService* threadService = nsDOMThreadService::get();
+
+  ThreadsafeStatus status =
+    threadService->GetContractIdThreadsafeStatus(contractId);
+
+  if (status == Unknown) {
+    nsCOMPtr<nsIThread> mainThread;
+    nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+    if (NS_FAILED(rv)) {
+      JS_ReportError(aCx, "Failed to get main thread!");
+      return JS_FALSE;
+    }
+
+    nsRefPtr<TestComponentThreadsafetyRunnable> runnable =
+      new TestComponentThreadsafetyRunnable(contractId, aService);
+
+    rv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
+    if (NS_FAILED(rv)) {
+      JS_ReportError(aCx, "Failed to check threadsafety!");
+      return JS_FALSE;
+    }
+
+    // The worker may have been canceled while waiting above. Check again.
+    if (worker->IsCanceled()) {
+      return JS_FALSE;
+    }
+
+    if (runnable->IsThreadsafe()) {
+      threadService->NoteThreadsafeContractId(contractId, PR_TRUE);
+      status = Threadsafe;
+    }
+    else {
+      threadService->NoteThreadsafeContractId(contractId, PR_FALSE);
+      status = NotThreadsafe;
+    }
+  }
+
+  if (status == NotThreadsafe) {
+    JS_ReportError(aCx, "ChromeWorker may not create an XPCOM object that is "
+                   "not threadsafe!");
+    return JS_FALSE;
+  }
+
+  nsCOMPtr<nsISupports> instance;
+  if (aService) {
+    instance = do_GetService(contractId.get());
+    if (!instance) {
+      JS_ReportError(aCx, "Could not get the service!");
+      return JS_FALSE;
+    }
+  }
+  else {
+    instance = do_CreateInstance(contractId.get());
+    if (!instance) {
+      JS_ReportError(aCx, "Could not create the instance!");
+      return JS_FALSE;
+    }
+  }
+
+  JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
+  if (!global) {
+    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
+    return JS_FALSE;
+  }
+
+  jsval val;
+  nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
+  if (NS_FAILED(nsContentUtils::WrapNative(aCx, global, instance, &val,
+                                           getter_AddRefs(wrapper)))) {
+    JS_ReportError(aCx, "Failed to wrap object!");
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, val);
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::MakeNewWorker(JSContext* aCx,
+                                    uintN aArgc,
+                                    jsval* aVp,
+                                    WorkerPrivilegeModel aPrivilegeModel)
+{
+  JSObject *obj = JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp));
+    
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Worker constructor must have an argument!");
+    return JS_FALSE;
+  }
+
+  // This pointer is protected by our pool, but it is *not* threadsafe and must
+  // not be used in any way other than to pass it along to the Initialize call.
+  nsIScriptGlobalObject* owner = worker->Pool()->ScriptGlobalObject();
+
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedWorker =
+    worker->GetWrappedNative();
+  if (!wrappedWorker) {
+    JS_ReportError(aCx, "Couldn't get wrapped native of worker!");
+    return JS_FALSE;
+  }
+
+  nsRefPtr<nsDOMWorker> newWorker =
+    new nsDOMWorker(worker, wrappedWorker, aPrivilegeModel);
+  if (!newWorker) {
+    JS_ReportOutOfMemory(aCx);
+    return JS_FALSE;
+  }
+
+  nsresult rv = newWorker->InitializeInternal(owner, aCx, obj, aArgc,
+                                              JS_ARGV(aCx, aVp));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Couldn't initialize new worker!");
+    return JS_FALSE;
+  }
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> workerWrapped;
+  jsval v;
+  rv = nsContentUtils::WrapNative(aCx, obj, static_cast<nsIWorker*>(newWorker), &v, 
+                                  getter_AddRefs(workerWrapped));
+  if (NS_FAILED(rv)) {
+    JS_ReportError(aCx, "Failed to wrap new worker!");
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, v);
+  return JS_TRUE;
+}
+
+#ifdef BUILD_CTYPES
+static char*
+UnicodeToNative(JSContext *cx, const jschar *source, size_t slen)
+{
+  nsCAutoString native;
+  nsDependentString unicode(reinterpret_cast<const PRUnichar*>(source), slen);
+  nsresult rv = NS_CopyUnicodeToNative(unicode, native);
+  if (NS_FAILED(rv)) {
+    JS_ReportError(cx, "could not convert string to native charset");
+    return NULL;
+  }
+
+  char* result = static_cast<char*>(JS_malloc(cx, native.Length() + 1));
+  if (!result)
+    return NULL;
+
+  memcpy(result, native.get(), native.Length() + 1);
+  return result;
+}
+
+static JSCTypesCallbacks sCallbacks = {
+  UnicodeToNative
+};
+
+JSBool
+nsDOMWorkerFunctions::CTypesLazyGetter(JSContext* aCx,
+                                       JSObject* aObj,
+                                       jsid aId,
+                                       jsval* aVp)
+{
+#ifdef DEBUG
+  {
+    NS_ASSERTION(JS_GetGlobalForObject(aCx, aObj) == aObj, "Bad object!");
+    NS_ASSERTION(JSID_IS_STRING(aId), "Not a string!");
+    NS_ASSERTION(nsDependentJSString(aId).EqualsLiteral("ctypes"), "Bad id!");
+  }
+#endif
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+  NS_ASSERTION(worker->IsPrivileged(), "This shouldn't be possible!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  jsval ctypes;
+  return JS_DeletePropertyById(aCx, aObj, aId) &&
+         JS_InitCTypesClass(aCx, aObj) &&
+         JS_GetProperty(aCx, aObj, "ctypes", &ctypes) &&
+         JS_SetCTypesCallbacks(aCx, JSVAL_TO_OBJECT(ctypes), &sCallbacks) &&
+         JS_GetPropertyById(aCx, aObj, aId, aVp);
+}
+#endif
+JSFunctionSpec gDOMWorkerFunctions[] = {
+  { "dump",                nsDOMWorkerFunctions::Dump,                1, 0 },
+  { "setTimeout",          nsDOMWorkerFunctions::SetTimeout,          1, 0 },
+  { "clearTimeout",        nsDOMWorkerFunctions::KillTimeout,         1, 0 },
+  { "setInterval",         nsDOMWorkerFunctions::SetInterval,         1, 0 },
+  { "clearInterval",       nsDOMWorkerFunctions::KillTimeout,         1, 0 },
+  { "importScripts",       nsDOMWorkerFunctions::LoadScripts,         1, 0 },
+  { "XMLHttpRequest",      nsDOMWorkerFunctions::NewXMLHttpRequest,   0, JSFUN_CONSTRUCTOR },
+  { "Worker",              nsDOMWorkerFunctions::NewWorker,           1, JSFUN_CONSTRUCTOR },
+  { "atob",                nsDOMWorkerFunctions::AtoB,                1, 0 },
+  { "btoa",                nsDOMWorkerFunctions::BtoA,                1, 0 },
+  { nsnull,                nsnull,                                    0, 0 }
+};
+JSFunctionSpec gDOMWorkerChromeFunctions[] = {
+  { "ChromeWorker",        nsDOMWorkerFunctions::NewChromeWorker,     1, JSFUN_CONSTRUCTOR },
+  { nsnull,                nsnull,                                    0, 0 }
+};
+enum DOMWorkerStructuredDataType
+{
+  // We have a special tag for XPCWrappedNatives that are being passed between
+  // threads. This will not work across processes and cannot be persisted. Only
+  // for ChromeWorker use at present.
+  DOMWORKER_SCTAG_WRAPPEDNATIVE = JS_SCTAG_USER_MIN + 0x1000,
+
+  DOMWORKER_SCTAG_END
+};
+
+PR_STATIC_ASSERT(DOMWORKER_SCTAG_END <= JS_SCTAG_USER_MAX);
+
+// static
+JSBool
+WriteStructuredClone(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> >* wrappedNatives =
+    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 this nsISupports is threadsafe.
+    nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(wrappedObject);
+    if (classInfo) {
+      PRUint32 flags;
+      if (NS_SUCCEEDED(classInfo->GetFlags(&flags)) &&
+          (flags & nsIClassInfo::THREADSAFE)) {
+        // Write the raw pointer into the stream, and add it to the list we're
+        // building.
+        return JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_WRAPPEDNATIVE, 0) &&
+               JS_WriteBytes(aWriter, &wrappedObject, sizeof(wrappedObject)) &&
+               wrappedNatives->AppendElement(wrappedObject);
+      }
+    }
+  }
+
+  // Something failed above, try using the runtime callbacks instead.
+  const JSStructuredCloneCallbacks* runtimeCallbacks =
+    aCx->runtime->structuredCloneCallbacks;
+  if (runtimeCallbacks) {
+    return runtimeCallbacks->write(aCx, aWriter, aObj, nsnull);
+  }
+
+  // We can't handle this object, throw an exception if one hasn't been thrown
+  // already.
+  if (!JS_IsExceptionPending(aCx)) {
+    nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+  }
+  return JS_FALSE;
+}
+
+nsDOMWorkerScope::nsDOMWorkerScope(nsDOMWorker* aWorker)
+: mWorker(aWorker),
+  mWrappedNative(nsnull),
+  mHasOnerror(PR_FALSE)
+{
+  NS_ASSERTION(aWorker, "Null pointer!");
+}
+
+NS_IMPL_ISUPPORTS_INHERITED3(nsDOMWorkerScope, nsDOMWorkerMessageHandler,
+                                               nsIWorkerScope,
+                                               nsIWorkerGlobalScope,
+                                               nsIXPCScriptable)
+
+NS_IMPL_CI_INTERFACE_GETTER4(nsDOMWorkerScope, nsIWorkerScope,
+                                               nsIWorkerGlobalScope,
+                                               nsIDOMEventTarget,
+                                               nsIXPCScriptable)
+
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerScope)
+NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerScope)
+
+// Need to return a scriptable helper so that XPConnect can get our
+// nsIXPCScriptable flags properly (to not enumerate QI, for instance).
+NS_IMETHODIMP
+nsDOMWorkerScope::GetHelperForLanguage(PRUint32 aLanguage,
+                                       nsISupports** _retval)
+{
+  if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
+    NS_ADDREF(*_retval = NS_ISUPPORTS_CAST(nsIWorkerScope*, this));
+  }
+  else {
+    *_retval = nsnull;
+  }
+  return NS_OK;
+}
+
+// Use the xpc_map_end.h macros to generate the nsIXPCScriptable methods we want
+// for the scope.
+
+#define XPC_MAP_CLASSNAME nsDOMWorkerScope
+#define XPC_MAP_QUOTED_CLASSNAME "DedicatedWorkerGlobalScope"
+#define XPC_MAP_WANT_POSTCREATE
+#define XPC_MAP_WANT_TRACE
+#define XPC_MAP_WANT_FINALIZE
+
+#define XPC_MAP_FLAGS                                      \
+  nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY           | \
+  nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY           | \
+  nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY           | \
+  nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE            | \
+  nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY            | \
+  nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES         | \
+  nsIXPCScriptable::WANT_ADDPROPERTY
+
+#define XPC_MAP_WANT_ADDPROPERTY
+
+#include "xpc_map_end.h"
+
+NS_IMETHODIMP
+nsDOMWorkerScope::PostCreate(nsIXPConnectWrappedNative*  aWrapper,
+                             JSContext* /* aCx */,
+                             JSObject* /* aObj */)
+{
+  NS_ASSERTION(!mWrappedNative, "Already got a wrapper?!");
+  mWrappedNative = aWrapper;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
+                        JSTracer* aTracer,
+                        JSObject* /*aObj */)
+{
+  nsDOMWorkerMessageHandler::Trace(aTracer);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
+                           JSContext* /* aCx */,
+                           JSObject* /* aObj */)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  ClearAllListeners();
+  mWrappedNative = nsnull;
+  return NS_OK;
+}
+
+already_AddRefed<nsIXPConnectWrappedNative>
+nsDOMWorkerScope::GetWrappedNative()
+{
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative = mWrappedNative;
+  NS_ASSERTION(wrappedNative, "Null wrapped native!");
+  return wrappedNative.forget();
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::AddProperty(nsIXPConnectWrappedNative* aWrapper,
+                              JSContext* aCx,
+                              JSObject* aObj,
+                              jsid aId,
+                              jsval* aVp,
+                              PRBool* _retval)
+{
+  // We're not going to be setting any exceptions manually so set _retval to
+  // true in the beginning.
+  *_retval = PR_TRUE;
+
+  // Bail out now if any of our prerequisites are not met. We only care about
+  // someone making an 'onmessage' or 'onerror' function so aId must be a
+  // string and aVp must be a function.
+  JSObject* funObj;
+  if (!(JSID_IS_STRING(aId) &&
+        JSVAL_IS_OBJECT(*aVp) &&
+        (funObj = JSVAL_TO_OBJECT(*aVp)) &&
+        JS_ObjectIsFunction(aCx, funObj))) {
+    return NS_OK;
+  }
+
+  JSFlatString *str = JSID_TO_FLAT_STRING(aId);
+
+  // Figure out which listener we're setting.
+  SetListenerFunc func;
+  if (JS_FlatStringEqualsAscii(str, "onmessage")) {
+    func = &nsDOMWorkerScope::SetOnmessage;
+  }
+  else if (JS_FlatStringEqualsAscii(str, "onerror")) {
+    func = &nsDOMWorkerScope::SetOnerror;
+  }
+  else {
+    // Some other function, we don't need to do anything special after all.
+    return NS_OK;
+  }
+
+  // Wrap the function as an nsIDOMEventListener.
+  nsCOMPtr<nsIDOMEventListener> listener;
+  nsresult rv =
+    nsContentUtils::XPConnect()->WrapJS(aCx, funObj,
+                                        NS_GET_IID(nsIDOMEventListener),
+                                        getter_AddRefs(listener));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // And pass the listener to the appropriate setter.
+  rv = (this->*func)(listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetSelf(nsIWorkerGlobalScope** aSelf)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aSelf);
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  NS_ADDREF(*aSelf = this);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetNavigator(nsIWorkerNavigator** _retval)
+{
+  if (!mNavigator) {
+    mNavigator = new nsDOMWorkerNavigator();
+    NS_ENSURE_TRUE(mNavigator, NS_ERROR_OUT_OF_MEMORY);
+  }
+
+  NS_ADDREF(*_retval = mNavigator);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetLocation(nsIWorkerLocation** _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsIWorkerLocation> location = mWorker->GetLocation();
+  NS_ASSERTION(location, "This should never be null!");
+
+  location.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetOnerror(nsIDOMEventListener** aOnerror)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aOnerror);
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  if (!mHasOnerror) {
+    // Spec says we have to return 'undefined' until something is set here.
+    nsIXPConnect* xpc = nsContentUtils::XPConnect();
+    NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
+
+    nsAXPCNativeCallContext* cc;
+    nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
+
+    jsval* retval;
+    rv = cc->GetRetValPtr(&retval);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *retval = JSVAL_VOID;
+    return cc->SetReturnValueWasSet(PR_TRUE);
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("error"));
+  listener.forget(aOnerror);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::SetOnerror(nsIDOMEventListener* aOnerror)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  mHasOnerror = PR_TRUE;
+
+  return SetOnXListener(NS_LITERAL_STRING("error"), aOnerror);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::PostMessage(/* JSObject aMessage */)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return mWorker->PostMessageInternal(PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::Close()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  return mWorker->Close();
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetOnmessage(nsIDOMEventListener** aOnmessage)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aOnmessage);
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("message"));
+  listener.forget(aOnmessage);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::SetOnmessage(nsIDOMEventListener* aOnmessage)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return SetOnXListener(NS_LITERAL_STRING("message"), aOnmessage);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::GetOnclose(nsIDOMEventListener** aOnclose)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aOnclose);
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("close"));
+  listener.forget(aOnclose);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::SetOnclose(nsIDOMEventListener* aOnclose)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsresult rv = SetOnXListener(NS_LITERAL_STRING("close"), aOnclose);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::RemoveEventListener(const nsAString& aType,
+                                      nsIDOMEventListener* aListener,
+                                      PRBool aUseCapture)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return nsDOMWorkerMessageHandler::RemoveEventListener(aType, aListener,
+                                                        aUseCapture);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::DispatchEvent(nsIDOMEvent* aEvent,
+                                PRBool* _retval)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return nsDOMWorkerMessageHandler::DispatchEvent(aEvent, _retval);
+}
+
+NS_IMETHODIMP
+nsDOMWorkerScope::AddEventListener(const nsAString& aType,
+                                   nsIDOMEventListener* aListener,
+                                   PRBool aUseCapture,
+                                   PRBool aWantsUntrusted,
+                                   PRUint8 optional_argc)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (mWorker->IsCanceled()) {
+    return NS_ERROR_ABORT;
+  }
+
+  return nsDOMWorkerMessageHandler::AddEventListener(aType, aListener,
+                                                     aUseCapture,
+                                                     aWantsUntrusted,
+                                                     optional_argc);
+}
+
+class nsWorkerHoldingRunnable : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsWorkerHoldingRunnable(nsDOMWorker* aWorker)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()) { }
+
+  NS_IMETHOD Run() {
+    return NS_OK;
+  }
+
+  void ReplaceWrappedNative(nsIXPConnectWrappedNative* aWrappedNative) {
+    mWorkerWN = aWrappedNative;
+  }
+
+protected:
+  virtual ~nsWorkerHoldingRunnable() { }
+
+  nsRefPtr<nsDOMWorker> mWorker;
+
+private:
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsWorkerHoldingRunnable, nsIRunnable)
+
+class nsDOMFireEventRunnable : public nsWorkerHoldingRunnable
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  nsDOMFireEventRunnable(nsDOMWorker* aWorker,
+                         nsDOMWorkerEvent* aEvent,
+                         PRBool aToInner)
+  : nsWorkerHoldingRunnable(aWorker), mEvent(aEvent), mToInner(aToInner)
+  {
+    NS_ASSERTION(aWorker && aEvent, "Null pointer!");
+  }
+
+  NS_IMETHOD Run() {
+#ifdef DEBUG
+    if (NS_IsMainThread()) {
+      NS_ASSERTION(!mToInner, "Should only run outer events on main thread!");
+      NS_ASSERTION(!mWorker->mParent, "Worker shouldn't have a parent!");
+    }
+    else {
+      JSContext* cx = nsDOMThreadService::GetCurrentContext();
+      nsDOMWorker* currentWorker = (nsDOMWorker*)JS_GetContextPrivate(cx);
+      NS_ASSERTION(currentWorker, "Must have a worker here!");
+
+      nsDOMWorker* targetWorker = mToInner ? mWorker.get() : mWorker->mParent;
+      NS_ASSERTION(currentWorker == targetWorker, "Wrong worker!");
+    }
+#endif
+    if (mWorker->IsCanceled()) {
+      return NS_ERROR_ABORT;
+    }
+
+    // If the worker is suspended and we're running on the main thread then we
+    // can't actually dispatch the event yet. Instead we queue it for whenever
+    // we resume.
+    if (mWorker->IsSuspended() && NS_IsMainThread()) {
+      if (!mWorker->QueueSuspendedRunnable(this)) {
+        NS_ERROR("Out of memory?!");
+        return NS_ERROR_ABORT;
+      }
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIDOMEventTarget> target = mToInner ?
+      static_cast<nsDOMWorkerMessageHandler*>(mWorker->GetInnerScope()) :
+      static_cast<nsDOMWorkerMessageHandler*>(mWorker);
+
+    NS_ASSERTION(target, "Null target!");
+    NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
+
+    mEvent->SetTarget(target);
+    return target->DispatchEvent(mEvent, nsnull);
+  }
+
+protected:
+  nsRefPtr<nsDOMWorkerEvent> mEvent;
+  PRBool mToInner;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(nsDOMFireEventRunnable, nsWorkerHoldingRunnable)
+
+// Standard NS_IMPL_THREADSAFE_ADDREF without the logging stuff (since this
+// class is made to be inherited anyway).
+NS_IMETHODIMP_(nsrefcnt)
+nsDOMWorkerFeature::AddRef()
+{
+  NS_ASSERTION(mRefCnt >= 0, "Illegal refcnt!");
+  return NS_AtomicIncrementRefcnt(mRefCnt);
+}
+
+// Custom NS_IMPL_THREADSAFE_RELEASE. Checks the mFreeToDie flag before calling
+// delete. If the flag is false then the feature still lives in the worker's
+// list and must be removed. We rely on the fact that the RemoveFeature method
+// calls AddRef and Release after setting the mFreeToDie flag so we won't leak.
+NS_IMETHODIMP_(nsrefcnt)
+nsDOMWorkerFeature::Release()
+{
+  NS_ASSERTION(mRefCnt, "Double release!");
+  nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
+  if (count == 0) {
+    if (mFreeToDie) {
+      mRefCnt = 1;
+      delete this;
+    }
+    else {
+      mWorker->RemoveFeature(this, nsnull);
+    }
+  }
+  return count;
+}
+
+NS_IMPL_QUERY_INTERFACE0(nsDOMWorkerFeature)
+
+nsDOMWorker::nsDOMWorker(nsDOMWorker* aParent,
+                         nsIXPConnectWrappedNative* aParentWN,
+                         WorkerPrivilegeModel aPrivilegeModel)
+: mParent(aParent),
+  mParentWN(aParentWN),
+  mPrivilegeModel(aPrivilegeModel),
+  mLock("nsDOMWorker.mLock"),
+  mInnerScope(nsnull),
+  mGlobal(NULL),
+  mNextTimeoutId(0),
+  mFeatureSuspendDepth(0),
+  mWrappedNative(nsnull),
+  mErrorHandlerRecursionCount(0),
+  mStatus(eRunning),
+  mExpirationTime(0),
+  mSuspended(PR_FALSE),
+  mCompileAttempted(PR_FALSE)
+{
+#ifdef DEBUG
+  PRBool mainThread = NS_IsMainThread();
+  NS_ASSERTION(aParent ? !mainThread : mainThread, "Wrong thread!");
+#endif
+}
+
+nsDOMWorker::~nsDOMWorker()
+{
+  if (mPool) {
+    mPool->NoteDyingWorker(this);
+  }
+
+  NS_ASSERTION(!mFeatures.Length(), "Live features!");
+  NS_ASSERTION(!mQueuedRunnables.Length(), "Events that never ran!");
+
+  nsCOMPtr<nsIThread> mainThread;
+  NS_GetMainThread(getter_AddRefs(mainThread));
+
+  nsIPrincipal* principal;
+  mPrincipal.forget(&principal);
+  if (principal) {
+    NS_ProxyRelease(mainThread, principal, PR_FALSE);
+  }
+
+  nsIURI* uri;
+  mBaseURI.forget(&uri);
+  if (uri) {
+    NS_ProxyRelease(mainThread, uri, PR_FALSE);
+  }
+}
+
+// static
+nsresult
+nsDOMWorker::NewWorker(nsISupports** aNewObject)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsCOMPtr<nsISupports> newWorker =
+    NS_ISUPPORTS_CAST(nsIWorker*, new nsDOMWorker(nsnull, nsnull, CONTENT));
+  NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
+
+  newWorker.forget(aNewObject);
+  return NS_OK;
+}
+
+// static
+nsresult
+nsDOMWorker::NewChromeDOMWorker(nsDOMWorker** aNewObject)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Subsumes nsContentUtils::IsCallerChrome
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  NS_ASSERTION(ssm, "Should never be null!");
+
+  PRBool enabled;
+  nsresult rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &enabled);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(enabled, NS_ERROR_DOM_SECURITY_ERR);
+
+  nsRefPtr<nsDOMWorker> newWorker = new nsDOMWorker(nsnull, nsnull, CHROME);
+  NS_ENSURE_TRUE(newWorker, NS_ERROR_OUT_OF_MEMORY);
+
+  newWorker.forget(aNewObject);
+  return NS_OK;
+}
+
+// static
+nsresult
+nsDOMWorker::NewChromeWorker(nsISupports** aNewObject)
+{
+  nsDOMWorker* newWorker;
+  nsresult rv = NewChromeDOMWorker(&newWorker);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aNewObject = NS_ISUPPORTS_CAST(nsIWorker*, newWorker);
+  return NS_OK;
+}
+
+NS_IMPL_ADDREF_INHERITED(nsDOMWorker, nsDOMWorkerMessageHandler)
+NS_IMPL_RELEASE_INHERITED(nsDOMWorker, nsDOMWorkerMessageHandler)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMWorker)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWorker)
+  NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+  NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+  NS_INTERFACE_MAP_ENTRY(nsIWorker)
+  NS_INTERFACE_MAP_ENTRY(nsIAbstractWorker)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventTarget,
+                                   nsDOMWorkerMessageHandler)
+  NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
+  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+NS_INTERFACE_MAP_END
+
+// Use the xpc_map_end.h macros to generate the nsIXPCScriptable methods we want
+// for the worker.
+
+#define XPC_MAP_CLASSNAME nsDOMWorker
+#define XPC_MAP_QUOTED_CLASSNAME "Worker"
+#define XPC_MAP_WANT_PRECREATE
+#define XPC_MAP_WANT_POSTCREATE
+#define XPC_MAP_WANT_TRACE
+#define XPC_MAP_WANT_FINALIZE
+
+#define XPC_MAP_FLAGS                                      \
+  nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY           | \
+  nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY           | \
+  nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY           | \
+  nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE            | \
+  nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY            | \
+  nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
+
+#include "xpc_map_end.h"
+
+NS_IMETHODIMP
+nsDOMWorker::PreCreate(nsISupports* aObject,
+                       JSContext* aCx,
+                       JSObject* /* aPlannedParent */,
+                       JSObject** aParent)
+{
+  nsCOMPtr<nsIWorker> iworker(do_QueryInterface(aObject));
+  NS_ENSURE_TRUE(iworker, NS_ERROR_UNEXPECTED);
+
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+  {
+    MutexAutoLock lock(mLock);
+    wrappedNative = mWrappedNative;
+  }
+
+  // Don't allow XPConnect to create multiple WrappedNatives for this object.
+  if (wrappedNative) {
+    JSObject* object;
+    nsresult rv = wrappedNative->GetJSObject(&object);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *aParent = JS_GetParent(aCx, object);
+  }
+
+  return IsPrivileged() ? NS_SUCCESS_CHROME_ACCESS_ONLY : NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::PostCreate(nsIXPConnectWrappedNative* aWrapper,
+                        JSContext* /* aCx */,
+                        JSObject* /* aObj */)
+{
+  MutexAutoLock lock(mLock);
+  mWrappedNative = aWrapper;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Trace(nsIXPConnectWrappedNative* /* aWrapper */,
+                   JSTracer* aTracer,
+                   JSObject* /*aObj */)
+{
+  PRBool canceled = PR_FALSE;
+  {
+    MutexAutoLock lock(mLock);
+    canceled = mStatus == eKilled;
+  }
+
+  if (!canceled) {
+    nsDOMWorkerMessageHandler::Trace(aTracer);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
+                      JSContext* aCx,
+                      JSObject* /* aObj */)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Don't leave dangling JSObject pointers in our handlers!
+  ClearAllListeners();
+
+  // Clear our wrapped native now that it has died.
+  {
+    MutexAutoLock lock(mLock);
+    mWrappedNative = nsnull;
+  }
+
+  // Do this *after* we null out mWrappedNative so that we don't hand out a
+  // freed pointer.
+  if (TerminateInternal(PR_TRUE) == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
+    // We're shutting down, jump right to Kill.
+    Kill();
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_CI_INTERFACE_GETTER3(nsDOMWorker, nsIWorker,
+                                          nsIAbstractWorker,
+                                          nsIDOMEventTarget)
+NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorker)
+NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorker)
+
+NS_IMETHODIMP
+nsDOMWorker::GetHelperForLanguage(PRUint32 aLanguage,
+                                  nsISupports** _retval)
+{
+  if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
+    NS_ADDREF(*_retval = NS_ISUPPORTS_CAST(nsIWorker*, this));
+  }
+  else {
+    *_retval = nsnull;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Initialize(nsISupports* aOwner,
+                        JSContext* aCx,
+                        JSObject* aObj,
+                        PRUint32 aArgc,
+                        jsval* aArgv)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ENSURE_ARG_POINTER(aOwner);
+
+  nsCOMPtr<nsIScriptGlobalObject> globalObj(do_QueryInterface(aOwner));
+  NS_ENSURE_TRUE(globalObj, NS_NOINTERFACE);
+
+  return InitializeInternal(globalObj, aCx, aObj, aArgc, aArgv);
+}
+
+nsresult
+nsDOMWorker::InitializeInternal(nsIScriptGlobalObject* aOwner,
+                                JSContext* aCx,
+                                JSObject* aObj,
+                                PRUint32 aArgc,
+                                jsval* aArgv)
+{
+  NS_ASSERTION(aCx, "Null context!");
+  NS_ASSERTION(aObj, "Null global object!");
+
+  NS_ENSURE_TRUE(aArgc, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
+  NS_ENSURE_ARG_POINTER(aArgv);
+
+  JSString* str = JS_ValueToString(aCx, aArgv[0]);
+  NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS);
+
+  nsDependentJSString depStr;
+  NS_ENSURE_TRUE(depStr.init(aCx, str), NS_ERROR_OUT_OF_MEMORY);
+
+  mScriptURL.Assign(depStr);
+  NS_ENSURE_FALSE(mScriptURL.IsEmpty(), NS_ERROR_INVALID_ARG);
+
+  nsresult rv;
+
+  // Figure out the principal and base URI to use if we're on the main thread.
+  // Otherwise this is a sub-worker and it will have its principal set by the
+  // script loader.
+  if (NS_IsMainThread()) {
+    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+    NS_ASSERTION(ssm, "Should never be null!");
+
+    PRBool isChrome;
+    rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &isChrome);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    NS_ASSERTION(isChrome || aOwner, "How can we have a non-chrome, non-window "
+                 "worker?!");
+
+    // Chrome callers (whether ChromeWorker of Worker) always get the system
+    // principal here as they're allowed to load anything. The script loader may
+    // change the principal later depending on the script uri.
+    if (isChrome) {
+      rv = ssm->GetSystemPrincipal(getter_AddRefs(mPrincipal));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    if (aOwner) {
+      // We're being created inside a window. Get the document's base URI and
+      // use it as our base URI.
+      nsCOMPtr<nsPIDOMWindow> domWindow = do_QueryInterface(aOwner, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsIDOMDocument* domDocument = domWindow->GetExtantDocument();
+      NS_ENSURE_STATE(domDocument);
+
+      nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      mBaseURI = document->GetDocBaseURI();
+
+      if (!mPrincipal) {
+        // Use the document's NodePrincipal as our principal if we're not being
+        // called from chrome.
+        mPrincipal = document->NodePrincipal();
+        NS_ENSURE_STATE(mPrincipal);
+      }
+    }
+    else {
+      // We're being created outside of a window. Need to figure out the script
+      // that is creating us in order for us to use relative URIs later on.
+      JSStackFrame* frame = JS_GetScriptedCaller(aCx, nsnull);
+      if (frame) {
+        JSScript* script = JS_GetFrameScript(aCx, frame);
+        NS_ENSURE_STATE(script);
+
+        const char* filename = JS_GetScriptFilename(aCx, script);
+
+        rv = NS_NewURI(getter_AddRefs(mBaseURI), filename);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+
+    NS_ASSERTION(mPrincipal, "Should have set the principal!");
+  }
+
+  NS_ASSERTION(!mGlobal, "Already got a global?!");
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> thisWrapped;
+  jsval v;
+  rv = nsContentUtils::WrapNative(aCx, aObj, static_cast<nsIWorker*>(this), &v,
+                                  getter_AddRefs(thisWrapped));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(mWrappedNative, "Post-create hook should have set this!");
+
+  mKillTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIThread> mainThread;
+  rv = NS_GetMainThread(getter_AddRefs(mainThread));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mKillTimer->SetTarget(mainThread);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // This is pretty cool - all we have to do to get our script executed is to
+  // pass a no-op runnable to the thread service and it will make sure we have
+  // a context and global object.
+  nsCOMPtr<nsIRunnable> runnable(new nsWorkerHoldingRunnable(this));
+  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+  nsRefPtr<nsDOMThreadService> threadService =
+    nsDOMThreadService::GetOrInitService();
+  NS_ENSURE_STATE(threadService);
+
+  rv = threadService->RegisterWorker(this, aOwner);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(mPool, "RegisterWorker should have set our pool!");
+
+  rv = threadService->Dispatch(this, runnable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+nsDOMWorker::Cancel()
+{
+  // Called by the pool when the window that created us is being torn down. Must
+  // always be on the main thread.
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // We set the eCanceled status to indicate this. It behaves just like the
+  // eTerminated status (canceled while close runnable is unscheduled, not
+  // canceled while close runnable is running) except that it always reports
+  // that it is canceled when running on the main thread. This status trumps all
+  // others (except eKilled). Have to do this because the window that created
+  // us has gone away and had its scope cleared so XPConnect will assert all
+  // over the place if we try to run anything.
+
+  PRBool enforceTimeout = PR_FALSE;
+  {
+    MutexAutoLock lock(mLock);
+
+    NS_ASSERTION(mStatus != eCanceled, "Canceled more than once?!");
+
+    if (mStatus == eKilled) {
+      return;
+    }
+
+    DOMWorkerStatus oldStatus = mStatus;
+    mStatus = eCanceled;
+    if (oldStatus != eRunning) {
+      enforceTimeout = PR_TRUE;
+    }
+  }
+
+  PRUint32 timeoutMS = nsDOMThreadService::GetWorkerCloseHandlerTimeoutMS();
+  NS_ASSERTION(timeoutMS, "This must not be 0!");
+
+#ifdef DEBUG
+  nsresult rv;
+#endif
+  if (enforceTimeout) {
+    // Tell the thread service to enforce a timeout on the close handler that
+    // is already scheduled.
+    nsDOMThreadService::get()->
+      SetWorkerTimeout(this, PR_MillisecondsToInterval(timeoutMS));
+
+#ifdef DEBUG
+    rv =
+#endif
+    mKillTimer->InitWithCallback(this, timeoutMS, nsITimer::TYPE_ONE_SHOT);
+    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to init kill timer!");
+
+    return;
+  }
+
+#ifdef DEBUG
+  rv =
+#endif
+  FireCloseRunnable(PR_MillisecondsToInterval(timeoutMS), PR_TRUE, PR_FALSE);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to fire close runnable!");
+}
+
+void
+nsDOMWorker::Kill()
+{
+  // Cancel all features and set our status to eKilled. This should only be
+  // called on the main thread by the thread service or our kill timer to
+  // indicate that the worker's close handler has run (or timed out).
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(IsClosing(), "Close handler should have run by now!");
+
+  // If the close handler finished before our kill timer then we don't need it
+  // any longer.
+  if (mKillTimer) {
+    mKillTimer->Cancel();
+    mKillTimer = nsnull;
+  }
+
+  PRUint32 count, index;
+  nsAutoTArray<nsRefPtr<nsDOMWorkerFeature>, 20> features;
+  {
+    MutexAutoLock lock(mLock);
+
+    if (mStatus == eKilled) {
+      NS_ASSERTION(mFeatures.Length() == 0, "Features added after killed!");
+      return;
+    }
+    mStatus = eKilled;
+
+    count = mFeatures.Length();
+    for (index = 0; index < count; index++) {
+      nsDOMWorkerFeature*& feature = mFeatures[index];
+
+#ifdef DEBUG
+      nsRefPtr<nsDOMWorkerFeature>* newFeature =
+#endif
+      features.AppendElement(feature);
+      NS_ASSERTION(newFeature, "Out of memory!");
+
+      feature->FreeToDie(PR_TRUE);
+    }
+
+    mFeatures.Clear();
+  }
+
+  count = features.Length();
+  for (index = 0; index < count; index++) {
+    features[index]->Cancel();
+  }
+
+  // Make sure we kill any queued runnables that we never had a chance to run.
+  mQueuedRunnables.Clear();
+
+  // We no longer need to keep our inner scope.
+  mInnerScope = nsnull;
+  mScopeWN = nsnull;
+  mGlobal = NULL;
+
+  // And we can let our parent die now too.
+  mParent = nsnull;
+  mParentWN = nsnull;
+}
+
+void
+nsDOMWorker::Suspend()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  PRBool shouldSuspendFeatures;
+  {
+    MutexAutoLock lock(mLock);
+    NS_ASSERTION(!mSuspended, "Suspended more than once!");
+    shouldSuspendFeatures = !mSuspended;
+    mSuspended = PR_TRUE;
+  }
+
+  if (shouldSuspendFeatures) {
+    SuspendFeatures();
+  }
+}
+
+void
+nsDOMWorker::Resume()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  PRBool shouldResumeFeatures;
+  {
+    MutexAutoLock lock(mLock);
+#ifdef DEBUG
+    // Should only have a mismatch if GC or Cancel happened while suspended.
+    if (!mSuspended) {
+      NS_ASSERTION(mStatus == eCanceled ||
+                   (mStatus == eTerminated && !mWrappedNative),
+                   "Not suspended!");
+    }
+#endif
+    shouldResumeFeatures = mSuspended;
+    mSuspended = PR_FALSE;
+  }
+
+  if (shouldResumeFeatures) {
+    ResumeFeatures();
+  }
+
+  // Repost any events that were queued for the main thread while suspended.
+  PRUint32 count = mQueuedRunnables.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    NS_DispatchToCurrentThread(mQueuedRunnables[index]);
+  }
+  mQueuedRunnables.Clear();
+}
+
+PRBool
+nsDOMWorker::IsCanceled()
+{
+  MutexAutoLock lock(mLock);
+  return IsCanceledNoLock();
+}
+
+PRBool
+nsDOMWorker::IsCanceledNoLock()
+{
+  // If we haven't started the close process then we're not canceled.
+  if (mStatus == eRunning) {
+    return PR_FALSE;
+  }
+
+  // There are several conditions under which we want JS code to abort and all
+  // other functions to bail:
+  // 1. If we've already run our close handler then we are canceled forevermore.
+  // 2. If we've been terminated then we want to pretend to be canceled until
+  //    our close handler is scheduled and running.
+  // 3. If we've been canceled then we pretend to be canceled until the close
+  //    handler has been scheduled.
+  // 4. If the close handler has run for longer than the allotted time then we
+  //    should be canceled as well.
+  // 5. If we're on the main thread then we'll pretend to be canceled if the
+  //    user has navigated away from the page.
+  return mStatus == eKilled ||
+         (mStatus == eTerminated && !mExpirationTime) ||
+         (mStatus == eCanceled && !mExpirationTime) ||
+         (mExpirationTime && mExpirationTime != PR_INTERVAL_NO_TIMEOUT &&
+          mExpirationTime <= PR_IntervalNow()) ||
+         (mStatus == eCanceled && NS_IsMainThread());
+}
+
+PRBool
+nsDOMWorker::IsClosing()
+{
+  MutexAutoLock lock(mLock);
+  return mStatus != eRunning;
+}
+
+PRBool
+nsDOMWorker::IsSuspended()
+{
+  MutexAutoLock lock(mLock);
+  return mSuspended;
+}
+
+nsresult
+nsDOMWorker::PostMessageInternal(PRBool aToInner)
+{
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+  NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
+
+  nsAXPCNativeCallContext* cc;
+  nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
+
+  PRUint32 argc;
+  rv = cc->GetArgc(&argc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!argc) {
+    return NS_ERROR_XPC_NOT_ENOUGH_ARGS;
+  }
+
+  jsval* argv;
+  rv = cc->GetArgvPtr(&argv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  JSContext* cx;
+  rv = cc->GetJSContext(&cx);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If we're a ChromeWorker then we allow wrapped natives to be passed via
+  // structured cloning by supplying a custom write callback. To do that we need
+  // to make sure they stay alive while the message is being sent, so we collect
+  // the wrapped natives in an array to be packaged with the message.
+  JSStructuredCloneCallbacks callbacks = {
+    nsnull, IsPrivileged() ? WriteStructuredClone : nsnull, nsnull
+  };
+
+  JSAutoRequest ar(cx);
+
+  JSAutoStructuredCloneBuffer buffer;
+  nsTArray<nsCOMPtr<nsISupports> > wrappedNatives;
+  if (!buffer.write(cx, argv[0], &callbacks, &wrappedNatives)) {
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
+  }
+
+  nsRefPtr<nsDOMWorkerMessageEvent> message = new nsDOMWorkerMessageEvent();
+  NS_ENSURE_TRUE(message, NS_ERROR_OUT_OF_MEMORY);
+
+  rv = message->InitMessageEvent(NS_LITERAL_STRING("message"), PR_FALSE,
+                                 PR_FALSE, EmptyString(), EmptyString(),
+                                 nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = message->SetJSData(cx, buffer, wrappedNatives);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsDOMFireEventRunnable> runnable =
+    new nsDOMFireEventRunnable(this, message, aToInner);
+  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+  // If aToInner is true then we want to target the runnable at this worker's
+  // thread. Otherwise we need to target the parent's thread.
+  nsDOMWorker* target = aToInner ? this : mParent;
+
+  // If this is a top-level worker then target the main thread. Otherwise use
+  // the thread service to find the target's thread.
+  if (!target) {
+    nsCOMPtr<nsIThread> mainThread;
+    rv = NS_GetMainThread(getter_AddRefs(mainThread));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mainThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  else {
+    rv = nsDOMThreadService::get()->Dispatch(target, runnable);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+PRBool
+nsDOMWorker::SetGlobalForContext(JSContext* aCx, nsLazyAutoRequest *aRequest,
+                                 JSAutoEnterCompartment *aComp)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  if (!CompileGlobalObject(aCx, aRequest, aComp)) {
+    return PR_FALSE;
+  }
+
+  JS_SetGlobalObject(aCx, mGlobal);
+  return PR_TRUE;
+}
+
+PRBool
+nsDOMWorker::CompileGlobalObject(JSContext* aCx, nsLazyAutoRequest *aRequest,
+                                 JSAutoEnterCompartment *aComp)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  // On success, we enter a request and a cross-compartment call that both
+  // belong to the caller. But on failure, we must not remain in a request or
+  // cross-compartment call. So we enter both only locally at first. On
+  // failure, the local request and call will automatically get cleaned
+  // up. Once success is certain, we swap them into *aRequest and *aCall.
+  nsLazyAutoRequest localRequest;
+  JSAutoEnterCompartment localAutoCompartment;
+  localRequest.enter(aCx);
+
+  PRBool success;
+  if (mGlobal) {
+    success = localAutoCompartment.enter(aCx, mGlobal);
+    NS_ENSURE_TRUE(success, PR_FALSE);
+
+    aRequest->swap(localRequest);
+    aComp->swap(localAutoCompartment);
+    return PR_TRUE;
+  }
+
+  if (mCompileAttempted) {
+    // Don't try to recompile a bad script.
+    return PR_FALSE;
+  }
+  mCompileAttempted = PR_TRUE;
+
+  NS_ASSERTION(!mScriptURL.IsEmpty(), "Must have a url here!");
+
+  NS_ASSERTION(!JS_GetGlobalObject(aCx), "Global object should be unset!");
+
+  nsRefPtr<nsDOMWorkerScope> scope = new nsDOMWorkerScope(this);
+  NS_ENSURE_TRUE(scope, PR_FALSE);
+
+  nsISupports* scopeSupports = NS_ISUPPORTS_CAST(nsIWorkerScope*, scope);
+
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+
+  const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
+                         nsIXPConnect::OMIT_COMPONENTS_OBJECT;
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> globalWrapper;
+  nsresult rv =
+    xpc->InitClassesWithNewWrappedGlobal(aCx, scopeSupports,
+                                         NS_GET_IID(nsISupports), nsnull,
+                                         NS_ISUPPORTS_CAST(nsIWorker*, this),
+                                         flags, getter_AddRefs(globalWrapper));
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+  JSObject* global;
+  rv = globalWrapper->GetJSObject(&global);
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+  NS_ASSERTION(JS_GetGlobalObject(aCx) == global, "Global object mismatch!");
+
+  success = localAutoCompartment.enter(aCx, global);
+  NS_ENSURE_TRUE(success, PR_FALSE);
+
+#ifdef DEBUG
+  {
+    jsval components;
+    if (JS_GetProperty(aCx, global, "Components", &components)) {
+      NS_ASSERTION(components == JSVAL_VOID,
+                   "Components property still defined!");
+    }
+  }
+#endif
+
+  // Set up worker thread functions.
+  success = JS_DefineFunctions(aCx, global, gDOMWorkerFunctions);
+  NS_ENSURE_TRUE(success, PR_FALSE);
+
+  success = JS_DefineProfilingFunctions(aCx, global);
+  NS_ENSURE_TRUE(success, PR_FALSE);
+
+  if (IsPrivileged()) {
+    // Add chrome functions.
+    success = JS_DefineFunctions(aCx, global, gDOMWorkerChromeFunctions);
+    NS_ENSURE_TRUE(success, PR_FALSE);
+
+    success = JS_DefineProperty(aCx, global, "XPCOM", JSVAL_VOID,
+                                nsDOMWorkerFunctions::XPCOMLazyGetter, nsnull,
+                                0);
+    NS_ENSURE_TRUE(success, PR_FALSE);
+
+#ifdef BUILD_CTYPES
+    // Add the lazy getter for ctypes.
+    success = JS_DefineProperty(aCx, global, "ctypes", JSVAL_VOID,
+                                nsDOMWorkerFunctions::CTypesLazyGetter, nsnull,
+                                0);
+    NS_ENSURE_TRUE(success, PR_FALSE);
+#endif
+  }
+
+  // From here on out we have to remember to null mGlobal, mInnerScope, and
+  // mScopeWN if something fails! We really don't need to hang on to mGlobal
+  // as long as we have mScopeWN, but it saves us a virtual call every time the
+  // worker is scheduled. Meh.
+  mGlobal = global;
+  mInnerScope = scope;
+  mScopeWN = scope->GetWrappedNative();
+  NS_ASSERTION(mScopeWN, "Should have a wrapped native here!");
+
+  nsRefPtr<nsDOMWorkerScriptLoader> loader =
+    new nsDOMWorkerScriptLoader(this);
+
+  rv = AddFeature(loader, aCx);
+  if (NS_FAILED(rv)) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    mScopeWN = nsnull;
+    return PR_FALSE;
+  }
+
+  rv = loader->LoadWorkerScript(aCx, mScriptURL);
+
+  JS_ReportPendingException(aCx);
+
+  if (NS_FAILED(rv)) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    mScopeWN = nsnull;
+    return PR_FALSE;
+  }
+
+  NS_ASSERTION(mPrincipal, "Script loader didn't set our principal!");
+  NS_ASSERTION(mBaseURI, "Script loader didn't set our base uri!");
+
+  // Make sure we kept the system principal.
+  if (IsPrivileged() && !nsContentUtils::IsSystemPrincipal(mPrincipal)) {
+    static const char warning[] = "ChromeWorker attempted to load a "
+                                  "non-chrome worker script!";
+    NS_WARNING(warning);
+
+    JS_ReportError(aCx, warning);
+
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    mScopeWN = nsnull;
+    return PR_FALSE;
+  }
+
+  rv = loader->ExecuteScripts(aCx);
+
+  JS_ReportPendingException(aCx);
+
+  if (NS_FAILED(rv)) {
+    mGlobal = NULL;
+    mInnerScope = nsnull;
+    mScopeWN = nsnull;
+    return PR_FALSE;
+  }
+
+  aRequest->swap(localRequest);
+  aComp->swap(localAutoCompartment);
+  return PR_TRUE;
+}
+
+void
+nsDOMWorker::SetPool(nsDOMWorkerPool* aPool)
+{
+  NS_ASSERTION(!mPool, "Shouldn't ever set pool more than once!");
+  mPool = aPool;
+}
+
+already_AddRefed<nsIXPConnectWrappedNative>
+nsDOMWorker::GetWrappedNative()
+{
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+  {
+    MutexAutoLock lock(mLock);
+    wrappedNative = mWrappedNative;
+  }
+  return wrappedNative.forget();
+}
+
+nsresult
+nsDOMWorker::AddFeature(nsDOMWorkerFeature* aFeature,
+                        JSContext* aCx)
+{
+  NS_ASSERTION(aFeature, "Null pointer!");
+
+  PRBool shouldSuspend;
+  {
+    // aCx may be null.
+    JSAutoSuspendRequest asr(aCx);
+
+    MutexAutoLock lock(mLock);
+
+    if (mStatus == eKilled) {
+      // No features may be added after we've been canceled. Sorry.
+      return NS_ERROR_FAILURE;
+    }
+
+    nsDOMWorkerFeature** newFeature = mFeatures.AppendElement(aFeature);
+    NS_ENSURE_TRUE(newFeature, NS_ERROR_OUT_OF_MEMORY);
+
+    aFeature->FreeToDie(PR_FALSE);
+    shouldSuspend = mFeatureSuspendDepth > 0;
+  }
+
+  if (shouldSuspend) {
+    aFeature->Suspend();
+  }
+
+  return NS_OK;
+}
+
+void
+nsDOMWorker::RemoveFeature(nsDOMWorkerFeature* aFeature,
+                           JSContext* aCx)
+{
+  NS_ASSERTION(aFeature, "Null pointer!");
+
+  // This *must* be a nsRefPtr so that we call Release after setting FreeToDie.
+  nsRefPtr<nsDOMWorkerFeature> feature(aFeature);
+  {
+    // aCx may be null.
+    JSAutoSuspendRequest asr(aCx);
+
+    MutexAutoLock lock(mLock);
+
+#ifdef DEBUG
+    PRBool removed =
+#endif
+    mFeatures.RemoveElement(aFeature);
+    NS_ASSERTION(removed, "Feature not in the list!");
+
+    aFeature->FreeToDie(PR_TRUE);
+  }
+}
+
+void
+nsDOMWorker::CancelTimeoutWithId(PRUint32 aId)
+{
+  nsRefPtr<nsDOMWorkerFeature> foundFeature;
+  {
+    MutexAutoLock lock(mLock);
+    PRUint32 count = mFeatures.Length();
+    for (PRUint32 index = 0; index < count; index++) {
+      nsDOMWorkerFeature*& feature = mFeatures[index];
+      if (feature->HasId() && feature->GetId() == aId) {
+        foundFeature = feature;
+        feature->FreeToDie(PR_TRUE);
+        mFeatures.RemoveElementAt(index);
+        break;
+      }
+    }
+  }
+
+  if (foundFeature) {
+    foundFeature->Cancel();
+  }
+}
+
+void
+nsDOMWorker::SuspendFeatures()
+{
+  nsAutoTArray<nsRefPtr<nsDOMWorkerFeature>, 20> features;
+  {
+    MutexAutoLock lock(mLock);
+
+    // We don't really have to worry about overflow here because the only way
+    // to do this is through recursive script loading, which uses the stack. We
+    // would exceed our stack limit long before this counter.
+    NS_ASSERTION(mFeatureSuspendDepth < PR_UINT32_MAX, "Shouldn't happen!");
+    if (++mFeatureSuspendDepth != 1) {
+      // Allow nested suspending of timeouts.
+      return;
+    }
+
+#ifdef DEBUG
+    nsRefPtr<nsDOMWorkerFeature>* newFeatures =
+#endif
+    features.AppendElements(mFeatures);
+    NS_WARN_IF_FALSE(newFeatures, "Out of memory!");
+  }
+
+  PRUint32 count = features.Length();
+  for (PRUint32 i = 0; i < count; i++) {
+    features[i]->Suspend();
+  }
+}
+
+void
+nsDOMWorker::ResumeFeatures()
+{
+  nsAutoTArray<nsRefPtr<nsDOMWorkerFeature>, 20> features;
+  {
+    MutexAutoLock lock(mLock);
+
+    NS_ASSERTION(mFeatureSuspendDepth > 0, "Shouldn't happen!");
+    if (--mFeatureSuspendDepth != 0) {
+      return;
+    }
+
+    features.AppendElements(mFeatures);
+  }
+
+  PRUint32 count = features.Length();
+  for (PRUint32 i = 0; i < count; i++) {
+    features[i]->Resume();
+  }
+}
+
+void
+nsDOMWorker::SetPrincipal(nsIPrincipal* aPrincipal)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aPrincipal, "Null pointer!");
+
+  mPrincipal = aPrincipal;
+}
+
+nsresult
+nsDOMWorker::SetBaseURI(nsIURI* aURI)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aURI, "Don't hand me a null pointer!");
+
+  mBaseURI = aURI;
+
+  nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
+  NS_ENSURE_TRUE(url, NS_ERROR_NO_INTERFACE);
+
+  mLocation = nsDOMWorkerLocation::NewLocation(url);
+  NS_ENSURE_TRUE(mLocation, NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+void
+nsDOMWorker::ClearBaseURI()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  mBaseURI = nsnull;
+  mLocation = nsnull;
+}
+
+nsresult
+nsDOMWorker::FireCloseRunnable(PRIntervalTime aTimeoutInterval,
+                               PRBool aClearQueue,
+                               PRBool aFromFinalize)
+{
+  // Resume the worker (but not its features) if we're currently suspended. This
+  // should only ever happen if we are being called from Cancel (page falling
+  // out of bfcache or quitting) or Finalize, in which case all we really want
+  // to do is unblock the waiting thread.
+  PRBool wakeUp;
+  {
+    MutexAutoLock lock(mLock);
+    NS_ASSERTION(mExpirationTime == 0,
+                 "Close runnable should not be scheduled already!");
+
+    if ((wakeUp = mSuspended)) {
+      NS_ASSERTION(mStatus == eCanceled ||
+                   (mStatus == eTerminated && aFromFinalize),
+                   "How can this happen otherwise?!");
+      mSuspended = PR_FALSE;
+    }
+  }
+
+  if (wakeUp) {
+    ReentrantMonitorAutoEnter mon(mPool->GetReentrantMonitor());
+    mon.NotifyAll();
+  }
+
+  nsRefPtr<nsDOMWorkerEvent> event = new nsDOMWorkerEvent();
+  NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv =
+    event->InitEvent(NS_LITERAL_STRING("close"), PR_FALSE, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsDOMFireEventRunnable> runnable =
+    new nsDOMFireEventRunnable(this, event, PR_TRUE);
+  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+  // Our worker has been collected and we want to keep the inner scope alive,
+  // so pass that along in the runnable.
+  if (aFromFinalize) {
+    // Make sure that our scope wrapped native exists here, but if the worker
+    // script failed to compile then it will be null already.
+    if (mGlobal) {
+      NS_ASSERTION(mScopeWN, "This shouldn't be null!");
+    }
+    runnable->ReplaceWrappedNative(mScopeWN);
+  }
+
+  return nsDOMThreadService::get()->Dispatch(this, runnable, aTimeoutInterval,
+                                             aClearQueue);
+}
+
+nsresult
+nsDOMWorker::Close()
+{
+  {
+    MutexAutoLock lock(mLock);
+    NS_ASSERTION(mStatus != eKilled, "This should be impossible!");
+    if (mStatus != eRunning) {
+      return NS_OK;
+    }
+    mStatus = eClosed;
+  }
+
+  nsresult rv = FireCloseRunnable(PR_INTERVAL_NO_TIMEOUT, PR_FALSE, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMWorker::TerminateInternal(PRBool aFromFinalize)
+{
+  {
+    MutexAutoLock lock(mLock);
+#ifdef DEBUG
+    if (!aFromFinalize) {
+      NS_ASSERTION(mStatus != eCanceled, "Shouldn't be able to get here!");
+    }
+#endif
+
+    if (mStatus == eRunning) {
+      // This is the beginning of the close process, fire an event and prevent
+      // any other close events from being generated.
+      mStatus = eTerminated;
+    }
+    else {
+      if (mStatus == eClosed) {
+        // The worker was previously closed which means that an expiration time
+        // might not be set. Setting the status to eTerminated will force the
+        // worker to jump to its close handler.
+        mStatus = eTerminated;
+      }
+      // No need to fire another close handler, it has already been done.
+      return NS_OK;
+    }
+  }
+
+  nsresult rv = FireCloseRunnable(PR_INTERVAL_NO_TIMEOUT, PR_TRUE,
+                                  aFromFinalize);
+  if (rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
+    return rv;
+  }
+
+  // Warn about other kinds of failures.
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+already_AddRefed<nsDOMWorker>
+nsDOMWorker::GetParent()
+{
+  nsRefPtr<nsDOMWorker> parent(mParent);
+  return parent.forget();
+}
+
+void
+nsDOMWorker::SetExpirationTime(PRIntervalTime aExpirationTime)
+{
+  {
+    MutexAutoLock lock(mLock);
+
+    NS_ASSERTION(mStatus != eRunning && mStatus != eKilled, "Bad status!");
+    NS_ASSERTION(!mExpirationTime || mExpirationTime == PR_INTERVAL_NO_TIMEOUT,
+                 "Overwriting a timeout that was previously set!");
+
+    mExpirationTime = aExpirationTime;
+  }
+}
+
+#ifdef DEBUG
+PRIntervalTime
+nsDOMWorker::GetExpirationTime()
+{
+  MutexAutoLock lock(mLock);
+  return mExpirationTime;
+}
+#endif
+
+// static
+JSObject*
+nsDOMWorker::ReadStructuredClone(JSContext* aCx,
+                                 JSStructuredCloneReader* aReader,
+                                 uint32 aTag,
+                                 uint32 aData,
+                                 void* aClosure)
+{
+  NS_ASSERTION(aCx, "Null context!");
+  NS_ASSERTION(aReader, "Null reader!");
+  NS_ASSERTION(!aClosure, "Shouldn't have a closure here!");
+
+  if (aTag == DOMWORKER_SCTAG_WRAPPEDNATIVE) {
+    NS_ASSERTION(!aData, "Huh?");
+
+    nsISupports* wrappedNative;
+    if (JS_ReadBytes(aReader, &wrappedNative, sizeof(wrappedNative))) {
+      NS_ASSERTION(wrappedNative, "Null pointer?!");
+
+      JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
+      if (global) {
+        jsval val;
+        nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
+        if (NS_SUCCEEDED(nsContentUtils::WrapNative(aCx, global, wrappedNative,
+                                                    &val,
+                                                    getter_AddRefs(wrapper)))) {
+          return JSVAL_TO_OBJECT(val);
+        }
+      }
+    }
+  }
+
+  // Something failed above, try using the runtime callbacks instead.
+  const JSStructuredCloneCallbacks* runtimeCallbacks =
+    aCx->runtime->structuredCloneCallbacks;
+  if (runtimeCallbacks) {
+    return runtimeCallbacks->read(aCx, aReader, aTag, aData, nsnull);
+  }
+
+  // We can't handle this object, throw an exception if one hasn't been thrown
+  // already.
+  if (!JS_IsExceptionPending(aCx)) {
+    nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+  }
+  return nsnull;
+}
+
+PRBool
+nsDOMWorker::QueueSuspendedRunnable(nsIRunnable* aRunnable)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  return mQueuedRunnables.AppendElement(aRunnable) ? PR_TRUE : PR_FALSE;
+}
+
+NS_IMETHODIMP
+nsDOMWorker::RemoveEventListener(const nsAString& aType,
+                                 nsIDOMEventListener* aListener,
+                                 PRBool aUseCapture)
+{
+  if (IsCanceled()) {
+    return NS_OK;
+  }
+
+  return nsDOMWorkerMessageHandler::RemoveEventListener(aType, aListener,
+                                                        aUseCapture);
+}
+
+NS_IMETHODIMP
+nsDOMWorker::DispatchEvent(nsIDOMEvent* aEvent,
+                           PRBool* _retval)
+{
+  {
+    MutexAutoLock lock(mLock);
+    if (IsCanceledNoLock()) {
+      return NS_OK;
+    }
+    if (mStatus == eTerminated) {
+      nsCOMPtr<nsIWorkerMessageEvent> messageEvent(do_QueryInterface(aEvent));
+      if (messageEvent) {
+        // This is a message event targeted to a terminated worker. Ignore it.
+        return NS_OK;
+      }
+    }
+  }
+
+  return nsDOMWorkerMessageHandler::DispatchEvent(aEvent, _retval);
+}
+
+NS_IMETHODIMP
+nsDOMWorker::AddEventListener(const nsAString& aType,
+                              nsIDOMEventListener* aListener,
+                              PRBool aUseCapture,
+                              PRBool aWantsUntrusted,
+                              PRUint8 aOptionalArgc)
+{
+  NS_ASSERTION(mWrappedNative, "Called after Finalize!");
+  if (IsCanceled()) {
+    return NS_OK;
+  }
+
+  return nsDOMWorkerMessageHandler::AddEventListener(aType, aListener,
+                                                     aUseCapture,
+                                                     aWantsUntrusted,
+                                                     aOptionalArgc);
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::PostMessage(/* JSObject aMessage */)
+{
+  {
+    MutexAutoLock lock(mLock);
+    // There's no reason to dispatch this message after the close handler has
+    // been triggered since it will never be allowed to run.
+    if (mStatus != eRunning) {
+      return NS_OK;
+    }
+  }
+
+  return PostMessageInternal(PR_TRUE);
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::GetOnerror(nsIDOMEventListener** aOnerror)
+{
+  NS_ENSURE_ARG_POINTER(aOnerror);
+
+  if (IsCanceled()) {
+    *aOnerror = nsnull;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("error"));
+
+  listener.forget(aOnerror);
+  return NS_OK;
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::SetOnerror(nsIDOMEventListener* aOnerror)
+{
+  NS_ASSERTION(mWrappedNative, "Called after Finalize!");
+  if (IsCanceled()) {
+    return NS_OK;
+  }
+
+  return SetOnXListener(NS_LITERAL_STRING("error"), aOnerror);
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::GetOnmessage(nsIDOMEventListener** aOnmessage)
+{
+  NS_ENSURE_ARG_POINTER(aOnmessage);
+
+  if (IsCanceled()) {
+    *aOnmessage = nsnull;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMEventListener> listener =
+    GetOnXListener(NS_LITERAL_STRING("message"));
+
+  listener.forget(aOnmessage);
+  return NS_OK;
+}
+
+/**
+ * See nsIWorker
+ */
+NS_IMETHODIMP
+nsDOMWorker::SetOnmessage(nsIDOMEventListener* aOnmessage)
+{
+  NS_ASSERTION(mWrappedNative, "Called after Finalize!");
+  if (IsCanceled()) {
+    return NS_OK;
+  }
+
+  return SetOnXListener(NS_LITERAL_STRING("message"), aOnmessage);
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Terminate()
+{
+  return TerminateInternal(PR_FALSE);
+}
+
+NS_IMETHODIMP
+nsDOMWorker::Notify(nsITimer* aTimer)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  Kill();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWorkerFactory::NewChromeWorker(nsIWorker** _retval)
+{
+  nsresult rv;
+
+  // Get the arguments from XPConnect.
+  nsCOMPtr<nsIXPConnect> xpc;
+  xpc = do_GetService(nsIXPConnect::GetCID());
+  NS_ASSERTION(xpc, "Could not get XPConnect");
+
+  nsAXPCNativeCallContext* cc;
+  rv = xpc->GetCurrentNativeCallContext(&cc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  JSContext* cx;
+  rv = cc->GetJSContext(&cx);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 argc;
+  rv = cc->GetArgc(&argc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  jsval* argv;
+  rv = cc->GetArgvPtr(&argv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Determine the current script global. We need it to register the worker.
+  // NewChromeDOMWorker will check that we are chrome, so no access check.
+  JSObject* global = JS_GetGlobalForScopeChain(cx);
+  NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
+
+  // May be null if we're being called from a JSM or something.
+  nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
+    nsJSUtils::GetStaticScriptGlobal(cx, global);
+
+  // Create, initialize, and return the worker.
+  nsRefPtr<nsDOMWorker> chromeWorker;
+  rv = nsDOMWorker::NewChromeDOMWorker(getter_AddRefs(chromeWorker));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = chromeWorker->InitializeInternal(scriptGlobal, cx, global, argc, argv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  chromeWorker.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(nsWorkerFactory, nsIWorkerFactory)
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorker.h
@@ -0,0 +1,471 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** 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
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@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 __NSDOMWORKER_H__
+#define __NSDOMWORKER_H__
+
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMWorkers.h"
+#include "nsIJSNativeInitializer.h"
+#include "nsIPrincipal.h"
+#include "nsITimer.h"
+#include "nsIURI.h"
+#include "nsIXPCScriptable.h"
+
+#include "jsapi.h"
+#include "mozilla/Mutex.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTPtrArray.h"
+
+#include "nsDOMWorkerMessageHandler.h"
+
+// {1295EFB5-8644-42B2-8B8E-80EEF56E4284}
+#define NS_WORKERFACTORY_CID \
+ {0x1295efb5, 0x8644, 0x42b2, \
+  {0x8b, 0x8e, 0x80, 0xee, 0xf5, 0x6e, 0x42, 0x84} }
+
+class nsDOMWorker;
+class nsDOMWorkerFeature;
+class nsDOMWorkerMessageHandler;
+class nsDOMWorkerNavigator;
+class nsDOMWorkerPool;
+class nsDOMWorkerTimeout;
+class nsICancelable;
+class nsIDOMEventListener;
+class nsIEventTarget;
+class nsIRunnable;
+class nsIScriptGlobalObject;
+class nsIXPConnectWrappedNative;
+
+class nsDOMWorkerScope : public nsDOMWorkerMessageHandler,
+                         public nsIWorkerScope,
+                         public nsIXPCScriptable
+{
+  friend class nsDOMWorker;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMEventHandler
+  NS_FORWARD_INTERNAL_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
+  NS_IMETHOD AddEventListener(const nsAString& aType,
+                              nsIDOMEventListener* aListener,
+                              PRBool aUseCapture,
+                              PRBool aWantsUntrusted,
+                              PRUint8 optional_argc);
+  NS_IMETHOD RemoveEventListener(const nsAString& aType,
+                                 nsIDOMEventListener* aListener,
+                                 PRBool aUseCapture);
+  NS_IMETHOD DispatchEvent(nsIDOMEvent* aEvent,
+                           PRBool* _retval);
+  NS_DECL_NSIWORKERGLOBALSCOPE
+  NS_DECL_NSIWORKERSCOPE
+  NS_DECL_NSIXPCSCRIPTABLE
+  NS_DECL_NSICLASSINFO
+
+  typedef NS_STDCALL_FUNCPROTO(nsresult, SetListenerFunc, nsDOMWorkerScope,
+                               SetOnmessage, (nsIDOMEventListener*));
+
+  nsDOMWorkerScope(nsDOMWorker* aWorker);
+
+protected:
+  already_AddRefed<nsIXPConnectWrappedNative> GetWrappedNative();
+
+private:
+  nsDOMWorker* mWorker;
+  nsIXPConnectWrappedNative* mWrappedNative;
+
+  nsRefPtr<nsDOMWorkerNavigator> mNavigator;
+
+  PRPackedBool mHasOnerror;
+};
+
+class nsLazyAutoRequest
+{
+public:
+  nsLazyAutoRequest() : mCx(nsnull) {}
+
+  ~nsLazyAutoRequest() {
+    if (mCx)
+      JS_EndRequest(mCx);
+  }
+
+  void enter(JSContext *aCx) {
+    JS_BeginRequest(aCx);
+    mCx = aCx;
+  }
+
+  bool entered() const { return mCx != nsnull; }
+
+  void swap(nsLazyAutoRequest &other) {
+    JSContext *tmp = mCx;
+    mCx = other.mCx;
+    other.mCx = tmp;
+  }
+
+private:
+  JSContext *mCx;
+};
+
+class nsDOMWorker : public nsDOMWorkerMessageHandler,
+                    public nsIWorker,
+                    public nsITimerCallback,
+                    public nsIJSNativeInitializer,
+                    public nsIXPCScriptable
+{
+  typedef mozilla::Mutex Mutex;
+
+  friend class nsDOMWorkerFeature;
+  friend class nsDOMWorkerFunctions;
+  friend class nsDOMWorkerScope;
+  friend class nsDOMWorkerScriptLoader;
+  friend class nsDOMWorkerTimeout;
+  friend class nsDOMWorkerXHR;
+  friend class nsDOMWorkerXHRProxy;
+  friend class nsReportErrorRunnable;
+  friend class nsDOMFireEventRunnable;
+
+  friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
+  friend void DOMWorkerErrorReporter(JSContext* aCx,
+                                     const char* aMessage,
+                                     JSErrorReport* aReport);
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // nsIDOMEventHandler
+  NS_FORWARD_INTERNAL_NSIDOMEVENTTARGET(nsDOMWorkerMessageHandler::)
+  NS_IMETHOD AddEventListener(const nsAString& aType,
+                              nsIDOMEventListener* aListener,
+                              PRBool aUseCapture,
+                              PRBool aWantsUntrusted,
+                              PRUint8 optional_argc);
+  NS_IMETHOD RemoveEventListener(const nsAString& aType,
+                                 nsIDOMEventListener* aListener,
+                                 PRBool aUseCapture);
+  NS_IMETHOD DispatchEvent(nsIDOMEvent* aEvent,
+                           PRBool* _retval);
+
+  NS_DECL_NSIABSTRACTWORKER
+  NS_DECL_NSIWORKER
+  NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSICLASSINFO
+  NS_DECL_NSIXPCSCRIPTABLE
+
+  static nsresult NewWorker(nsISupports** aNewObject);
+  static nsresult NewChromeWorker(nsISupports** aNewObject);
+  static nsresult NewChromeDOMWorker(nsDOMWorker** aNewObject);
+
+  enum WorkerPrivilegeModel { CONTENT, CHROME };
+
+  nsDOMWorker(nsDOMWorker* aParent,
+              nsIXPConnectWrappedNative* aParentWN,
+              WorkerPrivilegeModel aModel);
+
+  NS_IMETHOD Initialize(nsISupports* aOwner,
+                        JSContext* aCx,
+                        JSObject* aObj,
+                        PRUint32 aArgc,
+                        jsval* aArgv);
+
+  nsresult InitializeInternal(nsIScriptGlobalObject* aOwner,
+                              JSContext* aCx,
+                              JSObject* aObj,
+                              PRUint32 aArgc,
+                              jsval* aArgv);
+
+  void Cancel();
+  void Kill();
+  void Suspend();
+  void Resume();
+
+  // This just calls IsCanceledNoLock with an autolock around the call.
+  PRBool IsCanceled();
+
+  PRBool IsClosing();
+  PRBool IsSuspended();
+
+  PRBool SetGlobalForContext(JSContext* aCx, nsLazyAutoRequest *aRequest, JSAutoEnterCompartment *aComp);
+
+  void SetPool(nsDOMWorkerPool* aPool);
+
+  nsDOMWorkerPool* Pool() {
+    return mPool;
+  }
+
+  Mutex& GetLock() {
+    return mLock;
+  }
+
+  already_AddRefed<nsIXPConnectWrappedNative> GetWrappedNative();
+  already_AddRefed<nsDOMWorker> GetParent();
+
+  nsDOMWorkerScope* GetInnerScope() {
+    return mInnerScope;
+  }
+
+  void SetExpirationTime(PRIntervalTime aExpirationTime);
+#ifdef DEBUG
+  PRIntervalTime GetExpirationTime();
+#endif
+
+  PRBool IsPrivileged() {
+    return mPrivilegeModel == CHROME;
+  }
+
+  static JSObject* ReadStructuredClone(JSContext* aCx,
+                                       JSStructuredCloneReader* aReader,
+                                       uint32 aTag,
+                                       uint32 aData,
+                                       void* aClosure);
+
+  /**
+   * Use this chart to help figure out behavior during each of the closing
+   * statuses. Details below.
+   * 
+   * +=============+=============+=================+=======================+
+   * |   status    | clear queue | abort execution | close handler timeout |
+   * +=============+=============+=================+=======================+
+   * |   eClosed   |     yes     |       no        |          no           |
+   * +-------------+-------------+-----------------+-----------------------+
+   * | eTerminated |     yes     |       yes       |          no           |
+   * +-------------+-------------+-----------------+-----------------------+
+   * |  eCanceled  |     yes     |       yes       |          yes          |
+   * +-------------+-------------+-----------------+-----------------------+
+   * 
+   */
+
+  enum DOMWorkerStatus {
+    // This status means that the close handler has not yet been scheduled.
+    eRunning = 0,
+
+    // Inner script called Close() on the worker global scope. Setting this
+    // status causes the worker to clear its queue of events but does not abort
+    // the currently running script. The close handler is also scheduled with
+    // no expiration time. This status may be superseded by 'eTerminated' in
+    // which case the currently running script will be aborted as detailed
+    // below. It may also be superseded by 'eCanceled' at which point the close
+    // handler will be assigned an expiration time. Once the close handler has
+    // completed or timed out the status will be changed to 'eKilled'.
+    eClosed,
+
+    // Outer script called Terminate() on the worker or the worker object was
+    // garbage collected in its outer script. Setting this status causes the
+    // worker to abort immediately, clear its queue of events, and schedules the
+    // close handler with no expiration time. This status may be superseded by
+    // 'eCanceled' at which point the close handler will have an expiration time
+    // assigned. Once the close handler has completed or timed out the status
+    // will be changed to 'eKilled'.
+    eTerminated,
+
+    // Either the user navigated away from the owning page, the owning page fell
+    // out of bfcache, or the user quit the application. Setting this status
+    // causes the worker to abort immediately and schedules the close handler
+    // with an expiration time. Since the page has gone away the worker may not
+    // post any messages. Once the close handler has completed or timed out the
+    // status will be changed to 'eKilled'.
+    eCanceled,
+
+    // The close handler has run and the worker is effectively dead.
+    eKilled
+  };
+
+private:
+  ~nsDOMWorker();
+
+  nsresult PostMessageInternal(PRBool aToInner);
+
+  PRBool CompileGlobalObject(JSContext* aCx, nsLazyAutoRequest *aRequest, JSAutoEnterCompartment *aComp);
+
+  PRUint32 NextTimeoutId() {
+    return ++mNextTimeoutId;
+  }
+
+  nsresult AddFeature(nsDOMWorkerFeature* aFeature,
+                      JSContext* aCx);
+  void RemoveFeature(nsDOMWorkerFeature* aFeature,
+                     JSContext* aCx);
+  void CancelTimeoutWithId(PRUint32 aId);
+  void SuspendFeatures();
+  void ResumeFeatures();
+
+  nsIPrincipal* GetPrincipal() {
+    return mPrincipal;
+  }
+
+  void SetPrincipal(nsIPrincipal* aPrincipal);
+
+  nsIURI* GetBaseURI() {
+    return mBaseURI;
+  }
+
+  nsresult SetBaseURI(nsIURI* aURI);
+
+  void ClearBaseURI();
+
+  nsresult FireCloseRunnable(PRIntervalTime aTimeoutInterval,
+                             PRBool aClearQueue,
+                             PRBool aFromFinalize);
+  nsresult Close();
+
+  nsresult TerminateInternal(PRBool aFromFinalize);
+
+  nsIWorkerLocation* GetLocation() {
+    return mLocation;
+  }
+
+  PRBool QueueSuspendedRunnable(nsIRunnable* aRunnable);
+
+  // Determines if the worker should be considered "canceled". See the large
+  // comment in the implementation for more details.
+  PRBool IsCanceledNoLock();
+
+private:
+
+  // mParent will live as long as mParentWN but only mParentWN will keep the JS
+  // reflection alive, so we only hold one strong reference to mParentWN.
+  nsDOMWorker* mParent;
+  nsCOMPtr<nsIXPConnectWrappedNative> mParentWN;
+
+  // Whether or not this worker has chrome privileges. Never changed after the
+  // worker is created.
+  WorkerPrivilegeModel mPrivilegeModel;
+
+  Mutex mLock;
+
+  nsRefPtr<nsDOMWorkerPool> mPool;
+
+  nsDOMWorkerScope* mInnerScope;
+  nsCOMPtr<nsIXPConnectWrappedNative> mScopeWN;
+  JSObject* mGlobal;
+
+  PRUint32 mNextTimeoutId;
+
+  nsTArray<nsDOMWorkerFeature*> mFeatures;
+  PRUint32 mFeatureSuspendDepth;
+
+  nsString mScriptURL;
+
+  nsIXPConnectWrappedNative* mWrappedNative;
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsIURI> mBaseURI;
+
+  PRInt32 mErrorHandlerRecursionCount;
+
+  // Always protected by mLock
+  DOMWorkerStatus mStatus;
+
+  // Always protected by mLock
+  PRIntervalTime mExpirationTime;
+
+  nsCOMPtr<nsITimer> mKillTimer;
+
+  nsCOMPtr<nsIWorkerLocation> mLocation;
+
+  nsTArray<nsCOMPtr<nsIRunnable> > mQueuedRunnables;
+
+  PRPackedBool mSuspended;
+  PRPackedBool mCompileAttempted;
+};
+
+/**
+ * A worker "feature" holds the worker alive yet can be canceled, paused, and
+ * resumed by the worker. It is up to each derived class to implement these
+ * methods. This class uses a custom implementation of Release in order to
+ * ensure no races between Cancel and object destruction can occur, so derived
+ * classes must use the ISUPPORTS_INHERITED macros.
+ *
+ * To use this class you should inherit it and use the ISUPPORTS_INHERITED
+ * macros. Then add or remove an instance to the worker using the
+ * AddFeature/RemoveFeature functions. 
+ */
+class nsDOMWorkerFeature : public nsISupports
+{
+  friend class nsDOMWorker;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  nsDOMWorkerFeature(nsDOMWorker* aWorker)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mId(0),
+    mHasId(PR_FALSE), mFreeToDie(PR_TRUE) { }
+
+  nsDOMWorkerFeature(nsDOMWorker* aWorker, PRUint32 aId)
+  : mWorker(aWorker), mWorkerWN(aWorker->GetWrappedNative()), mId(aId),
+    mHasId(PR_TRUE), mFreeToDie(PR_TRUE) { }
+
+  virtual void Cancel() = 0;
+  virtual void Suspend() { }
+  virtual void Resume() { }
+
+  PRUint32 GetId() {
+    return mId;
+  }
+
+  PRBool HasId() {
+    return mHasId;
+  }
+
+protected:
+  virtual ~nsDOMWorkerFeature() { }
+
+private:
+  void FreeToDie(PRBool aFreeToDie) {
+    mFreeToDie = aFreeToDie;
+  }
+
+protected:
+  nsRefPtr<nsDOMWorker> mWorker;
+  nsCOMPtr<nsIXPConnectWrappedNative> mWorkerWN;
+  PRUint32 mId;
+
+private:
+  PRPackedBool mHasId;
+  PRPackedBool mFreeToDie;
+};
+
+class nsWorkerFactory : public nsIWorkerFactory
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWORKERFACTORY
+};
+
+#endif /* __NSDOMWORKER_H__ */
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerEvents.cpp
@@ -0,0 +1,635 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** 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
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@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 ***** */
+
+#include "nsDOMWorkerEvents.h"
+
+#include "nsIXMLHttpRequest.h"
+#include "nsIXPConnect.h"
+
+#include "jsapi.h"
+#include "nsAXPCNativeCallContext.h"