Merge for back out tracked by bug 655860. Also backs out bug 654990 because it depends on bugs in the back out range.
authorShawn Wilsher <me@shawnwilsher.com>
Mon, 09 May 2011 15:52:44 -0700
changeset 69465 65316725d03b17320542473d89362b5ccfd2f84b
parent 69464 dd9ba28d2bd9b813eed90c551bbf6d2cda5832b1 (current diff)
parent 69463 0abb7330c64ca7d61fa92e2e33a3117a5df452de (diff)
child 69466 7b78caa06ec2adf542c0e1c89792be82c2942494
push id76
push userbzbarsky@mozilla.com
push dateTue, 05 Jul 2011 17:00:57 +0000
treeherdermozilla-beta@d3a2732c35f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs655860, 654990
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge for back out tracked by bug 655860. Also backs out bug 654990 because it depends on bugs in the back out range.
layout/base/nsCSSFrameConstructor.cpp
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -987,16 +987,22 @@ pref("services.sync.prefs.sync.signon.re
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
 // Disable the error console and inspector
 pref("devtools.errorconsole.enabled", false);
 pref("devtools.inspector.enabled", false);
 
+// Enable the Scratchpad tool.
+pref("devtools.scratchpad.enabled", true);
+
+// Enable tools for Chrome development.
+pref("devtools.chrome.enabled", false);
+
 // The last Web Console height. This is initially 0 which means that the Web
 // Console will use the default height next time it shows.
 // Change to -1 if you do not want the Web Console to remember its last height.
 pref("devtools.hud.height", 0);
 
 // Whether the character encoding menu is under the main Firefox button. This
 // preference is a string so that localizers can alter it.
 pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -182,16 +182,21 @@
                     oncommand="HUDConsoleUI.toggleHUD();"
                     key="key_webConsole"/>
           <menuitem id="appmenu_pageInspect"
                     hidden="true"
                     label="&inspectMenu.label;"
                     type="checkbox"
                     command="Tools:Inspect"
                     key="key_inspect"/>
+          <menuitem id="appmenu_scratchpad"
+                    hidden="true"
+                    label="&scratchpad.label;"
+                    key="key_scratchpad"
+                    command="Tools:Scratchpad"/>
           <menuitem id="appmenu_pageSource"
                     label="&viewPageSourceCmd.label;"
                     command="View:PageSource"
                     key="key_viewSource"/>
           <menuitem id="appmenu_errorConsole"
                     hidden="true"
                     label="&errorConsoleCmd.label;"
                     key="key_errorConsole"
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -310,21 +310,16 @@
                               accesskey="&pageStylePersistentOnly.accesskey;"
                               type="radio"
                               checked="true"/>
                     <menuseparator/>
                   </menupopup>
                 </menu>
 #include browser-charsetmenu.inc
                 <menuseparator/>
-                <menuitem id="menu_pageSource"
-                          accesskey="&pageSourceCmd.accesskey;"
-                          label="&pageSourceCmd.label;"
-                          key="key_viewSource"
-                          command="View:PageSource"/>
                 <menuitem id="fullScreenItem"
                           accesskey="&fullScreenCmd.accesskey;"
                           label="&fullScreenCmd.label;"
                           key="key_fullScreen"
                           type="checkbox"
                           observes="View:FullScreen"/>
                 <menuitem id="menu_showAllTabs"
                           hidden="true"
@@ -534,34 +529,51 @@
                         oncommand="gSyncUI.openSetup()"/>
               <menuitem id="sync-syncnowitem"
                         label="&syncSyncNowItem.label;"
                         accesskey="&syncSyncNowItem.accesskey;"
                         observes="sync-syncnow-state"
                         oncommand="gSyncUI.doSync(event);"/>
 #endif
               <menuseparator id="devToolsSeparator"/>
-              <menuitem id="menu_pageinspect"
-                        type="checkbox"
-                        hidden="true"
-                        label="&inspectMenu.label;"
-                        accesskey="&inspectMenu.accesskey;"
-                        key="key_inspect"
-                        command="Tools:Inspect"/>
-              <menuitem id="javascriptConsole"
-                        hidden="true"
-                        label="&errorConsoleCmd.label;"
-                        accesskey="&errorConsoleCmd.accesskey;"
-                        key="key_errorConsole"
-                        oncommand="toJavaScriptConsole();"/>
-              <menuitem id="webConsole"
-                        label="&webConsoleCmd.label;"
-                        accesskey="&webConsoleCmd.accesskey;"
-                        key="key_webConsole"
-                        oncommand="HUDConsoleUI.toggleHUD();"/>
+              <menu id="webDeveloperMenu"
+                    label="&webDeveloperMenu.label;"
+                    accesskey="webDeveloperMenu.accesskey;">
+                <menupopup id="menuWebDeveloperPopup">
+                  <menuitem id="webConsole"
+                            label="&webConsoleCmd.label;"
+                            accesskey="&webConsoleCmd.accesskey;"
+                            key="key_webConsole"
+                            oncommand="HUDConsoleUI.toggleHUD();"/>
+                  <menuitem id="menu_pageinspect"
+                            type="checkbox"
+                            hidden="true"
+                            label="&inspectMenu.label;"
+                            accesskey="&inspectMenu.accesskey;"
+                            key="key_inspect"
+                            command="Tools:Inspect"/>
+                  <menuitem id="menu_scratchpad"
+                            hidden="true"
+                            label="&scratchpad.label;"
+                            accesskey="&scratchpad.accesskey;"
+                            key="key_scratchpad"
+                            command="Tools:Scratchpad"/>
+                  <menuitem id="menu_pageSource"
+                            accesskey="&pageSourceCmd.accesskey;"
+                            label="&pageSourceCmd.label;"
+                            key="key_viewSource"
+                            command="View:PageSource"/>
+                  <menuitem id="javascriptConsole"
+                            hidden="true"
+                            label="&errorConsoleCmd.label;"
+                            accesskey="&errorConsoleCmd.accesskey;"
+                            key="key_errorConsole"
+                            oncommand="toJavaScriptConsole();"/>
+                </menupopup>
+              </menu>
               <menuitem id="menu_pageInfo"
                         accesskey="&pageInfoCmd.accesskey;"
                         label="&pageInfoCmd.label;"
 #ifndef XP_WIN
                         key="key_viewInfo"
 #endif
                         command="View:PageInfo"/>
               <menuseparator id="sanitizeSeparator"/>
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -120,16 +120,17 @@
     <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
     <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="Browser:OpenLocation" oncommand="openLocation();"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
+    <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
   </commandset>
@@ -236,16 +237,18 @@
     <key id="key_openDownloads" key="&downloadsUnix.commandkey;" command="Tools:Downloads" modifiers="accel,shift"/>
 #else
     <key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/>
 #endif
     <key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
     <key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift" disabled="true"/>
     <key id="key_webConsole" key="&webConsoleCmd.commandkey;" oncommand="HUDConsoleUI.toggleHUD();" modifiers="accel,shift"/>
     <key id="key_inspect" key="&inspectMenu.commandkey;" command="Tools:Inspect" modifiers="accel,shift"/>
+    <key id="key_scratchpad" keycode="&scratchpad.keycode;"
+         keytext="&scratchpad.keytext;" command="Tools:Scratchpad"/>
     <key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile"  modifiers="accel"/>
     <key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
     <key id="printKb" key="&printCmd.commandkey;" command="cmd_print"  modifiers="accel"/>
     <key id="key_close" key="&closeCmd.key;" command="cmd_close" modifiers="accel"/>
     <key id="key_closeWindow" key="&closeCmd.key;" command="cmd_closeWindow" modifiers="accel,shift"/>
     <key id="key_undo"
          key="&undoCmd.key;"
          modifiers="accel"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -49,16 +49,17 @@
 #   Nils Maier <maierman@web.de>
 #   Rob Arnold <robarnold@cmu.edu>
 #   Dietrich Ayala <dietrich@mozilla.com>
 #   Gavin Sharp <gavin@gavinsharp.com>
 #   Justin Dolske <dolske@mozilla.com>
 #   Rob Campbell <rcampbell@mozilla.com>
 #   David Dahl <ddahl@mozilla.com>
 #   Patrick Walton <pcwalton@mozilla.com>
+#   Mihai Sucan <mihai.sucan@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
@@ -1648,16 +1649,26 @@ function delayedStartup(isLoadingBlank, 
   if (consoleEnabled) {
     document.getElementById("javascriptConsole").hidden = false;
     document.getElementById("key_errorConsole").removeAttribute("disabled");
 #ifdef MENUBAR_CAN_AUTOHIDE
     document.getElementById("appmenu_errorConsole").hidden = false;
 #endif
   }
 
+  // Enable Scratchpad in the UI, if the preference allows this.
+  let scratchpadEnabled = gPrefService.getBoolPref(Scratchpad.prefEnabledName);
+  if (scratchpadEnabled) {
+    document.getElementById("menu_scratchpad").hidden = false;
+    document.getElementById("Tools:Scratchpad").removeAttribute("disabled");
+#ifdef MENUBAR_CAN_AUTOHIDE
+    document.getElementById("appmenu_scratchpad").hidden = false;
+#endif
+  }
+
 #ifdef MENUBAR_CAN_AUTOHIDE
   // If the user (or the locale) hasn't enabled the top-level "Character
   // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
   // hide it.
   if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
                                              Ci.nsIPrefLocalizedString).data)
     document.getElementById("appmenu_charsetMenu").hidden = true;
 #endif
@@ -8626,16 +8637,29 @@ let AddonsMgrListener = {
   onDisabled: function(aAddon) this.onUninstalled(),
 };
 
 function toggleAddonBar() {
   let addonBar = document.getElementById("addon-bar");
   setToolbarVisibility(addonBar, addonBar.collapsed);
 }
 
+var Scratchpad = {
+  prefEnabledName: "devtools.scratchpad.enabled",
+
+  openScratchpad: function SP_openScratchpad() {
+    const SCRATCHPAD_WINDOW_URL = "chrome://browser/content/scratchpad.xul";
+    const SCRATCHPAD_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+
+    return Services.ww.openWindow(null, SCRATCHPAD_WINDOW_URL, "_blank",
+                                  SCRATCHPAD_WINDOW_FEATURES, null);
+  },
+};
+
+
 XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
 #ifdef XP_WIN
   // Only show resizers on Windows 2000 and XP
   let sysInfo = Components.classes["@mozilla.org/system-info;1"]
                           .getService(Components.interfaces.nsIPropertyBag2);
   return parseFloat(sysInfo.getProperty("version")) < 6;
 #else
   return false;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -235,17 +235,17 @@
     <panel id="inspector-tree-panel"
            orient="vertical"
            hidden="true"
            ignorekeys="true"
            noautofocus="true"
            noautohide="true"
            titlebar="normal"
            close="true"
-           onpopuphiding="InspectorUI.closeInspectorUI(true);"
+           onpopuphiding="InspectorUI.closeInspectorUI();"
            label="&inspectPanelTitle.label;">
       <toolbar id="inspector-toolbar"
                nowindowdrag="true">
         <toolbarbutton id="inspector-inspect-toolbutton"
                        label="&inspectButton.label;"
                        accesskey="&inspectButton.accesskey;"
                        class="toolbarbutton-text"
                        command="Inspector:Inspect"/>
--- a/browser/base/content/inspector.js
+++ b/browser/base/content/inspector.js
@@ -342,17 +342,17 @@ var InspectorUI = {
    * 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)
   {
     if (this.isTreePanelOpen) {
-      this.closeInspectorUI(true);
+      this.closeInspectorUI();
     } else {
       this.openInspectorUI();
     }
   },
 
   /**
    * Toggle the status of the inspector, starting or stopping it. Invoked
    * from the toolbar's Inspect button.
@@ -733,28 +733,30 @@ var InspectorUI = {
     }
   },
 
   /**
    * Close inspector UI and associated panels. Unhighlight and stop inspecting.
    * Remove event listeners for document scrolling, resize,
    * tabContainer.TabSelect and others.
    *
-   * @param boolean aClearStore tells if you want the store associated to the
-   * current tab/window to be cleared or not.
+   * @param boolean aKeepStore
+   *        Tells if you want the store associated to the current tab/window to
+   *        be cleared or not. Set this to true to not clear the store, or false
+   *        otherwise.
    */
-  closeInspectorUI: function IUI_closeInspectorUI(aClearStore)
+  closeInspectorUI: function IUI_closeInspectorUI(aKeepStore)
   {
     if (this.closing || !this.win || !this.browser) {
       return;
     }
 
     this.closing = true;
 
-    if (aClearStore) {
+    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);
       }
@@ -1023,17 +1025,17 @@ var InspectorUI = {
           if (this.isTreePanelOpen && this.isStylePanelOpen &&
               this.isDOMPanelOpen && this.treeLoaded) {
             this.notifyReady();
           }
         break;
       case "TabSelect":
         winID = this.getWindowID(gBrowser.selectedBrowser.contentWindow);
         if (this.isTreePanelOpen && winID != this.winID) {
-          this.closeInspectorUI(false);
+          this.closeInspectorUI(true);
           inspectorClosed = true;
         }
 
         if (winID && InspectorStore.hasID(winID)) {
           if (inspectorClosed && this.closing) {
             Services.obs.addObserver(function () {
               InspectorUI.openInspectorUI();
             }, "inspector-closed", false);
new file mode 100644
--- /dev/null
+++ b/browser/base/content/scratchpad.js
@@ -0,0 +1,627 @@
+/* vim:set ts=2 sw=2 sts=2 et:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Scratchpad.
+ *
+ * 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):
+ *   Rob Campbell <robcee@mozilla.com> (original author)
+ *   Erik Vold <erikvvold@gmail.com>
+ *   David Dahl <ddahl@mozilla.com>
+ *   Mihai Sucan <mihai.sucan@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 *****/
+
+/*
+ * Original version history can be found here:
+ * https://github.com/mozilla/workspace
+ *
+ * Copied and relicensed from the Public Domain.
+ * See bug 653934 for details.
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=653934
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource:///modules/PropertyPanel.jsm");
+
+const SCRATCHPAD_CONTEXT_CONTENT = 1;
+const SCRATCHPAD_CONTEXT_CHROME = 2;
+const SCRATCHPAD_WINDOW_URL = "chrome://browser/content/scratchpad.xul";
+const SCRATCHPAD_L10N = "chrome://browser/locale/scratchpad.properties";
+const SCRATCHPAD_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
+
+/**
+ * The scratchpad object handles the Scratchpad window functionality.
+ */
+var Scratchpad = {
+  /**
+   * The script execution context. This tells Scratchpad in which context the
+   * script shall execute.
+   *
+   * Possible values:
+   *   - SCRATCHPAD_CONTEXT_CONTENT to execute code in the context of the current
+   *   tab content window object.
+   *   - SCRATCHPAD_CONTEXT_CHROME to execute code in the context of the
+   *   currently active chrome window object.
+   */
+  executionContext: SCRATCHPAD_CONTEXT_CONTENT,
+
+  /**
+   * Retrieve the xul:textbox DOM element. This element holds the source code
+   * the user writes and executes.
+   */
+  get textbox() document.getElementById("scratchpad-textbox"),
+
+  /**
+   * Retrieve the xul:statusbarpanel DOM element. The status bar tells the
+   * current code execution context.
+   */
+  get statusbarStatus() document.getElementById("scratchpad-status"),
+
+  /**
+   * Get the selected text from the textbox.
+   */
+  get selectedText()
+  {
+    return this.textbox.value.substring(this.textbox.selectionStart,
+                                        this.textbox.selectionEnd);
+  },
+
+  /**
+   * Get the most recent chrome window of type navigator:browser.
+   */
+  get browserWindow() Services.wm.getMostRecentWindow("navigator:browser"),
+
+  /**
+   * Reference to the last chrome window of type navigator:browser. We use this
+   * to check if the chrome window changed since the last code evaluation.
+   */
+  _previousWindow: null,
+
+  /**
+   * Get the gBrowser object of the most recent browser window.
+   */
+  get gBrowser()
+  {
+    let recentWin = this.browserWindow;
+    return recentWin ? recentWin.gBrowser : null;
+  },
+
+  /**
+   * Cached Cu.Sandbox object for the active tab content window object.
+   */
+  _contentSandbox: null,
+
+  /**
+   * Get the Cu.Sandbox object for the active tab content window object. Note
+   * that the returned object is cached for later reuse. The cached object is
+   * kept only for the current browser window and it is reset for each context
+   * switch or navigator:browser window switch.
+   */
+  get contentSandbox()
+  {
+    if (!this.browserWindow) {
+      Cu.reportError(this.strings.
+                     GetStringFromName("browserWindow.unavailable"));
+      return;
+    }
+
+    if (!this._contentSandbox ||
+        this.browserWindow != this._previousBrowserWindow) {
+      let contentWindow = this.gBrowser.selectedBrowser.contentWindow;
+      this._contentSandbox = new Cu.Sandbox(contentWindow,
+        { sandboxPrototype: contentWindow, wantXrays: false });
+
+      this._previousBrowserWindow = this.browserWindow;
+    }
+
+    return this._contentSandbox;
+  },
+
+  /**
+   * Cached Cu.Sandbox object for the most recently active navigator:browser
+   * chrome window object.
+   */
+  _chromeSandbox: null,
+
+  /**
+   * Get the Cu.Sandbox object for the most recently active navigator:browser
+   * chrome window object. Note that the returned object is cached for later
+   * reuse. The cached object is kept only for the current browser window and it
+   * is reset for each context switch or navigator:browser window switch.
+   */
+  get chromeSandbox()
+  {
+    if (!this.browserWindow) {
+      Cu.reportError(this.strings.
+                     GetStringFromName("browserWindow.unavailable"));
+      return;
+    }
+
+    if (!this._chromeSandbox ||
+        this.browserWindow != this._previousBrowserWindow) {
+      this._chromeSandbox = new Cu.Sandbox(this.browserWindow,
+        { sandboxPrototype: this.browserWindow, wantXrays: false });
+
+      this._previousBrowserWindow = this.browserWindow;
+    }
+
+    return this._chromeSandbox;
+  },
+
+  /**
+   * Drop the textbox selection.
+   */
+  deselect: function SP_deselect()
+  {
+    this.textbox.selectionEnd = this.textbox.selectionStart;
+  },
+
+  /**
+   * Select a specific range in the Scratchpad xul:textbox.
+   *
+   * @param number aStart
+   *        Selection range start.
+   * @param number aEnd
+   *        Selection range end.
+   */
+  selectRange: function SP_selectRange(aStart, aEnd)
+  {
+    this.textbox.selectionStart = aStart;
+    this.textbox.selectionEnd = aEnd;
+  },
+
+  /**
+   * Evaluate a string in the active tab content window.
+   *
+   * @param string aString
+   *        The script you want evaluated.
+   * @return mixed
+   *         The script evaluation result.
+   */
+  evalInContentSandbox: function SP_evalInContentSandbox(aString)
+  {
+    let result;
+    try {
+      result = Cu.evalInSandbox(aString, this.contentSandbox, "1.8",
+                                "Scratchpad", 1);
+    }
+    catch (ex) {
+      this.openWebConsole();
+
+      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));
+
+      Services.console.logMessage(scriptError);
+    }
+
+    return result;
+  },
+
+  /**
+   * Evaluate a string in the most recent navigator:browser chrome window.
+   *
+   * @param string aString
+   *        The script you want evaluated.
+   * @return mixed
+   *         The script evaluation result.
+   */
+  evalInChromeSandbox: function SP_evalInChromeSandbox(aString)
+  {
+    let result;
+    try {
+      result = Cu.evalInSandbox(aString, this.chromeSandbox, "1.8",
+                                "Scratchpad", 1);
+    }
+    catch (ex) {
+      Cu.reportError(ex);
+      Cu.reportError(ex.stack);
+      this.openErrorConsole();
+    }
+
+    return result;
+  },
+
+  /**
+   * Evaluate a string in the currently desired context, that is either the
+   * chrome window or the tab content window object.
+   *
+   * @param string aString
+   *        The script you want to evaluate.
+   * @return mixed
+   *         The script evaluation result.
+   */
+  evalForContext: function SP_evaluateForContext(aString)
+  {
+    return this.executionContext == SCRATCHPAD_CONTEXT_CONTENT ?
+           this.evalInContentSandbox(aString) :
+           this.evalInChromeSandbox(aString);
+  },
+
+  /**
+   * Execute the selected text (if any) or the entire textbox content in the
+   * current context.
+   */
+  execute: function SP_execute()
+  {
+    let selection = this.selectedText || this.textbox.value;
+    let result = this.evalForContext(selection);
+    this.deselect();
+    return [selection, result];
+  },
+
+  /**
+   * Execute the selected text (if any) or the entire textbox content in the
+   * current context. The resulting object is opened up in the Property Panel
+   * for inspection.
+   */
+  inspect: function SP_inspect()
+  {
+    let [selection, result] = this.execute();
+
+    if (result) {
+      this.openPropertyPanel(selection, result);
+    }
+  },
+
+  /**
+   * Execute the selected text (if any) or the entire textbox content in the
+   * current context. The evaluation result is "printed" in the textbox after
+   * the selected text, or at the end of the textbox value if there is no
+   * selected text.
+   */
+  print: function SP_print()
+  {
+    let selectionStart = this.textbox.selectionStart;
+    let selectionEnd = this.textbox.selectionEnd;
+    if (selectionStart == selectionEnd) {
+      selectionEnd = this.textbox.value.length;
+    }
+
+    let [selection, result] = this.execute();
+    if (!result) {
+      return;
+    }
+
+    let firstPiece = this.textbox.value.slice(0, selectionEnd);
+    let lastPiece = this.textbox.value.
+                    slice(selectionEnd, this.textbox.value.length);
+
+    let newComment = "/*\n" + result.toString() + "\n*/";
+
+    this.textbox.value = firstPiece + newComment + lastPiece;
+
+    // Select the added comment.
+    this.selectRange(firstPiece.length, firstPiece.length + newComment.length);
+  },
+
+  /**
+   * Open the Property Panel to inspect the given object.
+   *
+   * @param string aEvalString
+   *        The string that was evaluated. This is re-used when the user updates
+   *        the properties list, by clicking the Update button.
+   * @param object aOutputObject
+   *        The object to inspect, which is the aEvalString evaluation result.
+   * @return object
+   *         The PropertyPanel object instance.
+   */
+  openPropertyPanel: function SP_openPropertyPanel(aEvalString, aOutputObject)
+  {
+    let self = this;
+    let propPanel;
+    // The property panel has a button:
+    // `Update`: reexecutes the string executed on the command line. The
+    // result will be inspected by this panel.
+    let buttons = [];
+
+    // If there is a evalString passed to this function, then add a `Update`
+    // button to the panel so that the evalString can be reexecuted to update
+    // the content of the panel.
+    if (aEvalString !== null) {
+      buttons.push({
+        label: this.strings.
+               GetStringFromName("propertyPanel.updateButton.label"),
+        accesskey: this.strings.
+                   GetStringFromName("propertyPanel.updateButton.accesskey"),
+        oncommand: function () {
+          try {
+            let result = self.evalForContext(aEvalString);
+
+            if (result !== undefined) {
+              propPanel.treeView.data = result;
+            }
+          }
+          catch (ex) { }
+        }
+      });
+    }
+
+    let doc = this.browserWindow.document;
+    let parent = doc.getElementById("mainPopupSet");
+    let title = aOutputObject.toString();
+    propPanel = new PropertyPanel(parent, doc, title, aOutputObject, buttons);
+
+    let panel = propPanel.panel;
+    panel.setAttribute("class", "scratchpad_propertyPanel");
+    panel.openPopup(null, "after_pointer", 0, 0, false, false);
+    panel.sizeTo(200, 400);
+
+    return propPanel;
+  },
+
+  // Menu Operations
+
+  /**
+   * Open a new Scratchpad window.
+   */
+  openScratchpad: function SP_openScratchpad()
+  {
+    Services.ww.openWindow(null, SCRATCHPAD_WINDOW_URL, "_blank",
+                           SCRATCHPAD_WINDOW_FEATURES, null);
+  },
+
+  /**
+   * Export the textbox content to a file.
+   *
+   * @param nsILocalFile aFile
+   *        The file where you want to save the textbox content.
+   * @param boolean aNoConfirmation
+   *        If the file already exists, ask for confirmation?
+   * @param boolean aSilentError
+   *        True if you do not want to display an error when file save fails,
+   *        false otherwise.
+   * @param function aCallback
+   *        Optional function you want to call when file save completes. It will
+   *        get the following arguments:
+   *        1) the nsresult status code for the export operation.
+   */
+  exportToFile: function SP_exportToFile(aFile, aNoConfirmation, aSilentError,
+                                         aCallback)
+  {
+    if (!aNoConfirmation && aFile.exists() &&
+        !window.confirm(this.strings.
+                        GetStringFromName("export.fileOverwriteConfirmation"))) {
+      return;
+    }
+
+    let fs = Cc["@mozilla.org/network/file-output-stream;1"].
+             createInstance(Ci.nsIFileOutputStream);
+    let modeFlags = 0x02 | 0x08 | 0x20;
+    fs.init(aFile, modeFlags, 0644, fs.DEFER_OPEN);
+
+    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+                    createInstance(Ci.nsIScriptableUnicodeConverter);
+    converter.charset = "UTF-8";
+    let input = converter.convertToInputStream(this.textbox.value);
+
+    let self = this;
+    NetUtil.asyncCopy(input, fs, function(aStatus) {
+      if (!aSilentError && !Components.isSuccessCode(aStatus)) {
+        window.alert(self.strings.GetStringFromName("saveFile.failed"));
+      }
+
+      if (aCallback) {
+        aCallback.call(self, aStatus);
+      }
+    });
+  },
+
+  /**
+   * Read the content of a file and put it into the textbox.
+   *
+   * @param nsILocalFile aFile
+   *        The file you want to save the textbox content into.
+   * @param boolean aSilentError
+   *        True if you do not want to display an error when file load fails,
+   *        false otherwise.
+   * @param function aCallback
+   *        Optional function you want to call when file load completes. It will
+   *        get the following arguments:
+   *        1) the nsresult status code for the import operation.
+   *        2) the data that was read from the file, if any.
+   */
+  importFromFile: function SP_importFromFile(aFile, aSilentError, aCallback)
+  {
+    // Prevent file type detection.
+    let channel = NetUtil.newChannel(aFile);
+    channel.contentType = "application/javascript";
+
+    let self = this;
+    NetUtil.asyncFetch(channel, function(aInputStream, aStatus) {
+      let content = null;
+
+      if (Components.isSuccessCode(aStatus)) {
+        content = NetUtil.readInputStreamToString(aInputStream,
+                                                  aInputStream.available());
+        self.textbox.value = content;
+      }
+      else if (!aSilentError) {
+        window.alert(self.strings.GetStringFromName("openFile.failed"));
+      }
+
+      if (aCallback) {
+        aCallback.call(self, aStatus, content);
+      }
+    });
+  },
+
+  /**
+   * Open a file to edit in the Scratchpad.
+   */
+  openFile: function SP_openFile()
+  {
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    fp.init(window, this.strings.GetStringFromName("openFile.title"),
+            Ci.nsIFilePicker.modeOpen);
+    fp.defaultString = "";
+    if (fp.show() != Ci.nsIFilePicker.returnCancel) {
+      document.title = this.filename = fp.file.path;
+      this.importFromFile(fp.file);
+    }
+  },
+
+  /**
+   * Save the textbox content to the currently open file.
+   */
+  saveFile: function SP_saveFile()
+  {
+    if (!this.filename) {
+      return this.saveFileAs();
+    }
+
+    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+    file.initWithPath(this.filename);
+    this.exportToFile(file, true);
+  },
+
+  /**
+   * Save the textbox content to a new file.
+   */
+  saveFileAs: function SP_saveFileAs()
+  {
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    fp.init(window, this.strings.GetStringFromName("saveFileAs"),
+            Ci.nsIFilePicker.modeSave);
+    fp.defaultString = "scratchpad.js";
+    if (fp.show() != Ci.nsIFilePicker.returnCancel) {
+      document.title = this.filename = fp.file.path;
+      this.exportToFile(fp.file);
+    }
+  },
+
+  /**
+   * Open the Error Console.
+   */
+  openErrorConsole: function SP_openErrorConsole()
+  {
+    this.browserWindow.toJavaScriptConsole();
+  },
+
+  /**
+   * Open the Web Console.
+   */
+  openWebConsole: function SP_openWebConsole()
+  {
+    if (!this.browserWindow.HUDConsoleUI.getOpenHUD()) {
+      this.browserWindow.HUDConsoleUI.toggleHUD();
+    }
+    this.browserWindow.focus();
+  },
+
+  /**
+   * Set the current execution context to be the active tab content window.
+   */
+  setContentContext: function SP_setContentContext()
+  {
+    let content = document.getElementById("sp-menu-content");
+    document.getElementById("sp-menu-chrome").removeAttribute("checked");
+    content.setAttribute("checked", true);
+    this.statusbarStatus.label = content.getAttribute("label");
+    this.executionContext = SCRATCHPAD_CONTEXT_CONTENT;
+    this.resetContext();
+  },
+
+  /**
+   * Set the current execution context to be the most recent chrome window.
+   */
+  setChromeContext: function SP_setChromeContext()
+  {
+    let chrome = document.getElementById("sp-menu-chrome");
+    document.getElementById("sp-menu-content").removeAttribute("checked");
+    chrome.setAttribute("checked", true);
+    this.statusbarStatus.label = chrome.getAttribute("label");
+    this.executionContext = SCRATCHPAD_CONTEXT_CHROME;
+    this.resetContext();
+  },
+
+  /**
+   * Reset the cached Cu.Sandbox object for the current context.
+   */
+  resetContext: function SP_resetContext()
+  {
+    this._chromeSandbox = null;
+    this._contentSandbox = null;
+    this._previousWindow = null;
+  },
+
+  /**
+   * Gets the ID of the outer window of the given DOM window object.
+   *
+   * @param nsIDOMWindow aWindow
+   * @return integer
+   *         the outer window ID
+   */
+  getWindowId: function SP_getWindowId(aWindow)
+  {
+    return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+           getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+  },
+
+  /**
+   * The Scratchpad window DOMContentLoaded event handler.
+   */
+  onLoad: function SP_onLoad()
+  {
+    let chromeContextMenu = document.getElementById("sp-menu-chrome");
+    let errorConsoleMenu = document.getElementById("sp-menu-errorConsole");
+    let errorConsoleCommand = document.getElementById("sp-cmd-errorConsole");
+    let chromeContextCommand = document.getElementById("sp-cmd-chromeContext");
+
+    let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
+    if (chrome) {
+      chromeContextMenu.removeAttribute("hidden");
+      errorConsoleMenu.removeAttribute("hidden");
+      errorConsoleCommand.removeAttribute("disabled");
+      chromeContextCommand.removeAttribute("disabled");
+    }
+  },
+};
+
+XPCOMUtils.defineLazyGetter(Scratchpad, "strings", function () {
+  return Services.strings.createBundle(SCRATCHPAD_L10N);
+});
+
+addEventListener("DOMContentLoaded", Scratchpad.onLoad.bind(Scratchpad), false);
+
new file mode 100644
--- /dev/null
+++ b/browser/base/content/scratchpad.xul
@@ -0,0 +1,338 @@
+<?xml version="1.0"?>
+#ifdef 0
+<!-- ***** 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 Scratchpad.
+   -
+   - 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):
+   -   Rob Campbell <robcee@mozilla.com> (original author)
+   -   Mihai Sucan <mihai.sucan@gmail.com>
+   -   Erik Vold <erikvvold@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 ***** -->
+#endif
+<!DOCTYPE window [
+<!ENTITY % scratchpadDTD SYSTEM "chrome://browser/locale/scratchpad.dtd" >
+ %scratchpadDTD;
+]>
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
+
+<window id="main-window"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="&window.title;"
+        windowtype="devtools:scratchpad"
+        screenX="4" screenY="4"
+        width="640" height="480"
+        persist="screenX screenY width height sizemode">
+
+<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
+<script type="application/javascript" src="chrome://browser/content/scratchpad.js"/>
+
+<commandset id="editMenuCommands"/>
+
+<commandset id="sp-commandset">
+  <command id="sp-cmd-newWindow" oncommand="Scratchpad.openScratchpad();"/>
+  <command id="sp-cmd-openFile" oncommand="Scratchpad.openFile();"/>
+  <command id="sp-cmd-save" oncommand="Scratchpad.saveFile();"/>
+  <command id="sp-cmd-saveas" oncommand="Scratchpad.saveFileAs();"/>
+
+  <!-- TODO: bug 650340 - implement printFile()
+  <command id="sp-cmd-printFile" oncommand="Scratchpad.printFile();" disabled="true"/>
+ -->
+
+  <command id="sp-cmd-close" oncommand="window.close();"/>
+  <command id="sp-cmd-execute" oncommand="Scratchpad.execute();"/>
+  <command id="sp-cmd-inspect" oncommand="Scratchpad.inspect();"/>
+  <command id="sp-cmd-print" oncommand="Scratchpad.print();"/>
+  <command id="sp-cmd-contentContext" oncommand="Scratchpad.setContentContext();"/>
+  <command id="sp-cmd-chromeContext" oncommand="Scratchpad.setChromeContext();" disabled="true"/>
+  <command id="sp-cmd-resetContext" oncommand="Scratchpad.resetContext();"/>
+  <command id="sp-cmd-errorConsole" oncommand="Scratchpad.openErrorConsole();" disabled="true"/>
+  <command id="sp-cmd-webConsole" oncommand="Scratchpad.openWebConsole();"/>
+</commandset>
+
+<keyset id="sp-keyset">
+  <key id="sp-key-window"
+       key="&newWindowCmd.commandkey;"
+       command="sp-cmd-newWindow"
+       modifiers="accel"/>
+  <key id="sp-key-open"
+       key="&openFileCmd.commandkey;"
+       command="sp-cmd-openFile"
+       modifiers="accel"/>
+  <key id="sp-key-save"
+       key="&saveFileCmd.commandkey;"
+       command="sp-cmd-save"
+       modifiers="accel"/>
+  <key id="sp-key-close"
+       key="&closeCmd.key;"
+       command="sp-cmd-close"
+       modifiers="accel"/>
+
+  <!-- TODO: bug 650340 - implement printFile
+  <key id="sp-key-printFile"
+       key="&printCmd.commandkey;"
+       command="sp-cmd-printFile"
+       modifiers="accel"/>
+  -->
+
+  <key id="key_cut"
+       key="&cutCmd.key;"
+       modifiers="accel"/>
+
+  <key id="key_copy"
+       key="&copyCmd.key;"
+       modifiers="accel"/>
+  <key id="key_paste"
+       key="&pasteCmd.key;"
+       modifiers="accel"/>
+  <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
+  <key id="key_undo" key="&undoCmd.key;" modifiers="accel"/>
+  <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift"/>
+  <key id="sp-key-execute"
+       key="&execute.key;"
+       command="sp-cmd-execute"
+       modifiers="accel"/>
+  <key id="sp-key-inspect"
+       key="&inspect.key;"
+       command="sp-cmd-inspect"
+       modifiers="accel"/>
+  <key id="sp-key-print"
+       key="&print.key;"
+       command="sp-cmd-print"
+       modifiers="accel"/>
+  <key id="sp-key-errorConsole"
+       key="&errorConsoleCmd.commandkey;"
+       command="sp-cmd-errorConsole"
+       modifiers="accel,shift"/>
+  <key id="sp-key-webConsole"
+       key="&webConsoleCmd.commandkey;"
+       command="sp-cmd-webConsole"
+       modifiers="accel,shift"/>
+</keyset>
+
+
+<menubar id="sp-menubar">
+  <menu id="sp-file-menu" label="&fileMenu.label;"
+        accesskey="&fileMenu.accesskey;">
+    <menupopup id="sp-menu-filepopup">
+      <menuitem id="sp-menu-newscratchpad"
+                label="&newWindowCmd.label;"
+                accesskey="&newWindowCmd.accesskey;"
+                key="sp-key-window"
+                command="sp-cmd-newWindow"/>
+      <menuseparator/>
+      <menuitem id="sp-menu-open"
+                label="&openFileCmd.label;"
+                command="sp-cmd-openFile"
+                key="sp-key-open"
+                accesskey="&openFileCmd.accesskey;"/>
+      <menuitem id="sp-menu-save"
+                label="&saveFileCmd.label;"
+                accesskey="&saveFileCmd.accesskey;"
+                key="sp-key-save"
+                command="sp-cmd-save"/>
+      <menuitem id="sp-menu-saveas"
+                label="&saveFileAsCmd.label;"
+                accesskey="&saveFileAsCmd.accesskey;"
+                command="sp-cmd-saveas"/>
+      <menuseparator/>
+
+      <!-- TODO: bug 650340 - implement printFile
+      <menuitem id="sp-menu-print"
+                label="&printCmd.label;"
+                accesskey="&printCmd.accesskey;"
+                command="sp-cmd-printFile"/>
+      <menuseparator/>
+      -->
+
+      <menuitem id="sp-menu-close"
+                label="&closeCmd.label;"
+                key="sp-key-close"
+                accesskey="&closeCmd.accesskey;"
+                command="sp-cmd-close"/>
+    </menupopup>
+  </menu>
+
+  <menu id="sp-edit-menu" label="&editMenu.label;"
+        accesskey="&editMenu.accesskey;">
+    <menupopup id="sp-menu_editpopup">
+      <menuitem id="sp-menu_undo"
+                label="&undoCmd.label;"
+                key="key_undo"
+                accesskey="&undoCmd.accesskey;"
+                disabled="true"
+                oncommand="cmd_undo"/>
+      <menuitem id="sp-menu-redo"
+                label="&redoCmd.label;"
+                key="key_redo"
+                disabled="true"
+                accesskey="&redoCmd.accesskey;"
+                command="cmd_redo"/>
+      <menuseparator/>
+      <menuitem id="sp-menu-cut"
+                label="&cutCmd.label;"
+                key="key_cut"
+                accesskey="&cutCmd.accesskey;"
+                command="cmd_cut"/>
+      <menuitem id="sp-menu-copy"
+                label="&copyCmd.label;"
+                key="key_copy"
+                accesskey="&copyCmd.accesskey;"
+                command="cmd_copy"/>
+      <menuitem id="sp-menu-paste"
+                label="&pasteCmd.label;"
+                key="key_paste"
+                accesskey="&pasteCmd.accesskey;"
+                command="cmd_paste"/>
+      <menuseparator/>
+      <menuitem id="sp-menu-selectAll"
+                label="&selectAllCmd.label;"
+                key="key_selectAll"
+                accesskey="&selectAllCmd.accesskey;"
+                command="cmd_selectAll"/>
+      <menuseparator/>
+
+      <!-- TODO: bug 650345 - implement search and replace
+      <menuitem id="sp-menu-find"
+                label="&findOnCmd.label;"
+                accesskey="&findOnCmd.accesskey;"
+                key="key_find"
+                disabled="true"
+                command="cmd_find"/>
+      <menuitem id="sp-menu-findAgain"
+                label="&findAgainCmd.label;"
+                accesskey="&findAgainCmd.accesskey;"
+                key="key_findAgain"
+                disabled="true"
+                command="cmd_findAgain"/>
+      <menuseparator id="sp-execute-separator"/>
+      -->
+
+      <menuitem id="sp-text-execute"
+                label="&execute.label;"
+                accesskey="&execute.accesskey;"
+                key="sp-key-execute"
+                command="sp-cmd-execute"/>
+      <menuitem id="sp-text-inspect"
+                label="&inspect.label;"
+                accesskey="&inspect.accesskey;"
+                key="sp-key-inspect"
+                command="sp-cmd-inspect"/>
+      <menuitem id="sp-text-print"
+                label="&print.label;"
+                accesskey="&print.accesskey;"
+                key="sp-key-print"
+                command="sp-cmd-print"/>
+    </menupopup>
+  </menu>
+
+  <menu id="sp-context-menu"
+        label="&contextMenu.label;"
+        accesskey="&contextMenu.accesskey;">
+    <menupopup id="sp-menu-context">
+      <menuitem id="sp-menu-content"
+                label="&contentContext.label;"
+                accesskey="&contentContext.accesskey;"
+                command="sp-cmd-contentContext"
+                checked="true"
+                type="radio"/>
+      <menuitem id="sp-menu-chrome" hidden="true"
+                command="sp-cmd-chromeContext"
+                label="&chromeContext.label;"
+                accesskey="&chromeContext.accesskey;"
+                type="radio"/>
+      <menuseparator/>
+      <menuitem id="sp-menu-resetContext"
+                command="sp-cmd-resetContext"
+                label="&resetContext.label;"
+                accesskey="&resetContext.accesskey;"/>
+    </menupopup>
+  </menu>
+
+  <menu id="sp-tools-menu"
+        label="&toolsMenu.label;"
+        accesskey="&toolsMenu.accesskey;">
+    <menupopup id="sp-menu-tools">
+      <menuitem id="sp-menu-errorConsole" hidden="true"
+                label="&errorConsoleCmd.label;"
+                accesskey="&errorConsoleCmd.accesskey;"
+                key="sp-key-errorConsole"
+                command="sp-cmd-errorConsole"/>
+      <menuitem id="sp-menu-webConsole"
+                label="&webConsoleCmd.label;"
+                accesskey="&webConsoleCmd.accesskey;"
+                key="sp-key-webConsole"
+                command="sp-cmd-webConsole"/>
+    </menupopup>
+  </menu>
+</menubar>
+
+<popupset id="scratchpad-popups">
+  <menupopup id="scratchpad-text-popup">
+    <menuitem id="menu_cut"/>
+    <menuitem id="menu_copy"/>
+    <menuitem id="menu_paste"/>
+    <menuitem id="menu_delete"/>
+    <menuseparator/>
+    <menuitem id="menu_selectAll"/>
+    <menuseparator/>
+    <menuitem id="sp-text-execute"
+              label="&execute.label;"
+              accesskey="&execute.accesskey;"
+              key="sp-key-execute"
+              command="sp-cmd-execute"/>
+    <menuitem id="sp-text-inspect"
+              label="&inspect.label;"
+              accesskey="&inspect.accesskey;"
+              key="sp-key-inspect"
+              command="sp-cmd-inspect"/>
+    <menuitem id="sp-text-print"
+              label="&print.label;"
+              accesskey="&print.accesskey;"
+              key="sp-key-print"
+              command="sp-cmd-print"/>
+  </menupopup>
+</popupset>
+
+<textbox id="scratchpad-textbox"
+         multiline="true"
+         flex="1"
+         context="scratchpad-text-popup"
+         placeholder="&textbox.placeholder;" />
+<statusbar id="scratchpad-statusbar" align="end">
+  <statusbarpanel id="scratchpad-status"
+                  label="&contentContext.label;"
+                  class="statusbarpanel-iconic-text"/>
+  <spacer flex="1"/>
+</statusbar>
+</window>
--- a/browser/base/content/tabview/search.js
+++ b/browser/base/content/tabview/search.js
@@ -213,53 +213,49 @@ TabMatcher.prototype = {
       var name = tab.$tabTitle[0].innerHTML;
       let url = TabUtils.URLOf(tab);
       return !name.match(self.term, "i") && !url.match(self.term, "i");
     });
   },
   
   // ---------
   // Function: _getTabsForOtherWindows
-  // Returns an array of <TabItem>s and <xul:tabs>s representing that
-  // tabs from all windows but the currently focused window. <TabItem>s
-  // will be returned for windows in which Panorama has been activated at
-  // least once, while <xul:tab>s will be return for windows in which
-  // Panorama has never been activated.
-  _getTabsForOtherWindows: function TabMatcher__getTabsForOtherWindows(){
-    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                       .getService(Components.interfaces.nsIWindowMediator);
-    var enumerator = wm.getEnumerator("navigator:browser");    
-    var currentWindow = wm.getMostRecentWindow("navigator:browser");
-    
-    var allTabs = [];    
+  // Returns an array of <TabItem>s and <xul:tabs>s representing tabs
+  // from all windows but the current window. <TabItem>s will be returned
+  // for windows in which Panorama has been activated at least once, while
+  // <xul:tab>s will be returned for windows in which Panorama has never
+  // been activated.
+  _getTabsForOtherWindows: function TabMatcher__getTabsForOtherWindows() {
+    var enumerator = Services.wm.getEnumerator("navigator:browser");
+    var allTabs = [];
+
     while (enumerator.hasMoreElements()) {
       var win = enumerator.getNext();
-      // This function gets tabs from other windows: not the one you currently
-      // have focused.
-      if (win != currentWindow) {
+      // This function gets tabs from other windows, not from the current window
+      if (win != gWindow) {
         // If TabView is around iterate over all tabs, else get the currently
         // shown tabs...
-        
         let tvWindow = win.TabView.getContentWindow();
         if (tvWindow)
-          allTabs = allTabs.concat( tvWindow.TabItems.getItems() );
+          allTabs = allTabs.concat(tvWindow.TabItems.getItems());
         else
           // win.gBrowser.tabs isn't a proper array, so we can't use concat
-          for (var i=0; i<win.gBrowser.tabs.length; i++) allTabs.push( win.gBrowser.tabs[i] );
-      } 
+          for (let i = 0; i < win.gBrowser.tabs.length; i++)
+            allTabs.push(win.gBrowser.tabs[i]);
+      }
     }
-    return allTabs;    
+    return allTabs;
   },
   
   // ----------
   // Function: matchedTabsFromOtherWindows
   // Returns an array of <TabItem>s and <xul:tab>s that match the search term
-  // from all windows but the currently focused window. <TabItem>s will be
-  // returned for windows in which Panorama has been activated at least once,
-  // while <xul:tab>s will be return for windows in which Panorama has never
+  // from all windows but the current window. <TabItem>s will be returned for
+  // windows in which Panorama has been activated at least once, while
+  // <xul:tab>s will be returned for windows in which Panorama has never
   // been activated.
   // (new TabMatcher("app")).matchedTabsFromOtherWindows();
   matchedTabsFromOtherWindows: function TabMatcher_matchedTabsFromOtherWindows(){
     if (this.term.length < 2)
       return [];
     
     var tabs = this._getTabsForOtherWindows();
     tabs = this._filterAndSortForMatches(tabs);
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -187,16 +187,23 @@ endif
                  browser_inspector_domPanel.js \
                  browser_inspector_iframeTest.js \
                  browser_inspector_scrolling.js \
                  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_scratchpad_initialization.js \
+                 browser_scratchpad_contexts.js \
+                 browser_scratchpad_execute_print.js \
+                 browser_scratchpad_inspect.js \
+                 browser_scratchpad_files.js \
+                 browser_scratchpad_ui.js \
+                 browser_scratchpad_bug_646070_chrome_context_pref.js \
                  browser_overflowScroll.js \
                  browser_pageInfo.js \
                  browser_page_style_menu.js \
                  browser_pinnedTabs.js \
                  browser_plainTextLinks.js \
                  browser_pluginnotification.js \
                  browser_relatedTabs.js \
                  browser_sanitize-passwordDisabledHosts.js \
@@ -217,16 +224,17 @@ endif
                  browser_tabs_owner.js \
                  browser_urlHighlight.js \
                  browser_visibleFindSelection.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
                  browser_visibleTabs_bookmarkAllTabs.js \
                  browser_visibleTabs_tabPreview.js \
+                 browser_webdev_menu.js \
                  bug592338.html \
                  disablechrome.html \
                  discovery.html \
                  domplate_test.js \
                  moz.png \
                  test_bug435035.html \
                  test_bug462673.html \
                  page_style_sample.html \
--- a/browser/base/content/test/browser_inspector_initialization.js
+++ b/browser/base/content/test/browser_inspector_initialization.js
@@ -51,17 +51,17 @@ function runInspectorTests()
   Services.obs.removeObserver(runInspectorTests, "inspector-opened", false);
   Services.obs.addObserver(finishInspectorTests, "inspector-closed", false);
   let iframe = document.getElementById("inspector-tree-iframe");
   is(InspectorUI.treeIFrame, iframe, "Inspector IFrame matches");
   ok(InspectorUI.inspecting, "Inspector is highlighting");
   ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
   ok(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
   ok(InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is open");
-  InspectorUI.closeInspectorUI(true);
+  InspectorUI.closeInspectorUI();
 }
 
 function finishInspectorTests()
 {
   Services.obs.removeObserver(finishInspectorTests, "inspector-closed", false);
   ok(!InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is closed");
   ok(!InspectorUI.isStylePanelOpen, "Inspector Style Panel is closed");
   ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
--- a/browser/base/content/test/browser_inspector_tab_switch.js
+++ b/browser/base/content/test/browser_inspector_tab_switch.js
@@ -152,16 +152,17 @@ function inspectorTabUnload1(evt)
   tab1window = tab1 = tab2 = div = null;
 
   // Make sure the Inspector is still open and that the state is correct.
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
   ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
   ok(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
   is(InspectorStore.length, 1, "InspectorStore.length = 1");
 
+  InspectorUI.closeInspectorUI();
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_scratchpad_bug_646070_chrome_context_pref.js
@@ -0,0 +1,68 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Reference to the Scratchpad chrome window object.
+let gScratchpadWindow;
+
+let gOldPref;
+let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gOldPref = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
+  Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, true);
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    ok(Scratchpad, "Scratchpad variable exists");
+
+    gScratchpadWindow = Scratchpad.openScratchpad();
+    gScratchpadWindow.addEventListener("load", runTests, false);
+  }, true);
+
+  content.location = "data:text/html,Scratchpad test for bug 646070 - chrome context preference";
+}
+
+function runTests()
+{
+  gScratchpadWindow.removeEventListener("load", arguments.callee, false);
+
+  let sp = gScratchpadWindow.Scratchpad;
+  ok(sp, "Scratchpad object exists in new window");
+
+  let chromeContextMenu = gScratchpadWindow.document.
+                          getElementById("sp-menu-chrome");
+  ok(chromeContextMenu, "Chrome context menuitem element exists");
+  ok(!chromeContextMenu.hasAttribute("hidden"),
+     "Chrome context menuitem is visible");
+
+  let errorConsoleCommand = gScratchpadWindow.document.
+                            getElementById("sp-cmd-errorConsole");
+  ok(errorConsoleCommand, "Error console command element exists");
+  ok(!errorConsoleCommand.hasAttribute("disabled"),
+     "Error console command is enabled");
+
+  let errorConsoleMenu = gScratchpadWindow.document.
+                         getElementById("sp-menu-errorConsole");
+  ok(errorConsoleMenu, "Error console menu element exists");
+  ok(!errorConsoleMenu.hasAttribute("hidden"),
+     "Error console menuitem is visible");
+
+  let chromeContextCommand = gScratchpadWindow.document.
+                            getElementById("sp-cmd-chromeContext");
+  ok(chromeContextCommand, "Chrome context command element exists");
+  ok(!chromeContextCommand.hasAttribute("disabled"),
+     "Chrome context command is disabled");
+
+  Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, gOldPref);
+
+  gScratchpadWindow.close();
+  gScratchpadWindow = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_scratchpad_contexts.js
@@ -0,0 +1,125 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Reference to the Scratchpad chrome window object.
+let gScratchpadWindow;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gScratchpadWindow = Scratchpad.openScratchpad();
+    gScratchpadWindow.addEventListener("load", runTests, false);
+  }, true);
+
+  content.location = "data:text/html,test context switch in Scratchpad";
+}
+
+function runTests()
+{
+  gScratchpadWindow.removeEventListener("load", arguments.callee, false);
+
+  let sp = gScratchpadWindow.Scratchpad;
+
+  let contentMenu = gScratchpadWindow.document.getElementById("sp-menu-content");
+  let chromeMenu = gScratchpadWindow.document.getElementById("sp-menu-chrome");
+  let statusbar = sp.statusbarStatus;
+
+  ok(contentMenu, "found #sp-menu-content");
+  ok(chromeMenu, "found #sp-menu-chrome");
+  ok(statusbar, "found Scratchpad.statusbarStatus");
+
+  sp.setContentContext();
+
+  is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_CONTENT,
+     "executionContext is content");
+
+  is(contentMenu.getAttribute("checked"), "true",
+     "content menuitem is checked");
+
+  ok(!chromeMenu.hasAttribute("checked"),
+     "chrome menuitem is not checked");
+
+  is(statusbar.getAttribute("label"), contentMenu.getAttribute("label"),
+     "statusbar label is correct");
+
+  ok(sp.textbox, "textbox exists");
+  sp.textbox.value = "window.foobarBug636725 = 'aloha';";
+
+  ok(!content.wrappedJSObject.foobarBug636725,
+     "no content.foobarBug636725");
+
+  sp.execute();
+
+  is(content.wrappedJSObject.foobarBug636725, "aloha",
+     "content.foobarBug636725 has been set");
+
+  sp.setChromeContext();
+
+  is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_CHROME,
+     "executionContext is chrome");
+
+  is(chromeMenu.getAttribute("checked"), "true",
+     "chrome menuitem is checked");
+
+  ok(!contentMenu.hasAttribute("checked"),
+     "content menuitem is not checked");
+
+  is(statusbar.getAttribute("label"), chromeMenu.getAttribute("label"),
+     "statusbar label is correct");
+
+  sp.textbox.value = "window.foobarBug636725 = 'aloha2';";
+
+  ok(!window.foobarBug636725, "no window.foobarBug636725");
+
+  sp.execute();
+
+  is(window.foobarBug636725, "aloha2", "window.foobarBug636725 has been set");
+
+  sp.textbox.value = "window.gBrowser";
+
+  is(typeof sp.execute()[1].addTab, "function",
+     "chrome context has access to chrome objects");
+
+  // Check that the sandbox is cached.
+
+  sp.textbox.value = "typeof foobarBug636725cache;";
+  is(sp.execute()[1], "undefined", "global variable does not exist");
+
+  sp.textbox.value = "var foobarBug636725cache = 'foo';";
+  sp.execute();
+
+  sp.textbox.value = "typeof foobarBug636725cache;";
+  is(sp.execute()[1], "string",
+     "global variable exists across two different executions");
+
+  sp.resetContext();
+
+  is(sp.execute()[1], "undefined",
+     "global variable no longer exists after calling resetContext()");
+
+  sp.textbox.value = "var foobarBug636725cache2 = 'foo';";
+  sp.execute();
+
+  sp.textbox.value = "typeof foobarBug636725cache2;";
+  is(sp.execute()[1], "string",
+     "global variable exists across two different executions");
+
+  sp.setContentContext();
+
+  is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_CONTENT,
+     "executionContext is content");
+
+  is(sp.execute()[1], "undefined",
+     "global variable no longer exists after changing the context");
+
+  gScratchpadWindow.close();
+  gScratchpadWindow = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_scratchpad_execute_print.js
@@ -0,0 +1,115 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Reference to the Scratchpad chrome window object.
+let gScratchpadWindow;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gScratchpadWindow = Scratchpad.openScratchpad();
+    gScratchpadWindow.addEventListener("load", runTests, false);
+  }, true);
+
+  content.location = "data:text/html,<p>test execute() and print() in Scratchpad";
+}
+
+function runTests()
+{
+  gScratchpadWindow.removeEventListener("load", arguments.callee, false);
+
+  let sp = gScratchpadWindow.Scratchpad;
+
+  content.wrappedJSObject.foobarBug636725 = 1;
+
+  ok(sp.textbox, "textbox exists");
+  sp.textbox.value = "++window.foobarBug636725";
+
+  let exec = sp.execute();
+  is(exec[0], sp.textbox.value, "execute()[0] is correct");
+  is(exec[1], content.wrappedJSObject.foobarBug636725,
+     "execute()[1] is correct");
+
+  is(sp.textbox.value, "++window.foobarBug636725",
+     "execute() does not change the textbox value");
+
+  is(content.wrappedJSObject.foobarBug636725, 2,
+     "execute() updated window.foobarBug636725");
+
+  sp.print();
+
+  is(content.wrappedJSObject.foobarBug636725, 3,
+     "print() updated window.foobarBug636725");
+
+  is(sp.textbox.value, "++window.foobarBug636725/*\n3\n*/",
+     "print() shows evaluation result in the textbox");
+
+  is(sp.selectedText, "/*\n3\n*/", "selectedText is correct");
+  is(sp.textbox.selectionStart, 24, "selectionStart is correct");
+  is(sp.textbox.selectionEnd, 31, "selectionEnd is correct");
+
+  // Test selection execute() and print().
+
+  sp.textbox.value = "window.foobarBug636725 = 'a';\n" +
+                     "window.foobarBug636725 = 'b';";
+
+  sp.selectRange(1, 2);
+
+  is(sp.textbox.selectionStart, 1, "selectionStart is 1");
+  is(sp.textbox.selectionEnd, 2, "selectionEnd is 2");
+
+  sp.selectRange(0, 29);
+
+  is(sp.textbox.selectionStart, 0, "selectionStart is 0");
+  is(sp.textbox.selectionEnd, 29, "selectionEnd is 29");
+
+  exec = sp.execute();
+
+  is(exec[0], "window.foobarBug636725 = 'a';",
+     "execute()[0] is correct");
+  is(exec[1], "a",
+     "execute()[1] is correct");
+
+  is(sp.textbox.value, "window.foobarBug636725 = 'a';\n" +
+                       "window.foobarBug636725 = 'b';",
+     "execute() does not change the textbox value");
+
+  is(content.wrappedJSObject.foobarBug636725, "a",
+     "execute() worked for the selected range");
+
+  sp.textbox.value = "window.foobarBug636725 = 'c';\n" +
+                     "window.foobarBug636725 = 'b';";
+
+  sp.selectRange(0, 22);
+
+  sp.print();
+
+  is(content.wrappedJSObject.foobarBug636725, "a",
+     "print() worked for the selected range");
+
+  is(sp.textbox.value, "window.foobarBug636725" +
+                       "/*\na\n*/" +
+                       " = 'c';\n" +
+                       "window.foobarBug636725 = 'b';",
+     "print() shows evaluation result in the textbox");
+
+  is(sp.selectedText, "/*\na\n*/", "selectedText is correct");
+  is(sp.textbox.selectionStart, 22, "selectionStart is correct");
+  is(sp.textbox.selectionEnd, 29, "selectionEnd is correct");
+
+  sp.deselect();
+
+  ok(!sp.selectedText, "selectedText is empty");
+  is(sp.textbox.selectionStart, sp.textbox.selectionEnd, "deselect() works");
+
+  gScratchpadWindow.close();
+  gScratchpadWindow = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_scratchpad_files.js
@@ -0,0 +1,149 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+// Reference to the Scratchpad chrome window object.
+let gScratchpadWindow;
+
+// Reference to the Scratchpad object.
+let gScratchpad;
+
+// Reference to the temporary nsIFile we will work with.
+let gFile;
+
+// The temporary file content.
+let gFileContent = "hello.world('bug636725');";
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gScratchpadWindow = Scratchpad.openScratchpad();
+    gScratchpadWindow.addEventListener("load", runTests, false);
+  }, true);
+
+  content.location = "data:text/html,<p>test file open and save in Scratchpad";
+}
+
+function runTests()
+{
+  gScratchpadWindow.removeEventListener("load", arguments.callee, false);
+
+  gScratchpad = gScratchpadWindow.Scratchpad;
+
+  // Create a temporary file.
+  gFile = FileUtils.getFile("TmpD", ["fileForBug636725.tmp"]);
+  gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+
+  // Write the temporary file.
+  let fout = Cc["@mozilla.org/network/file-output-stream;1"].
+             createInstance(Ci.nsIFileOutputStream);
+  fout.init(gFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
+            0644, fout.DEFER_OPEN);
+
+  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+                  createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  let fileContentStream = converter.convertToInputStream(gFileContent);
+
+  NetUtil.asyncCopy(fileContentStream, fout, tempFileSaved);
+}
+
+function tempFileSaved(aStatus)
+{
+  ok(Components.isSuccessCode(aStatus),
+     "the temporary file was saved successfully");
+
+  // Import the file into Scratchpad.
+  gScratchpad.importFromFile(gFile.QueryInterface(Ci.nsILocalFile),  true,
+                            fileImported);
+}
+
+function fileImported(aStatus, aFileContent)
+{
+  ok(Components.isSuccessCode(aStatus),
+     "the temporary file was imported successfully with Scratchpad");
+
+  is(aFileContent, gFileContent,
+     "received data is correct");
+
+  is(gScratchpad.textbox.value, gFileContent,
+     "the textbox.value is correct");
+
+  // Save the file after changes.
+  gFileContent += "// omg, saved!";
+  gScratchpad.textbox.value = gFileContent;
+
+  gScratchpad.exportToFile(gFile.QueryInterface(Ci.nsILocalFile), true, true,
+                          fileExported);
+}
+
+function fileExported(aStatus)
+{
+  ok(Components.isSuccessCode(aStatus),
+     "the temporary file was exported successfully with Scratchpad");
+
+  let oldContent = gFileContent;
+
+  // Attempt another file save, with confirmation which returns false.
+  gFileContent += "// omg, saved twice!";
+  gScratchpad.textbox.value = gFileContent;
+
+  let oldConfirm = gScratchpadWindow.confirm;
+  let askedConfirmation = false;
+  gScratchpadWindow.confirm = function() {
+    askedConfirmation = true;
+    return false;
+  };
+
+  gScratchpad.exportToFile(gFile.QueryInterface(Ci.nsILocalFile), false, true,
+                          fileExported2);
+
+  gScratchpadWindow.confirm = oldConfirm;
+
+  ok(askedConfirmation, "exportToFile() asked for overwrite confirmation");
+
+  gFileContent = oldContent;
+
+  let channel = NetUtil.newChannel(gFile);
+  channel.contentType = "application/javascript";
+
+  // Read back the temporary file.
+  NetUtil.asyncFetch(channel, fileRead);
+}
+
+function fileExported2()
+{
+  ok(false, "exportToFile() did not cancel file overwrite");
+}
+
+function fileRead(aInputStream, aStatus)
+{
+  ok(Components.isSuccessCode(aStatus),
+     "the temporary file was read back successfully");
+
+  let updatedContent =
+    NetUtil.readInputStreamToString(aInputStream, aInputStream.available());;
+
+  is(updatedContent, gFileContent, "file properly updated");
+
+  // Done!
+  gFile.remove(false);
+  gFile = null;
+  gScratchpad = null;
+  gScratchpadWindow.close();
+  gScratchpadWindow = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_scratchpad_initialization.js
@@ -0,0 +1,63 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Reference to the Scratchpad chrome window object.
+let gScratchpadWindow;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    ok(Scratchpad, "Scratchpad variable exists");
+
+    gScratchpadWindow = Scratchpad.openScratchpad();
+    gScratchpadWindow.addEventListener("load", runTests, false);
+  }, true);
+
+  content.location = "data:text/html,initialization test for Scratchpad";
+}
+
+function runTests()
+{
+  gScratchpadWindow.removeEventListener("load", arguments.callee, false);
+
+  let sp = gScratchpadWindow.Scratchpad;
+  ok(sp, "Scratchpad object exists in new window");
+  is(typeof sp.execute, "function", "Scratchpad.execute() exists");
+  is(typeof sp.inspect, "function", "Scratchpad.inspect() exists");
+  is(typeof sp.print, "function", "Scratchpad.print() exists");
+
+  let chromeContextMenu = gScratchpadWindow.document.
+                          getElementById("sp-menu-chrome");
+  ok(chromeContextMenu, "Chrome context menuitem element exists");
+  is(chromeContextMenu.getAttribute("hidden"), "true",
+     "Chrome context menuitem is hidden");
+
+  let errorConsoleCommand = gScratchpadWindow.document.
+                            getElementById("sp-cmd-errorConsole");
+  ok(errorConsoleCommand, "Error console command element exists");
+  is(errorConsoleCommand.getAttribute("disabled"), "true",
+     "Error console command is disabled");
+
+  let errorConsoleMenu = gScratchpadWindow.document.
+                         getElementById("sp-menu-errorConsole");
+  ok(errorConsoleMenu, "Error console menu element exists");
+  is(errorConsoleMenu.getAttribute("hidden"), "true",
+     "Error console menu item is hidden");
+
+  let chromeContextCommand = gScratchpadWindow.document.
+                            getElementById("sp-cmd-chromeContext");
+  ok(chromeContextCommand, "Chrome context command element exists");
+  is(chromeContextCommand.getAttribute("disabled"), "true",
+     "Chrome context command is disabled");
+
+  gScratchpadWindow.close();
+  gScratchpadWindow = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_scratchpad_inspect.js
@@ -0,0 +1,65 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Reference to the Scratchpad chrome window object.
+let gScratchpadWindow;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gScratchpadWindow = Scratchpad.openScratchpad();
+    gScratchpadWindow.addEventListener("load", runTests, false);
+  }, true);
+
+  content.location = "data:text/html,<title>foobarBug636725</title>" +
+    "<p>test inspect() in Scratchpad";
+}
+
+function runTests()
+{
+  gScratchpadWindow.removeEventListener("load", arguments.callee, false);
+
+  let sp = gScratchpadWindow.Scratchpad;
+
+  ok(sp.textbox, "textbox exists");
+  sp.textbox.value = "document";
+
+  sp.inspect();
+
+  let propPanel = document.querySelector(".scratchpad_propertyPanel");
+  ok(propPanel, "property panel is open");
+
+  propPanel.addEventListener("popupshown", function() {
+    propPanel.removeEventListener("popupshown", arguments.callee, false);
+
+    let tree = propPanel.querySelector("tree");
+    ok(tree, "property panel tree found");
+
+    let column = tree.columns[0];
+    let found = false;
+
+    for (let i = 0; i < tree.view.rowCount; i++) {
+      let cell = tree.view.getCellText(i, column);
+      if (cell == 'title: "foobarBug636725"') {
+        found = true;
+        break;
+      }
+    }
+    ok(found, "found the document.title property");
+
+    executeSoon(function() {
+      propPanel.hidePopup();
+
+      gScratchpadWindow.close();
+      gScratchpadWindow = null;
+      gBrowser.removeCurrentTab();
+      finish();
+    });
+  }, false);
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_scratchpad_ui.js
@@ -0,0 +1,81 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Reference to the Scratchpad chrome window object.
+let gScratchpadWindow;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    gScratchpadWindow = Scratchpad.openScratchpad();
+    gScratchpadWindow.addEventListener("load", runTests, false);
+  }, true);
+
+  content.location = "data:text/html,<title>foobarBug636725</title>" +
+    "<p>test inspect() in Scratchpad";
+}
+
+function runTests()
+{
+  gScratchpadWindow.removeEventListener("load", arguments.callee, false);
+
+  let sp = gScratchpadWindow.Scratchpad;
+  let doc = gScratchpadWindow.document;
+
+  let methodsAndItems = {
+    "sp-menu-newscratchpad": "openScratchpad",
+    "sp-menu-open": "openFile",
+    "sp-menu-save": "saveFile",
+    "sp-menu-saveas": "saveFileAs",
+    "sp-text-execute": "execute",
+    "sp-text-inspect": "inspect",
+    "sp-menu-content": "setContentContext",
+    "sp-menu-chrome": "setChromeContext",
+    "sp-menu-resetContext": "resetContext",
+    "sp-menu-errorConsole": "openErrorConsole",
+    "sp-menu-webConsole": "openWebConsole",
+  };
+
+  let lastMethodCalled = null;
+  sp.__noSuchMethod__ = function(aMethodName) {
+    lastMethodCalled = aMethodName;
+  };
+
+  for (let id in methodsAndItems) {
+    lastMethodCalled = null;
+
+    let methodName = methodsAndItems[id];
+    let oldMethod = sp[methodName];
+    ok(oldMethod, "found method " + methodName + " in Scratchpad object");
+
+    delete sp[methodName];
+
+    let menu = doc.getElementById(id);
+    ok(menu, "found menuitem #" + id);
+
+    try {
+      menu.doCommand();
+    }
+    catch (ex) {
+      ok(false, "exception thrown while executing the command of menuitem #" + id);
+    }
+
+    ok(lastMethodCalled == methodName,
+       "method " + methodName + " invoked by the associated menuitem");
+
+    sp[methodName] = oldMethod;
+  }
+
+  delete sp.__noSuchMethod__;
+
+  gScratchpadWindow.close();
+  gScratchpadWindow = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_webdev_menu.js
@@ -0,0 +1,43 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function menuTest()
+{
+  gBrowser.selectedBrowser.removeEventListener("load", menuTest, true);
+
+  let menuContents = [
+    "menu_pageinspect",
+    "webConsole",
+    "menu_scratchpad",
+    "menu_pageSource",
+    "javascriptConsole"
+  ];
+
+  let menu = document.getElementById("webDeveloperMenu");
+  ok(menu, "we have the menu");
+
+  let popup = menu.firstChild;
+  is(popup.id, "menuWebDeveloperPopup", "menu first child is menuWebDeveloperPopup");
+
+  is(popup.childNodes.length, menuContents.length, "popup childNodes.length matches");
+
+  for(let a = 0; a < popup.children.length; a++) {
+    isnot(menuContents.indexOf(popup.children[a].id), -1, "menuitem " + popup.children[a].id + " in popup");
+  };
+
+  gBrowser.removeCurrentTab();
+  finish();  
+}
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", menuTest, true);
+
+  content.location = "data:text/html,<title>Web Developer Menu Test</title>" +
+    "<p>testing the Web Developer Menu";
+}
+
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -24,16 +24,18 @@ browser.jar:
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 *       content/browser/fullscreen-video.xhtml        (content/fullscreen-video.xhtml)
 *       content/browser/inspector.html                (content/inspector.html)
+*       content/browser/scratchpad.xul                (content/scratchpad.xul)
+*       content/browser/scratchpad.js                 (content/scratchpad.js)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
 *       content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
 *       content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
 *       content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
 *       content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
 *       content/browser/pageinfo/feeds.xml            (content/pageinfo/feeds.xml)
 *       content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
 *       content/browser/pageinfo/security.js          (content/pageinfo/security.js)
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -1776,18 +1776,19 @@ SessionStoreService.prototype = {
       }
       catch (ex) { debug(ex); }
     }
 
     if (aEntry.docIdentifier) {
       entry.docIdentifier = aEntry.docIdentifier;
     }
 
-    if (aEntry.stateData) {
-      entry.stateData = aEntry.stateData;
+    if (aEntry.stateData != null) {
+      entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
+      entry.structuredCloneVersion = aEntry.stateData.formatVersion;
     }
 
     if (!(aEntry instanceof Ci.nsISHContainer)) {
       return entry;
     }
     
     if (aEntry.childCount > 0) {
       entry.children = [];
@@ -2993,18 +2994,23 @@ SessionStoreService.prototype = {
         aIdMap.used[id] = true;
       }
       shEntry.ID = id;
     }
 
     if (aEntry.docshellID)
       shEntry.docshellID = aEntry.docshellID;
 
-    if (aEntry.stateData) {
-      shEntry.stateData = aEntry.stateData;
+    if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
+      shEntry.stateData =
+        Cc["@mozilla.org/docshell/structured-clone-container;1"].
+        createInstance(Ci.nsIStructuredCloneContainer);
+
+      shEntry.stateData.initFromBase64(aEntry.structuredCloneState,
+                                       aEntry.structuredCloneVersion);
     }
 
     if (aEntry.scroll) {
       var scrollPos = (aEntry.scroll || "0,0").split(",");
       scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
       shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
     }
 
--- a/browser/components/sessionstore/test/browser/browser_500328.js
+++ b/browser/components/sessionstore/test/browser/browser_500328.js
@@ -63,18 +63,17 @@ function checkState(tab) {
       let elem = doc.createElement("div");
       elem.id = "new-elem";
       doc.body.appendChild(elem);
 
       contentWindow.history.forward();
     }
     else if (popStateCount == 1) {
       popStateCount++;
-      is(JSON.stringify(aEvent.state), JSON.stringify({obj3:3}),
-         "second popstate object.");
+      is(aEvent.state.obj3.toString(), '/^a$/', "second popstate object.");
 
       // Make sure that the new-elem node is present in the document.  If it's
       // not, then this history entry has a different doc identifier than the
       // previous entry, which is bad.
       let doc = contentWindow.document;
       let newElem = doc.getElementById("new-elem");
       ok(newElem, "doc should contain new-elem.");
       newElem.parentNode.removeChild(newElem);
@@ -115,22 +114,22 @@ function test() {
 
     tabBrowser.addEventListener("load", function(aEvent) {
       tabBrowser.removeEventListener("load", arguments.callee, true);
 
       // After these push/replaceState calls, the window should have three
       // history entries:
       //   testURL (state object: null)      <-- oldest
       //   testURL (state object: {obj1:1})
-      //   page2   (state object: {obj3:3})  <-- newest
+      //   page2   (state object: {obj3:/^a$/})  <-- newest
       let contentWindow = tab.linkedBrowser.contentWindow;
       let history = contentWindow.history;
       history.pushState({obj1:1}, "title-obj1");
       history.pushState({obj2:2}, "title-obj2", "page2");
-      history.replaceState({obj3:3}, "title-obj3");
+      history.replaceState({obj3:/^a$/}, "title-obj3");
 
       let state = ss.getTabState(tab);
       gBrowser.removeTab(tab);
 
       // Restore the state into a new tab.  Things don't work well when we
       // restore into the old tab, but that's not a real use case anyway.
       let tab2 = gBrowser.addTab("about:blank");
       ss.setTabState(tab2, state, true);
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -174,28 +174,46 @@ can reach it easily. -->
 <!ENTITY downloads.tooltip            "Display the progress of ongoing downloads">
 <!ENTITY downloads.accesskey          "D">
 <!ENTITY downloads.commandkey         "j">
 <!ENTITY downloadsUnix.commandkey     "y">
 <!ENTITY addons.label                 "Add-ons">
 <!ENTITY addons.accesskey             "A">
 <!ENTITY addons.commandkey            "A">
 
+<!ENTITY webDeveloperMenu.label       "Web Developer">
+<!ENTITY webDeveloperMenu.accesskey   "W">
+
 <!ENTITY errorConsoleCmd.label        "Error Console">
 <!ENTITY errorConsoleCmd.accesskey    "C">
 <!ENTITY errorConsoleCmd.commandkey   "j">
 
 <!ENTITY webConsoleCmd.label          "Web Console">
 <!ENTITY webConsoleCmd.accesskey      "W">
 <!ENTITY webConsoleCmd.commandkey     "k">
 
 <!ENTITY inspectMenu.label            "Inspect">
 <!ENTITY inspectMenu.accesskey        "T">
 <!ENTITY inspectMenu.commandkey       "I">
 
+<!-- LOCALIZATION NOTE (scratchpad.label): This menu item label appears
+  -  in the Tools menu. See bug 653093.
+  -  The Scratchpad is intended to provide a simple text editor for creating
+  -  and evaluating bits of JavaScript code for the purposes of function
+  -  prototyping, experimentation and convenient scripting.
+  -
+  -  It's quite possible that you won't have a good analogue for the word
+  -  "Scratchpad" in your locale. You should feel free to find a close
+  -  approximation to it or choose a word (or words) that means
+  -  "simple discardable text editor". -->
+<!ENTITY scratchpad.label             "Scratchpad">
+<!ENTITY scratchpad.accesskey         "r">
+<!ENTITY scratchpad.keycode           "VK_F4">
+<!ENTITY scratchpad.keytext           "F4">
+
 <!ENTITY inspectPanelTitle.label      "HTML">
 <!ENTITY inspectButton.label          "Inspect">
 <!ENTITY inspectButton.accesskey      "I">
 <!ENTITY inspectNextButton.label      "Next">
 <!ENTITY inspectNextButton.accesskey  "N">
 <!ENTITY inspectPreviousButton.label  "Previous">
 <!ENTITY inspectPreviousButton.accesskey "P">
 <!ENTITY inspectStyleButton.label     "Style">
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/scratchpad.dtd
@@ -0,0 +1,103 @@
+<!-- LOCALIZATION NOTE : FILE This file contains the Scratchpad window strings -->
+<!-- LOCALIZATION NOTE : FILE Do not translate commandkeys -->
+
+<!-- LOCALIZATION NOTE (scratchpad.title):
+  -  The Scratchpad is intended to provide a simple text editor for creating
+  -  and evaluating bits of JavaScript code for the purposes of function
+  -  prototyping, experimentation and convenient scripting.
+  -
+  -  It's quite possible that you won't have a good analogue for the word
+  -  "Scratchpad" in your locale. You should feel free to find a close
+  -  approximation to it or choose a word (or words) that means
+  -  "simple discardable text editor". -->
+<!ENTITY window.title                 "Scratchpad">
+
+<!ENTITY fileMenu.label               "File">
+<!ENTITY fileMenu.accesskey           "F">
+
+<!ENTITY newWindowCmd.label           "New Window">
+<!ENTITY newWindowCmd.accesskey       "N">
+<!ENTITY newWindowCmd.commandkey      "n">
+
+<!ENTITY openFileCmd.label            "Open File…">
+<!ENTITY openFileCmd.accesskey        "O">
+<!ENTITY openFileCmd.commandkey       "o">
+
+<!ENTITY saveFileCmd.label            "Save">
+<!ENTITY saveFileCmd.accesskey        "S">
+<!ENTITY saveFileCmd.commandkey       "s">
+
+<!ENTITY saveFileAsCmd.label          "Save As…">
+<!ENTITY saveFileAsCmd.accesskey      "A">
+
+<!ENTITY closeCmd.label               "Close">
+<!ENTITY closeCmd.key                 "W">
+<!ENTITY closeCmd.accesskey           "C">
+
+<!ENTITY editMenu.label               "Edit">
+<!ENTITY editMenu.accesskey           "E">
+
+<!ENTITY undoCmd.label                "Undo">
+<!ENTITY undoCmd.key                  "Z">
+<!ENTITY undoCmd.accesskey            "U">
+
+<!ENTITY redoCmd.label                "Redo">
+<!ENTITY redoCmd.key                  "Y">
+<!ENTITY redoCmd.accesskey            "R">
+
+<!ENTITY cutCmd.label                 "Cut">
+<!ENTITY cutCmd.key                   "X">
+<!ENTITY cutCmd.accesskey             "t">
+
+<!ENTITY copyCmd.label                "Copy">
+<!ENTITY copyCmd.key                  "C">
+<!ENTITY copyCmd.accesskey            "C">
+
+<!ENTITY pasteCmd.label               "Paste">
+<!ENTITY pasteCmd.key                 "V">
+<!ENTITY pasteCmd.accesskey           "P">
+
+<!ENTITY selectAllCmd.label           "Select All">
+<!ENTITY selectAllCmd.key             "A">
+<!ENTITY selectAllCmd.accesskey       "A">
+
+<!ENTITY execute.label                "Execute">
+<!ENTITY execute.accesskey            "E">
+<!ENTITY execute.key                  "t">
+
+<!ENTITY inspect.label                "Inspect">
+<!ENTITY inspect.accesskey            "I">
+<!ENTITY inspect.key                  "i">
+
+<!ENTITY print.label                  "Print">
+<!ENTITY print.accesskey              "p">
+<!ENTITY print.key                    "r">
+
+<!ENTITY contextMenu.label            "Context">
+<!ENTITY contextMenu.accesskey        "C">
+
+<!ENTITY contentContext.label         "Content">
+<!ENTITY contentContext.accesskey     "C">
+
+<!ENTITY chromeContext.label          "Chrome">
+<!ENTITY chromeContext.accesskey      "H">
+
+<!-- LOCALIZATION NOTE (resetContext.label): This command allows the developer
+  -  to reset/clear the global object of the context where the code executes.
+  -->
+<!ENTITY resetContext.label           "Reset">
+<!ENTITY resetContext.accesskey       "R">
+
+<!ENTITY toolsMenu.label              "Tools">
+<!ENTITY toolsMenu.accesskey          "T">
+
+<!ENTITY errorConsoleCmd.label        "Error Console">
+<!ENTITY errorConsoleCmd.accesskey    "C">
+<!ENTITY errorConsoleCmd.commandkey   "j">
+
+<!ENTITY webConsoleCmd.label          "Web Console">
+<!ENTITY webConsoleCmd.accesskey      "W">
+<!ENTITY webConsoleCmd.commandkey     "k">
+
+<!ENTITY textbox.placeholder          "// Enter some JavaScript, select it, right click and select Execute, Inspect or Print.">
+
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/scratchpad.properties
@@ -0,0 +1,30 @@
+# LOCALIZATION NOTE  (propertyPanel.updateButton.label): Used in the Property
+# Panel that is opened by the Scratchpad window when inspecting an object. This
+# is the Update button label.
+propertyPanel.updateButton.label=Update
+propertyPanel.updateButton.accesskey=U
+
+# LOCALIZATION NOTE  (export.fileOverwriteConfirmation): This is displayed when
+# the user attempts to save to an already existing file.
+export.fileOverwriteConfirmation=File exists. Overwrite?
+
+# LOCALIZATION NOTE  (browserWindow.unavailable): This error message is shown
+# when Scratchpad does not find any recently active window of navigator:browser
+# type.
+browserWindow.unavailable=Scratchpad cannot find any browser window to execute the code in.
+
+# LOCALIZATION NOTE  (openFile.title): This is the file picker title, when you
+# open a file from Scratchpad.
+openFile.title=Open File
+
+# LOCALIZATION NOTE  (openFile.failed): This is the message displayed when file
+# open fails.
+openFile.failed=Failed to read the file.
+
+# LOCALIZATION NOTE  (saveFileAs): This is the file picker title, when you save
+# a file in Scratchpad.
+saveFileAs=Save File As
+
+# LOCALIZATION NOTE  (saveFile.failed): This is the message displayed when file
+# save fails.
+saveFile.failed=The file save operation failed.
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -10,16 +10,18 @@
     locale/browser/aboutSessionRestore.dtd         (%chrome/browser/aboutSessionRestore.dtd)
 #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/inspector.properties            (%chrome/browser/inspector.properties)
+    locale/browser/scratchpad.properties           (%chrome/browser/scratchpad.properties)
+    locale/browser/scratchpad.dtd                  (%chrome/browser/scratchpad.dtd)
     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-aero.css
+++ b/browser/themes/winstripe/browser/browser-aero.css
@@ -83,17 +83,17 @@
     -moz-margin-end: 3px;
   }
 
   #main-window {
     -moz-appearance: -moz-win-borderless-glass;
     background: transparent;
   }
 
-  #main-window[chromemargin^="0,"][sizemode="normal"]:not([inFullscreen="true"]) #navigator-toolbox {
+  #main-window[chromemargin^="0,"][sizemode=normal] #navigator-toolbox {
     margin-top: -7px;
   }
 
   /* Artificially draw window borders that are covered by lwtheme, see bug 591930. */
   #main-window[sizemode="normal"] > #titlebar > #titlebar-content:-moz-lwtheme {
     border-top: 2px solid;
     -moz-border-top-colors: @glassActiveBorderColor@ rgba(255,255,255,.6);
   }
@@ -109,95 +109,93 @@
   #main-window[sizemode="normal"] #titlebar-buttonbox:-moz-lwtheme {
     margin-top: -2px;
   }
 
   #appmenu-button {
     margin-bottom: -1px; /* compensate white outer border */
   }
 
-  #main-window:not(:-moz-lwtheme)[inFullscreen="true"] {
+  #main-window[sizemode=fullscreen]:not(:-moz-lwtheme) {
     -moz-appearance: none;
     background-color: #556;
   }
 
   #toolbar-menubar:not(:-moz-lwtheme),
   #TabsToolbar[tabsontop=true]:not(:-moz-lwtheme),
   #navigator-toolbox[tabsontop=false] > #nav-bar:not(:-moz-lwtheme),
   #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child:not(:-moz-lwtheme) {
     background-color: transparent !important;
     color: black;
     text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
     border-left-style: none !important;
     border-right-style: none !important;
   }
 
   /* Vertical toolbar border */
-  #main-window[sizemode=normal] #navigator-toolbox:not([inFullscreen])::after,
-  #main-window[sizemode=normal] #navigator-toolbox[tabsontop=true] > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not([inFullscreen]),
-  #main-window[sizemode=normal] #navigator-toolbox[tabsontop=false] > toolbar:not(#toolbar-menubar):not(#nav-bar):not([inFullScreen]) {
+  #main-window[sizemode=normal] #navigator-toolbox::after,
+  #main-window[sizemode=normal] #navigator-toolbox[tabsontop=true] > toolbar:not(#toolbar-menubar):not(#TabsToolbar),
+  #main-window[sizemode=normal] #navigator-toolbox[tabsontop=false] > toolbar:not(#toolbar-menubar):not(#nav-bar) {
     border-left: 1px solid @toolbarShadowColor@;
     border-right: 1px solid @toolbarShadowColor@;
     background-clip: padding-box;
   }
   #main-window[sizemode=normal] #navigator-toolbox > toolbar:-moz-lwtheme {
     border-color: transparent !important;
   }
-  #main-window[sizemode=normal]:not([inFullscreen]) #browser-border-start,
-  #main-window[sizemode=normal]:not([inFullscreen]) #browser-border-end {
+  #main-window[sizemode=normal] #browser-border-start,
+  #main-window[sizemode=normal] #browser-border-end {
     display: -moz-box;
     background-color: @toolbarShadowColor@;
     width: 1px;
   }
   #main-window[sizemode=normal] #browser-bottombox {
     border: 1px solid @toolbarShadowColor@;
     border-top-style: none;
   }
 
   #main-window[sizemode=normal][tabsontop=false] #PersonalToolbar:not(:-moz-lwtheme) {
     border-top-left-radius: 3.5px;
     border-top-right-radius: 3.5px;
   }
 
   /* Toolbar shadow behind tabs */
   /* This code is only needed for restored windows (i.e. sizemode=normal)
-     because of the border radius on the toolbar below the tab bar.
-     :not([inFullscreen=true]) is added to ease the transition to and from
-     fullscreen mode, as the sizemode attribute isn't updated immediately. */
-  #main-window[sizemode=normal]:not([inFullscreen=true])[tabsontop=true] #nav-bar:not(:-moz-lwtheme),
+     because of the border radius on the toolbar below the tab bar. */
+  #main-window[sizemode=normal][tabsontop=true] #nav-bar:not(:-moz-lwtheme),
   #main-window[sizemode=normal][tabsontop=true] > #nav-bar[collapsed=true]:not([customizing]) + toolbar:not(:-moz-lwtheme),
   #main-window[sizemode=normal][tabsontop=true] > #nav-bar[collapsed=true]:not([customizing]) + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme) {
     border-top: 1px solid @toolbarShadowColor@;
     border-top-left-radius: 3.5px;
     border-top-right-radius: 3.5px;
     background-clip: padding-box;
   }
-  #main-window[sizemode=normal]:not([inFullscreen]):not([disablechrome]) #TabsToolbar[tabsontop=true]:not(:-moz-lwtheme) {
+  #main-window[sizemode=normal]:not([disablechrome]) #TabsToolbar[tabsontop=true]:not(:-moz-lwtheme) {
     margin-bottom: -1px;
     background-image: none !important;
   }
-  #main-window[sizemode=normal]:not([inFullscreen]):not([disablechrome]) #tabbrowser-tabs[tabsontop=true] > .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox > .scrollbox-innerbox:not(:-moz-lwtheme) {
+  #main-window[sizemode=normal]:not([disablechrome]) #tabbrowser-tabs[tabsontop=true] > .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox > .scrollbox-innerbox:not(:-moz-lwtheme) {
     position: relative;
   }
 
   #navigator-toolbox[tabsontop=false] > #PersonalToolbar {
     margin-top: 3px;
   }
   #navigator-toolbox[tabsontop=false] > #PersonalToolbar:not(:-moz-lwtheme) {
     margin-top: 2px;
     border-top: 1px solid @toolbarShadowColor@;
     background-image: -moz-linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
   }
 
-  #main-window[sizemode=normal] #TabsToolbar[tabsontop=true]:not([inFullscreen]) {
+  #main-window[sizemode=normal] #TabsToolbar[tabsontop=true] {
     padding-left: 4px;
     padding-right: 4px;
   }
 
-  #main-window[sizemode=normal] #TabsToolbar[tabsontop=false]:not([inFullscreen]) {
+  #main-window[sizemode=normal] #TabsToolbar[tabsontop=false] {
     padding-left: 2px;
     padding-right: 2px;
   }
 
   /* Make the window draggable by glassed toolbars (bug 555081) */
   #toolbar-menubar:not([autohide="true"]),
   #TabsToolbar[tabsontop="true"],
   #navigator-toolbox[tabsontop="false"] > #nav-bar,
@@ -253,17 +251,17 @@
   .searchbar-textbox[focused="true"] {
     background-color: white;
   }
 
   .tabbrowser-tab:not(:-moz-lwtheme) {
     text-shadow: none;
   }
 
-  #main-window[sizemode=normal]:not([inFullscreen=true]) .statuspanel-inner {
+  #main-window[sizemode=normal] .statuspanel-inner {
     /* align with the browser's side borders */
     padding-left: 1px;
     padding-right: 1px;
   }
 
   #allTabs-panel,
   #ctrlTab-panel {
     background: transparent;
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -1552,17 +1552,17 @@ richlistitem[type~="action"][actiontype=
 #TabsToolbar:not(:-moz-lwtheme),
 #TabsToolbar[tabsontop=false] {
   background-image:
     -moz-linear-gradient(bottom, @toolbarShadowColor@ 1px, rgba(0,0,0,.05) 1px, transparent 50%);
 }
 
 %ifndef WINSTRIPE_AERO
 @media all and (-moz-windows-default-theme) {
-  #main-window[sizemode=normal] #TabsToolbar:not([inFullscreen]) {
+  #main-window[sizemode=normal] #TabsToolbar {
     padding-left: 2px;
     padding-right: 2px;
   }
 }
 %endif
 
 .tabbrowser-tab,
 .tabs-newtab-button {
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -125,16 +125,17 @@ class nsIXTFService;
 #endif
 #ifdef IBMBIDI
 class nsIBidiKeyboard;
 #endif
 class nsIMIMEHeaderParam;
 class nsIObserver;
 class nsPresContext;
 class nsIChannel;
+class nsAutoScriptBlockerSuppressNodeRemoved;
 struct nsIntMargin;
 class nsPIDOMWindow;
 class nsIDocumentLoaderFactory;
 
 #ifndef have_PrefChangedFunc_typedef
 typedef int (*PR_CALLBACK PrefChangedFunc)(const char *, void *);
 #define have_PrefChangedFunc_typedef
 #endif
@@ -179,22 +180,28 @@ struct nsShortcutCandidate {
   {
   }
   PRUint32 mCharCode;
   PRBool   mIgnoreShift;
 };
 
 class nsContentUtils
 {
+  friend class nsAutoScriptBlockerSuppressNodeRemoved;
   typedef mozilla::dom::Element Element;
 
 public:
   static nsresult Init();
 
   /**
+   * Get a JSContext from the document's scope object.
+   */
+  static JSContext* GetContextFromDocument(nsIDocument *aDocument);
+
+  /**
    * Get a scope from aNewDocument. Also get a context through the scope of one
    * of the documents, from the stack or the safe context.
    *
    * @param aOldDocument The document to try to get a context from. May be null.
    * @param aNewDocument The document to get aNewScope from.
    * @param aCx [out] Context gotten through one of the scopes, from the stack
    *                  or the safe context.
    * @param aNewScope [out] Scope gotten from aNewDocument.
@@ -926,16 +933,44 @@ public:
    *
    * @return true if there are mutation listeners of the specified type
    */
   static PRBool HasMutationListeners(nsINode* aNode,
                                      PRUint32 aType,
                                      nsINode* aTargetForSubtreeModified);
 
   /**
+   * Quick helper to determine whether there are any mutation listeners
+   * of a given type that apply to any content in this document. It is valid
+   * to pass null for aDocument here, in which case this function always
+   * returns PR_TRUE.
+   *
+   * @param aDocument The document to search for listeners
+   * @param aType     The type of listener (NS_EVENT_BITS_MUTATION_*)
+   *
+   * @return true if there are mutation listeners of the specified type
+   */
+  static PRBool HasMutationListeners(nsIDocument* aDocument,
+                                     PRUint32 aType);
+  /**
+   * Synchronously fire DOMNodeRemoved on aChild. Only fires the event if
+   * there really are listeners by checking using the HasMutationListeners
+   * function above. The function makes sure to hold the relevant objects alive
+   * for the duration of the event firing. However there are no guarantees
+   * that any of the objects are alive by the time the function returns.
+   * If you depend on that you need to hold references yourself.
+   *
+   * @param aChild    The node to fire DOMNodeRemoved at.
+   * @param aParent   The parent of aChild.
+   * @param aOwnerDoc The ownerDocument of aChild.
+   */
+  static void MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent,
+                                   nsIDocument* aOwnerDoc);
+
+  /**
    * This method creates and dispatches a trusted event.
    * Works only with events which can be created by calling
    * nsIDOMDocumentEvent::CreateEvent() with parameter "Events".
    * @param aDoc           The document which will be used to create the event.
    * @param aTarget        The target of the event, should be QIable to
    *                       nsIDOMEventTarget.
    * @param aEventName     The name of the event.
    * @param aCanBubble     Whether the event can bubble.
@@ -1875,16 +1910,19 @@ private:
 
 #ifdef IBMBIDI
   static nsIBidiKeyboard* sBidiKeyboard;
 #endif
 
   static PRBool sInitialized;
   static PRUint32 sScriptBlockerCount;
   static PRUint32 sRemovableScriptBlockerCount;
+#ifdef DEBUG
+  static PRUint32 sDOMNodeRemovedSuppressCount;
+#endif
   static nsCOMArray<nsIRunnable>* sBlockedScriptRunners;
   static PRUint32 sRunnersCountAtFirstBlocker;
   static PRUint32 sScriptBlockerCountWhereRunnersPrevented;
 
   static nsIInterfaceRequestor* sSameOriginChecker;
 
   static PRBool sIsHandlingKeyBoardEvent;
   static PRBool sAllowXULXBL_for_file;
@@ -1966,16 +2004,31 @@ public:
 
 private:
   PRUint32 mNestingLevel;
   nsCOMPtr<nsIDocument> mDocument;
   nsCOMPtr<nsIDocumentObserver> mObserver;
   MOZILLA_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+class NS_STACK_CLASS nsAutoScriptBlockerSuppressNodeRemoved :
+                          public nsAutoScriptBlocker {
+public:
+  nsAutoScriptBlockerSuppressNodeRemoved() {
+#ifdef DEBUG
+    ++nsContentUtils::sDOMNodeRemovedSuppressCount;
+#endif
+  }
+  ~nsAutoScriptBlockerSuppressNodeRemoved() {
+#ifdef DEBUG
+    --nsContentUtils::sDOMNodeRemovedSuppressCount;
+#endif
+  }
+};
+
 #define NS_INTERFACE_MAP_ENTRY_TEAROFF(_interface, _allocator)                \
   if (aIID.Equals(NS_GET_IID(_interface))) {                                  \
     foundInterface = static_cast<_interface *>(_allocator);                   \
     if (!foundInterface) {                                                    \
       *aInstancePtr = nsnull;                                                 \
       return NS_ERROR_OUT_OF_MEMORY;                                          \
     }                                                                         \
   } else
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -62,16 +62,17 @@
 #include "nsPIDOMWindow.h"
 #ifdef MOZ_SMIL
 #include "nsSMILAnimationController.h"
 #endif // MOZ_SMIL
 #include "nsIScriptGlobalObject.h"
 #include "nsIDocumentEncoder.h"
 #include "nsIAnimationFrameListener.h"
 #include "nsEventStates.h"
+#include "nsIStructuredCloneContainer.h"
 
 class nsIContent;
 class nsPresContext;
 class nsIPresShell;
 class nsIDocShell;
 class nsStyleSet;
 class nsIStyleSheet;
 class nsIStyleRule;
@@ -118,18 +119,18 @@ class Loader;
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 
 #define NS_IDOCUMENT_IID      \
-{ 0x2c6ad63f, 0xb7b9, 0x42f8, \
- { 0xbd, 0xde, 0x76, 0x0a, 0x83, 0xe3, 0xb0, 0x49 } }
+{ 0x26ef6218, 0xcd5e, 0x4953,  \
+ { 0xbb, 0x57, 0xb8, 0x50, 0x29, 0xa1, 0xae, 0x40 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
@@ -1417,22 +1418,23 @@ public:
     Doc_Theme_Uninitialized, // not determined yet
     Doc_Theme_None,
     Doc_Theme_Neutral,
     Doc_Theme_Dark,
     Doc_Theme_Bright
   };
 
   /**
-   * Set the document's pending state object (as serialized to JSON).
+   * Set the document's pending state object (as serialized using structured
+   * clone).
    */
-  void SetCurrentStateObject(nsAString &obj)
+  void SetStateObject(nsIStructuredCloneContainer *scContainer)
   {
-    mCurrentStateObject.Assign(obj);
-    mCurrentStateObjectCached = nsnull;
+    mStateObjectContainer = scContainer;
+    mStateObjectCached = nsnull;
   }
 
   /**
    * Returns Doc_Theme_None if there is no lightweight theme specified,
    * Doc_Theme_Dark for a dark theme, Doc_Theme_Bright for a light theme, and
    * Doc_Theme_Neutral for any other theme. This is used to determine the state
    * of the pseudoclasses :-moz-lwtheme and :-moz-lwtheme-text.
    */
@@ -1510,17 +1512,17 @@ public:
   // Add/Remove images from the document image tracker
   virtual nsresult AddImage(imgIRequest* aImage) = 0;
   virtual nsresult RemoveImage(imgIRequest* aImage) = 0;
 
   // Makes the images on this document locked/unlocked. By default, the locking
   // state is unlocked/false.
   virtual nsresult SetImageLockingState(PRBool aLocked) = 0;
 
-  virtual nsresult GetMozCurrentStateObject(nsIVariant** aResult) = 0;
+  virtual nsresult GetStateObject(nsIVariant** aResult) = 0;
 
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
@@ -1719,34 +1721,33 @@ protected:
   PRUint32 mEventsSuppressed;
 
   /**
    * The number number of external scripts (ones with the src attribute) that
    * have this document as their owner and that are being evaluated right now.
    */
   PRUint32 mExternalScriptsBeingEvaluated;
 
-  nsString mCurrentStateObject;
-
   // Weak reference to mScriptGlobalObject QI:d to nsPIDOMWindow,
   // updated on every set of mSecriptGlobalObject.
   nsPIDOMWindow *mWindow;
 
   nsCOMPtr<nsIDocumentEncoder> mCachedEncoder;
 
   AnimationListenerList mAnimationFrameListeners;
 
   // The session history entry in which we're currently bf-cached. Non-null
   // if and only if we're currently in the bfcache.
   nsISHEntry* mSHEntry;
 
   // Our base target.
   nsString mBaseTarget;
 
-  nsCOMPtr<nsIVariant> mCurrentStateObjectCached;
+  nsCOMPtr<nsIStructuredCloneContainer> mStateObjectContainer;
+  nsCOMPtr<nsIVariant> mStateObjectCached;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -448,34 +448,17 @@ public:
   {
     return ReplaceOrInsertBefore(PR_TRUE, aNewChild, aOldChild, aReturn);
   }
   nsINode*
   AppendChild(nsINode *aNewChild, nsresult *aReturn)
   {
     return InsertBefore(aNewChild, nsnull, aReturn);
   }
-  nsresult RemoveChild(nsINode *aOldChild)
-  {
-    if (!aOldChild) {
-      return NS_ERROR_NULL_POINTER;
-    }
-
-    if (IsNodeOfType(eDATA_NODE)) {
-      return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-    }
-
-    PRInt32 index = IndexOf(aOldChild);
-    if (index == -1) {
-      // aOldChild isn't one of our children.
-      return NS_ERROR_DOM_NOT_FOUND_ERR;
-    }
-
-    return RemoveChildAt(index, PR_TRUE);
-  }
+  nsresult RemoveChild(nsINode *aOldChild);
 
   /**
    * Insert a content node at a particular index.  This method handles calling
    * BindToTree on the child appropriately.
    *
    * @param aKid the content to insert
    * @param aIndex the index it is being inserted at (the index it will have
    *        after it is inserted)
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -161,16 +161,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsIUGenCategory.h"
 #include "nsIDragService.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsCPrefetchService.h"
 #include "nsIChromeRegistry.h"
+#include "nsEventDispatcher.h"
 #include "nsIMIMEHeaderParam.h"
 #include "nsIDOMXULCommandEvent.h"
 #include "nsIDOMDragEvent.h"
 #include "nsDOMDataTransfer.h"
 #include "nsHtml5Module.h"
 #include "nsPresContext.h"
 #include "nsLayoutStatics.h"
 #include "nsLayoutUtils.h"
@@ -248,16 +249,19 @@ nsTArray<nsISupports**> *nsContentUtils:
 nsIScriptRuntime *nsContentUtils::sScriptRuntimes[NS_STID_ARRAY_UBOUND];
 PRInt32 nsContentUtils::sScriptRootCount[NS_STID_ARRAY_UBOUND];
 PRUint32 nsContentUtils::sJSGCThingRootCount;
 #ifdef IBMBIDI
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull;
 #endif
 PRUint32 nsContentUtils::sScriptBlockerCount = 0;
 PRUint32 nsContentUtils::sRemovableScriptBlockerCount = 0;
+#ifdef DEBUG
+PRUint32 nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
+#endif
 nsCOMArray<nsIRunnable>* nsContentUtils::sBlockedScriptRunners = nsnull;
 PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
 PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
 
 PRBool nsContentUtils::sIsHandlingKeyBoardEvent = PR_FALSE;
 PRBool nsContentUtils::sAllowXULXBL_for_file = PR_FALSE;
 
@@ -1386,18 +1390,18 @@ nsContentUtils::InProlog(nsINode *aNode)
   }
 
   nsIDocument* doc = static_cast<nsIDocument*>(parent);
   nsIContent* root = doc->GetRootElement();
 
   return !root || doc->IndexOf(aNode) < doc->IndexOf(root);
 }
 
-static JSContext *
-GetContextFromDocument(nsIDocument *aDocument)
+JSContext *
+nsContentUtils::GetContextFromDocument(nsIDocument *aDocument)
 {
   nsIScriptGlobalObject *sgo = aDocument->GetScopeObject();
   if (!sgo) {
     // No script global, no context.
     return nsnull;
   }
 
   nsIScriptContext *scx = sgo->GetContext();
@@ -3644,22 +3648,16 @@ nsContentUtils::HasMutationListeners(nsI
                                      PRUint32 aType,
                                      nsINode* aTargetForSubtreeModified)
 {
   nsIDocument* doc = aNode->GetOwnerDoc();
   if (!doc) {
     return PR_FALSE;
   }
 
-  NS_ASSERTION((aNode->IsNodeOfType(nsINode::eCONTENT) &&
-                static_cast<nsIContent*>(aNode)->
-                  IsInNativeAnonymousSubtree()) ||
-               sScriptBlockerCount == sRemovableScriptBlockerCount,
-               "Want to fire mutation events, but it's not safe");
-
   // global object will be null for documents that don't have windows.
   nsPIDOMWindow* window = doc->GetInnerWindow();
   // This relies on nsEventListenerManager::AddEventListener, which sets
   // all mutation bits when there is a listener for DOMSubtreeModified event.
   if (window && !window->HasMutationListeners(aType)) {
     return PR_FALSE;
   }
 
@@ -3709,16 +3707,73 @@ nsContentUtils::HasMutationListeners(nsI
     }
     aNode = aNode->GetNodeParent();
   }
 
   return PR_FALSE;
 }
 
 /* static */
+PRBool
+nsContentUtils::HasMutationListeners(nsIDocument* aDocument,
+                                     PRUint32 aType)
+{
+  nsPIDOMWindow* window = aDocument ?
+    aDocument->GetInnerWindow() : nsnull;
+
+  // This relies on nsEventListenerManager::AddEventListener, which sets
+  // all mutation bits when there is a listener for DOMSubtreeModified event.
+  return !window || window->HasMutationListeners(aType);
+}
+
+void
+nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent,
+                                     nsIDocument* aOwnerDoc)
+{
+  NS_PRECONDITION(aChild, "Missing child");
+  NS_PRECONDITION(aChild->GetNodeParent() == aParent, "Wrong parent");
+  NS_PRECONDITION(aChild->GetOwnerDoc() == aOwnerDoc, "Wrong owner-doc");
+
+  // This checks that IsSafeToRunScript is true since we don't want to fire
+  // events when that is false. We can't rely on nsEventDispatcher to assert
+  // this in this situation since most of the time there are no mutation
+  // event listeners, in which case we won't even attempt to dispatch events.
+  // However this also allows for two exceptions. First off, we don't assert
+  // if the mutation happens to native anonymous content since we never fire
+  // mutation events on such content anyway.
+  // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
+  // that is a know case when we'd normally fire a mutation event, but can't
+  // make that safe and so we suppress it at this time. Ideally this should
+  // go away eventually.
+  NS_ASSERTION(aChild->IsNodeOfType(nsINode::eCONTENT) &&
+               static_cast<nsIContent*>(aChild)->
+                 IsInNativeAnonymousSubtree() ||
+               IsSafeToRunScript() ||
+               sDOMNodeRemovedSuppressCount,
+               "Want to fire DOMNodeRemoved event, but it's not safe");
+
+  // Having an explicit check here since it's an easy mistake to fall into,
+  // and there might be existing code with problems. We'd rather be safe
+  // than fire DOMNodeRemoved in all corner cases. We also rely on it for
+  // nsAutoScriptBlockerSuppressNodeRemoved.
+  if (!IsSafeToRunScript()) {
+    return;
+  }
+
+  if (HasMutationListeners(aChild,
+        NS_EVENT_BITS_MUTATION_NODEREMOVED, aParent)) {
+    nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED);
+    mutation.mRelatedNode = do_QueryInterface(aParent);
+
+    mozAutoSubtreeModified subtree(aOwnerDoc, aParent);
+    nsEventDispatcher::Dispatch(aChild, nsnull, &mutation);
+  }
+}
+
+/* static */
 void
 nsContentUtils::TraverseListenerManager(nsINode *aNode,
                                         nsCycleCollectionTraversalCallback &cb)
 {
   if (!sEventListenerManagersHash.ops) {
     // We're already shut down, just return.
     return;
   }
@@ -4079,16 +4134,47 @@ nsContentUtils::CreateDocument(const nsA
 }
 
 /* static */
 nsresult
 nsContentUtils::SetNodeTextContent(nsIContent* aContent,
                                    const nsAString& aValue,
                                    PRBool aTryReuse)
 {
+  // Fire DOMNodeRemoved mutation events before we do anything else.
+  nsCOMPtr<nsIContent> owningContent;
+
+  // Batch possible DOMSubtreeModified events.
+  mozAutoSubtreeModified subtree(nsnull, nsnull);
+
+  // Scope firing mutation events so that we don't carry any state that
+  // might be stale
+  {
+    // We're relying on mozAutoSubtreeModified to keep a strong reference if
+    // needed.
+    nsIDocument* doc = aContent->GetOwnerDoc();
+
+    // Optimize the common case of there being no observers
+    if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
+      subtree.UpdateTarget(doc, nsnull);
+      owningContent = aContent;
+      nsCOMPtr<nsINode> child;
+      bool skipFirst = aTryReuse;
+      for (child = aContent->GetFirstChild();
+           child && child->GetNodeParent() == aContent;
+           child = child->GetNextSibling()) {
+        if (skipFirst && child->IsNodeOfType(nsINode::eTEXT)) {
+          skipFirst = false;
+          continue;
+        }
+        nsContentUtils::MaybeFireNodeRemoved(child, aContent, doc);
+      }
+    }
+  }
+
   // Might as well stick a batch around this since we're performing several
   // mutations.
   mozAutoDocUpdate updateBatch(aContent->GetCurrentDoc(),
     UPDATE_CONTENT_MODEL, PR_TRUE);
 
   PRUint32 childCount = aContent->GetChildCount();
 
   if (aTryReuse && !aValue.IsEmpty()) {
@@ -4102,17 +4188,17 @@ nsContentUtils::SetNodeTextContent(nsICo
         NS_ENSURE_SUCCESS(rv, rv);
 
         removeIndex = 1;
       }
       else {
         aContent->RemoveChildAt(removeIndex, PR_TRUE);
       }
     }
-    
+
     if (removeIndex == 1) {
       return NS_OK;
     }
   }
   else {
     // i is unsigned, so i >= is always true
     for (PRUint32 i = childCount; i-- != 0; ) {
       aContent->RemoveChildAt(i, PR_TRUE);
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -54,16 +54,17 @@
 #include "nsEventDispatcher.h"
 #include "nsGkAtoms.h"
 #include "nsCOMArray.h"
 #include "nsNodeUtils.h"
 #include "nsIEventListenerManager.h"
 #include "nsTextNode.h"
 #include "mozAutoDocUpdate.h"
 #include "nsMutationEvent.h"
+#include "nsPLDOMEvent.h"
 
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 PRBool nsDOMAttribute::sInitialized;
 
 nsDOMAttribute::nsDOMAttribute(nsDOMAttributeMap *aAttrMap,
                                already_AddRefed<nsINodeInfo> aNodeInfo,
@@ -615,33 +616,16 @@ nsDOMAttribute::RemoveChildAt(PRUint32 a
   if (aIndex != 0 || !mChild) {
     return NS_OK;
   }
 
   {
     nsCOMPtr<nsIContent> child = mChild;
     nsMutationGuard::DidMutate();
     mozAutoDocUpdate updateBatch(GetOwnerDoc(), UPDATE_CONTENT_MODEL, aNotify);
-    nsMutationGuard guard;
-  
-    mozAutoSubtreeModified subtree(nsnull, nsnull);
-    if (aNotify &&
-        nsContentUtils::HasMutationListeners(mChild,
-                                             NS_EVENT_BITS_MUTATION_NODEREMOVED,
-                                             this)) {
-      mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-      nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED);
-      mutation.mRelatedNode =
-        do_QueryInterface(static_cast<nsIAttribute*>(this));
-      subtree.UpdateTarget(GetOwnerDoc(), this);
-      nsEventDispatcher::Dispatch(mChild, nsnull, &mutation);
-    }
-    if (guard.Mutated(0) && mChild != child) {
-      return NS_OK;
-    }
 
     doRemoveChild(aNotify);
   }
 
   nsString nullString;
   SetDOMStringToNull(nullString);
   SetValue(nullString);
   return NS_OK;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1803,17 +1803,17 @@ static const char* kNSURIs[] = {
   " (XBL)",
   " (MathML)",
   " (RDF)",
   " (XUL)"
 };
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
   if (NS_UNLIKELY(cb.WantDebugInfo())) {
-    char name[72];
+    char name[512];
     PRUint32 nsid = tmp->GetDefaultNamespaceID();
     nsCAutoString uri;
     if (tmp->mDocumentURI)
       tmp->mDocumentURI->GetSpec(uri);
     if (nsid < NS_ARRAY_LENGTH(kNSURIs)) {
       PR_snprintf(name, sizeof(name), "nsDocument%s %s", kNSURIs[nsid],
                   uri.get());
     }
@@ -1876,17 +1876,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptEventManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXPathEvaluatorTearoff)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLayoutHistoryState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnloadBlocker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMImplementation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginalDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedEncoder)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentStateObjectCached)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStateObjectCached)
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPreloadingImages)
 
   for (PRUint32 i = 0; i < tmp->mAnimationFrameListeners.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAnimationFrameListeners[i]");
@@ -5979,63 +5979,95 @@ BlastSubtreeToPieces(nsINode *aNode)
 #endif
       aNode->RemoveChildAt(0, PR_FALSE);
 
     // XXX Should we abort here?
     NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, RemoveChildAt shouldn't fail!");
   }
 }
 
+
+class nsUserDataCaller : public nsRunnable
+{
+public:
+  nsUserDataCaller(nsCOMArray<nsINode>& aNodesWithProperties,
+                   nsIDocument* aOwnerDoc)
+    : mNodesWithProperties(aNodesWithProperties),
+      mOwnerDoc(aOwnerDoc)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    nsNodeUtils::CallUserDataHandlers(mNodesWithProperties, mOwnerDoc,
+                                      nsIDOMUserDataHandler::NODE_ADOPTED,
+                                      PR_FALSE);
+    return NS_OK;
+  }
+
+private:
+  nsCOMArray<nsINode> mNodesWithProperties;
+  nsCOMPtr<nsIDocument> mOwnerDoc;
+};
+
 NS_IMETHODIMP
 nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult)
 {
   NS_ENSURE_ARG(aAdoptedNode);
 
   *aResult = nsnull;
 
   nsresult rv = nsContentUtils::CheckSameOrigin(this, aAdoptedNode);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsINode> adoptedNode;
+  nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode);
+  
+  // Scope firing mutation events so that we don't carry any state that
+  // might be stale
+  {
+    nsINode* parent = adoptedNode->GetNodeParent();
+    if (parent) {
+      nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent,
+                                           adoptedNode->GetOwnerDoc());
+    }
+  }
+
+  nsAutoScriptBlocker scriptBlocker;
+
   PRUint16 nodeType;
   aAdoptedNode->GetNodeType(&nodeType);
   switch (nodeType) {
     case nsIDOMNode::ATTRIBUTE_NODE:
     {
       // Remove from ownerElement.
-      nsCOMPtr<nsIDOMAttr> adoptedAttr = do_QueryInterface(aAdoptedNode, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsCOMPtr<nsIDOMAttr> adoptedAttr = do_QueryInterface(aAdoptedNode);
+      NS_ASSERTION(adoptedAttr, "Attribute not implementing nsIDOMAttr");
 
       nsCOMPtr<nsIDOMElement> ownerElement;
       rv = adoptedAttr->GetOwnerElement(getter_AddRefs(ownerElement));
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (ownerElement) {
         nsCOMPtr<nsIDOMAttr> newAttr;
         rv = ownerElement->RemoveAttributeNode(adoptedAttr,
                                                getter_AddRefs(newAttr));
         NS_ENSURE_SUCCESS(rv, rv);
 
         newAttr.swap(adoptedAttr);
       }
 
-      adoptedNode = do_QueryInterface(adoptedAttr, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
       break;
     }
     case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
     case nsIDOMNode::ELEMENT_NODE:
     case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
     case nsIDOMNode::TEXT_NODE:
     case nsIDOMNode::CDATA_SECTION_NODE:
     case nsIDOMNode::COMMENT_NODE:
     {
-      adoptedNode = do_QueryInterface(aAdoptedNode, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
-
       // We don't want to adopt an element into its own contentDocument or into
       // a descendant contentDocument, so we check if the frameElement of this
       // document or any of its parents is the adopted node or one of its
       // descendants.
       nsIDocument *doc = this;
       do {
         nsPIDOMWindow *win = doc->GetWindow();
         if (win) {
@@ -6044,23 +6076,19 @@ nsDocument::AdoptNode(nsIDOMNode *aAdopt
           if (node &&
               nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
             return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
           }
         }
       } while ((doc = doc->GetParentDocument()));
 
       // Remove from parent.
-      nsCOMPtr<nsIDOMNode> parent;
-      aAdoptedNode->GetParentNode(getter_AddRefs(parent));
-      NS_ENSURE_SUCCESS(rv, rv);
-
+      nsINode* parent = adoptedNode->GetNodeParent();
       if (parent) {
-        nsCOMPtr<nsIDOMNode> newChild;
-        rv = parent->RemoveChild(aAdoptedNode, getter_AddRefs(newChild));
+        rv = parent->RemoveChildAt(parent->IndexOf(adoptedNode), PR_TRUE);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       break;
     }
     case nsIDOMNode::ENTITY_REFERENCE_NODE:
     {
       return NS_ERROR_NOT_IMPLEMENTED;
@@ -6130,24 +6158,23 @@ nsDocument::AdoptNode(nsIDOMNode *aAdopt
     if (NS_FAILED(rv)) {
       // Disconnect all nodes from their parents.
       BlastSubtreeToPieces(adoptedNode);
 
       return rv;
     }
   }
 
-  rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, this,
-                                         nsIDOMUserDataHandler::NODE_ADOPTED,
-                                         PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (adoptedNode->GetOwnerDoc() != this) {
-    return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
-  }
+  if (nodesWithProperties.Count()) {
+    nsContentUtils::AddScriptRunner(new nsUserDataCaller(nodesWithProperties,
+                                                         this));
+  }
+
+  NS_ASSERTION(adoptedNode->GetOwnerDoc() == this,
+               "Should still be in the document we just got adopted into");
 
   return CallQueryInterface(adoptedNode, aResult);
 }
 
 NS_IMETHODIMP
 nsDocument::NormalizeDocument()
 {
   return Normalize();
@@ -7557,20 +7584,18 @@ nsDocument::MutationEventDispatched(nsIN
         realTargets.AppendObject(possibleTarget);
       }
     }
 
     mSubtreeModifiedTargets.Clear();
 
     PRInt32 realTargetCount = realTargets.Count();
     for (PRInt32 k = 0; k < realTargetCount; ++k) {
-      mozAutoRemovableBlockerRemover blockerRemover(this);
-
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_SUBTREEMODIFIED);
-      nsEventDispatcher::Dispatch(realTargets[k], nsnull, &mutation);
+      (new nsPLDOMEvent(realTargets[k], mutation))->RunDOMEventWhenSafe();
     }
   }
 }
 
 void
 nsDocument::AddStyleRelevantLink(Link* aLink)
 {
   NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
@@ -8148,57 +8173,32 @@ nsIDocument::ScheduleBeforePaintEvent(ns
       !mPresShell ||
       mPresShell->GetPresContext()->RefreshDriver()->
         ScheduleBeforePaintEvent(this);
   }
 
 }
 
 nsresult
-nsDocument::GetMozCurrentStateObject(nsIVariant** aState)
-{
-  // Get the document's current state object. This is the object returned form
-  // both document.mozCurrentStateObject as well as popStateEvent.state
+nsDocument::GetStateObject(nsIVariant** aState)
+{
+  // Get the document's current state object. This is the object backing both
+  // history.state and popStateEvent.state.
+  //
+  // mStateObjectContainer may be null; this just means that there's no
+  // current state object.
 
   nsCOMPtr<nsIVariant> stateObj;
-  // Parse the JSON, if there's any to parse.
-  if (!mCurrentStateObjectCached && !mCurrentStateObject.IsEmpty()) {
-    // Get the JSContext associated with this document. We need this for
-    // deserialization.
-    nsIScriptGlobalObject *sgo = GetScopeObject();
-    NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
-
-    nsIScriptContext *scx = sgo->GetContext();
-    NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
-
-    JSContext *cx = (JSContext*) scx->GetNativeContext();
-
-    // Make sure we in the request while we have jsval on the native stack.
-    JSAutoRequest ar(cx);
-
-    // If our json call triggers a JS-to-C++ call, we want that call to use cx
-    // as the context.  So we push cx onto the context stack.
-    nsCxPusher cxPusher;
-
-    jsval jsStateObj = JSVAL_NULL;
-
-    // Deserialize the state object into an nsIVariant.
-    nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
-    NS_ENSURE_TRUE(cxPusher.Push(cx), NS_ERROR_FAILURE);
-    nsresult rv = json->DecodeToJSVal(mCurrentStateObject, cx, &jsStateObj);
-    NS_ENSURE_SUCCESS(rv, rv);
-    cxPusher.Pop();
-
-    nsCOMPtr<nsIXPConnect> xpconnect = do_GetService(nsIXPConnect::GetCID());
-    NS_ENSURE_TRUE(xpconnect, NS_ERROR_FAILURE);
-    rv = xpconnect->JSValToVariant(cx, &jsStateObj, getter_AddRefs(mCurrentStateObjectCached));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  NS_IF_ADDREF(*aState = mCurrentStateObjectCached);
+  if (!mStateObjectCached && mStateObjectContainer) {
+    JSContext *cx = nsContentUtils::GetContextFromDocument(this);
+    mStateObjectContainer->
+      DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
+  }
+
+  NS_IF_ADDREF(*aState = mStateObjectCached);
   
   return NS_OK;
 }
 
 nsresult
 nsDocument::AddImage(imgIRequest* aImage)
 {
   NS_ENSURE_ARG_POINTER(aImage);
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -974,17 +974,17 @@ public:
   virtual Element *GetElementById(const nsAString& aElementId);
 
   virtual Element *LookupImageElement(const nsAString& aElementId);
 
   virtual NS_HIDDEN_(nsresult) AddImage(imgIRequest* aImage);
   virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage);
   virtual NS_HIDDEN_(nsresult) SetImageLockingState(PRBool aLocked);
 
-  virtual nsresult GetMozCurrentStateObject(nsIVariant** aResult);
+  virtual nsresult GetStateObject(nsIVariant** aResult);
 
 protected:
   friend class nsNodeUtils;
 
   /**
    * Check that aId is not empty and log a message to the console
    * service if it is.
    * @returns PR_TRUE if aId looks correct, PR_FALSE otherwise.
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -58,16 +58,17 @@
 #include "nsIDOMUserDataHandler.h"
 #include "nsChangeHint.h"
 #include "nsEventDispatcher.h"
 #include "nsCOMArray.h"
 #include "nsNodeUtils.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozAutoDocUpdate.h"
+#include "nsPLDOMEvent.h"
 
 #include "pldhash.h"
 #include "prprf.h"
 
 namespace css = mozilla::css;
 
 nsGenericDOMDataNode::nsGenericDOMDataNode(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsIContent(aNodeInfo)
@@ -386,29 +387,27 @@ nsGenericDOMDataNode::SetTextInternal(PR
       aOffset == textLength,
       aOffset,
       endOffset,
       aLength
     };
     nsNodeUtils::CharacterDataChanged(this, &info);
 
     if (haveMutationListeners) {
-      mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_CHARACTERDATAMODIFIED);
 
       mutation.mPrevAttrValue = oldValue;
       if (aLength > 0) {
         nsAutoString val;
         mText.AppendTo(val);
         mutation.mNewAttrValue = do_GetAtom(val);
       }
 
       mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-      nsEventDispatcher::Dispatch(this, nsnull, &mutation);
+      (new nsPLDOMEvent(this, mutation))->RunDOMEventWhenSafe();
     }
   }
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 
@@ -951,48 +950,68 @@ nsGenericTextNode::GetWholeText(nsAStrin
 }
 
 nsIContent*
 nsGenericTextNode::ReplaceWholeText(const nsAFlatString& aContent,
                                     nsresult* aResult)
 {
   *aResult = NS_OK;
 
-  // Batch possible DOMSubtreeModified events.
-  mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
-  mozAutoDocUpdate updateBatch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, PR_TRUE);
-
+  // Handle parent-less nodes
   nsCOMPtr<nsIContent> parent = GetParent();
-
-  // Handle parent-less nodes
   if (!parent) {
     if (aContent.IsEmpty()) {
       return nsnull;
     }
 
-    SetText(aContent.get(), aContent.Length(), PR_TRUE);
+    SetNodeValue(aContent);
     return this;
   }
 
+  // We're relying on mozAutoSubtreeModified to keep the doc alive here.
+  nsIDocument* doc = GetOwnerDoc();
+
+  // Batch possible DOMSubtreeModified events.
+  mozAutoSubtreeModified subtree(doc, nsnull);
+
   PRInt32 index = parent->IndexOf(this);
   if (index < 0) {
     NS_WARNING("Trying to use .replaceWholeText with an anonymous text node "
                "child of a binding parent?");
     *aResult = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     return nsnull;
   }
 
   // We don't support entity references or read-only nodes, so remove the
   // logically adjacent text nodes (which therefore must all be siblings of
   // this) and set this one to the provided text, if that text isn't empty.
   PRInt32 first =
     FirstLogicallyAdjacentTextNode(parent, index);
   PRInt32 last =
     LastLogicallyAdjacentTextNode(parent, index, parent->GetChildCount());
 
+  // Fire mutation events. Optimize the common case of there being no
+  // listeners
+  if (nsContentUtils::
+        HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
+    for (PRInt32 i = first; i <= last; ++i) {
+      nsCOMPtr<nsIContent> child = parent->GetChildAt((PRUint32)i);
+      if (child &&
+          (i != index || aContent.IsEmpty())) {
+        nsContentUtils::MaybeFireNodeRemoved(child, parent, doc);
+      }
+    }
+  }
+
+  // Remove the needed nodes
+  // Don't want to use 'doc' here since it might no longer be the correct
+  // document.
+  mozAutoDocUpdate updateBatch(parent->GetCurrentDoc(), UPDATE_CONTENT_MODEL,
+                               PR_TRUE);
+
   do {
     if (last == index && !aContent.IsEmpty())
       continue;
 
     parent->RemoveChildAt(last, PR_TRUE);
   } while (last-- > first);
 
   // Empty string means we removed this node too.
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -129,16 +129,17 @@
 #include "nsLayoutUtils.h"
 #include "nsIView.h"
 #include "nsIViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsXBLInsertionPoint.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
+#include "nsPLDOMEvent.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif /* MOZ_XUL */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
@@ -519,16 +520,40 @@ nsINode::GetOwnerDocument(nsIDOMDocument
   *aOwnerDocument = nsnull;
 
   nsIDocument *ownerDoc = GetOwnerDocument();
 
   return ownerDoc ? CallQueryInterface(ownerDoc, aOwnerDocument) : NS_OK;
 }
 
 nsresult
+nsINode::RemoveChild(nsINode *aOldChild)
+{
+  if (!aOldChild) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  if (IsNodeOfType(eDATA_NODE)) {
+    return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
+  }
+
+  if (aOldChild && aOldChild->GetNodeParent() == this) {
+    nsContentUtils::MaybeFireNodeRemoved(aOldChild, this, GetOwnerDoc());
+  }
+
+  PRInt32 index = IndexOf(aOldChild);
+  if (index == -1) {
+    // aOldChild isn't one of our children.
+    return NS_ERROR_DOM_NOT_FOUND_ERR;
+  }
+
+  return RemoveChildAt(index, PR_TRUE);
+}
+
+nsresult
 nsINode::ReplaceOrInsertBefore(PRBool aReplace, nsIDOMNode* aNewChild,
                                nsIDOMNode* aRefChild, nsIDOMNode** aReturn)
 {
   nsCOMPtr<nsINode> newChild = do_QueryInterface(aNewChild);
 
   nsresult rv;
   nsCOMPtr<nsINode> refChild;
   if (aRefChild) {
@@ -2709,18 +2734,24 @@ nsGenericElement::JoinTextNodes(nsIConte
   }
 
   return rv;
 }
 
 nsresult
 nsGenericElement::Normalize()
 {
+  // We're relying on mozAutoSubtreeModified to keep the doc alive here.
+  nsIDocument* doc = GetOwnerDoc();
+
   // Batch possible DOMSubtreeModified events.
-  mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
+  mozAutoSubtreeModified subtree(doc, nsnull);
+
+  bool hasRemoveListeners = nsContentUtils::
+    HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED);
 
   nsresult result = NS_OK;
   PRUint32 index, count = GetChildCount();
 
   for (index = 0; (index < count) && (NS_OK == result); index++) {
     nsIContent *child = GetChildAt(index);
 
     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(child);
@@ -2728,39 +2759,45 @@ nsGenericElement::Normalize()
       PRUint16 nodeType;
       node->GetNodeType(&nodeType);
 
       switch (nodeType) {
         case nsIDOMNode::TEXT_NODE:
 
           // ensure that if the text node is empty, it is removed
           if (0 == child->TextLength()) {
+            if (hasRemoveListeners) {
+              nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
+            }
             result = RemoveChildAt(index, PR_TRUE);
             if (NS_FAILED(result)) {
               return result;
             }
 
             count--;
             index--;
             break;
           }
  
           if (index+1 < count) {
             // Get the sibling. If it's also a text node, then
             // remove it from the tree and join the two text
             // nodes.
-            nsIContent *sibling = GetChildAt(index + 1);
+            nsCOMPtr<nsIContent> sibling = GetChildAt(index + 1);
 
             nsCOMPtr<nsIDOMNode> siblingNode = do_QueryInterface(sibling);
 
             if (siblingNode) {
               PRUint16 siblingNodeType;
               siblingNode->GetNodeType(&siblingNodeType);
 
               if (siblingNodeType == nsIDOMNode::TEXT_NODE) {
+                if (hasRemoveListeners) {
+                  nsContentUtils::MaybeFireNodeRemoved(sibling, this, doc);
+                }
                 result = RemoveChildAt(index+1, PR_TRUE);
                 if (NS_FAILED(result)) {
                   return result;
                 }
 
                 result = JoinTextNodes(child, sibling);
                 if (NS_FAILED(result)) {
                   return result;
@@ -3512,72 +3549,75 @@ nsGenericElement::InsertChildAt(nsIConte
   NS_PRECONDITION(aKid, "null ptr");
 
   return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
 }
 
 static nsresult
 AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
 {
+  NS_ASSERTION(!aNode->GetNodeParent(),
+               "Should have removed from parent already");
+
   nsIDocument *doc = aParent->GetOwnerDoc();
 
   nsresult rv;
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMNode> adoptedNode;
   rv = domDoc->AdoptNode(node, getter_AddRefs(adoptedNode));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (doc != aParent->GetOwnerDoc()) {
-    return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
-  }
-
+  NS_ASSERTION(aParent->GetOwnerDoc() == doc,
+               "ownerDoc chainged while adopting");
   NS_ASSERTION(adoptedNode == node, "Uh, adopt node changed nodes?");
   NS_ASSERTION(aParent->HasSameOwnerDoc(aNode),
                "ownerDocument changed again after adopting!");
 
   return NS_OK;
 }
 
 nsresult
 nsINode::doInsertChildAt(nsIContent* aKid, PRUint32 aIndex,
                          PRBool aNotify, nsAttrAndChildArray& aChildArray)
 {
+  NS_PRECONDITION(!aKid->GetNodeParent(),
+                  "Inserting node that already has parent");
   nsresult rv;
 
+  // The id-handling code, and in the future possibly other code, need to
+  // react to unexpected attribute changes.
+  nsMutationGuard::DidMutate();
+
+  // Do this before checking the child-count since this could cause mutations
+  nsIDocument* doc = GetCurrentDoc();
+  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
+
   if (!HasSameOwnerDoc(aKid)) {
     nsCOMPtr<nsIDOMNode> kid = do_QueryInterface(aKid, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
- 
+
     PRUint16 nodeType = 0;
     rv = kid->GetNodeType(&nodeType);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // DocumentType nodes are the only nodes that can have a null
     // ownerDocument according to the DOM spec, and we need to allow
     // inserting them w/o calling AdoptNode().
 
     if (nodeType != nsIDOMNode::DOCUMENT_TYPE_NODE || aKid->GetOwnerDoc()) {
       rv = AdoptNodeIntoOwnerDoc(this, aKid);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
-  // The id-handling code, and in the future possibly other code, need to
-  // react to unexpected attribute changes.
-  nsMutationGuard::DidMutate();
-
-  // Do this before checking the child-count since this could cause mutations
-  nsIDocument* doc = GetCurrentDoc();
-  mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
-
   PRUint32 childCount = aChildArray.ChildCount();
   NS_ENSURE_TRUE(aIndex <= childCount, NS_ERROR_ILLEGAL_VALUE);
   PRBool isAppend = (aIndex == childCount);
 
   rv = aChildArray.InsertChildAt(aKid, aIndex);
   NS_ENSURE_SUCCESS(rv, rv);
   if (aIndex == 0) {
     mFirstChild = aKid;
@@ -3605,23 +3645,21 @@ nsINode::doInsertChildAt(nsIContent* aKi
     if (parent && isAppend) {
       nsNodeUtils::ContentAppended(parent, aKid, aIndex);
     } else {
       nsNodeUtils::ContentInserted(this, aKid, aIndex);
     }
 
     if (nsContentUtils::HasMutationListeners(aKid,
           NS_EVENT_BITS_MUTATION_NODEINSERTED, this)) {
-      mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-      
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED);
       mutation.mRelatedNode = do_QueryInterface(this);
 
       mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-      nsEventDispatcher::Dispatch(aKid, nsnull, &mutation);
+      (new nsPLDOMEvent(aKid, mutation))->RunDOMEventWhenSafe();
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsGenericElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationEvent)
@@ -3637,51 +3675,26 @@ nsGenericElement::RemoveChildAt(PRUint32
   return NS_OK;
 }
 
 nsresult
 nsINode::doRemoveChildAt(PRUint32 aIndex, PRBool aNotify,
                          nsIContent* aKid, nsAttrAndChildArray& aChildArray,
                          PRBool aMutationEvent)
 {
-  nsIDocument* doc = GetCurrentDoc();
-
-  nsMutationGuard::DidMutate();
-
   NS_PRECONDITION(aKid && aKid->GetNodeParent() == this &&
                   aKid == GetChildAt(aIndex) &&
                   IndexOf(aKid) == (PRInt32)aIndex, "Bogus aKid");
 
+  nsMutationGuard::DidMutate();
+
+  nsIDocument* doc = GetCurrentDoc();
+
   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
 
-  nsMutationGuard guard;
-
-  mozAutoSubtreeModified subtree(nsnull, nsnull);
-  if (aNotify &&
-      aMutationEvent &&
-      nsContentUtils::HasMutationListeners(aKid,
-        NS_EVENT_BITS_MUTATION_NODEREMOVED, this)) {
-    mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-
-    nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED);
-    mutation.mRelatedNode = do_QueryInterface(this);
-
-    subtree.UpdateTarget(GetOwnerDoc(), this);
-    nsEventDispatcher::Dispatch(aKid, nsnull, &mutation);
-  }
-
-  // Someone may have removed the kid or any of its siblings while that event
-  // was processing.
-  if (guard.Mutated(0)) {
-    aIndex = IndexOf(aKid);
-    if (static_cast<PRInt32>(aIndex) < 0) {
-      return NS_OK;
-    }
-  }
-
   nsIContent* previousSibling = aKid->GetPreviousSibling();
 
   if (GetFirstChild() == aKid) {
     mFirstChild = aKid->GetNextSibling();
   }
 
   aChildArray.RemoveChildAt(aIndex);
 
@@ -3980,46 +3993,85 @@ nsGenericElement::FireNodeInserted(nsIDo
                                    nsCOMArray<nsIContent>& aNodes)
 {
   PRInt32 count = aNodes.Count();
   for (PRInt32 i = 0; i < count; ++i) {
     nsIContent* childContent = aNodes[i];
 
     if (nsContentUtils::HasMutationListeners(childContent,
           NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
-      mozAutoRemovableBlockerRemover blockerRemover(aDoc);
-
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED);
       mutation.mRelatedNode = do_QueryInterface(aParent);
 
       mozAutoSubtreeModified subtree(aDoc, aParent);
-      nsEventDispatcher::Dispatch(childContent, nsnull, &mutation);
+      (new nsPLDOMEvent(childContent, mutation))->RunDOMEventWhenSafe();
     }
   }
 }
 
 nsresult
 nsINode::ReplaceOrInsertBefore(PRBool aReplace, nsINode* aNewChild,
                                nsINode* aRefChild)
 {
   if (!aNewChild || (aReplace && !aRefChild)) {
     return NS_ERROR_NULL_POINTER;
   }
 
-  if (!IsNodeOfType(eDOCUMENT) &&
-      !IsNodeOfType(eDOCUMENT_FRAGMENT) &&
-      !IsElement()) {
+  if ((!IsNodeOfType(eDOCUMENT) &&
+       !IsNodeOfType(eDOCUMENT_FRAGMENT) &&
+       !IsElement()) ||
+      !aNewChild->IsNodeOfType(eCONTENT)){
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
 
+  PRUint16 nodeType = 0;
+  nsresult res = aNewChild->GetNodeType(&nodeType);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // Before we do anything else, fire all DOMNodeRemoved mutation events
+  // We do this up front as to avoid having to deal with script running
+  // at random places further down.
+  // Scope firing mutation events so that we don't carry any state that
+  // might be stale
+  {
+    // This check happens again further down (though then using IndexOf).
+    // We're only checking this here to avoid firing mutation events when
+    // none should be fired.
+    // It's ok that we do the check twice in the case when firing mutation
+    // events as we need to recheck after running script anyway.
+    if (aRefChild && aRefChild->GetNodeParent() != this) {
+      return NS_ERROR_DOM_NOT_FOUND_ERR;
+    }
+
+    // If we're replacing, fire for node-to-be-replaced
+    if (aReplace) {
+      nsContentUtils::MaybeFireNodeRemoved(aRefChild, this, GetOwnerDoc());
+    }
+
+    // If the new node already has a parent, fire for removing from old
+    // parent
+    nsINode* oldParent = aNewChild->GetNodeParent();
+    if (oldParent) {
+      nsContentUtils::MaybeFireNodeRemoved(aNewChild, oldParent,
+                                           aNewChild->GetOwnerDoc());
+    }
+
+    // If we're inserting a fragment, fire for all the children of the
+    // fragment
+    if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+      static_cast<nsGenericElement*>(aNewChild)->FireNodeRemovedForChildren();
+    }
+  }
+
+  nsIDocument* doc = GetOwnerDoc();
+  nsIContent* newContent = static_cast<nsIContent*>(aNewChild);
   nsIContent* refContent;
-  nsresult res = NS_OK;
   PRInt32 insPos;
 
-  mozAutoDocConditionalContentUpdateBatch batch(GetCurrentDoc(), PR_TRUE);
+  mozAutoDocUpdate batch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, PR_TRUE);
 
   // Figure out which index to insert at
   if (aRefChild) {
     insPos = IndexOf(aRefChild);
     if (insPos < 0) {
       return NS_ERROR_DOM_NOT_FOUND_ERR;
     }
 
@@ -4032,66 +4084,71 @@ nsINode::ReplaceOrInsertBefore(PRBool aR
 
     refContent = static_cast<nsIContent*>(aRefChild);
   }
   else {
     insPos = GetChildCount();
     refContent = nsnull;
   }
 
-  if (!aNewChild->IsNodeOfType(eCONTENT)) {
-    return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-  }
-
-  nsIContent* newContent = static_cast<nsIContent*>(aNewChild);
-
-  PRUint16 nodeType = 0;
-  res = aNewChild->GetNodeType(&nodeType);
-  NS_ENSURE_SUCCESS(res, res);
-
   // Make sure that the inserted node is allowed as a child of its new parent.
   if (!IsAllowedAsChild(newContent, nodeType, this, aReplace, refContent)) {
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
 
+  // If we're replacing
+  if (aReplace) {
+    refContent = GetChildAt(insPos + 1);
+
+    res = RemoveChildAt(insPos, PR_TRUE);
+    NS_ENSURE_SUCCESS(res, res);
+  }
+
+  if (newContent->IsRootOfAnonymousSubtree()) {
+    // This is anonymous content.  Don't allow its insertion
+    // anywhere, since it might have UnbindFromTree calls coming
+    // its way.
+    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
+
+  // Remove the new child from the old parent if one exists
+  nsINode* oldParent = newContent->GetNodeParent();
+  if (oldParent) {
+    PRInt32 removeIndex = oldParent->IndexOf(newContent);
+    if (removeIndex < 0) {
+      // newContent is anonymous.  We can't deal with this, so just bail
+      NS_ERROR("How come our flags didn't catch this?");
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    }
+    
+    NS_ASSERTION(!(oldParent == this && removeIndex == insPos),
+                 "invalid removeIndex");
+
+    res = oldParent->RemoveChildAt(removeIndex, PR_TRUE);
+    NS_ENSURE_SUCCESS(res, res);
+
+    // Adjust insert index if the node we ripped out was a sibling
+    // of the node we're inserting before
+    if (oldParent == this && removeIndex < insPos) {
+      --insPos;
+    }
+  }
+
+  // Move new child over to our document if needed. Do this after removing
+  // it from its parent so that AdoptNode doesn't fire DOMNodeRemoved
   // DocumentType nodes are the only nodes that can have a null
   // ownerDocument according to the DOM spec, and we need to allow
   // inserting them w/o calling AdoptNode().
   if (!HasSameOwnerDoc(newContent) &&
       (nodeType != nsIDOMNode::DOCUMENT_TYPE_NODE ||
        newContent->GetOwnerDoc())) {
     res = AdoptNodeIntoOwnerDoc(this, aNewChild);
     NS_ENSURE_SUCCESS(res, res);
   }
 
-  // If we're replacing
-  if (aReplace) {
-    refContent = GetChildAt(insPos + 1);
-
-    nsMutationGuard guard;
-
-    res = RemoveChildAt(insPos, PR_TRUE);
-    NS_ENSURE_SUCCESS(res, res);
-
-    if (guard.Mutated(1)) {
-      insPos = refContent ? IndexOf(refContent) : GetChildCount();
-      if (insPos < 0) {
-        return NS_ERROR_DOM_NOT_FOUND_ERR;
-      }
-
-      // Passing PR_FALSE for aIsReplace since we now have removed the node
-      // to be replaced.
-      if (!IsAllowedAsChild(newContent, nodeType, this, PR_FALSE, refContent)) {
-        return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-      }
-    }
-  }
-
-  nsIDocument *doc = GetOwnerDoc();
-
   /*
    * Check if we're inserting a document fragment. If we are, we need
    * to remove the children of the document fragment and add them
    * individually (i.e. we don't add the actual document fragment).
    */
   if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
     PRUint32 count = newContent->GetChildCount();
 
@@ -4184,84 +4241,28 @@ nsINode::ReplaceOrInsertBefore(PRBool aR
 
     // Notify
     if (appending) {
       nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this),
                                    firstInsertedContent, firstInsPos);
     }
 
     // Fire mutation events. Optimize for the case when there are no listeners
-    nsPIDOMWindow* window = nsnull;
-    if (doc &&
-        (((window = doc->GetInnerWindow()) &&
-          window->HasMutationListeners(NS_EVENT_BITS_MUTATION_NODEINSERTED)) ||
-         !window)) {
+    if (nsContentUtils::
+          HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
       nsGenericElement::FireNodeInserted(doc, this, fragChildren);
     }
   }
   else {
     // Not inserting a fragment but rather a single node.
 
-    if (newContent->IsRootOfAnonymousSubtree()) {
-      // This is anonymous content.  Don't allow its insertion
-      // anywhere, since it might have UnbindFromTree calls coming
-      // its way.
-      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-    }
-
-    // Remove the element from the old parent if one exists
-    nsINode* oldParent = newContent->GetNodeParent();
-
-    if (oldParent) {
-      PRInt32 removeIndex = oldParent->IndexOf(newContent);
-
-      if (removeIndex < 0) {
-        // newContent is anonymous.  We can't deal with this, so just bail
-        NS_ERROR("How come our flags didn't catch this?");
-        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-      }
-      
-      NS_ASSERTION(!(oldParent == this && removeIndex == insPos),
-                   "invalid removeIndex");
-
-      nsMutationGuard guard;
-
-      res = oldParent->RemoveChildAt(removeIndex, PR_TRUE);
-      NS_ENSURE_SUCCESS(res, res);
-
-      // Adjust insert index if the node we ripped out was a sibling
-      // of the node we're inserting before
-      if (oldParent == this && removeIndex < insPos) {
-        --insPos;
-      }
-
-      if (guard.Mutated(1)) {
-        if (doc != newContent->GetOwnerDoc()) {
-          return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
-        }
-
-        insPos = refContent ? IndexOf(refContent) : GetChildCount();
-        if (insPos < 0) {
-          // Someone seriously messed up the childlist. We have no idea
-          // where to insert the new child, so just bail.
-          return NS_ERROR_DOM_NOT_FOUND_ERR;
-        }
-
-        if (newContent->GetNodeParent() ||
-            !IsAllowedAsChild(newContent, nodeType, this, PR_FALSE,
-                              refContent)) {
-          return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-        }
-      }
-    }
-
     // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654
     //       We need to reparent here for nodes for which the parent of their
     //       wrapper is not the wrapper for their ownerDocument (XUL elements,
-    //       form controls, ...).
+    //       form controls, ...). Also applies in the fragment code above.
 
     res = InsertChildAt(newContent, insPos, PR_TRUE);
     NS_ENSURE_SUCCESS(res, res);
   }
 
   return res;
 }
 
@@ -4746,18 +4747,16 @@ nsGenericElement::SetAttrAndNotify(PRInt
     mNodeInfo->GetDocument()->AddXMLEventsContent(this);
   }
   if (aValueForAfterSetAttr) {
     rv = AfterSetAttr(aNamespaceID, aName, aValueForAfterSetAttr, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aFireMutation) {
-    mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-    
     nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED);
 
     nsCOMPtr<nsIDOMAttr> attrNode;
     nsAutoString ns;
     nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
     GetAttributeNodeNS(ns, nsDependentAtomString(aName),
                        getter_AddRefs(attrNode));
     mutation.mRelatedNode = attrNode;
@@ -4769,17 +4768,17 @@ nsGenericElement::SetAttrAndNotify(PRInt
       mutation.mNewAttrValue = do_GetAtom(newValue);
     }
     if (!aOldValue.IsEmpty()) {
       mutation.mPrevAttrValue = do_GetAtom(aOldValue);
     }
     mutation.mAttrChange = aModType;
 
     mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-    nsEventDispatcher::Dispatch(this, nsnull, &mutation);
+    (new nsPLDOMEvent(this, mutation))->RunDOMEventWhenSafe();
   }
 
   return NS_OK;
 }
 
 PRBool
 nsGenericElement::ParseAttribute(PRInt32 aNamespaceID,
                                  nsIAtom* aAttribute,
@@ -4988,33 +4987,31 @@ nsGenericElement::UnsetAttr(PRInt32 aNam
     nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                   nsIDOMMutationEvent::REMOVAL);
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nsnull, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (hasMutationListeners) {
-    mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-
     nsCOMPtr<nsIDOMEventTarget> node =
       do_QueryInterface(static_cast<nsIContent *>(this));
     nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED);
 
     mutation.mRelatedNode = attrNode;
     mutation.mAttrName = aName;
 
     nsAutoString value;
     oldValue.ToString(value);
     if (!value.IsEmpty())
       mutation.mPrevAttrValue = do_GetAtom(value);
     mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
 
     mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-    nsEventDispatcher::Dispatch(this, nsnull, &mutation);
+    (new nsPLDOMEvent(this, mutation))->RunDOMEventWhenSafe();
   }
 
   return NS_OK;
 }
 
 const nsAttrName*
 nsGenericElement::GetAttrNameAt(PRUint32 aIndex) const
 {
@@ -5435,16 +5432,36 @@ nsGenericElement::PostHandleEventForLink
     NS_NOTREACHED("switch statements not in sync");
     return NS_ERROR_UNEXPECTED;
   }
 
   return rv;
 }
 
 void
+nsGenericElement::FireNodeRemovedForChildren()
+{
+  nsIDocument* doc = GetOwnerDoc();
+  // Optimize the common case
+  if (!nsContentUtils::
+        HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
+    return;
+  }
+
+  nsCOMPtr<nsIDocument> owningDoc = doc;
+
+  nsCOMPtr<nsINode> child;
+  for (child = GetFirstChild();
+       child && child->GetNodeParent() == this;
+       child = child->GetNextSibling()) {
+    nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
+  }
+}
+
+void
 nsGenericElement::GetLinkTarget(nsAString& aTarget)
 {
   aTarget.Truncate();
 }
 
 // NOTE: The aPresContext pointer is NOT addrefed.
 // *aSelectorList might be null even if NS_OK is returned; this
 // happens when all the selectors were pseudo-element selectors.
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -345,18 +345,16 @@ public:
     return nsContentUtils::GetContextForEventHandlers(this, aRv);
   }
   virtual void GetTextContent(nsAString &aTextContent)
   {
     nsContentUtils::GetNodeTextContent(this, PR_TRUE, aTextContent);
   }
   virtual nsresult SetTextContent(const nsAString& aTextContent)
   {
-    // Batch possible DOMSubtreeModified events.
-    mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
     return nsContentUtils::SetNodeTextContent(this, aTextContent, PR_FALSE);
   }
 
   // nsIContent interface methods
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
@@ -764,16 +762,21 @@ public:
   virtual nsAttrInfo GetAttrInfo(PRInt32 aNamespaceID, nsIAtom* aName) const;
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericElement)
 
   virtual void NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
   {
   }
 
+  /**
+   * Fire a DOMNodeRemoved mutation event for all children of this node
+   */
+  void FireNodeRemovedForChildren();
+
 protected:
   /**
    * Set attribute and (if needed) notify documentobservers and fire off
    * mutation events.  This will send the AttributeChanged notification.
    * Callers of this method are responsible for calling AttributeWillChange,
    * since that needs to happen before the new attr value has been set, and
    * in particular before it has been parsed.
    *
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -258,16 +258,17 @@ GK_ATOM(count, "count")
 GK_ATOM(crop, "crop")
 GK_ATOM(curpos, "curpos")
 GK_ATOM(current, "current")
 GK_ATOM(currentloop, "currentloop")
 GK_ATOM(cycler, "cycler")
 GK_ATOM(data, "data")
 GK_ATOM(datalist, "datalist")
 GK_ATOM(dataType, "data-type")
+GK_ATOM(dateTime, "date-time")
 GK_ATOM(datasources, "datasources")
 GK_ATOM(datetime, "datetime")
 GK_ATOM(dblclick, "dblclick")
 GK_ATOM(dd, "dd")
 GK_ATOM(debug, "debug")
 GK_ATOM(decimalFormat, "decimal-format")
 GK_ATOM(decimalSeparator, "decimal-separator")
 GK_ATOM(deck, "deck")
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -359,16 +359,26 @@ nsresult
 nsNodeUtils::CallUserDataHandlers(nsCOMArray<nsINode> &aNodesWithProperties,
                                   nsIDocument *aOwnerDocument,
                                   PRUint16 aOperation, PRBool aCloned)
 {
   NS_PRECONDITION(!aCloned || (aNodesWithProperties.Count() % 2 == 0),
                   "Expected aNodesWithProperties to contain original and "
                   "cloned nodes.");
 
+  if (!nsContentUtils::IsSafeToRunScript()) {
+    if (nsContentUtils::IsChromeDoc(aOwnerDocument)) {
+      NS_WARNING("Fix the caller! Userdata callback disabled.");
+    } else {
+      NS_ERROR("This is unsafe! Fix the caller! Userdata callback disabled.");
+    }
+
+    return NS_OK;
+  }
+
   nsPropertyTable *table = aOwnerDocument->PropertyTable(DOM_USER_DATA_HANDLER);
 
   // Keep the document alive, just in case one of the handlers causes it to go
   // away.
   nsCOMPtr<nsIDocument> ownerDoc = aOwnerDocument;
 
   nsHandlerData handlerData;
   handlerData.mOperation = aOperation;
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -1167,17 +1167,17 @@ CollapseRangeAfterDelete(nsIDOMRange *aR
  *
  * @param aNode The node to remove.
  */
 static nsresult
 RemoveNode(nsIDOMNode* aNode)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   nsCOMPtr<nsINode> parent = node->GetNodeParent();
-  return parent ? parent->RemoveChildAt(parent->IndexOf(node), PR_TRUE) : NS_OK;
+  return parent ? parent->RemoveChild(node) : NS_OK;
 }
 
 /**
  * Split a data node into two parts.
  *
  * @param aStartNode          The original node we are trying to split.
  * @param aStartIndex         The index at which to split.
  * @param aEndNode            The second node.
--- a/content/base/test/test_bug548463.html
+++ b/content/base/test/test_bug548463.html
@@ -43,17 +43,17 @@ function testAdoptFromDOMNodeRemoved(nod
   var thrown = false;
   try {
     document.getElementById("otherContent").appendChild(nodeToAppend);
   }
   catch (e) {
     thrown = true;
   }
 
-  ok(thrown, "adoptNode while appending should throw");
+  ok(!thrown, "adoptNode while appending should not throw");
 }
 
 var frag = document.createDocumentFragment();
 frag.appendChild(elem1);
 frag.appendChild(elem2);
 testAdoptFromDOMNodeRemoved(frag, elem1, elem2);
 
 content.appendChild(elem1);
@@ -71,14 +71,14 @@ function changeOwnerDocument()
 elem1.setUserData("foo", "bar", changeOwnerDocument);
 try {
   document.adoptNode(elem1);
 }
 catch (e) {
   thrown = true;
 }
 
-ok(thrown, "adoptNode while adopting should throw");
+ok(!thrown, "adoptNode while adopting should not throw");
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/base/test/test_bug626262.html
+++ b/content/base/test/test_bug626262.html
@@ -39,17 +39,17 @@ addLoadEvent(function() {
   try {
     window.addEventListener("DOMNodeRemoved", a, false);
     parent.appendChild(text);
   }
   catch (e) {
     thrown = true;
   }
 
-  ok(thrown, "changing ownerDocument during adoptNode should throw");
+  ok(!thrown, "changing ownerDocument during adoptNode should not throw");
 
   SimpleTest.finish();
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/events/public/nsPLDOMEvent.h
+++ b/content/events/public/nsPLDOMEvent.h
@@ -64,19 +64,21 @@ public:
       mBubbles(aBubbles),
       mDispatchChromeOnly(aDispatchChromeOnly)
   { }
 
   nsPLDOMEvent(nsINode *aEventNode, nsIDOMEvent *aEvent)
     : mEventNode(aEventNode), mEvent(aEvent), mDispatchChromeOnly(PR_FALSE)
   { }
 
+  nsPLDOMEvent(nsINode *aEventNode, nsEvent &aEvent);
+
   NS_IMETHOD Run();
   nsresult PostDOMEvent();
-  nsresult RunDOMEventWhenSafe();
+  void RunDOMEventWhenSafe();
 
   nsCOMPtr<nsINode>     mEventNode;
   nsCOMPtr<nsIDOMEvent> mEvent;
   nsString              mEventType;
   PRPackedBool          mBubbles;
   PRPackedBool          mDispatchChromeOnly;
 };
 
--- a/content/events/src/nsPLDOMEvent.cpp
+++ b/content/events/src/nsPLDOMEvent.cpp
@@ -37,16 +37,31 @@
 
 #include "nsPLDOMEvent.h"
 #include "nsIDOMEvent.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsContentUtils.h"
+#include "nsEventDispatcher.h"
+#include "nsGUIEvent.h"
+
+nsPLDOMEvent::nsPLDOMEvent(nsINode *aEventNode, nsEvent &aEvent)
+  : mEventNode(aEventNode), mDispatchChromeOnly(PR_FALSE)
+{
+  PRBool trusted = NS_IS_TRUSTED_EVENT(&aEvent);
+  nsEventDispatcher::CreateEvent(nsnull, &aEvent, EmptyString(),
+                                 getter_AddRefs(mEvent));
+  NS_ASSERTION(mEvent, "Should never fail to create an event");
+  nsCOMPtr<nsIPrivateDOMEvent> priv = do_QueryInterface(mEvent);
+  NS_ASSERTION(priv, "Should also not fail to QI to nsIDOMEventPrivate");
+  priv->DuplicatePrivateData();
+  priv->SetTrusted(trusted);
+}
 
 NS_IMETHODIMP nsPLDOMEvent::Run()
 {
   if (!mEventNode) {
     return NS_OK;
   }
 
   if (mEvent) {
@@ -70,19 +85,19 @@ NS_IMETHODIMP nsPLDOMEvent::Run()
   return NS_OK;
 }
 
 nsresult nsPLDOMEvent::PostDOMEvent()
 {
   return NS_DispatchToCurrentThread(this);
 }
 
-nsresult nsPLDOMEvent::RunDOMEventWhenSafe()
+void nsPLDOMEvent::RunDOMEventWhenSafe()
 {
-  return nsContentUtils::AddScriptRunner(this) ? NS_OK : NS_ERROR_FAILURE;
+  nsContentUtils::AddScriptRunner(this);
 }
 
 nsLoadBlockingPLDOMEvent::~nsLoadBlockingPLDOMEvent()
 {
   if (mBlockedDoc) {
     mBlockedDoc->UnblockOnload(PR_TRUE);
   }
 }
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -95,16 +95,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug586961.xul \
 		test_clickevent_on_input.html \
 		test_bug593959.html \
 		test_bug591815.html \
 		test_bug605242.html \
 		test_bug613634.html \
 		test_bug607464.html \
 		test_bug624127.html \
+		test_bug650493.html \
 		test_bug641477.html \
 		test_bug648573.html \
 		test_bug615597.html \
 		$(NULL)
 
 #bug 585630
 ifneq (mobile,$(MOZ_BUILD_APP))
 _TEST_FILES += \
--- a/content/events/test/test_bug288392.html
+++ b/content/events/test/test_bug288392.html
@@ -15,72 +15,80 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 <div id="mutationTarget">
 </div>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 288392 **/
-var subtreeModifiedCount = 0;
+var subtreeModifiedCount;
 
 function subtreeModified(e)
 {
   ++subtreeModifiedCount;
 }
 
 function doTest() {
   var targetNode = document.getElementById("mutationTarget");
   targetNode.addEventListener("DOMSubtreeModified", subtreeModified, false);
 
+  subtreeModifiedCount = 0;
   var temp = document.createElement("DIV");
   targetNode.appendChild(temp);
-  ok(subtreeModifiedCount == 1,
+  is(subtreeModifiedCount, 1,
      "Appending a child node should have dispatched a DOMSubtreeModified event");
 
+  subtreeModifiedCount = 0;
   temp.setAttribute("foo", "bar");
-  ok(subtreeModifiedCount == 2,
+  is(subtreeModifiedCount, 1,
      "Setting an attribute should have dispatched a DOMSubtreeModified event");
 
+  subtreeModifiedCount = 0;
   targetNode.removeChild(temp);
-  ok(subtreeModifiedCount == 3,
+  is(subtreeModifiedCount, 1,
      "Removing a child node should have dispatched a DOMSubtreeModified event");
 
   // Testing events in a subtree, which is not in the document.
   var subtree = document.createElement("div");
   var s = "<e1 attr1='value1'>Something1</e1><e2 attr2='value2'>Something2</e2>";
-
   subtree.innerHTML = s;
   subtree.addEventListener("DOMSubtreeModified", subtreeModified, false);
 
+  subtreeModifiedCount = 0;
   subtree.firstChild.firstChild.data = "foo";
-  ok(subtreeModifiedCount == 4,
+  is(subtreeModifiedCount, 1,
      "Editing character data should have dispatched a DOMSubtreeModified event");
 
+  subtreeModifiedCount = 0;
   subtree.firstChild.removeChild(subtree.firstChild.firstChild);
-  ok(subtreeModifiedCount == 5,
+  is(subtreeModifiedCount, 1,
      "Removing a child node should have dispatched a DOMSubtreeModified event");
 
+  subtreeModifiedCount = 0;
   subtree.firstChild.setAttribute("foo", "bar");
-  ok(subtreeModifiedCount == 6,
+  is(subtreeModifiedCount, 1,
      "Setting an attribute should have dispatched a DOMSubtreeModified event");
 
+  subtreeModifiedCount = 0;
   subtree.textContent = "foobar";
-  ok(subtreeModifiedCount == 7,
+  is(subtreeModifiedCount, 1,
      "Setting .textContent should have dispatched a DOMSubtreeModified event");
 
+  subtreeModifiedCount = 0;
   subtree.innerHTML = s;
-  ok(subtreeModifiedCount == 8,
+  is(subtreeModifiedCount, 1,
      "Setting .innerHTML should have dispatched a DOMSubtreeModified event");
 
+  subtreeModifiedCount = 0;
   subtree.removeEventListener("DOMSubtreeModified", subtreeModified, false);
   subtree.appendChild(document.createTextNode(""));
   subtree.addEventListener("DOMSubtreeModified", subtreeModified, false);
   subtree.normalize();
-  ok(subtreeModifiedCount == 9,
+  is(subtreeModifiedCount, 1,
      "Calling normalize() should have dispatched a DOMSubtreeModified event");
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(doTest);
 addLoadEvent(SimpleTest.finish);
 
 </script>
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug650493.html
@@ -0,0 +1,188 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=650493
+-->
+<head>
+  <title>Test for Bug 650493</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650493">Mozilla Bug 650493</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function getNodes() {
+  var walker = document.createTreeWalker($('content'), NodeFilter.SHOW_ALL, null, false);
+  var nodes = [];
+  do {
+    nodes.push(walker.currentNode);
+  } while(walker.nextNode());
+
+  return nodes;
+}
+
+function check() {
+  var current = getNodes();
+  is(nodes.length, current.length, "length after " + testName);
+  nodes.forEach(function(val, index) {
+    ok(current.indexOf(val) > -1, "nodes[" + index + "] (" + val + ") shouldn't exist after " + testName);
+  });
+}
+
+var nodes;
+var testName;
+
+nodes = getNodes();
+check("empty");
+
+// Set up listeners
+root = $('content');
+root.addEventListener("DOMNodeInserted", function(e) {
+  is(e.isTrusted, true, "untrusted mutation event");
+  var w = document.createTreeWalker(e.target, NodeFilter.SHOW_ALL, null, false);
+  do {
+    is(nodes.indexOf(w.currentNode), -1, "already have inserted node (" + w.currentNode + ") when " + testName);
+    nodes.push(w.currentNode);
+  } while(w.nextNode());
+}, false);
+root.addEventListener("DOMNodeRemoved", function(e) {
+  is(e.isTrusted, true, "untrusted mutation event");
+  var w = document.createTreeWalker(e.target, NodeFilter.SHOW_ALL, null, false);
+  do {
+    var index = nodes.indexOf(w.currentNode);
+    ok(index != -1, "missing removed node (" + w.currentNode + ") when " + testName);
+    nodes.splice(index, 1);
+  } while(w.nextNode());
+}, false);
+
+testName = "text-only innerHTML";
+root.innerHTML = "hello world";
+check();
+
+testName = "innerHTML with <b>";
+root.innerHTML = "<b>bold</b> world";
+check();
+
+testName = "complex innerHTML";
+root.innerHTML = "<b>b<span>old</span></b> <strong>world";
+check();
+
+testName = "replacing using .textContent";
+root.textContent = "i'm just a plain text minding my own business";
+check();
+
+testName = "clearing using .textContent";
+root.textContent = "";
+check();
+
+testName = "inserting using .textContent";
+root.textContent = "i'm new text!!";
+check();
+
+testName = "inserting using .textContent";
+root.textContent = "i'm new text!!";
+check();
+
+testName = "preparing to normalize";
+root.innerHTML = "<u><b>foo</b></u>  ";
+var u = root.firstChild;
+is(u.nodeName, "U", "got the right node");
+var b = u.firstChild;
+is(b.nodeName, "B", "got the right node");
+b.insertBefore(document.createTextNode(""), b.firstChild);
+b.insertBefore(document.createTextNode(""), b.firstChild);
+b.appendChild(document.createTextNode(""));
+b.appendChild(document.createTextNode("hello"));
+b.appendChild(document.createTextNode("world"));
+u.appendChild(document.createTextNode("foo"));
+u.appendChild(document.createTextNode(""));
+u.appendChild(document.createTextNode("bar"));
+check();
+
+testName = "normalizing";
+root.normalize();
+check();
+
+testName = "prepare script/style";
+script = document.createElement("script");
+script.appendChild(document.createTextNode("void(0);"));
+root.appendChild(script);
+style = document.createElement("style");
+root.appendChild(style);
+check();
+
+testName = "set something in script";
+script.text = "something";
+check();
+
+testName = "set something in style";
+style.innerHTML = "something { dislay: none; }";
+check();
+
+testName = "moving style";
+root.insertBefore(style, root.firstChild);
+check();
+
+testName = "replacing script";
+root.replaceChild(b, script);
+check();
+
+testName = "doc-fragment insert in the middle";
+frag = document.createDocumentFragment();
+frag.addEventListener("DOMNodeRemoved", function(e) {
+  var index = children.indexOf(e.target);
+  ok(index != -1, "unknown child removed from fragment");
+  children.splice(index, 1);
+}, false);
+var children = [];
+children.push(document.createTextNode("foo"));
+children.push(document.createTextNode("bar"));
+children.push(document.createElement("span"));
+children.push(document.createElement("b"));
+children[2].appendChild(document.createElement("i"));
+children.forEach(function(child) { frag.appendChild(child); });
+ok(root.firstChild, "need to have children in order to test inserting before end");
+root.replaceChild(frag, root.firstChild);
+check();
+is(children.length, 0, "should have received DOMNodeRemoved for all frag children when inserting");
+is(frag.childNodes.length, 0, "fragment should be empty when inserting");
+
+testName = "doc-fragment append at the end";
+children.push(document.createTextNode("foo"));
+children.push(document.createTextNode("bar"));
+children.push(document.createElement("span"));
+children.push(document.createElement("b"));
+children[2].appendChild(document.createElement("i"));
+children.forEach(function(child) { frag.appendChild(child); });
+root.appendChild(frag);
+check();
+is(children.length, 0, "should have received DOMNodeRemoved for all frag children when appending");
+is(frag.childNodes.length, 0, "fragment should be empty when appending");
+
+testName = "prepare replaceWholeText";
+var textNode;
+root.appendChild(document.createTextNode("hello"));
+root.appendChild(document.createTextNode(""));
+root.appendChild(textNode = document.createTextNode("world"));
+root.appendChild(document.createTextNode("fahrvergnugen"));
+root.appendChild(document.createTextNode(""));
+root.appendChild(document.createElement("div"));
+root.appendChild(document.createTextNode("smorgasbord"));
+root.appendChild(document.createTextNode(""));
+check();
+
+testName = "run replaceWholeText";
+textNode.replaceWholeText("supercalifragilisticexpialidocious");
+check();
+
+</script>
+</body>
+</html>
+
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -697,25 +697,30 @@ nsGenericHTMLElement::GetInnerHTML(nsASt
 nsresult
 nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
 {
   nsIDocument* doc = GetOwnerDoc();
   NS_ENSURE_STATE(doc);
 
   nsresult rv = NS_OK;
 
+  // Batch possible DOMSubtreeModified events.
+  mozAutoSubtreeModified subtree(doc, nsnull);
+
+  FireNodeRemovedForChildren();
+
   // This BeginUpdate/EndUpdate pair is important to make us reenable the
   // scriptloader before the last EndUpdate call.
   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, PR_TRUE);
 
-  // Batch possible DOMSubtreeModified events.
-  mozAutoSubtreeModified subtree(doc, nsnull);
-
-  // Remove childnodes
-  nsContentUtils::SetNodeTextContent(this, EmptyString(), PR_FALSE);
+  // Remove childnodes.
+  // i is unsigned, so i >= is always true
+  for (PRUint32 i = GetChildCount(); i-- != 0; ) {
+    RemoveChildAt(i, PR_TRUE);
+  }
 
   nsCOMPtr<nsIDOMDocumentFragment> df;
 
   // Strong ref since appendChild can fire events
   nsRefPtr<nsScriptLoader> loader = doc->ScriptLoader();
   PRBool scripts_enabled = loader->GetEnabled();
   loader->SetEnabled(PR_FALSE);
 
@@ -737,22 +742,19 @@ nsGenericHTMLElement::SetInnerHTML(const
                                          GetNameSpaceID(),
                                          doc->GetCompatibilityMode() ==
                                              eCompatibility_NavQuirks,
                                          PR_TRUE);
     doc->SetFragmentParser(parser);
 
     // HTML5 parser has notified, but not fired mutation events.
     // Fire mutation events. Optimize for the case when there are no listeners
-    nsPIDOMWindow* window = nsnull;
     PRInt32 newChildCount = GetChildCount();
-    if (newChildCount &&
-        (((window = doc->GetInnerWindow()) &&
-          window->HasMutationListeners(NS_EVENT_BITS_MUTATION_NODEINSERTED)) ||
-         !window)) {
+    if (newChildCount && nsContentUtils::
+          HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
       nsCOMArray<nsIContent> childNodes;
       NS_ASSERTION(newChildCount - oldChildCount >= 0,
                    "What, some unexpected dom mutation has happened?");
       childNodes.SetCapacity(newChildCount - oldChildCount);
       for (nsINode::ChildIterator iter(this); !iter.IsDone(); iter.Next()) {
         childNodes.AppendObject(iter);
       }
       nsGenericElement::FireNodeInserted(doc, this, childNodes);
--- a/content/xslt/src/xslt/txEXSLTFunctions.cpp
+++ b/content/xslt/src/xslt/txEXSLTFunctions.cpp
@@ -41,16 +41,17 @@
 #include "txAtoms.h"
 #include "txExecutionState.h"
 #include "txExpr.h"
 #include "txIXPathContext.h"
 #include "txNodeSet.h"
 #include "txOutputFormat.h"
 #include "txRtfHandler.h"
 #include "txXPathTreeWalker.h"
+#include "nsPrintfCString.h"
 
 #ifndef TX_EXE
 #include "nsComponentManagerUtils.h"
 #include "nsContentCID.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIContent.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsIDOMText.h"
@@ -220,16 +221,17 @@ struct txEXSLTFunctionDescriptor
     PRInt32 mNamespaceID;
     const char* mNamespaceURI;
 };
 
 static const char kEXSLTCommonNS[] = "http://exslt.org/common";
 static const char kEXSLTSetsNS[] = "http://exslt.org/sets";
 static const char kEXSLTStringsNS[] = "http://exslt.org/strings";
 static const char kEXSLTMathNS[] = "http://exslt.org/math";
+static const char kEXSLTDatesAndTimesNS[] = "http://exslt.org/dates-and-times";
 
 // The order of this table must be the same as the
 // txEXSLTFunctionCall::eType enum
 static txEXSLTFunctionDescriptor descriptTable[] =
 {
     { 1, 1, Expr::NODESET_RESULT, &txXSLTAtoms::nodeSet, 0, kEXSLTCommonNS }, // NODE_SET
     { 1, 1, Expr::STRING_RESULT,  &txXSLTAtoms::objectType, 0, kEXSLTCommonNS }, // OBJECT_TYPE
     { 2, 2, Expr::NODESET_RESULT, &txXSLTAtoms::difference, 0, kEXSLTSetsNS }, // DIFFERENCE
@@ -240,16 +242,17 @@ static txEXSLTFunctionDescriptor descrip
     { 2, 2, Expr::NODESET_RESULT, &txXSLTAtoms::trailing, 0, kEXSLTSetsNS }, // TRAILING
     { 1, 1, Expr::STRING_RESULT,  &txXSLTAtoms::concat, 0, kEXSLTStringsNS }, // CONCAT
     { 1, 2, Expr::STRING_RESULT,  &txXSLTAtoms::split, 0, kEXSLTStringsNS }, // SPLIT
     { 1, 2, Expr::STRING_RESULT,  &txXSLTAtoms::tokenize, 0, kEXSLTStringsNS }, // TOKENIZE
     { 1, 1, Expr::NUMBER_RESULT,  &txXSLTAtoms::max, 0, kEXSLTMathNS }, // MAX
     { 1, 1, Expr::NUMBER_RESULT,  &txXSLTAtoms::min, 0, kEXSLTMathNS }, // MIN
     { 1, 1, Expr::NODESET_RESULT, &txXSLTAtoms::highest, 0, kEXSLTMathNS }, // HIGHEST
     { 1, 1, Expr::NODESET_RESULT, &txXSLTAtoms::lowest, 0, kEXSLTMathNS }, // LOWEST
+    { 0, 0, Expr::STRING_RESULT,  &txXSLTAtoms::dateTime, 0, kEXSLTDatesAndTimesNS }, // DATE_TIME
 
 };
 
 class txEXSLTFunctionCall : public FunctionCall
 {
 public:
     // The order of this enum must be the same as the descriptTable
     // table above
@@ -264,17 +267,18 @@ public:
         LEADING,
         TRAILING,
         CONCAT,
         SPLIT,
         TOKENIZE,
         MAX,
         MIN,
         HIGHEST,
-        LOWEST
+        LOWEST,
+        DATE_TIME
     };
     
     txEXSLTFunctionCall(eType aType)
       : mType(aType)
     {
     }
 
     TX_DECL_FUNCTION
@@ -682,16 +686,46 @@ txEXSLTFunctionCall::evaluate(txIEvalCon
                     NS_ENSURE_SUCCESS(rv, rv);
                 }
             }
 
             NS_ADDREF(*aResult = resultSet);
 
             return NS_OK;
         }
+        case DATE_TIME:
+        {
+            // http://exslt.org/date/functions/date-time/
+            // format: YYYY-MM-DDTTHH:MM:SS.sss+00:00
+            char formatstr[] = "%04hd-%02ld-%02ldT%02ld:%02ld:%02ld.%03ld%c%02ld:%02ld";
+            const size_t max = sizeof("YYYY-MM-DDTHH:MM:SS.sss+00:00");
+            
+            PRExplodedTime prtime;
+            PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prtime);
+            
+            PRInt32 offset = (prtime.tm_params.tp_gmt_offset +
+              prtime.tm_params.tp_dst_offset) / 60;
+              
+            PRBool isneg = offset < 0;
+            if (isneg) offset = -offset;
+            
+            StringResult* strRes;
+            rv = aContext->recycler()->getStringResult(&strRes);
+            NS_ENSURE_SUCCESS(rv, rv);
+            
+            CopyASCIItoUTF16(nsPrintfCString(max, formatstr,
+              prtime.tm_year, prtime.tm_month + 1, prtime.tm_mday,
+              prtime.tm_hour, prtime.tm_min, prtime.tm_sec,
+              prtime.tm_usec / 10000,
+              isneg ? '-' : '+', offset / 60, offset % 60), strRes->mValue);
+              
+            *aResult = strRes;
+
+            return NS_OK;
+        }
     }
 
     aContext->receiveError(NS_LITERAL_STRING("Internal error"),
                            NS_ERROR_UNEXPECTED);
     return NS_ERROR_UNEXPECTED;
 }
 
 Expr::ResultType
--- a/content/xslt/src/xslt/txStylesheetCompiler.cpp
+++ b/content/xslt/src/xslt/txStylesheetCompiler.cpp
@@ -992,16 +992,18 @@ static txFunctionFactoryMapping kExtensi
     { "", kNameSpaceID_Unknown, TX_ConstructXSLTFunction },
     { "http://exslt.org/common", kNameSpaceID_Unknown,
       TX_ConstructEXSLTFunction },
     { "http://exslt.org/sets", kNameSpaceID_Unknown,
       TX_ConstructEXSLTFunction },
     { "http://exslt.org/strings", kNameSpaceID_Unknown,
       TX_ConstructEXSLTFunction },
     { "http://exslt.org/math", kNameSpaceID_Unknown,
+      TX_ConstructEXSLTFunction },
+    { "http://exslt.org/dates-and-times", kNameSpaceID_Unknown,
       TX_ConstructEXSLTFunction }
 };
 
 extern nsresult
 TX_ResolveFunctionCallXPCOM(const nsCString &aContractID, PRInt32 aNamespaceID,
                             nsIAtom *aName, nsISupports *aState,
                             FunctionCall **aFunction);
 
--- a/content/xslt/tests/mochitest/Makefile.in
+++ b/content/xslt/tests/mochitest/Makefile.in
@@ -49,13 +49,14 @@ include $(topsrcdir)/config/rules.mk
 		test_bug427060.html \
 		test_bug468208.html \
 		test_bug453441.html \
 		test_bug511487.html \
 		test_bug551412.html \
 		test_bug551654.html \
 		test_bug566629.html \
 		test_bug566629.xhtml \
+		test_bug603159.html \
 		test_exslt_regex.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/xslt/tests/mochitest/test_bug603159.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=603159
+-->
+<head>
+  <title>Test for Bug 603159</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=603159">Mozilla Bug 603159</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 603159 **/
+
+  var style =
+    '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ' +
+                    'xmlns:date="http://exslt.org/dates-and-times" '+
+                    'version="1.0">' +
+      '<xsl:output method="html"/>' +
+      '<xsl:template match="/">' +
+        '<xsl:value-of select="date:date-time()" /> ' +
+      '</xsl:template>' +
+    '</xsl:stylesheet>';
+  var styleDoc = new DOMParser().parseFromString (style, "text/xml");
+
+  var data = '<root/>';
+  var originalDoc = new DOMParser().parseFromString(data, "text/xml");
+
+  var processor = new XSLTProcessor();
+  processor.importStylesheet(styleDoc);
+
+  var fragment = processor.transformToFragment(originalDoc, document);
+  var content = document.getElementById("content");
+  content.appendChild(fragment);
+  
+  // use Gecko's Date.parse to parse, then compare the milliseconds since epoch
+  var xslt_ms = Date.parse(content.innerHTML);
+  var now_ms = new Date().getTime();
+  var accepted_diff = 30 * 60 * 1000; // 30 minutes
+  var diff = Math.abs(now_ms - xslt_ms);
+  
+  ok(diff < accepted_diff, "generated timestamp should be not more than "
+    + accepted_diff + " ms before 'now', but the difference was: " + diff);
+  
+  content.innerHTML = '';
+</script>
+</pre>
+</body>
+</html>
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -119,16 +119,17 @@
 #include "nsXULPopupListener.h"
 #include "nsRuleWalker.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsCSSParser.h"
 #include "nsIListBoxObject.h"
 #include "nsContentUtils.h"
 #include "nsContentList.h"
 #include "nsMutationEvent.h"
+#include "nsPLDOMEvent.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsPIDOMWindow.h"
 #include "nsDOMAttributeMap.h"
 #include "nsGkAtoms.h"
 #include "nsXULContentUtils.h"
 #include "nsNodeUtils.h"
 #include "nsFrameLoader.h"
 #include "prlog.h"
@@ -1473,30 +1474,27 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpa
             MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, aNotify);
             doc->ContentStateChanged(this, stateMask);
         }
         nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                       nsIDOMMutationEvent::REMOVAL);
     }
 
     if (hasMutationListeners) {
-        mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-
         nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED);
 
         mutation.mRelatedNode = attrNode;
         mutation.mAttrName = aName;
 
         if (!oldValue.IsEmpty())
           mutation.mPrevAttrValue = do_GetAtom(oldValue);
         mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
 
         mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-        nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this),
-                                    nsnull, &mutation);
+        (new nsPLDOMEvent(this, mutation))->RunDOMEventWhenSafe();
     }
 
     return NS_OK;
 }
 
 void
 nsXULElement::RemoveBroadcaster(const nsAString & broadcasterId)
 {
--- a/docshell/base/Makefile.in
+++ b/docshell/base/Makefile.in
@@ -114,9 +114,10 @@ CPPSRCS = \
 # static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES += \
   -I$(srcdir)/../shistory/src \
   -I$(topsrcdir)/layout/base \
+  -I$(topsrcdir)/js/src/xpconnect/src \
   $(NULL)
--- a/docshell/base/crashtests/crashtests.list
+++ b/docshell/base/crashtests/crashtests.list
@@ -1,11 +1,11 @@
 load 40929-1.html
 load 369126-1.html
 load 403574-1.xhtml
 load 430124-1.html
 load 430628-1.html
-asserts(1-4) load 432114-1.html # bug 570215
-asserts(1) load 432114-2.html # bug 570215
+load 432114-1.html
+load 432114-2.html
 load 436900-1.html
 asserts(0-2) load 436900-2.html # bug 566159
 load 500328-1.html
 load 514779-1.xhtml
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -155,16 +155,18 @@
 #include "nsIPermissionManager.h"
 #include "nsStreamUtils.h"
 #include "nsIController.h"
 #include "nsPICommandUpdater.h"
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIWebBrowserChrome3.h"
 #include "nsITabChild.h"
 #include "nsIStrictTransportSecurityService.h"
+#include "nsStructuredCloneContainer.h"
+#include "nsIStructuredCloneContainer.h"
 
 // Editor-related
 #include "nsIEditingSession.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsIDOMDocument.h"
 #include "nsICachingChannel.h"
@@ -7736,31 +7738,27 @@ nsDocShell::SetupNewViewer(nsIContentVie
     // viewer still set to hidden.
 
     return NS_OK;
 }
 
 nsresult
 nsDocShell::SetDocCurrentStateObj(nsISHEntry *shEntry)
 {
-    nsresult rv;
-
     nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
     NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
 
-    nsAutoString stateData;
-    if (shEntry) {
-        rv = shEntry->GetStateData(stateData);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        // if shEntry is null, we just set the pending state object to the
-        // empty string.
-    }
-
-    document->SetCurrentStateObject(stateData);
+    nsCOMPtr<nsIStructuredCloneContainer> scContainer;
+    nsresult rv = shEntry->GetStateData(getter_AddRefs(scContainer));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // It's OK for scContainer too be null here; that just means there's no
+    // state data associated with this history entry.
+    document->SetStateObject(scContainer);
+
     return NS_OK;
 }
 
 nsresult
 nsDocShell::CheckLoadingPermissions()
 {
     // This method checks whether the caller may load content into
     // this docshell. Even though we've done our best to hide windows
@@ -9408,84 +9406,24 @@ nsDocShell::SetReferrerURI(nsIURI * aURI
 {
     mReferrerURI = aURI;        // This assigment addrefs
 }
 
 //*****************************************************************************
 // nsDocShell: Session History
 //*****************************************************************************
 
-nsresult
-nsDocShell::StringifyJSValVariant(JSContext *aCx, nsIVariant *aData,
-                                  nsAString &aResult)
-{
-    nsresult rv;
-    aResult.Truncate();
-
-    // First, try to extract a jsval from the variant |aData|.  This works only
-    // if the variant implements GetAsJSVal.
-    jsval jsData;
-    rv = aData->GetAsJSVal(&jsData);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
-
-    nsCOMPtr<nsIJSContextStack> contextStack;
-    JSContext *cx = aCx;
-    if (!cx) {
-        // Now get the JSContext associated with the current document.
-        // First get the current document.
-        nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
-        NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
-
-        // Get the JSContext from the document, like we do in
-        // nsContentUtils::GetContextFromDocument().
-        nsIScriptGlobalObject *sgo = document->GetScopeObject();
-        NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
-
-        nsIScriptContext *scx = sgo->GetContext();
-        NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
-
-        cx = (JSContext *)scx->GetNativeContext();
-
-        // If our json call triggers a JS-to-C++ call, we want that call to use
-        // aCx as the context.  So we push aCx onto the context stack.
-        contextStack =
-            do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        contextStack->Push(cx);
-    }
-
-    nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
-    if(json) {
-        // Do the encoding
-        rv = json->EncodeFromJSVal(&jsData, cx, aResult);
-    }
-    else {
-        rv = NS_ERROR_FAILURE;
-    }
-
-    if (contextStack) {
-        if (NS_FAILED(rv)) {
-            JS_ClearPendingException(cx);
-        }
-
-        contextStack->Pop(&cx);
-    }
-
-    return rv;
-}
-
 NS_IMETHODIMP
 nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
                      const nsAString& aURL, PRBool aReplace, JSContext* aCx)
 {
     // Implements History.pushState and History.replaceState
 
     // Here's what we do, roughly in the order specified by HTML5:
-    // 1. Serialize aData to JSON.
+    // 1. Serialize aData using structured clone.
     // 2. If the third argument is present,
     //     a. Resolve the url, relative to the first script's base URL
     //     b. If (a) fails, raise a SECURITY_ERR
     //     c. Compare the resulting absolute URL to the document's address.  If
     //        any part of the URLs difer other than the <path>, <query>, and
     //        <fragment> components, raise a SECURITY_ERR and abort.
     // 3. If !aReplace:
     //     Remove from the session history all entries after the current entry,
@@ -9509,60 +9447,76 @@ nsDocShell::AddState(nsIVariant *aData, 
     // active content viewer.  Since EvictContentViewers at the end of step 5
     // might run script, we can't just put a script blocker around the critical
     // section.
     //
     // Note that we completely ignore the aTitle parameter.
 
     nsresult rv;
 
-    // Step 1: Clone aData by getting its JSON representation.
-    //
-    // StringifyJSValVariant might cause arbitrary JS to run, and this code
-    // might navigate the page we're on, potentially to a different origin! (bug
+    nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
+    NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+    // Step 1: Serialize aData using structured clone.
+    nsCOMPtr<nsIStructuredCloneContainer> scContainer;
+
+    // scContainer->Init might cause arbitrary JS to run, and this code might
+    // navigate the page we're on, potentially to a different origin! (bug
     // 634834)  To protect against this, we abort if our principal changes due
-    // to the stringify call.
-    nsString dataStr;
+    // to the InitFromVariant() call.
     {
         nsCOMPtr<nsIDocument> origDocument =
             do_GetInterface(GetAsSupports(this));
         if (!origDocument)
             return NS_ERROR_DOM_SECURITY_ERR;
         nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
 
-        rv = StringifyJSValVariant(aCx, aData, dataStr);
+        scContainer = new nsStructuredCloneContainer();
+        JSContext *cx = aCx;
+        if (!cx) {
+            cx = nsContentUtils::GetContextFromDocument(document);
+        }
+        rv = scContainer->InitFromVariant(aData, cx);
+
+        // If we're running in the document's context and the structured clone
+        // failed, clear the context's pending exception.  See bug 637116.
+        if (NS_FAILED(rv) && !aCx) {
+            JS_ClearPendingException(aCx);
+        }
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsCOMPtr<nsIDocument> newDocument =
             do_GetInterface(GetAsSupports(this));
         if (!newDocument)
             return NS_ERROR_DOM_SECURITY_ERR;
         nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
 
         PRBool principalsEqual = PR_FALSE;
         origPrincipal->Equals(newPrincipal, &principalsEqual);
         NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
     }
 
     // Check that the state object isn't too long.
-    // Default max length: 640k chars.
+    // Default max length: 640k bytes.
     PRInt32 maxStateObjSize = 0xA0000;
     if (mPrefs) {
         mPrefs->GetIntPref("browser.history.maxStateObjectSize",
                            &maxStateObjSize);
     }
     if (maxStateObjSize < 0) {
         maxStateObjSize = 0;
     }
-    NS_ENSURE_TRUE(dataStr.Length() <= (PRUint32)maxStateObjSize,
+
+    PRUint64 scSize;
+    rv = scContainer->GetSerializedNBytes(&scSize);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    NS_ENSURE_TRUE(scSize <= (PRUint32)maxStateObjSize,
                    NS_ERROR_ILLEGAL_VALUE);
 
-    nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
-    NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
-
     // Step 2: Resolve aURL
     PRBool equalURIs = PR_TRUE;
     nsCOMPtr<nsIURI> oldURI = mCurrentURI;
     nsCOMPtr<nsIURI> newURI;
     if (aURL.Length() == 0) {
         newURI = mCurrentURI;
     }
     else {
@@ -9688,17 +9642,17 @@ nsDocShell::AddState(nsIVariant *aData, 
 
     } else {
         newSHEntry = mOSHE;
         newSHEntry->SetURI(newURI);
     }
 
     // Step 4: Modify new/original session history entry and clear its POST
     // data, if there is any.
-    newSHEntry->SetStateData(dataStr);
+    newSHEntry->SetStateData(scContainer);
     newSHEntry->SetPostData(nsnull);
 
     // Step 5: If aReplace is false, indicating that we're doing a pushState
     // rather than a replaceState, notify bfcache that we've added a page to
     // the history so it can evict content viewers if appropriate.
     if (!aReplace) {
         nsCOMPtr<nsISHistory> rootSH;
         GetRootSessionHistory(getter_AddRefs(rootSH));
@@ -9726,17 +9680,17 @@ nsDocShell::AddState(nsIVariant *aData, 
         SetCurrentURI(newURI, nsnull, PR_TRUE);
         document->SetDocumentURI(newURI);
 
         AddURIVisit(newURI, oldURI, oldURI, 0);
     }
     else {
         FireDummyOnLocationChange();
     }
-    document->SetCurrentStateObject(dataStr);
+    document->SetStateObject(scContainer);
 
     return NS_OK;
 }
 
 PRBool
 nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
 {
     // I believe none of the about: urls should go in the history. But then
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -330,19 +330,19 @@ protected:
                                   nsIChannel * aChannel);
     virtual nsresult DoChannelLoad(nsIChannel * aChannel,
                                    nsIURILoader * aURILoader,
                                    PRBool aBypassClassifier);
 
     nsresult ScrollToAnchor(nsACString & curHash, nsACString & newHash,
                             PRUint32 aLoadType);
 
-    // Tries to stringify a given variant by converting it to JSON.  This only
+    // Tries to serialize a given variant using structured clone.  This only
     // works if the variant is backed by a JSVal.
-    nsresult StringifyJSValVariant(JSContext *aCx, nsIVariant *aData,
+    nsresult SerializeJSValVariant(JSContext *aCx, nsIVariant *aData,
                                    nsAString &aResult);
 
     // Returns PR_TRUE if would have called FireOnLocationChange,
     // but did not because aFireOnLocationChange was false on entry.
     // In this case it is the caller's responsibility to ensure
     // FireOnLocationChange is called.
     // In all other cases PR_FALSE is returned.
     PRBool OnLoadingSite(nsIChannel * aChannel,
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -67,16 +67,18 @@
 // session history
 #include "nsSHEntry.h"
 #include "nsSHistory.h"
 #include "nsSHTransaction.h"
 
 // download history
 #include "nsDownloadHistory.h"
 
+#include "nsStructuredCloneContainer.h"
+
 static PRBool gInitialized = PR_FALSE;
 
 // The one time initialization for this module
 static nsresult
 Initialize()
 {
   NS_PRECONDITION(!gInitialized, "docshell module already initialized");
   if (gInitialized) {
@@ -126,16 +128,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsExterna
 // session history
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHEntry)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHTransaction)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHistory)
 
 // download history
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadHistory)
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsStructuredCloneContainer)
+
 NS_DEFINE_NAMED_CID(NS_DOCSHELL_CID);
 NS_DEFINE_NAMED_CID(NS_DEFAULTURIFIXUP_CID);
 NS_DEFINE_NAMED_CID(NS_WEBNAVIGATION_INFO_CID);
 NS_DEFINE_NAMED_CID(NS_ABOUT_REDIRECTOR_MODULE_CID);
 NS_DEFINE_NAMED_CID(NS_URI_LOADER_CID);
 NS_DEFINE_NAMED_CID(NS_DOCUMENTLOADER_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_EXTERNALHELPERAPPSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_EXTERNALPROTOCOLHANDLER_CID);
@@ -153,16 +157,17 @@ NS_DEFINE_NAMED_CID(NS_EXTERNALSHARINGAP
 NS_DEFINE_NAMED_CID(NS_EXTERNALURLHANDLERSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_SHENTRY_CID);
 NS_DEFINE_NAMED_CID(NS_HISTORYENTRY_CID);
 NS_DEFINE_NAMED_CID(NS_SHTRANSACTION_CID);
 NS_DEFINE_NAMED_CID(NS_SHISTORY_CID);
 NS_DEFINE_NAMED_CID(NS_SHISTORY_INTERNAL_CID);
 NS_DEFINE_NAMED_CID(NS_DOWNLOADHISTORY_CID);
+NS_DEFINE_NAMED_CID(NS_STRUCTUREDCLONECONTAINER_CID);
 
 
 const mozilla::Module::CIDEntry kDocShellCIDs[] = {
   { &kNS_DOCSHELL_CID, false, NULL, nsDocShellConstructor },
   { &kNS_DEFAULTURIFIXUP_CID, false, NULL, nsDefaultURIFixupConstructor },
   { &kNS_WEBNAVIGATION_INFO_CID, false, NULL, nsWebNavigationInfoConstructor },
   { &kNS_ABOUT_REDIRECTOR_MODULE_CID, false, NULL, nsAboutRedirector::Create },
   { &kNS_URI_LOADER_CID, false, NULL, nsURILoaderConstructor },
@@ -183,16 +188,17 @@ const mozilla::Module::CIDEntry kDocShel
   { &kNS_EXTERNALURLHANDLERSERVICE_CID, false, NULL, nsExternalURLHandlerServiceConstructor },
 #endif
   { &kNS_SHENTRY_CID, false, NULL, nsSHEntryConstructor },
   { &kNS_HISTORYENTRY_CID, false, NULL, nsSHEntryConstructor },
   { &kNS_SHTRANSACTION_CID, false, NULL, nsSHTransactionConstructor },
   { &kNS_SHISTORY_CID, false, NULL, nsSHistoryConstructor },
   { &kNS_SHISTORY_INTERNAL_CID, false, NULL, nsSHistoryConstructor },
   { &kNS_DOWNLOADHISTORY_CID, false, NULL, nsDownloadHistoryConstructor },
+  { &kNS_STRUCTUREDCLONECONTAINER_CID, false, NULL, nsStructuredCloneContainerConstructor },
   { NULL }
 };
 
 const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
   { "@mozilla.org/docshell;1", &kNS_DOCSHELL_CID },
   { NS_URIFIXUP_CONTRACTID, &kNS_DEFAULTURIFIXUP_CID },
   { NS_WEBNAVIGATION_INFO_CONTRACTID, &kNS_WEBNAVIGATION_INFO_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
@@ -231,16 +237,17 @@ const mozilla::Module::ContractIDEntry k
   { NS_EXTERNALURLHANDLERSERVICE_CONTRACTID, &kNS_EXTERNALURLHANDLERSERVICE_CID },
 #endif
   { NS_SHENTRY_CONTRACTID, &kNS_SHENTRY_CID },
   { NS_HISTORYENTRY_CONTRACTID, &kNS_HISTORYENTRY_CID },
   { NS_SHTRANSACTION_CONTRACTID, &kNS_SHTRANSACTION_CID },
   { NS_SHISTORY_CONTRACTID, &kNS_SHISTORY_CID },
   { NS_SHISTORY_INTERNAL_CONTRACTID, &kNS_SHISTORY_INTERNAL_CID },
   { NS_DOWNLOADHISTORY_CONTRACTID, &kNS_DOWNLOADHISTORY_CID },
+  { NS_STRUCTUREDCLONECONTAINER_CONTRACTID, &kNS_STRUCTUREDCLONECONTAINER_CID },
   { NULL }
 };
 
 static const mozilla::Module kDocShellModule = {
   mozilla::Module::kVersion,
   kDocShellCIDs,
   kDocShellContracts,
   NULL,
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -45,16 +45,17 @@
 #include "nsIHistoryEntry.idl"
 
 interface nsILayoutHistoryState;
 interface nsIContentViewer;
 interface nsIURI;
 interface nsIInputStream;
 interface nsIDocShellTreeItem;
 interface nsISupportsArray;
+interface nsIStructuredCloneContainer;
 %{C++
 struct nsIntRect;
 class nsDocShellEditorData;
 %}
 [ref] native nsIntRect(nsIntRect);
 [ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
 
 
@@ -196,19 +197,19 @@ interface nsISHEntry : nsIHistoryEntry
      * Get the owner, if any, that was associated with the channel
      * that the document that was loaded to create this history entry
      * came from.
      */
     attribute nsISupports owner;
 
     /**
      * Get/set data associated with this history state via a pushState() call,
-     * encoded as JSON.
+     * serialized using structured clone.
      **/
-    attribute AString stateData;
+    attribute nsIStructuredCloneContainer stateData;
 
     /**
      * Gets the owning pointer to the editor data assosicated with
      * this shistory entry. This forgets its pointer, so free it when
      * you're done.
      */
     [noscript, notxpcom] nsDocShellEditorDataPtr forgetEditorData();
 
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -137,18 +137,18 @@ nsSHEntry::nsSHEntry(const nsSHEntry &ot
   , mExpired(other.mExpired)
   , mSticky(PR_TRUE)
   , mDynamicallyCreated(other.mDynamicallyCreated)
   // XXX why not copy mContentType?
   , mCacheKey(other.mCacheKey)
   , mParent(other.mParent)
   , mViewerBounds(0, 0, 0, 0)
   , mOwner(other.mOwner)
+  , mDocShellID(other.mDocShellID)
   , mStateData(other.mStateData)
-  , mDocShellID(other.mDocShellID)
 {
 }
 
 static PRBool
 ClearParentPtr(nsISHEntry* aEntry, void* /* aData */)
 {
   if (aEntry) {
     aEntry->SetParent(nsnull);
@@ -987,26 +987,27 @@ nsSHEntry::SetEditorData(nsDocShellEdito
 
 PRBool
 nsSHEntry::HasDetachedEditor()
 {
   return mEditorData != nsnull;
 }
 
 NS_IMETHODIMP
-nsSHEntry::GetStateData(nsAString &aStateData)
+nsSHEntry::GetStateData(nsIStructuredCloneContainer **aContainer)
 {
-  aStateData.Assign(mStateData);
+  NS_ENSURE_ARG_POINTER(aContainer);
+  NS_IF_ADDREF(*aContainer = mStateData);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHEntry::SetStateData(const nsAString &aDataStr)
+nsSHEntry::SetStateData(nsIStructuredCloneContainer *aContainer)
 {
-  mStateData.Assign(aDataStr);
+  mStateData = aContainer;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::IsDynamicallyAdded(PRBool* aAdded)
 {
   *aAdded = mDynamicallyCreated;
   return NS_OK;
--- a/docshell/shistory/src/nsSHEntry.h
+++ b/docshell/shistory/src/nsSHEntry.h
@@ -112,14 +112,14 @@ private:
   nsISHEntry *                    mParent;  // weak reference
   nsCOMPtr<nsISupports>           mWindowState;
   nsIntRect                       mViewerBounds;
   nsCOMArray<nsIDocShellTreeItem> mChildShells;
   nsCOMPtr<nsISupportsArray>      mRefreshURIList;
   nsCOMPtr<nsISupports>           mOwner;
   nsExpirationState               mExpirationState;
   nsAutoPtr<nsDocShellEditorData> mEditorData;
-  nsString                        mStateData;
   PRUint64                        mDocShellID;
   PRUint32                        mLastTouched;
+  nsCOMPtr<nsIStructuredCloneContainer> mStateData;
 };
 
 #endif /* nsSHEntry_h */
--- a/docshell/test/Makefile.in
+++ b/docshell/test/Makefile.in
@@ -82,16 +82,17 @@ include $(topsrcdir)/config/rules.mk
 		file_bug385434_3.html \
 		test_bug509055.html \
 		file_bug509055.html \
 		test_bug529119-1.html \
 		test_bug529119-2.html \
 		bug529119-window.html \
 		test_bug540462.html \
 		file_bug540462.html \
+		test_bug551225.html \
 		test_bug580069.html \
 		file_bug580069_1.html \
 		file_bug580069_2.sjs \
 		test_bug590573.html \
 		file_bug590573_1.html \
 		file_bug590573_2.html \
 		test_bug598895.html \
 		test_bug634834.html \
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_bug551225.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=551225
+-->
+<head>
+  <title>Test for Bug 551225</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=551225">Mozilla Bug 551225</a>
+
+<script type="application/javascript;version=1.7">
+
+/** Test for Bug 551225 **/
+
+obj = {
+  a: new Date('1/1/2000'),
+  b: /^foo$/,
+  c: 'bar'
+};
+
+history.replaceState(obj, '', '');
+is(history.state.a.toString(), new Date('1/1/2000').toString(), 'Date object.');
+is(history.state.b.toString(), '/^foo$/', 'Regex');
+is(history.state.c, 'bar', 'Other state');
+
+</script>
+</body>
+</html>
--- a/dom/base/ConsoleAPI.js
+++ b/dom/base/ConsoleAPI.js
@@ -16,16 +16,17 @@
  * 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)
  *  Ryan Flint <rflint@mozilla.com>
  *  Rob Campbell <rcampbell@mozilla.com>
+ *  Mihai Sucan <mihai.sucan@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
@@ -74,37 +75,42 @@ ConsoleAPI.prototype = {
         self.notifyObservers(id, "warn", arguments);
       },
       error: function CA_error() {
         self.notifyObservers(id, "error", arguments);
       },
       debug: function CA_debug() {
         self.notifyObservers(id, "log", arguments);
       },
+      trace: function CA_trace() {
+        self.notifyObservers(id, "trace", self.getStackTrace());
+      },
       __exposedProps__: {
         log: "r",
         info: "r",
         warn: "r",
         error: "r",
         debug: "r",
+        trace: "r",
       }
     };
 
     // We need to return an actual content object here, instead of a wrapped
     // chrome object. This allows things like console.log.bind() to work.
     let sandbox = Cu.Sandbox(aWindow);
     let contentObject = Cu.evalInSandbox(
         "(function(x) {\
           var bind = Function.bind;\
           var obj = {\
             log: bind.call(x.log, x),\
             info: bind.call(x.info, x),\
             warn: bind.call(x.warn, x),\
             error: bind.call(x.error, x),\
             debug: bind.call(x.debug, x),\
+            trace: bind.call(x.trace, x),\
             __noSuchMethod__: function() {}\
           };\
           Object.defineProperty(obj, '__mozillaConsole__', { value: true });\
           return obj;\
         })", sandbox)(chromeObject);
 
       return contentObject;
   },
@@ -121,12 +127,37 @@ ConsoleAPI.prototype = {
       level: aLevel,
       arguments: aArguments
     };
 
     consoleEvent.wrappedJSObject = consoleEvent;
 
     Services.obs.notifyObservers(consoleEvent,
                                  "console-api-log-event", aID);
-  }
+  },
+
+  /**
+   * 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.
+   **/
+  getStackTrace: function CA_getStackTrace() {
+    let stack = [];
+    let frame = Components.stack.caller;
+    while (frame = frame.caller) {
+      if (frame.language == Ci.nsIProgrammingLanguage.JAVASCRIPT ||
+          frame.language == Ci.nsIProgrammingLanguage.JAVASCRIPT2) {
+        stack.push({
+          filename: frame.filename,
+          lineNumber: frame.lineNumber,
+          functionName: frame.name,
+          language: frame.language,
+        });
+      }
+    }
+
+    return stack;
+  },
 };
 
 let NSGetFactory = XPCOMUtils.generateNSGetFactory([ConsoleAPI]);
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -79,16 +79,17 @@ EXPORTS = \
   nsIScriptObjectPrincipal.h \
   nsIScriptRuntime.h \
   nsIScriptTimeoutHandler.h \
   nsPIDOMWindow.h \
   nsPIWindowRoot.h \
   nsFocusManager.h \
   nsWrapperCache.h \
   nsContentPermissionHelper.h \
+  nsStructuredCloneContainer.h \
   $(NULL)
 
 CPPSRCS =			\
 	nsBarProps.cpp          \
 	nsDOMException.cpp 	\
 	nsDOMWindowUtils.cpp 	\
 	nsJSEnvironment.cpp	\
 	nsJSTimeoutHandler.cpp	\
@@ -103,16 +104,17 @@ CPPSRCS =			\
 	nsMimeTypeArray.cpp	\
 	nsPluginArray.cpp	\
 	nsWindowRoot.cpp	\
 	nsDOMClassInfo.cpp	\
 	nsScriptNameSpaceManager.cpp \
 	nsDOMScriptObjectFactory.cpp \
 	nsQueryContentEventResult.cpp \
 	nsContentPermissionHelper.cpp \
+	nsStructuredCloneContainer.cpp \
 	$(NULL)
 
 include $(topsrcdir)/dom/dom-config.mk
 
 ifdef MOZ_JSDEBUGGER
 DEFINES += -DMOZ_JSDEBUGGER
 endif
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7767,20 +7767,20 @@ nsGlobalWindow::DispatchSyncPopState()
   nsresult rv = NS_OK;
 
   // Bail if the window is frozen.
   if (IsFrozen()) {
     return NS_OK;
   }
 
   // Get the document's pending state object -- it contains the data we're
-  // going to send along with the popstate event.  The object is serialized as
-  // JSON.
+  // going to send along with the popstate event.  The object is serialized
+  // using structured clone.
   nsCOMPtr<nsIVariant> stateObj;
-  rv = mDoc->GetMozCurrentStateObject(getter_AddRefs(stateObj));
+  rv = mDoc->GetStateObject(getter_AddRefs(stateObj));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Obtain a presentation shell for use in creating a popstate event.
   nsIPresShell *shell = mDoc->GetShell();
   nsRefPtr<nsPresContext> presContext;
   if (shell) {
     presContext = shell->GetPresContext();
   }
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -349,17 +349,17 @@ nsHistory::GetState(nsIVariant **aState)
   if (!nsContentUtils::CanCallerAccess(win->GetOuterWindow()))
     return NS_ERROR_DOM_SECURITY_ERR;
 
   nsCOMPtr<nsIDocument> doc =
     do_QueryInterface(win->GetExtantDocument());
   if (!doc)
     return NS_ERROR_NOT_AVAILABLE;
 
-  return doc->GetMozCurrentStateObject(aState);
+  return doc->GetStateObject(aState);
 }
 
 NS_IMETHODIMP
 nsHistory::Item(PRUint32 aIndex, nsAString& aReturn)
 {
   aReturn.Truncate();
   if (!nsContentUtils::IsCallerTrustedForRead()) {
     return NS_ERROR_DOM_SECURITY_ERR;
new file mode 100644
--- /dev/null
+++ b/dom/base/nsStructuredCloneContainer.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sw=2 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The 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):
+ *   Justin Lebar <justin.lebar@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 ***** */
+
+#include "nsStructuredCloneContainer.h"
+
+#include "nsCOMPtr.h"
+#include "nsIDocument.h"
+#include "nsIJSContextStack.h"
+#include "nsIScriptContext.h"
+#include "nsIVariant.h"
+#include "nsServiceManagerUtils.h"
+#include "nsContentUtils.h"
+#include "xpcprivate.h"
+
+NS_IMPL_ADDREF(nsStructuredCloneContainer)
+NS_IMPL_RELEASE(nsStructuredCloneContainer)
+
+NS_INTERFACE_MAP_BEGIN(nsStructuredCloneContainer)
+  NS_INTERFACE_MAP_ENTRY(nsIStructuredCloneContainer)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+nsStructuredCloneContainer::nsStructuredCloneContainer()
+  : mData(nsnull), mSize(0), mVersion(0)
+{
+}
+
+nsStructuredCloneContainer::~nsStructuredCloneContainer()
+{
+  free(mData);
+}
+
+nsresult
+nsStructuredCloneContainer::InitFromVariant(nsIVariant *aData, JSContext *aCx)
+{
+  NS_ENSURE_STATE(!mData);
+  NS_ENSURE_ARG_POINTER(aData);
+  NS_ENSURE_ARG_POINTER(aCx);
+
+  // First, try to extract a jsval from the variant |aData|.  This works only
+  // if the variant implements GetAsJSVal.
+  jsval jsData;
+  nsresult rv = aData->GetAsJSVal(&jsData);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
+
+  // Make sure that we serialize in the right context.
+  JSAutoRequest ar(aCx);
+  JSAutoEnterCompartment ac;
+  NS_ENSURE_STATE(ac.enter(aCx, JS_GetGlobalObject(aCx)));
+
+  nsCxPusher cxPusher;
+  cxPusher.Push(aCx);
+
+  PRUint64* jsBytes = nsnull;
+  PRBool success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize,
+                                           nsnull, nsnull);
+  NS_ENSURE_STATE(success);
+  NS_ENSURE_STATE(jsBytes);
+
+  // Copy jsBytes into our own buffer.
+  mData = (PRUint64*) malloc(mSize);
+  if (!mData) {
+    mSize = 0;
+    mVersion = 0;
+    return NS_ERROR_FAILURE;
+  }
+  else {
+    mVersion = JS_STRUCTURED_CLONE_VERSION;
+  }
+
+  memcpy(mData, jsBytes, mSize);
+  return NS_OK;
+}
+
+nsresult
+nsStructuredCloneContainer::InitFromBase64(const nsAString &aData,
+                                           PRUint32 aFormatVersion,
+                                           JSContext *aCx)
+{
+  NS_ENSURE_STATE(!mData);
+
+  NS_ConvertUTF16toUTF8 data(aData);
+
+  nsCAutoString binaryData;
+  nsresult rv = nsXPConnect::Base64Decode(data, binaryData);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Copy the string's data into our own buffer.
+  mData = (PRUint64*) malloc(binaryData.Length());
+  NS_ENSURE_STATE(mData);
+  memcpy(mData, binaryData.get(), binaryData.Length());
+
+  mSize = binaryData.Length();
+  mVersion = aFormatVersion;
+  return NS_OK;
+}
+
+
+nsresult
+nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx,
+                                                 nsIVariant **aData)
+{
+  NS_ENSURE_STATE(mData);
+  NS_ENSURE_ARG_POINTER(aData);
+  *aData = nsnull;
+
+  // Deserialize to a jsval.
+  jsval jsStateObj;
+  PRBool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion,
+                                          &jsStateObj, nsnull, nsnull);
+  NS_ENSURE_STATE(success);
+
+  // Now wrap the jsval as an nsIVariant.
+  nsCOMPtr<nsIVariant> varStateObj;
+  nsCOMPtr<nsIXPConnect> xpconnect = do_GetService(nsIXPConnect::GetCID());
+  NS_ENSURE_STATE(xpconnect);
+  xpconnect->JSValToVariant(aCx, &jsStateObj, getter_AddRefs(varStateObj));
+  NS_ENSURE_STATE(varStateObj);
+
+  NS_IF_ADDREF(*aData = varStateObj);
+  return NS_OK;
+}
+
+nsresult
+nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
+{
+  NS_ENSURE_STATE(mData);
+  aOut.Truncate();
+
+  nsCAutoString binaryData(reinterpret_cast<char*>(mData), mSize);
+  nsCAutoString base64Data;
+  nsresult rv = nsXPConnect::Base64Encode(binaryData, base64Data);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aOut.Assign(NS_ConvertASCIItoUTF16(base64Data));
+  return NS_OK;
+}
+
+nsresult
+nsStructuredCloneContainer::GetSerializedNBytes(PRUint64 *aSize)
+{
+  NS_ENSURE_STATE(mData);
+  NS_ENSURE_ARG_POINTER(aSize);
+
+  // mSize is a size_t, while aSize is a PRUint64.  We rely on an implicit cast
+  // here so that we'll get a compile error if a size_t-to-uint64 cast is
+  // narrowing.
+  *aSize = mSize;
+
+  return NS_OK;
+}
+
+nsresult
+nsStructuredCloneContainer::GetFormatVersion(PRUint32 *aFormatVersion)
+{
+  NS_ENSURE_STATE(mData);
+  NS_ENSURE_ARG_POINTER(aFormatVersion);
+  *aFormatVersion = mVersion;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/nsStructuredCloneContainer.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sw=2 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The 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):
+ *   Justin Lebar <justin.lebar@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 ***** */
+
+#ifndef nsStructuredCloneContainer_h__
+#define nsStructuredCloneContainer_h__
+
+#include "nsIStructuredCloneContainer.h"
+#include "jsapi.h"
+
+#define NS_STRUCTUREDCLONECONTAINER_CLASSNAME "nsStructuredCloneContainer"
+#define NS_STRUCTUREDCLONECONTAINER_CONTRACTID \
+  "@mozilla.org/docshell/structured-clone-container;1"
+#define NS_STRUCTUREDCLONECONTAINER_CID \
+{ /* 38bd0634-0fd4-46f0-b85f-13ced889eeec */       \
+  0x38bd0634,                                      \
+  0x0fd4,                                          \
+  0x46f0,                                          \
+  {0xb8, 0x5f, 0x13, 0xce, 0xd8, 0x89, 0xee, 0xec} \
+}
+
+class nsStructuredCloneContainer : public nsIStructuredCloneContainer
+{
+  public:
+    nsStructuredCloneContainer();
+    ~nsStructuredCloneContainer();
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISTRUCTUREDCLONECONTAINER
+
+  private:
+    PRUint64* mData;
+
+    // This needs to be size_t rather than a PR-type so it matches the JS API.
+    size_t mSize;
+    PRUint32 mVersion;
+};
+
+#endif
--- a/dom/interfaces/base/Makefile.in
+++ b/dom/interfaces/base/Makefile.in
@@ -82,11 +82,12 @@ XPIDLSRCS =					\
 	nsIDOMNSFeatureFactory.idl		\
         nsIDOMClientRect.idl			\
         nsIDOMClientRectList.idl		\
 	nsIFocusManager.idl			\
 	nsIQueryContentEventResult.idl		\
 	nsITabChild.idl				\
 	nsITabParent.idl			\
 	nsIDOMGlobalPropertyInitializer.idl	\
+	nsIStructuredCloneContainer.idl		\
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/base/nsIStructuredCloneContainer.idl
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sw=2 et tw=80:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The 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):
+ *   Justin Lebar <justin.lebar@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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+interface nsIDocument;
+
+%{C++
+struct JSContext;
+%}
+
+/**
+ * This interface acts as a container for an object serialized using the
+ * structured clone algorithm.
+ *
+ * You can copy an object into an nsIStructuredCloneContainer using
+ * initFromVariant or initFromBase64.  It's an error to initialize an
+ * nsIStructuredCloneContainer more than once.
+ *
+ * Once you've initialized the container, you can get a copy of the object it
+ * stores by calling deserializeToVariant.  You can also get a base-64-encoded
+ * string containing a copy of the container's serialized data, using
+ * getDataAsBase64.
+ */
+[scriptable, uuid(400a282d-7157-4ed0-85b4-8bdc2fa634cd)]
+interface nsIStructuredCloneContainer : nsISupports
+{
+  /**
+   * Initialize this structured clone container so it contains a clone of the
+   * given variant. aData must be backed by a jsval.
+   */
+  [implicit_jscontext]
+  void initFromVariant(in nsIVariant aData);
+
+  /**
+   * Initialize this structured clone container from a base-64-encoded byte
+   * stream, stored in aData.  aFormatVersion should be the version of the
+   * structured clone algorithm which was used to generate aData.
+   */
+  [implicit_jscontext]
+  void initFromBase64(in AString aData,in unsigned long aFormatVersion);
+
+  /**
+   * Deserialize the object this conatiner holds, returning it wrapped as
+   * an nsIVariant.
+   */
+  [implicit_jscontext]
+  nsIVariant deserializeToVariant();
+
+  /**
+   * Get this structured clone container's data as a base-64-encoded string.
+   */
+  AString getDataAsBase64();
+
+  /**
+   * Get the size in bytes of this container's serialized data.
+   */
+  readonly attribute unsigned long long serializedNBytes;
+
+  /**
+   * Get the version of the structured clone algorithm which was used to
+   * generate this container's serialized buffer.
+   */
+  readonly attribute unsigned long formatVersion;
+};
--- a/dom/tests/browser/browser_ConsoleAPITests.js
+++ b/dom/tests/browser/browser_ConsoleAPITests.js
@@ -16,16 +16,17 @@
  *
  * 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>
  *  Rob Campbell <rcampbell@mozilla.com>
+ *  Mihai Sucan <mihai.sucan@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
@@ -68,25 +69,49 @@ var gWindow;
 
 function testConsoleData(aMessageObject) {
   let messageWindow = getWindowByWindowId(aMessageObject.ID);
   is(messageWindow, gWindow, "found correct window by window ID");
 
   is(aMessageObject.level, gLevel, "expected level received");
   ok(aMessageObject.arguments, "we have arguments");
   is(aMessageObject.arguments.length, gArgs.length, "arguments.length matches");
-  gArgs.forEach(function (a, i) {
-    is(aMessageObject.arguments[i], a, "correct arg " + i);
-  });
 
-  if (aMessageObject.level == "error") {
+  if (gLevel == "trace") {
+    is(aMessageObject.arguments.toSource(), gArgs.toSource(),
+       "stack trace is correct");
+
     // Test finished
     ConsoleObserver.destroy();
     finish();
   }
+  else {
+    gArgs.forEach(function (a, i) {
+      is(aMessageObject.arguments[i], a, "correct arg " + i);
+    });
+  }
+
+  if (aMessageObject.level == "error") {
+    // Now test console.trace()
+    startTraceTest();
+  }
+}
+
+function startTraceTest() {
+  gLevel = "trace";
+  gArgs = [
+    {filename: TEST_URI, lineNumber: 6, functionName: null, language: 2},
+    {filename: TEST_URI, lineNumber: 11, functionName: "foobar585956b", language: 2},
+    {filename: TEST_URI, lineNumber: 15, functionName: "foobar585956a", language: 2},
+    {filename: TEST_URI, lineNumber: 1, functionName: "onclick", language: 2}
+  ];
+
+  let button = gWindow.document.getElementById("test-trace");
+  ok(button, "found #test-trace button");
+  EventUtils.synthesizeMouse(button, 2, 2, {}, gWindow);
 }
 
 var gLevel, gArgs;
 function expect(level) {
   gLevel = level;
   gArgs = Array.slice(arguments, 1);
 }
 
@@ -109,16 +134,17 @@ function consoleAPISanityTest() {
   let win = XPCNativeWrapper.unwrap(gWindow);
   ok(win.console, "we have a console attached");
   ok(win.console, "we have a console attached, 2nd attempt");
 
   ok(win.console.log, "console.log is here");
   ok(win.console.info, "console.info is here");
   ok(win.console.warn, "console.warn is here");
   ok(win.console.error, "console.error is here");
+  ok(win.console.trace, "console.trace is here");
 }
 
 var ConsoleObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   init: function CO_init() {
     Services.obs.addObserver(this, "console-api-log-event", false);
   },
--- a/dom/tests/browser/test-console-api.html
+++ b/dom/tests/browser/test-console-api.html
@@ -1,19 +1,33 @@
 <!DOCTYPE HTML>
 <html dir="ltr" xml:lang="en-US" lang="en-US"><head>
     <title>Console API test page</title>
     <script type="text/javascript">
+      window.foobar585956c = function(a) {
+        console.trace();
+        return a+"c";
+      };
+
+      function foobar585956b(a) {
+        return foobar585956c(a+"b");
+      }
+
+      function foobar585956a(omg) {
+        return foobar585956b(omg + "a");
+      }
+
       function test() {
         var str = "Test Message."
         console.foobar(str); // if this throws, we don't execute following funcs
         console.log(str);
         console.info(str);
         console.warn(str);
         console.error(str);
       }
     </script>
   </head>
   <body>
     <h1>Console API Test Page</h1>
     <button onclick="test();">Log stuff</button>
+    <button id="test-trace" onclick="foobar585956a('omg');">Test trace</button>
   </body>
 </html>
--- a/dom/tests/mochitest/chrome/Makefile.in
+++ b/dom/tests/mochitest/chrome/Makefile.in
@@ -63,10 +63,17 @@ include $(topsrcdir)/config/rules.mk
 		test_activation.xul \
 		window_activation.xul \
 		test_DOMWindowCreated.xul \
 		DOMWindowCreated_chrome.xul \
 		DOMWindowCreated_content.html \
 		test_sandbox_image.xul \
 		$(NULL)
 
+ifeq (WINNT,$(OS_ARCH))
+_TEST_FILES += \
+		test_sizemode_attribute.xul \
+		sizemode_attribute.xul \
+		$(NULL)
+endif
+
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/chrome/sizemode_attribute.xul
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+  Test for fullscreen sizemode in chrome
+  -->
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        persist="sizemode"
+        sizemode="normal"
+        onload="nextStep()">
+
+<script type="text/javascript;version=1.8">
+let tests = [
+  function test1() {
+    checkAndContinue("normal");
+  },
+
+  function test2() {
+    listen("fullscreen", function() checkAndContinue("fullscreen"));
+    window.fullScreen = true;
+  },
+
+  function test3() {
+    listen("fullscreen", function() checkAndContinue("normal"));
+    window.fullScreen = false;
+  },
+
+  function test4() {
+    listen("resize", function() checkAndContinue("maximized"));
+    window.maximize();
+  },
+
+  function test5() {
+    listen("fullscreen", function() checkAndContinue("fullscreen"));
+    window.fullScreen = true;
+  },
+
+  function test6() {
+    listen("fullscreen", function() checkAndContinue("maximized"));
+    window.fullScreen = false;
+  },
+
+  function test7() {
+    listen("resize", function() checkAndContinue("normal"));
+    window.restore();
+  },
+
+  function test8() {
+    window.opener.wrappedJSObject.done();
+  }
+];
+
+function nextStep() {
+  tests.shift()();
+}
+
+function listen(event, fn) {
+  window.addEventListener(event, function listener() {
+    window.removeEventListener(event, listener, false);
+    fn();
+  }, false);
+}
+
+function checkAndContinue(sizemode) {
+
+  let windowStates = {
+    "fullscreen": window.STATE_FULLSCREEN,
+    "normal": window.STATE_NORMAL,
+    "maximized": window.STATE_MAXIMIZED
+  };
+
+  setTimeout(function() {
+    is(window.document.documentElement.getAttribute("sizemode"), sizemode, "sizemode attribute should match actual window state");
+    is(window.fullScreen, sizemode == "fullscreen", "window.fullScreen should match actual window state");
+    is(window.windowState, windowStates[sizemode], "window.sizeMode should match actual window state");
+    nextStep();
+  }, 0);
+}
+
+let is = window.opener.wrappedJSObject.is;
+
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+
+</body>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/chrome/test_sizemode_attribute.xul
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+  Test for the sizemode attribute
+  -->
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        sizemode="fullscreen">
+
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>      
+
+<script>
+SimpleTest.waitForExplicitFinish();
+
+newwindow = window.open("sizemode_attribute.xul", "_blank","chrome,resizable=yes");
+
+function done() {
+  newwindow.close();
+  SimpleTest.finish();
+}
+
+</script>
+<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px;"/>
+
+</window>
--- a/dom/tests/mochitest/general/test_consoleAPI.html
+++ b/dom/tests/mochitest/general/test_consoleAPI.html
@@ -21,16 +21,17 @@ function doTest() {
   }
 
   var expectedProps = {
     "log": "function",
     "info": "function",
     "warn": "function",
     "error": "function",
     "debug": "function",
+    "trace": "function",
     "__noSuchMethod__": "function"
   };
 
   var foundProps = 0;
   for (var prop in console) {
     foundProps++;
     is(typeof(console[prop]), expectedProps[prop], "expect console prop " + prop + " exists");
   }
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -9198,17 +9198,17 @@ nsHTMLEditRules::DocumentModified()
 void
 nsHTMLEditRules::DocumentModifiedWorker()
 {
   if (!mHTMLEditor) {
     return;
   }
 
   // DeleteNode below may cause a flush, which could destroy the editor
-  nsAutoRemovableScriptBlocker scriptBlocker;
+  nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
   nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(mHTMLEditor);
   nsCOMPtr<nsISelection> selection;
   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(res, );
 
   // Delete our bogus node, if we have one, since the document might not be
   // empty any more.
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -18,23 +18,16 @@
     <uses-feature android:name="android.hardware.location" android:required="false"/>
     <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
     <uses-feature android:name="android.hardware.touchscreen"/>
 
     <application android:label="@MOZ_APP_DISPLAYNAME@"
 		 android:icon="@drawable/icon"
 		 android:debuggable="true">
 
-        <!-- for network link status -->
-        <receiver android:name="org.mozilla.gecko.GeckoConnectivityReceiver">
-            <intent-filter>
-                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
-            </intent-filter>
-        </receiver>
-
         <activity android:name="App"
                   android:label="@MOZ_APP_DISPLAYNAME@"
                   android:configChanges="keyboard|keyboardHidden|mcc|mnc"
                   android:windowSoftInputMode="stateUnspecified|adjustResize"
                   android:launchMode="singleTask"
                   android:theme="@style/GreyTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -65,20 +65,21 @@ abstract public class GeckoApp
 {
     public static final String ACTION_ALERT_CLICK = "org.mozilla.gecko.ACTION_ALERT_CLICK";
     public static final String ACTION_ALERT_CLEAR = "org.mozilla.gecko.ACTION_ALERT_CLEAR";
 
     public static FrameLayout mainLayout;
     public static GeckoSurfaceView surfaceView;
     public static GeckoApp mAppContext;
     public static boolean mFullscreen = false;
-    public static boolean mStartedEarly = false;
     public static File sGREDir = null;
     static Thread mLibLoadThread = null;
     public Handler mMainHandler;
+    private IntentFilter mConnectivityFilter;
+    private BroadcastReceiver mConnectivityReceiver;
 
     enum LaunchState {PreLaunch, Launching, WaitButton,
                       Launched, GeckoRunning, GeckoExiting};
     private static LaunchState sLaunchState = LaunchState.PreLaunch;
 
 
     static boolean checkLaunchState(LaunchState checkState) {
         synchronized(sLaunchState) {
@@ -156,19 +157,16 @@ abstract public class GeckoApp
                     else
                         showErrorDialog(getString(R.string.error_loading_file));
                     Looper.loop();
                     return;
                 }
 
                 // and then fire us up
                 String env = i.getStringExtra("env0");
-                if (GeckoApp.mStartedEarly) {
-                    GeckoAppShell.putenv("MOZ_APP_RESTART=" + startup_time);
-                }
                 GeckoAppShell.runGecko(getApplication().getPackageResourcePath(),
                                        i.getStringExtra("args"),
                                        i.getDataString());
             }
         }.start();
         return true;
     }
 
@@ -252,16 +250,20 @@ abstract public class GeckoApp
                                                            int id)
                                        {
                                            GeckoApp.this.finish();
                                            System.exit(0);
                                        }
                                    })
                 .show();
         }
+
+        mConnectivityFilter = new IntentFilter();
+        mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mConnectivityReceiver = new GeckoConnectivityReceiver();
     }
 
     boolean IsNewInstall() {
         File appIni = new File(sGREDir, "application.ini");
         return !appIni.exists();
     }
 
     boolean IsUnsupportedDevice() {
@@ -344,32 +346,36 @@ abstract public class GeckoApp
         // has come to the foreground yet; for Gecko, we may want to
         // stop repainting, for example.
 
         // Whatever we do here should be fast, because we're blocking
         // the next activity from showing up until we finish.
 
         // onPause will be followed by either onResume or onStop.
         super.onPause();
+
+        unregisterReceiver(mConnectivityReceiver);
     }
 
     @Override
     public void onResume()
     {
         Log.i("GeckoApp", "resume");
         if (checkLaunchState(LaunchState.GeckoRunning))
             GeckoAppShell.onResume();
         // After an onPause, the activity is back in the foreground.
         // Undo whatever we did in onPause.
         super.onResume();
 
         // Just in case. Normally we start in onNewIntent
         if (checkLaunchState(LaunchState.PreLaunch) ||
             checkLaunchState(LaunchState.Launching))
             onNewIntent(getIntent());
+
+        registerReceiver(mConnectivityReceiver, mConnectivityFilter);
     }
 
     @Override
     public void onStop()
     {
         Log.i("GeckoApp", "stop");
         // We're about to be stopped, potentially in preparation for
         // being destroyed.  We're killable after this point -- as I
--- a/embedding/android/GeckoConnectivityReceiver.java
+++ b/embedding/android/GeckoConnectivityReceiver.java
@@ -53,12 +53,10 @@ public class GeckoConnectivityReceiver
             status = "unknown";
         else if (!info.isConnected())
             status = "down";
         else
             status = "up";
 
         if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
             GeckoAppShell.onChangeNetworkLinkStatus(status);
-        else
-            GeckoApp.mStartedEarly = true;
     }
 }
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -199,17 +199,17 @@ load 406675-1.html
 load 408292.html
 load 408299.html
 load 408450-1.xhtml
 load 409461-1.xhtml
 load 409513.html
 load 410967.html
 load 411870-1.html
 load 412651-1.html
-asserts(6) load 413587-1.svg # Bug 563481?
+load 413587-1.svg
 load 414058-1.html
 load 414175-1.xul
 load 420031-1.html
 load 420213-1.html
 load 420219-1.html
 load 420651-1.xhtml
 load 421203-1.xul
 load 423107-1.xhtml
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8278,20 +8278,18 @@ nsCSSFrameConstructor::EndUpdate()
 {
   if (mUpdateCount == 1) {
     // This is the end of our last update.  Before we decrement
     // mUpdateCount, recalc quotes and counters as needed.
 
     RecalcQuotesAndCounters();
     NS_ASSERTION(mUpdateCount == 1, "Odd update count");
   }
-  // Negative update counts don't make sense
-  if (mUpdateCount > 0) {
-    --mUpdateCount;
-  }
+  NS_ASSERTION(mUpdateCount, "Negative mUpdateCount!");
+  --mUpdateCount;
 }
 
 void
 nsCSSFrameConstructor::RecalcQuotesAndCounters()
 {
   if (mQuotesDirty) {
     mQuotesDirty = PR_FALSE;
     mQuoteList.RecalcAll();
--- a/layout/base/tests/TestPoisonArea.cpp
+++ b/layout/base/tests/TestPoisonArea.cpp
@@ -219,36 +219,26 @@ static const ia64_instr _return_instr =
 // Miscellaneous Windows/Unix portability gumph
 
 #ifdef _WIN32
 // Uses of this function deliberately leak the string.
 static LPSTR
 StrW32Error(DWORD errcode)
 {
   LPSTR errmsg;
-
-#ifndef WINCE
   FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                  FORMAT_MESSAGE_FROM_SYSTEM |
                  FORMAT_MESSAGE_IGNORE_INSERTS,
                  NULL, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  (LPSTR) &errmsg, 0, NULL);
 
   // FormatMessage puts an unwanted newline at the end of the string
   size_t n = strlen(errmsg)-1;
   while (errmsg[n] == '\r' || errmsg[n] == '\n') n--;
   errmsg[n+1] = '\0';
-#else
-  // CE doesn't have FormatMessageA so we just stringify the error code.
-  // Use LocalAlloc for consistency with the regular Windows code path.
-  // "code \0" is 6 bytes, and a 32-bit number might need 10 more.
-  errmsg = (LPSTR)LocalAlloc(LMEM_FIXED, 16);
-  _snprintf(errmsg, 16, "code %u", errcode);
-#endif
-
   return errmsg;
 }
 #define LastErrMsg() (StrW32Error(GetLastError()))
 
 // Because we use VirtualAlloc in MEM_RESERVE mode, the "page size" we want
 // is the allocation granularity.
 static SYSTEM_INFO _sinfo;
 #undef PAGESIZE
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -47,18 +47,17 @@ fails == canvas-drawImage-slice-1b.html 
 fails-if(d2d) == img-simple-3.html  img-simple-3-ref.html # bug 633072
 == img-simple-4.html  lime100x100-ref.html
 fails-if(d2d) == img-simple-5.html  img-simple-5-ref.html # bug 633072
 == img-simple-6.html  lime100x100-ref.html
 fails-if(d2d) == img-simple-7.html  img-simple-7-ref.html # bug 633072
 
 # Test with mix of <html:img> and <svg:image> referring to the same images,
 # with a variety of preserveAspectRatio values in play.
-# NOTE: The reference cases in this test triggers 72 assertions (bug 563481)
-fails-if(Android) asserts(72) == img-and-image-1.html img-and-image-1-ref.svg
+fails-if(Android) == img-and-image-1.html img-and-image-1-ref.svg
 
 # More complex <img> tests
 == img-content-outside-viewBox-1.html img-content-outside-viewBox-1-ref.html
 == img-dyn-1.html img-dyn-1-ref.html
 == img-foreignObject-1.html lime100x100-ref.html
 == img-foreignObject-embed-1.html lime100x100-ref.html
 == img-foreignObject-embed-2.html lime100x100-ref.html
 
--- a/layout/reftests/svg/image/reftest.list
+++ b/layout/reftests/svg/image/reftest.list
@@ -14,16 +14,14 @@
 == image-x-01.svg             image-x-01-ref.svg
 == image-xy-01.svg            image-xy-01-ref.svg
 == image-y-01.svg             image-y-01-ref.svg
 == image-zoom-01a.svg         image-zoom-01-ref.svg
 == image-zoom-01b.svg         image-zoom-01-ref.svg
 == image-zoom-02.svg          image-zoom-02-ref.svg
 
 # Tests for <image> with preserveAspectRatio
-# NOTE: The reference cases in the following tests trigger 20+ assertions each
-# (1 per <symbol> element), due to bug 563481.
-asserts(20) == image-preserveAspectRatio-01-raster.svg image-preserveAspectRatio-01-ref.svg
-asserts(20) == image-preserveAspectRatio-01-svg.svg    image-preserveAspectRatio-01-ref.svg
-asserts(20) == image-preserveAspectRatio-02-raster.svg image-preserveAspectRatio-02-ref.svg
-asserts(20) == image-preserveAspectRatio-02-svg.svg    image-preserveAspectRatio-02-ref.svg
-asserts(21) == image-preserveAspectRatio-03.svg        image-preserveAspectRatio-03-ref.svg
-asserts(21) == image-preserveAspectRatio-04.svg        image-preserveAspectRatio-04-ref.svg
+== image-preserveAspectRatio-01-raster.svg image-preserveAspectRatio-01-ref.svg
+== image-preserveAspectRatio-01-svg.svg    image-preserveAspectRatio-01-ref.svg
+== image-preserveAspectRatio-02-raster.svg image-preserveAspectRatio-02-ref.svg
+== image-preserveAspectRatio-02-svg.svg    image-preserveAspectRatio-02-ref.svg
+== image-preserveAspectRatio-03.svg        image-preserveAspectRatio-03-ref.svg
+== image-preserveAspectRatio-04.svg        image-preserveAspectRatio-04-ref.svg
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -178,17 +178,17 @@ random-if(gtk2Widget) == objectBoundingB
 == selector-01.svg pass.svg
 == stroke-width-percentage-01.svg pass.svg
 == style-property-not-on-script-element-01.svg pass.svg
 == style-without-type-attribute.svg pass.svg
 == svg-in-foreignObject-01.xhtml svg-in-foreignObject-01-ref.xhtml
 == svg-in-foreignObject-02.xhtml svg-in-foreignObject-01-ref.xhtml # reuse -01-ref.xhtml
 random-if(gtk2Widget) == text-font-weight-01.svg text-font-weight-01-ref.svg # bug 386713
 fails-if(Android) == switch-01.svg pass.svg # bug 652050
-asserts(3) == symbol-01.svg symbol-01-ref.svg # see bug 563481
+== symbol-01.svg symbol-01-ref.svg
 == text-gradient-01.svg text-gradient-01-ref.svg
 random-if(winWidget) == text-gradient-02.svg text-gradient-02-ref.svg # see bug 590101
 == text-gradient-03.svg pass.svg
 == text-in-link-01.svg text-in-link-01-ref.svg
 == text-in-link-02.svg text-in-link-02-ref.svg
 == text-in-link-03.svg text-in-link-03-ref.svg
 # Tests for bug 546813: sanity-check using HTML text, then test SVG behavior.
 fails-if(Android) != text-language-00.xhtml text-language-00-ref.xhtml
--- a/layout/reftests/svg/smil/reftest.list
+++ b/layout/reftests/svg/smil/reftest.list
@@ -125,17 +125,17 @@ fails == anim-fillcolor-1.svg      anim-
 == anim-feImage-preserveAspectRatio-01.svg lime.svg
 == anim-svg-preserveAspectRatio-01.svg lime.svg
 
 # animate some string attributes:
 == anim-filter-href-01.svg lime.svg
 == anim-gradient-href-01.svg lime.svg
 == anim-image-href-01.svg lime.svg
 == anim-pattern-href-01.svg lime.svg
-asserts(9) == anim-use-href-01.svg lime.svg  # the asserts here are bug 563481
+== anim-use-href-01.svg lime.svg
 
 # animate the class attribute
 == anim-class-01.svg lime.svg
 == anim-class-02.svg lime.svg
 == anim-class-03.svg lime.svg
 == anim-class-04.svg anim-class-04-ref.svg
 
 # animate with some paint server values
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -707,17 +707,17 @@ Declaration::GetValue(nsCSSProperty aPro
         }
       }
       break;
     }
 #ifdef MOZ_CSS_ANIMATIONS
     case eCSSProperty_animation: {
       const nsCSSProperty* subprops =
         nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
-      static const size_t numProps = 8;
+      static const size_t numProps = 7;
       NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
                         "unexpected number of subproperties");
       const nsCSSValue* values[numProps];
       const nsCSSValueList* lists[numProps];
 
       for (PRUint32 i = 0; i < numProps; ++i) {
         values[i] = data->ValueFor(subprops[i]);
         NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -574,19 +574,19 @@ protected:
   PRBool ParseColorOpacity(PRUint8& aOpacity);
   PRBool ParseEnum(nsCSSValue& aValue, const PRInt32 aKeywordTable[]);
   PRBool ParseVariant(nsCSSValue& aValue,
                       PRInt32 aVariantMask,
                       const PRInt32 aKeywordTable[]);
   PRBool ParseNonNegativeVariant(nsCSSValue& aValue,
                                  PRInt32 aVariantMask,
                                  const PRInt32 aKeywordTable[]);
-  PRBool ParsePositiveNonZeroVariant(nsCSSValue& aValue,
-                                     PRInt32 aVariantMask,
-                                     const PRInt32 aKeywordTable[]);
+  PRBool ParseOneOrLargerVariant(nsCSSValue& aValue,
+                                 PRInt32 aVariantMask,
+                                 const PRInt32 aKeywordTable[]);
   PRBool ParseCounter(nsCSSValue& aValue);
   PRBool ParseAttr(nsCSSValue& aValue);
   PRBool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
   PRBool TranslateDimension(nsCSSValue& aValue, PRInt32 aVariantMask,
                             float aNumber, const nsString& aUnit);
   PRBool ParseImageRect(nsCSSValue& aImage);
   PRBool ParseElement(nsCSSValue& aValue);
   PRBool ParseColorStop(nsCSSValueGradient* aGradient);
@@ -4465,29 +4465,35 @@ CSSParserImpl::ParseNonNegativeVariant(n
   return PR_FALSE;
 }
 
 // Note that callers passing VARIANT_CALC in aVariantMask will get
 // full-range parsing inside the calc() expression, and the code that
 // computes the calc will be required to clamp the resulting value to an
 // appropriate range.
 PRBool
-CSSParserImpl::ParsePositiveNonZeroVariant(nsCSSValue& aValue,
-                                           PRInt32 aVariantMask,
-                                           const PRInt32 aKeywordTable[])
+CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
+                                       PRInt32 aVariantMask,
+                                       const PRInt32 aKeywordTable[])
 {
   // The variant mask must only contain non-numeric variants or the ones
   // that we specifically handle.
   NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
+                                      VARIANT_NUMBER |
                                       VARIANT_INTEGER)) == 0,
                     "need to update code below to handle additional variants");
 
   if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
     if (aValue.GetUnit() == eCSSUnit_Integer) {
-      if (aValue.GetIntValue() <= 0) {
+      if (aValue.GetIntValue() < 1) {
+        UngetToken();
+        return PR_FALSE;
+      }
+    } else if (eCSSUnit_Number == aValue.GetUnit()) {
+      if (aValue.GetFloatValue() < 1.0f) {
         UngetToken();
         return PR_FALSE;
       }
     }
     return PR_TRUE;
   }
   return PR_FALSE;
 }
@@ -5670,27 +5676,18 @@ CSSParserImpl::ParseSingleValueProperty(
   const PRInt32 *kwtable = nsCSSProps::kKeywordTableTable[aPropID];
   switch (nsCSSProps::ValueRestrictions(aPropID)) {
     default:
       NS_ABORT_IF_FALSE(PR_FALSE, "should not be reached");
     case 0:
       return ParseVariant(aValue, variant, kwtable);
     case CSS_PROPERTY_VALUE_NONNEGATIVE:
       return ParseNonNegativeVariant(aValue, variant, kwtable);
-    case CSS_PROPERTY_VALUE_POSITIVE_NONZERO:
-      return ParsePositiveNonZeroVariant(aValue, variant, kwtable);
     case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
-      NS_ABORT_IF_FALSE((variant &
-                         ~(VARIANT_ALL_NONNUMERIC | VARIANT_NUMBER)) == 0,
-                        "need to update code to handle additional variants");
-      if (!ParseVariant(aValue, variant, kwtable))
-        return PR_FALSE;
-      // Enforce the restriction that the value is greater than 1.
-      return aValue.GetUnit() != eCSSUnit_Number || 
-             aValue.GetFloatValue() >= 1.0f;
+      return ParseOneOrLargerVariant(aValue, variant, kwtable);
   }
 }
 
 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
 struct NS_STACK_CLASS ExtractFirstFamilyData {
   nsAutoString mFamilyName;
   PRBool mGood;
   ExtractFirstFamilyData() : mFamilyName(), mGood(PR_FALSE) {}
@@ -8193,17 +8190,17 @@ CSSParserImpl::ParseTransitionStepTiming
 {
   NS_ASSERTION(!mHavePushBack &&
                mToken.mType == eCSSToken_Function &&
                mToken.mIdent.LowerCaseEqualsLiteral("steps"),
                "unexpected initial state");
 
   nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
 
-  if (!ParsePositiveNonZeroVariant(val->Item(0), VARIANT_INTEGER, nsnull)) {
+  if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nsnull)) {
     return PR_FALSE;
   }
 
   PRInt32 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
   if (ExpectSymbol(',', PR_TRUE)) {
     if (!GetToken(PR_TRUE)) {
       return PR_FALSE;
     }
@@ -8262,17 +8259,17 @@ CSSParserImpl::ParseAnimationOrTransitio
   // additional properties
   if (ParseVariant(tempValue, VARIANT_INHERIT, nsnull)) {
     for (PRUint32 i = 0; i < aNumProperties; ++i) {
       AppendValue(aProperties[i], tempValue);
     }
     return eParseAnimationOrTransitionShorthand_Inherit;
   }
 
-  static const size_t maxNumProperties = 8;
+  static const size_t maxNumProperties = 7;
   NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties,
                     "can't handle this many properties");
   nsCSSValueList *cur[maxNumProperties];
   PRBool parsedProperty[maxNumProperties];
 
   for (size_t i = 0; i < aNumProperties; ++i) {
     cur[i] = nsnull;
   }
@@ -8423,17 +8420,16 @@ CSSParserImpl::ParseAnimation()
     // Must check 'animation-delay' after 'animation-duration', since
     // that's our assumption about what the spec means for the shorthand
     // syntax (the first time given is the duration, and the second
     // given is the delay).
     eCSSProperty_animation_delay,
     eCSSProperty_animation_direction,
     eCSSProperty_animation_fill_mode,
     eCSSProperty_animation_iteration_count,
-    eCSSProperty_animation_play_state,
     // Must check 'animation-name' after 'animation-timing-function',
     // 'animation-direction', 'animation-fill-mode',
     // 'animation-iteration-count', and 'animation-play-state' since
     // 'animation-property' accepts any keyword.
     eCSSProperty_animation_name
   };
   static const PRUint32 numProps = NS_ARRAY_LENGTH(kAnimationProperties);
   // this is a shorthand property that accepts -property, -delay,
@@ -8443,18 +8439,17 @@ CSSParserImpl::ParseAnimation()
   nsCSSValue initialValues[numProps];
   initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
   initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
                                eCSSUnit_Enumerated);
   initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
   initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated);
   initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated);
   initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
-  initialValues[6].SetIntValue(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING, eCSSUnit_Enumerated);
-  initialValues[7].SetNoneValue();
+  initialValues[6].SetNoneValue();
 
   nsCSSValue values[numProps];
 
   ParseAnimationOrTransitionShorthandResult spres =
     ParseAnimationOrTransitionShorthand(kAnimationProperties,
                                         initialValues, values, numProps);
   if (spres != eParseAnimationOrTransitionShorthand_Values) {
     return spres != eParseAnimationOrTransitionShorthand_Error;
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -302,46 +302,50 @@ CSS_PROP_SHORTHAND(
     _moz_outline_radius,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadius),
     CSS_PROPERTY_PARSE_FUNCTION)
 CSS_PROP_OUTLINE(
     -moz-outline-radius-topleft,
     _moz_outline_radius_topLeft,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusTopleft),
     CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     nsnull,
     offsetof(nsStyleOutline, mOutlineRadius),
     eStyleAnimType_Corner_TopLeft)
 CSS_PROP_OUTLINE(
     -moz-outline-radius-topright,
     _moz_outline_radius_topRight,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusTopright),
     CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     nsnull,
     offsetof(nsStyleOutline, mOutlineRadius),
     eStyleAnimType_Corner_TopRight)
 CSS_PROP_OUTLINE(
     -moz-outline-radius-bottomright,
     _moz_outline_radius_bottomRight,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusBottomright),
     CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     nsnull,
     offsetof(nsStyleOutline, mOutlineRadius),
     eStyleAnimType_Corner_BottomRight)
 CSS_PROP_OUTLINE(
     -moz-outline-radius-bottomleft,
     _moz_outline_radius_bottomLeft,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusBottomleft),
     CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     nsnull,
     offsetof(nsStyleOutline, mOutlineRadius),
     eStyleAnimType_Corner_BottomLeft)
 CSS_PROP_TEXT(
     -moz-tab-size,
     _moz_tab_size,
@@ -553,16 +557,17 @@ CSS_PROP_BACKGROUND(
     eStyleAnimType_None)
 CSS_PROP_BACKGROUND(
     background-size,
     background_size,
     BackgroundSize,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
         CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     kBackgroundSizeKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_Custom)
 CSS_PROP_DISPLAY(
     -moz-binding,
     binding,
@@ -987,17 +992,18 @@ CSS_PROP_BORDER(
     kBoxPropSourceKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 #endif
 CSS_PROP_TABLEBORDER(
     border-spacing,
     border_spacing,
     BorderSpacing,
-    CSS_PROPERTY_PARSE_FUNCTION,
+    CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_VALUE_NONNEGATIVE,
     0,
     nsnull,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_Custom) // XXX bug 3935
 CSS_PROP_SHORTHAND(
     -moz-border-start,
     border_start,
     CSS_PROP_DOMPROP_PREFIXED(BorderStart),
@@ -1118,49 +1124,53 @@ CSS_PROP_SHORTHAND(
     BorderRadius,
     CSS_PROPERTY_PARSE_FUNCTION)
 CSS_PROP_BORDER(
     border-top-left-radius,
     border_top_left_radius,
     BorderTopLeftRadius,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     nsnull,
     offsetof(nsStyleBorder, mBorderRadius),
     eStyleAnimType_Corner_TopLeft)
 CSS_PROP_BORDER(
     border-top-right-radius,
     border_top_right_radius,
     BorderTopRightRadius,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     nsnull,
     offsetof(nsStyleBorder, mBorderRadius),
     eStyleAnimType_Corner_TopRight)
 CSS_PROP_BORDER(
     border-bottom-right-radius,
     border_bottom_right_radius,
     BorderBottomRightRadius,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     nsnull,
     offsetof(nsStyleBorder, mBorderRadius),
     eStyleAnimType_Corner_BottomRight)
 CSS_PROP_BORDER(
     border-bottom-left-radius,
     border_bottom_left_radius,
     BorderBottomLeftRadius,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     0,
     nsnull,
     offsetof(nsStyleBorder, mBorderRadius),
     eStyleAnimType_Corner_BottomLeft)
 CSS_PROP_POSITION(
     bottom,
     bottom,
@@ -1174,16 +1184,17 @@ CSS_PROP_POSITION(
 CSS_PROP_BORDER(
     box-shadow,
     box_shadow,
     BoxShadow,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER |
         CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
+        // NOTE: some components must be nonnegative
     0,
     kBoxShadowTypeKTable,
     offsetof(nsStyleBorder, mBoxShadow),
     eStyleAnimType_Shadow)
 CSS_PROP_POSITION(
     -moz-box-sizing,
     box_sizing,
     CSS_PROP_DOMPROP_PREFIXED(BoxSizing),
@@ -1232,17 +1243,17 @@ CSS_PROP_COLOR(
     eStyleAnimType_Color)
 CSS_PROP_COLUMN(
     -moz-column-count,
     _moz_column_count,
     CSS_PROP_DOMPROP_PREFIXED(ColumnCount),
     CSS_PROPERTY_PARSE_VALUE |
         // Need to reject 0 in addition to negatives.  If we accept 0, we
         // need to change NS_STYLE_COLUMN_COUNT_AUTO to something else.
-        CSS_PROPERTY_VALUE_POSITIVE_NONZERO,
+        CSS_PROPERTY_VALUE_AT_LEAST_ONE,
     VARIANT_AHI,
     nsnull,
     offsetof(nsStyleColumn, mColumnCount),
     eStyleAnimType_Custom)
 CSS_PROP_COLUMN(
     -moz-column-width,
     _moz_column_width,
     CSS_PROP_DOMPROP_PREFIXED(ColumnWidth),
@@ -1431,16 +1442,17 @@ CSS_PROP_FONT(
     // but setting it requires setting mFont.size as well.
     offsetof(nsStyleFont, mSize),
     eStyleAnimType_nscoord)
 CSS_PROP_FONT(
     font-size-adjust,
     font_size_adjust,
     FontSizeAdjust,
     CSS_PROPERTY_PARSE_VALUE |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
     VARIANT_HON | VARIANT_SYSFONT,
     nsnull,
     offsetof(nsStyleFont, mFont.sizeAdjust),
     eStyleAnimType_float)
 CSS_PROP_FONT(
     font-stretch,
     font_stretch,
@@ -1473,16 +1485,17 @@ CSS_PROP_FONT(
     eStyleAnimType_EnumU8)
 CSS_PROP_FONT(
     font-weight,
     font_weight,
     FontWeight,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_PARSER_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
+        // NOTE: This property has range restrictions on interpolation!
     0,
     kFontWeightKTable,
     offsetof(nsStyleFont, mFont.weight),
     eStyleAnimType_Custom)
 CSS_PROP_UIRESET(
     -moz-force-broken-image-icon,
     force_broken_image_icon,
     CSS_PROP_DOMPROP_PREFIXED(ForceBrokenImageIcon),
@@ -1800,17 +1813,17 @@ CSS_PROP_DISPLAY(
     nsnull,
     offsetof(nsStyleDisplay, mOpacity),
     eStyleAnimType_float) // XXX bug 3935
 CSS_PROP_BACKENDONLY(
     orphans,
     orphans,
     Orphans,
     CSS_PROPERTY_PARSE_VALUE |
-        CSS_PROPERTY_VALUE_POSITIVE_NONZERO,
+        CSS_PROPERTY_VALUE_AT_LEAST_ONE,
     VARIANT_HI,
     nsnull)
 CSS_PROP_SHORTHAND(
     outline,
     outline,
     Outline,
     CSS_PROPERTY_PARSE_FUNCTION)
 CSS_PROP_OUTLINE(
@@ -2190,16 +2203,17 @@ CSS_PROP_TEXT(
 CSS_PROP_TEXT(
     text-shadow,
     text_shadow,
     TextShadow,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
         CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED,
+        // NOTE: some components must be nonnegative
     0,
     nsnull,
     offsetof(nsStyleText, mTextShadow),
     eStyleAnimType_Shadow)
 CSS_PROP_TEXT(
     text-transform,
     text_transform,
     TextTransform,
@@ -2360,17 +2374,17 @@ CSS_PROP_TEXT(
     kWhitespaceKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_BACKENDONLY(
     widows,
     widows,
     Widows,
     CSS_PROPERTY_PARSE_VALUE |
-        CSS_PROPERTY_VALUE_POSITIVE_NONZERO,
+        CSS_PROPERTY_VALUE_AT_LEAST_ONE,
     VARIANT_HI,
     nsnull)
 CSS_PROP_POSITION(
     width,
     width,
     Width,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_NONNEGATIVE |
@@ -2471,17 +2485,17 @@ CSS_PROP_XUL(
     kBoxPackKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None) // XXX bug 3935
 CSS_PROP_XUL(
     -moz-box-ordinal-group,
     box_ordinal_group,
     CSS_PROP_DOMPROP_PREFIXED(BoxOrdinalGroup),
     CSS_PROPERTY_PARSE_VALUE |
-        CSS_PROPERTY_VALUE_POSITIVE_NONZERO,
+        CSS_PROPERTY_VALUE_AT_LEAST_ONE,
     VARIANT_HI,
     nsnull,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_XUL(
     -moz-stack-sizing,
     stack_sizing,
     CSS_PROP_DOMPROP_PREFIXED(StackSizing),
@@ -2492,37 +2506,42 @@ CSS_PROP_XUL(
     eStyleAnimType_None)
 
 #ifdef MOZ_MATHML
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_FONT(
     -moz-script-level,
     script_level,
     ScriptLevel,
+    // REVIEW: no range restriction?
+    // NOTE: CSSParserImpl::ParseSingleValueProperty only accepts this
+    // property when mUnsafeRulesEnabled is set.
     CSS_PROPERTY_PARSE_VALUE,
     // script-level can take Integer or Number values, but only Integer
     // ("relative") values can be specified in a style sheet.
     VARIANT_HI,
     nsnull,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_FONT(
     -moz-script-size-multiplier,
     script_size_multiplier,
     ScriptSizeMultiplier,
-    CSS_PROPERTY_PARSE_VALUE,
+    // REVIEW: no range restriction?
+    CSS_PROPERTY_PARSE_INACCESSIBLE,
     0,
     nsnull,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_FONT(
     -moz-script-min-size,
     script_min_size,
     ScriptMinSize,
-    CSS_PROPERTY_PARSE_VALUE,
+    // REVIEW: no range restriction?
+    CSS_PROPERTY_PARSE_INACCESSIBLE,
     0,
     nsnull,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 #endif
 #endif
 
 CSS_PROP_SVGRESET(
@@ -2720,16 +2739,17 @@ CSS_PROP_SVG(
     offsetof(nsStyleSVG, mStroke),
     eStyleAnimType_PaintServer)
 CSS_PROP_SVG(
     stroke-dasharray,
     stroke_dasharray,
     StrokeDasharray,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+        // NOTE: Internal values have range restrictions.
     0,
     nsnull,
     CSS_PROP_NO_OFFSET, /* property stored in 2 separate members */
     eStyleAnimType_Custom)
 CSS_PROP_SVG(
     stroke-dashoffset,
     stroke_dashoffset,
     StrokeDashoffset,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1573,17 +1573,16 @@ const PRUint32 nsCSSProps::kFlagsTable[e
 #ifdef MOZ_CSS_ANIMATIONS
 static const nsCSSProperty gAnimationSubpropTable[] = {
   eCSSProperty_animation_duration,
   eCSSProperty_animation_timing_function,
   eCSSProperty_animation_delay,
   eCSSProperty_animation_direction,
   eCSSProperty_animation_fill_mode,
   eCSSProperty_animation_iteration_count,
-  eCSSProperty_animation_play_state,
   // List animation-name last so we serialize it last, in case it has
   // a value that conflicts with one of the other properties.  (See
   // how Declaration::GetValue serializes 'animation'.
   eCSSProperty_animation_name,
   eCSSProperty_UNKNOWN
 };
 #endif
 
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -108,21 +108,18 @@
 PR_STATIC_ASSERT((CSS_PROPERTY_PARSE_PROPERTY_MASK &
                   CSS_PROPERTY_VALUE_PARSER_FUNCTION) == 0);
 
 #define CSS_PROPERTY_VALUE_RESTRICTION_MASK       (3<<13)
 // The parser (in particular, CSSParserImpl::ParseSingleValueProperty)
 // should enforce that the value of this property must be 0 or larger.
 #define CSS_PROPERTY_VALUE_NONNEGATIVE            (1<<13)
 // The parser (in particular, CSSParserImpl::ParseSingleValueProperty)
-// should enforce that the value of this property must be greater than 0.
-#define CSS_PROPERTY_VALUE_POSITIVE_NONZERO       (2<<13)
-// The parser (in particular, CSSParserImpl::ParseSingleValueProperty)
 // should enforce that the value of this property must be 1 or larger.
-#define CSS_PROPERTY_VALUE_AT_LEAST_ONE           (3<<13)
+#define CSS_PROPERTY_VALUE_AT_LEAST_ONE           (2<<13)
 
 // NOTE: next free bit is (1<<15)
 
 /**
  * Types of animatable values.
  */
 enum nsStyleAnimType {
   // requires a custom implementation in
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -712,49 +712,84 @@ inline PRUint8 ClampColor(double aColor)
 {
   if (aColor >= MAX_PACKED_COLOR_COMPONENT)
     return MAX_PACKED_COLOR_COMPONENT;
   if (aColor <= 0.0)
     return 0;
   return NSToIntRound(aColor);
 }
 
+template <typename T>
+T
+RestrictValue(PRUint32 aRestrictions, T aValue)
+{
+  T result = aValue;
+  switch (aRestrictions) {
+    case 0:
+      break;
+    case CSS_PROPERTY_VALUE_NONNEGATIVE:
+      if (result < 0) {
+        result = 0;
+      }
+      break;
+    case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
+      if (result < 1) {
+        result = 1;
+      }
+      break;
+    default:
+      NS_ABORT_IF_FALSE(PR_FALSE, "bad value restriction");
+      break;
+  }
+  return result;
+}
+
+template <typename T>
+T
+RestrictValue(nsCSSProperty aProperty, T aValue)
+{
+  return RestrictValue(nsCSSProps::ValueRestrictions(aProperty), aValue);
+}
+
 static inline void
 AddCSSValuePixel(double aCoeff1, const nsCSSValue &aValue1,
                  double aCoeff2, const nsCSSValue &aValue2,
-                 nsCSSValue &aResult)
+                 nsCSSValue &aResult, PRUint32 aValueRestrictions = 0)
 {
   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
-  aResult.SetFloatValue(aCoeff1 * aValue1.GetFloatValue() +
-                        aCoeff2 * aValue2.GetFloatValue(),
+  aResult.SetFloatValue(RestrictValue(aValueRestrictions,
+                                      aCoeff1 * aValue1.GetFloatValue() +
+                                      aCoeff2 * aValue2.GetFloatValue()),
                         eCSSUnit_Pixel);
 }
 
 static inline void
 AddCSSValueNumber(double aCoeff1, const nsCSSValue &aValue1,
                   double aCoeff2, const nsCSSValue &aValue2,
-                  nsCSSValue &aResult)
+                  nsCSSValue &aResult, PRUint32 aValueRestrictions = 0)
 {
   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
-  aResult.SetFloatValue(aCoeff1 * aValue1.GetFloatValue() +
-                        aCoeff2 * aValue2.GetFloatValue(),
+  aResult.SetFloatValue(RestrictValue(aValueRestrictions,
+                                      aCoeff1 * aValue1.GetFloatValue() +
+                                      aCoeff2 * aValue2.GetFloatValue()),
                         eCSSUnit_Number);
 }
 
 static inline void
 AddCSSValuePercent(double aCoeff1, const nsCSSValue &aValue1,
                    double aCoeff2, const nsCSSValue &aValue2,
-                   nsCSSValue &aResult)
+                   nsCSSValue &aResult, PRUint32 aValueRestrictions = 0)
 {
   NS_ABORT_IF_FALSE(aValue1.GetUnit() == eCSSUnit_Percent, "unexpected unit");
   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Percent, "unexpected unit");
-  aResult.SetPercentValue(aCoeff1 * aValue1.GetPercentValue() +
-                          aCoeff2 * aValue2.GetPercentValue());
+  aResult.SetPercentValue(RestrictValue(aValueRestrictions,
+                                        aCoeff1 * aValue1.GetPercentValue() +
+                                        aCoeff2 * aValue2.GetPercentValue()));
 }
 
 // Add two canonical-form calc values (eUnit_Calc) to make another
 // canonical-form calc value.
 static void
 AddCSSValueCanonicalCalc(double aCoeff1, const nsCSSValue &aValue1,
                          double aCoeff2, const nsCSSValue &aValue2,
                          nsCSSValue &aResult)
@@ -793,17 +828,19 @@ AddShadowItems(double aCoeff1, const nsC
   NS_ABORT_IF_FALSE(aValue2.GetUnit() == eCSSUnit_Array,
                     "wrong unit");
   nsCSSValue::Array *array1 = aValue1.GetArrayValue();
   nsCSSValue::Array *array2 = aValue2.GetArrayValue();
   nsRefPtr<nsCSSValue::Array> resultArray = nsCSSValue::Array::Create(6);
 
   for (size_t i = 0; i < 4; ++i) {
     AddCSSValuePixel(aCoeff1, array1->Item(i), aCoeff2, array2->Item(i),
-                     resultArray->Item(i));
+                     resultArray->Item(i),
+                     // blur radius must be nonnegative
+                     (i == 2) ? CSS_PROPERTY_VALUE_NONNEGATIVE : 0);
   }
 
   const nsCSSValue& color1 = array1->Item(4);
   const nsCSSValue& color2 = array2->Item(4);
   const nsCSSValue& inset1 = array1->Item(5);
   const nsCSSValue& inset2 = array2->Item(5);
   if (color1.GetUnit() != color2.GetUnit() ||
       inset1.GetUnit() != inset2.GetUnit()) {
@@ -1354,16 +1391,21 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
       return PR_FALSE;
 
     case eUnit_Enumerated:
       switch (aProperty) {
         case eCSSProperty_font_stretch: {
           // Animate just like eUnit_Integer.
           PRInt32 result = NS_floor(aCoeff1 * double(aValue1.GetIntValue()) +
                                     aCoeff2 * double(aValue2.GetIntValue()));
+          if (result < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED) {
+            result = NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED;
+          } else if (result > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
+            result = NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED;
+          }
           aResultValue.SetIntValue(result, eUnit_Enumerated);
           return PR_TRUE;
         }
         default:
           return PR_FALSE;
       }
     case eUnit_Visibility: {
       PRInt32 val1 = aValue1.GetIntValue() == NS_STYLE_VISIBILITY_VISIBLE;
@@ -1377,36 +1419,43 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
     case eUnit_Integer: {
       // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
       // says we should use floor
       PRInt32 result = NS_floor(aCoeff1 * double(aValue1.GetIntValue()) +
                                 aCoeff2 * double(aValue2.GetIntValue()));
       if (aProperty == eCSSProperty_font_weight) {
         NS_ASSERTION(result > 0, "unexpected value");
         result -= result % 100;
+        if (result < 100) {
+          result = 100;
+        } else if (result > 900) {
+          result = 900;
+        }
+      } else {
+        result = RestrictValue(aProperty, result);
       }
       aResultValue.SetIntValue(result, eUnit_Integer);
       return PR_TRUE;
     }
     case eUnit_Coord: {
-      aResultValue.SetCoordValue(NSToCoordRound(
+      aResultValue.SetCoordValue(RestrictValue(aProperty, NSToCoordRound(
         aCoeff1 * aValue1.GetCoordValue() +
-        aCoeff2 * aValue2.GetCoordValue()));
+        aCoeff2 * aValue2.GetCoordValue())));
       return PR_TRUE;
     }
     case eUnit_Percent: {
-      aResultValue.SetPercentValue(
+      aResultValue.SetPercentValue(RestrictValue(aProperty,
         aCoeff1 * aValue1.GetPercentValue() +
-        aCoeff2 * aValue2.GetPercentValue());
+        aCoeff2 * aValue2.GetPercentValue()));
       return PR_TRUE;
     }
     case eUnit_Float: {
-      aResultValue.SetFloatValue(
+      aResultValue.SetFloatValue(RestrictValue(aProperty,
         aCoeff1 * aValue1.GetFloatValue() +
-        aCoeff2 * aValue2.GetFloatValue());
+        aCoeff2 * aValue2.GetFloatValue()));
       return PR_TRUE;
     }
     case eUnit_Color: {
       nscolor color1 = aValue1.GetColorValue();
       nscolor color2 = aValue2.GetColorValue();
       // FIXME (spec): The CSS transitions spec doesn't say whether
       // colors are premultiplied, but things work better when they are,
       // so use premultiplication.  Spec issue is still open per
@@ -1473,27 +1522,28 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
           unit[0] == eCSSUnit_URL) {
         return PR_FALSE;
       }
 
       nsAutoPtr<nsCSSValuePair> result(new nsCSSValuePair);
       static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
         &nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
       };
+      PRUint32 restrictions = nsCSSProps::ValueRestrictions(aProperty);
       for (PRUint32 i = 0; i < 2; ++i) {
         nsCSSValue nsCSSValuePair::*member = pairValues[i];
         switch (unit[i]) {
           case eCSSUnit_Pixel:
             AddCSSValuePixel(aCoeff1, pair1->*member, aCoeff2, pair2->*member,
-                             result->*member);
+                             result->*member, restrictions);
             break;
           case eCSSUnit_Percent:
             AddCSSValuePercent(aCoeff1, pair1->*member,
                                aCoeff2, pair2->*member,
-                               result->*member);
+                               result->*member, restrictions);
             break;
           case eCSSUnit_Calc:
             AddCSSValueCanonicalCalc(aCoeff1, pair1->*member,
                                      aCoeff2, pair2->*member,
                                      result->*member);
             break;
           default:
             NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
@@ -1501,16 +1551,18 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
         }
       }
 
       aResultValue.SetAndAdoptCSSValuePairValue(result.forget(),
                                                 eUnit_CSSValuePair);
       return PR_TRUE;
     }
     case eUnit_CSSRect: {
+      NS_ABORT_IF_FALSE(nsCSSProps::ValueRestrictions(aProperty) == 0,
+                        "must add code for handling value restrictions");
       const nsCSSRect *rect1 = aValue1.GetCSSRectValue();
       const nsCSSRect *rect2 = aValue2.GetCSSRectValue();
       if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
           rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
           rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
           rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
         // At least until we have calc()
         return PR_FALSE;
@@ -1583,19 +1635,21 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
         nsCSSValueList *item = new nsCSSValueList;
         if (!item) {
           return PR_FALSE;
         }
         *resultTail = item;
         resultTail = &item->mNext;
 
         if (v1.GetUnit() == eCSSUnit_Number) {
-          AddCSSValueNumber(aCoeff1, v1, aCoeff2, v2, item->mValue);
+          AddCSSValueNumber(aCoeff1, v1, aCoeff2, v2, item->mValue,
+                            CSS_PROPERTY_VALUE_NONNEGATIVE);
         } else {
-          AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, item->mValue);
+          AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, item->mValue,
+                             CSS_PROPERTY_VALUE_NONNEGATIVE);
         }
 
         list1 = list1->mNext;
         if (!list1) {
           list1 = aValue1.GetCSSValueListValue();
         }
         list2 = list2->mNext;
         if (!list2) {
@@ -1730,31 +1784,32 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
         }
         *resultTail = item;
         resultTail = &item->mNext;
 
         static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
           &nsCSSValuePairList::mXValue,
           &nsCSSValuePairList::mYValue,
         };
+        PRUint32 restrictions = nsCSSProps::ValueRestrictions(aProperty);
         for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(pairListValues); ++i) {
           const nsCSSValue &v1 = list1->*(pairListValues[i]);
           const nsCSSValue &v2 = list2->*(pairListValues[i]);
           nsCSSValue &vr = item->*(pairListValues[i]);
           nsCSSUnit unit =
             GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
           if (unit == eCSSUnit_Null) {
             return PR_FALSE;
           }
           switch (unit) {
             case eCSSUnit_Pixel:
-              AddCSSValuePixel(aCoeff1, v1, aCoeff2, v2, vr);
+              AddCSSValuePixel(aCoeff1, v1, aCoeff2, v2, vr, restrictions);
               break;
             case eCSSUnit_Percent:
-              AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, vr);
+              AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, vr, restrictions);
               break;
             case eCSSUnit_Calc:
               AddCSSValueCanonicalCalc(aCoeff1, v1, aCoeff2, v2, vr);
               break;
             default:
               if (v1 != v2) {
                 return PR_FALSE;
               }
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -74,18 +74,18 @@ function initial_font_family_is_sans_ser
 }
 var gInitialFontFamilyIsSansSerif = initial_font_family_is_sans_serif();
 
 var gCSSProperties = {
 	"-moz-animation": {
 		domProp: "MozAnimation",
 		inherited: false,
 		type: CSS_TYPE_TRUE_SHORTHAND,
-		subproperties: [ "-moz-animation-name", "-moz-animation-duration", "-moz-animation-timing-function", "-moz-animation-delay", "-moz-animation-direction", "-moz-animation-fill-mode", "-moz-animation-iteration-count", "-moz-animation-play-state" ],
-		initial_values: [ "none none 0s 0s ease normal running 1.0", "none", "0s", "ease", "normal", "running", "1.0" ],
+		subproperties: [ "-moz-animation-name", "-moz-animation-duration", "-moz-animation-timing-function", "-moz-animation-delay", "-moz-animation-direction", "-moz-animation-fill-mode", "-moz-animation-iteration-count" ],
+		initial_values: [ "none none 0s 0s ease normal 1.0", "none", "0s", "ease", "normal", "1.0" ],
 		other_values: [ "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ],
 		invalid_values: [  "2s inherit", "inherit 2s", "2s bounce, 1s inherit", "2s inherit, 1s bounce", "2s initial" ]
 	},
 	"-moz-animation-delay": {
 		domProp: "MozAnimationDelay",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "0s", "0ms" ],
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -44,133 +44,197 @@ function has_num(str)
     return !!String(str).match(/^([\d.]+)/);
 }
 
 function any_unit_to_num(str)
 {
     return Number(String(str).match(/^([\d.]+)/)[1]);
 }
 
+var FUNC_NEGATIVE = "cubic-bezier(0.25, -2, 0.75, 1)";
+
 var supported_properties = {
     "border-bottom-left-radius": [ test_radius_transition ],
     "border-bottom-right-radius": [ test_radius_transition ],
     "border-top-left-radius": [ test_radius_transition ],
     "border-top-right-radius": [ test_radius_transition ],
     "-moz-box-flex": [ test_float_zeroToOne_transition,
-                       test_float_aboveOne_transition ],
+                       test_float_aboveOne_transition,
+                       test_float_zeroToOne_clamped ],
     "box-shadow": [ test_shadow_transition ],
-    "-moz-column-count": [ test_pos_integer_or_auto_transition ],
-    "-moz-column-gap": [ test_length_transition ],
+    "-moz-column-count": [ test_pos_integer_or_auto_transition,
+                           test_integer_at_least_one_clamping ],
+    "-moz-column-gap": [ test_length_transition,
+                         test_length_clamped ],
     "-moz-column-rule-color": [ test_color_transition,
                                 test_border_color_transition ],
-    "-moz-column-rule-width": [ test_length_transition ],
-    "-moz-column-width": [ test_length_transition ],
+    "-moz-column-rule-width": [ test_length_transition,
+                                test_length_clamped ],
+    "-moz-column-width": [ test_length_transition,
+                           test_length_clamped ],
     "-moz-image-region": [ test_rect_transition ],
     "-moz-outline-radius-bottomleft": [ test_radius_transition ],
     "-moz-outline-radius-bottomright": [ test_radius_transition ],
     "-moz-outline-radius-topleft": [ test_radius_transition ],
     "-moz-outline-radius-topright": [ test_radius_transition ],
     "-moz-text-decoration-color": [ test_color_transition,
                                     test_border_color_transition ],
     "-moz-transform": [ test_transform_transition ],
     "-moz-transform-origin": [ test_length_pair_transition,
-                               test_length_percent_pair_transition ],
+                               test_length_percent_pair_transition,
+                               test_length_percent_pair_unclamped ],
     "background-color": [ test_color_transition ],
-    "background-position": [ test_background_position_transition ],
-    "background-size": [ test_background_size_transition ],
+    "background-position": [ test_background_position_transition,
+                             // FIXME: We don't currently test clamping,
+                             // since background-position uses calc() as
+                             // an intermediate form.
+                             /* test_length_percent_pair_unclamped */ ],
+    "background-size": [ test_background_size_transition,
+                         // FIXME: We don't currently test clamping,
+                         // since background-size uses calc() as an
+                         // intermediate form.
+                         /* test_length_percent_pair_clamped */ ],
     "border-bottom-color": [ test_color_transition,
                              test_border_color_transition ],
-    "border-bottom-width": [ test_length_transition ],
+    "border-bottom-width": [ test_length_transition,
+                             test_length_clamped ],
     "border-left-color": [ test_color_transition,
                            test_border_color_transition ],
-    "border-left-width": [ test_length_transition ],
+    "border-left-width": [ test_length_transition,
+                           test_length_clamped ],
     "border-right-color": [ test_color_transition,
                             test_border_color_transition ],
-    "border-right-width": [ test_length_transition ],
-    "border-spacing": [ test_length_pair_transition ],
+    "border-right-width": [ test_length_transition,
+                            test_length_clamped ],
+    "border-spacing": [ test_length_pair_transition,
+                        test_length_pair_transition_clamped ],
     "border-top-color": [ test_color_transition,
                           test_border_color_transition ],
-    "border-top-width": [ test_length_transition ],
+    "border-top-width": [ test_length_transition,
+                           test_length_clamped ],
     "bottom": [ test_length_transition, test_percent_transition,
-                test_length_percent_calc_transition ],
+                test_length_percent_calc_transition,
+                test_length_unclamped, test_percent_unclamped ],
     "clip": [ test_rect_transition ],
     "color": [ test_color_transition ],
     "fill": [ test_color_transition ],
-    "fill-opacity" : [ test_float_zeroToOne_transition ],
+    "fill-opacity" : [ test_float_zeroToOne_transition,
+                       // opacity is clamped in computed style
+                       // (not parsing/interpolation)
+                       test_float_zeroToOne_clamped ],
     "flood-color": [ test_color_transition ],
-    "flood-opacity" : [ test_float_zeroToOne_transition ],
+    "flood-opacity" : [ test_float_zeroToOne_transition,
+                        // opacity is clamped in computed style
+                        // (not parsing/interpolation)
+                        test_float_zeroToOne_clamped ],
     "font-size": [ test_length_transition, test_percent_transition,
-                   test_length_percent_calc_transition ],
+                   test_length_percent_calc_transition,
+                   test_length_clamped, test_percent_clamped ],
     "font-size-adjust": [ test_float_zeroToOne_transition,
-                          test_float_aboveOne_transition ],
+                          test_float_aboveOne_transition,
+                          /* FIXME: font-size-adjust treats zero specially */
+                          /* test_float_zeroToOne_clamped */ ],
     "font-stretch": [ test_font_stretch ],
     "font-weight": [ test_font_weight ],
     "height": [ test_length_transition, test_percent_transition,
-                test_length_percent_calc_transition ],
+                test_length_percent_calc_transition,
+                test_length_clamped, test_percent_clamped ],
     "left": [ test_length_transition, test_percent_transition,
-              test_length_percent_calc_transition ],
-    "letter-spacing": [ test_length_transition ],
+              test_length_percent_calc_transition,
+              test_length_unclamped, test_percent_unclamped ],
+    "letter-spacing": [ test_length_transition, test_length_unclamped ],
     "lighting-color": [ test_color_transition ],
     // NOTE: when calc() is supported on 'line-height', we should add
     // test_length_percent_calc_transition.
-    "line-height": [ test_length_transition, test_percent_transition ],
+    "line-height": [ test_length_transition, test_percent_transition,
+                     test_length_clamped, test_percent_clamped ],
     "margin-bottom": [ test_length_transition, test_percent_transition,
-                       test_length_percent_calc_transition ],
+                       test_length_percent_calc_transition,
+                       test_length_unclamped, test_percent_unclamped ],
     "margin-left": [ test_length_transition, test_percent_transition,
-                     test_length_percent_calc_transition ],
+                     test_length_percent_calc_transition,
+                     test_length_unclamped, test_percent_unclamped ],
     "margin-right": [ test_length_transition, test_percent_transition,
-                      test_length_percent_calc_transition ],
+                      test_length_percent_calc_transition,
+                      test_length_unclamped, test_percent_unclamped ],
     "margin-top": [ test_length_transition, test_percent_transition,
-                    test_length_percent_calc_transition ],
-    "marker-offset": [ test_length_transition ],
+                    test_length_percent_calc_transition,
+                    test_length_unclamped, test_percent_unclamped ],
+    "marker-offset": [ test_length_transition,
+                       test_length_unclamped ],
     "max-height": [ test_length_transition, test_percent_transition,
-                    test_length_percent_calc_transition ],
+                    test_length_percent_calc_transition,
+                    test_length_clamped, test_percent_clamped ],
     "max-width": [ test_length_transition, test_percent_transition,
-                   test_length_percent_calc_transition ],
+                   test_length_percent_calc_transition,
+                   test_length_clamped, test_percent_clamped ],
     "min-height": [ test_length_transition, test_percent_transition,
-                    test_length_percent_calc_transition ],
+                    test_length_percent_calc_transition,
+                    test_length_clamped, test_percent_clamped ],
     "min-width": [ test_length_transition, test_percent_transition,
-                   test_length_percent_calc_transition ],
-    "opacity" : [ test_float_zeroToOne_transition ],
+                   test_length_percent_calc_transition,
+                   test_length_clamped, test_percent_clamped ],
+    "opacity" : [ test_float_zeroToOne_transition,
+                  // opacity is clamped in computed style
+                  // (not parsing/interpolation)
+                  test_float_zeroToOne_clamped ],
     "outline-color": [ test_color_transition ],
-    "outline-offset": [ test_length_transition ],
-    "outline-width": [ test_length_transition ],
+    "outline-offset": [ test_length_transition, test_length_unclamped ],
+    "outline-width": [ test_length_transition, test_length_clamped ],
     "padding-bottom": [ test_length_transition, test_percent_transition,
-                        test_length_percent_calc_transition ],
+                        test_length_percent_calc_transition,
+                        test_length_clamped, test_percent_clamped ],
     "padding-left": [ test_length_transition, test_percent_transition,
-                      test_length_percent_calc_transition ],
+                      test_length_percent_calc_transition,
+                      test_length_clamped, test_percent_clamped ],
     "padding-right": [ test_length_transition, test_percent_transition,
-                       test_length_percent_calc_transition ],
+                       test_length_percent_calc_transition,
+                       test_length_clamped, test_percent_clamped ],
     "padding-top": [ test_length_transition, test_percent_transition,
-                     test_length_percent_calc_transition ],
+                     test_length_percent_calc_transition,
+                     test_length_clamped, test_percent_clamped ],
     "right": [ test_length_transition, test_percent_transition,
-               test_length_percent_calc_transition ],
+               test_length_percent_calc_transition,
+               test_length_unclamped, test_percent_unclamped ],
     "stop-color": [ test_color_transition ],
-    "stop-opacity" : [ test_float_zeroToOne_transition ],
+    "stop-opacity" : [ test_float_zeroToOne_transition,
+                       // opacity is clamped in computed style
+                       // (not parsing/interpolation)
+                       test_float_zeroToOne_clamped ],
     "stroke": [ test_color_transition ],
     "stroke-dasharray": [ test_dasharray_transition ],
     // NOTE: when calc() is supported on 'stroke-dashoffset', we should
     // add test_length_percent_calc_transition.
-    "stroke-dashoffset": [ test_length_transition, test_percent_transition ],
-    "stroke-miterlimit": [ test_float_aboveOne_transition ],
-    "stroke-opacity" : [ test_float_zeroToOne_transition ],
+    "stroke-dashoffset": [ test_length_transition, test_percent_transition,
+                           test_length_unclamped, test_percent_unclamped ],
+    "stroke-miterlimit": [ test_float_aboveOne_transition,
+                           test_float_aboveOne_clamped ],
+    "stroke-opacity" : [ test_float_zeroToOne_transition,
+                         // opacity is clamped in computed style
+                         // (not parsing/interpolation)
+                         test_float_zeroToOne_clamped ],
     // NOTE: when calc() is supported on 'stroke-width', we should add
     // test_length_percent_calc_transition.
-    "stroke-width": [ test_length_transition, test_percent_transition ],
+    "stroke-width": [ test_length_transition, test_percent_transition,
+                      test_length_clamped, test_percent_clamped ],
     "text-indent": [ test_length_transition, test_percent_transition,
-                     test_length_percent_calc_transition ],
+                     test_length_percent_calc_transition,
+                     test_length_unclamped, test_percent_unclamped ],
     "text-shadow": [ test_shadow_transition ],
     "top": [ test_length_transition, test_percent_transition,
-             test_length_percent_calc_transition ],
+             test_length_percent_calc_transition,
+             test_length_unclamped, test_percent_unclamped ],
     "vertical-align": [ test_length_transition, test_percent_transition,
-                        test_length_percent_calc_transition ],
+                        test_length_percent_calc_transition,
+                        test_length_unclamped, test_percent_unclamped ],
     "visibility": [ test_visibility_transition ],
     "width": [ test_length_transition, test_percent_transition,
-               test_length_percent_calc_transition ],
-    "word-spacing": [ test_length_transition ],
+               test_length_percent_calc_transition,
+               test_length_clamped, test_percent_clamped ],
+    "word-spacing": [ test_length_transition, test_length_unclamped ],
     "z-index": [ test_zindex_transition, test_pos_integer_or_auto_transition ],
 };
 
 var div = document.getElementById("display");
 var cs = getComputedStyle(div, "");
 
 var prop;
 for (prop in supported_properties) {
@@ -299,42 +363,96 @@ function test_length_transition(prop) {
      "length-valued property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "12px", "");
   is(cs.getPropertyValue(prop), "6px",
      "length-valued property " + prop + ": interpolation of lengths");
   check_distance(prop, "4px", "6px", "12px");
 }
 
+function test_length_clamped(prop) {
+  test_length_clamped_or_unclamped(prop, true);
+}
+
+function test_length_unclamped(prop) {
+  test_length_clamped_or_unclamped(prop, false);
+}
+
+function test_length_clamped_or_unclamped(prop, is_clamped) {
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0px", "");
+  is(cs.getPropertyValue(prop), "0px",
+     "length-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "100px", "");
+  (is_clamped ? is : isnot)(cs.getPropertyValue(prop), "0px",
+     "length-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
+}
+
 // Test using float values in the range [0, 1] (e.g. opacity)
 function test_float_zeroToOne_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "0.3", "");
   is(cs.getPropertyValue(prop), "0.3",
      "float-valued property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "0.8", "");
   is(cs.getPropertyValue(prop), "0.425",
      "float-valued property " + prop + ": interpolation of floats");
   check_distance(prop, "0.3", "0.425", "0.8");
 }
 
+function test_float_zeroToOne_clamped(prop) {
+  test_float_zeroToOne_clamped_or_unclamped(prop, true);
+}
+function test_float_zeroToOne_unclamped(prop) {
+  test_float_zeroToOne_clamped_or_unclamped(prop, false);
+}
+
+function test_float_zeroToOne_clamped_or_unclamped(prop, is_clamped) {
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0", "");
+  is(cs.getPropertyValue(prop), "0",
+     "float-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "1", "");
+  (is_clamped ? is : isnot)(cs.getPropertyValue(prop), "0",
+     "float-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
+}
+
 // Test using float values in the range [1, infinity) (e.g. stroke-miterlimit)
 function test_float_aboveOne_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "1", "");
   is(cs.getPropertyValue(prop), "1",
      "float-valued property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "2.1", "");
   is(cs.getPropertyValue(prop), "1.275",
      "float-valued property " + prop + ": interpolation of floats");
   check_distance(prop, "1", "1.275", "2.1");
 }
 
+function test_float_aboveOne_clamped(prop) {
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "1", "");
+  is(cs.getPropertyValue(prop), "1",
+     "float-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "5", "");
+  is(cs.getPropertyValue(prop), "1",
+     "float-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
+}
+
 function test_percent_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "25%", "");
   var av = cs.getPropertyValue(prop);
   var a = any_unit_to_num(av);
   div.style.setProperty(prop, "75%", "");
   var bv = cs.getPropertyValue(prop);
   var b = any_unit_to_num(bv);
@@ -346,16 +464,36 @@ function test_percent_transition(prop) {
   is(any_unit_to_num(res) * 4, 3 * b + a,
      "percent-valued property " + prop + ": interpolation of percents: " +
      res + " should be a quarter of the way between " + bv + " and " + av);
   ok(has_num(res),
      "percent-valued property " + prop + ": percent computes to number");
   check_distance(prop, "25%", "37.5%", "75%");
 }
 
+function test_percent_clamped(prop) {
+  test_percent_clamped_or_unclamped(prop, true);
+}
+
+function test_percent_unclamped(prop) {
+  test_percent_clamped_or_unclamped(prop, false);
+}
+
+function test_percent_clamped_or_unclamped(prop, is_clamped) {
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0%", "");
+  var zero_val = cs.getPropertyValue(prop); // flushes too
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "150%", "");
+  (is_clamped ? is : isnot)(cs.getPropertyValue(prop), zero_val,
+     "percent-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
+}
+
 function test_length_percent_calc_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "0%", "");
   var av = cs.getPropertyValue(prop);
   var a = any_unit_to_num(av);
   div.style.setProperty(prop, "100%", "");
   var bv = cs.getPropertyValue(prop);
   var b = any_unit_to_num(bv);
@@ -430,16 +568,42 @@ function test_color_transition(prop) {
   is(cs.getPropertyValue(prop), "rgb(96, 48, 32)",
      "color-valued property " + prop + ": interpolation of currentColor");
 
   check_distance(prop, "rgb(255, 28, 0)", "rgb(210, 42, 32)",
                        "rgb(75, 84, 128)");
   check_distance(prop, "rgb(128, 64, 0)", "rgb(96, 48, 32)", "currentColor");
 
   (prop == "color" ? div.parentNode : div).style.removeProperty("color");
+
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "rgb(0, 255, 0)", "");
+  var vals = cs.getPropertyValue(prop).match(/rgb\(([^, ]*), ([^, ]*), ([^, ]*)\)/);
+  is(vals.length, 4,
+     "color-valued property " + prop + ": flush before clamping test (length)");
+  is(vals[1], "0",
+     "color-valued property " + prop + ": flush before clamping test (red)");
+  is(vals[2], "255",
+     "color-valued property " + prop + ": flush before clamping test (green)");
+  is(vals[3], "0",
+     "color-valued property " + prop + ": flush before clamping test (blue)");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "rgb(255, 0, 128)", "");
+  // FIXME: Once we support non-sRGB colors, these tests will need fixing.
+  vals = cs.getPropertyValue(prop).match(/rgb\(([^, ]*), ([^, ]*), ([^, ]*)\)/);
+  is(vals.length, 4,
+     "color-valued property " + prop + ": clamping of negatives (length)");
+  is(vals[1], "0",
+     "color-valued property " + prop + ": clamping of negatives (red)");
+  is(vals[2], "255",
+     "color-valued property " + prop + ": clamping of above-range (green)");
+  is(vals[3], "0",
+     "color-valued property " + prop + ": clamping of negatives (blue)");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
 }
 
 function test_border_color_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "rgb(128, 64, 0)", "");
   div.style.setProperty("color", "rgb(0, 0, 128)", "");
   is(cs.getPropertyValue(prop), "rgb(128, 64, 0)",
      "color-valued property " + prop + ": computed value before transition");
@@ -498,16 +662,55 @@ function test_shadow_transition(prop) {
   var defaultColor = cs.getPropertyValue("color") + " ";
   div.style.setProperty(prop, "2px 2px 2px", "");
   is(cs.getPropertyValue(prop), defaultColor + "2px 2px 2px" + spreadStr,
      "shadow-valued property " + prop + ": non-interpolable cases");
   div.style.setProperty(prop, "6px 14px 10px", "");
   is(cs.getPropertyValue(prop), defaultColor + "3px 5px 4px" + spreadStr,
      "shadow-valued property " + prop + ": interpolation without color");
   check_distance(prop, "2px 2px 2px", "3px 5px 4px", "6px 14px 10px");
+
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0px 0px 0px black", "");
+  is(cs.getPropertyValue(prop), "rgb(0, 0, 0) 0px 0px 0px" + spreadStr,
+     "shadow-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "10px 10px 10px black", "");
+  var vals = cs.getPropertyValue(prop).split(" ");
+  is(vals.length, 6 + (prop == "box-shadow"), "unexpected number of values");
+  is(vals.slice(0, 3).join(" "), "rgb(0, 0, 0)",
+     "shadow-valued property " + prop + " (color): clamping of negatives");
+  isnot(vals[3], "0px",
+        "shadow-valued property " + prop + " (x): clamping of negatives");
+  isnot(vals[4], "0px",
+        "shadow-valued property " + prop + " (y): clamping of negatives");
+  is(vals[5], "0px",
+     "shadow-valued property " + prop + " (radius): clamping of negatives");
+  if (prop == "box-shadow") {
+    div.style.setProperty("-moz-transition-property", "none", "");
+    div.style.setProperty(prop, "0px 0px 0px 0px black", "");
+    is(cs.getPropertyValue(prop), "rgb(0, 0, 0) 0px 0px 0px 0px",
+       "shadow-valued property " + prop + ": flush before clamping test");
+    div.style.setProperty("-moz-transition-property", prop, "");
+    div.style.setProperty(prop, "10px 10px 10px 10px black", "");
+    var vals = cs.getPropertyValue(prop).split(" ");
+    is(vals.length, 7, "unexpected number of values");
+    is(vals.slice(0, 3).join(" "), "rgb(0, 0, 0)",
+       "shadow-valued property " + prop + " (color): clamping of negatives");
+    isnot(vals[3], "0px",
+          "shadow-valued property " + prop + " (x): clamping of negatives");
+    isnot(vals[4], "0px",
+          "shadow-valued property " + prop + " (y): clamping of negatives");
+    is(vals[5], "0px",
+       "shadow-valued property " + prop + " (radius): clamping of negatives");
+    isnot(vals[6], "0px",
+          "shadow-valued property " + prop + " (spread): clamping of negatives");
+  }
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
 }
 
 function test_dasharray_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "3", "");
   is(cs.getPropertyValue(prop), "3",
      "dasharray-valued property " + prop +
      ": computed value before transition");
@@ -541,16 +744,27 @@ function test_dasharray_transition(prop)
                        "4,8,12,16");
   div.style.setProperty(prop, "2,50%,6,10", "");
   is(cs.getPropertyValue(prop), "2, 50%, 6, 10",
      "dasharray-valued property " + prop + ": non-interpolability of mixed units");
   div.style.setProperty(prop, "6,30%,2,2", "");
   is(cs.getPropertyValue(prop), "3, 45%, 5, 8",
      "dasharray-valued property " + prop + ": interpolation of dasharray");
   check_distance(prop, "2,50%,6,10", "3, 45%, 5, 8", "6,30%,2,2");
+
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0,0%", "");
+  is(cs.getPropertyValue(prop), "0, 0%",
+     "dasharray-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "5, 25%", "");
+  is(cs.getPropertyValue(prop), "0, 0%",
+     "dasharray-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
 }
 
 function test_radius_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
 
   // FIXME: Test a square for now, since we haven't updated to the spec
   // for vertical components being relative to the height.
   // Note: We use powers of two here so the floating-point math comes out
@@ -603,16 +817,27 @@ function test_radius_transition(prop) {
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "64px 16px", "");
   is(cs.getPropertyValue(prop), "28px",
      "radius-valued property " + prop + ": interpolation of radius with mixed units");
   check_distance(prop, "6.25% 12.5%",
                  "-moz-calc(4.6875% + 16px) -moz-calc(9.375% + 4px)",
                  "64px 16px");
 
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0px 0px", "");
+  is(cs.getPropertyValue(prop), "0px",
+     "radius-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "10px 20px", "");
+  is(cs.getPropertyValue(prop), "0px",
+     "radius-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
+
   test_length_percent_calc_transition(prop);
 
   div.style.removeProperty("width");
   div.style.removeProperty("height");
   div.style.removeProperty("border");
   div.style.removeProperty("padding");
 }
 
@@ -631,16 +856,27 @@ function test_zindex_transition(prop) {
      "integer-valued property " + prop + ": auto not interpolable");
   div.style.setProperty(prop, "-4", "");
   is(cs.getPropertyValue(prop), "-4",
      "integer-valued property " + prop + ": computed value before transition");
   div.style.setProperty(prop, "8", "");
   is(cs.getPropertyValue(prop), "-1",
      "integer-valued property " + prop + ": interpolation of lengths");
   check_distance(prop, "-4", "-1", "8");
+
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0", "");
+  is(cs.getPropertyValue(prop), "0",
+     "integer-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "100", "");
+  isnot(cs.getPropertyValue(prop), "0",
+        "integer-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
 }
 
 function test_font_stretch(prop) {
   is(prop, "font-stretch", "only designed for one property");
 
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "normal", "");
   is(cs.getPropertyValue(prop), "normal",
@@ -654,16 +890,35 @@ function test_font_stretch(prop) {
   div.style.setProperty(prop, "expanded", "");
   is(cs.getPropertyValue(prop), "expanded",
      "font-stretch property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "extra-condensed", "");
   is(cs.getPropertyValue(prop), "normal",
      "font-stretch property " + prop + ": interpolation of font-stretches");
   check_distance(prop, "expanded", "semi-expanded", "condensed");
+
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "ultra-condensed", "");
+  is(cs.getPropertyValue(prop), "ultra-condensed",
+     "font-stretch property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "ultra-expanded", "");
+  is(cs.getPropertyValue(prop), "ultra-condensed",
+     "font-stretch property " + prop + ": clamping of values");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "ultra-expanded", "");
+  is(cs.getPropertyValue(prop), "ultra-expanded",
+     "font-stretch property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "ultra-condensed", "");
+  is(cs.getPropertyValue(prop), "ultra-expanded",
+     "font-stretch property " + prop + ": clamping of values");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
 }
 
 function test_font_weight(prop) {
   is(prop, "font-weight", "only designed for one property");
 
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "normal", "");
   is(cs.getPropertyValue(prop), "400",
@@ -678,16 +933,35 @@ function test_font_weight(prop) {
   div.style.setProperty(prop, "900", "");
   is(cs.getPropertyValue(prop), "900",
      "font-weight property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "100", "");
   is(cs.getPropertyValue(prop), "700",
      "font-weight property " + prop + ": interpolation of font-weights");
   check_distance(prop, "900", "700", "100");
+
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "100", "");
+  is(cs.getPropertyValue(prop), "100",
+     "font-weight property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "900", "");
+  is(cs.getPropertyValue(prop), "100",
+     "font-weight property " + prop + ": clamping of values");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "900", "");
+  is(cs.getPropertyValue(prop), "900",
+     "font-weight property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "100", "");
+  is(cs.getPropertyValue(prop), "900",
+     "font-weight property " + prop + ": clamping of values");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
 }
 
 function test_pos_integer_or_auto_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "4", "");
   is(cs.getPropertyValue(prop), "4",
      "integer-valued property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
@@ -702,40 +976,87 @@ function test_pos_integer_or_auto_transi
   is(cs.getPropertyValue(prop), "8",
      "integer-valued property " + prop + ": computed value before transition");
   div.style.setProperty(prop, "4", "");
   is(cs.getPropertyValue(prop), "7",
      "integer-valued property " + prop + ": interpolation of lengths");
   check_distance(prop, "8", "7", "4");
 }
 
+function test_integer_at_least_one_clamping(prop) {
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "1", "");
+  is(cs.getPropertyValue(prop), "1",
+     "integer-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "5", "");
+  is(cs.getPropertyValue(prop), "1",
+     "integer-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
+}
+
 function test_length_pair_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "4px 6px", "");
   is(cs.getPropertyValue(prop), "4px 6px",
      "length-valued property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "12px 10px", "");
   is(cs.getPropertyValue(prop), "6px 7px",
      "length-valued property " + prop + ": interpolation of lengths");
   check_distance(prop, "4px 6px", "6px 7px", "12px 10px");
 }
 
+function test_length_pair_transition_clamped(prop) {
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0px 0px", "");
+  is(cs.getPropertyValue(prop), "0px 0px",
+     "length-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "30px 50px", "");
+  is(cs.getPropertyValue(prop), "0px 0px",
+     "length-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
+}
+
 function test_length_percent_pair_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "4px 50%", "");
   is(cs.getPropertyValue(prop), "4px 50%",
      "length-valued property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "12px 70%", "");
   is(cs.getPropertyValue(prop), "6px 55%",
      "length-valued property " + prop + ": interpolation of lengths");
   check_distance(prop, "4px 50%", "6px 55%", "12px 70%");
 }
 
+function test_length_percent_pair_clamped(prop) {
+  test_length_percent_pair_clamped_or_unclamped(prop, true);
+}
+
+function test_length_percent_pair_unclamped(prop) {
+  test_length_percent_pair_clamped_or_unclamped(prop, false);
+}
+
+function test_length_percent_pair_clamped_or_unclamped(prop, is_clamped) {
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "0px 0%", "");
+  is(cs.getPropertyValue(prop), "0px 0%",
+     "length+percent-valued property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "30px 25%", "");
+  (is_clamped ? is : isnot)(cs.getPropertyValue(prop), "0px 0%",
+     "length+percent-valued property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
+}
+
 function test_rect_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "rect(4px, 16px, 12px, 6px)", "");
   is(cs.getPropertyValue(prop), "rect(4px, 16px, 12px, 6px)",
      "rect-valued property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
   div.style.setProperty(prop, "rect(0px, 4px, 4px, 2px)", "");
   is(cs.getPropertyValue(prop), "rect(3px, 13px, 10px, 5px)",
@@ -747,16 +1068,45 @@ function test_rect_transition(prop) {
     div.style.setProperty(prop, "rect(0px, 6px, 4px, auto)", "");
     is(cs.getPropertyValue(prop), "rect(0px, 6px, 4px, auto)",
        "rect-valued property " + prop + ": can't interpolate auto components");
     div.style.setProperty(prop, "rect(0px, 6px, 4px, 2px)", "");
   }
   div.style.setProperty(prop, "auto", "");
   is(cs.getPropertyValue(prop), "auto",
      "rect-valued property " + prop + ": can't interpolate auto components");
+
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "rect(-10px, 30px, 0px, 0px)", "");
+  var vals = cs.getPropertyValue(prop).match(/rect\(([^, ]*), ([^, ]*), ([^, ]*), ([^, ]*)\)/);
+  is(vals.length, 5,
+     "rect-valued property " + prop + ": flush before clamping test (length)");
+  is(vals[1], "-10px",
+     "rect-valued property " + prop + ": flush before clamping test (top)");
+  is(vals[2], "30px",
+     "rect-valued property " + prop + ": flush before clamping test (right)");
+  is(vals[3], "0px",
+     "rect-valued property " + prop + ": flush before clamping test (bottom)");
+  is(vals[4], "0px",
+     "rect-valued property " + prop + ": flush before clamping test (left)");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "rect(0px, 40px, 10px, 10px)", "");
+  vals = cs.getPropertyValue(prop).match(/rect\(([^, ]*), ([^, ]*), ([^, ]*), ([^, ]*)\)/);
+  is(vals.length, 5,
+     "rect-valued property " + prop + ": clamping of negatives (length)");
+  isnot(vals[1], "-10px",
+     "rect-valued property " + prop + ": clamping of negatives (top)");
+  isnot(vals[2], "30px",
+     "rect-valued property " + prop + ": clamping of negatives (right)");
+  isnot(vals[3], "0px",
+     "rect-valued property " + prop + ": clamping of negatives (bottom)");
+  isnot(vals[4], "0px",
+     "rect-valued property " + prop + ": clamping of negatives (left)");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
 }
 
 function test_visibility_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "visible", "");
   is(cs.getPropertyValue(prop), "visible",
      "visibility property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
@@ -764,16 +1114,35 @@ function test_visibility_transition(prop
   is(cs.getPropertyValue(prop), "visible",
      "visibility property " + prop + ": interpolation of visibility");
   isnot(get_distance(prop, "visible", "hidden"), 0,
         "distance between visible and hidden should not be zero");
   is(get_distance(prop, "visible", "visible"), 0,
      "distance between visible and visible should not be zero");
   is(get_distance(prop, "hidden", "hidden"), 0,
      "distance between hidden and hidden should not be zero");
+
+  div.style.setProperty("-moz-transition-timing-function", FUNC_NEGATIVE, "");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "visible", "");
+  is(cs.getPropertyValue(prop), "visible",
+     "visibility property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "hidden", "");
+  is(cs.getPropertyValue(prop), "visible",
+     "visibility property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-property", "none", "");
+  div.style.setProperty(prop, "hidden", "");
+  is(cs.getPropertyValue(prop), "hidden",
+     "visibility property " + prop + ": flush before clamping test");
+  div.style.setProperty("-moz-transition-property", prop, "");
+  div.style.setProperty(prop, "visible", "");
+  is(cs.getPropertyValue(prop), "hidden",
+     "visibility property " + prop + ": clamping of negatives");
+  div.style.setProperty("-moz-transition-timing-function", "linear", "");
 }
 
 function test_background_size_transition(prop) {
   div.style.setProperty("-moz-transition-property", "none", "");
   div.style.setProperty(prop, "50% 80%", "");
   is(cs.getPropertyValue(prop), "50% 80%",
      "property " + prop + ": computed value before transition");
   div.style.setProperty("-moz-transition-property", prop, "");
@@ -1141,14 +1510,16 @@ function test_transform_transition(prop)
       ok(pass,
          "interpolation of transitions: " + test.start + " to " + test.end +
          ": " + actual + " should approximately equal " + test.expected);
     }
     if ("expected_uncomputed" in test) {
       check_distance(prop, test.start, test.expected_uncomputed, test.end);
     }
   }
+
+  // FIXME: should perhaps test that no clamping occurs
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -572,18 +572,18 @@ pref("dom.indexedDB.warningQuota", 5);
 
 // prevent video elements from preloading too much data
 pref("media.preload.default", 1); // default to preload none
 pref("media.preload.auto", 2);    // preload metadata if preload=auto
 
 //  0: don't show fullscreen keyboard
 //  1: always show fullscreen keyboard
 // -1: show fullscreen keyboard based on threshold pref
-pref("widget.ime.android.landscape_fullscreen", 0);
-pref("widget.ime.android.fullscreen_threshold", 300); // in hundreths of inches
+pref("widget.ime.android.landscape_fullscreen", -1);
+pref("widget.ime.android.fullscreen_threshold", 250); // in hundreths of inches
 
 // optimize images memory usage
 pref("image.mem.decodeondraw", true);
 pref("content.image.allow_locking", false);
 pref("image.mem.min_discard_timeout_ms", 20000);
 
 // enable touch events interfaces
 pref("dom.w3c_touch_events.enabled", true);
--- a/mobile/chrome/content/firstrun/firstrun.xhtml
+++ b/mobile/chrome/content/firstrun/firstrun.xhtml
@@ -117,17 +117,24 @@
         }
 
         function init() {
           setupLinks();
           startDiscovery();
         }
         
         function startDiscovery() {
-          let doc = getChromeWin().document;
+          let win = getChromeWin();
+          let [leftWidth, rightWidth] = win.Browser.computeSidebarVisibility();
+          if (leftWidth > 0 || rightWidth > 0) {
+            endDiscovery();
+            return;
+          }
+
+          let doc = win.document;
           let broadcaster = doc.getElementById("bcast_uidiscovery");
 
           doc.addEventListener("animationend", endDiscovery, false);
           doc.addEventListener("PanBegin", endDiscovery, false);
           broadcaster.setAttribute("mode", "discovery");
         }
 
         function endDiscovery() {
--- a/other-licenses/bsdiff/Makefile.in
+++ b/other-licenses/bsdiff/Makefile.in
@@ -49,17 +49,17 @@ HOST_PROGRAM = mbsdiff$(BIN_SUFFIX)
 HOST_CSRCS = bsdiff.c
 
 ifdef MOZ_NATIVE_BZ2
 HOST_LIBS += $(BZ2_LIBS)
 else
 HOST_LIBS += $(DIST)/host/lib/$(LIB_PREFIX)hostbz2.$(LIB_SUFFIX)
 endif
 
-ifneq (,$(filter WINCE WINNT,$(HOST_OS_ARCH)))
+ifeq ($(HOST_OS_ARCH),WINNT)
 HOST_EXTRA_LIBS += $(call EXPAND_LIBNAME,Ws2_32)
 endif
 
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/mozapps/update/updater
 
 include $(topsrcdir)/config/rules.mk
 
 CXXFLAGS += $(BZ2_CFLAGS)
--- a/toolkit/components/console/hudservice/HUDService.jsm
+++ b/toolkit/components/console/hudservice/HUDService.jsm
@@ -140,16 +140,17 @@ const SEVERITY_LOG = 3;
 
 // A mapping from the console API log event levels to the Web Console
 // severities.
 const LEVELS = {
   error: SEVERITY_ERROR,
   warn: SEVERITY_WARNING,
   info: SEVERITY_INFO,
   log: SEVERITY_LOG,
+  trace: SEVERITY_LOG,
 };
 
 // The lowest HTTP response code (inclusive) that is considered an error.
 const MIN_HTTP_ERROR_CODE = 400;
 // The highest HTTP response code (exclusive) that is considered an error.
 const MAX_HTTP_ERROR_CODE = 600;
 
 // HTTP status codes.
@@ -1433,28 +1434,34 @@ HUD_SERVICE.prototype =
    * Deactivate a HeadsUpDisplay for the given tab context.
    *
    * @param nsIDOMWindow aContext
    * @param aAnimated animate closing the web console?
    * @returns void
    */
   deactivateHUDForContext: function HS_deactivateHUDForContext(aContext, aAnimated)
   {
-    let window = aContext.linkedBrowser.contentWindow;
+    let browser = aContext.linkedBrowser;
+    let window = browser.contentWindow;
     let nBox = aContext.ownerDocument.defaultView.
       getNotificationBox(window);
     let hudId = "hud_" + nBox.id;
     let displayNode = nBox.querySelector("#" + hudId);
 
     if (hudId in this.hudReferences && displayNode) {
       if (!aAnimated) {
         this.storeHeight(hudId);
       }
 
+      let hud = this.hudReferences[hudId];
+      browser.webProgress.removeProgressListener(hud.progressListener);
+      delete hud.progressListener;
+
       this.unregisterDisplay(displayNode);
+
       window.focus();
     }
   },
 
   /**
    * Clear the specified HeadsUpDisplay
    *
    * @param string|nsIDOMNode aHUD
@@ -1976,22 +1983,99 @@ HUD_SERVICE.prototype =
                                                          aLevel,
                                                          aArguments)
   {
     // Pipe the message to createMessageNode().
     let hud = HUDService.hudReferences[aHUDId];
     function formatResult(x) {
       return (typeof(x) == "string") ? x : hud.jsterm.formatResult(x);
     }
-    let mappedArguments = Array.map(aArguments, formatResult);
-    let joinedArguments = Array.join(mappedArguments, " ");
+
+    let body = null;
+    let clipboardText = null;
+    let sourceURL = null;
+    let sourceLine = 0;
+
+    switch (aLevel) {
+      case "log":
+      case "info":
+      case "warn":
+      case "error":
+      case "debug":
+        let mappedArguments = Array.map(aArguments, formatResult);
+        body = Array.join(mappedArguments, " ");
+        break;
+
+      case "trace":
+        let filename = ConsoleUtils.abbreviateSourceURL(aArguments[0].filename);
+        let functionName = aArguments[0].functionName ||
+                           this.getStr("stacktrace.anonymousFunction");
+        let lineNumber = aArguments[0].lineNumber;
+
+        body = this.getFormatStr("stacktrace.outputMessage",
+                                 [filename, functionName, lineNumber]);
+
+        sourceURL = aArguments[0].filename;
+        sourceLine = aArguments[0].lineNumber;
+
+        clipboardText = "";
+
+        aArguments.forEach(function(aFrame) {
+          clipboardText += aFrame.filename + " :: " +
+                           aFrame.functionName + " :: " +
+                           aFrame.lineNumber + "\n";
+        });
+
+        clipboardText = clipboardText.trimRight();
+        break;
+
+      default:
+        Cu.reportError("Unknown Console API log level: " + aLevel);
+        return;
+    }
+
     let node = ConsoleUtils.createMessageNode(hud.outputNode.ownerDocument,
                                               CATEGORY_WEBDEV,
                                               LEVELS[aLevel],
-                                              joinedArguments);
+                                              body,
+                                              sourceURL,
+                                              sourceLine,
+                                              clipboardText);
+
+    // Make the node bring up the property panel, to allow the user to inspect
+    // the stack trace.
+    if (aLevel == "trace") {
+      node._stacktrace = aArguments;
+
+      let linkNode = node.querySelector(".webconsole-msg-body");
+      linkNode.classList.add("hud-clickable");
+      linkNode.setAttribute("aria-haspopup", "true");
+
+      node.addEventListener("mousedown", function(aEvent) {
+        this._startX = aEvent.clientX;
+        this._startY = aEvent.clientY;
+      }, false);
+
+      node.addEventListener("click", function(aEvent) {
+        if (aEvent.detail != 1 || aEvent.button != 0 ||
+            (this._startX != aEvent.clientX &&
+             this._startY != aEvent.clientY)) {
+          return;
+        }
+
+        if (!this._panelOpen) {
+          let propPanel = hud.jsterm.openPropertyPanel(null,
+                                                       node._stacktrace,
+                                                       this);
+          propPanel.panel.setAttribute("hudId", aHUDId);
+          this._panelOpen = true;
+        }
+      }, false);
+    }
+
     ConsoleUtils.outputMessageNode(node, aHUDId);
   },
 
   /**
    * Get OutputNode by Id
    *
    * @param string aId
    * @returns nsIDOMNode (richlistbox)
@@ -2636,45 +2720,24 @@ HUD_SERVICE.prototype =
     2: "typeException", // JSREPORT_EXCEPTION
     4: "typeError", // JSREPORT_STRICT | JSREPORT_ERROR
     5: "typeStrict", // JSREPORT_STRICT | JSREPORT_WARNING
     8: "typeError", // JSREPORT_STRICT_MODE_ERROR
     13: "typeWarning", // JSREPORT_STRICT_MODE_ERROR | JSREPORT_WARNING | JSREPORT_ERROR
   },
 
   /**
-   * Closes the Console, if any, that resides on the given tab.
-   *
-   * @param nsIDOMNode aTab
-   *        The tab on which to close the console.
-   * @returns void
-   */
-  closeConsoleOnTab: function HS_closeConsoleOnTab(aTab)
-  {
-    let xulDocument = aTab.ownerDocument;
-    let xulWindow = xulDocument.defaultView;
-    let gBrowser = xulWindow.gBrowser;
-    let linkedBrowser = aTab.linkedBrowser;
-    let notificationBox = gBrowser.getNotificationBox(linkedBrowser);
-    let hudId = "hud_" + notificationBox.getAttribute("id");
-    let outputNode = xulDocument.getElementById(hudId);
-    if (outputNode != null) {
-      this.unregisterDisplay(outputNode);
-    }
-  },
-
-  /**
    * onTabClose event handler function
    *
    * @param aEvent
    * @returns void
    */
   onTabClose: function HS_onTabClose(aEvent)
   {
-    this.closeConsoleOnTab(aEvent.target);
+    this.deactivateHUDForContext(aEvent.target, false);
   },
 
   /**
    * Called whenever a browser window closes. Cleans up any consoles still
    * around.
    *
    * @param nsIDOMEvent aEvent
    *        The dispatched event.
@@ -2682,17 +2745,17 @@ HUD_SERVICE.prototype =
    */
   onWindowUnload: function HS_onWindowUnload(aEvent)
   {
     let gBrowser = aEvent.target.defaultView.gBrowser;
     let tabContainer = gBrowser.tabContainer;
 
     let tab = tabContainer.firstChild;
     while (tab != null) {
-      this.closeConsoleOnTab(tab);
+      this.deactivateHUDForContext(tab, false);
       tab = tab.nextSibling;
     }
   },
 
   /**
    * windowInitializer - checks what Gecko app is running and inits the HUD
    *
    * @param nsIDOMWindow aContentWindow
@@ -2754,16 +2817,21 @@ HUD_SERVICE.prototype =
                      contentWindow: aContentWindow
                    };
 
       hud = new HeadsUpDisplay(config);
 
       HUDService.registerHUDReference(hud);
       let windowId = this.getWindowId(aContentWindow.top);
       this.windowIds[windowId] = hudId;
+
+      hud.progressListener = new ConsoleProgressListener(hudId);
+
+      _browser.webProgress.addProgressListener(hud.progressListener,
+        Ci.nsIWebProgress.NOTIFY_STATE_ALL);
     }
     else {
       hud = this.hudReferences[hudId];
       if (aContentWindow == aContentWindow.top) {
         // TODO: name change?? doesn't actually re-attach the console
         hud.reattachConsole(aContentWindow);
       }
     }
@@ -2908,18 +2976,24 @@ HUD_SERVICE.prototype =
    * @returns void
    */
   copySelectedItems: function HS_copySelectedItems(aOutputNode)
   {
     // Gather up the selected items and concatenate their clipboard text.
 
     let strings = [];
     let newGroup = false;
-    for (let i = 0; i < aOutputNode.selectedCount; i++) {
-      let item = aOutputNode.selectedItems[i];
+
+    let children = aOutputNode.children;
+
+    for (let i = 0; i < children.length; i++) {
+      let item = children[i];
+      if (!item.selected) {
+        continue;
+      }
 
       // Add dashes between groups so that group boundaries show up in the
       // copied output.
       if (i > 0 && item.classList.contains("webconsole-new-group")) {
         newGroup = true;
       }
 
       // Ensure the selected item hasn't been filtered by type or string.
@@ -3522,17 +3596,17 @@ let ConsoleAPIObserver = {
       // Find the HUD ID for the topmost window
       let hudId = HUDService.getHudIdByWindow(win.top);
       if (!hudId)
         return;
 
       HUDService.logConsoleAPIMessage(hudId, aMessage.level, aMessage.arguments);
     }
     else if (aTopic == "quit-application-granted") {
-      this.shutdown();
+      HUDService.shutdown();
     }
   },
 
   shutdown: function CAO_shutdown()
   {
     Services.obs.removeObserver(this, "quit-application-granted");
     Services.obs.removeObserver(this, "console-api-log-event");
   }
@@ -3782,16 +3856,20 @@ function JSPropertyProvider(aScope, aInp
   return {
     matchProp: matchProp,
     matches: matches
   };
 }
 
 function isIteratorOrGenerator(aObject)
 {
+  if (aObject === null) {
+    return false;
+  }
+
   if (typeof aObject == "object") {
     if (typeof aObject.__iterator__ == "function" ||
         aObject.constructor && aObject.constructor.name == "Iterator") {
       return true;
     }
 
     let str = aObject.toString();
     if (typeof aObject.next == "function" &&
@@ -5440,16 +5518,28 @@ HeadsUpDisplayUICommands = {
     }
     else {
       HUDService.activateHUDForContext(gBrowser.selectedTab, true);
       HUDService.animate(hudId, ANIMATE_IN);
     }
   },
 
   /**
+   * Find the hudId for the active chrome window.
+   * @return string|null
+   *         The hudId or null if the active chrome window has no open Web
+   *         Console.
+   */
+  getOpenHUD: function UIC_getOpenHUD() {
+    let chromeWindow = HUDService.currentContext();
+    let contentWindow = chromeWindow.gBrowser.selectedBrowser.contentWindow;
+    return HUDService.getHudIdByWindow(contentWindow);
+  },
+
+  /**
    * The event handler that is called whenever a user switches a filter on or
    * off.
    *
    * @param nsIDOMEvent aEvent
    *        The event that triggered the filter change.
    * @return boolean
    */
   toggleFilter: function UIC_toggleFilter(aEvent) {
@@ -5840,17 +5930,17 @@ HUDWindowObserver = {
     else if (aTopic == "xpcom-shutdown") {
       this.uninit();
     }
   },
 
   uninit: function HWO_uninit()
   {
     Services.obs.removeObserver(this, "content-document-global-created");
-    HUDService.shutdown();
+    Services.obs.removeObserver(this, "xpcom-shutdown");
     this.initialConsoleCreated = false;
   },
 
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 // CommandController
 ///////////////////////////////////////////////////////////////////////////////
@@ -6002,16 +6092,97 @@ HUDConsoleObserver = {
 
       default:
         HUDService.reportPageError(CATEGORY_JS, aSubject);
         return;
     }
   }
 };
 
+/**
+ * A WebProgressListener that listens for location changes, to update HUDService
+ * state information on page navigation.
+ *
+ * @constructor
+ * @param string aHudId
+ *        The HeadsUpDisplay ID.
+ */
+function ConsoleProgressListener(aHudId)
+{
+  this.hudId = aHudId;
+}
+
+ConsoleProgressListener.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                         Ci.nsISupportsWeakReference]),
+
+  onStateChange: function CPL_onStateChange(aProgress, aRequest, aState,
+                                            aStatus)
+  {
+    if (!(aState & Ci.nsIWebProgressListener.STATE_START)) {
+      return;
+    }
+
+    let uri = null;
+    if (aRequest instanceof Ci.imgIRequest) {
+      let imgIRequest = aRequest.QueryInterface(Ci.imgIRequest);
+      uri = imgIRequest.URI;
+    }
+    else if (aRequest instanceof Ci.nsIChannel) {
+      let nsIChannel = aRequest.QueryInterface(Ci.nsIChannel);
+      uri = nsIChannel.URI;
+    }
+
+    if (!uri || !uri.schemeIs("file") && !uri.schemeIs("ftp")) {
+      return;
+    }
+
+    let outputNode = HUDService.hudReferences[this.hudId].outputNode;
+
+    let chromeDocument = outputNode.ownerDocument;
+    let msgNode = chromeDocument.createElementNS(HTML_NS, "html:span");
+
+    // Create the clickable URL part of the message.
+    let linkNode = chromeDocument.createElementNS(HTML_NS, "html:span");
+    linkNode.appendChild(chromeDocument.createTextNode(uri.spec));
+    linkNode.classList.add("hud-clickable");
+    linkNode.classList.add("webconsole-msg-url");
+
+    linkNode.addEventListener("mousedown", function(aEvent) {
+      this._startX = aEvent.clientX;
+      this._startY = aEvent.clientY;
+    }, false);
+
+    linkNode.addEventListener("click", function(aEvent) {
+      if (aEvent.detail == 1 && aEvent.button == 0 &&
+          this._startX == aEvent.clientX && this._startY == aEvent.clientY) {
+        let viewSourceUtils = chromeDocument.defaultView.gViewSourceUtils;
+        viewSourceUtils.viewSource(uri.spec, null, chromeDocument);
+      }
+    }, false);
+
+    msgNode.appendChild(linkNode);
+
+    let messageNode = ConsoleUtils.createMessageNode(chromeDocument,
+                                                     CATEGORY_NETWORK,
+                                                     SEVERITY_LOG,
+                                                     msgNode,
+                                                     null,
+                                                     null,
+                                                     uri.spec);
+
+    ConsoleUtils.outputMessageNode(messageNode, this.hudId);
+  },
+
+  onLocationChange: function() {},
+  onStatusChange: function() {},
+  onProgressChange: function() {},
+  onSecurityChange: function() {},
+};
+
 ///////////////////////////////////////////////////////////////////////////
 // appName
 ///////////////////////////////////////////////////////////////////////////
 
 /**
  * Get the app's name so we can properly dispatch app-specific
  * methods per API call
  * @returns Gecko application name
--- a/toolkit/components/console/hudservice/PropertyPanel.jsm
+++ b/toolkit/components/console/hudservice/PropertyPanel.jsm
@@ -130,35 +130,66 @@ function presentableValueFor(aObject)
       return {
         type: TYPE_OBJECT,
         display: m ? m[1] : "Object"
       };
   }
 }
 
 /**
+ * Tells if the given function is native or not.
+ *
+ * @param function aFunction
+ *        The function you want to check if it is native or not.
+ *
+ * @return boolean
+ *         True if the given function is native, false otherwise.
+ */
+function isNativeFunction(aFunction)
+{
+  return typeof aFunction == "function" && !("prototype" in aFunction);
+}
+
+/**
  * Get an array of property name value pairs for the tree.
  *
  * @param object aObject
  *        The object to get properties for.
  * @returns array of object
  *          Objects have the name, value, display, type, children properties.
  */
 function namesAndValuesOf(aObject)
 {
   let pairs = [];
-  let value, presentable;
+  let value, presentable, getter;
+
+  let isDOMDocument = aObject instanceof Ci.nsIDOMDocument;
 
   for (var propName in aObject) {
-    try {
-      value = aObject[propName];
-      presentable = presentableValueFor(value);
+    // See bug 632275: skip deprecated width and height properties.
+    if (isDOMDocument && (propName == "width" || propName == "height")) {
+      continue;
     }
-    catch (ex) {
-      continue;
+
+    // Also skip non-native getters.
+    // TODO: implement a safer way to skip non-native getters. See bug 647235.
+    getter = aObject.__lookupGetter__ ?
+             aObject.__lookupGetter__(propName) : null;
+    if (getter && !isNativeFunction(getter)) {
+      value = ""; // Value is never displayed.
+      presentable = {type: TYPE_OTHER, display: "Getter"};
+    }
+    else {
+      try {
+        value = aObject[propName];
+        presentable = presentableValueFor(value);
+      }
+      catch (ex) {
+        continue;
+      }
     }
 
     let pair = {};
     pair.name = propName;
     pair.display = propName + ": " + presentable.display;
     pair.type = presentable.type;
     pair.value = value;
 
--- a/toolkit/components/console/hudservice/tests/browser/Makefile.in
+++ b/toolkit/components/console/hudservice/tests/browser/Makefile.in
@@ -123,19 +123,23 @@ include $(topsrcdir)/config/rules.mk
 	browser_webconsole_bug_618078_network_exceptions.js \
 	browser_webconsole_bug_613280_jsterm_copy.js \
 	browser_webconsole_bug_630733_response_redirect_headers.js \
 	browser_webconsole_bug_621644_jsterm_dollar.js \
 	browser_webconsole_bug_632817.js \
 	browser_webconsole_bug_611795.js \
 	browser_webconsole_bug_618311_close_panels.js \
 	browser_webconsole_bug_618311_private_browsing.js \
+	browser_webconsole_bug_626484_output_copy_order.js \
 	browser_webconsole_bug_632347_iterators_generators.js \
 	browser_webconsole_bug_642108_refForOutputNode.js \
 	browser_webconsole_bug_642108_pruneTest.js \
+	browser_webconsole_bug_585956_console_trace.js \
+	browser_webconsole_bug_595223_file_uri.js \
+	browser_webconsole_bug_632275_getters_document_width.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	test-console.html \
 	test-network.html \
 	test-network-request.html \
 	test-mutation.html \
@@ -192,15 +196,17 @@ include $(topsrcdir)/config/rules.mk
 	test-console-extras.html \
 	test-bug-603750-websocket.html \
 	test-bug-603750-websocket.js \
 	test-bug-599725-response-headers.sjs \
 	test-bug-618078-network-exceptions.html \
 	test-bug-630733-response-redirect-headers.sjs \
 	test-bug-621644-jsterm-dollar.html \
 	test-bug-632347-iterators-generators.html \
+	test-bug-585956-console-trace.html \
+	test-bug-632275-getters.html \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_585956_console_trace.js
@@ -0,0 +1,76 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Console test suite.
+ *
+ * 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):
+ *   Mihai Sucan <mihai.sucan@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 ***** */
+
+const TEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-bug-585956-console-trace.html";
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", tabLoaded, true);
+}
+
+function tabLoaded() {
+  browser.removeEventListener("load", tabLoaded, true);
+
+  openConsole();
+
+  browser.addEventListener("load", tabReloaded, true);
+  content.location.reload();
+}
+
+function tabReloaded() {
+  browser.removeEventListener("load", tabReloaded, true);
+
+  // The expected stack trace object.
+  let stacktrace = [
+    { filename: TEST_URI, lineNumber: 9, functionName: null, language: 2 },
+    { filename: TEST_URI, lineNumber: 14, functionName: "foobar585956b", language: 2 },
+    { filename: TEST_URI, lineNumber: 18, functionName: "foobar585956a", language: 2 },
+    { filename: TEST_URI, lineNumber: 21, functionName: null, language: 2 }
+  ];
+
+  let hudId = HUDService.getHudIdByWindow(content);
+  let HUD = HUDService.hudReferences[hudId];
+
+  let node = HUD.outputNode.querySelector(".hud-log");
+  ok(node, "found trace log node");
+  ok(node._stacktrace, "found stacktrace object");
+  is(node._stacktrace.toSource(), stacktrace.toSource(), "stacktrace is correct");
+  isnot(node.textContent.indexOf("bug-585956"), -1, "found file name");
+
+  finishTest();
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_595223_file_uri.js
@@ -0,0 +1,79 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Web Console test suite.
+ *
+ * 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):
+ *   Mihai Sucan <mihai.sucan@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 ***** */
+
+const TEST_FILE = "test-network.html";
+
+function tabLoad(aEvent) {
+  browser.removeEventListener(aEvent.type, arguments.callee, true);
+
+  openConsole();
+
+  let hudId = HUDService.getHudIdByWindow(content);
+  hud = HUDService.hudReferences[hudId];
+
+  browser.addEventListener("load", tabReload, true);
+
+  content.location.reload();
+}
+
+function tabReload(aEvent) {
+  browser.removeEventListener(aEvent.type, arguments.callee, true);
+
+  let textContent = hud.outputNode.textContent;
+  isnot(textContent.indexOf("test-network.html"), -1,
+        "found test-network.html");
+  isnot(textContent.indexOf("test-image.png"), -1, "found test-image.png");
+  isnot(textContent.indexOf("testscript.js"), -1, "found testscript.js");
+  isnot(textContent.indexOf("running network console logging tests"), -1,
+        "found the console.log() message from testscript.js");
+
+  finishTest();
+}
+
+function test() {
+  let jar = getJar(getRootDirectory(gTestPath));
+  let dir = jar ?
+            extractJarToTmp(jar) :
+            getChromeDir(getResolvedURI(gTestPath));
+  dir.append(TEST_FILE);
+
+  let uri = Services.io.newFileURI(dir);
+
+  addTab(uri.spec);
+  browser.addEventListener("load", tabLoad, true);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_626484_output_copy_order.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+let itemsSet;
+
+function test() {
+  addTab("data:text/html,Web Console test for bug 626484");
+  browser.addEventListener("load", tabLoaded, true);
+}
+
+function tabLoaded(aEvent) {
+  browser.removeEventListener(aEvent.type, arguments.callee, true);
+  openConsole();
+
+  let console = browser.contentWindow.wrappedJSObject.console;
+  console.log("The first line.");
+  console.log("The second line.");
+  console.log("The last line.");
+
+  let hudId = HUDService.getHudIdByWindow(content);
+  HUD = HUDService.hudReferences[hudId];
+  outputNode = HUD.outputNode;
+
+  itemsSet = [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1],
+    [2, 1, 0]];
+
+  nextTest();
+}
+
+function nextTest() {
+  if (itemsSet.length === 0) {
+    outputNode.clearSelection();
+    HUD.jsterm.clearOutput();
+    finish();
+  }
+  else {
+    outputNode.clearSelection();
+    let items = itemsSet.shift();
+    items.forEach(function (index) {
+      outputNode.addItemToSelection(outputNode.getItemAtIndex(index));
+    });
+    outputNode.focus();
+    waitForClipboard(getExpectedClipboardText(items.length),
+      clipboardSetup, nextTest, nextTest);
+  }
+}
+
+function getExpectedClipboardText(aItemCount) {
+  let expectedClipboardText = [];
+  for (let i = 0; i < aItemCount; i++) {
+    let item = outputNode.getItemAtIndex(i);
+    expectedClipboardText.push("[" +
+      ConsoleUtils.timestampString(item.timestamp) + "] " +
+      item.clipboardText);
+  }
+  return expectedClipboardText.join("\n");
+}
+
+function clipboardSetup() {
+  goDoCommand("cmd_copy");
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_632275_getters_document_width.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-bug-632275-getters.html";
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", tabLoaded, true);
+}
+
+function tabLoaded() {
+  browser.removeEventListener("load", tabLoaded, true);
+  openConsole();
+
+  let hudId = HUDService.getHudIdByWindow(content);
+  let HUD = HUDService.hudReferences[hudId];
+  let jsterm = HUD.jsterm;
+
+  let doc = content.wrappedJSObject.document;
+
+  let panel = jsterm.openPropertyPanel("Test1", doc);
+
+  let rows = panel.treeView._rows;
+  let find = function(regex) {
+    return rows.some(function(row) {
+      return regex.test(row.display);
+    });
+  };
+
+  ok(!find(/^(width|height):/), "no document.width/height");
+
+  panel.destroy();
+
+  let getterValue = doc.foobar._val;
+
+  panel = jsterm.openPropertyPanel("Test2", doc.foobar);
+  rows = panel.treeView._rows;
+
+  is(getterValue, doc.foobar._val, "getter did not execute");
+  is(getterValue+1, doc.foobar.val, "getter executed");
+  is(getterValue+1, doc.foobar._val, "getter executed (recheck)");
+
+  ok(find(/^val: Getter$/),
+     "getter is properly displayed");
+
+  ok(find(new RegExp("^_val: " + getterValue + "$")),
+     "getter _val is properly displayed");
+
+  panel.destroy();
+
+  executeSoon(function() {
+    let textContent = HUD.outputNode.textContent;
+    is(textContent.indexOf("document.body.client"), -1,
+       "no document.width/height warning displayed");
+
+    finishTest();
+  });
+}
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_js_input_expansion.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_js_input_expansion.js
@@ -88,12 +88,15 @@ function testJSInputExpansion() {
   let newerHeight = getHeight();
 
   ok(newerHeight > newHeight, "height changed: " + newerHeight);
 
   // Test if the inputNode shrinks again.
   input.value = "";
   EventUtils.synthesizeKey("d", {});
   let height = getHeight();
-  // is(height, initialHeight, "height shrank to original size");
+  info("height: " + height);
+  info("initialHeight: " + initialHeight);
+  let finalHeightDifference = Math.abs(initialHeight - height);
+  ok(finalHeightDifference <= 1, "height shrank to original size within 1px");
 
   finishTest();
 }
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_jsterm.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_jsterm.js
@@ -148,10 +148,15 @@ function testJSTerm()
 
   // check that pprint(function) shows function source, bug 618344
   jsterm.clearOutput();
   jsterm.execute("pprint(print)");
   label = jsterm.outputNode.querySelector(".webconsole-msg-output");
   isnot(label.textContent.indexOf("SEVERITY_LOG"), -1,
         "pprint(function) shows function source");
 
+  // check that an evaluated null produces "null", bug 650780
+  jsterm.clearOutput();
+  jsterm.execute("null");
+  checkResult("null", "null is null", 1);
+
   finishTest();
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/test-bug-585956-console-trace.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>Web Console test for bug 585956 - console.trace()</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+         http://creativecommons.org/publicdomain/zero/1.0/ -->
+<script type="application/javascript">
+window.foobar585956c = function(a) {
+  console.trace();
+  return a+"c";
+};
+
+function foobar585956b(a) {
+  return foobar585956c(a+"b");
+}
+
+function foobar585956a(omg) {
+  return foobar585956b(omg + "a");
+}
+
+foobar585956a("omg");
+</script>
+  </head>
+  <body>
+    <p>Web Console test for bug 585956 - console.trace().</p>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/test-bug-632275-getters.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>Web Console test for bug 632275 - getters</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+         http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<script type="application/javascript;version=1.8">
+  document.foobar = {
+    _val: 5,
+    get val() { return ++this._val; }
+  };
+</script>
+
+  </head>
+  <body>
+    <p>Web Console test for bug 632275 - getters.</p>
+  </body>
+</html>
--- a/toolkit/components/console/hudservice/tests/browser/test-console-extras.html
+++ b/toolkit/components/console/hudservice/tests/browser/test-console-extras.html
@@ -6,17 +6,16 @@
         console.log("start");
         console.time();
         console.timeEnd()
         console.exception()
         console.assert()
         console.clear()
         console.dir()
         console.dirxml()
-        console.trace()
         console.group()
         console.groupCollapsed()
         console.groupEnd()
         console.profile()
         console.profileEnd()
         console.count()
         console.table()
         console.log("end");
--- a/toolkit/locales/en-US/chrome/global/headsUpDisplay.properties
+++ b/toolkit/locales/en-US/chrome/global/headsUpDisplay.properties
@@ -109,8 +109,19 @@ NetworkPanel.imageSizeDeltaDurationMS=%Sx%Spx, Δ%Sms
 # NetworkPanel. E.g. any kind of text is easy to display, but some audio or
 # flash data received from the server can't be displayed.
 #
 # The %S is replaced by the content type, that can't be displayed, examples are
 #  o application/x-shockwave-flash
 #  o music/crescendo
 NetworkPanel.responseBodyUnableToDisplay.content=Unable to display responses of type "%S"
 ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
+
+# LOCALIZATION NOTE (stacktrace.anonymousFunction):
+# This string is used to display JavaScript functions that have no given name -
+# they are said to be anonymous. See stacktrace.outputMessage.
+stacktrace.anonymousFunction=<anonymous>
+
+# LOCALIZATION NOTE (stacktrace.outputMessage):
+# This string is used in the Web Console output to identify a web developer call
+# to console.trace(). The stack trace of JavaScript function calls is displayed.
+# In this minimal message we only show the last call.
+stacktrace.outputMessage=Stack trace from %S, function %S, line %S.
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -1679,17 +1679,17 @@ NS_IMETHODIMP nsWindow::SetSizeMode(PRIn
         break;
 
       default :
         mode = SW_RESTORE;
     }
     ::ShowWindow(mWnd, mode);
     // we dispatch an activate event here to ensure that the right child window
     // is focused
-    if (mode == SW_RESTORE || mode == SW_MAXIMIZE)
+    if (mode == SW_RESTORE || mode == SW_MAXIMIZE || mode == SW_SHOW)
       DispatchFocusToTopLevelWindow(NS_ACTIVATE);
   }
   return rv;
 }
 
 // Constrain a potential move to fit onscreen
 NS_METHOD nsWindow::ConstrainPosition(PRBool aAllowSlop,
                                       PRInt32 *aX, PRInt32 *aY)