merge m-c to fx-team
authorRob Campbell <rcampbell@mozilla.com>
Sat, 05 Nov 2011 10:11:34 -0300
changeset 81171 b78ada5e3d701458bd1d53bdec12dbc925d6dd41
parent 81170 49b98a76f55b8c3a1b85f397a3f4238c40f9e160 (current diff)
parent 81162 1a4de3faac0597de429a8f161a8b1a405d5efa75 (diff)
child 81172 e697147121b3258da64e1cea557bc43ded0987ce
child 81175 56e7358abdb3c4f8bb82b064bf749081b181f802
push idunknown
push userunknown
push dateunknown
milestone10.0a1
merge m-c to fx-team
browser/devtools/scratchpad/test/Makefile.in
browser/devtools/webconsole/test/browser/Makefile.in
gfx/ots/ots-fix-gcc46.patch
--- a/Makefile.in
+++ b/Makefile.in
@@ -88,16 +88,17 @@ DIST_GARBAGE = config.cache config.log c
 
 default alldep all:: $(topsrcdir)/configure config.status
 	$(RM) -r $(DIST)/sdk
 	$(RM) -r $(DIST)/include
 	$(RM) -r $(DIST)/private
 	$(RM) -r $(DIST)/public
 	$(RM) -r $(DIST)/bin/components
 	$(RM) -r _tests
+	$(RM) -r $(mochitestdir)
 
 $(topsrcdir)/configure: $(topsrcdir)/configure.in
 	@echo "STOP!  configure.in has changed, and your configure is out of date."
 	@echo "Please rerun autoconf and re-configure your build directory."
 	@echo "To ignore this message, touch 'configure' in the source directory,"
 	@echo "but your build might not succeed."
 	@exit 1
 
@@ -208,16 +209,18 @@ ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
 # No point in clobbering if PGO has been explicitly disabled.
 ifndef NO_PROFILE_GUIDED_OPTIMIZE
 maybe_clobber_profiledbuild: clean
 else
 maybe_clobber_profiledbuild:
 endif
 else
 maybe_clobber_profiledbuild:
+	$(RM) $(DIST)/bin/*.pgc
+	find $(DIST)/$(MOZ_APP_NAME) -name "*.pgc" -exec mv {} $(DIST)/bin \;
 endif
 
 .PHONY: maybe_clobber_profiledbuild
 
 # Look for R_386_PC32 relocations in shared libs, these
 # break x86_64 builds and SELinux users.
 ifeq ($(OS_TARGET)_$(TARGET_XPCOM_ABI),Linux_x86-gcc3)
 scheck::
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -108,9 +108,9 @@ include $(topsrcdir)/config/rules.mk
 		test_textboxes.xul \
 		testTextboxes.js \
 		text.js \
 		treeview.css \
 		treeview.js \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/actions/Makefile.in
+++ b/accessible/tests/mochitest/actions/Makefile.in
@@ -56,9 +56,9 @@ include $(topsrcdir)/config/rules.mk
 		test_link.html \
 		test_media.html \
 		test_select.html \
 		test_tree.xul \
 		test_treegrid.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/attributes/Makefile.in
+++ b/accessible/tests/mochitest/attributes/Makefile.in
@@ -50,9 +50,9 @@ include $(topsrcdir)/config/rules.mk
 		test_obj_css.html \
 		test_obj_group.html \
 		test_obj_group.xul \
 		test_obj_group_tree.xul \
 		test_text.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/editabletext/Makefile.in
+++ b/accessible/tests/mochitest/editabletext/Makefile.in
@@ -47,9 +47,9 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		editabletext.js \
 		test_1.html \
 		test_2.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -89,9 +89,9 @@ include $(topsrcdir)/config/rules.mk
 		test_text_alg.html \
 		test_text.html \
 		test_textattrchange.html \
 		test_tree.xul \
 		test_valuechange.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/focus/Makefile.in
+++ b/accessible/tests/mochitest/focus/Makefile.in
@@ -46,9 +46,9 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		test_focusedChild.html \
 		test_takeFocus.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/hyperlink/Makefile.in
+++ b/accessible/tests/mochitest/hyperlink/Makefile.in
@@ -47,9 +47,9 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		hyperlink.js \
 		test_general.html \
 		test_general.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/hypertext/Makefile.in
+++ b/accessible/tests/mochitest/hypertext/Makefile.in
@@ -46,9 +46,9 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_general.html \
 		test_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/name/Makefile.in
+++ b/accessible/tests/mochitest/name/Makefile.in
@@ -57,9 +57,9 @@ include $(topsrcdir)/config/rules.mk
 		test_list.html \
 		test_markup.html \
 		test_nsRootAcc.xul \
 		test_tree.xul \
 		markuprules.xml \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/relations/Makefile.in
+++ b/accessible/tests/mochitest/relations/Makefile.in
@@ -49,9 +49,9 @@ include $(topsrcdir)/config/rules.mk
 		test_general.html \
 		test_general.xul \
 		test_tabbrowser.xul \
 		test_tree.xul \
 		test_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/selectable/Makefile.in
+++ b/accessible/tests/mochitest/selectable/Makefile.in
@@ -50,9 +50,9 @@ include $(topsrcdir)/config/rules.mk
 		test_listbox.xul \
 		test_menu.xul \
 		test_menulist.xul \
 		test_select.html \
 		test_tree.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/states/Makefile.in
+++ b/accessible/tests/mochitest/states/Makefile.in
@@ -66,9 +66,9 @@ include $(topsrcdir)/config/rules.mk
 		z_frames.html \
 		z_frames_article.html \
 		z_frames_checkbox.html \
 		z_frames_textbox.html \
 		z_frames_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/table/Makefile.in
+++ b/accessible/tests/mochitest/table/Makefile.in
@@ -64,9 +64,9 @@ include $(topsrcdir)/config/rules.mk
 		test_struct_listbox.xul \
 		test_struct_table.html \
 		test_struct_tree.xul \
 		test_table_1.html \
 		test_table_2.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/text/Makefile.in
+++ b/accessible/tests/mochitest/text/Makefile.in
@@ -51,9 +51,9 @@ include $(topsrcdir)/config/rules.mk
 		test_hypertext.html \
 		test_passwords.html \
 		test_singleline.html \
 		test_whitespaces.html \
 		test_words.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/textselection/Makefile.in
+++ b/accessible/tests/mochitest/textselection/Makefile.in
@@ -45,9 +45,9 @@ relativesrcdir  = accessible/textselecti
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_general.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/tree/Makefile.in
+++ b/accessible/tests/mochitest/tree/Makefile.in
@@ -74,9 +74,9 @@ include $(topsrcdir)/config/rules.mk
 		test_tree.xul \
 		test_txtcntr.html \
 		test_txtctrl.html \
 		test_txtctrl.xul \
 		wnd.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/treeupdate/Makefile.in
+++ b/accessible/tests/mochitest/treeupdate/Makefile.in
@@ -58,9 +58,9 @@ include $(topsrcdir)/config/rules.mk
 		test_recreation.html \
 		test_select.html \
 		test_textleaf.html \
 		test_visibility.html \
 		test_whitespace.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/value/Makefile.in
+++ b/accessible/tests/mochitest/value/Makefile.in
@@ -47,9 +47,9 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		test_general.html \
 		test_progress.html \
 		test_progress.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/a11y/$(relativesrcdir)
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3903,22 +3903,39 @@ var FullScreen = {
       this._isAnimating = false;
       // This is needed if they use the context menu to quit fullscreen
       this._isPopupOpen = false;
 
       this.cleanup();
     }
   },
 
+  exitDomFullScreen : function(e) {
+    document.mozCancelFullScreen();
+  },
+
   enterDomFullScreen : function(event) {
-    if (!document.mozFullScreen) {
+    // We receive "mozfullscreenchange" events for each subdocument which
+    // is an ancestor of the document containing the element which requested
+    // full-screen. Only add listeners and show warning etc when the event we
+    // receive is targeted at the chrome document, i.e. only once every time
+    // we enter DOM full-screen mode.
+    if (!document.mozFullScreen || event.target.ownerDocument != document) {
       return;
     }
     this.showWarning(true);
 
+    // Exit DOM full-screen mode upon open, close, or change tab.
+    gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
+    gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
+    gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
+
+    // Exit DOM full-screen mode when the browser window loses focus (ALT+TAB, etc).
+    window.addEventListener("deactivate", this.exitDomFullScreen, true);
+
     // Cancel any "hide the toolbar" animation which is in progress, and make
     // the toolbar hide immediately.
     clearInterval(this._animationInterval);
     clearTimeout(this._animationTimeout);
     this._isAnimating = false;
     this._shouldAnimate = false;
     this.mouseoverToggle(false);
 
@@ -3941,16 +3958,20 @@ var FullScreen = {
       gPrefService.removeObserver("browser.fullscreen", this);
 
       let fullScrToggler = document.getElementById("fullscr-toggler");
       if (fullScrToggler) {
         fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
         fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
       }
       this.cancelWarning();
+      gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
+      gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
+      gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
+      window.removeEventListener("deactivate", this.exitDomFullScreen, true);
     }
   },
 
   observe: function(aSubject, aTopic, aData)
   {
     if (aData == "browser.fullscreen.autohide") {
       if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
         gBrowser.mPanelContainer.addEventListener("mousemove",
@@ -8164,17 +8185,17 @@ let DownloadMonitorPanel = {
       return;
     }
 
     // Find the download with the longest remaining time
     let numPaused = 0;
     let maxTime = -Infinity;
     let dls = gDownloadMgr.activeDownloads;
     while (dls.hasMoreElements()) {
-      let dl = dls.getNext().QueryInterface(Ci.nsIDownload);
+      let dl = dls.getNext();
       if (dl.state == gDownloadMgr.DOWNLOAD_DOWNLOADING) {
         // Figure out if this download takes longer
         if (dl.speed > 0 && dl.size > 0)
           maxTime = Math.max(maxTime, (dl.size - dl.amountTransferred) / dl.speed);
         else
           maxTime = -1;
       }
       else if (dl.state == gDownloadMgr.DOWNLOAD_PAUSED)
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -260,12 +260,12 @@ else
 		$(NULL)
 
 # TODO: Activate after carbon test plugin lands, bug 628651
 # 		browser_maconly_carbon_mismatch_plugin.js \
 
 endif
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/certerror/test/Makefile.in
+++ b/browser/components/certerror/test/Makefile.in
@@ -44,10 +44,10 @@ relativesrcdir  = browser/components/cer
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = browser_bug431826.js \
     $(NULL)
 
 libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 
--- a/browser/components/feeds/test/Makefile.in
+++ b/browser/components/feeds/test/Makefile.in
@@ -56,9 +56,9 @@ include $(topsrcdir)/config/rules.mk
 		test_bug494328.html \
 		bug494328-data.xml \
 		test_bug589543.html \
 		bug589543-data.xml \
 		test_registerHandler.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/browser/components/feeds/test/chrome/Makefile.in
+++ b/browser/components/feeds/test/chrome/Makefile.in
@@ -51,13 +51,13 @@ include $(topsrcdir)/config/rules.mk
 
 _CHROME_FILES	= \
 		test_423060.xul \
 		test_bug368464.html \
 		test_bug408328.html \
 		$(NULL)
 
 libs:: $(_HTTP_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
 
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -756,16 +756,17 @@ BrowserGlue.prototype = {
     catch (e) {
     }
   },
 
 #ifdef MOZ_TELEMETRY_REPORTING
   _showTelemetryNotification: function BG__showTelemetryNotification() {
     const PREF_TELEMETRY_PROMPTED = "toolkit.telemetry.prompted";
     const PREF_TELEMETRY_ENABLED  = "toolkit.telemetry.enabled";
+    const PREF_TELEMETRY_REJECTED  = "toolkit.telemetry.rejected";
     const PREF_TELEMETRY_INFOURL  = "toolkit.telemetry.infoURL";
     const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner";
     // This is used to reprompt users when privacy message changes
     const TELEMETRY_PROMPT_REV = 2;
 
     var telemetryPrompted = null;
     try {
       telemetryPrompted = Services.prefs.getIntPref(PREF_TELEMETRY_PROMPTED);
@@ -798,24 +799,27 @@ BrowserGlue.prototype = {
                       callback:  function(aNotificationBar, aButton) {
                         Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
                       }
                     },
                     {
                       label:     browserBundle.GetStringFromName("telemetryNoButtonLabel"),
                       accessKey: browserBundle.GetStringFromName("telemetryNoButtonAccessKey"),
                       popup:     null,
-                      callback:  function(aNotificationBar, aButton) {}
+                      callback:  function(aNotificationBar, aButton) {
+                        Services.prefs.setBoolPref(PREF_TELEMETRY_REJECTED, true);
+                      }
                     }
                   ];
 
     // Set pref to indicate we've shown the notification.
     Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV);
 
     var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+    notification.setAttribute("hideclose", true);
     notification.persistence = 6; // arbitrary number, just so bar sticks around for a bit
 
     let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     let link = notification.ownerDocument.createElementNS(XULNS, "label");
     link.className = "text-link telemetry-text-link";
     link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel"));
     link.addEventListener('click', function() {
       // Open the learn more url in a new tab
--- a/browser/components/places/tests/browser/Makefile.in
+++ b/browser/components/places/tests/browser/Makefile.in
@@ -74,9 +74,9 @@ include $(topsrcdir)/config/rules.mk
 	browser_toolbar_migration.js \
 	browser_library_batch_delete.js \
 	browser_555547.js \
 	browser_416459_cut.js \
 	browser_library_downloads.js \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/places/tests/chrome/Makefile.in
+++ b/browser/components/places/tests/chrome/Makefile.in
@@ -51,9 +51,9 @@ include $(topsrcdir)/config/rules.mk
 	test_0_bug510634.xul \
 	test_bug549192.xul \
 	test_bug549491.xul \
 	test_editBookmarkOverlay_tags_liveUpdate.xul \
 	test_bug631374_tags_selector_scroll.xul \
 	$(NULL)
 
 libs:: $(_CHROME_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
--- a/browser/components/preferences/tests/Makefile.in
+++ b/browser/components/preferences/tests/Makefile.in
@@ -55,9 +55,9 @@ include $(topsrcdir)/config/rules.mk
     browser_privacypane_6.js \
     browser_privacypane_7.js \
     browser_privacypane_8.js \
     browser_permissions.js \
     browser_chunk_permissions.js \
     $(NULL)
 
 libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -94,9 +94,9 @@ ifneq (Linux,$(OS_ARCH))
 _BROWSER_TEST_FILES += \
 		browser_privatebrowsing_beforeunload_enter.js \
 		browser_privatebrowsing_beforeunload_exit.js \
 		browser_privatebrowsing_cookieacceptdialog.js \
 		$(NULL)
 endif
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/safebrowsing/content/test/Makefile.in
+++ b/browser/components/safebrowsing/content/test/Makefile.in
@@ -51,10 +51,10 @@ ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _NON_MAC_BROWSER_TESTS = browser_bug415846.js
 endif
 
 _BROWSER_FILES = browser_bug400731.js \
                  $(_NON_MAC_BROWSER_TESTS) \
     $(NULL)
 
 libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 
--- a/browser/components/search/test/Makefile.in
+++ b/browser/components/search/test/Makefile.in
@@ -52,9 +52,9 @@ include $(topsrcdir)/config/rules.mk
 	                  426329.xml \
 	                  browser_483086.js \
 	                  483086-1.xml \
 	                  483086-2.xml \
 	                  test.html \
 	                  $(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -163,9 +163,9 @@ include $(topsrcdir)/config/rules.mk
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	browser_625016.js \
 	$(NULL)
 endif
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -257,16 +257,19 @@ LaunchHelper(nsAutoString& aPath)
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowsShellService::ShortcutMaintenance()
 {
   nsresult rv;
 
+  // XXX App ids were updated to a constant install path hash,
+  // XXX this code can be removed after a few upgrade cycles.
+
   // Launch helper.exe so it can update the application user model ids on
   // shortcuts in the user's taskbar and start menu. This keeps older pinned
   // shortcuts grouped correctly after major updates. Note, we also do this
   // through the upgrade installer script, however, this is the only place we
   // have a chance to trap links created by users who do control the install/
   // update process of the browser.
 
   nsCOMPtr<nsIWinTaskbar> taskbarInfo =
--- a/browser/components/shell/test/Makefile.in
+++ b/browser/components/shell/test/Makefile.in
@@ -47,9 +47,9 @@ XPCSHELL_TESTS	= unit
 
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = browser_420786.js \
     browser_633221.js \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/tabview/test/Makefile.in
+++ b/browser/components/tabview/test/Makefile.in
@@ -184,9 +184,9 @@ include $(topsrcdir)/config/rules.mk
                  head.js \
                  search1.html \
                  search2.html \
                  test_bug600645.html \
                  test_bug644097.html \
                  $(NULL)
 
 libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/test/browser/Makefile.in
+++ b/browser/components/test/browser/Makefile.in
@@ -44,9 +44,9 @@ relativesrcdir  = browser/components/tes
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
   browser_bug538331.js \
   $(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/components/wintaskbar/test/Makefile.in
+++ b/browser/components/wintaskbar/test/Makefile.in
@@ -44,11 +44,11 @@ relativesrcdir  = browser/components/win
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = browser_taskbar_preview.js \
     $(NULL)
 
 libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 
 
--- a/browser/devtools/highlighter/test/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -66,9 +66,9 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_breadcrumbs.html \
 		browser_inspector_breadcrumbs.js \
 		$(NULL)
 
 # Disabled due to constant failures
 # 		browser_inspector_treePanel_click.js \
 
 libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/devtools/scratchpad/test/Makefile.in
+++ b/browser/devtools/scratchpad/test/Makefile.in
@@ -54,9 +54,9 @@ include $(topsrcdir)/config/rules.mk
 		browser_scratchpad_bug_646070_chrome_context_pref.js \
 		browser_scratchpad_bug_660560_tab.js \
 		browser_scratchpad_open.js \
 		browser_scratchpad_restore.js \
 		browser_scratchpad_bug_679467_falsy.js \
 		browser_scratchpad_bug_699130_edit_ui_updates.js \
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/devtools/shared/test/Makefile.in
+++ b/browser/devtools/shared/test/Makefile.in
@@ -52,12 +52,12 @@ include $(topsrcdir)/config/rules.mk
   head.js \
   $(NULL)
 
 _BROWSER_TEST_PAGES = \
   browser_templater_basic.html \
   $(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/devtools/sourceeditor/test/Makefile.in
+++ b/browser/devtools/sourceeditor/test/Makefile.in
@@ -47,9 +47,9 @@ include $(topsrcdir)/config/rules.mk
 _BROWSER_TEST_FILES = \
 		browser_sourceeditor_initialization.js \
 		browser_bug684862_paste_html.js \
 		browser_bug687573_vscroll.js \
 		browser_bug687568_pagescroll.js \
 		browser_bug687580_drag_and_drop.js \
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/devtools/styleinspector/test/browser/Makefile.in
+++ b/browser/devtools/styleinspector/test/browser/Makefile.in
@@ -60,12 +60,12 @@ include $(topsrcdir)/config/rules.mk
   $(NULL)
 
 _BROWSER_TEST_PAGES = \
   browser_styleinspector_webconsole.htm \
   browser_bug683672.html \
   $(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/devtools/webconsole/test/browser/Makefile.in
+++ b/browser/devtools/webconsole/test/browser/Makefile.in
@@ -220,12 +220,12 @@ include $(topsrcdir)/config/rules.mk
 	test-bug-646025-console-file-location.html \
 	test-bug-678816-content.js \
 	test-file-location.js \
 	browser_gcli_inspect.html \
 	test-bug-658368-time-methods.html \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/fuel/test/Makefile.in
+++ b/browser/fuel/test/Makefile.in
@@ -51,9 +51,9 @@ include $(topsrcdir)/config/rules.mk
 		browser_Bookmarks.js \
 		browser_Browser.js \
 		ContentA.html \
 		ContentB.html \
 		ContentWithFrames.html \
 		$(NULL)
 
 libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -1,24 +1,13 @@
 #filter substitution
 
-# Win7: AppVendor, AppName, and AppVersion must match the application.ini values
-# of Vendor, Name, and Version. These values are used in registering shortcuts
-# with the taskbar. ExplicitAppUserModelID registration when the app launches is
-# handled in widget/src/windows/WinTaskbar.cpp.
-
-!define AppVendor             "Mozilla"
+# These defines should match application.ini settings
 !define AppName               "Firefox"
 !define AppVersion            "@APP_VERSION@"
-#ifdef HAVE_64BIT_OS
-; differentiate 64-bit builds, we do the same in widget.
-!define AppUserModelID        "${AppVendor}.${AppName}.${AppVersion}.Win64"
-#else
-!define AppUserModelID        "${AppVendor}.${AppName}.${AppVersion}"
-#endif
 !define GREVersion            @MOZILLA_VERSION@
 !define AB_CD                 "@AB_CD@"
 
 !define FileMainEXE           "@MOZ_APP_NAME@.exe"
 !define WindowClass           "FirefoxMessageWindow"
 !define DDEApplication        "Firefox"
 !define AppRegName            "Firefox"
 
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -32,16 +32,17 @@
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 # Required Plugins:
 # AppAssocReg   http://nsis.sourceforge.net/Application_Association_Registration_plug-in
 # ApplicationID http://nsis.sourceforge.net/ApplicationID_plug-in
+# CityHash      http://mxr.mozilla.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
 # ShellLink     http://nsis.sourceforge.net/ShellLink_plug-in
 # UAC           http://nsis.sourceforge.net/UAC_plug-in
 
 ; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
 !verbose 3
 
 ; 7-Zip provides better compression than the lzma from NSIS so we add the files
 ; uncompressed and use 7-Zip to create a SFX archive of it
@@ -102,16 +103,17 @@ VIAddVersionKey "OriginalFilename" "setu
 !insertmacro AddDDEHandlerValues
 !insertmacro ChangeMUIHeaderImage
 !insertmacro CheckForFilesInUse
 !insertmacro CleanUpdatesDir
 !insertmacro CopyFilesFromDir
 !insertmacro CreateRegKey
 !insertmacro GetPathFromString
 !insertmacro GetParent
+!insertmacro InitHashAppModelId
 !insertmacro IsHandlerForInstallDir
 !insertmacro IsPinnedToTaskBar
 !insertmacro LogDesktopShortcut
 !insertmacro LogQuickLaunchShortcut
 !insertmacro LogStartMenuShortcut
 !insertmacro ManualCloseAppPrompt
 !insertmacro PinnedToStartMenuLnkCount
 !insertmacro RegCleanAppHandler
@@ -314,16 +316,19 @@ Section "-Application" APP_IDX
     ${UpdateProtocolHandlers}
 
     ReadRegStr $0 HKLM "Software\mozilla.org\Mozilla" "CurrentVersion"
     ${If} "$0" != "${GREVersion}"
       WriteRegStr HKLM "Software\mozilla.org\Mozilla" "CurrentVersion" "${GREVersion}"
     ${EndIf}
   ${EndIf}
 
+  ; setup the application model id registration value
+  ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
   ${RemoveDeprecatedKeys}
 
   ; The previous installer adds several regsitry values to both HKLM and HKCU.
   ; We now try to add to HKLM and if that fails to HKCU
 
   ; The order that reg keys and values are added is important if you use the
   ; uninstall log to remove them on uninstall. When using the uninstall log you
   ; MUST add children first so they will be removed first on uninstall so they
@@ -428,31 +433,33 @@ Section "-Application" APP_IDX
   ; since this will either add it for the user if unelevated or All Users if
   ; elevated.
   ${If} $AddStartMenuSC == 1
     CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
     ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
       ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
                                            "$INSTDIR"
       ${If} ${AtLeastWin7}
-        ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "${AppUserModelID}"
+      ${AndIf} "$AppUserModelID" != ""
+        ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "$AppUserModelID"
       ${EndIf}
       ${LogMsg} "Added Shortcut: $SMPROGRAMS\${BrandFullName}.lnk"
     ${Else}
       ${LogMsg} "** ERROR Adding Shortcut: $SMPROGRAMS\${BrandFullName}.lnk"
     ${EndIf}
   ${EndIf}
 
   ${If} $AddDesktopSC == 1
     CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
     ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
       ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandFullName}.lnk" \
                                              "$INSTDIR"
       ${If} ${AtLeastWin7}
-        ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "${AppUserModelID}"
+      ${AndIf} "$AppUserModelID" != ""
+        ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID"
       ${EndIf}
       ${LogMsg} "Added Shortcut: $DESKTOP\${BrandFullName}.lnk"
     ${Else}
       ${LogMsg} "** ERROR Adding Shortcut: $DESKTOP\${BrandFullName}.lnk"
     ${EndIf}
   ${EndIf}
 
   ; If elevated the Quick Launch shortcut must be added from the unelevated
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -39,16 +39,20 @@
 
   ; Remove registry entries for non-existent apps and for apps that point to our
   ; install location in the Software\Mozilla key and uninstall registry entries
   ; that point to our install location for both HKCU and HKLM.
   SetShellVarContext current  ; Set SHCTX to the current user (e.g. HKCU)
   ${RegCleanMain} "Software\Mozilla"
   ${RegCleanUninstall}
   ${UpdateProtocolHandlers}
+
+  ; setup the application model id registration value
+  ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
   ; Win7 taskbar and start menu link maintenance
   Call FixShortcutAppModelIDs
 
   ClearErrors
   WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
   ${If} ${Errors}
     StrCpy $TmpVal "HKCU" ; used primarily for logging
   ${Else}
@@ -185,52 +189,56 @@
   WriteRegDWORD HKLM "$R1" "IconsVisible" 1
 
   SetShellVarContext all  ; Set $DESKTOP to All Users
   ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
     CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
     ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
       ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR"
       ${If} ${AtLeastWin7}
-        ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "${AppUserModelID}"
+      ${AndIf} "$AppUserModelID" != ""
+        ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID"
       ${EndIf}
     ${Else}
       SetShellVarContext current  ; Set $DESKTOP to the current user's desktop
       ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
         CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
         ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
           ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandFullName}.lnk" \
                                                  "$INSTDIR"
           ${If} ${AtLeastWin7}
-            ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "${AppUserModelID}"
+          ${AndIf} "$AppUserModelID" != ""
+            ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID"
           ${EndIf}
         ${EndIf}
       ${EndUnless}
     ${EndIf}
   ${EndUnless}
 
   SetShellVarContext all  ; Set $SMPROGRAMS to All Users
   ${Unless} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
     CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
     ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
       ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
                                              "$INSTDIR"
       ${If} ${AtLeastWin7}
-        ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "${AppUserModelID}"
+      ${AndIf} "$AppUserModelID" != ""
+        ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "$AppUserModelID"
       ${EndIf}
     ${Else}
       SetShellVarContext current  ; Set $SMPROGRAMS to the current user's Start
                                   ; Menu Programs directory
       ${Unless} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
         CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
         ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
           ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
                                                  "$INSTDIR"
           ${If} ${AtLeastWin7}
-            ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "${AppUserModelID}"
+          ${AndIf} "$AppUserModelID" != ""
+            ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "$AppUserModelID"
           ${EndIf}
         ${EndIf}
       ${EndUnless}
     ${EndIf}
   ${EndUnless}
 
   ; Windows 7 doesn't use the QuickLaunch directory
   ${Unless} ${AtLeastWin7}
@@ -748,17 +756,19 @@
           ${Unless} ${FileExists} "$SMPROGRAMS\$1"
             SetShellVarContext current ; Set SHCTX to the current user
             ${Unless} ${FileExists} "$SMPROGRAMS\$1"
               StrCpy $8 "true"
               CreateShortCut "$SMPROGRAMS\$1" "$INSTDIR\${FileMainEXE}"
               ${If} ${FileExists} "$SMPROGRAMS\$1"
                 ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\$1" \
                                                        "$INSTDIR"
-                ApplicationID::Set "$SMPROGRAMS\$1" "${AppUserModelID}"
+                ${If} "$AppUserModelID" != ""
+                  ApplicationID::Set "$SMPROGRAMS\$1" "$AppUserModelID"
+                ${EndIf}
               ${EndIf}
             ${EndUnless}
           ${EndUnless}
 
           ${If} ${FileExists} "$SMPROGRAMS\$1"
             ; Count of Start Menu pinned shortcuts before unpinning.
             ${PinnedToStartMenuLnkCount} $R9
 
@@ -830,18 +840,19 @@
               Pop $4
               ${If} "$INSTDIR\${FileMainEXE}" == "$4"
                 CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" \
                                "$INSTDIR\${FileMainEXE}"
                 ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
                   ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
                                                          "$INSTDIR"
                   ${If} ${AtLeastWin7}
+                  ${AndIf} "$AppUserModelID" != ""
                     ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" \
-                                       "${AppUserModelID}"
+                                       "$AppUserModelID"
                   ${EndIf}
                 ${EndIf}
               ${EndIf}
             ${EndIf}
           ${EndUnless}
         ${EndIf}
       ${EndIf}
       ; Remove the application's Start Menu Programs directory, shortcuts, and
@@ -937,17 +948,16 @@
   Push "mozsqlite3.dll"
   Push "xpcom.dll"
   Push "crashreporter.exe"
   Push "updater.exe"
   Push "${FileMainEXE}"
 !macroend
 !define PushFilesToCheck "!insertmacro PushFilesToCheck"
 
-
 ; Sets this installation as the default browser by setting the registry keys
 ; under HKEY_CURRENT_USER via registry calls and using the AppAssocReg NSIS
 ; plugin for Vista and above. This is a function instead of a macro so it is
 ; easily called from an elevated instance of the binary. Since this can be
 ; called by an elevated instance logging is not performed in this function.
 Function SetAsDefaultAppUserHKCU
   ; Only set as the user's StartMenuInternet browser if the StartMenuInternet
   ; registry keys are for this install.
@@ -981,17 +991,20 @@ Function SetAsDefaultAppUserHKCU
   ${EndIf}
   ${RemoveDeprecatedKeys}
 
   ${PinToTaskBar}
 FunctionEnd
 
 ; Helper for updating the shortcut application model IDs.
 Function FixShortcutAppModelIDs
-  ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "${AppUserModelID}" $0
+  ${If} ${AtLeastWin7}
+  ${AndIf} "$AppUserModelID" != ""
+    ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "$AppUserModelID" $0
+  ${EndIf}
 FunctionEnd
 
 ; The !ifdef NO_LOG prevents warnings when compiling the installer.nsi due to
 ; this function only being used by the uninstaller.nsi.
 !ifdef NO_LOG
 
 Function SetAsDefaultAppUser
   ; It is only possible to set this installation of the application as the
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -31,16 +31,17 @@
 # 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 *****
 
 # Required Plugins:
 # AppAssocReg http://nsis.sourceforge.net/Application_Association_Registration_plug-in
+# CityHash    http://mxr.mozilla.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
 # ShellLink   http://nsis.sourceforge.net/ShellLink_plug-in
 # UAC         http://nsis.sourceforge.net/UAC_plug-in
 
 ; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
 !verbose 3
 
 ; 7-Zip provides better compression than the lzma from NSIS so we add the files
 ; uncompressed and use 7-Zip to create a SFX archive of it
@@ -87,16 +88,17 @@ Var TmpVal
 VIAddVersionKey "FileDescription" "${BrandShortName} Helper"
 VIAddVersionKey "OriginalFilename" "helper.exe"
 
 !insertmacro AddDDEHandlerValues
 !insertmacro CleanVirtualStore
 !insertmacro ElevateUAC
 !insertmacro GetLongPath
 !insertmacro GetPathFromString
+!insertmacro InitHashAppModelId
 !insertmacro IsHandlerForInstallDir
 !insertmacro IsPinnedToTaskBar
 !insertmacro LogDesktopShortcut
 !insertmacro LogQuickLaunchShortcut
 !insertmacro LogStartMenuShortcut
 !insertmacro PinnedToStartMenuLnkCount
 !insertmacro RegCleanAppHandler
 !insertmacro RegCleanMain
@@ -112,16 +114,17 @@ VIAddVersionKey "OriginalFilename" "help
 !insertmacro un.ChangeMUIHeaderImage
 !insertmacro un.CheckForFilesInUse
 !insertmacro un.CleanUpdatesDir
 !insertmacro un.CleanVirtualStore
 !insertmacro un.DeleteRelativeProfiles
 !insertmacro un.DeleteShortcuts
 !insertmacro un.GetLongPath
 !insertmacro un.GetSecondInstallPath
+!insertmacro un.InitHashAppModelId
 !insertmacro un.ManualCloseAppPrompt
 !insertmacro un.ParseUninstallLog
 !insertmacro un.RegCleanAppHandler
 !insertmacro un.RegCleanFileHandler
 !insertmacro un.RegCleanMain
 !insertmacro un.RegCleanUninstall
 !insertmacro un.RegCleanProtocolHandler
 !insertmacro un.RemoveQuotesFromPath
@@ -224,23 +227,33 @@ Section "Uninstall"
   ${MUI_INSTALLOPTIONS_READ} $0 "unconfirm.ini" "Field 3" "State"
   ${If} "$0" == "1"
     ${un.DeleteRelativeProfiles} "Mozilla\Firefox"
     RmDir "$APPDATA\Mozilla\Extensions\{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
     RmDir "$APPDATA\Mozilla\Extensions"
     RmDir "$APPDATA\Mozilla"
   ${EndIf}
 
+  ; setup the application model id registration value
+  ${un.InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
   SetShellVarContext current  ; Set SHCTX to HKCU
   ${un.RegCleanMain} "Software\Mozilla"
   ${un.RegCleanUninstall}
   ${un.DeleteShortcuts}
 
   ; Unregister resources associated with Win7 taskbar jump lists.
-  ApplicationID::UninstallJumpLists "${AppUserModelID}"
+  ${If} ${AtLeastWin7}
+  ${AndIf} "$AppUserModelID" != ""
+    ApplicationID::UninstallJumpLists "$AppUserModelID"
+  ${EndIf}
+
+  ; Remove any app model id's stored in the registry for this install path
+  DeleteRegValue HKCU "Software\Mozilla\${AppName}\TaskBarIDs" "$INSTDIR"
+  DeleteRegValue HKLM "Software\Mozilla\${AppName}\TaskBarIDs" "$INSTDIR"
 
   ClearErrors
   WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
   ${If} ${Errors}
     StrCpy $TmpVal "HKCU" ; used primarily for logging
   ${Else}
     SetShellVarContext all  ; Set SHCTX to HKLM
     DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
@@ -572,16 +585,20 @@ Function un.preFinish
   ${EndUnless}
 FunctionEnd
 !endif
 
 ################################################################################
 # Initialization Functions
 
 Function .onInit
+  ; We need this set up for most of the helper.exe operations.
+  !ifdef AppName
+  ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+  !endif
   ${UninstallOnInitCommon}
 FunctionEnd
 
 Function un.onInit
   StrCpy $LANGUAGE 0
 
   ${un.UninstallUnOnInitCommon}
 
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -1899,16 +1899,26 @@ panel[dimmed="true"] {
 #addon-bar {
   box-shadow: 0 1px 0 rgba(0,0,0,.15) inset;
   padding: 0;
   min-height: 20px;
 }
 
 #status-bar {
   min-height: 0;
+  -moz-appearance: none;
+  background-color: transparent;
+  border: none;
+}
+
+#addon-bar[customizing] > #status-bar {
+  opacity: .5;
+  background-image: -moz-repeating-linear-gradient(-45deg,
+                                                   rgba(255,255,255,.3), rgba(255,255,255,.3) 5px,
+                                                   rgba(0,0,0,.3) 5px, rgba(0,0,0,.3) 10px);
 }
 
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
 
 #addonbar-closebutton {
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -2464,16 +2464,23 @@ panel[dimmed="true"] {
   -moz-appearance: statusbar;
 }
 
 #status-bar {
   -moz-appearance: none;
   padding-right: 0;
 }
 
+#addon-bar[customizing] > #status-bar {
+  opacity: .5;
+  background-image: -moz-repeating-linear-gradient(-45deg,
+                                                   rgba(255,255,255,.3), rgba(255,255,255,.3) 5px,
+                                                   rgba(0,0,0,.3) 5px, rgba(0,0,0,.3) 10px);
+}
+
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
 
 #addonbar-closebutton {
   padding: 0;
   margin: 0 4px;
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -2563,16 +2563,23 @@ panel[dimmed="true"] {
 
 #status-bar {
   -moz-appearance: none;
   background-color: transparent;
   border: none;
   min-height: 0;
 }
 
+#addon-bar[customizing] > #status-bar {
+  opacity: .5;
+  background-image: -moz-repeating-linear-gradient(-45deg,
+                                                   rgba(255,255,255,.3), rgba(255,255,255,.3) 5px,
+                                                   rgba(0,0,0,.3) 5px, rgba(0,0,0,.3) 10px);
+}
+
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
 
 #addonbar-closebutton {
   border: none;
   padding: 0 5px;
--- a/build/unix/gnu-ld-scripts/jemalloc-standalone-linkage-version-script
+++ b/build/unix/gnu-ld-scripts/jemalloc-standalone-linkage-version-script
@@ -1,14 +1,15 @@
 {
    global:
 		_malloc_postfork;
 		_malloc_prefork;
 		jemalloc_stats;
 		malloc_usable_size;
+		je_malloc_usable_size_in_advance;
 		posix_memalign;
 		free;
 		realloc;
 		calloc;
 		malloc;
 		memalign;
 		valloc;
 		__free_hook;
--- a/caps/tests/mochitest/Makefile.in
+++ b/caps/tests/mochitest/Makefile.in
@@ -53,9 +53,9 @@ include $(topsrcdir)/config/rules.mk
 
 test_bug292789.html : % : %.in
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
 	     $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
 
 GARBAGE += test_bug292789.html
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/config/config.mk
+++ b/config/config.mk
@@ -51,16 +51,20 @@ endif
 INCLUDED_CONFIG_MK = 1
 
 EXIT_ON_ERROR = set -e; # Shell loops continue past errors without this.
 
 ifndef topsrcdir
 topsrcdir	= $(DEPTH)
 endif
 
+ifndef mochitestdir
+mochitestdir	= _mochitest
+endif
+
 ifndef INCLUDED_AUTOCONF_MK
 include $(DEPTH)/config/autoconf.mk
 endif
 
 COMMA = ,
 
 # Sanity check some variables
 CHECK_VARS := \
@@ -193,16 +197,21 @@ OS_CFLAGS += -FR
 OS_CXXFLAGS += -FR
 endif
 else # ! MOZ_DEBUG
 
 # MOZ_DEBUG_SYMBOLS generates debug symbols in separate PDB files.
 # Used for generating an optimized build with debugging symbols.
 # Used in the Windows nightlies to generate symbols for crash reporting.
 ifdef MOZ_DEBUG_SYMBOLS
+ifeq ($(AS),yasm)
+ASFLAGS += -g cv8
+else
+ASFLAGS += -Zi
+endif
 OS_CXXFLAGS += -Zi -UDEBUG -DNDEBUG
 OS_CFLAGS += -Zi -UDEBUG -DNDEBUG
 ifdef HAVE_64BIT_OS
 OS_LDFLAGS += -DEBUG -OPT:REF,ICF
 else
 OS_LDFLAGS += -DEBUG -OPT:REF
 endif
 endif
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -800,21 +800,21 @@ endif
 
 # In the second pass, we need to merge the pgc files into the pgd file.
 # The compiler would do this for us automatically if they were in the right
 # place, but they're in dist/bin.
 ifneq (,$(SHARED_LIBRARY)$(PROGRAM))
 export::
 ifdef PROGRAM
 	$(PYTHON) $(topsrcdir)/build/win32/pgomerge.py \
-	  $(PROGRAM:$(BIN_SUFFIX)=) $(DIST)/$(MOZ_APP_NAME)
+	  $(PROGRAM:$(BIN_SUFFIX)=) $(DIST)/bin
 endif
 ifdef SHARED_LIBRARY
 	$(PYTHON) $(topsrcdir)/build/win32/pgomerge.py \
-	  $(SHARED_LIBRARY_NAME) $(DIST)/$(MOZ_APP_NAME)
+	  $(SHARED_LIBRARY_NAME) $(DIST)/bin
 endif
 endif # SHARED_LIBRARY || PROGRAM
 endif # WINNT_
 endif # MOZ_PROFILE_USE
 ifdef MOZ_PROFILE_GENERATE
 # Clean up profiling data during PROFILE_GENERATE phase
 export::
 ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_)
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/693212.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+  function boom() { 
+    select = document.createElementNS("http://www.w3.org/1999/xhtml", "select");
+    text = document.body.childNodes[0];
+    select.setAttribute("onDOMSubtreeModified", "text.parentNode.removeChild(text);");
+    select.appendChild(text);
+  }
+</script>
+</head>
+
+<body onload="boom()">
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/698974-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+document.createDocumentFragment().querySelector("div");
+</script>
--- a/content/base/crashtests/crashtests.list
+++ b/content/base/crashtests/crashtests.list
@@ -91,8 +91,10 @@ load 637214-1.svg
 load 637214-2.svg
 load 642022-1.html
 load 646184.html
 load 658845-1.svg
 load 667336-1.html
 load 679459.html
 load 679689-1.html
 load 682463.html
+load 693212.xhtml
+load 698974-1.html
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -740,20 +740,20 @@ public:
    * Returns the element which either requested DOM full-screen mode, or
    * contains the element which requested DOM full-screen mode if the
    * requestee is in a subdocument. Note this element must be *in*
    * this document.
    */
   virtual Element* GetFullScreenElement() = 0;
 
   /**
-   * Requests that the document make aElement the full-screen element,
-   * and move into full-screen mode.
+   * Asynchronously requests that the document make aElement the full-screen
+   * element, and move into full-screen mode.
    */
-  virtual void RequestFullScreen(Element* aElement) = 0;
+  virtual void AsyncRequestFullScreen(Element* aElement) = 0;
 
   /**
    * Requests that the document, and all documents in its hierarchy exit
    * from DOM full-screen mode.
    */
   virtual void CancelFullScreen() = 0;
 
   /**
@@ -1873,16 +1873,25 @@ NS_NewDOMDocument(nsIDOMDocument** aInst
                   const nsAString& aQualifiedName, 
                   nsIDOMDocumentType* aDoctype,
                   nsIURI* aDocumentURI,
                   nsIURI* aBaseURI,
                   nsIPrincipal* aPrincipal,
                   bool aLoadedAsData,
                   nsIScriptGlobalObject* aEventObject,
                   bool aSVGDocument);
+
+// This is used only for xbl documents created from the startup cache.
+// Non-cached documents are created in the same manner as xml documents.
+nsresult
+NS_NewXBLDocument(nsIDOMDocument** aInstancePtrResult,
+                  nsIURI* aDocumentURI,
+                  nsIURI* aBaseURI,
+                  nsIPrincipal* aPrincipal);
+
 nsresult
 NS_NewPluginDocument(nsIDocument** aInstancePtrResult);
 
 inline nsIDocument*
 nsINode::GetOwnerDocument() const
 {
   nsIDocument* ownerDoc = OwnerDoc();
 
--- a/content/base/public/nsINameSpaceManager.h
+++ b/content/base/public/nsINameSpaceManager.h
@@ -42,30 +42,32 @@
 #include "nsStringGlue.h"
 
 class nsIAtom;
 class nsString;
 
 #define kNameSpaceID_Unknown -1
 // 0 is special at C++, so use a static const PRInt32 for
 // kNameSpaceID_None to keep if from being cast to pointers
+// Note that the XBL cache assumes (and asserts) that it can treat a
+// single-byte value higher than kNameSpaceID_LastBuiltin specially. 
 static const PRInt32 kNameSpaceID_None = 0;
 #define kNameSpaceID_XMLNS    1 // not really a namespace, but it needs to play the game
 #define kNameSpaceID_XML      2
 #define kNameSpaceID_XHTML    3
 #define kNameSpaceID_XLink    4
 #define kNameSpaceID_XSLT     5
 #define kNameSpaceID_XBL      6
 #define kNameSpaceID_MathML   7
 #define kNameSpaceID_RDF      8
 #define kNameSpaceID_XUL      9
 #define kNameSpaceID_SVG      10
 #define kNameSpaceID_XMLEvents 11
 #define kNameSpaceID_LastBuiltin          11 // last 'built-in' namespace
-
+ 
 #define NS_NAMESPACEMANAGER_CONTRACTID "@mozilla.org/content/namespacemanager;1"
 
 #define NS_INAMESPACEMANAGER_IID \
   { 0xd74e83e6, 0xf932, 0x4289, \
     { 0xac, 0x95, 0x9e, 0x10, 0x24, 0x30, 0x88, 0xd6 } }
 
 /**
  * The Name Space Manager tracks the association between a NameSpace
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -45,16 +45,17 @@
 #include "nsINodeInfo.h"
 #include "nsCOMPtr.h"
 #include "nsWrapperCache.h"
 #include "nsIProgrammingLanguage.h" // for ::JAVASCRIPT
 #include "nsDOMError.h"
 #include "nsDOMString.h"
 #include "jspubtd.h"
 #include "nsDOMMemoryReporter.h"
+#include "nsIVariant.h"
 
 // Including 'windows.h' will #define GetClassInfo to something else.
 #ifdef XP_WIN
 #ifdef GetClassInfo
 #undef GetClassInfo
 #endif
 #endif
 
@@ -71,17 +72,16 @@ class nsEventChainPreVisitor;
 class nsEventChainPostVisitor;
 class nsEventListenerManager;
 class nsIPrincipal;
 class nsIMutationObserver;
 class nsChildContentList;
 class nsNodeWeakReference;
 class nsNodeSupportsWeakRefTearoff;
 class nsIEditor;
-class nsIVariant;
 class nsIDOMUserDataHandler;
 class nsAttrAndChildArray;
 class nsXPCClassInfo;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -209,18 +209,23 @@
 #include "imgILoader.h"
 #include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
+// Reference to the document which requested DOM full-screen mode.
 nsWeakPtr nsDocument::sFullScreenDoc = nsnull;
 
+// Reference to the root document of the branch containing the document
+// which requested DOM full-screen mode.
+nsWeakPtr nsDocument::sFullScreenRootDoc = nsnull;
+
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
 
 nsIdentifierMapEntry::~nsIdentifierMapEntry()
@@ -5015,24 +5020,22 @@ nsDocument::GetAnonymousNodes(nsIDOMElem
 
   nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
   return BindingManager()->GetAnonymousNodesFor(content, aResult);
 }
 
 NS_IMETHODIMP
 nsDocument::CreateRange(nsIDOMRange** aReturn)
 {
-  nsresult rv = NS_NewRange(aReturn);
-
-  if (NS_SUCCEEDED(rv)) {
-    (*aReturn)->SetStart(this, 0);
-    (*aReturn)->SetEnd(this, 0);
-  }
-
-  return rv;
+  nsRefPtr<nsRange> range = new nsRange();
+  nsresult rv = range->Set(this, 0, this, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  range.forget(aReturn);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::CreateNodeIterator(nsIDOMNode *aRoot,
                                PRUint32 aWhatToShow,
                                nsIDOMNodeFilter *aFilter,
                                bool aEntityReferenceExpansion,
                                nsIDOMNodeIterator **_retval)
@@ -7298,16 +7301,31 @@ nsDocument::OnPageShow(bool aPersisted,
 static bool
 NotifyPageHide(nsIDocument* aDocument, void* aData)
 {
   const bool* aPersistedPtr = static_cast<const bool*>(aData);
   aDocument->OnPageHide(*aPersistedPtr, nsnull);
   return true;
 }
 
+static bool
+SetFullScreenState(nsIDocument* aDoc, Element* aElement, bool aIsFullScreen);
+
+static void
+SetWindowFullScreen(nsIDocument* aDoc, bool aValue);
+
+static bool
+ResetFullScreen(nsIDocument* aDocument, void* aData) {
+  if (aDocument->IsFullScreenDoc()) {
+    ::SetFullScreenState(aDocument, nsnull, false);
+    aDocument->EnumerateSubDocuments(ResetFullScreen, nsnull);
+  }
+  return true;
+}
+
 void
 nsDocument::OnPageHide(bool aPersisted,
                        nsIDOMEventTarget* aDispatchStartTarget)
 {
   // Send out notifications that our <link> elements are detached,
   // but only if this is not a full unload.
   Element* root = GetRootElement();
   if (aPersisted && root) {
@@ -7349,16 +7367,39 @@ nsDocument::OnPageHide(bool aPersisted,
   DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
 
   mVisible = false;
 
   UpdateVisibilityState();
   
   EnumerateExternalResources(NotifyPageHide, &aPersisted);
   EnumerateFreezableElements(NotifyActivityChanged, nsnull);
+
+  if (IsFullScreenDoc()) {
+    // A full-screen doc has been hidden. We need to ensure we exit
+    // full-screen, i.e. remove full-screen state from all full-screen
+    // documents, and exit the top-level window from full-screen mode.
+    // Unfortunately by the time a doc is hidden, it has been removed
+    // from the doc tree, so we can't just call CancelFullScreen()...
+
+    // So firstly reset full-screen state in *this* document. OnPageHide()
+    // is called in every hidden document, so doing this ensures all hidden
+    // documents have their state reset.
+    ::SetFullScreenState(this, nsnull, false);
+
+    // Next walk the document tree of still visible documents, and reset
+    // their full-screen state. We then move the top-level window out
+    // of full-screen mode.
+    nsCOMPtr<nsIDocument> fullScreenRoot(do_QueryReferent(sFullScreenRootDoc));
+    if (fullScreenRoot) {
+      fullScreenRoot->EnumerateSubDocuments(ResetFullScreen, nsnull);
+      SetWindowFullScreen(fullScreenRoot, false);
+      sFullScreenRootDoc = nsnull;
+    }
+  }
 }
 
 void
 nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
 {
   NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
                mSubtreeModifiedTargets.Count() == 0,
                "mSubtreeModifiedTargets not cleared after dispatching?");
@@ -8453,16 +8494,17 @@ nsDocument::CancelFullScreen()
   nsCOMPtr<nsIDocument> doc(do_QueryReferent(sFullScreenDoc));
   while (doc != nsnull) {
     if (::SetFullScreenState(doc, nsnull, false)) {
       DispatchFullScreenChange(doc);
     }
     doc = doc->GetParentDocument();
   }
   sFullScreenDoc = nsnull;
+  sFullScreenRootDoc = nsnull;
 
   // Move the window out of full-screen mode.
   SetWindowFullScreen(this, false);
 
   return;
 }
 
 bool
@@ -8497,28 +8539,76 @@ GetCommonAncestor(nsIDocument* aDoc1, ns
     if (child1 != child2) {
       break;
     }
     parent = child1;
   }
   return parent;
 }
 
-void
-nsDocument::RequestFullScreen(Element* aElement)
-{
-  if (!aElement || !nsContentUtils::IsFullScreenApiEnabled() || !GetWindow()) {
-    if (aElement) {
-      nsRefPtr<nsPLDOMEvent> e =
-        new nsPLDOMEvent(aElement,
-                         NS_LITERAL_STRING("mozfullscreenerror"),
-                         true,
-                         false);
-      e->PostDOMEvent();
-    }
+// Returns the root document in a document hierarchy.
+static nsIDocument*
+GetRootDocument(nsIDocument* aDoc)
+{
+  if (!aDoc)
+    return nsnull;
+  nsIDocument* doc = aDoc;
+  while (doc->GetParentDocument()) {
+    doc = doc->GetParentDocument();
+  }
+  return doc;
+}
+
+class nsCallRequestFullScreen : public nsRunnable
+{
+public:
+  nsCallRequestFullScreen(Element* aElement)
+    : mElement(aElement),
+      mDoc(aElement->OwnerDoc()),
+      mWasCallerChrome(nsContentUtils::IsCallerChrome())
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    nsDocument* doc = static_cast<nsDocument*>(mDoc.get());
+    doc->RequestFullScreen(mElement, mWasCallerChrome);
+    return NS_OK;
+  }
+
+  nsRefPtr<Element> mElement;
+  nsCOMPtr<nsIDocument> mDoc;
+  bool mWasCallerChrome;
+};
+
+void
+nsDocument::AsyncRequestFullScreen(Element* aElement)
+{
+  if (!aElement) {
+    return;
+  }
+  // Request full-screen asynchronously.
+  nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement));
+  NS_DispatchToCurrentThread(event);
+}
+
+void
+nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome)
+{
+  if (!aElement ||
+      !aElement->IsInDoc() ||
+      aElement->OwnerDoc() != this ||
+      !IsFullScreenEnabled(aWasCallerChrome) ||
+      !GetWindow()) {
+    nsRefPtr<nsPLDOMEvent> e =
+      new nsPLDOMEvent(this,
+                       NS_LITERAL_STRING("mozfullscreenerror"),
+                       true,
+                       false);
+    e->PostDOMEvent();
     return;
   }
 
   // Turn off full-screen state in all documents which were previously
   // full-screen but which shouldn't be after this request is granted.
   // Note commonAncestor will be null when in a separate browser window
   // to the requesting document.
   nsIDocument* commonAncestor = nsnull;
@@ -8535,16 +8625,20 @@ nsDocument::RequestFullScreen(Element* a
   }
   if (!commonAncestor && fullScreenDoc) {
     // Other doc is in another browser window. Move it out of full-screen.
     // Note that nsGlobalWindow::SetFullScreen() proxies to the root window
     // in its hierarchy, and does not operate on the a per-nsIDOMWindow basis.
     SetWindowFullScreen(fullScreenDoc, false);
   }
 
+  // Remember the root document, so that if a full-screen document is hidden
+  // we can reset full-screen state the remaining visible full-screen documents.
+  sFullScreenRootDoc = do_GetWeakReference(GetRootDocument(this));
+
   // Set the full-screen element. This sets the full-screen style on the
   // element, and the full-screen-ancestor styles on ancestors of the element
   // in this document.
   if (SetFullScreenState(aElement, true)) {
     DispatchFullScreenChange(aElement);
   }
 
   // Propagate up the document hierarchy, setting the full-screen element as
@@ -8599,49 +8693,53 @@ nsDocument::GetMozFullScreen(bool *aFull
   *aFullScreen = IsFullScreenDoc();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
 {
   NS_ENSURE_ARG_POINTER(aFullScreen);
-  *aFullScreen = false;
-
-  if (nsContentUtils::IsCallerChrome() &&
-      nsContentUtils::IsFullScreenApiEnabled()) {
+  *aFullScreen = IsFullScreenEnabled(nsContentUtils::IsCallerChrome());
+  return NS_OK;
+}
+
+bool
+nsDocument::IsFullScreenEnabled(bool aCallerIsChrome)
+{
+  if (nsContentUtils::IsFullScreenApiEnabled() && aCallerIsChrome) {
     // Chrome code can always use the full-screen API, provided it's not
-    // explicitly disabled.
-    *aFullScreen = true;
-    return NS_OK;
+    // explicitly disabled. Note IsCallerChrome() returns true when running
+    // in an nsRunnable, so don't use GetMozFullScreenEnabled() from an
+    // nsRunnable!
+    return true;
   }
 
   if (!nsContentUtils::IsFullScreenApiEnabled() ||
       nsContentUtils::HasPluginWithUncontrolledEventDispatch(this) ||
       !IsVisible()) {
-    return NS_OK;
+    return false;
   }
 
   // Ensure that all ancestor <iframe> elements have the mozallowfullscreen
   // boolean attribute set.
   nsINode* node = static_cast<nsINode*>(this);
   do {
     nsIContent* content = static_cast<nsIContent*>(node);
     if (content->IsHTML(nsGkAtoms::iframe) &&
         !content->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) {
       // The node requesting fullscreen, or one of its crossdoc ancestors,
       // is an iframe which doesn't have the "mozalllowfullscreen" attribute.
       // This request is not authorized by the parent document.
-      return NS_OK;
+      return false;
     }
     node = nsContentUtils::GetCrossDocParentNode(node);
   } while (node);
 
-  *aFullScreen = true;
-  return NS_OK;
+  return true;
 }
 
 PRInt64
 nsDocument::SizeOf() const
 {
   PRInt64 size = MemoryReporter::GetBasicSize<nsDocument, nsIDocument>(this);
   size += mAttrStyleSheet ? mAttrStyleSheet->DOMSizeOf() : 0;
   return size;
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -888,16 +888,20 @@ public:
   void DoNotifyPossibleTitleChange();
 
   nsExternalResourceMap& ExternalResourceMap()
   {
     return mExternalResourceMap;
   }
 
   void SetLoadedAsData(bool aLoadedAsData) { mLoadedAsData = aLoadedAsData; }
+  void SetLoadedAsInteractiveData(bool aLoadedAsInteractiveData)
+  {
+    mLoadedAsInteractiveData = aLoadedAsInteractiveData;
+  }
 
   nsresult CloneDocHelper(nsDocument* clone) const;
 
   void MaybeInitializeFinalizeFrameLoaders();
 
   void MaybeEndOutermostXBLUpdate();
 
   virtual void MaybePreLoadImage(nsIURI* uri,
@@ -943,33 +947,46 @@ public:
   virtual nsresult GetStateObject(nsIVariant** aResult);
 
   virtual nsDOMNavigationTiming* GetNavigationTiming() const;
   virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming);
 
   virtual Element* FindImageMap(const nsAString& aNormalizedMapName);
 
   virtual Element* GetFullScreenElement();
-  virtual void RequestFullScreen(Element* aElement);
+  virtual void AsyncRequestFullScreen(Element* aElement);
   virtual void CancelFullScreen();
   virtual bool IsFullScreenDoc();
 
+  // This is called asynchronously by nsIDocument::AsyncRequestFullScreen()
+  // to move document into full-screen mode if allowed. aWasCallerChrome
+  // should be true when nsIDocument::AsyncRequestFullScreen() was called
+  // by chrome code.
+  void RequestFullScreen(Element* aElement, bool aWasCallerChrome);
+
   // Returns true if making this change results in a change in the full-screen
   // state of this document.
   bool SetFullScreenState(Element* aElement, bool aIsFullScreen);
  
   // This method may fire a DOM event; if it does so it will happen
   // synchronously.
   void UpdateVisibilityState();
   // Posts an event to call UpdateVisibilityState
   virtual void PostVisibilityUpdateEvent();
 
 protected:
   friend class nsNodeUtils;
 
+  // Returns true if a request for DOM full-screen is currently enabled in
+  // this document. This returns true if there are no windowed plugins in this
+  // doc tree, and if the document is visible, and if the api is not
+  // disabled by pref. aIsCallerChrome must contain the return value of
+  // nsContentUtils::IsCallerChrome() from the context we're checking.
+  bool IsFullScreenEnabled(bool aIsCallerChrome);
+
   /**
    * Check that aId is not empty and log a message to the console
    * service if it is.
    * @returns true if aId looks correct, false otherwise.
    */
   inline bool CheckGetElementByIdArg(const nsAString& aId)
   {
     if (aId.IsEmpty()) {
@@ -1074,16 +1091,23 @@ protected:
   // that, unlike mScriptGlobalObject, is never unset once set. This
   // is a weak reference to avoid leaks due to circular references.
   nsWeakPtr mScopeObject;
 
   // The document which requested (and was granted) full-screen. All ancestors
   // of this document will also be full-screen.
   static nsWeakPtr sFullScreenDoc;
 
+  // The root document of the doctree containing the document which requested
+  // full-screen. This root document will also be in full-screen state, as will
+  // all the descendents down to the document which requested full-screen. This
+  // reference allows us to reset full-screen state on all documents when a
+  // document is hidden/navigation occurs.
+  static nsWeakPtr sFullScreenRootDoc;
+
   nsRefPtr<nsEventListenerManager> mListenerManager;
   nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
   nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   nsRefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
    *    entries as needed).
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -5394,30 +5394,33 @@ inline static nsresult FindMatchingEleme
   TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
                                    doc);
 
   // Fast-path selectors involving IDs.  We can only do this if aRoot
   // is in the document and the document is not in quirks mode, since
   // ID selectors are case-insensitive in quirks mode.  Also, only do
   // this if selectorList only has one selector, because otherwise
   // ordering the elements correctly is a pain.
-  NS_ASSERTION(aRoot->IsElement() || aRoot->IsNodeOfType(nsINode::eDOCUMENT),
-               "Unexpected root node");
+  NS_ASSERTION(aRoot->IsElement() || aRoot->IsNodeOfType(nsINode::eDOCUMENT) ||
+               !aRoot->IsInDoc(),
+               "The optimization below to check ContentIsDescendantOf only for "
+               "elements depends on aRoot being either an element or a "
+               "document if it's in the document.");
   if (aRoot->IsInDoc() &&
       doc->GetCompatibilityMode() != eCompatibility_NavQuirks &&
       !selectorList->mNext &&
       selectorList->mSelectors->mIDList) {
     nsIAtom* id = selectorList->mSelectors->mIDList->mAtom;
     const nsSmallVoidArray* elements =
       doc->GetAllElementsForId(nsDependentAtomString(id));
 
     // XXXbz: Should we fall back to the tree walk if aRoot is not the
     // document and |elements| is long, for some value of "long"?
     if (elements) {
-      for (PRUint32 i = 0; i < elements->Count(); ++i) {
+      for (PRInt32 i = 0; i < elements->Count(); ++i) {
         Element *element = static_cast<Element*>(elements->ElementAt(i));
         if (!aRoot->IsElement() ||
             nsContentUtils::ContentIsDescendantOf(element, aRoot)) {
           // We have an element with the right id and it's a descendant
           // of aRoot.  Make sure it really matches the selector.
           if (nsCSSRuleProcessor::SelectorListMatches(element, matchingContext,
                                                       selectorList)) {
             aList.AppendElement(element);
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -503,17 +503,17 @@ nsObjectLoadingContent::~nsObjectLoading
   }
 }
 
 // nsIRequestObserver
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
                                        nsISupports *aContext)
 {
-  if (aRequest != mChannel) {
+  if (aRequest != mChannel || !aRequest) {
     // This is a bit of an edge case - happens when a new load starts before the
     // previous one got here
     return NS_BINDING_ABORTED;
   }
 
   AutoNotifier notifier(this, true);
 
   if (!IsSuccessfulRequest(aRequest)) {
@@ -775,16 +775,18 @@ nsObjectLoadingContent::OnStartRequest(n
   return NS_BINDING_ABORTED;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStopRequest(nsIRequest *aRequest,
                                       nsISupports *aContext,
                                       nsresult aStatusCode)
 {
+  NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
+
   if (aRequest != mChannel) {
     return NS_BINDING_ABORTED;
   }
 
   mChannel = nsnull;
 
   if (mFinalListener) {
     mFinalListener->OnStopRequest(aRequest, aContext, aStatusCode);
@@ -793,18 +795,23 @@ nsObjectLoadingContent::OnStopRequest(ns
 
   // Return value doesn't matter
   return NS_OK;
 }
 
 
 // nsIStreamListener
 NS_IMETHODIMP
-nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext, nsIInputStream *aInputStream, PRUint32 aOffset, PRUint32 aCount)
+nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest,
+                                        nsISupports *aContext,
+                                        nsIInputStream *aInputStream,
+                                        PRUint32 aOffset, PRUint32 aCount)
 {
+  NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
+
   if (aRequest != mChannel) {
     return NS_BINDING_ABORTED;
   }
 
   if (mFinalListener) {
     return mFinalListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
   }
 
--- a/content/base/src/nsPlainTextSerializer.cpp
+++ b/content/base/src/nsPlainTextSerializer.cpp
@@ -302,38 +302,39 @@ nsPlainTextSerializer::AppendText(nsICon
     
   NS_ASSERTION(aStartOffset >= 0, "Negative start offset for text fragment!");
   if ( aStartOffset < 0 )
     return NS_ERROR_INVALID_ARG;
 
   NS_ENSURE_ARG(aText);
 
   nsresult rv = NS_OK;
-  PRInt32 length = 0;
-  nsAutoString textstr;
 
   nsIContent* content = aText;
   const nsTextFragment* frag;
   if (!content || !(frag = content->GetText())) {
     return NS_ERROR_FAILURE;
   }
   
   PRInt32 endoffset = (aEndOffset == -1) ? frag->GetLength() : aEndOffset;
   NS_ASSERTION(aStartOffset <= endoffset, "A start offset is beyond the end of the text fragment!");
 
-  length = endoffset - aStartOffset;
+  PRInt32 length = endoffset - aStartOffset;
   if (length <= 0) {
     return NS_OK;
   }
 
+  nsAutoString textstr;
   if (frag->Is2b()) {
     textstr.Assign(frag->Get2b() + aStartOffset, length);
   }
   else {
-    textstr.AssignWithConversion(frag->Get1b()+aStartOffset, length);
+    // AssignASCII is for 7-bit character only, so don't use it
+    const char *data = frag->Get1b();
+    CopyASCIItoUTF16(Substring(data + aStartOffset, data + endoffset), textstr);
   }
 
   mOutputString = &aStr;
 
   // We have to split the string across newlines
   // to match parser behavior
   PRInt32 start = 0;
   PRInt32 offset = textstr.FindCharInSet("\n\r");
--- a/content/base/src/nsRange.h
+++ b/content/base/src/nsRange.h
@@ -93,16 +93,27 @@ public:
   
   // nsIRange interface
   virtual nsINode* GetCommonAncestor() const;
   virtual void Reset();
   virtual nsresult SetStart(nsINode* aParent, PRInt32 aOffset);
   virtual nsresult SetEnd(nsINode* aParent, PRInt32 aOffset);
   virtual nsresult CloneRange(nsIRange** aNewRange) const;
 
+  nsresult Set(nsINode* aStartParent, PRInt32 aStartOffset,
+               nsINode* aEndParent, PRInt32 aEndOffset)
+  {
+    // If this starts being hot, we may be able to optimize this a bit,
+    // but for now just set start and end separately.
+    nsresult rv = SetStart(aStartParent, aStartOffset);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return SetEnd(aEndParent, aEndOffset);
+  }
+
   NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult);
 
   // nsIMutationObserver methods
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
 
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -541,19 +541,19 @@ endif
 #		test_bug503473.html \
 #		file_bug503473-frame.sjs \
 
 _BROWSER_TEST_FILES = \
 		browser_bug593387.js \
 		$(NULL)
 
 libs:: $(_TEST_FILES1)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs:: $(_TEST_FILES2)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
 
--- a/content/base/test/chrome/Makefile.in
+++ b/content/base/test/chrome/Makefile.in
@@ -69,12 +69,12 @@ include $(topsrcdir)/config/rules.mk
     test_bug357450.xul \
     test_bug571390.xul \
     test_bug574596.html \
     test_bug683852.xul \
     test_bug599295.html \
     $(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -990,17 +990,17 @@ bool WebGLContext::IsExtensionSupported(
             MakeContextCurrent();
             isSupported = gl->IsExtensionSupported(gl->IsGLES2() ? GLContext::OES_texture_float 
                                                                  : GLContext::ARB_texture_float);
 	    break;
         case WebGL_OES_standard_derivatives:
             // We always support this extension.
             isSupported = true;
             break;
-        case WebGL_WEBKIT_lose_context:
+        case WebGL_WEBGL_EXT_lose_context:
             // We always support this extension.
             isSupported = true;
             break;
         default:
             isSupported = false;
     }
 
     return isSupported;
@@ -1022,28 +1022,28 @@ WebGLContext::GetExtension(const nsAStri
     if (aName.EqualsLiteral("OES_texture_float")) {
         if (IsExtensionSupported(WebGL_OES_texture_float))
             ei = WebGL_OES_texture_float;
     }
     else if (aName.EqualsLiteral("OES_standard_derivatives")) {
         if (IsExtensionSupported(WebGL_OES_standard_derivatives))
             ei = WebGL_OES_standard_derivatives;
     }
-    else if (aName.EqualsLiteral("WEBKIT_lose_context")) {
-        if (IsExtensionSupported(WebGL_WEBKIT_lose_context))
-            ei = WebGL_WEBKIT_lose_context;
+    else if (aName.EqualsLiteral("WEBGL_EXT_lose_context")) {
+        if (IsExtensionSupported(WebGL_WEBGL_EXT_lose_context))
+            ei = WebGL_WEBGL_EXT_lose_context;
     }
 
     if (ei != WebGLExtensionID_Max) {
         if (!IsExtensionEnabled(ei)) {
             switch (ei) {
                 case WebGL_OES_standard_derivatives:
                     mEnabledExtensions[ei] = new WebGLExtensionStandardDerivatives(this);
                     break;
-                case WebGL_WEBKIT_lose_context:
+                case WebGL_WEBGL_EXT_lose_context:
                     mEnabledExtensions[ei] = new WebGLExtensionLoseContext(this);
                     break;
                 // create an extension for any types that don't
                 // have any additional tokens or methods
                 default:
                     mEnabledExtensions[ei] = new WebGLExtension(this);
                     break;
             }
@@ -1469,16 +1469,18 @@ WebGLContext::GetSupportedExtensions(nsI
     NS_ENSURE_TRUE(wrval, NS_ERROR_FAILURE);
 
     nsTArray<const char *> extList;
 
     if (IsExtensionSupported(WebGL_OES_texture_float))
         extList.InsertElementAt(extList.Length(), "OES_texture_float");
     if (IsExtensionSupported(WebGL_OES_standard_derivatives))
         extList.InsertElementAt(extList.Length(), "OES_standard_derivatives");
+    if (IsExtensionSupported(WebGL_WEBGL_EXT_lose_context))
+        extList.InsertElementAt(extList.Length(), "WEBGL_EXT_lose_context");
 
     nsresult rv;
     if (extList.Length() > 0) {
         rv = wrval->SetAsArray(nsIDataType::VTYPE_CHAR_STR, nsnull,
                                extList.Length(), &extList[0]);
     } else {
         rv = wrval->SetAsEmptyArray();
     }
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -521,17 +521,17 @@ protected:
     PRInt32 mGLMaxVaryingVectors;
     PRInt32 mGLMaxFragmentUniformVectors;
     PRInt32 mGLMaxVertexUniformVectors;
 
     // extensions
     enum WebGLExtensionID {
         WebGL_OES_texture_float,
         WebGL_OES_standard_derivatives,
-        WebGL_WEBKIT_lose_context,
+        WebGL_WEBGL_EXT_lose_context,
         WebGLExtensionID_Max
     };
     nsCOMPtr<WebGLExtension> mEnabledExtensions[WebGLExtensionID_Max];
     bool IsExtensionEnabled(WebGLExtensionID ext) const {
         NS_ABORT_IF_FALSE(ext >= 0 && ext < WebGLExtensionID_Max, "bogus index!");
         return mEnabledExtensions[ext] != nsnull;
     }
     bool IsExtensionSupported(WebGLExtensionID ei);
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -174,9 +174,9 @@ endif
 #	test_2d.gradient.radial.cone.behind.html \
 #	test_2d.gradient.radial.cone.beside.html \
 
 # This test is bogus according to the spec; see bug 407107
 # test_2d.path.rect.zero.6.html
 
 # split up into groups to work around command-line length limits
 libs:: $(_TEST_FILES_0)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/content/canvas/test/crossorigin/Makefile.in
+++ b/content/canvas/test/crossorigin/Makefile.in
@@ -49,9 +49,9 @@ include $(topsrcdir)/config/rules.mk
 	image-allow-star.png \
 	image-allow-star.png^headers^ \
 	image.png \
 	test_canvas2d_crossorigin.html \
 	test_webgl_crossorigin_textures.html \
 	$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/content/canvas/test/webgl/Makefile.in
+++ b/content/canvas/test/webgl/Makefile.in
@@ -47,13 +47,13 @@ include $(topsrcdir)/config/rules.mk
   test_webgl_conformance_test_suite.html \
   00_test_list.txt \
   failing_tests_linux.txt \
   failing_tests_windows.txt \
   failing_tests_mac.txt \
   $(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 	$(TAR) -cf - -C $(srcdir) \
 	  resources \
 	  conformance \
-	  | $(TAR) -xf - -C $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	  | $(TAR) -xf - -C $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/content/canvas/test/webgl/resources/js-test-pre.js
+++ b/content/canvas/test/webgl/resources/js-test-pre.js
@@ -129,19 +129,20 @@ function testFailedRender(msg, ref, test
     if (typeof test.getImageData == 'function') {
         testData = test.canvas.toDataURL();
     } else {
         testData = arrayToURLData(test, width, height);
     }
     
     testFailed(msg);
 
-    var data = 'REFTEST TEST-UNEXPECTED-FAIL | ' + msg + ' | image comparison (==)\n' +
+    var data = 'REFTEST TEST-KNOWN-FAIL | ' + msg + ' | image comparison (==)\n' +
                'REFTEST   IMAGE 1 (TEST): ' + testData + '\n' +
                'REFTEST   IMAGE 2 (REFERENCE): ' + refData;
+    dump('The following information is for debugging purposes only. It will always print TEST-KNOWN-FAIL, even if it is unexpected.');
     dump('FAIL: ' + data + '\n');
     dump('To view the differences between these image renderings, go to the following link: https://hg.mozilla.org/mozilla-central/raw-file/tip/layout/tools/reftest/reftest-analyzer.xhtml#log=' +
     encodeURIComponent(encodeURIComponent(data)) + '\n');
 }
 
 function arrayToURLData(buf, width, height)
 {
     var cv = document.createElement('canvas');
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -417,16 +417,24 @@ nsDOMEvent::GetTimeStamp(PRUint64* aTime
 
 NS_IMETHODIMP
 nsDOMEvent::StopPropagation()
 {
   mEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMEvent::StopImmediatePropagation()
+{
+  mEvent->flags |=
+    (NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY | NS_EVENT_FLAG_STOP_DISPATCH);
+  return NS_OK;
+}
+
 static nsIDocument* GetDocumentForReport(nsEvent* aEvent)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aEvent->currentTarget);
   if (node)
     return node->OwnerDoc();
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aEvent->currentTarget);
   if (!window)
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -371,19 +371,19 @@ nsEventTargetChainItem::HandleEventTarge
       }
     }
     item = item->mParent;
   }
   aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_BUBBLE;
 
   if (!(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT)) {
     // Dispatch to the system event group.  Make sure to clear the
-    // STOP_DISPATCH flag since this resets for each event group
-    // per DOM3 Events.
-    aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_STOP_DISPATCH;
+    // STOP_DISPATCH flag since this resets for each event group.
+    aVisitor.mEvent->flags &=
+      ~(NS_EVENT_FLAG_STOP_DISPATCH | NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY);
 
     // Setting back the original target of the event.
     aVisitor.mEvent->target = aVisitor.mEvent->originalTarget;
 
     // Special handling if PresShell (or some other caller)
     // used a callback object.
     if (aCallback) {
       aCallback->HandleEvent(aVisitor);
@@ -391,16 +391,21 @@ nsEventTargetChainItem::HandleEventTarge
 
     // Retarget for system event group (which does the default handling too).
     // Setting back the target which was used also for default event group.
     aVisitor.mEvent->target = firstTarget;
     HandleEventTargetChain(aVisitor, aFlags | NS_EVENT_FLAG_SYSTEM_EVENT,
                            aCallback,
                            createdELMs != nsEventListenerManager::sCreatedCount,
                            aPusher);
+
+    // After dispatch, clear all the propagation flags so that
+    // system group listeners don't affect to the event.
+    aVisitor.mEvent->flags &=
+      ~(NS_EVENT_FLAG_STOP_DISPATCH | NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY);
   }
 
   return NS_OK;
 }
 
 #define NS_CHAIN_POOL_SIZE 128
 
 class ChainItemPool {
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -759,16 +759,19 @@ nsEventListenerManager::HandleEventInter
   if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
     aEvent->flags |= NS_EVENT_FLAG_NO_DEFAULT;
   }
 
   nsAutoTObserverArray<nsListenerStruct, 2>::EndLimitedIterator iter(mListeners);
   nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
   bool hasListener = false;
   while (iter.HasMore()) {
+    if (aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY) {
+      break;
+    }
     nsListenerStruct* ls = &iter.GetNext();
     // Check that the phase is same in event and event listener.
     // Handle only trusted events, except when listener permits untrusted events.
     if (ListenerCanHandle(ls, aEvent)) {
       hasListener = true;
       // XXX The (mFlags & aFlags) test here seems fragile. Shouldn't we
       // specifically only test the capture/bubble flags.
       if ((ls->mFlags & aFlags & ~NS_EVENT_FLAG_SYSTEM_EVENT) &&
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -106,16 +106,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug656954.html \
 		test_bug659350.html \
 		test_bug662678.html \
 		test_bug667919-1.html \
 		test_bug667919-2.html \
 		test_bug667612.html \
 		empty.js \
 		test_bug689564.html \
+		test_bug698929.html \
 		$(NULL)
 
 #bug 585630
 ifneq (mobile,$(MOZ_BUILD_APP))
 _TEST_FILES += \
 		test_dragstart.html \
 		$(NULL)
 endif
@@ -141,12 +142,12 @@ endif
 		test_bug602962.xul \
 		test_bug617528.xul \
 		window_bug617528.xul \
 		test_bug679494.xul \
 		file_bug679494.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug698929.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=698929
+-->
+<head>
+  <title>Test for Bug 698929</title>
+  <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=698929">Mozilla Bug 698929</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 698929 **/
+
+  var e = document.createEvent("Event");
+  e.initEvent("foo", true, true);
+  var c = 0;
+  var b = 0;
+  document.addEventListener("foo",
+    function() {
+      ++c;
+    });
+  document.body.addEventListener("foo", function(e) {
+    ++b;
+    e.stopImmediatePropagation();
+  });
+  document.body.addEventListener("foo", function(e) {
+    ++b;
+  });
+  document.body.dispatchEvent(e);
+  document.documentElement.dispatchEvent(e);
+  is(c, 1, "Listener in the document should have been called once.");
+  is(b, 1, "Listener in the body should have been called once.");
+
+
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -3399,49 +3399,28 @@ nsGenericHTMLElement::Focus()
 
 nsresult nsGenericHTMLElement::MozRequestFullScreen()
 {
   // Only grant full-screen requests if this is called from inside a trusted
   // event handler (i.e. inside an event handler for a user initiated event).
   // This stops the full-screen from being abused similar to the popups of old,
   // and it also makes it harder for bad guys' script to go full-screen and
   // spoof the browser chrome/window and phish logins etc.
-  nsIDocument* doc = OwnerDoc();
-  if (!nsContentUtils::IsRequestFullScreenAllowed() ||
-      !IsInDoc()) {
+  if (!nsContentUtils::IsRequestFullScreenAllowed()) {
     nsRefPtr<nsPLDOMEvent> e =
-      new nsPLDOMEvent(this,
+      new nsPLDOMEvent(OwnerDoc(),
                        NS_LITERAL_STRING("mozfullscreenerror"),
                        true,
                        false);
     e->PostDOMEvent();
     return NS_OK;
   }
 
-  nsCOMPtr<nsIDOMDocument> domDocument(do_QueryInterface(doc));
-  NS_ENSURE_STATE(domDocument);
-  bool fullScreenEnabled;
-  domDocument->GetMozFullScreenEnabled(&fullScreenEnabled);
-  if (!fullScreenEnabled) {
-    nsRefPtr<nsPLDOMEvent> e =
-      new nsPLDOMEvent(this,
-                       NS_LITERAL_STRING("mozfullscreenerror"),
-                       true,
-                       false);
-    e->PostDOMEvent();
-    return NS_OK;
-  }
-
-  doc->RequestFullScreen(this);
-#ifdef DEBUG
-  bool fullscreen;
-  domDocument->GetMozFullScreen(&fullscreen);
-  NS_ASSERTION(fullscreen, "Document should report fullscreen");
-  NS_ASSERTION(doc->IsFullScreenDoc(), "Should be in full screen state!");
-#endif
+  OwnerDoc()->AsyncRequestFullScreen(this);
+
   return NS_OK;
 }
 
 nsresult nsGenericHTMLElement::Click()
 {
   if (HasFlag(NODE_HANDLING_CLICK))
     return NS_OK;
 
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -278,17 +278,19 @@ include $(topsrcdir)/config/rules.mk
 		test_bug677658.html \
 		test_bug677463.html \
 		test_bug682886.html \
 		file_fullscreen-api.html \
 		file_fullscreen-api-keys.html \
 		test_fullscreen-api.html \
 		file_fullscreen-plugins.html \
 		file_fullscreen-denied.html \
+		file_fullscreen-denied-inner.html \
 		file_fullscreen-hidden.html \
+		file_fullscreen-navigation.html \
 		test_li_attributes_reflection.html \
 		test_ol_attributes_reflection.html \
 		test_bug651956.html \
 		test_bug694503.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/content/html/content/test/bug649134/Makefile.in
+++ b/content/html/content/test/bug649134/Makefile.in
@@ -42,9 +42,9 @@ VPATH          = @srcdir@
 relativesrcdir = content/html/content/test/bug649134
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = file_bug649134-1.sjs file_bug649134-2.sjs index.html
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/content/html/content/test/file_fullscreen-api-keys.html
+++ b/content/html/content/test/file_fullscreen-api-keys.html
@@ -89,25 +89,29 @@ function testScriptInitiatedKeyEvents() 
   ok(document.mozFullScreen,
      "Should remain in full-screen mode for script initiated key events for " + gKeyName);
 }
 
 function testNextKey() {
   if (!document.mozFullScreen) {
     document.body.mozRequestFullScreen();
   }
-  ok(document.mozFullScreen, "Must be in full-screen mode");
+  // mozRequestFullScreen() is async...
+  setTimeout(
+    function() {
+      ok(document.mozFullScreen, "Must be in full-screen mode");
 
-  gKeyName = keyList[gKeyTestIndex].code;
-  gKeyCode = KeyEvent["DOM_" + gKeyName];
-  gKeySuppressed = keyList[gKeyTestIndex].suppressed;
-  gKeyTestIndex++;
+      gKeyName = keyList[gKeyTestIndex].code;
+      gKeyCode = KeyEvent["DOM_" + gKeyName];
+      gKeySuppressed = keyList[gKeyTestIndex].suppressed;
+      gKeyTestIndex++;
 
-  testScriptInitiatedKeyEvents();
-  testTrustedKeyEvents();
+      testScriptInitiatedKeyEvents();
+      testTrustedKeyEvents();
+    }, 0);
 }
 
 window.addEventListener("keydown", keyHandler, true);
 window.addEventListener("keyup", keyHandler, true);
 window.addEventListener("keypress", keyHandler, true);
 setTimeout(testNextKey, 0);
 
 </script>
--- a/content/html/content/test/file_fullscreen-api.html
+++ b/content/html/content/test/file_fullscreen-api.html
@@ -93,48 +93,61 @@ function fullScreenChange(event) {
     case 2: {
       ok(document.mozFullScreen, "Should be back in full-screen mode (second time)");
       is(event.target, iframe,
          "Event target should be full-screen element container");
       is(document.mozFullScreenElement, iframe,
         "Full-screen element should be iframe element.");
       var fse = fullScreenElement();
       fse.mozRequestFullScreen();
-      ok(document.mozFullScreen, "Should still be full-screen mode after re-requesting.");
-      is(document.mozFullScreenElement, fse, "Full-screen element should have switched to requestee.");
-      var _innerFrame = iframe.contentDocument.getElementById("inner-frame");
-      _innerFrame.contentDocument.body.appendChild(fse);
-      ok(!document.mozFullScreen, "Should exit full-screen after transplanting FSE");
-      is(document.mozFullScreenElement, null, "Full-screen element transplanted, should be null.");
-      is(iframe.contentDocument.mozFullScreenElement, null, "Full-screen element in outer frame should be null.");
-      is(_innerFrame.contentDocument.mozFullScreenElement, null, "Full-screen element in inner frame should be null.");
-      ok(!iframe.contentDocument.mozFullScreen, "Outer frame should not acquire full-screen status.");
-      ok(!_innerFrame.contentDocument.mozFullScreen, "Inner frame should not acquire full-screen status.");
       
-      document.body.appendChild(fse);
+      setTimeout(
+        function() {
+          ok(document.mozFullScreen, "Should still be full-screen mode after re-requesting.");
+          is(document.mozFullScreenElement, fse, "Full-screen element should have switched to requestee.");
+          var _innerFrame = iframe.contentDocument.getElementById("inner-frame");
+          _innerFrame.contentDocument.body.appendChild(fse);
+          ok(!document.mozFullScreen, "Should exit full-screen after transplanting FSE");
+          is(document.mozFullScreenElement, null, "Full-screen element transplanted, should be null.");
+          is(iframe.contentDocument.mozFullScreenElement, null, "Full-screen element in outer frame should be null.");
+          is(_innerFrame.contentDocument.mozFullScreenElement, null, "Full-screen element in inner frame should be null.");
+          ok(!iframe.contentDocument.mozFullScreen, "Outer frame should not acquire full-screen status.");
+          ok(!_innerFrame.contentDocument.mozFullScreen, "Inner frame should not acquire full-screen status.");
+          
+          document.body.appendChild(fse);
+        }, 0);
+      
       break;
     }
     case 3: {
       ok(!document.mozFullScreen, "Should be back in non-full-screen mode (second time)");
       is(event.target, document,
          "Event target should be full-screen element container");
       is(document.mozFullScreenElement, null, "Full-screen element should be null.");
       document.body.removeChild(iframe);
       iframe = null;
+      
+      // Do a request out of document. It should be denied.
+      // Continue test in the following mozfullscreenerror handler.
       outOfDocElement = document.createElement("div");
-      outOfDocElement.mozRequestFullScreen();
-      ok(!document.mozFullScreen, "Requests for full-screen from not-in-doc elements should fail.");
+      var f =
+      function(e) {
+        document.removeEventListener("mozfullscreenerror", f, false);
+        ok(!document.mozFullScreen, "Requests for full-screen from not-in-doc elements should fail.");
 
-      container = document.createElement("div");
-      inDocElement = document.createElement("div");
-      container.appendChild(inDocElement);
-      fullScreenElement().appendChild(container);
-      
-      inDocElement.mozRequestFullScreen();
-      ok(document.mozFullScreen, "Should grant request to return to full-screen mode (third time)");
+        container = document.createElement("div");
+        inDocElement = document.createElement("div");
+        container.appendChild(inDocElement);
+        fullScreenElement().appendChild(container);
+        
+        inDocElement.mozRequestFullScreen();
+      };
+      document.addEventListener("mozfullscreenerror", f, false);
+      outOfDocElement.mozRequestFullScreen();
+
       break;
     }
     case 4: {
       ok(document.mozFullScreen, "Should still be in full-screen mode (third time)");
       is(event.target, inDocElement, "Event target should be inDocElement");
       is(document.mozFullScreenElement, inDocElement,
         "FSE should be inDocElement.");       
       
@@ -152,21 +165,21 @@ function fullScreenChange(event) {
         "Should not have a full-screen element again.");
       break;
     }
     case 5: {
       ok(!document.mozFullScreen, "Should be back in non-full-screen mode (third time)");
       setRequireTrustedContext(true);
       fullscreendenied = false;
       fullScreenElement().mozRequestFullScreen();
-      ok(!document.mozFullScreen, "Should still be in normal mode, because calling context isn't trusted.");
 
       setTimeout(
         function() {
           ok(fullscreendenied, "Request for fullscreen should have been denied because calling context isn't trusted");
+          ok(!document.mozFullScreen, "Should still be in normal mode, because calling context isn't trusted.");
           button = document.createElement("button");
           button.onclick = function(){fullScreenElement().mozRequestFullScreen();}
           fullScreenElement().appendChild(button);
           sendMouseClick(button);
         }, 0);
       break;
     }
     case 6: {
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_fullscreen-denied-inner.html
@@ -0,0 +1,22 @@
+<html>
+  <body onload='foo();'>
+  <script>
+    function foo() {
+      document.addEventListener('mozfullscreenerror',
+        function() {
+          parent.ok(true, "Request from an iframe without mozallowfullscreen should be denied");
+          parent.finish();
+        },
+        false);
+      document.addEventListener('mozfullscreenchange',
+        function() {
+          parent.ok(false, "Request from an iframe without mozallowfullscreen should be denied, but was granted!");
+          parent.finish();
+        },
+        false);
+      parent.is(document.mozFullScreenEnabled, false, "Full-screen should not be enabled, coz mozallowfullscreen isn't present.");
+      document.body.mozRequestFullScreen();    
+    }
+  </script>
+  </body>
+</html>
--- a/content/html/content/test/file_fullscreen-denied.html
+++ b/content/html/content/test/file_fullscreen-denied.html
@@ -24,24 +24,16 @@ Test DOM full-screen API.
 function ok(condition, msg) {
   opener.ok(condition, msg);
 }
 
 function is(a, b, msg) {
   opener.is(a, b, msg);
 }
 
-/*
-<html>
-  <body onload='document.body.mozRequestFullScreen();'>
-  </body>
-</html>
-*/
-var requestFullScreenContents = "data:text/html;charset=utf-8,<html>%0D%0A  <body onload%3D'document.body.mozRequestFullScreen()%3B'>%0D%0A  <%2Fbody>%0D%0A<%2Fhtml>";
-
 var fullscreendenied = false;
 
 document.addEventListener("mozfullscreenerror", function(){fullscreendenied=true;}, false);
 
 var gotFullScreenChange = false;
 
 function run() {
   document.addEventListener("mozfullscreenchange",
@@ -49,18 +41,18 @@ function run() {
       ok(false, "Should never receive a mozfullscreenchange event in the main window.");
       gotFullScreenChange = true;
     },
     false);
 
   // Request full-screen from a non trusted context (this script isn't a user
   // generated event!).
   SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", true);
+  fullscreendenied = false;
   document.body.mozRequestFullScreen();
-  fullscreendenied = false;
   setTimeout(
     function() {
       ok(!document.mozFullScreen, "Should not grant request in non-truested context");
       ok(fullscreendenied, "Request in non-trusted event handler should be denied");
       // Test requesting full-screen mode in a long-running user-generated event handler.
       // The request in the key handler should not be granted.
       window.addEventListener("keypress", keyHandler, false);
       synthesizeKey("VK_A", {});
@@ -82,28 +74,27 @@ function keyHandler(event) {
     function() {
       ok(fullscreendenied, "Request in long running event handler should be denied");
       ok(!document.mozFullScreen, "Should not grant request in long-running event handler.");
       
       // Disable the requirement for trusted contexts only, so the tests are easier
       // to write.
       SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);  
       
-      // Create an iframe without a mozallowfullscreen sttribute, whose contents requests
+      // Create an iframe without a mozallowfullscreen attribute, whose contents requests
       // full-screen. The request should be denied, and we should not receive a fullscreenchange
       // event in this document.
       var iframe = document.createElement("iframe");
-      iframe.src = requestFullScreenContents;
+      iframe.src = "file_fullscreen-denied-inner.html";
       document.body.appendChild(iframe);
-     
-      setTimeout(
-        function() {
-          ok(!gotFullScreenChange, "Should not ever grant a fullscreen request in this doc.");
-          opener.nextTest();
-        }, 0);
     }, 0);
 }
 
+function finish() {
+  ok(!gotFullScreenChange, "Should not ever grant a fullscreen request in this doc.");
+  opener.nextTest();
+}
+
 </script>
 </pre>
 <div id="full-screen-element"></div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_fullscreen-navigation.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=685402
+-->
+<head>
+  <title>Test for Bug 685402</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="boom();" style="background-color: gray;">
+
+<iframe id="f" src="data:text/html,<body text=green>1" mozallowfullscreen></iframe>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685402">Mozilla Bug 685402</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 685402 **/
+
+var frameWin;
+var e1;
+var prevEnabled;
+var prevTrusted;
+
+function boom()
+{
+  frameWin = document.getElementById("f").contentWindow;
+  e1 = frameWin.document.body;
+  e1.mozRequestFullScreen();
+  setTimeout(
+    function() {
+      opener.ok(document.mozFullScreen, "Request should be granted");
+      frameWin.location = "data:text/html,<body text=blue onload='parent.b2()'>2";
+    }, 0);
+}
+
+function b2()
+{
+  opener.ok(!document.mozFullScreen, "Should have left full-screen due to navigation.");
+  opener.nextTest();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/content/test/file_fullscreen-plugins.html
+++ b/content/html/content/test/file_fullscreen-plugins.html
@@ -69,25 +69,36 @@ function scheduleTest() {
 function startTest() {
   ok(!document.mozFullScreen, "Should not be in full-screen mode initially");
   document.body.mozRequestFullScreen();
 
   if (isMacOs) {
     // Running on MacOS, all plugins are effectively windowless, request for full-screen should be granted.
     // Continue test in the (mac-specific) "mozfullscreenchange" handler.
     return;
+  } else {
+    // Non-MacOS, request should be denied, carry on the test after receiving error event.
+    document.addEventListener("mozfullscreenerror", nonMacTest, false);
   }
+}
 
+function nonMacTest() {
+  document.removeEventListener("mozfullscreenerror", nonMacTest, false);
   ok(!document.mozFullScreen, "Request for full-screen from a document containing windowed plugin should be denied.");
 
   // Remove plugin in this document. Should still be a windowed plugin in sub-document.
   windowedPlugin = document.getElementById("windowed-plugin");
   windowedPlugin.parentNode.removeChild(windowedPlugin);
 
+  document.addEventListener("mozfullscreenerror", nonMacTest2, false);
   document.body.mozRequestFullScreen();
+}
+
+function nonMacTest2() {
+  document.removeEventListener("mozfullscreenerror", nonMacTest2, false);
   ok(!document.mozFullScreen, "Request for full-screen from a document with subdocument containing windowed plugin should be denied.");
   // Remove subdoc which contains windowed plugin, request full-screen, request should be granted.
   // Continue test in "mozfullscreenchange" handler.
   var f = document.getElementById("subdoc-plugin");
   f.parentNode.removeChild(f);
   document.body.mozRequestFullScreen();
 }
 
--- a/content/html/content/test/forms/Makefile.in
+++ b/content/html/content/test/forms/Makefile.in
@@ -62,10 +62,10 @@ include $(topsrcdir)/config/rules.mk
 		test_textarea_attributes_reflection.html \
 		test_validation.html \
 		test_maxlength_attribute.html \
 		test_datalist_element.html \
 		test_form_attributes_reflection.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
--- a/content/html/content/test/test_fullscreen-api.html
+++ b/content/html/content/test/test_fullscreen-api.html
@@ -35,17 +35,18 @@ SpecialPowers.setBoolPref("full-screen-a
 // Run the tests which go full-screen in new windows, as mochitests normally
 // run in an iframe, which by default will not have the mozallowfullscreen
 // attribute set, so full-screen won't work.
 var gTestWindows = [
   "file_fullscreen-denied.html",
   "file_fullscreen-api.html",
   "file_fullscreen-api-keys.html",
   "file_fullscreen-plugins.html",
-  "file_fullscreen-hidden.html"
+  "file_fullscreen-hidden.html",
+  "file_fullscreen-navigation.html"
 ];
 
 var testWindow = null;
 var gTestIndex = 0;
 
 function nextTest() {
   if (testWindow) {
     testWindow.close();
--- a/content/html/document/test/Makefile.in
+++ b/content/html/document/test/Makefile.in
@@ -110,14 +110,14 @@ include $(topsrcdir)/config/rules.mk
 ifneq (mobile,$(MOZ_BUILD_APP))
 _BROWSER_TEST_FILES = \
 		browser_bug592641.js \
 		bug592641_img.jpg \
 		$(NULL)
 endif
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 ifneq (mobile,$(MOZ_BUILD_APP))
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 endif
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -298,9 +298,9 @@ ifdef MOZ_WAVE
 		$(NULL)
 else
 _TEST_FILES += \
 		test_can_play_type_no_wave.html \
 		$(NULL)
 endif
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/smil/crashtests/699325-1.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+  <rect fill="blue" height="100" width="100">
+    <animate id="a" attributeName="x"  calcMode="paced" values="50; 50; 50"/>
+  </rect>
+</svg>
--- a/content/smil/crashtests/crashtests.list
+++ b/content/smil/crashtests/crashtests.list
@@ -37,8 +37,9 @@ load 615872-1.svg
 load 650732-1.svg
 load 665334-1.svg
 load 669225-1.svg
 load 669225-2.svg
 load 670313-1.svg
 load 678822-1.svg
 load 678847-1.svg
 load 678938-1.svg
+load 699325-1.svg
--- a/content/smil/nsSMILAnimationController.cpp
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -203,16 +203,17 @@ nsSMILAnimationController::RegisterAnima
   if (mDeferredStartSampling) {
     mDeferredStartSampling = false;
     if (mChildContainerTable.Count()) {
       // mAnimationElementTable was empty, but now we've added its 1st element
       NS_ABORT_IF_FALSE(mAnimationElementTable.Count() == 1,
                         "we shouldn't have deferred sampling if we already had "
                         "animations registered");
       StartSampling(GetRefreshDriver());
+      Sample(); // Run the first sample manually
     } // else, don't sample until a time container is registered (via AddChild)
   }
 }
 
 void
 nsSMILAnimationController::UnregisterAnimationElement(
                                   nsISMILAnimationElement* aAnimationElement)
 {
@@ -812,18 +813,18 @@ nsSMILAnimationController::GetTargetIden
 
 nsresult
 nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild)
 {
   TimeContainerPtrKey* key = mChildContainerTable.PutEntry(&aChild);
   NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
 
   if (!mPauseState && mChildContainerTable.Count() == 1) {
+    MaybeStartSampling(GetRefreshDriver());
     Sample(); // Run the first sample manually
-    MaybeStartSampling(GetRefreshDriver());
   }
 
   return NS_OK;
 }
 
 void
 nsSMILAnimationController::RemoveChild(nsSMILTimeContainer& aChild)
 {
--- a/content/smil/nsSMILAnimationFunction.cpp
+++ b/content/smil/nsSMILAnimationFunction.cpp
@@ -556,16 +556,23 @@ nsSMILAnimationFunction::ComputePacedPos
     aTo = &aValues[1];
     return NS_OK;
   }
 
   double totalDistance = ComputePacedTotalDistance(aValues);
   if (totalDistance == COMPUTE_DISTANCE_ERROR)
     return NS_ERROR_FAILURE;
 
+  // If we have 0 total distance, then it's unclear where our "paced" position
+  // should be.  We can just fail, which drops us into discrete animation mode.
+  // (That's fine, since our values are apparently indistinguishable anyway.)
+  if (totalDistance == 0.0) {
+    return NS_ERROR_FAILURE;
+  }
+
   // total distance we should have moved at this point in time.
   // (called 'remainingDist' due to how it's used in loop below)
   double remainingDist = aSimpleProgress * totalDistance;
 
   // Must be satisfied, because totalDistance is a sum of (non-negative)
   // distances, and aSimpleProgress is non-negative
   NS_ASSERTION(remainingDist >= 0, "distance values must be non-negative");
 
--- a/content/smil/test/Makefile.in
+++ b/content/smil/test/Makefile.in
@@ -64,16 +64,17 @@ include $(topsrcdir)/config/rules.mk
 	  test_smilContainerBinding.xhtml \
 	  test_smilCrossContainer.xhtml \
 	  test_smilCSSFontStretchRelative.xhtml \
 	  test_smilCSSFromBy.xhtml \
 	  test_smilCSSFromTo.xhtml \
 	  test_smilCSSInherit.xhtml \
 	  test_smilCSSInvalidValues.xhtml \
 	  test_smilCSSPaced.xhtml \
+	  test_smilDynamicDelayedBeginElement.xhtml \
 	  test_smilMappedAttrFromTo.xhtml \
 	  test_smilMappedAttrFromBy.xhtml \
 	  test_smilMappedAttrPaced.xhtml \
 	  test_smilReset.xhtml \
 	  test_smilRestart.xhtml \
 	  test_smilExtDoc.xhtml \
 	  test_smilFillMode.xhtml \
 	  test_smilGetStartTime.xhtml \
@@ -90,9 +91,9 @@ include $(topsrcdir)/config/rules.mk
 	  test_smilTimeEvents.xhtml \
 	  test_smilTiming.xhtml \
 	  test_smilTimingZeroIntervals.xhtml \
 	  test_smilUpdatedInterval.xhtml \
 	  test_smilXHR.xhtml \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $^ $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/smil/test/test_smilDynamicDelayedBeginElement.xhtml
@@ -0,0 +1,96 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=699143
+-->
+<head>
+  <title>Test for Bug 699143</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="smilTestUtils.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=699143">Mozilla Bug 699143</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <svg xmlns="http://www.w3.org/2000/svg">
+    <rect id="r" height="500px" width="500px" fill="blue"/>
+  </svg>
+</div>
+<pre id="test">
+<script type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 699143 **/
+SimpleTest.waitForExplicitFinish();
+
+// Values for 'width' attr on the <rect> above
+const INITIAL_VAL = "500px"
+const FROM_VAL = "20px";
+const TO_VAL   = "80px";
+
+// Helper function
+function createAnim() {
+  var a = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
+  a.setAttribute('attributeName', 'width');
+  a.setAttribute('from', FROM_VAL);
+  a.setAttribute('to',   TO_VAL);
+  a.setAttribute('begin', 'indefinite');
+  a.setAttribute('dur', '3s');
+  a.setAttribute('fill', 'freeze');
+  return a;
+}
+
+// Main Functions
+function main() {
+  if (!SMILUtil.isSMILEnabled()) {
+    ok(false, "SMIL dosn't seem to be enabled");
+    SimpleTest.finish();
+    return;
+  }
+
+  // In unpatched Firefox builds, we'll only trigger Bug 699143 if we insert
+  // an animation and call beginElement() **after** the document start-time.
+  // Hence, we use executeSoon here to allow some time to pass.
+  SimpleTest.executeSoon(runTest);
+}
+
+function runTest() {
+  var svg = SMILUtil.getSVGRoot();
+
+  is(svg.getCurrentTime(), 0,
+     "even though we've allowed time to pass, we shouldn't have bothered " +
+     "updating the current time, since there aren't any animation elements");
+
+  // Insert an animation elem (should affect currentTime but not targeted attr)
+  var r = document.getElementById("r");
+  var a = createAnim();
+  r.appendChild(a);
+  isnot(svg.getCurrentTime(), 0,
+       "insertion of first animation element should have triggered a " +
+       "synchronous sample and updated our current time");
+  is(r.width.animVal.valueAsString, INITIAL_VAL,
+     "inserted animation shouldn't have affected its targeted attribute, " +
+     "since it doesn't have any intervals yet");
+
+  // Trigger the animation & be sure it takes effect
+  a.beginElement();
+  is(r.width.animVal.valueAsString, FROM_VAL,
+     "beginElement() should activate our animation & set its 'from' val");
+
+  // Rewind to time=0 & check target attr, to be sure beginElement()-generated
+  // interval starts later than that.
+  svg.setCurrentTime(0);
+  is(r.width.animVal.valueAsString, INITIAL_VAL,
+     "after rewinding to 0, our beginElement()-generated interval " +
+     "shouldn't be active yet");
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", main, false);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
--- a/content/svg/content/test/Makefile.in
+++ b/content/svg/content/test/Makefile.in
@@ -101,9 +101,9 @@ include $(topsrcdir)/config/rules.mk
 		test_valueLeaks.xhtml \
 		viewport-helper.svg \
 		test_viewport.html \
 		zoom-helper.svg \
 		test_zoom.xhtml \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/content/xbl/src/Makefile.in
+++ b/content/xbl/src/Makefile.in
@@ -59,16 +59,17 @@ CPPSRCS		= \
     nsXBLProtoImplField.cpp \
     nsXBLProtoImpl.cpp \
     nsXBLEventHandler.cpp \
     nsXBLWindowKeyHandler.cpp \
     nsXBLPrototypeHandler.cpp \
     nsXBLService.cpp \
     nsBindingManager.cpp \
     nsXBLInsertionPoint.cpp \
+    nsXBLSerialize.cpp \
 		$(NULL)
 
 EXPORTS   = \
   nsBindingManager.h \
   nsXBLBinding.h \
   $(NULL)
 
 include $(topsrcdir)/config/config.mk
--- a/content/xbl/src/nsXBLContentSink.cpp
+++ b/content/xbl/src/nsXBLContentSink.cpp
@@ -561,17 +561,19 @@ nsresult
 nsXBLContentSink::ConstructBinding(PRUint32 aLineNumber)
 {
   nsCOMPtr<nsIContent> binding = GetCurrentContent();
   nsAutoString id;
   binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
   NS_ConvertUTF16toUTF8 cid(id);
 
   nsresult rv = NS_OK;
-  
+
+  // Don't create a binding with no id. nsXBLPrototypeBinding::Read also
+  // performs this check.
   if (!cid.IsEmpty()) {
     mBinding = new nsXBLPrototypeBinding();
     if (!mBinding)
       return NS_ERROR_OUT_OF_MEMORY;
       
     rv = mBinding->Init(cid, mDocInfo, binding, !mFoundFirstBinding);
     if (NS_SUCCEEDED(rv) &&
         NS_SUCCEEDED(mDocInfo->SetPrototypeBinding(cid, mBinding))) {
@@ -889,16 +891,18 @@ nsXBLContentSink::CreateElement(const PR
   if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
 #endif
     return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
                                            aLineNumber, aResult,
                                            aAppendContent, aFromParser);
 #ifdef MOZ_XUL
   }
 
+  // Note that this needs to match the code in nsXBLPrototypeBinding::ReadContentNode.
+
   *aAppendContent = true;
   nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
   if (!prototype)
     return NS_ERROR_OUT_OF_MEMORY;
 
   prototype->mNodeInfo = aNodeInfo;
   // XXX - we need to do exactly what the XUL content-sink does (eg,
   // look for 'type', 'version' etc attributes)
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -51,17 +51,23 @@
 #include "nsIScriptError.h"
 #include "nsIChromeRegistry.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
 #include "mozilla/Services.h"
 #include "xpcpublic.h"
- 
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/scache/StartupCacheUtils.h"
+
+using namespace mozilla::scache;
+
+static const char kXBLCachePrefix[] = "xblcache";
+
 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 // An XBLDocumentInfo object has a special context associated with it which we can use to pre-compile 
 // properties and methods of XBL bindings against.
 class nsXBLDocGlobalObject : public nsIScriptGlobalObject,
                              public nsIScriptObjectPrincipal
 {
 public:
@@ -557,16 +563,148 @@ nsXBLDocumentInfo::SetPrototypeBinding(c
   nsCStringKey key(flat.get());
   NS_ENSURE_STATE(!mBindingTable->Get(&key));
   mBindingTable->Put(&key, aBinding);
 
   return NS_OK;
 }
 
 void
+nsXBLDocumentInfo::RemovePrototypeBinding(const nsACString& aRef)
+{
+  if (mBindingTable) {
+    // Use a flat string to avoid making a copy.
+    const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
+    nsCStringKey key(flat);
+    mBindingTable->Remove(&key);
+  }
+}
+
+// Callback to enumerate over the bindings from this document and write them
+// out to the cache.
+bool
+WriteBinding(nsHashKey *aKey, void *aData, void* aClosure)
+{
+  nsXBLPrototypeBinding* binding = static_cast<nsXBLPrototypeBinding *>(aData);
+  binding->Write((nsIObjectOutputStream*)aClosure);
+
+  return kHashEnumerateNext;
+}
+
+// static
+nsresult
+nsXBLDocumentInfo::ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo)
+{
+  *aDocInfo = nsnull;
+
+  nsCAutoString spec(kXBLCachePrefix);
+  nsresult rv = PathifyURI(aURI, spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  StartupCache* startupCache = StartupCache::GetSingleton();
+  NS_ENSURE_TRUE(startupCache, NS_ERROR_FAILURE);
+
+  nsAutoArrayPtr<char> buf;
+  PRUint32 len;
+  rv = startupCache->GetBuffer(spec.get(), getter_Transfers(buf), &len);
+  // GetBuffer will fail if the binding is not in the cache.
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsCOMPtr<nsIObjectInputStream> stream;
+  rv = NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+  buf.forget();
+
+  // The file compatibility.ini stores the build id. This is checked in
+  // nsAppRunner.cpp and will delete the cache if a different build is
+  // present. However, we check that the version matches here to be safe. 
+  PRUint32 version;
+  rv = stream->Read32(&version);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (version != XBLBinding_Serialize_Version) {
+    // The version that exists is different than expected, likely created with a
+    // different build, so invalidate the cache.
+    startupCache->InvalidateCache();
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal;
+  nsContentUtils::GetSecurityManager()->
+    GetSystemPrincipal(getter_AddRefs(principal));
+
+  nsCOMPtr<nsIDOMDocument> domdoc;
+  rv = NS_NewXBLDocument(getter_AddRefs(domdoc), aURI, nsnull, principal);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
+  nsRefPtr<nsXBLDocumentInfo> docInfo = NS_NewXBLDocumentInfo(doc);
+
+  while (1) {
+    PRUint8 flags;
+    nsresult rv = stream->Read8(&flags);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (flags == XBLBinding_Serialize_NoMoreBindings)
+      break;
+
+    nsXBLPrototypeBinding* binding = new nsXBLPrototypeBinding();
+    rv = binding->Read(stream, docInfo, doc, flags);
+    if (NS_FAILED(rv)) {
+      delete binding;
+      return rv;
+    }
+  }
+
+  docInfo.swap(*aDocInfo);
+  return NS_OK;
+}
+
+nsresult
+nsXBLDocumentInfo::WritePrototypeBindings()
+{
+  // Only write out bindings with the system principal
+  if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal()))
+    return NS_OK;
+
+  nsCAutoString spec(kXBLCachePrefix);
+  nsresult rv = PathifyURI(DocumentURI(), spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  StartupCache* startupCache = StartupCache::GetSingleton();
+  NS_ENSURE_TRUE(startupCache, rv);
+
+  nsCOMPtr<nsIObjectOutputStream> stream;
+  nsCOMPtr<nsIStorageStream> storageStream;
+  rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(stream),
+                                           getter_AddRefs(storageStream),
+                                           true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = stream->Write32(XBLBinding_Serialize_Version);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (mBindingTable)
+    mBindingTable->Enumerate(WriteBinding, stream);
+
+  // write a end marker at the end
+  rv = stream->Write8(XBLBinding_Serialize_NoMoreBindings);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  stream->Close();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 len;
+  nsAutoArrayPtr<char> buf;
+  rv = NewBufferFromStorageStream(storageStream, getter_Transfers(buf), &len);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return startupCache->PutBuffer(spec.get(), buf, len);
+}
+
+void
 nsXBLDocumentInfo::SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding)
 {
   mFirstBinding = aBinding;
 }
 
 bool FlushScopedSkinSheets(nsHashKey* aKey, void* aData, void* aClosure)
 {
   nsXBLPrototypeBinding* proto = (nsXBLPrototypeBinding*)aData;
--- a/content/xbl/src/nsXBLDocumentInfo.h
+++ b/content/xbl/src/nsXBLDocumentInfo.h
@@ -63,25 +63,32 @@ public:
   bool GetScriptAccess() { return mScriptAccess; }
 
   nsIURI* DocumentURI() { return mDocument->GetDocumentURI(); }
 
   nsXBLPrototypeBinding* GetPrototypeBinding(const nsACString& aRef);
   nsresult SetPrototypeBinding(const nsACString& aRef,
                                nsXBLPrototypeBinding* aBinding);
 
+  // This removes the binding without deleting it
+  void RemovePrototypeBinding(const nsACString& aRef);
+
+  nsresult WritePrototypeBindings();
+
   void SetFirstPrototypeBinding(nsXBLPrototypeBinding* aBinding);
   
   void FlushSkinStylesheets();
 
   bool IsChrome() { return mIsChrome; }
 
   // nsIScriptGlobalObjectOwner methods
   virtual nsIScriptGlobalObject* GetScriptGlobalObject();
 
+  static nsresult ReadPrototypeBindings(nsIURI* aURI, nsXBLDocumentInfo** aDocInfo);
+
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsXBLDocumentInfo,
                                                          nsIScriptGlobalObjectOwner)
 
 private:
   nsCOMPtr<nsIDocument> mDocument;
   bool mScriptAccess;
   bool mIsChrome;
   // the binding table owns each nsXBLPrototypeBinding
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -42,16 +42,17 @@
 #include "nsContentUtils.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptGlobalObjectOwner.h"
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMNode.h"
 #include "nsXBLPrototypeBinding.h"
+#include "nsXBLProtoImplProperty.h"
 
 // Checks that the version is not modified in a given scope.
 class AutoVersionChecker
 {
   JSContext * const cx;
   JSVersion versionBefore;
 
 public:
@@ -295,16 +296,169 @@ nsXBLProtoImpl::DestroyMembers()
 
   delete mMembers;
   mMembers = nsnull;
   mConstructor = nsnull;
   mDestructor = nsnull;
 }
 
 nsresult
+nsXBLProtoImpl::Read(nsIScriptContext* aContext,
+                     nsIObjectInputStream* aStream,
+                     nsXBLPrototypeBinding* aBinding,
+                     nsIScriptGlobalObject* aGlobal)
+{
+  // Set up a class object first so that deserialization is possible
+  JSContext *cx = static_cast<JSContext *>(aContext->GetNativeContext());
+  JSObject *global = aGlobal->GetGlobalJSObject();
+
+  void* classObject;
+  nsresult rv = aBinding->InitClass(mClassName, cx, global, global, &classObject);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(classObject, NS_ERROR_FAILURE);
+
+  mClassObject = (JSObject*) classObject;
+
+  nsXBLProtoImplField* previousField = nsnull;
+  nsXBLProtoImplMember* previousMember = nsnull;
+
+  do {
+    XBLBindingSerializeDetails type;
+    rv = aStream->Read8(&type);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (type == XBLBinding_Serialize_NoMoreItems)
+      break;
+
+    switch (type & XBLBinding_Serialize_Mask) {
+      case XBLBinding_Serialize_Field:
+      {
+        nsXBLProtoImplField* field =
+          new nsXBLProtoImplField(type & XBLBinding_Serialize_ReadOnly);
+        rv = field->Read(aContext, aStream);
+        if (NS_FAILED(rv)) {
+          delete field;
+          return rv;
+        }
+
+        if (previousField) {
+          previousField->SetNext(field);
+        }
+        else {
+          mFields = field;
+        }
+        previousField = field;
+
+        break;
+      }
+      case XBLBinding_Serialize_GetterProperty:
+      case XBLBinding_Serialize_SetterProperty:
+      case XBLBinding_Serialize_GetterSetterProperty:
+      {
+        nsAutoString name;
+        nsresult rv = aStream->ReadString(name);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsXBLProtoImplProperty* prop =
+          new nsXBLProtoImplProperty(name.get(), type & XBLBinding_Serialize_ReadOnly);
+        rv = prop->Read(aContext, aStream, type & XBLBinding_Serialize_Mask);
+        if (NS_FAILED(rv)) {
+          delete prop;
+          return rv;
+        }
+
+        previousMember = AddMember(prop, previousMember);
+        break;
+      }
+      case XBLBinding_Serialize_Method:
+      {
+        nsAutoString name;
+        rv = aStream->ReadString(name);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsXBLProtoImplMethod* method = new nsXBLProtoImplMethod(name.get());
+        rv = method->Read(aContext, aStream);
+        if (NS_FAILED(rv)) {
+          delete method;
+          return rv;
+        }
+
+        previousMember = AddMember(method, previousMember);
+        break;
+      }
+      case XBLBinding_Serialize_Constructor:
+      {
+        mConstructor = new nsXBLProtoImplAnonymousMethod();
+        rv = mConstructor->Read(aContext, aStream);
+        if (NS_FAILED(rv)) {
+          delete mConstructor;
+          mConstructor = nsnull;
+          return rv;
+        }
+
+        previousMember = AddMember(mConstructor, previousMember);
+        break;
+      }
+      case XBLBinding_Serialize_Destructor:
+      {
+        mDestructor = new nsXBLProtoImplAnonymousMethod();
+        rv = mDestructor->Read(aContext, aStream);
+        if (NS_FAILED(rv)) {
+          delete mDestructor;
+          mDestructor = nsnull;
+          return rv;
+        }
+
+        previousMember = AddMember(mDestructor, previousMember);
+        break;
+      }
+      default:
+        NS_ERROR("Unexpected binding member type");
+        break;
+    }
+  } while (1);
+
+  return NS_OK;
+}
+
+nsresult
+nsXBLProtoImpl::Write(nsIScriptContext* aContext,
+                      nsIObjectOutputStream* aStream,
+                      nsXBLPrototypeBinding* aBinding)
+{
+  nsresult rv;
+
+  if (!mClassObject) {
+    rv = CompilePrototypeMembers(aBinding);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = aStream->WriteStringZ(mClassName.get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (nsXBLProtoImplField* curr = mFields; curr; curr = curr->GetNext()) {
+    rv = curr->Write(aContext, aStream);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) {
+    if (curr == mConstructor) {
+      rv = mConstructor->Write(aContext, aStream, XBLBinding_Serialize_Constructor);
+    }
+    else if (curr == mDestructor) {
+      rv = mDestructor->Write(aContext, aStream, XBLBinding_Serialize_Destructor);
+    }
+    else {
+      rv = curr->Write(aContext, aStream);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return aStream->Write8(XBLBinding_Serialize_NoMoreItems);
+}
+
+nsresult
 NS_NewXBLProtoImpl(nsXBLPrototypeBinding* aBinding, 
                    const PRUnichar* aClassName, 
                    nsXBLProtoImpl** aResult)
 {
   nsXBLProtoImpl* impl = new nsXBLProtoImpl();
   if (!impl)
     return NS_ERROR_OUT_OF_MEMORY;
   if (aClassName)
--- a/content/xbl/src/nsXBLProtoImpl.h
+++ b/content/xbl/src/nsXBLProtoImpl.h
@@ -100,17 +100,36 @@ public:
   // Undefine all our fields from object |obj| (which should be a
   // JSObject for a bound element).
   void UndefineFields(JSContext* cx, JSObject* obj) const;
 
   bool CompiledMembers() const {
     return mClassObject != nsnull;
   }
 
+  nsresult Read(nsIScriptContext* aContext,
+                nsIObjectInputStream* aStream,
+                nsXBLPrototypeBinding* aBinding,
+                nsIScriptGlobalObject* aGlobal);
+  nsresult Write(nsIScriptContext* aContext,
+                 nsIObjectOutputStream* aStream,
+                 nsXBLPrototypeBinding* aBinding);
+
 protected:
+  // used by Read to add each member
+  nsXBLProtoImplMember* AddMember(nsXBLProtoImplMember* aMember,
+                                  nsXBLProtoImplMember* aPreviousMember)
+  {
+    if (aPreviousMember)
+      aPreviousMember->SetNext(aMember);
+    else
+      mMembers = aMember;
+    return aMember;
+  }
+
   void DestroyMembers();
   
 public:
   nsCString mClassName; // The name of the class. 
 
 protected:
   void* mClassObject;   // The class object for the binding. We'll use this to pre-compile properties 
                         // and methods for the binding.
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -43,16 +43,18 @@
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "mozilla/FunctionTimer.h"
 #include "nsXBLProtoImplField.h"
 #include "nsIScriptContext.h"
 #include "nsContentUtils.h"
 #include "nsIURI.h"
+#include "nsXBLSerialize.h"
+#include "nsXBLPrototypeBinding.h"
 
 nsXBLProtoImplField::nsXBLProtoImplField(const PRUnichar* aName, const PRUnichar* aReadOnly)
   : mNext(nsnull),
     mFieldText(nsnull),
     mFieldTextLength(0),
     mLineNumber(0)
 {
   MOZ_COUNT_CTOR(nsXBLProtoImplField);
@@ -61,16 +63,30 @@ nsXBLProtoImplField::nsXBLProtoImplField
   mJSAttributes = JSPROP_ENUMERATE;
   if (aReadOnly) {
     nsAutoString readOnly; readOnly.Assign(aReadOnly);
     if (readOnly.LowerCaseEqualsLiteral("true"))
       mJSAttributes |= JSPROP_READONLY;
   }
 }
 
+
+nsXBLProtoImplField::nsXBLProtoImplField(bool aIsReadOnly)
+  : mNext(nsnull),
+    mFieldText(nsnull),
+    mFieldTextLength(0),
+    mLineNumber(0)
+{
+  MOZ_COUNT_CTOR(nsXBLProtoImplField);
+
+  mJSAttributes = JSPROP_ENUMERATE;
+  if (aIsReadOnly)
+    mJSAttributes |= JSPROP_READONLY;
+}
+
 nsXBLProtoImplField::~nsXBLProtoImplField()
 {
   MOZ_COUNT_DTOR(nsXBLProtoImplField);
   if (mFieldText)
     nsMemory::Free(mFieldText);
   NS_Free(mName);
   NS_CONTENT_DELETE_LIST_MEMBER(nsXBLProtoImplField, this, mNext);
 }
@@ -147,8 +163,50 @@ nsXBLProtoImplField::InstallField(nsIScr
                              name.Length(), result, nsnull, nsnull,
                              mJSAttributes)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   *aDidInstall = true;
   return NS_OK;
 }
+
+nsresult
+nsXBLProtoImplField::Read(nsIScriptContext* aContext,
+                          nsIObjectInputStream* aStream)
+{
+  nsAutoString name;
+  nsresult rv = aStream->ReadString(name);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mName = ToNewUnicode(name);
+
+  rv = aStream->Read32(&mLineNumber);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString fieldText;
+  rv = aStream->ReadString(fieldText);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mFieldTextLength = fieldText.Length();
+  if (mFieldTextLength)
+    mFieldText = ToNewUnicode(fieldText);
+
+  return NS_OK;
+}
+
+nsresult
+nsXBLProtoImplField::Write(nsIScriptContext* aContext,
+                           nsIObjectOutputStream* aStream)
+{
+  XBLBindingSerializeDetails type = XBLBinding_Serialize_Field;
+
+  if (mJSAttributes & JSPROP_READONLY) {
+    type |= XBLBinding_Serialize_ReadOnly;
+  }
+
+  nsresult rv = aStream->Write8(type);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->WriteWStringZ(mName);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->Write32(mLineNumber);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return aStream->WriteWStringZ(mFieldText ? mFieldText : EmptyString().get());
+}
--- a/content/xbl/src/nsXBLProtoImplField.h
+++ b/content/xbl/src/nsXBLProtoImplField.h
@@ -47,32 +47,36 @@
 #include "nsXBLProtoImplMember.h"
 
 class nsIURI;
 
 class nsXBLProtoImplField
 {
 public:
   nsXBLProtoImplField(const PRUnichar* aName, const PRUnichar* aReadOnly);
+  nsXBLProtoImplField(const bool aIsReadOnly);
   ~nsXBLProtoImplField();
 
   void AppendFieldText(const nsAString& aText);
   void SetLineNumber(PRUint32 aLineNumber) {
     mLineNumber = aLineNumber;
   }
   
   nsXBLProtoImplField* GetNext() const { return mNext; }
   void SetNext(nsXBLProtoImplField* aNext) { mNext = aNext; }
 
   nsresult InstallField(nsIScriptContext* aContext,
                         JSObject* aBoundNode,
                         nsIPrincipal* aPrincipal,
                         nsIURI* aBindingDocURI,
                         bool* aDidInstall) const;
 
+  nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream);
+  nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream);
+
   const PRUnichar* GetName() const { return mName; }
 
 protected:
   nsXBLProtoImplField* mNext;
   PRUnichar* mName;
   PRUnichar* mFieldText;
   PRUint32 mFieldTextLength;
   PRUint32 mLineNumber;
--- a/content/xbl/src/nsXBLProtoImplMember.h
+++ b/content/xbl/src/nsXBLProtoImplMember.h
@@ -114,14 +114,20 @@ public:
                                  void* aTargetClassObject,
                                  const nsCString& aClassStr) = 0;
   virtual nsresult CompileMember(nsIScriptContext* aContext,
                                  const nsCString& aClassStr,
                                  void* aClassObject)=0;
 
   virtual void Trace(TraceCallback aCallback, void *aClosure) const = 0;
 
+  virtual nsresult Write(nsIScriptContext* aContext,
+                         nsIObjectOutputStream* aStream)
+  {
+    return NS_OK;
+  }
+
 protected:
   nsXBLProtoImplMember* mNext;  // The members of an implementation are chained.
   PRUnichar* mName;               // The name of the field, method, or property.
 };
 
 #endif // nsXBLProtoImplMember_h__
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -46,16 +46,17 @@
 #include "mozilla/FunctionTimer.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsXBLProtoImplMethod.h"
 #include "nsIScriptContext.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
+#include "nsXBLPrototypeBinding.h"
 
 nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) :
   nsXBLProtoImplMember(aName), 
   mUncompiledMethod(BIT_UNCOMPILED)
 {
   MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
 }
 
@@ -263,16 +264,48 @@ void
 nsXBLProtoImplMethod::Trace(TraceCallback aCallback, void *aClosure) const
 {
   if (IsCompiled() && mJSMethodObject) {
     aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject, "mJSMethodObject", aClosure);
   }
 }
 
 nsresult
+nsXBLProtoImplMethod::Read(nsIScriptContext* aContext,
+                           nsIObjectInputStream* aStream)
+{
+  void* methodCode;
+  nsresult rv = XBL_DeserializeFunction(aContext, aStream, this, &methodCode);
+  mJSMethodObject = (JSObject *)methodCode;
+  if (NS_FAILED(rv)) {
+    SetUncompiledMethod(nsnull);
+    return rv;
+  }
+
+#ifdef DEBUG
+  mIsCompiled = true;
+#endif
+
+  return NS_OK;
+}
+
+nsresult
+nsXBLProtoImplMethod::Write(nsIScriptContext* aContext,
+                            nsIObjectOutputStream* aStream)
+{
+  nsresult rv = aStream->Write8(XBLBinding_Serialize_Method);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aStream->WriteWStringZ(mName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return XBL_SerializeFunction(aContext, aStream, mJSMethodObject);
+}
+
+nsresult
 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
 {
   NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
   
   if (!mJSMethodObject) {
     // Nothing to do here
     return NS_OK;
   }
@@ -343,8 +376,24 @@ nsXBLProtoImplAnonymousMethod::Execute(n
     JS_ReportPendingException(cx);
     if (saved)
         JS_RestoreFrameChain(cx);
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
+
+nsresult
+nsXBLProtoImplAnonymousMethod::Write(nsIScriptContext* aContext,
+                                     nsIObjectOutputStream* aStream,
+                                     XBLBindingSerializeDetails aType)
+{
+  if (mJSMethodObject) {
+    nsresult rv = aStream->Write8(aType);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = XBL_SerializeFunction(aContext, aStream, mJSMethodObject);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
--- a/content/xbl/src/nsXBLProtoImplMethod.h
+++ b/content/xbl/src/nsXBLProtoImplMethod.h
@@ -40,16 +40,17 @@
 #define nsXBLProtoImplMethod_h__
 
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "jsapi.h"
 #include "nsIContent.h"
 #include "nsString.h"
 #include "nsXBLProtoImplMember.h"
+#include "nsXBLSerialize.h"
 
 struct nsXBLParameter {
   nsXBLParameter* mNext;
   char* mName;
 
   nsXBLParameter(const nsAString& aName) {
     MOZ_COUNT_CTOR(nsXBLParameter);
     mName = ToNewCString(aName);
@@ -125,16 +126,19 @@ public:
                                  void* aTargetClassObject,
                                  const nsCString& aClassStr);
   virtual nsresult CompileMember(nsIScriptContext* aContext,
                                  const nsCString& aClassStr,
                                  void* aClassObject);
 
   virtual void Trace(TraceCallback aCallback, void *aClosure) const;
 
+  nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream);
+  virtual nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream);
+
   bool IsCompiled() const
   {
     return !(mUncompiledMethod & BIT_UNCOMPILED);
   }
   void SetUncompiledMethod(nsXBLUncompiledMethod* aUncompiledMethod)
   {
     mUncompiledMethod = PRUptrdiff(aUncompiledMethod) | BIT_UNCOMPILED;
   }
@@ -170,11 +174,15 @@ public:
   // prototype implementation).
   virtual nsresult InstallMember(nsIScriptContext* aContext,
                                  nsIContent* aBoundElement, 
                                  void* aScriptObject,
                                  void* aTargetClassObject,
                                  const nsCString& aClassStr) {
     return NS_OK;
   }
+
+  nsresult Write(nsIScriptContext* aContext,
+                 nsIObjectOutputStream* aStream,
+                 XBLBindingSerializeDetails aType);
 };
 
 #endif // nsXBLProtoImplMethod_h__
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp
+++ b/content/xbl/src/nsXBLProtoImplProperty.cpp
@@ -43,16 +43,18 @@
 #include "nsIDocument.h"
 #include "nsString.h"
 #include "nsXBLProtoImplProperty.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsContentUtils.h"
+#include "nsXBLPrototypeBinding.h"
+#include "nsXBLSerialize.h"
 
 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
                                                const PRUnichar* aGetter, 
                                                const PRUnichar* aSetter,
                                                const PRUnichar* aReadOnly) :
   nsXBLProtoImplMember(aName), 
   mGetterText(nsnull),
   mSetterText(nsnull),
@@ -70,16 +72,32 @@ nsXBLProtoImplProperty::nsXBLProtoImplPr
   }
 
   if (aGetter)
     AppendGetterText(nsDependentString(aGetter));
   if (aSetter)
     AppendSetterText(nsDependentString(aSetter));
 }
 
+nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
+                                               const bool aIsReadOnly)
+  : nsXBLProtoImplMember(aName),
+    mGetterText(nsnull),
+    mSetterText(nsnull),
+    mJSAttributes(JSPROP_ENUMERATE)
+#ifdef DEBUG
+  , mIsCompiled(false)
+#endif
+{
+  MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
+
+  if (aIsReadOnly)
+    mJSAttributes |= JSPROP_READONLY;
+}
+
 nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
 {
   MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
 
   if (!(mJSAttributes & JSPROP_GETTER)) {
     delete mGetterText;
   }
 
@@ -331,8 +349,78 @@ nsXBLProtoImplProperty::Trace(TraceCallb
               "mJSGetterObject", aClosure);
   }
 
   if (mJSAttributes & JSPROP_SETTER) {
     aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSSetterObject,
               "mJSSetterObject", aClosure);
   }
 }
+
+nsresult
+nsXBLProtoImplProperty::Read(nsIScriptContext* aContext,
+                             nsIObjectInputStream* aStream,
+                             XBLBindingSerializeDetails aType)
+{
+  nsresult rv;
+  void* scriptObject;
+
+  if (aType == XBLBinding_Serialize_GetterProperty ||
+      aType == XBLBinding_Serialize_GetterSetterProperty) {
+    rv = XBL_DeserializeFunction(aContext, aStream, this, &scriptObject);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mJSGetterObject = (JSObject *)scriptObject;
+    mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
+  }
+
+  if (aType == XBLBinding_Serialize_SetterProperty ||
+      aType == XBLBinding_Serialize_GetterSetterProperty) {
+    rv = XBL_DeserializeFunction(aContext, aStream, this, &scriptObject);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mJSSetterObject = (JSObject *)scriptObject;
+    mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
+  }
+
+#ifdef DEBUG
+  mIsCompiled = true;
+#endif
+
+  return NS_OK;
+}
+
+nsresult
+nsXBLProtoImplProperty::Write(nsIScriptContext* aContext,
+                              nsIObjectOutputStream* aStream)
+{
+  XBLBindingSerializeDetails type;
+
+  if (mJSAttributes & JSPROP_GETTER) {
+    type = mJSAttributes & JSPROP_SETTER ?
+           XBLBinding_Serialize_GetterSetterProperty :
+           XBLBinding_Serialize_GetterProperty;
+  }
+  else {
+    type = XBLBinding_Serialize_SetterProperty;
+  }
+
+  if (mJSAttributes & JSPROP_READONLY) {
+    type |= XBLBinding_Serialize_ReadOnly;
+  }
+
+  nsresult rv = aStream->Write8(type);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->WriteWStringZ(mName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (mJSAttributes & JSPROP_GETTER) {
+    rv = XBL_SerializeFunction(aContext, aStream, mJSGetterObject);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (mJSAttributes & JSPROP_SETTER) {
+    rv = XBL_SerializeFunction(aContext, aStream, mJSSetterObject);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
--- a/content/xbl/src/nsXBLProtoImplProperty.h
+++ b/content/xbl/src/nsXBLProtoImplProperty.h
@@ -39,25 +39,28 @@
 #ifndef nsXBLProtoImplProperty_h__
 #define nsXBLProtoImplProperty_h__
 
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "jsapi.h"
 #include "nsIContent.h"
 #include "nsString.h"
+#include "nsXBLSerialize.h"
 #include "nsXBLProtoImplMember.h"
 
 class nsXBLProtoImplProperty: public nsXBLProtoImplMember
 {
 public:
   nsXBLProtoImplProperty(const PRUnichar* aName,
                          const PRUnichar* aGetter, 
                          const PRUnichar* aSetter,
                          const PRUnichar* aReadOnly);
+
+  nsXBLProtoImplProperty(const PRUnichar* aName, bool aIsReadOnly);
  
   virtual ~nsXBLProtoImplProperty();
 
   void AppendGetterText(const nsAString& aGetter);
   void AppendSetterText(const nsAString& aSetter);
 
   void SetGetterLineNumber(PRUint32 aLineNumber);
   void SetSetterLineNumber(PRUint32 aLineNumber);
@@ -68,16 +71,22 @@ public:
                                  void* aTargetClassObject,
                                  const nsCString& aClassStr);
   virtual nsresult CompileMember(nsIScriptContext* aContext,
                                  const nsCString& aClassStr,
                                  void* aClassObject);
 
   virtual void Trace(TraceCallback aCallback, void *aClosure) const;
 
+  nsresult Read(nsIScriptContext* aContext,
+                nsIObjectInputStream* aStream,
+                XBLBindingSerializeDetails aType);
+  virtual nsresult Write(nsIScriptContext* aContext,
+                         nsIObjectOutputStream* aStream);
+
 protected:
   union {
     // The raw text for the getter (prior to compilation).
     nsXBLTextWithLineNumber* mGetterText;
     // The JS object for the getter (after compilation)
     JSObject *               mJSGetterObject;
   };
 
--- a/content/xbl/src/nsXBLPrototypeBinding.cpp
+++ b/content/xbl/src/nsXBLPrototypeBinding.cpp
@@ -56,35 +56,66 @@
 #include "nsIDocument.h"
 #include "nsIXMLContentSink.h"
 #include "nsContentCID.h"
 #include "nsXMLDocument.h"
 #include "nsXBLService.h"
 #include "nsXBLBinding.h"
 #include "nsXBLInsertionPoint.h"
 #include "nsXBLPrototypeBinding.h"
+#include "nsXBLContentSink.h"
 #include "nsFixedSizeAllocator.h"
 #include "xptinfo.h"
 #include "nsIInterfaceInfoManager.h"
 #include "nsIDocumentObserver.h"
 #include "nsGkAtoms.h"
 #include "nsXBLProtoImpl.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
 
 #include "nsIScriptContext.h"
+#include "nsIScriptError.h"
 
 #include "nsIStyleRuleProcessor.h"
 #include "nsXBLResourceLoader.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 
+#ifdef MOZ_XUL
+#include "nsXULElement.h"
+#endif
+
+using namespace mozilla::dom;
+
 // Helper Classes =====================================================================
 
+// Internal helper class for managing our IID table.
+class nsIIDKey : public nsHashKey {
+  public:
+    nsIID mKey;
+
+  public:
+    nsIIDKey(REFNSIID key) : mKey(key) {}
+    ~nsIIDKey(void) {}
+
+    PRUint32 HashCode(void) const {
+      // Just use the 32-bit m0 field.
+      return mKey.m0;
+    }
+
+    bool Equals(const nsHashKey *aKey) const {
+      return mKey.Equals( ((nsIIDKey*) aKey)->mKey);
+    }
+
+    nsHashKey *Clone(void) const {
+      return new nsIIDKey(mKey);
+    }
+};
+
 // nsXBLAttributeEntry and helpers.  This class is used to efficiently handle
 // attribute changes in anonymous content.
 
 class nsXBLAttributeEntry {
 public:
   nsIAtom* GetSrcAttribute() { return mSrcAttribute; }
   nsIAtom* GetDstAttribute() { return mDstAttribute; }
   PRInt32 GetDstNameSpace() { return mDstNameSpace; }
@@ -271,22 +302,23 @@ static const PRInt32 kInsInitialSize = (
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLPrototypeBinding::nsXBLPrototypeBinding()
 : mImplementation(nsnull),
   mBaseBinding(nsnull),
   mInheritStyle(true), 
-  mHasBaseProto(true),
+  mCheckedBaseProto(false),
   mKeyHandlersRegistered(false),
   mResources(nsnull),
   mAttributeTable(nsnull),
   mInsertionPointTable(nsnull),
-  mInterfaceTable(nsnull)
+  mInterfaceTable(nsnull),
+  mBaseNameSpaceID(kNameSpaceID_None)
 {
   MOZ_COUNT_CTOR(nsXBLPrototypeBinding);
   gRefCnt++;
 
   if (gRefCnt == 1) {
     kAttrPool = new nsFixedSizeAllocator();
     if (kAttrPool) {
       kAttrPool->Init("XBL Attribute Entries", kAttrBucketSizes, kAttrNumBuckets, kAttrInitialSize);
@@ -313,17 +345,21 @@ nsXBLPrototypeBinding::Init(const nsACSt
   if (aFirstBinding) {
     rv = mBindingURI->Clone(getter_AddRefs(mAlternateBindingURI));
     NS_ENSURE_SUCCESS(rv, rv);
   }
   mBindingURI->SetRef(aID);
 
   mXBLDocInfoWeak = aInfo;
 
-  SetBindingElement(aElement);
+  // aElement will be null when reading from the cache, but the element will
+  // still be set later.
+  if (aElement) {
+    SetBindingElement(aElement);
+  }
   return NS_OK;
 }
 
 bool nsXBLPrototypeBinding::CompareBindingURI(nsIURI* aURI) const
 {
   bool equal = false;
   mBindingURI->Equals(aURI, &equal);
   if (!equal && mAlternateBindingURI) {
@@ -986,16 +1022,17 @@ bool SetAttrs(nsHashKey* aKey, void* aDa
 
       nsIContent *realElement =
         changeData->mProto->LocateInstance(changeData->mBoundElement, content,
                                            changeData->mContent, element);
 
       if (realElement) {
         realElement->SetAttr(dstNs, dst, value, false);
 
+        // XXXndeakin shouldn't this be done in lieu of SetAttr?
         if ((dst == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) ||
             (realElement->NodeInfo()->Equals(nsGkAtoms::html,
                                              kNameSpaceID_XUL) &&
              dst == nsGkAtoms::value && !value.IsEmpty())) {
 
           nsCOMPtr<nsIContent> textContent;
           NS_NewTextNode(getter_AddRefs(textContent),
                          realElement->NodeInfo()->NodeInfoManager());
@@ -1068,32 +1105,66 @@ DeleteAttributeEntry(nsHashKey* aKey, vo
 static bool
 DeleteAttributeTable(nsHashKey* aKey, void* aData, void* aClosure)
 {
   delete static_cast<nsObjectHashtable*>(aData);
   return true;
 }
 
 void
+nsXBLPrototypeBinding::EnsureAttributeTable()
+{
+  if (!mAttributeTable) {
+    mAttributeTable = new nsObjectHashtable(nsnull, nsnull,
+                                            DeleteAttributeTable,
+                                            nsnull, 4);
+  }
+}
+
+void
+nsXBLPrototypeBinding::AddToAttributeTable(PRInt32 aSourceNamespaceID, nsIAtom* aSourceTag,
+                                           PRInt32 aDestNamespaceID, nsIAtom* aDestTag,
+                                           nsIContent* aContent)
+{
+    nsPRUint32Key nskey(aSourceNamespaceID);
+    nsObjectHashtable* attributesNS =
+      static_cast<nsObjectHashtable*>(mAttributeTable->Get(&nskey));
+    if (!attributesNS) {
+      attributesNS = new nsObjectHashtable(nsnull, nsnull,
+                                           DeleteAttributeEntry,
+                                           nsnull, 4);
+      mAttributeTable->Put(&nskey, attributesNS);
+    }
+  
+    nsXBLAttributeEntry* xblAttr =
+      nsXBLAttributeEntry::Create(aSourceTag, aDestTag, aDestNamespaceID, aContent);
+
+    nsISupportsKey key(aSourceTag);
+    nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry*>
+                                            (attributesNS->Get(&key));
+    if (!entry) {
+      attributesNS->Put(&key, xblAttr);
+    } else {
+      while (entry->GetNext())
+        entry = entry->GetNext();
+      entry->SetNext(xblAttr);
+    }
+}
+
+void
 nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement)
 {
   // Don't add entries for <children> elements, since those will get
   // removed from the DOM when we construct the insertion point table.
   if (!aElement->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
     nsAutoString inherits;
     aElement->GetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, inherits);
 
     if (!inherits.IsEmpty()) {
-      if (!mAttributeTable) {
-        mAttributeTable = new nsObjectHashtable(nsnull, nsnull,
-                                                DeleteAttributeTable,
-                                                nsnull, 4);
-        if (!mAttributeTable)
-          return;
-      }
+      EnsureAttributeTable();
 
       // The user specified at least one attribute.
       char* str = ToNewCString(inherits);
       char* newStr;
       // XXX We should use a strtok function that tokenizes PRUnichars
       // so that we don't have to convert from Unicode to ASCII and then back
 
       char* token = nsCRT::strtok( str, ", ", &newStr );
@@ -1130,48 +1201,17 @@ nsXBLPrototypeBinding::ConstructAttribut
           rv = nsContentUtils::SplitQName(aElement, tok, &atomNsID, 
                                           getter_AddRefs(atom));
           if (NS_FAILED(rv))
             return;
           attribute = atom;
           attributeNsID = atomNsID;
         }
 
-        nsPRUint32Key nskey(atomNsID);
-        nsObjectHashtable* attributesNS =
-          static_cast<nsObjectHashtable*>(mAttributeTable->Get(&nskey));
-        if (!attributesNS) {
-          attributesNS = new nsObjectHashtable(nsnull, nsnull,
-                                               DeleteAttributeEntry,
-                                               nsnull, 4);
-          if (!attributesNS)
-            return;
-
-          mAttributeTable->Put(&nskey, attributesNS);
-        }
-      
-        // Create an XBL attribute entry.
-        nsXBLAttributeEntry* xblAttr =
-          nsXBLAttributeEntry::Create(atom, attribute, attributeNsID, aElement);
-
-        // Now we should see if some element within our anonymous
-        // content is already observing this attribute.
-        nsISupportsKey key(atom);
-        nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry*>
-                                                (attributesNS->Get(&key));
-
-        if (!entry) {
-          // Put it in the table.
-          attributesNS->Put(&key, xblAttr);
-        } else {
-          while (entry->GetNext())
-            entry = entry->GetNext();
-
-          entry->SetNext(xblAttr);
-        }
+        AddToAttributeTable(atomNsID, atom, attributeNsID, attribute, aElement);
 
         // Now remove the inherits attribute from the element so that it doesn't
         // show up on clones of the element.  It is used
         // by the template only, and we don't need it anymore.
         // XXXdwh Don't do this for XUL elements, since it faults them into heavyweight
         // elements. Should nuke from the prototype instead.
         // aElement->UnsetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, false);
 
@@ -1238,17 +1278,17 @@ nsXBLPrototypeBinding::ConstructInsertio
 
       char* token = nsCRT::strtok( str, "| ", &newStr );
       while( token != NULL ) {
         nsAutoString tok;
         tok.AssignWithConversion(token);
 
         // Build an atom out of this string.
         nsCOMPtr<nsIAtom> atom = do_GetAtom(tok);
-           
+
         nsISupportsKey key(atom);
         xblIns->AddRef();
         mInsertionPointTable->Put(&key, xblIns);
           
         token = nsCRT::strtok( newStr, "| ", &newStr );
       }
 
       nsMemory::Free(str);
@@ -1421,8 +1461,927 @@ nsXBLPrototypeBinding::CreateKeyHandlers
 
       if (handler)
         handler->AddProtoHandler(curr);
     }
 
     curr = curr->GetNextHandler();
   }
 }
+
+nsresult
+nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream,
+                            nsXBLDocumentInfo* aDocInfo,
+                            nsIDocument* aDocument,
+                            PRUint8 aFlags)
+{
+  mInheritStyle = (aFlags & XBLBinding_Serialize_InheritStyle) ? true : false;
+
+  // nsXBLContentSink::ConstructBinding doesn't create a binding with an empty
+  // id, so we don't here either.
+  nsCAutoString id;
+  nsresult rv = aStream->ReadCString(id);
+
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(!id.IsEmpty(), NS_ERROR_FAILURE);
+
+  nsCAutoString baseBindingURI;
+  rv = aStream->ReadCString(baseBindingURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mCheckedBaseProto = true;
+
+  if (!baseBindingURI.IsEmpty()) {
+    rv = NS_NewURI(getter_AddRefs(mBaseBindingURI), baseBindingURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = ReadNamespace(aStream, mBaseNameSpaceID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString baseTag;
+  rv = aStream->ReadString(baseTag);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!baseTag.IsEmpty()) {
+    mBaseTag = do_GetAtom(baseTag);
+  }
+
+  aDocument->CreateElem(NS_LITERAL_STRING("binding"), nsnull, kNameSpaceID_XBL,
+                        getter_AddRefs(mBinding));
+
+  nsCOMPtr<nsIContent> child;
+  rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(), getter_AddRefs(child));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  Element* rootElement = aDocument->GetRootElement();
+  if (rootElement)
+    rootElement->AppendChildTo(mBinding, false);
+
+  if (child) {
+    mBinding->AppendChildTo(child, false);
+  }
+
+  PRUint32 interfaceCount;
+  rv = aStream->Read32(&interfaceCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (interfaceCount > 0) {
+    NS_ASSERTION(!mInterfaceTable, "non-null mInterfaceTable");
+    mInterfaceTable = new nsSupportsHashtable(interfaceCount);
+    NS_ENSURE_TRUE(mInterfaceTable, NS_ERROR_OUT_OF_MEMORY);
+
+    for (; interfaceCount > 0; interfaceCount--) {
+      nsIID iid;
+      aStream->ReadID(&iid);
+      nsIIDKey key(iid);
+      mInterfaceTable->Put(&key, mBinding);
+    }
+  }
+
+  nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(do_QueryObject(aDocInfo));
+  nsIScriptGlobalObject* globalObject = globalOwner->GetScriptGlobalObject();
+  NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
+
+  nsIScriptContext *context = globalObject->GetContext();
+  NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
+
+  bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding;
+  rv = Init(id, aDocInfo, nsnull, isFirstBinding);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We need to set the prototype binding before reading the nsXBLProtoImpl,
+  // as it may be retrieved within.
+  rv = aDocInfo->SetPrototypeBinding(id, this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString className;
+  rv = aStream->ReadCString(className);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!className.IsEmpty()) {
+    nsXBLProtoImpl* impl; // NS_NewXBLProtoImpl will set mImplementation for us
+    NS_NewXBLProtoImpl(this, NS_ConvertUTF8toUTF16(className).get(), &impl);
+
+    // This needs to happen after SetPrototypeBinding as calls are made to
+    // retrieve the mapped bindings from within here. However, if an error
+    // occurs, the mapping should be removed again so that we don't keep an
+    // invalid binding around.
+    rv = mImplementation->Read(context, aStream, this, globalObject);
+    if (NS_FAILED(rv)) {
+      aDocInfo->RemovePrototypeBinding(id);
+      return rv;
+    }
+  }
+
+  // Next read in the handlers.
+  nsXBLPrototypeHandler* previousHandler = nsnull;
+
+  do {
+    XBLBindingSerializeDetails type;
+    rv = aStream->Read8(&type);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (type == XBLBinding_Serialize_NoMoreItems)
+      break;
+
+    NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Handler,
+                 "invalid handler type");
+
+    nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(this);
+    rv = handler->Read(context, aStream);
+    if (NS_FAILED(rv)) {
+      delete handler;
+      return rv;
+    }
+
+    if (previousHandler) {
+      previousHandler->SetNextHandler(handler);
+    }
+    else {
+      SetPrototypeHandlers(handler);
+    }
+    previousHandler = handler;
+  } while (1);
+
+  // Finally, read in the resources.
+  do {
+    XBLBindingSerializeDetails type;
+    rv = aStream->Read8(&type);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (type == XBLBinding_Serialize_NoMoreItems)
+      break;
+
+    NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Stylesheet ||
+                 (type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Image, "invalid resource type");
+
+    nsAutoString src;
+    rv = aStream->ReadString(src);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    AddResource(type == XBLBinding_Serialize_Stylesheet ? nsGkAtoms::stylesheet :
+                                                          nsGkAtoms::image, src);
+  } while (1);
+
+  if (isFirstBinding) {
+    aDocInfo->SetFirstPrototypeBinding(this);
+  }
+
+  return NS_OK;
+}
+
+static
+bool
+GatherInsertionPoints(nsHashKey *aKey, void *aData, void* aClosure)
+{
+  ArrayOfInsertionPointsByContent* insertionPointsByContent =
+    static_cast<ArrayOfInsertionPointsByContent *>(aClosure);
+
+  nsXBLInsertionPointEntry* entry = static_cast<nsXBLInsertionPointEntry *>(aData);
+
+  // Add a new blank array if it isn't already there.
+  nsAutoTArray<InsertionItem, 1>* list;
+  if (!insertionPointsByContent->Get(entry->GetInsertionParent(), &list)) {
+    list = new nsAutoTArray<InsertionItem, 1>;
+    insertionPointsByContent->Put(entry->GetInsertionParent(), list);
+  }
+
+  // Add the item to the array and ensure it stays sorted by insertion index.
+  nsIAtom* atom = static_cast<nsIAtom *>(
+                    static_cast<nsISupportsKey *>(aKey)->GetValue());
+  InsertionItem newitem(entry->GetInsertionIndex(), atom, entry->GetDefaultContent());
+  list->InsertElementSorted(newitem);
+
+  return kHashEnumerateNext;
+}
+
+static
+bool
+WriteInterfaceID(nsHashKey *aKey, void *aData, void* aClosure)
+{
+  // We can just write out the ids. The cache will be invalidated when a
+  // different build is used, so we don't need to worry about ids changing.
+  nsID iid = ((nsIIDKey *)aKey)->mKey;
+  static_cast<nsIObjectOutputStream *>(aClosure)->WriteID(iid);
+  return kHashEnumerateNext;
+}
+
+nsresult
+nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream)
+{
+  // This writes out the binding. Note that mCheckedBaseProto,
+  // mKeyHandlersRegistered and mKeyHandlers are not serialized as they are
+  // computed on demand.
+
+  nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(do_QueryObject(mXBLDocInfoWeak));
+  nsIScriptGlobalObject* globalObject = globalOwner->GetScriptGlobalObject();
+  NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
+
+  nsIScriptContext *context = globalObject->GetContext();
+  NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
+
+  PRUint8 flags = mInheritStyle ? XBLBinding_Serialize_InheritStyle : 0;
+
+  // mAlternateBindingURI is only set on the first binding.
+  if (mAlternateBindingURI) {
+    flags |= XBLBinding_Serialize_IsFirstBinding;
+  }
+
+  nsresult rv = aStream->Write8(flags);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString id;
+  mBindingURI->GetRef(id);
+  rv = aStream->WriteStringZ(id.get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // write out the extends and display attribute values
+  nsCAutoString extends;
+  ResolveBaseBinding();
+  if (mBaseBindingURI)
+    mBaseBindingURI->GetSpec(extends);
+
+  rv = aStream->WriteStringZ(extends.get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = WriteNamespace(aStream, mBaseNameSpaceID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString baseTag;
+  if (mBaseTag) {
+    mBaseTag->ToString(baseTag);
+  }
+  rv = aStream->WriteWStringZ(baseTag.get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The binding holds insertions points keyed by the tag that is going to be
+  // inserted, however, the cache would prefer to know insertion points keyed
+  // by where they are in the content hierarchy. GatherInsertionPoints is used
+  // to iterate over the insertion points and store them temporarily in this
+  // latter hashtable.
+  ArrayOfInsertionPointsByContent insertionPointsByContent;
+  insertionPointsByContent.Init();
+  if (mInsertionPointTable) {
+    mInsertionPointTable->Enumerate(GatherInsertionPoints, &insertionPointsByContent);
+  }
+
+  nsIContent* content = GetImmediateChild(nsGkAtoms::content);
+  if (content) {
+    rv = WriteContentNode(aStream, content, insertionPointsByContent);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  else {
+    // Write a marker to indicate that there is no content.
+    rv = aStream->Write8(XBLBinding_Serialize_NoContent);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Enumerate and write out the implemented interfaces.
+  if (mInterfaceTable) {
+    rv = aStream->Write32(mInterfaceTable->Count());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mInterfaceTable->Enumerate(WriteInterfaceID, aStream);
+  }
+  else {
+    rv = aStream->Write32(0);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Write out the implementation details.
+  if (mImplementation) {
+    rv = mImplementation->Write(context, aStream, this);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  else {
+    // Write out an empty classname. This indicates that the binding does not
+    // define an implementation.
+    rv = aStream->WriteWStringZ(EmptyString().get());
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Write out the handlers.
+  nsXBLPrototypeHandler* handler = mPrototypeHandler;
+  while (handler) {
+    rv = handler->Write(context, aStream);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    handler = handler->GetNextHandler();
+  }
+
+  aStream->Write8(XBLBinding_Serialize_NoMoreItems);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Write out the resources
+  if (mResources) {
+    rv = mResources->Write(aStream);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Write out an end mark at the end.
+  return aStream->Write8(XBLBinding_Serialize_NoMoreItems);
+}
+
+nsresult
+nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
+                                       nsIDocument* aDocument,
+                                       nsNodeInfoManager* aNim,
+                                       nsIContent** aContent)
+{
+  *aContent = nsnull;
+
+  PRInt32 namespaceID;
+  nsresult rv = ReadNamespace(aStream, namespaceID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // There is no content to read so just return.
+  if (namespaceID == XBLBinding_Serialize_NoContent)
+    return NS_OK;
+
+  nsCOMPtr<nsIContent> content;
+
+  // If this is a text type, just read the string and return.
+  if (namespaceID == XBLBinding_Serialize_TextNode ||
+      namespaceID == XBLBinding_Serialize_CDATANode ||
+      namespaceID == XBLBinding_Serialize_CommentNode) {
+    switch (namespaceID) {
+      case XBLBinding_Serialize_TextNode:
+        rv = NS_NewTextNode(getter_AddRefs(content), aNim);
+        break;
+      case XBLBinding_Serialize_CDATANode:
+        rv = NS_NewXMLCDATASection(getter_AddRefs(content), aNim);
+        break;
+      case XBLBinding_Serialize_CommentNode:
+        rv = NS_NewCommentNode(getter_AddRefs(content), aNim);
+        break;
+      default:
+        break;
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoString text;
+    rv = aStream->ReadString(text);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    content->SetText(text, false);
+    content.swap(*aContent);
+    return NS_OK;
+  }
+
+  // Otherwise, it's an element, so read its tag, attributes and children.
+  nsAutoString prefix, tag;
+  rv = aStream->ReadString(prefix);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIAtom> prefixAtom;
+  if (!prefix.IsEmpty())
+    prefixAtom = do_GetAtom(prefix);
+
+  rv = aStream->ReadString(tag);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
+  nsCOMPtr<nsINodeInfo> nodeInfo =
+    aNim->GetNodeInfo(tagAtom, prefixAtom, namespaceID, nsIDOMNode::ELEMENT_NODE);
+
+  PRUint32 attrCount;
+  rv = aStream->Read32(&attrCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Create XUL prototype elements, or regular elements for other namespaces.
+  // This needs to match the code in nsXBLContentSink::CreateElement.
+#ifdef MOZ_XUL
+  if (namespaceID == kNameSpaceID_XUL) {
+    nsIURI* documentURI = aDocument->GetDocumentURI();
+
+    nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
+    NS_ENSURE_TRUE(prototype, NS_ERROR_OUT_OF_MEMORY);
+
+    prototype->mNodeInfo = nodeInfo;
+    prototype->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
+
+    nsCOMPtr<Element> result;
+    nsresult rv =
+      nsXULElement::Create(prototype, aDocument, false, getter_AddRefs(result));
+    NS_ENSURE_SUCCESS(rv, rv);
+    content = result;
+
+    nsXULPrototypeAttribute* attrs = nsnull;
+    if (attrCount > 0) {
+      attrs = new nsXULPrototypeAttribute[attrCount];
+    }
+
+    prototype->mAttributes = attrs;
+    prototype->mNumAttributes = attrCount;
+
+    for (PRUint32 i = 0; i < attrCount; i++) {
+      rv = ReadNamespace(aStream, namespaceID);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsAutoString prefix, name, val;
+      rv = aStream->ReadString(prefix);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = aStream->ReadString(name);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = aStream->ReadString(val);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
+      if (namespaceID == kNameSpaceID_None) {
+        attrs[i].mName.SetTo(nameAtom);
+      }
+      else {
+        nsCOMPtr<nsIAtom> prefixAtom;
+        if (!prefix.IsEmpty())
+          prefixAtom = do_GetAtom(prefix);
+
+        nsCOMPtr<nsINodeInfo> ni =
+          aNim->GetNodeInfo(nameAtom, prefixAtom,
+                            namespaceID, nsIDOMNode::ATTRIBUTE_NODE);
+        attrs[i].mName.SetTo(ni);
+      }
+      
+      rv = prototype->SetAttrAt(i, val, documentURI);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+  else {
+#endif
+    nsCOMPtr<nsINodeInfo> ni = nodeInfo;
+    NS_NewElement(getter_AddRefs(content), nodeInfo->NamespaceID(),
+                  ni.forget(), mozilla::dom::NOT_FROM_PARSER);
+
+    for (PRUint32 i = 0; i < attrCount; i++) {
+      rv = ReadNamespace(aStream, namespaceID);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsAutoString prefix, name, val;
+      rv = aStream->ReadString(prefix);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = aStream->ReadString(name);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = aStream->ReadString(val);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIAtom> prefixAtom;
+      if (!prefix.IsEmpty())
+        prefixAtom = do_GetAtom(prefix);
+
+      nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
+      content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
+    }
+
+#ifdef MOZ_XUL
+  }
+#endif
+
+  // Now read the attribute forwarding entries (xbl:inherits)
+
+  PRInt32 srcNamespaceID, destNamespaceID;
+  rv = ReadNamespace(aStream, srcNamespaceID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  while (srcNamespaceID != XBLBinding_Serialize_NoMoreAttributes) {
+    nsAutoString srcAttribute, destAttribute;
+    rv = aStream->ReadString(srcAttribute);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = ReadNamespace(aStream, destNamespaceID);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = aStream->ReadString(destAttribute);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIAtom> srcAtom = do_GetAtom(srcAttribute);
+    nsCOMPtr<nsIAtom> destAtom = do_GetAtom(destAttribute);
+
+    EnsureAttributeTable();
+    AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content);
+
+    rv = ReadNamespace(aStream, srcNamespaceID);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Next, read the insertion points.
+  PRUint32 insertionPointIndex;
+  rv = aStream->Read32(&insertionPointIndex);
+  NS_ENSURE_SUCCESS(rv, rv);
+  while (insertionPointIndex != XBLBinding_Serialize_NoMoreInsertionPoints) {
+    nsRefPtr<nsXBLInsertionPointEntry> xblIns =
+      nsXBLInsertionPointEntry::Create(content);
+    xblIns->SetInsertionIndex(insertionPointIndex);
+
+    // If the insertion point has default content, read it.
+    nsCOMPtr<nsIContent> defaultContent;
+    rv = ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(defaultContent));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (defaultContent) {
+      xblIns->SetDefaultContent(defaultContent);
+
+      rv = defaultContent->BindToTree(nsnull, content, nsnull, false);
+      if (NS_FAILED(rv)) {
+        defaultContent->UnbindFromTree();
+        return rv;
+      }
+    }
+
+    if (!mInsertionPointTable) {
+      mInsertionPointTable = new nsObjectHashtable(nsnull, nsnull,
+                                                   DeleteInsertionPointEntry,
+                                                   nsnull, 4);
+    }
+
+    // Now read in the tags that can be inserted at this point, which is
+    // specified by the syntax includes="menupopup|panel", and add them to
+    // the hash.
+    PRUint32 count;
+    rv = aStream->Read32(&count);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    for (; count > 0; count --) {
+      aStream->ReadString(tag);
+      nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
+
+      nsISupportsKey key(tagAtom);
+      NS_ADDREF(xblIns.get());
+      mInsertionPointTable->Put(&key, xblIns);
+    }
+
+    rv = aStream->Read32(&insertionPointIndex);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Finally, read in the child nodes.
+  PRUint32 childCount;
+  rv = aStream->Read32(&childCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (PRUint32 i = 0; i < childCount; i++) {
+    nsCOMPtr<nsIContent> child;
+    ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child));
+
+    // Child may be null if this was a comment for example and can just be ignored.
+    if (child) {
+      content->AppendChildTo(child, false);
+    }
+  }
+
+  content.swap(*aContent);
+  return NS_OK;
+}
+
+// This structure holds information about a forwarded attribute that needs to be
+// written out. This is used because we need several fields passed within the
+// enumeration closure.
+struct WriteAttributeData
+{
+  nsXBLPrototypeBinding* binding;
+  nsIObjectOutputStream* stream;
+  nsIContent* content;
+  PRInt32 srcNamespace;
+
+  WriteAttributeData(nsXBLPrototypeBinding* aBinding,
+                     nsIObjectOutputStream* aStream,
+                     nsIContent* aContent)
+    : binding(aBinding), stream(aStream), content(aContent)
+  { }
+};
+
+static
+bool
+WriteAttribute(nsHashKey *aKey, void *aData, void* aClosure)
+{
+  WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
+  nsIObjectOutputStream* stream = data->stream;
+  const PRInt32 srcNamespace = data->srcNamespace;
+
+  nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry *>(aData);
+  do {
+    if (entry->GetElement() == data->content) {
+      data->binding->WriteNamespace(stream, srcNamespace);
+      stream->WriteWStringZ(nsDependentAtomString(entry->GetSrcAttribute()).get());
+      data->binding->WriteNamespace(stream, entry->GetDstNameSpace());
+      stream->WriteWStringZ(nsDependentAtomString(entry->GetDstAttribute()).get());
+    }
+
+    entry = entry->GetNext();
+  } while (entry);
+
+  return kHashEnumerateNext;
+}
+
+// WriteAttributeNS is the callback to enumerate over the attribute
+// forwarding entries. Since these are stored in a hash of hashes,
+// we need to iterate over the inner hashes, calling WriteAttribute
+// to do the actual work.
+static
+bool
+WriteAttributeNS(nsHashKey *aKey, void *aData, void* aClosure)
+{
+  WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
+  data->srcNamespace = static_cast<nsPRUint32Key *>(aKey)->GetValue();
+
+  nsObjectHashtable* attributes = static_cast<nsObjectHashtable*>(aData);
+  attributes->Enumerate(WriteAttribute, data);
+
+  return kHashEnumerateNext;
+}
+
+nsresult
+nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
+                                        nsIContent* aNode,
+                                        ArrayOfInsertionPointsByContent& aInsertionPointsByContent)
+{
+  nsresult rv;
+
+  if (!aNode->IsElement()) {
+    // Text is writen out as a single byte for the type, followed by the text.
+    PRUint8 type = XBLBinding_Serialize_NoContent;
+    switch (aNode->NodeType()) {
+      case nsIDOMNode::TEXT_NODE:
+        type = XBLBinding_Serialize_TextNode;
+        break;
+      case nsIDOMNode::CDATA_SECTION_NODE:
+        type = XBLBinding_Serialize_CDATANode;
+        break;
+      case nsIDOMNode::COMMENT_NODE:
+        type = XBLBinding_Serialize_CommentNode;
+        break;
+      default:
+        break;
+    }
+
+    rv = aStream->Write8(type);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoString content;
+    aNode->GetText()->AppendTo(content);
+    return aStream->WriteWStringZ(content.get());
+  }
+
+  // Otherwise, this is an element.
+
+  // Write the namespace id followed by the tag name
+  rv = WriteNamespace(aStream, aNode->GetNameSpaceID());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString prefixStr;
+  aNode->NodeInfo()->GetPrefix(prefixStr);
+  rv = aStream->WriteWStringZ(prefixStr.get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aStream->WriteWStringZ(nsDependentAtomString(aNode->Tag()).get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Write attributes
+  PRUint32 count = aNode->GetAttrCount();
+  rv = aStream->Write32(count);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 i;
+  for (i = 0; i < count; i++) {
+    // Write out the namespace id, the namespace prefix, the local tag name,
+    // and the value, in that order.
+
+    const nsAttrName* attr = aNode->GetAttrNameAt(i);
+
+    // XXXndeakin don't write out xbl:inherits?
+    PRInt32 namespaceID = attr->NamespaceID();
+    rv = WriteNamespace(aStream, namespaceID);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoString prefixStr;
+    nsIAtom* prefix = attr->GetPrefix();
+    if (prefix)
+      prefix->ToString(prefixStr);
+    rv = aStream->WriteWStringZ(prefixStr.get());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = aStream->WriteWStringZ(nsDependentAtomString(attr->LocalName()).get());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoString val;
+    aNode->GetAttr(attr->NamespaceID(), attr->LocalName(), val);
+    rv = aStream->WriteWStringZ(val.get());
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Write out the attribute fowarding information
+  if (mAttributeTable) {
+    WriteAttributeData data(this, aStream, aNode);
+    mAttributeTable->Enumerate(WriteAttributeNS, &data);
+  }
+  rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Write out the insertion points.
+  nsAutoTArray<InsertionItem, 1>* list;
+  if (aInsertionPointsByContent.Get(aNode, &list)) {
+    PRUint32 lastInsertionIndex = 0xFFFFFFFF;
+
+    // Iterate over the insertion points and see if they match this point
+    // in the content tree by comparing insertion indices.
+    for (PRUint32 l = 0; l < list->Length(); l++) {
+      InsertionItem item = list->ElementAt(l);
+      // The list is sorted by insertionIndex so all items pertaining to
+      // this point will be in the list in order. We only need to write out the
+      // default content and the number of tags once for each index.
+      if (item.insertionIndex != lastInsertionIndex) {
+        lastInsertionIndex = item.insertionIndex;
+        aStream->Write32(item.insertionIndex);
+        // If the insertion point has default content, write that out, or
+        // write out XBLBinding_Serialize_NoContent if there isn't any.
+        if (item.defaultContent) {
+          rv = WriteContentNode(aStream, item.defaultContent,
+                                aInsertionPointsByContent);
+        }
+        else {
+          rv = aStream->Write8(XBLBinding_Serialize_NoContent);
+        }
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        // Determine how many insertion points share the same index, so that
+        // the total count can be written out.
+        PRUint32 icount = 1;
+        for (PRUint32 i = l + 1; i < list->Length(); i++) {
+          if (list->ElementAt(i).insertionIndex != lastInsertionIndex)
+            break;
+          icount++;
+        }
+
+        rv = aStream->Write32(icount);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      rv = aStream->WriteWStringZ(nsDependentAtomString(list->ElementAt(l).tag).get());
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  // Write out an end marker after the insertion points. If there weren't
+  // any, this will be all that is written out.
+  rv = aStream->Write32(XBLBinding_Serialize_NoMoreInsertionPoints);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Finally, write out the child nodes.
+  count = aNode->GetChildCount();
+  rv = aStream->Write32(count);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (i = 0; i < count; i++) {
+    rv = WriteContentNode(aStream, aNode->GetChildAt(i), aInsertionPointsByContent);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream,
+                                     PRInt32& aNameSpaceID)
+{
+  PRUint8 namespaceID;
+  nsresult rv = aStream->Read8(&namespaceID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (namespaceID == XBLBinding_Serialize_CustomNamespace) {
+    nsAutoString namesp;
+    rv = aStream->ReadString(namesp);
+    NS_ENSURE_SUCCESS(rv, rv);
+  
+    nsContentUtils::NameSpaceManager()->RegisterNameSpace(namesp, aNameSpaceID);
+  }
+  else {
+    aNameSpaceID = namespaceID;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeBinding::WriteNamespace(nsIObjectOutputStream* aStream,
+                                      PRInt32 aNameSpaceID)
+{
+  // Namespaces are stored as a single byte id for well-known namespaces.
+  // This saves time and space as other namespaces aren't very common in
+  // XBL. If another namespace is used however, the namespace id will be
+  // XBLBinding_Serialize_CustomNamespace and the string namespace written
+  // out directly afterwards.
+  nsresult rv;
+
+  if (aNameSpaceID <= kNameSpaceID_LastBuiltin) {
+    rv = aStream->Write8((PRInt8)aNameSpaceID);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  else {
+    rv = aStream->Write8(XBLBinding_Serialize_CustomNamespace);
+    NS_ENSURE_SUCCESS(rv, rv);
+  
+    nsAutoString namesp;
+    nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, namesp);
+    aStream->WriteWStringZ(namesp.get());
+  }
+
+  return NS_OK;
+}
+
+
+bool CheckTagNameWhiteList(PRInt32 aNameSpaceID, nsIAtom *aTagName)
+{
+  static nsIContent::AttrValuesArray kValidXULTagNames[] =  {
+    &nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser,
+    &nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu,
+    &nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup,
+    &nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer,
+    &nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nsnull};
+
+  PRUint32 i;
+  if (aNameSpaceID == kNameSpaceID_XUL) {
+    for (i = 0; kValidXULTagNames[i]; ++i) {
+      if (aTagName == *(kValidXULTagNames[i])) {
+        return true;
+      }
+    }
+  }
+  else if (aNameSpaceID == kNameSpaceID_SVG &&
+           aTagName == nsGkAtoms::generic) {
+    return true;
+  }
+
+  return false;
+}
+
+nsresult
+nsXBLPrototypeBinding::ResolveBaseBinding()
+{
+  if (mCheckedBaseProto)
+    return NS_OK;
+  mCheckedBaseProto = true;
+
+  nsCOMPtr<nsIDocument> doc = mXBLDocInfoWeak->GetDocument();
+
+  // Check for the presence of 'extends' and 'display' attributes
+  nsAutoString display, extends;
+  mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends);
+  if (extends.IsEmpty())
+    return NS_OK;
+
+  mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
+  bool hasDisplay = !display.IsEmpty();
+
+  nsAutoString value(extends);
+       
+  // Now slice 'em up to see what we've got.
+  nsAutoString prefix;
+  PRInt32 offset;
+  if (hasDisplay) {
+    offset = display.FindChar(':');
+    if (-1 != offset) {
+      display.Left(prefix, offset);
+      display.Cut(0, offset+1);
+    }
+  }
+  else {
+    offset = extends.FindChar(':');
+    if (-1 != offset) {
+      extends.Left(prefix, offset);
+      extends.Cut(0, offset+1);
+      display = extends;
+    }
+  }
+
+  nsAutoString nameSpace;
+
+  if (!prefix.IsEmpty()) {
+    mBinding->LookupNamespaceURI(prefix, nameSpace);
+    if (!nameSpace.IsEmpty()) {
+      PRInt32 nameSpaceID =
+        nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
+
+      nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
+      // Check the white list
+      if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
+        const PRUnichar* params[] = { display.get() };
+        nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
+                                       "InvalidExtendsBinding",
+                                        params, NS_ARRAY_LENGTH(params),
+                                        doc->GetDocumentURI(),
+                                        EmptyString(), 0, 0,
+                                        nsIScriptError::errorFlag,
+                                        "XBL");
+        NS_ASSERTION(!nsXBLService::IsChromeOrResourceURI(doc->GetDocumentURI()),
+                     "Invalid extends value");
+        return NS_ERROR_ILLEGAL_VALUE;
+      }
+
+      SetBaseTag(nameSpaceID, tagName);
+    }
+  }
+
+  if (hasDisplay || nameSpace.IsEmpty()) {
+    mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
+    mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false);
+
+    return NS_NewURI(getter_AddRefs(mBaseBindingURI), value,
+                     doc->GetDocumentCharacterSet().get(),
+                     doc->GetDocBaseURI());
+  }
+
+  return NS_OK;
+}
--- a/content/xbl/src/nsXBLPrototypeBinding.h
+++ b/content/xbl/src/nsXBLPrototypeBinding.h
@@ -42,45 +42,75 @@
 #include "nsCOMPtr.h"
 #include "nsXBLPrototypeResources.h"
 #include "nsXBLPrototypeHandler.h"
 #include "nsXBLProtoImplMethod.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsWeakReference.h"
 #include "nsIContent.h"
 #include "nsHashtable.h"
+#include "nsClassHashtable.h"
 #include "nsXBLDocumentInfo.h"
 #include "nsCOMArray.h"
 #include "nsXBLProtoImpl.h"
 
 class nsIAtom;
 class nsIDocument;
 class nsIScriptContext;
 class nsSupportsHashtable;
 class nsIXBLService;
 class nsFixedSizeAllocator;
 class nsXBLProtoImplField;
 class nsXBLBinding;
 class nsCSSStyleSheet;
 
+// This structure represents an insertion point, and is used when writing out
+// insertion points. It contains comparison operators so that it can be stored
+// in an array sorted by index.
+struct InsertionItem {
+  PRUint32 insertionIndex;
+  nsIAtom* tag;
+  nsIContent* defaultContent;
+
+  InsertionItem(PRUint32 aInsertionIndex, nsIAtom* aTag, nsIContent* aDefaultContent)
+    : insertionIndex(aInsertionIndex), tag(aTag), defaultContent(aDefaultContent) { }
+
+  bool operator<(const InsertionItem& item) const
+  {
+    NS_ASSERTION(insertionIndex != item.insertionIndex || defaultContent == item.defaultContent,
+                 "default content is different for same index");
+    return insertionIndex < item.insertionIndex;
+  }
+
+  bool operator==(const InsertionItem& item) const
+  {
+    NS_ASSERTION(insertionIndex != item.insertionIndex || defaultContent == item.defaultContent,
+                 "default content is different for same index");
+    return insertionIndex == item.insertionIndex;
+  }
+};
+
+typedef nsClassHashtable<nsISupportsHashKey, nsAutoTArray<InsertionItem, 1> > ArrayOfInsertionPointsByContent;
+
 // *********************************************************************/
 // The XBLPrototypeBinding class
 
 // Instances of this class are owned by the nsXBLDocumentInfo object returned
 // by XBLDocumentInfo().  Consumers who want to refcount things should refcount
 // that.
 class nsXBLPrototypeBinding
 {
 public:
   already_AddRefed<nsIContent> GetBindingElement();
   void SetBindingElement(nsIContent* aElement);
 
   nsIURI* BindingURI() const { return mBindingURI; }
   nsIURI* AlternateBindingURI() const { return mAlternateBindingURI; }
   nsIURI* DocURI() const { return mXBLDocInfoWeak->DocumentURI(); }
+  nsIURI* GetBaseBindingURI() const { return mBaseBindingURI; }
 
   // Checks if aURI refers to this binding by comparing to both possible
   // binding URIs.
   bool CompareBindingURI(nsIURI* aURI) const;
 
   bool GetAllowScripts();
 
   nsresult BindingAttached(nsIContent* aBoundElement);
@@ -138,19 +168,16 @@ public:
                         nsIContent* aAnonymousContent, bool aNotify);
 
   void SetBasePrototype(nsXBLPrototypeBinding* aBinding);
   nsXBLPrototypeBinding* GetBasePrototype() { return mBaseBinding; }
 
   nsXBLDocumentInfo* XBLDocumentInfo() const { return mXBLDocInfoWeak; }
   bool IsChrome() { return mXBLDocInfoWeak->IsChrome(); }
   
-  bool HasBasePrototype() { return mHasBaseProto; }
-  void SetHasBasePrototype(bool aHasBase) { mHasBaseProto = aHasBase; }
-
   void SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent);
 
   nsIStyleRuleProcessor* GetRuleProcessor();
   nsXBLPrototypeResources::sheet_array_type* GetStyleSheets();
 
   bool HasInsertionPoints() { return mInsertionPointTable != nsnull; }
   
   bool HasStyleSheets() {
@@ -176,26 +203,106 @@ public:
   void SetBaseTag(PRInt32 aNamespaceID, nsIAtom* aTag);
 
   bool ImplementsInterface(REFNSIID aIID) const;
 
   nsresult AddResourceListener(nsIContent* aBoundElement);
 
   void Initialize();
 
+  nsresult ResolveBaseBinding();
+
   const nsCOMArray<nsXBLKeyEventHandler>* GetKeyEventHandlers()
   {
     if (!mKeyHandlersRegistered) {
       CreateKeyHandlers();
       mKeyHandlersRegistered = true;
     }
 
     return &mKeyHandlers;
   }
 
+  /**
+   * Read this binding from the stream aStream into the xbl document aDocument.
+   * aDocInfo should be the xbl document info for the binding document.
+   * aFlags can contain XBLBinding_Serialize_InheritStyle to indicate that
+   * mInheritStyle flag should be set, and XBLBinding_Serialize_IsFirstBinding
+   * to indicate the first binding in a document.
+   */
+  nsresult Read(nsIObjectInputStream* aStream,
+                nsXBLDocumentInfo* aDocInfo,
+                nsIDocument* aDocument,
+                PRUint8 aFlags);
+
+  /**
+   * Write this binding to the stream.
+   */
+  nsresult Write(nsIObjectOutputStream* aStream);
+
+  /**
+   * Read a content node from aStream and return it in aChild.
+   * aDocument and aNim are the document and node info manager for the document
+   * the child will be inserted into.
+   */
+  nsresult ReadContentNode(nsIObjectInputStream* aStream,
+                           nsIDocument* aDocument,
+                           nsNodeInfoManager* aNim,
+                           nsIContent** aChild);
+
+  /**
+   * Write the content node aNode to aStream.
+   * aInsertionPointsByContent is a hash of the insertion points in the binding,
+   * keyed by where there are in the content hierarchy.
+   *
+   * This method is called recursively for each child descendant. For the topmost
+   * call, aNode must be an element.
+   *
+   * Text, CDATA and comment nodes are serialized as:
+   *   the constant XBLBinding_Serialize_TextNode, XBLBinding_Serialize_CDATANode
+   *     or XBLBinding_Serialize_CommentNode
+   *   the text for the node
+   * Elements are serialized in the following format:
+   *   node's namespace, written with WriteNamespace
+   *   node's namespace prefix
+   *   node's tag
+   *   32-bit attribute count
+   *   table of attributes:
+   *     attribute's namespace, written with WriteNamespace
+   *     attribute's namespace prefix
+   *     attribute's tag
+   *     attribute's value
+   *   attribute forwarding table:
+   *     source namespace
+   *     source attribute
+   *     destination namespace
+   *     destination attribute
+   *   the constant XBLBinding_Serialize_NoMoreAttributes
+   *   insertion points within this node:
+   *     child index to insert within node
+   *     default content serialized in the same manner or XBLBinding_Serialize_NoContent
+   *     count of insertion points at that index
+   *       that number of string tags (those in the <children>'s includes attribute)
+   *   the constant XBLBinding_Serialize_NoMoreInsertionPoints
+   *   32-bit count of the number of child nodes
+   *     each child node is serialized in the same manner in sequence
+   *   the constant XBLBinding_Serialize_NoContent
+   */
+  nsresult WriteContentNode(nsIObjectOutputStream* aStream,
+                            nsIContent* aNode,
+                            ArrayOfInsertionPointsByContent& aInsertionPointsByContent);
+
+  /**
+   * Read or write a namespace id from or to aStream. If the namespace matches
+   * one of the built-in ones defined in nsINameSpaceManager.h, it will be written as
+   * a single byte with that value. Otherwise, XBLBinding_Serialize_CustomNamespace is
+   * written out, followed by a string written with writeWStringZ.
+   */
+  nsresult ReadNamespace(nsIObjectInputStream* aStream, PRInt32& aNameSpaceID);
+  nsresult WriteNamespace(nsIObjectOutputStream* aStream, PRInt32 aNameSpaceID);
+
 public:
   nsXBLPrototypeBinding();
   ~nsXBLPrototypeBinding();
 
   // Init must be called after construction to initialize the prototype
   // binding.  It may well throw errors (eg on out-of-memory).  Do not confuse
   // this with the Initialize() method, which must be called after the
   // binding's handlers, properties, etc are all set.
@@ -223,61 +330,46 @@ public:
    * has the localname given by aTag and is in the XBL namespace.
    */
   nsIContent* GetImmediateChild(nsIAtom* aTag);
   nsIContent* LocateInstance(nsIContent* aBoundElt,
                              nsIContent* aTemplRoot,
                              nsIContent* aCopyRoot,
                              nsIContent* aTemplChild);
 
-protected:  
+protected:
+  // Ensure that mAttributeTable has been created.
+  void EnsureAttributeTable();
+  // Ad an entry to the attribute table
+  void AddToAttributeTable(PRInt32 aSourceNamespaceID, nsIAtom* aSourceTag,
+                           PRInt32 aDestNamespaceID, nsIAtom* aDestTag,
+                           nsIContent* aContent);
   void ConstructAttributeTable(nsIContent* aElement);
   void ConstructInsertionTable(nsIContent* aElement);
   void GetNestedChildren(nsIAtom* aTag, PRInt32 aNamespace,
                          nsIContent* aContent,
                          nsCOMArray<nsIContent> & aList);
   void CreateKeyHandlers();
 
-protected:
-  // Internal helper class for managing our IID table.
-  class nsIIDKey : public nsHashKey {
-    protected:
-      nsIID mKey;
-  
-    public:
-      nsIIDKey(REFNSIID key) : mKey(key) {}
-      ~nsIIDKey(void) {}
-
-      PRUint32 HashCode(void) const {
-        // Just use the 32-bit m0 field.
-        return mKey.m0;
-      }
-
-      bool Equals(const nsHashKey *aKey) const {
-        return mKey.Equals( ((nsIIDKey*) aKey)->mKey);
-      }
-
-      nsHashKey *Clone(void) const {
-        return new nsIIDKey(mKey);
-      }
-  };
-
 // MEMBER VARIABLES
 protected:
   nsCOMPtr<nsIURI> mBindingURI;
   nsCOMPtr<nsIURI> mAlternateBindingURI; // Alternate id-less URI that is only non-null on the first binding.
   nsCOMPtr<nsIContent> mBinding; // Strong. We own a ref to our content element in the binding doc.
   nsAutoPtr<nsXBLPrototypeHandler> mPrototypeHandler; // Strong. DocInfo owns us, and we own the handlers.
-  
+
+  // the url of the base binding
+  nsCOMPtr<nsIURI> mBaseBindingURI;
+
   nsXBLProtoImpl* mImplementation; // Our prototype implementation (includes methods, properties, fields,
                                    // the constructor, and the destructor).
 
   nsXBLPrototypeBinding* mBaseBinding; // Weak.  The docinfo will own our base binding.
   bool mInheritStyle;
-  bool mHasBaseProto;
+  bool mCheckedBaseProto;
   bool mKeyHandlersRegistered;
  
   nsXBLPrototypeResources* mResources; // If we have any resources, this will be non-null.
                                       
   nsXBLDocumentInfo* mXBLDocInfoWeak; // A pointer back to our doc info.  Weak, since it owns us.
 
   nsObjectHashtable* mAttributeTable; // A table for attribute containers. Namespace IDs are used as
                                       // keys in the table. Containers are nsObjectHashtables.
@@ -290,9 +382,8 @@ protected:
 
   PRInt32 mBaseNameSpaceID;    // If we extend a tagname/namespace, then that information will
   nsCOMPtr<nsIAtom> mBaseTag;  // be stored in here.
 
   nsCOMArray<nsXBLKeyEventHandler> mKeyHandlers;
 };
 
 #endif
-
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -74,16 +74,17 @@
 #include "nsGUIEvent.h"
 #include "nsIXPConnect.h"
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsDOMCID.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 #include "nsXBLEventHandler.h"
+#include "nsXBLSerialize.h"
 #include "nsEventDispatcher.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
                      NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
@@ -118,41 +119,44 @@ nsXBLPrototypeHandler::nsXBLPrototypeHan
                                              const PRUnichar* aAllowUntrusted,
                                              nsXBLPrototypeBinding* aBinding,
                                              PRUint32 aLineNumber)
   : mHandlerText(nsnull),
     mLineNumber(aLineNumber),
     mNextHandler(nsnull),
     mPrototypeBinding(aBinding)
 {
-  ++gRefCnt;
-  if (gRefCnt == 1)
-    // Get the primary accelerator key.
-    InitAccessKeys();
+  Init();
 
   ConstructPrototype(nsnull, aEvent, aPhase, aAction, aCommand, aKeyCode,
                      aCharCode, aModifiers, aButton, aClickCount,
                      aGroup, aPreventDefault, aAllowUntrusted);
 }
 
 nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement)
   : mHandlerElement(nsnull),
     mLineNumber(0),
     mNextHandler(nsnull),
     mPrototypeBinding(nsnull)
 {
-  ++gRefCnt;
-  if (gRefCnt == 1)
-    // Get the primary accelerator key.
-    InitAccessKeys();
+  Init();
 
   // Make sure our prototype is initialized.
   ConstructPrototype(aHandlerElement);
 }
 
+nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding)
+  : mHandlerText(nsnull),
+    mLineNumber(nsnull),
+    mNextHandler(nsnull),
+    mPrototypeBinding(aBinding)
+{
+  Init();
+}
+
 nsXBLPrototypeHandler::~nsXBLPrototypeHandler()
 {
   --gRefCnt;
   if (mType & NS_HANDLER_TYPE_XUL) {
     NS_IF_RELEASE(mHandlerElement);
   } else if (mHandlerText) {
     nsMemory::Free(mHandlerText);
   }
@@ -1025,8 +1029,72 @@ nsXBLPrototypeHandler::ModifiersMatchMas
   if (mKeyMask & cControlMask) {
     key ? key->GetCtrlKey(&keyPresent) : mouse->GetCtrlKey(&keyPresent);
     if (keyPresent != ((mKeyMask & cControl) != 0))
       return false;
   }
 
   return true;
 }
+
+nsresult
+nsXBLPrototypeHandler::Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream)
+{
+  nsresult rv = aStream->Read8(&mPhase);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->Read8(&mKeyMask);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->Read8(&mType);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->Read8(&mMisc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 detail; 
+  rv = aStream->Read32(&detail);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mDetail = detail;
+
+  nsAutoString name;
+  rv = aStream->ReadString(name);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mEventName = do_GetAtom(name);
+
+  rv = aStream->Read32(&mLineNumber);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString handlerText;
+  rv = aStream->ReadString(handlerText);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!handlerText.IsEmpty())
+    mHandlerText = ToNewUnicode(handlerText);
+
+  return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeHandler::Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream)
+{
+  // Make sure we don't write out NS_HANDLER_TYPE_XUL types, as they are used
+  // for <keyset> elements.
+  if (mType & NS_HANDLER_TYPE_XUL)
+    return NS_OK;
+
+  XBLBindingSerializeDetails type = XBLBinding_Serialize_Handler;
+
+  nsresult rv = aStream->Write8(type);
+  rv = aStream->Write8(mPhase);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->Write8(mKeyMask);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->Write8(mType);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->Write8(mMisc);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aStream->Write32(mDetail);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aStream->WriteWStringZ(nsDependentAtomString(mEventName).get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aStream->Write32(mLineNumber);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return aStream->WriteWStringZ(mHandlerText ? mHandlerText : EmptyString().get());
+}
--- a/content/xbl/src/nsXBLPrototypeHandler.h
+++ b/content/xbl/src/nsXBLPrototypeHandler.h
@@ -83,16 +83,19 @@ public:
                         const PRUnichar* aPreventDefault,
                         const PRUnichar* aAllowUntrusted,
                         nsXBLPrototypeBinding* aBinding,
                         PRUint32 aLineNumber);
 
   // This constructor is used only by XUL key handlers (e.g., <key>)
   nsXBLPrototypeHandler(nsIContent* aKeyElement);
 
+  // This constructor is used for handlers loaded from the cache
+  nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding);
+
   ~nsXBLPrototypeHandler();
 
   // if aCharCode is not zero, it is used instead of the charCode of aKeyEvent.
   bool KeyEventMatched(nsIDOMKeyEvent* aKeyEvent,
                          PRUint32 aCharCode = 0,
                          bool aIgnoreShiftKey = false);
   inline bool KeyEventMatched(nsIAtom* aEventType,
                                 nsIDOMKeyEvent* aEvent,
@@ -152,20 +155,30 @@ public:
 
   // This returns a valid value only if HasAllowUntrustedEventsAttr returns
   // true.
   bool AllowUntrustedEvents()
   {
     return (mType & NS_HANDLER_ALLOW_UNTRUSTED) != 0;
   }
 
+  nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream);
+  nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream);
+
 public:
   static PRUint32 gRefCnt;
   
 protected:
+  void Init() {
+    ++gRefCnt;
+    if (gRefCnt == 1)
+      // Get the primary accelerator key.
+      InitAccessKeys();
+  }
+
   already_AddRefed<nsIController> GetController(nsIDOMEventTarget* aTarget);
   
   inline PRInt32 GetMatchingKeyCode(const nsAString& aKeyName);
   void ConstructPrototype(nsIContent* aKeyElement, 
                           const PRUnichar* aEvent=nsnull, const PRUnichar* aPhase=nsnull,
                           const PRUnichar* aAction=nsnull, const PRUnichar* aCommand=nsnull,
                           const PRUnichar* aKeyCode=nsnull, const PRUnichar* aCharCode=nsnull,
                           const PRUnichar* aModifiers=nsnull, const PRUnichar* aButton=nsnull,
--- a/content/xbl/src/nsXBLPrototypeResources.cpp
+++ b/content/xbl/src/nsXBLPrototypeResources.cpp
@@ -134,8 +134,16 @@ nsXBLPrototypeResources::FlushSkinSheets
 
     mStyleSheetList.AppendElement(newSheet);
   }
   mRuleProcessor = new nsCSSRuleProcessor(mStyleSheetList, 
                                           nsStyleSet::eDocSheet);
 
   return NS_OK;
 }
+
+nsresult
+nsXBLPrototypeResources::Write(nsIObjectOutputStream* aStream)
+{
+  if (mLoader)
+    return mLoader->Write(aStream);
+  return NS_OK;
+}
--- a/content/xbl/src/nsXBLPrototypeResources.h
+++ b/content/xbl/src/nsXBLPrototypeResources.h
@@ -58,16 +58,18 @@ class nsCSSStyleSheet;
 class nsXBLPrototypeResources
 {
 public:
   void LoadResources(bool* aResult);
   void AddResource(nsIAtom* aResourceType, const nsAString& aSrc);
   void AddResourceListener(nsIContent* aElement);
   nsresult FlushSkinSheets();
 
+  nsresult Write(nsIObjectOutputStream* aStream);
+
   nsXBLPrototypeResources(nsXBLPrototypeBinding* aBinding);
   ~nsXBLPrototypeResources();
 
 // MEMBER VARIABLES
   nsXBLResourceLoader* mLoader; // A loader object. Exists only long enough to load resources, and then it dies.
   typedef nsTArray<nsRefPtr<nsCSSStyleSheet> > sheet_array_type;
   sheet_array_type mStyleSheetList; // A list of loaded stylesheets for this binding.
 
--- a/content/xbl/src/nsXBLResourceLoader.cpp
+++ b/content/xbl/src/nsXBLResourceLoader.cpp
@@ -285,8 +285,30 @@ nsXBLResourceLoader::NotifyBoundElements
   }
 
   // Clear out the whole array.
   mBoundElements.Clear();
 
   // Delete ourselves.
   NS_RELEASE(mResources->mLoader);
 }
+
+nsresult
+nsXBLResourceLoader::Write(nsIObjectOutputStream* aStream)
+{
+  nsresult rv;
+
+  for (nsXBLResource* curr = mResourceList; curr; curr = curr->mNext) {
+    if (curr->mType == nsGkAtoms::image)
+      rv = aStream->Write8(XBLBinding_Serialize_Image);
+    else if (curr->mType == nsGkAtoms::stylesheet)
+      rv = aStream->Write8(XBLBinding_Serialize_Stylesheet);
+    else
+      continue;
+
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = aStream->WriteWStringZ(curr->mSrc.get());
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
--- a/content/xbl/src/nsXBLResourceLoader.h
+++ b/content/xbl/src/nsXBLResourceLoader.h
@@ -86,16 +86,18 @@ public:
   void AddResourceListener(nsIContent* aElement);
 
   nsXBLResourceLoader(nsXBLPrototypeBinding* aBinding,
                       nsXBLPrototypeResources* aResources);
   virtual ~nsXBLResourceLoader();
 
   void NotifyBoundElements();
 
+  nsresult Write(nsIObjectOutputStream* aStream);
+
 // MEMBER VARIABLES
   nsXBLPrototypeBinding* mBinding; // A pointer back to our binding.
   nsXBLPrototypeResources* mResources; // A pointer back to our resources
                                        // information.  May be null if the
                                        // resources have already been
                                        // destroyed.
   
   nsXBLResource* mResourceList; // The list of resources we need to load.
new file mode 100644
--- /dev/null
+++ b/content/xbl/src/nsXBLSerialize.cpp
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsXBLSerialize.h"
+#include "nsDOMScriptObjectHolder.h"
+#include "nsContentUtils.h"
+#include "jsxdrapi.h"
+
+nsresult
+XBL_SerializeFunction(nsIScriptContext* aContext,
+                      nsIObjectOutputStream* aStream,
+                      JSObject* aFunctionObject)
+{
+  nsresult rv;
+
+  JSContext* cx = (JSContext*) aContext->GetNativeContext();
+  JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
+  if (!xdr)
+    return NS_ERROR_OUT_OF_MEMORY;
+  xdr->userdata = (void*) aStream;
+
+  jsval funval = OBJECT_TO_JSVAL(aFunctionObject);
+
+  JSAutoRequest ar(cx);
+  if (! ::JS_XDRFunctionObject(xdr, &aFunctionObject)) {
+    rv = NS_ERROR_FAILURE;
+  } else {
+    uint32 size;
+    const char* data = reinterpret_cast<const char*>
+                                         (::JS_XDRMemGetData(xdr, &size));
+    NS_ASSERTION(data, "no decoded JSXDRState data!");
+
+    rv = aStream->Write32(size);
+    if (NS_SUCCEEDED(rv))
+      rv = aStream->WriteBytes(data, size);
+  }
+
+  ::JS_XDRDestroy(xdr);
+
+  return rv;
+}
+
+// static
+nsresult
+XBL_DeserializeFunction(nsIScriptContext* aContext,
+                        nsIObjectInputStream* aStream,
+                        void* aHolder,
+                        void **aScriptObject)
+{
+  *aScriptObject = nsnull;
+
+  JSObject* functionObject = nsnull;
+
+  PRUint32 size;
+  nsresult rv = aStream->Read32(&size);
+  if (NS_FAILED(rv))
+    return rv;
+
+  char* data;
+  rv = aStream->ReadBytes(size, &data);
+  if (NS_FAILED(rv))
+    return rv;
+
+  JSContext* cx = (JSContext*) aContext->GetNativeContext();
+  JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
+  if (!xdr) {
+    rv = NS_ERROR_OUT_OF_MEMORY;
+  } else {
+    xdr->userdata = (void*) aStream;
+    JSAutoRequest ar(cx);
+    ::JS_XDRMemSetData(xdr, data, size);
+
+    if (! ::JS_XDRFunctionObject(xdr, &functionObject)) {
+      rv = NS_ERROR_FAILURE;
+    }
+
+    uint32 junk;
+    data = (char*) ::JS_XDRMemGetData(xdr, &junk);
+    ::JS_XDRMemSetData(xdr, NULL, 0);
+    ::JS_XDRDestroy(xdr);
+  }
+
+  nsMemory::Free(data);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aScriptObject = functionObject;
+
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/content/xbl/src/nsXBLSerialize.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *   Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsXBLSerialize_h__
+#define nsXBLSerialize_h__
+
+#include "jsapi.h"
+
+#include "nsIScriptContext.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsINameSpaceManager.h"
+
+typedef PRUint8 XBLBindingSerializeDetails;
+
+// A version number to ensure we don't load cached data in a different
+// file format.
+#define XBLBinding_Serialize_Version 0x00000001
+
+// Set for the first binding in a document
+#define XBLBinding_Serialize_IsFirstBinding 1
+
+// Set to indicate that nsXBLPrototypeBinding::mInheritStyle should be true
+#define XBLBinding_Serialize_InheritStyle 2
+
+// Appears at the end of the serialized data to indicate that no more bindings
+// are present for this document.
+#define XBLBinding_Serialize_NoMoreBindings 0x80
+
+// Implementation member types. The serialized value for each member contains one
+// of these values, combined with the read-only flag XBLBinding_Serialize_ReadOnly.
+// Use XBLBinding_Serialize_Mask to filter out the read-only flag and check for
+// just the member type.
+#define XBLBinding_Serialize_NoMoreItems 0 // appears at the end of the members list
+#define XBLBinding_Serialize_Field 1
+#define XBLBinding_Serialize_GetterProperty 2
+#define XBLBinding_Serialize_SetterProperty 3
+#define XBLBinding_Serialize_GetterSetterProperty 4
+#define XBLBinding_Serialize_Method 5
+#define XBLBinding_Serialize_Constructor 6
+#define XBLBinding_Serialize_Destructor 7
+#define XBLBinding_Serialize_Handler 8
+#define XBLBinding_Serialize_Image 9
+#define XBLBinding_Serialize_Stylesheet 10
+#define XBLBinding_Serialize_Mask 0x0F
+#define XBLBinding_Serialize_ReadOnly 0x80
+
+// Appears at the end of the list of insertion points to indicate that there
+// are no more. 
+#define XBLBinding_Serialize_NoMoreInsertionPoints 0xFFFFFFFF
+
+// When serializing content nodes, a single-byte namespace id is written out
+// first. The special values below can appear in place of a namespace id.
+
+// Indicates that this is not one of the built-in namespaces defined in 
+// nsINameSpaceManager.h. The string form will be serialized immediately
+// following.
+#define XBLBinding_Serialize_CustomNamespace 0xFE
+
+// Flags to indicate a non-element node. Otherwise, it is an element. 
+#define XBLBinding_Serialize_TextNode 0xFB
+#define XBLBinding_Serialize_CDATANode 0xFC
+#define XBLBinding_Serialize_CommentNode 0xFD
+
+// Indicates that there is no content to serialize/deserialize
+#define XBLBinding_Serialize_NoContent 0xFF
+
+// Appears at the end of the forwarded attributes list to indicate that there
+// are no more attributes.
+#define XBLBinding_Serialize_NoMoreAttributes 0xFF
+
+PR_STATIC_ASSERT(XBLBinding_Serialize_CustomNamespace >= kNameSpaceID_LastBuiltin);
+
+nsresult
+XBL_SerializeFunction(nsIScriptContext* aContext,
+                      nsIObjectOutputStream* aStream,
+                      JSObject* aFunctionObject);
+
+nsresult
+XBL_DeserializeFunction(nsIScriptContext* aContext,
+                        nsIObjectInputStream* aStream,
+                        void* aHolder,
+                        void **aScriptObject);
+
+#endif // nsXBLSerialize_h__
--- a/content/xbl/src/nsXBLService.cpp
+++ b/content/xbl/src/nsXBLService.cpp
@@ -80,38 +80,29 @@
 #include "nsContentErrors.h"
 
 #include "nsIPresShell.h"
 #include "nsIDocumentObserver.h"
 #include "nsFrameManager.h"
 #include "nsStyleContext.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptError.h"
+#include "nsXBLSerialize.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
 #include "nsIDOMEventListener.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 
 #define NS_MAX_XBL_BINDING_RECURSION 20
 
-static bool IsChromeOrResourceURI(nsIURI* aURI)
-{
-  bool isChrome = false;
-  bool isResource = false;
-  if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && 
-      NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
-      return (isChrome || isResource);
-  return false;
-}
-
 static bool
 IsAncestorBinding(nsIDocument* aDocument,
                   nsIURI* aChildBindingURI,
                   nsIContent* aChild)
 {
   NS_ASSERTION(aDocument, "expected a document");
   NS_ASSERTION(aChildBindingURI, "expected a binding URI");
   NS_ASSERTION(aChild, "expected a child content");
@@ -144,41 +135,16 @@ IsAncestorBinding(nsIDocument* aDocument
                                       "XBL", aDocument);
       return true;
     }
   }
 
   return false;
 }
 
-bool CheckTagNameWhiteList(PRInt32 aNameSpaceID, nsIAtom *aTagName)
-{
-  static nsIContent::AttrValuesArray kValidXULTagNames[] =  {
-    &nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser,
-    &nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu,
-    &nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup,
-    &nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer,
-    &nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nsnull};
-
-  PRUint32 i;
-  if (aNameSpaceID == kNameSpaceID_XUL) {
-    for (i = 0; kValidXULTagNames[i]; ++i) {
-      if (aTagName == *(kValidXULTagNames[i])) {
-        return true;
-      }
-    }
-  }
-  else if (aNameSpaceID == kNameSpaceID_SVG &&
-           aTagName == nsGkAtoms::generic) {
-    return true;
-  }
-
-  return false;
-}
-
 // Individual binding requests.
 class nsXBLBindingRequest
 {
 public:
   nsCOMPtr<nsIURI> mBindingURI;
   nsCOMPtr<nsIContent> mBoundElement;
 
   static nsXBLBindingRequest*
@@ -445,31 +411,31 @@ nsXBLStreamListener::HandleEvent(nsIDOME
     }
 
     // Put our doc info in the doc table.
     nsBindingManager *xblDocBindingManager = bindingDocument->BindingManager();
     nsRefPtr<nsXBLDocumentInfo> info =
       xblDocBindingManager->GetXBLDocumentInfo(documentURI);
     xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
     if (!info) {
-      if (IsChromeOrResourceURI(documentURI)) {
+      if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
         NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
       }
       nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
                                       "MalformedXBL",
                                       nsnull, 0, documentURI,
                                       EmptyString(), 0, 0,
                                       nsIScriptError::warningFlag,
                                       "XBL");
       return NS_ERROR_FAILURE;
     }
 
     // If the doc is a chrome URI, then we put it into the XUL cache.
 #ifdef MOZ_XUL
-    if (IsChromeOrResourceURI(documentURI)) {
+    if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
       nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
       if (cache && cache->IsEnabled())
         cache->PutXBLDocumentInfo(info);
     }
 #endif
   
     bindingManager->PutXBLDocumentInfo(info);
 
@@ -528,16 +494,29 @@ nsXBLService::~nsXBLService(void)
 
     // At this point, the only hash table entries should be for referenced
     // XBL class structs held by unfinalized JS binding objects.
     delete gClassTable;
     gClassTable = nsnull;
   }
 }
 
+// static
+bool
+nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
+{
+  bool isChrome = false;
+  bool isResource = false;
+  if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && 
+      NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
+      return (isChrome || isResource);
+  return false;
+}
+
+
 // This function loads a particular XBL file and installs all of the bindings
 // onto the element.
 NS_IMETHODIMP
 nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
                            nsIPrincipal* aOriginPrincipal, bool aAugmentFlag,
                            nsXBLBinding** aBinding, bool* aResolveStyle) 
 {
   NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
@@ -857,181 +836,98 @@ nsXBLService::GetBinding(nsIContent* aBo
   nsresult rv = LoadBindingDocumentInfo(aBoundElement, boundDocument, aURI,
                                         aOriginPrincipal,
                                         false, getter_AddRefs(docInfo));
   NS_ENSURE_SUCCESS(rv, rv);
   
   if (!docInfo)
     return NS_ERROR_FAILURE;
 
-  // Get our doc info and determine our script access.
-  nsCOMPtr<nsIDocument> doc = docInfo->GetDocument();
-
   nsXBLPrototypeBinding* protoBinding = docInfo->GetPrototypeBinding(ref);
 
   NS_WARN_IF_FALSE(protoBinding, "Unable to locate an XBL binding");
   if (!protoBinding)
     return NS_ERROR_FAILURE;
 
   NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(protoBinding->BindingURI()),
                  NS_ERROR_OUT_OF_MEMORY);
   nsCOMPtr<nsIURI> altBindingURI = protoBinding->AlternateBindingURI();
   if (altBindingURI) {
     NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(altBindingURI),
                    NS_ERROR_OUT_OF_MEMORY);
   }
 
-  nsCOMPtr<nsIContent> child = protoBinding->GetBindingElement();
-
   // Our prototype binding must have all its resources loaded.
   bool ready = protoBinding->LoadResources();
   if (!ready) {
     // Add our bound element to the protos list of elts that should
     // be notified when the stylesheets and scripts finish loading.
     protoBinding->AddResourceListener(aBoundElement);
     return NS_ERROR_FAILURE; // The binding isn't ready yet.
   }
 
-  // If our prototype already has a base, then don't check for an "extends" attribute.
-  nsRefPtr<nsXBLBinding> baseBinding;
-  bool hasBase = protoBinding->HasBasePrototype();
+  rv = protoBinding->ResolveBaseBinding();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsIURI* baseBindingURI;
   nsXBLPrototypeBinding* baseProto = protoBinding->GetBasePrototype();
   if (baseProto) {
-    // Use the NodePrincipal() of the <binding> element in question
-    // for the security check.
-    rv = GetBinding(aBoundElement, baseProto->BindingURI(), aPeekOnly,
+    baseBindingURI = baseProto->BindingURI();
+  }
+  else {
+    baseBindingURI = protoBinding->GetBaseBindingURI();
+    if (baseBindingURI) {
+      PRUint32 count = aDontExtendURIs.Length();
+      for (PRUint32 index = 0; index < count; ++index) {
+        bool equal;
+        rv = aDontExtendURIs[index]->Equals(baseBindingURI, &equal);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (equal) {
+          nsCAutoString spec, basespec;
+          protoBinding->BindingURI()->GetSpec(spec);
+          NS_ConvertUTF8toUTF16 protoSpec(spec);
+          baseBindingURI->GetSpec(basespec);
+          NS_ConvertUTF8toUTF16 baseSpecUTF16(basespec);
+          const PRUnichar* params[] = { protoSpec.get(), baseSpecUTF16.get() };
+          nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
+                                          "CircularExtendsBinding",
+                                          params, NS_ARRAY_LENGTH(params),
+                                          boundDocument->GetDocumentURI(),
+                                          EmptyString(), 0, 0,
+                                          nsIScriptError::warningFlag,
+                                          "XBL");
+          return NS_ERROR_ILLEGAL_VALUE;
+        }
+      }
+    }
+  }
+
+  nsRefPtr<nsXBLBinding> baseBinding;
+  if (baseBindingURI) {
+    nsCOMPtr<nsIContent> child = protoBinding->GetBindingElement();
+    rv = GetBinding(aBoundElement, baseBindingURI, aPeekOnly,
                     child->NodePrincipal(), aIsReady,
                     getter_AddRefs(baseBinding), aDontExtendURIs);
     if (NS_FAILED(rv))
       return rv; // We aren't ready yet.
   }
-  else if (hasBase) {
-    // Check for the presence of 'extends' and 'display' attributes
-    nsAutoString display, extends;
-    child->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
-    child->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends);
-    bool hasDisplay = !display.IsEmpty();
-    bool hasExtends = !extends.IsEmpty();
-    
-    nsAutoString value(extends);
-         
-    if (!hasExtends) 
-      protoBinding->SetHasBasePrototype(false);
-    else {
-      // Now slice 'em up to see what we've got.
-      nsAutoString prefix;
-      PRInt32 offset;
-      if (hasDisplay) {
-        offset = display.FindChar(':');
-        if (-1 != offset) {
-          display.Left(prefix, offset);
-          display.Cut(0, offset+1);
-        }
-      }
-      else if (hasExtends) {
-        offset = extends.FindChar(':');
-        if (-1 != offset) {
-          extends.Left(prefix, offset);
-          extends.Cut(0, offset+1);
-          display = extends;
-        }
-      }
-
-      nsAutoString nameSpace;
-
-      if (!prefix.IsEmpty()) {
-        child->LookupNamespaceURI(prefix, nameSpace);
-
-        if (!nameSpace.IsEmpty()) {
-          if (!hasDisplay) {
-            // We extend some widget/frame. We don't really have a
-            // base binding.
-            protoBinding->SetHasBasePrototype(false);
-            //child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
-          }
-
-          PRInt32 nameSpaceID =
-            nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
-
-          nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
-          // Check the white list
-          if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
-            const PRUnichar* params[] = { display.get() };
-            nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
-                                            "InvalidExtendsBinding",
-                                            params, ArrayLength(params),
-                                            nsnull,
-                                            EmptyString(), 0, 0,
-                                            nsIScriptError::errorFlag,
-                                            "XBL", doc);
-            NS_ASSERTION(!IsChromeOrResourceURI(aURI),
-                         "Invalid extends value");
-            return NS_ERROR_ILLEGAL_VALUE;
-          }
-
-          protoBinding->SetBaseTag(nameSpaceID, tagName);
-        }
-      }
-
-      if (hasExtends && (hasDisplay || nameSpace.IsEmpty())) {
-        // Look up the prefix.
-        // We have a base class binding. Load it right now.
-        nsCOMPtr<nsIURI> bindingURI;
-        rv = NS_NewURI(getter_AddRefs(bindingURI), value,
-                       doc->GetDocumentCharacterSet().get(),
-                       doc->GetDocBaseURI());
-        NS_ENSURE_SUCCESS(rv, rv);
-        
-        PRUint32 count = aDontExtendURIs.Length();
-        for (PRUint32 index = 0; index < count; ++index) {
-          bool equal;
-          rv = aDontExtendURIs[index]->Equals(bindingURI, &equal);
-          NS_ENSURE_SUCCESS(rv, rv);
-          if (equal) {
-            nsCAutoString spec;
-            protoBinding->BindingURI()->GetSpec(spec);
-            NS_ConvertUTF8toUTF16 protoSpec(spec);
-            const PRUnichar* params[] = { protoSpec.get(), value.get() };
-            nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
-                                            "CircularExtendsBinding",
-                                            params, ArrayLength(params),
-                                            nsnull,
-                                            EmptyString(), 0, 0,
-                                            nsIScriptError::warningFlag,
-                                            "XBL", boundDocument);
-            return NS_ERROR_ILLEGAL_VALUE;
-          }
-        }
-
-        // Use the NodePrincipal() of the <binding> element in question
-        // for the security check.
-        rv = GetBinding(aBoundElement, bindingURI, aPeekOnly,
-                        child->NodePrincipal(), aIsReady,
-                        getter_AddRefs(baseBinding), aDontExtendURIs);
-        if (NS_FAILED(rv))
-          return rv; // Binding not yet ready or an error occurred.
-        if (!aPeekOnly) {
-          // Make sure to set the base prototype.
-          baseProto = baseBinding->PrototypeBinding();
-          protoBinding->SetBasePrototype(baseProto);
-          child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
-          child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false);
-        }
-      }
-    }
-  }
 
   *aIsReady = true;
+
   if (!aPeekOnly) {
     // Make a new binding
     nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);
     NS_ENSURE_TRUE(newBinding, NS_ERROR_OUT_OF_MEMORY);
 
-    if (baseBinding)
-      newBinding->SetBaseBinding(baseBinding);
+    if (baseBinding) {
+      if (!baseProto) {
+        protoBinding->SetBasePrototype(baseBinding->PrototypeBinding());
+      }
+       newBinding->SetBaseBinding(baseBinding);
+    }
 
     NS_ADDREF(*aResult = newBinding);
   }
 
   return NS_OK;
 }
 
 static bool SchemeIs(nsIURI* aURI, const char* aScheme)
@@ -1156,43 +1052,62 @@ nsXBLService::LoadBindingDocumentInfo(ns
         // Create a new load observer.
         if (!xblListener->HasRequest(aBindingURI, aBoundElement)) {
           nsXBLBindingRequest* req = nsXBLBindingRequest::Create(mPool, aBindingURI, aBoundElement);
           xblListener->AddRequest(req);
         }
         return NS_OK;
       }
     }
-     
+
+#ifdef MOZ_XUL
+    // Next, look in the startup cache
+    bool useStartupCache = useXULCache && IsChromeOrResourceURI(documentURI);
+    if (!info && useStartupCache) {
+      rv = nsXBLDocumentInfo::ReadPrototypeBindings(documentURI, getter_AddRefs(info));
+      if (NS_SUCCEEDED(rv)) {
+        cache->PutXBLDocumentInfo(info);
+
+        if (bindingManager) {
+          // Cache it in our binding manager's document table.
+          bindingManager->PutXBLDocumentInfo(info);
+        }
+      }
+    }
+#endif
+
     if (!info) {
       // Finally, if all lines of defense fail, we go and fetch the binding
       // document.
       
       // Always load chrome synchronously
       bool chrome;
       if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
         aForceSyncLoad = true;
 
       nsCOMPtr<nsIDocument> document;
       FetchBindingDocument(aBoundElement, aBoundDocument, documentURI,
                            aBindingURI, aForceSyncLoad, getter_AddRefs(document));
-   
+
       if (document) {
         nsBindingManager *xblDocBindingManager = document->BindingManager();
         info = xblDocBindingManager->GetXBLDocumentInfo(documentURI);
         if (!info) {
           NS_ERROR("An XBL file is malformed.  Did you forget the XBL namespace on the bindings tag?");
           return NS_ERROR_FAILURE;
         }
         xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
 
         // If the doc is a chrome URI, then we put it into the XUL cache.
 #ifdef MOZ_XUL
-        if (useXULCache && IsChromeOrResourceURI(documentURI)) {
+        if (useStartupCache) {
           cache->PutXBLDocumentInfo(info);
+
+          // now write the bindings into the startup cache
+          info->WritePrototypeBindings();
         }
 #endif
         
         if (bindingManager) {
           // Also put it in our binding manager's document table.
           bindingManager->PutXBLDocumentInfo(info);
         }
       }
--- a/content/xbl/src/nsXBLService.h
+++ b/content/xbl/src/nsXBLService.h
@@ -59,16 +59,18 @@ class nsSupportsHashtable;
 class nsHashtable;
 
 class nsXBLService : public nsIXBLService,
                      public nsIObserver,
                      public nsSupportsWeakReference
 {
   NS_DECL_ISUPPORTS
 
+  static bool IsChromeOrResourceURI(nsIURI* aURI);
+
   // This function loads a particular XBL file and installs all of the bindings
   // onto the element.  aOriginPrincipal must not be null here.
   NS_IMETHOD LoadBindings(nsIContent* aContent, nsIURI* aURL,
                           nsIPrincipal* aOriginPrincipal, bool aAugmentFlag,
                           nsXBLBinding** aBinding, bool* aResolveStyle);
 
   // Indicates whether or not a binding is fully loaded.
   NS_IMETHOD BindingReady(nsIContent* aBoundElement, nsIURI* aURI, bool* aIsReady);
--- a/content/xbl/test/Makefile.in
+++ b/content/xbl/test/Makefile.in
@@ -77,13 +77,13 @@ include $(topsrcdir)/config/rules.mk
 _CHROME_FILES = \
 		test_bug296375.xul \
 		test_bug378518.xul \
 		test_bug398135.xul \
 		test_bug398492.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
 
--- a/content/xml/document/src/nsXMLDocument.cpp
+++ b/content/xml/document/src/nsXMLDocument.cpp
@@ -214,16 +214,37 @@ NS_NewXMLDocument(nsIDocument** aInstanc
     NS_RELEASE(doc);
   }
 
   *aInstancePtrResult = doc;
 
   return rv;
 }
 
+nsresult
+NS_NewXBLDocument(nsIDOMDocument** aInstancePtrResult,
+                  nsIURI* aDocumentURI,
+                  nsIURI* aBaseURI,
+                  nsIPrincipal* aPrincipal)
+{
+  nsresult rv = NS_NewDOMDocument(aInstancePtrResult,
+                                  NS_LITERAL_STRING("http://www.mozilla.org/xbl"),
+                                  NS_LITERAL_STRING("bindings"), nsnull,
+                                  aDocumentURI, aBaseURI, aPrincipal, false,
+                                  nsnull, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDocument> idoc = do_QueryInterface(*aInstancePtrResult);
+  nsDocument* doc = static_cast<nsDocument*>(idoc.get());
+  doc->SetLoadedAsInteractiveData(true);
+  doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
+
+  return NS_OK;
+}
+
   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 
 nsXMLDocument::nsXMLDocument(const char* aContentType)
   : nsDocument(aContentType),
     mAsync(true)
 {
 
--- a/content/xml/document/test/Makefile.in
+++ b/content/xml/document/test/Makefile.in
@@ -53,9 +53,9 @@ include $(topsrcdir)/config/rules.mk
 		test_viewport.xhtml \
 		test_bug293347.html \
 		file_bug293347.xml \
 		file_bug293347xslt.xml \
 		test_bug691215.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/content/xslt/tests/mochitest/Makefile.in
+++ b/content/xslt/tests/mochitest/Makefile.in
@@ -55,9 +55,9 @@ include $(topsrcdir)/config/rules.mk
 		test_bug566629.html \
 		test_bug566629.xhtml \
 		test_bug603159.html \
 		test_bug667315.html \
 		test_exslt_regex.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/content/xul/content/test/Makefile.in
+++ b/content/xul/content/test/Makefile.in
@@ -51,12 +51,12 @@ include $(topsrcdir)/config/rules.mk
 _CHROME_FILES = \
 		test_bug330705-2.xul \
 		test_bug233643.xul \
  		test_bug398289.html \
  		398289-resource.xul \
  		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
--- a/content/xul/document/test/Makefile.in
+++ b/content/xul/document/test/Makefile.in
@@ -59,9 +59,9 @@ include $(topsrcdir)/config/rules.mk
 		test_bug497875.xul \
 		bug497875-iframe.xul \
 		test_bug335375.xul \
 		overlay1_bug335375.xul \
 		overlay2_bug335375.xul \
 		$(NULL)
 
 libs:: $(_CHROME_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
--- a/content/xul/templates/tests/chrome/Makefile.in
+++ b/content/xul/templates/tests/chrome/Makefile.in
@@ -261,9 +261,9 @@ include $(topsrcdir)/config/rules.mk
 		test_tmpl_regenerate.xul \
 		test_bug441785.xul \
 		bug441785-1.rdf \
 		bug441785-2.rdf \
 		test_sortservice.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
--- a/docshell/test/Makefile.in
+++ b/docshell/test/Makefile.in
@@ -127,9 +127,9 @@ include $(topsrcdir)/config/rules.mk
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _TEST_FILES += \
 		test_bug511449.html \
 		file_bug511449.html \
 		$(NULL)
 endif
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
--- a/docshell/test/browser/Makefile.in
+++ b/docshell/test/browser/Makefile.in
@@ -63,9 +63,9 @@ include $(topsrcdir)/config/rules.mk
 		file_bug655270.html \
 		favicon_bug655270.ico \
 		browser_bug670318.js \
 		file_bug670318.html \
 		browser_bug673467.js \
 		$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
--- a/docshell/test/chrome/Makefile.in
+++ b/docshell/test/chrome/Makefile.in
@@ -125,15 +125,15 @@ include $(topsrcdir)/config/rules.mk
 		$(NULL)
 
 _DOCSHELL_SUBHARNESS = \
     docshell_helpers.js \
     generic.html \
     $(NULL)
 
 libs:: $(_HTTP_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
 
 libs:: $(_DOCSHELL_SUBHARNESS)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/chrome/$(relativesrcdir)
--- a/docshell/test/navigation/Makefile.in
+++ b/docshell/test/navigation/Makefile.in
@@ -89,14 +89,14 @@ ifneq (mobile,$(MOZ_BUILD_APP))
 		bug343515_pg3.html \
 		bug343515_pg3_1.html \
 		bug343515_pg3_2.html \
 		bug343515_pg3_1_1.html \
 		$(NULL)
 endif
 
 libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
 ifneq (mobile,$(MOZ_BUILD_APP))
 libs:: $(_BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 endif
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -507,16 +507,17 @@
 
 #include "nsDOMTouchEvent.h"
 #include "nsIDOMCustomEvent.h"
 
 #include "nsWrapperCacheInlines.h"
 #include "dombindings.h"
 
 #include "nsIDOMBatteryManager.h"
+#include "BatteryManager.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 static const char kDOMStringBundleURL[] =
   "chrome://global/locale/dom/dom.properties";
@@ -2285,17 +2286,18 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Navigator, nsIDOMNavigator)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigator)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation)
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMNavigatorDesktopNotification,
                                         Navigator::HasDesktopNotificationSupport())
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorBattery)
+    DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMNavigatorBattery,
+                                        battery::BatteryManager::HasSupport())
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPlugin)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(PluginArray, nsIDOMPluginArray)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPluginArray)
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1912,16 +1912,17 @@ nsJSContext::CallEventHandler(nsISupport
                                                 principal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     jsval funval = OBJECT_TO_JSVAL(funobj);
     JSAutoEnterCompartment ac;
     js::ForceFrame ff(mContext, funobj);
     if (!ac.enter(mContext, funobj) || !ff.enter() ||
         !JS_WrapObject(mContext, &target)) {
+      ReportPendingException();
       sSecurityManager->PopContextPrincipal(mContext);
       return NS_ERROR_FAILURE;
     }
 
     Maybe<nsRootedJSValueArray> tempStorage;
 
     // Use |target| as the scope for wrapping the arguments, since aScope is
     // the safe scope in many cases, which isn't very useful.  Wrapping aTarget
--- a/dom/battery/BatteryManager.cpp
+++ b/dom/battery/BatteryManager.cpp
@@ -35,16 +35,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/Hal.h"
 #include "BatteryManager.h"
 #include "nsIDOMClassInfo.h"
 #include "Constants.h"
 #include "nsDOMEvent.h"
+#include "mozilla/Preferences.h"
 
 /**
  * We have to use macros here because our leak analysis tool things we are
  * leaking strings when we have |static const nsString|. Sad :(
  */
 #define LEVELCHANGE_EVENT_NAME    NS_LITERAL_STRING("levelchange")
 #define CHARGINGCHANGE_EVENT_NAME NS_LITERAL_STRING("chargingchange")
 
@@ -186,11 +187,17 @@ BatteryManager::Notify(const hal::Batter
     DispatchTrustedEventToSelf(CHARGINGCHANGE_EVENT_NAME);
   }
 
   if (previousLevel != mLevel) {
     DispatchTrustedEventToSelf(LEVELCHANGE_EVENT_NAME);
   }
 }
 
+/* static */ bool
+BatteryManager::HasSupport()
+{
+  return Preferences::GetBool("dom.battery.enabled", true);
+}
+
 } // namespace battery
 } // namespace dom
 } // namespace mozilla
--- a/dom/battery/BatteryManager.h
+++ b/dom/battery/BatteryManager.h
@@ -69,16 +69,23 @@ public:
   void Shutdown();
 
   // For IObserver.
   void Notify(const hal::BatteryInformation& aBatteryInfo);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BatteryManager,
                                            nsDOMEventTargetHelper)
 
+  /**
+   * Returns whether the battery api is supported (ie. not disabled by the user)
+   * @return whether the battery api is supported.
+   */
+  static bool HasSupport();
+
+
 private:
   /**
    * Dispatch a trusted non-cancellable and non-bubbling event to itself.
    */
   nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
 
   /**
    * Update the battery information stored in the battery manager object using
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -62,68 +62,79 @@ USING_INDEXEDDB_NAMESPACE
 namespace {
 
 class GetKeyHelper : public AsyncConnectionHelper
 {
 public:
   GetKeyHelper(IDBTransaction* aTransaction,
                IDBRequest* aRequest,
                IDBIndex* aIndex,
-               const Key& aKey)
-  : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex), mKey(aKey)
+               IDBKeyRange* aKeyRange)
+  : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
+    mKeyRange(aKeyRange)
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mIndex = nsnull;
+    mKeyRange = nsnull;
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 protected:
   // In-params.
   nsRefPtr<IDBIndex> mIndex;
+  nsRefPtr<IDBKeyRange> mKeyRange;
+
+  // Out-params.
   Key mKey;
 };
 
 class GetHelper : public GetKeyHelper
 {
 public:
   GetHelper(IDBTransaction* aTransaction,
             IDBRequest* aRequest,
             IDBIndex* aIndex,
-            const Key& aKey)
-  : GetKeyHelper(aTransaction, aRequest, aIndex, aKey)
+            IDBKeyRange* aKeyRange)
+  : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange)
   { }
 
   ~GetHelper()
   {
     IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
   }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
+  void ReleaseMainThreadObjects()
+  {
+    IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
+    GetKeyHelper::ReleaseMainThreadObjects();
+  }
+
 protected:
   JSAutoStructuredCloneBuffer mCloneBuffer;
 };
 
 class GetAllKeysHelper : public GetKeyHelper
 {
 public:
   GetAllKeysHelper(IDBTransaction* aTransaction,
                    IDBRequest* aRequest,
                    IDBIndex* aIndex,
-                   const Key& aKey,
+                   IDBKeyRange* aKeyRange,
                    const PRUint32 aLimit)
-  : GetKeyHelper(aTransaction, aRequest, aIndex, aKey), mLimit(aLimit)
+  : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit)
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
 protected:
   const PRUint32 mLimit;
@@ -131,34 +142,37 @@ protected:
 };
 
 class GetAllHelper : public GetKeyHelper
 {
 public:
   GetAllHelper(IDBTransaction* aTransaction,
                IDBRequest* aRequest,
                IDBIndex* aIndex,
-               const Key& aKey,
+               IDBKeyRange* aKeyRange,
                const PRUint32 aLimit)
-  : GetKeyHelper(aTransaction, aRequest, aIndex, aKey), mLimit(aLimit)
+  : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit)
   { }
 
   ~GetAllHelper()
   {
     for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
       IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
     }
   }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
+    for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
+      IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
+    }
     GetKeyHelper::ReleaseMainThreadObjects();
   }
 
 protected:
   const PRUint32 mLimit;
   nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
 };
 
@@ -399,27 +413,30 @@ IDBIndex::Get(const jsval& aKey,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
-  Key key;
-  nsresult rv = key.SetFromJSVal(aCx, aKey);
-  if (NS_FAILED(rv)) {
-    return rv;
+  nsRefPtr<IDBKeyRange> keyRange;
+  nsresult rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!keyRange) {
+    // Must specify a key or keyRange for get().
+    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetHelper> helper =
-    new GetHelper(transaction, request, this, key);
+    new GetHelper(transaction, request, this, keyRange);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -429,27 +446,30 @@ IDBIndex::GetKey(const jsval& aKey,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
-  Key key;
-  nsresult rv = key.SetFromJSVal(aCx, aKey);
-  if (NS_FAILED(rv)) {
-    return rv;
+  nsRefPtr<IDBKeyRange> keyRange;
+  nsresult rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!keyRange) {
+    // Must specify a key or keyRange for get().
+    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetKeyHelper> helper =
-    new GetKeyHelper(transaction, request, this, key);
+    new GetKeyHelper(transaction, request, this, keyRange);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
@@ -462,32 +482,35 @@ IDBIndex::GetAll(const jsval& aKey,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
-  Key key;
-  if (aOptionalArgCount && NS_FAILED(key.SetFromJSVal(aCx, aKey))) {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
+  nsresult rv;
+
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aOptionalArgCount) {
+    rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetAllHelper> helper =
-    new GetAllHelper(transaction, request, this, key, aLimit);
+    new GetAllHelper(transaction, request, this, keyRange, aLimit);
 
-  nsresult rv = helper->DispatchToTransactionPool();
+  rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBIndex::GetAllKeys(const jsval& aKey,
@@ -500,30 +523,31 @@ IDBIndex::GetAllKeys(const jsval& aKey,
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   nsresult rv;
 
-  Key key;
-  if (aOptionalArgCount && NS_FAILED(key.SetFromJSVal(aCx, aKey))) {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aOptionalArgCount) {
+    rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetAllKeysHelper> helper =
-    new GetAllKeysHelper(transaction, request, this, key, aLimit);
+    new GetAllKeysHelper(transaction, request, this, keyRange, aLimit);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
@@ -648,81 +672,142 @@ IDBIndex::Count(const jsval& aKey,
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 nsresult
-GetKeyHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
+GetKeyHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
 {
-  NS_ASSERTION(aConnection, "Passed a null connection!");
+  NS_ASSERTION(mKeyRange, "Must have a key range here!");
+
+  nsCString keyColumn;
+  nsCString indexTable;
 
-  nsCOMPtr<mozIStorageStatement> stmt =
-    mTransaction->IndexGetStatement(mIndex->IsUnique(),
-                                    mIndex->IsAutoIncrement());
+  if (mIndex->IsAutoIncrement()) {
+    keyColumn.AssignLiteral("ai_object_data_id");
+    if (mIndex->IsUnique()) {
+      indexTable.AssignLiteral("ai_unique_index_data");
+    }
+    else {
+      indexTable.AssignLiteral("ai_index_data");
+    }
+  }
+  else {
+    keyColumn.AssignLiteral("object_data_key");
+    if (mIndex->IsUnique()) {
+      indexTable.AssignLiteral("unique_index_data");
+    }
+    else {
+      indexTable.AssignLiteral("index_data");
+    }
+  }
+
+  nsCString keyRangeClause;
+  mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
+
+  NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
+
+  NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
+
+  nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
+                    NS_LITERAL_CSTRING(" FROM ") + indexTable +
+                    NS_LITERAL_CSTRING(" WHERE ") + indexId +
+                    NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause +
+                    NS_LITERAL_CSTRING(" LIMIT 1");
+
+  nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
-  nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
-                                      mIndex->Id());
+  nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  NS_NAMED_LITERAL_CSTRING(value, "value");
-
-  rv = mKey.BindToStatement(stmt, value);
+  rv = mKeyRange->BindToStatement(stmt);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mKey.Unset();
-
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = mKey.SetFromStatement(stmt, 0);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 GetKeyHelper::GetSuccessResult(JSContext* aCx,
                                jsval* aVal)
 {
-  NS_ASSERTION(!mKey.IsUnset(), "Badness!");
   return mKey.ToJSVal(aCx, aVal);
 }
 
 nsresult
-GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
+GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
 {
-  NS_ASSERTION(aConnection, "Passed a null connection!");
+  NS_ASSERTION(mKeyRange, "Must have a key range here!");
+
+  nsCString objectTable;
+  nsCString joinTable;
+  nsCString objectColumn;
 
-  nsCOMPtr<mozIStorageStatement> stmt =
-    mTransaction->IndexGetObjectStatement(mIndex->IsUnique(),
-                                          mIndex->IsAutoIncrement());
+  if (mIndex->IsAutoIncrement()) {
+    objectTable.AssignLiteral("ai_object_data");
+    objectColumn.AssignLiteral("ai_object_data_id");
+    if (mIndex->IsUnique()) {
+      joinTable.AssignLiteral("ai_unique_index_data");
+    }
+    else {
+      joinTable.AssignLiteral("ai_index_data");
+    }
+  }
+  else {
+    objectTable.AssignLiteral("object_data");
+    objectColumn.AssignLiteral("object_data_id");
+    if (mIndex->IsUnique()) {
+      joinTable.AssignLiteral("unique_index_data");
+    }
+    else {
+      joinTable.AssignLiteral("index_data");
+    }
+  }
+
+  nsCString keyRangeClause;
+  mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
+
+  NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
+
+  NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
+
+  nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + objectTable +
+                    NS_LITERAL_CSTRING(" INNER JOIN ") + joinTable +
+                    NS_LITERAL_CSTRING(" ON ") + objectTable +
+                    NS_LITERAL_CSTRING(".id = ") + joinTable +
+                    NS_LITERAL_CSTRING(".") + objectColumn +
+                    NS_LITERAL_CSTRING(" WHERE ") + indexId +
+                    NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause +
+                    NS_LITERAL_CSTRING(" LIMIT 1");
+
+  nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
-  nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
-                                      mIndex->Id());
+  nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  NS_NAMED_LITERAL_CSTRING(value, "value");
-
-  rv = mKey.BindToStatement(stmt, value);
+  rv = mKeyRange->BindToStatement(stmt);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mKey.Unset();
-
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
                                                              mCloneBuffer);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -739,25 +824,18 @@ GetHelper::GetSuccessResult(JSContext* a
 
   mCloneBuffer.clear();
 
   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
   return NS_OK;
 }
 
 nsresult
-GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
+GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
 {
-  NS_ASSERTION(aConnection, "Passed a null connection!");
-
-  if (!mKeys.SetCapacity(50)) {
-    NS_ERROR("Out of memory!");
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
-
   nsCString keyColumn;
   nsCString tableName;
 
   if (mIndex->IsAutoIncrement()) {
     keyColumn.AssignLiteral("ai_object_data_id");
     if (mIndex->IsUnique()) {
       tableName.AssignLiteral("ai_unique_index_data");
     }
@@ -770,50 +848,50 @@ GetAllKeysHelper::DoDatabaseWork(mozISto
     if (mIndex->IsUnique()) {
       tableName.AssignLiteral("unique_index_data");
     }
     else {
       tableName.AssignLiteral("index_data");
     }
   }
 
-  NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
-  NS_NAMED_LITERAL_CSTRING(value, "value");
-
-  nsCString keyClause;
-  if (!mKey.IsUnset()) {
-    keyClause = NS_LITERAL_CSTRING(" AND ") + value +
-                NS_LITERAL_CSTRING(" = :") + value;
+  nsCString keyRangeClause;
+  if (mKeyRange) {
+    mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
   }
 
   nsCString limitClause;
   if (mLimit != PR_UINT32_MAX) {
     limitClause = NS_LITERAL_CSTRING(" LIMIT ");
     limitClause.AppendInt(mLimit);
   }
 
+  NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
+
   nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
                     NS_LITERAL_CSTRING(" FROM ") + tableName +
                     NS_LITERAL_CSTRING(" WHERE ") + indexId  +
-                    NS_LITERAL_CSTRING(" = :") + indexId + keyClause +
+                    NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause +
                     limitClause;
-  
+
   nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  if (!mKey.IsUnset()) {
-    rv = mKey.BindToStatement(stmt, value);
+  if (mKeyRange) {
+    rv = mKeyRange->BindToStatement(stmt);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  mKeys.SetCapacity(50);
+
   bool hasResult;
   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
     if (mKeys.Capacity() == mKeys.Length()) {
       mKeys.SetCapacity(mKeys.Capacity() * 2);
     }
 
     Key* key = mKeys.AppendElement();
     NS_ASSERTION(key, "This shouldn't fail!");
@@ -870,25 +948,18 @@ GetAllKeysHelper::GetSuccessResult(JSCon
     }
   }
 
   *aVal = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
 
 nsresult
-GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
+GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
 {
-  NS_ASSERTION(aConnection, "Passed a null connection!");
-
-  if (!mCloneBuffers.SetCapacity(50)) {
-    NS_ERROR("Out of memory!");
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
-
   nsCString dataTableName;
   nsCString objectDataId;
   nsCString indexTableName;
 
   if (mIndex->IsAutoIncrement()) {
     dataTableName.AssignLiteral("ai_object_data");
     objectDataId.AssignLiteral("ai_object_data_id");
     if (mIndex->IsUnique()) {
@@ -905,59 +976,56 @@ GetAllHelper::DoDatabaseWork(mozIStorage
       indexTableName.AssignLiteral("unique_index_data");
     }
     else {
       indexTableName.AssignLiteral("index_data");
     }
   }
 
   NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
-  NS_NAMED_LITERAL_CSTRING(value, "value");
 
-  nsCString keyClause;
-  if (!mKey.IsUnset()) {
-    keyClause = NS_LITERAL_CSTRING(" AND ") + value +
-                NS_LITERAL_CSTRING(" = :") + value;
+  nsCString keyRangeClause;
+  if (mKeyRange) {
+    mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
   }
 
   nsCString limitClause;
   if (mLimit != PR_UINT32_MAX) {
     limitClause = NS_LITERAL_CSTRING(" LIMIT ");
     limitClause.AppendInt(mLimit);
   }
 
   nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + dataTableName +
                     NS_LITERAL_CSTRING(" INNER JOIN ") + indexTableName  +
                     NS_LITERAL_CSTRING(" ON ") + dataTableName +
                     NS_LITERAL_CSTRING(".id = ") + indexTableName +
                     NS_LITERAL_CSTRING(".") + objectDataId +
                     NS_LITERAL_CSTRING(" WHERE ") + indexId  +
-                    NS_LITERAL_CSTRING(" = :") + indexId + keyClause +
+                    NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause +
                     limitClause;
 
   nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  if (!mKey.IsUnset()) {
-    rv = mKey.BindToStatement(stmt, value);
+  if (mKeyRange) {
+    rv = mKeyRange->BindToStatement(stmt);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  mCloneBuffers.SetCapacity(50);
+
   bool hasResult;
   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
     if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
-      if (!mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2)) {
-        NS_ERROR("Out of memory!");
-        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-      }
+      mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2);
     }
 
     JSAutoStructuredCloneBuffer* buffer = mCloneBuffers.AppendElement();
     NS_ASSERTION(buffer, "This shouldn't fail!");
 
     rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0, *buffer);
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -1010,26 +1078,19 @@ OpenKeyCursorHelper::DoDatabaseWork(mozI
   }
 
   NS_NAMED_LITERAL_CSTRING(id, "id");
   NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
   NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
 
   NS_NAMED_LITERAL_CSTRING(value, "value");
 
-  nsCAutoString keyRangeClause;
+  nsCString keyRangeClause;
   if (mKeyRange) {
-    if (!mKeyRange->Lower().IsUnset()) {
-      AppendConditionClause(value, lowerKeyName, false,
-                            !mKeyRange->IsLowerOpen(), keyRangeClause);
-    }
-    if (!mKeyRange->Upper().IsUnset()) {
-      AppendConditionClause(value, upperKeyName, true,
-                            !mKeyRange->IsUpperOpen(), keyRangeClause);
-    }
+    mKeyRange->GetBindingClause(value, keyRangeClause);
   }
 
   nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value;
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
       directionClause.AppendLiteral(" ASC");
       break;
@@ -1056,24 +1117,18 @@ OpenKeyCursorHelper::DoDatabaseWork(mozI
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(id, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (mKeyRange) {
-    if (!mKeyRange->Lower().IsUnset()) {
-      rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    if (!mKeyRange->Upper().IsUnset()) {
-      rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = mKeyRange->BindToStatement(stmt);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!hasResult) {
     mKey.Unset();
@@ -1209,33 +1264,24 @@ OpenCursorHelper::DoDatabaseWork(mozISto
       indexTable.AssignLiteral("unique_index_data");
     }
     else {
       indexTable.AssignLiteral("index_data");
     }
   }
 
   NS_NAMED_LITERAL_CSTRING(id, "id");
-  NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
-  NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
 
   nsCString value = indexTable + NS_LITERAL_CSTRING(".value");
   nsCString data = objectTable + NS_LITERAL_CSTRING(".data");
   nsCString keyValue = objectTable + NS_LITERAL_CSTRING(".") + keyValueColumn;
 
-  nsCAutoString keyRangeClause;
+  nsCString keyRangeClause;
   if (mKeyRange) {
-    if (!mKeyRange->Lower().IsUnset()) {
-      AppendConditionClause(value, lowerKeyName, false,
-                            !mKeyRange->IsLowerOpen(), keyRangeClause);
-    }
-    if (!mKeyRange->Upper().IsUnset()) {
-      AppendConditionClause(value, upperKeyName, true,
-                            !mKeyRange->IsUpperOpen(), keyRangeClause);
-    }
+    mKeyRange->GetBindingClause(value, keyRangeClause);
   }
 
   nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value;
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
       directionClause.AppendLiteral(" ASC");
       break;
@@ -1269,24 +1315,18 @@ OpenCursorHelper::DoDatabaseWork(mozISto
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(id, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (mKeyRange) {
-    if (!mKeyRange->Lower().IsUnset()) {
-      rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    if (!mKeyRange->Upper().IsUnset()) {
-      rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = mKeyRange->BindToStatement(stmt);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!hasResult) {
     mKey.Unset();
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -42,16 +42,18 @@
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 #include "mozilla/dom/indexedDB/Key.h"
 
 #include "nsIIDBKeyRange.h"
 
 #include "nsCycleCollectionParticipant.h"
 
+class mozIStorageStatement;
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class IDBKeyRange : public nsIIDBKeyRange
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIIDBKEYRANGE
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBKeyRange)
@@ -95,16 +97,83 @@ public:
     return mLowerOpen;
   }
 
   bool IsUpperOpen() const
   {
     return mUpperOpen;
   }
 
+  bool IsOnly() const
+  {
+    return mIsOnly;
+  }
+
+  void GetBindingClause(const nsACString& aKeyColumnName,
+                        nsACString& _retval) const
+  {
+    NS_NAMED_LITERAL_CSTRING(andStr, " AND ");
+    NS_NAMED_LITERAL_CSTRING(spacecolon, " :");
+    NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
+
+    if (IsOnly()) {
+      // Both keys are set and they're equal.
+      _retval = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") + spacecolon +
+                lowerKey;
+    }
+    else {
+      nsCAutoString clause;
+
+      if (!Lower().IsUnset()) {
+        // Lower key is set.
+        clause.Append(andStr + aKeyColumnName);
+        clause.AppendLiteral(" >");
+        if (!IsLowerOpen()) {
+          clause.AppendLiteral("=");
+        }
+        clause.Append(spacecolon + lowerKey);
+      }
+
+      if (!Upper().IsUnset()) {
+        // Upper key is set.
+        clause.Append(andStr + aKeyColumnName);
+        clause.AppendLiteral(" <");
+        if (!IsUpperOpen()) {
+          clause.AppendLiteral("=");
+        }
+        clause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key"));
+      }
+
+      _retval = clause;
+    }
+  }
+
+  nsresult BindToStatement(mozIStorageStatement* aStatement) const
+  {
+    NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key");
+
+    if (IsOnly()) {
+      return Lower().BindToStatement(aStatement, lowerKey);
+    }
+
+    nsresult rv;
+
+    if (!Lower().IsUnset()) {
+      rv = Lower().BindToStatement(aStatement, lowerKey);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    }
+
+    if (!Upper().IsUnset()) {
+      rv = Upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key"));
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    }
+
+    return NS_OK;
+  }
+
 protected:
   ~IDBKeyRange() { }
 
   Key mLower;
   Key mUpper;
   jsval mCachedLowerVal;
   jsval mCachedUpperVal;
   bool mLowerOpen;
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -112,55 +112,56 @@ private:
 };
 
 class GetHelper : public AsyncConnectionHelper
 {
 public:
   GetHelper(IDBTransaction* aTransaction,
             IDBRequest* aRequest,
             IDBObjectStore* aObjectStore,
-            const Key& aKey)
+            IDBKeyRange* aKeyRange)
   : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
-    mKey(aKey)
+    mKeyRange(aKeyRange)
   { }
 
   ~GetHelper()
   {
     IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
   }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mObjectStore = nsnull;
+    mKeyRange = nsnull;
     IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 protected:
   // In-params.
   nsRefPtr<IDBObjectStore> mObjectStore;
-  Key mKey;
+  nsRefPtr<IDBKeyRange> mKeyRange;
 
 private:
   // Out-params.
   JSAutoStructuredCloneBuffer mCloneBuffer;
 };
 
 class DeleteHelper : public GetHelper
 {
 public:
   DeleteHelper(IDBTransaction* aTransaction,
                IDBRequest* aRequest,
                IDBObjectStore* aObjectStore,
-               const Key& aKey)
-  : GetHelper(aTransaction, aRequest, aObjectStore, aKey)
+               IDBKeyRange* aKeyRange)
+  : GetHelper(aTransaction, aRequest, aObjectStore, aKeyRange)
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 };
 
 class ClearHelper : public AsyncConnectionHelper
@@ -1058,31 +1059,30 @@ IDBObjectStore::Get(const jsval& aKey,
                     nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mTransaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
-  Key key;
-  nsresult rv = key.SetFromJSVal(aCx, aKey);
-  if (NS_FAILED(rv)) {
-    // Maybe this is a key range.
-    return GetAll(aKey, 0, aCx, 1, _retval);
-  }
-
-  if (key.IsUnset()) {
+  nsRefPtr<IDBKeyRange> keyRange;
+  nsresult rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!keyRange) {
+    // Must specify a key or keyRange for get().
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  nsRefPtr<GetHelper> helper(new GetHelper(mTransaction, request, this, key));
+  nsRefPtr<GetHelper> helper =
+    new GetHelper(mTransaction, request, this, keyRange);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
@@ -1158,31 +1158,30 @@ IDBObjectStore::Delete(const jsval& aKey
   if (!mTransaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   if (!IsWriteAllowed()) {
     return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
   }
 
-  Key key;
-  nsresult rv = key.SetFromJSVal(aCx, aKey);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (key.IsUnset()) {
+  nsRefPtr<IDBKeyRange> keyRange;
+  nsresult rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!keyRange) {
+    // Must specify a key or keyRange for delete().
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<DeleteHelper> helper =
-    new DeleteHelper(mTransaction, request, this, key);
+    new DeleteHelper(mTransaction, request, this, keyRange);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
@@ -1542,22 +1541,42 @@ AddHelper::DoDatabaseWork(mozIStorageCon
 
   if (autoIncrement && !unsetKey) {
     mayOverwrite = true;
   }
 
   nsCOMPtr<mozIStorageStatement> stmt;
   if (!mOverwrite && !unsetKey) {
     // Make sure the key doesn't exist already
-    stmt = mTransaction->GetStatement(autoIncrement);
+    NS_NAMED_LITERAL_CSTRING(id, "id");
+    NS_NAMED_LITERAL_CSTRING(osidStr, "osid");
+
+    nsCString table;
+    nsCString value;
+
+    if (autoIncrement) {
+      table.AssignLiteral("ai_object_data");
+      value = id;
+    }
+    else {
+      table.AssignLiteral("object_data");
+      value.AssignLiteral("key_value");
+    }
+
+    nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + table +
+                      NS_LITERAL_CSTRING(" WHERE ") + value +
+                      NS_LITERAL_CSTRING(" = :") + id +
+                      NS_LITERAL_CSTRING(" AND object_store_id = :") + osidStr;
+
+    stmt = mTransaction->GetCachedStatement(query);
     NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     mozStorageStatementScoper scoper(stmt);
 
-    rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
+    rv = stmt->BindInt64ByName(osidStr, osid);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     rv = mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("id"));
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool hasResult;
     rv = stmt->ExecuteStep(&hasResult);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@@ -1701,36 +1720,53 @@ AddHelper::GetSuccessResult(JSContext* a
   NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   mCloneBuffer.clear();
 
   return mKey.ToJSVal(aCx, aVal);
 }
 
 nsresult
-GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
+GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
 {
-  NS_PRECONDITION(aConnection, "Passed a null connection!");
-
-  nsCOMPtr<mozIStorageStatement> stmt =
-    mTransaction->GetStatement(mObjectStore->IsAutoIncrement());
+  NS_ASSERTION(mKeyRange, "Must have a key range here!");
+
+  nsCString table;
+  nsCString value;
+  if (mObjectStore->IsAutoIncrement()) {
+    table.AssignLiteral("ai_object_data");
+    value.AssignLiteral("id");
+  }
+  else {
+    table.AssignLiteral("object_data");
+    value.AssignLiteral("key_value");
+  }
+
+  nsCString keyRangeClause;
+  mKeyRange->GetBindingClause(value, keyRangeClause);
+
+  NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
+
+  NS_NAMED_LITERAL_CSTRING(osid, "osid");
+
+  nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + table +
+                    NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid +
+                    keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1");
+
+  nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
-  nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
-                                      mObjectStore->Id());
+  nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  NS_ASSERTION(!mKey.IsUnset(), "Must have a key here!");
-
-  rv = mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("id"));
+  rv = mKeyRange->BindToStatement(stmt);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Search for it!
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
                                                              mCloneBuffer);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1747,47 +1783,66 @@ GetHelper::GetSuccessResult(JSContext* a
 
   mCloneBuffer.clear();
 
   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
   return NS_OK;
 }
 
 nsresult
-DeleteHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
+DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */)
 {
-  NS_PRECONDITION(aConnection, "Passed a null connection!");
-
-  nsCOMPtr<mozIStorageStatement> stmt =
-    mTransaction->DeleteStatement(mObjectStore->IsAutoIncrement());
+  NS_ASSERTION(mKeyRange, "Must have a key range here!");
+
+  nsCString table;
+  nsCString value;
+  if (mObjectStore->IsAutoIncrement()) {
+    table.AssignLiteral("ai_object_data");
+    value.AssignLiteral("id");
+  }
+  else {
+    table.AssignLiteral("object_data");
+    value.AssignLiteral("key_value");
+  }
+
+  nsCString keyRangeClause;
+  mKeyRange->GetBindingClause(value, keyRangeClause);
+
+  NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
+
+  NS_NAMED_LITERAL_CSTRING(osid, "osid");
+
+  nsCString query = NS_LITERAL_CSTRING("DELETE FROM ") + table +
+                    NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid +
+                    keyRangeClause;
+
+  nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
-  nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
-                                      mObjectStore->Id());
+  nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  NS_ASSERTION(!mKey.IsUnset(), "Must have a key here!");
-
-  rv = mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value"));
+  rv = mKeyRange->BindToStatement(stmt);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
 DeleteHelper::GetSuccessResult(JSContext* aCx,
                                jsval* aVal)
 {
-  NS_ASSERTION(!mKey.IsUnset(), "Badness!");
-  return mKey.ToJSVal(aCx, aVal);
+  // XXX Will fix this for real in a bit.
+  *aVal = JSVAL_TRUE;
+  return NS_OK;
 }
 
 nsresult
 ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_PRECONDITION(aConnection, "Passed a null connection!");
 
   nsCString table;
@@ -1827,29 +1882,20 @@ OpenCursorHelper::DoDatabaseWork(mozISto
     keyColumn.AssignLiteral("id");
   }
   else {
     table.AssignLiteral("object_data");
     keyColumn.AssignLiteral("key_value");
   }
 
   NS_NAMED_LITERAL_CSTRING(id, "id");
-  NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
-  NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
-
-  nsCAutoString keyRangeClause;
+
+  nsCString keyRangeClause;
   if (mKeyRange) {
-    if (!mKeyRange->Lower().IsUnset()) {
-      AppendConditionClause(keyColumn, lowerKeyName, false,
-                            !mKeyRange->IsLowerOpen(), keyRangeClause);
-    }
-    if (!mKeyRange->Upper().IsUnset()) {
-      AppendConditionClause(keyColumn, upperKeyName, true,
-                            !mKeyRange->IsUpperOpen(), keyRangeClause);
-    }
+    mKeyRange->GetBindingClause(keyColumn, keyRangeClause);
   }
 
   nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyColumn;
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
       directionClause += NS_LITERAL_CSTRING(" ASC");
       break;
@@ -1873,24 +1919,18 @@ OpenCursorHelper::DoDatabaseWork(mozISto
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (mKeyRange) {
-    if (!mKeyRange->Lower().IsUnset()) {
-      rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    if (!mKeyRange->Upper().IsUnset()) {
-      rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = mKeyRange->BindToStatement(stmt);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!hasResult) {
     mKey.Unset();
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -358,132 +358,16 @@ IDBTransaction::AddStatement(bool aCreat
     "UPDATE object_data "
     "SET data = :data "
     "WHERE object_store_id = :osid "
     "AND key_value = :key_value"
   );
 }
 
 already_AddRefed<mozIStorageStatement>
-IDBTransaction::DeleteStatement(bool aAutoIncrement)
-{
-  if (aAutoIncrement) {
-    return GetCachedStatement(
-      "DELETE FROM ai_object_data "
-      "WHERE id = :key_value "
-      "AND object_store_id = :osid"
-    );
-  }
-  return GetCachedStatement(
-    "DELETE FROM object_data "
-    "WHERE key_value = :key_value "
-    "AND object_store_id = :osid"
-  );
-}
-
-already_AddRefed<mozIStorageStatement>
-IDBTransaction::GetStatement(bool aAutoIncrement)
-{
-  if (aAutoIncrement) {
-    return GetCachedStatement(
-      "SELECT data "
-      "FROM ai_object_data "
-      "WHERE id = :id "
-      "AND object_store_id = :osid"
-    );
-  }
-  return GetCachedStatement(
-    "SELECT data "
-    "FROM object_data "
-    "WHERE key_value = :id "
-    "AND object_store_id = :osid"
-  );
-}
-
-already_AddRefed<mozIStorageStatement>
-IDBTransaction::IndexGetStatement(bool aUnique,
-                                  bool aAutoIncrement)
-{
-  if (aAutoIncrement) {
-    if (aUnique) {
-      return GetCachedStatement(
-        "SELECT ai_object_data_id "
-        "FROM ai_unique_index_data "
-        "WHERE index_id = :index_id "
-        "AND value = :value"
-      );
-    }
-    return GetCachedStatement(
-      "SELECT ai_object_data_id "
-      "FROM ai_index_data "
-      "WHERE index_id = :index_id "
-      "AND value = :value"
-    );
-  }
-  if (aUnique) {
-    return GetCachedStatement(
-      "SELECT object_data_key "
-      "FROM unique_index_data "
-      "WHERE index_id = :index_id "
-      "AND value = :value"
-    );
-  }
-  return GetCachedStatement(
-    "SELECT object_data_key "
-    "FROM index_data "
-    "WHERE index_id = :index_id "
-    "AND value = :value"
-  );
-}
-
-already_AddRefed<mozIStorageStatement>
-IDBTransaction::IndexGetObjectStatement(bool aUnique,
-                                        bool aAutoIncrement)
-{
-  if (aAutoIncrement) {
-    if (aUnique) {
-      return GetCachedStatement(
-        "SELECT data "
-        "FROM ai_object_data "
-        "INNER JOIN ai_unique_index_data "
-        "ON ai_object_data.id = ai_unique_index_data.ai_object_data_id "
-        "WHERE index_id = :index_id "
-        "AND value = :value"
-      );
-    }
-    return GetCachedStatement(
-      "SELECT data "
-      "FROM ai_object_data "
-      "INNER JOIN ai_index_data "
-      "ON ai_object_data.id = ai_index_data.ai_object_data_id "
-      "WHERE index_id = :index_id "
-      "AND value = :value"
-    );
-  }
-  if (aUnique) {
-    return GetCachedStatement(
-      "SELECT data "
-      "FROM object_data "
-      "INNER JOIN unique_index_data "
-      "ON object_data.id = unique_index_data.object_data_id "
-      "WHERE index_id = :index_id "
-      "AND value = :value"
-    );
-  }
-  return GetCachedStatement(
-    "SELECT data "
-    "FROM object_data "
-    "INNER JOIN index_data "
-    "ON object_data.id = index_data.object_data_id "
-    "WHERE index_id = :index_id "
-    "AND value = :value"
-  );
-}
-
-already_AddRefed<mozIStorageStatement>
 IDBTransaction::IndexUpdateStatement(bool aAutoIncrement,
                                      bool aUnique,
                                      bool aOverwrite)
 {
   if (aAutoIncrement) {
     if (aUnique) {
       if (aOverwrite) {
         return GetCachedStatement(
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -114,30 +114,16 @@ public:
   nsresult GetOrCreateConnection(mozIStorageConnection** aConnection);
 
   already_AddRefed<mozIStorageStatement>
   AddStatement(bool aCreate,
                bool aOverwrite,
                bool aAutoIncrement);
 
   already_AddRefed<mozIStorageStatement>
-  DeleteStatement(bool aAutoIncrement);
-
-  already_AddRefed<mozIStorageStatement>
-  GetStatement(bool aAutoIncrement);
-
-  already_AddRefed<mozIStorageStatement>
-  IndexGetStatement(bool aUnique,
-                    bool aAutoIncrement);
-
-  already_AddRefed<mozIStorageStatement>
-  IndexGetObjectStatement(bool aUnique,
-                          bool aAutoIncrement);
-
-  already_AddRefed<mozIStorageStatement>
   IndexUpdateStatement(bool aAutoIncrement,
                        bool aUnique,
                        bool aOverwrite);
 
   already_AddRefed<mozIStorageStatement>
   GetCachedStatement(const nsACString& aQuery);
 
   template<int N>
--- a/dom/indexedDB/nsIIDBVersionChangeEvent.idl
+++ b/dom/indexedDB/nsIIDBVersionChangeEvent.idl
@@ -34,14 +34,14 @@
  * 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 "nsIDOMEvent.idl"
 
-[scriptable, builtinclass, uuid(2c5159dc-7d71-4fc6-a3b3-884ed7586456)]
+[scriptable, builtinclass, uuid(52e21085-d6cd-4886-a5eb-19b47d13abf6)]
 interface nsIIDBVersionChangeEvent : nsIDOMEvent
 {
   readonly attribute unsigned long long oldVersion;
   readonly attribute unsigned long long newVersion;
 };
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -79,16 +79,17 @@ TEST_FILES = \
   test_leaving_page.html \
   test_objectCursors.html \
   test_objectStore_inline_autoincrement_key_added_on_put.html \
   test_objectStore_remove_values.html \
   test_object_identity.html \
   test_odd_result_order.html \
   test_open_empty_db.html \
   test_open_objectStore.html \
+  test_optionalArguments.html \
   test_overlapping_transactions.html \
   test_put_get_values.html \
   test_put_get_values_autoIncrement.html \
   test_readonly_transactions.html \
   test_remove_index.html \
   test_remove_objectStore.html \
   test_request_readyState.html \
   test_success_events_after_abort.html \
@@ -118,14 +119,14 @@ BROWSER_TEST_FILES = \
   browser_privateBrowsing.js \
   browser_quotaPrompt.html \
   browser_quotaPromptAllow.js \
   browser_quotaPromptDeny.js \
   head.js \
   $(NULL)
 
 libs:: $(BROWSER_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/browser/$(relativesrcdir)
 endif
 
 libs:: $(TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/$(mochitestdir)/tests/$(relativesrcdir)
 
--- a/dom/indexedDB/test/test_cursors.html
+++ b/dom/indexedDB/test/test_cursors.html
@@ -299,17 +299,17 @@
           is(cursor.key, sortedKeys[keyIndex], "Correct key");
           is(cursor.primaryKey, sortedKeys[keyIndex], "Correct primary key");
           is(cursor.value, "foo", "Correct value");
 
           if (keyIndex == 4) {
             request = cursor.delete();
             request.onerror = errorHandler;
             request.onsuccess = function(event) {
-              is(event.target.result, sortedKeys[4], "Correct key");
+              is(event.target.result, true, "Actually deleted something");
               is(keyIndex, 5, "Got result of remove before next continue");
               gotRemoveEvent = true;
             };
           }
 
           keyIndex++;
           cursor.continue();
         }
--- a/dom/indexedDB/test/test_getAll.html
+++ b/dom/indexedDB/test/test_getAll.html
@@ -96,30 +96,24 @@
 
       is(event.target.result instanceof Array, true, "Got an array object");
       is(event.target.result.length, 4, "Correct length");
 
       for (let i in event.target.result) {
         is(event.target.result[i], values[parseInt(i) + 3], "Same value");
       }
 
-      // Get should take a key range also.
+      // Get should take a key range also but it doesn't return an array.
       request = db.transaction("foo").objectStore("foo").get(keyRange);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
-      is(event.target.result instanceof Array, true, "Got an array object");
-      is(event.target.result.length, 4, "Correct length");
-
-      for (let i in event.target.result) {
-        is(event.target.result[i], values[parseInt(i) + 3], "Same value");
-      }
-
-      keyRange = IDBKeyRange.bound(4, 7);
+      is(event.target.result instanceof Array, false, "Not an array object");
+      is(event.target.result, values[3], "Correct value");
 
       request = db.transaction("foo").objectStore("foo").getAll(keyRange, 2);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       is(event.target.result instanceof Array, true, "Got an array object");
       is(event.target.result.length, 2, "Correct length");
--- a/dom/indexedDB/test/test_odd_result_order.html
+++ b/dom/indexedDB/test/test_odd_result_order.html
@@ -69,17 +69,17 @@
 
       key = undefined;
       setTimeout(function() {
         key = request.result;
         continueToNextStep();
       }, 0);
       yield;
 
-      is(key, data.key, "Got the right key");
+      is(key, true, "Got the right key");
 
       finishTest();
       yield;
     }
   </script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 </head>
 
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_optionalArguments.html
@@ -0,0 +1,1715 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+    function testSteps()
+    {
+      const osName = "people";
+      const indexName = "weight";
+
+      const data = [
+        { ssn: "237-23-7732", name: "Bob", height: 60, weight: 120 },
+        { ssn: "237-23-7733", name: "Ann", height: 52, weight: 110 },
+        { ssn: "237-23-7734", name: "Ron", height: 73, weight: 180 },
+        { ssn: "237-23-7735", name: "Sue", height: 58, weight: 130 },
+        { ssn: "237-23-7736", name: "Joe", height: 65, weight: 150 },
+        { ssn: "237-23-7737", name: "Pat", height: 65 },
+        { ssn: "237-23-7738", name: "Mel", height: 66, weight: {} },
+        { ssn: "237-23-7739", name: "Tom", height: 62, weight: 130 }
+      ];
+
+      const weightSort = [1, 0, 3, 7, 4, 2];
+
+      let request = mozIndexedDB.open(window.location.pathname, 1);
+      request.onerror = errorHandler;
+      request.onupgradeneeded = grabEventAndContinueHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      is(event.type, "upgradeneeded", "Got upgradeneeded event");
+
+      let db = event.target.result;
+      db.onerror = errorHandler;
+
+      let objectStore = db.createObjectStore(osName, { keyPath: "ssn" });
+      objectStore.createIndex(indexName, "weight", { unique: false });
+
+      for each (let i in data) {
+        objectStore.add(i);
+      }
+
+      event = yield;
+
+      is(event.type, "success", "Got success event");
+
+      try {
+        IDBKeyRange.bound(1, -1);
+        ok(false, "Bound keyRange with backwards args should throw!");
+      }
+      catch (e) {
+        is(e.code, IDBDatabaseException.DATA_ERR, "Threw correct exception");
+      }
+
+      try {
+        IDBKeyRange.bound(1, 1);
+        ok(true, "Bound keyRange with same arg should be ok");
+      }
+      catch (e) {
+        ok(false, "Bound keyRange with same arg should have been ok");
+      }
+
+      try {
+        IDBKeyRange.bound(1, 1, true);
+        ok(false, "Bound keyRange with same arg and open should throw!");
+      }
+      catch (e) {
+        is(e.code, IDBDatabaseException.DATA_ERR, "Threw correct exception");
+      }
+
+      try {
+        IDBKeyRange.bound(1, 1, true, true);
+        ok(false, "Bound keyRange with same arg and open should throw!");
+      }
+      catch (e) {
+        is(e.code, IDBDatabaseException.DATA_ERR, "Threw correct exception");
+      }
+
+      objectStore = db.transaction(osName).objectStore(osName);
+
+      try {
+        objectStore.get();
+        ok(false, "Get with unspecified arg should have thrown");
+      }
+      catch(e) {
+        ok(true, "Get with unspecified arg should have thrown");
+      }
+
+      try {
+        objectStore.get(undefined);
+        ok(false, "Get with undefined should have thrown");
+      }
+      catch(e) {
+        ok(true, "Get with undefined arg should have thrown");
+      }
+
+      try {
+        objectStore.get(null);
+        ok(false, "Get with null should have thrown");
+      }
+      catch(e) {
+        is(e instanceof IDBDatabaseException, true,
+           "Got right kind of exception");
+        is(e.code, IDBDatabaseException.DATA_ERR, "Correct error code.");
+      }
+
+      objectStore.get(data[2].ssn).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.name, data[2].name, "Correct data");
+
+      let keyRange = IDBKeyRange.only(data[2].ssn);
+
+      objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.name, data[2].name, "Correct data");
+
+      keyRange = IDBKeyRange.lowerBound(data[2].ssn);
+
+      objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.name, data[2].name, "Correct data");
+
+      keyRange = IDBKeyRange.lowerBound(data[2].ssn, true);
+
+      objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.name, data[3].name, "Correct data");
+
+      keyRange = IDBKeyRange.upperBound(data[2].ssn);
+
+      objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.name, data[0].name, "Correct data");
+
+      keyRange = IDBKeyRange.bound(data[2].ssn, data[4].ssn);
+
+      objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.name, data[2].name, "Correct data");
+
+      keyRange = IDBKeyRange.bound(data[2].ssn, data[4].ssn, true);
+
+      objectStore.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.name, data[3].name, "Correct data");
+
+      objectStore = db.transaction(osName, IDBTransaction.READ_WRITE)
+                      .objectStore(osName);
+
+      try {
+        objectStore.delete();
+        ok(false, "Delete with unspecified arg should have thrown");
+      }
+      catch(e) {
+        ok(true, "Delete with unspecified arg should have thrown");
+      }
+
+      try {
+        objectStore.delete(undefined);
+        ok(false, "Delete with undefined should have thrown");
+      }
+      catch(e) {
+        ok(true, "Delete with undefined arg should have thrown");
+      }
+
+      try {
+        objectStore.delete(null);
+        ok(false, "Delete with null should have thrown");
+      }
+      catch(e) {
+        is(e instanceof IDBDatabaseException, true,
+           "Got right kind of exception");
+        is(e.code, IDBDatabaseException.DATA_ERR, "Correct error code.");
+      }
+
+      objectStore.count().onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data.length, "Correct count");
+
+      objectStore.delete(data[2].ssn).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, true, "Correct result");
+
+      objectStore.count().onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data.length - 1, "Correct count");
+
+      keyRange = IDBKeyRange.bound(data[3].ssn, data[5].ssn);
+
+      objectStore.delete(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, true, "Correct result");
+
+      objectStore.count().onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data.length - 4, "Correct count");
+
+      keyRange = IDBKeyRange.lowerBound(10);
+
+      objectStore.delete(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, true, "Correct result");
+
+      objectStore.count().onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, 0, "Correct count");
+
+      event.target.transaction.oncomplete = grabEventAndContinueHandler;
+
+      for each (let i in data) {
+        objectStore.add(i);
+      }
+
+      yield;
+
+      objectStore = db.transaction(osName).objectStore(osName);
+
+      objectStore.count().onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data.length, "Correct count");
+
+      let count = 0;
+
+      objectStore.openCursor().onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, data.length, "Correct count for no arg to openCursor");
+
+      count = 0;
+
+      objectStore.openCursor(null).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, data.length, "Correct count for null arg to openCursor");
+
+      count = 0;
+
+      objectStore.openCursor(undefined).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, data.length, "Correct count for undefined arg to openCursor");
+
+      count = 0;
+
+      objectStore.openCursor(data[2].ssn).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1, "Correct count for single key arg to openCursor");
+
+      count = 0;
+
+      objectStore.openCursor("foo").onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for non-existent single key arg to openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.only(data[2].ssn);
+
+      objectStore.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1, "Correct count for only keyRange arg to openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound(data[2].ssn);
+
+      objectStore.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, data.length - 2,
+         "Correct count for lowerBound arg to openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound(data[2].ssn, true);
+
+      objectStore.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, data.length - 3,
+         "Correct count for lowerBound arg to openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound("foo");
+
+      objectStore.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for non-existent lowerBound arg to openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[2].ssn, data[3].ssn);
+
+      objectStore.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 2, "Correct count for bound arg to openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[2].ssn, data[3].ssn, true);
+
+      objectStore.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1, "Correct count for bound arg to openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[2].ssn, data[3].ssn, true, true);
+
+      objectStore.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0, "Correct count for bound arg to openCursor");
+
+      let index = objectStore.index(indexName);
+
+      count = 0;
+
+      index.openKeyCursor().onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for unspecified arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openKeyCursor(null).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for null arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openKeyCursor(undefined).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for undefined arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openKeyCursor(data[0].weight).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1, "Correct count for single key arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openKeyCursor("foo").onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for non-existent key arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.only("foo");
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for non-existent keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.only(data[0].weight);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1,
+         "Correct count for only keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for lowerBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight, true);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length - 1,
+         "Correct count for lowerBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound("foo");
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for lowerBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1,
+         "Correct count for upperBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight, true);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for upperBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(data[weightSort[weightSort.length - 1]].weight);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for upperBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(data[weightSort[weightSort.length - 1]].weight,
+                                        true);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length - 1,
+         "Correct count for upperBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound("foo");
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for upperBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(0);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for upperBound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[weightSort.length - 1]].weight);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for bound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[weightSort.length - 1]].weight,
+                                   true);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length - 1,
+         "Correct count for bound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[weightSort.length - 1]].weight,
+                                   true, true);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length - 2,
+         "Correct count for bound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight - 1,
+                                   data[weightSort[weightSort.length - 1]].weight + 1);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for bound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight - 2,
+                                   data[weightSort[0]].weight - 1);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for bound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[1]].weight,
+                                   data[weightSort[2]].weight);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 3,
+         "Correct count for bound keyRange arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openCursor().onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for unspecified arg to index.openCursor");
+
+      count = 0;
+
+      index.openCursor(null).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for null arg to index.openCursor");
+
+      count = 0;
+
+      index.openCursor(undefined).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for undefined arg to index.openCursor");
+
+      count = 0;
+
+      index.openCursor(data[0].weight).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1, "Correct count for single key arg to index.openCursor");
+
+      count = 0;
+
+      index.openCursor("foo").onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for non-existent key arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.only("foo");
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for non-existent keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.only(data[0].weight);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1,
+         "Correct count for only keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for lowerBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight, true);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length - 1,
+         "Correct count for lowerBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.lowerBound("foo");
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for lowerBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1,
+         "Correct count for upperBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight, true);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for upperBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(data[weightSort[weightSort.length - 1]].weight);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for upperBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(data[weightSort[weightSort.length - 1]].weight,
+                                        true);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length - 1,
+         "Correct count for upperBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound("foo");
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for upperBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.upperBound(0);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for upperBound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[weightSort.length - 1]].weight);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for bound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[weightSort.length - 1]].weight,
+                                   true);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length - 1,
+         "Correct count for bound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[weightSort.length - 1]].weight,
+                                   true, true);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length - 2,
+         "Correct count for bound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight - 1,
+                                   data[weightSort[weightSort.length - 1]].weight + 1);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for bound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight - 2,
+                                   data[weightSort[0]].weight - 1);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for bound keyRange arg to index.openCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.bound(data[weightSort[1]].weight,
+                                   data[weightSort[2]].weight);
+
+      index.openCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 3,
+         "Correct count for bound keyRange arg to index.openCursor");
+
+      try {
+        index.get();
+        ok(false, "Get with unspecified arg should have thrown");
+      }
+      catch(e) {
+        ok(true, "Get with unspecified arg should have thrown");
+      }
+
+      try {
+        index.get(undefined);
+        ok(false, "Get with undefined should have thrown");
+      }
+      catch(e) {
+        ok(true, "Get with undefined arg should have thrown");
+      }
+
+      try {
+        index.get(null);
+        ok(false, "Get with null should have thrown");
+      }
+      catch(e) {
+        is(e instanceof IDBDatabaseException, true,
+           "Got right kind of exception");
+        is(e.code, IDBDatabaseException.DATA_ERR, "Correct error code.");
+      }
+
+      index.get(data[0].weight).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[0].weight, "Got correct result");
+
+      keyRange = IDBKeyRange.only(data[0].weight);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[0].weight, "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[weightSort[0]].weight,
+         "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight - 1);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[weightSort[0]].weight,
+         "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight + 1);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[weightSort[1]].weight,
+         "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight, true);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[weightSort[1]].weight,
+         "Got correct result");
+
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[1]].weight);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[weightSort[0]].weight,
+         "Got correct result");
+
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[1]].weight, true);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[weightSort[1]].weight,
+         "Got correct result");
+
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[1]].weight, true, true);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, undefined, "Got correct result");
+
+      keyRange = IDBKeyRange.upperBound(data[weightSort[5]].weight);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result.weight, data[weightSort[0]].weight,
+         "Got correct result");
+
+      keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight, true);
+
+      index.get(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, undefined, "Got correct result");
+
+      try {
+        index.getKey();
+        ok(false, "Get with unspecified arg should have thrown");
+      }
+      catch(e) {
+        ok(true, "Get with unspecified arg should have thrown");
+      }
+
+      try {
+        index.getKey(undefined);
+        ok(false, "Get with undefined should have thrown");
+      }
+      catch(e) {
+        ok(true, "Get with undefined arg should have thrown");
+      }
+
+      try {
+        index.getKey(null);
+        ok(false, "Get with null should have thrown");
+      }
+      catch(e) {
+        is(e instanceof IDBDatabaseException, true,
+           "Got right kind of exception");
+        is(e.code, IDBDatabaseException.DATA_ERR, "Correct error code.");
+      }
+
+      index.getKey(data[0].weight).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[0].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.only(data[0].weight);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[0].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[weightSort[0]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight - 1);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[weightSort[0]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight + 1);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[weightSort[1]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(data[weightSort[0]].weight, true);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[weightSort[1]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[1]].weight);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[weightSort[0]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[1]].weight, true);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[weightSort[1]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.bound(data[weightSort[0]].weight,
+                                   data[weightSort[1]].weight, true, true);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, undefined, "Got correct result");
+
+      keyRange = IDBKeyRange.upperBound(data[weightSort[5]].weight);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, data[weightSort[0]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.upperBound(data[weightSort[0]].weight, true);
+
+      index.getKey(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result, undefined, "Got correct result");
+
+      count = 0;
+
+      index.openKeyCursor().onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for no arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openKeyCursor(null).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for null arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openKeyCursor(undefined).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, weightSort.length,
+         "Correct count for undefined arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openKeyCursor(data[weightSort[0]].weight).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1, "Correct count for single key arg to index.openKeyCursor");
+
+      count = 0;
+
+      index.openKeyCursor("foo").onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 0,
+         "Correct count for non-existent single key arg to index.openKeyCursor");
+
+      count = 0;
+      keyRange = IDBKeyRange.only(data[weightSort[0]].weight);
+
+      index.openKeyCursor(keyRange).onsuccess = function(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          count++;
+          cursor.continue();
+        }
+        else {
+          testGenerator.next();
+        }
+      }
+      yield;
+
+      is(count, 1,
+         "Correct count for only keyRange arg to index.openKeyCursor");
+
+      objectStore.getAll(data[1].ssn).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, 1, "Got correct length");
+      is(event.target.result[0].ssn, data[1].ssn, "Got correct result");
+
+      objectStore.getAll(null).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, data.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i].ssn, data[i].ssn, "Got correct value");
+      }
+
+      objectStore.getAll(undefined).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, data.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i].ssn, data[i].ssn, "Got correct value");
+      }
+
+      objectStore.getAll().onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, data.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i].ssn, data[i].ssn, "Got correct value");
+      }
+
+      keyRange = IDBKeyRange.lowerBound(0);
+
+      objectStore.getAll(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, data.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i].ssn, data[i].ssn, "Got correct value");
+      }
+
+      index.getAll().onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, weightSort.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i].ssn, data[weightSort[i]].ssn,
+           "Got correct value");
+      }
+
+      index.getAll(undefined).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, weightSort.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i].ssn, data[weightSort[i]].ssn,
+           "Got correct value");
+      }
+
+      index.getAll(null).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, weightSort.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i].ssn, data[weightSort[i]].ssn,
+           "Got correct value");
+      }
+
+      index.getAll(data[weightSort[0]].weight).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, 1, "Got correct length");
+      is(event.target.result[0].ssn, data[weightSort[0]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(0);
+
+      index.getAll(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, weightSort.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i].ssn, data[weightSort[i]].ssn,
+           "Got correct value");
+      }
+
+      index.getAllKeys().onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, weightSort.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i], data[weightSort[i]].ssn,
+           "Got correct value");
+      }
+
+      index.getAllKeys(undefined).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, weightSort.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i], data[weightSort[i]].ssn,
+           "Got correct value");
+      }
+
+      index.getAllKeys(null).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, weightSort.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i], data[weightSort[i]].ssn,
+           "Got correct value");
+      }
+
+      index.getAllKeys(data[weightSort[0]].weight).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, 1, "Got correct length");
+      is(event.target.result[0], data[weightSort[0]].ssn, "Got correct result");
+
+      keyRange = IDBKeyRange.lowerBound(0);
+
+      index.getAllKeys(keyRange).onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      is(event.target.result instanceof Array, true, "Got an array");
+      is(event.target.result.length, weightSort.length, "Got correct length");
+      for (let i in event.target.result) {
+        is(event.target.result[i], data[weightSort[i]].ssn,
+           "Got correct value");
+      }
+
+      finishTest();
+      yield;
+    }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
--- a/dom/interfaces/events/nsIDOMAnimationEvent.idl
+++ b/dom/interfaces/events/nsIDOMAnimationEvent.idl
@@ -38,17 +38,17 @@
 #include "nsIDOMEvent.idl"
 
 /**
  * Animation events are defined in:
  * http://www.w3.org/TR/css3-animations/#animation-events-
  * http://dev.w3.org/csswg/css3-animations/#animation-events-
  */
 
-[scriptable, uuid(656d5f7c-c116-4154-8afd-f2c7458c7fb7)]
+[scriptable, uuid(cf76ec40-85fb-4623-b637-59a50af36d60)]
 interface nsIDOMAnimationEvent : nsIDOMEvent {
   readonly attribute DOMString          animationName;
   readonly attribute float              elapsedTime;
   void               initAnimationEvent(in DOMString typeArg,
                                         in boolean canBubbleArg,
                                         in boolean cancelableArg,
                                         in DOMString propertyNameArg,
                                         in float elapsedTimeArg);
--- a/dom/interfaces/events/nsIDOMBeforeUnloadEvent.idl
+++ b/dom/interfaces/events/nsIDOMBeforeUnloadEvent.idl
@@ -43,17 +43,17 @@
  * sent to handlers of the "beforeunload" event. This event is
  * non-standard. Interface derived from Microsoft IE's event
  * implementation.
  *
  * http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/reference/events.asp
  *
  */
 
-[scriptable, uuid(da19e9dc-dea2-4a1d-a958-9be375c9799c)]
+[scriptable, uuid(d286b454-0ed0-4e4b-b51d-3cf6d8c1299d)]
 interface nsIDOMBeforeUnloadEvent : nsIDOMEvent
 {
   /**
    * Attribute used to pass back a return value from a beforeunload
    * handler
    */
            attribute DOMString          returnValue;
 };
--- a/dom/interfaces/events/nsIDOMCloseEvent.idl
+++ b/dom/interfaces/events/nsIDOMCloseEvent.idl
@@ -40,17 +40,17 @@
 
 /**
  * The nsIDOMCloseEvent interface is the interface to the event
  * close on a WebSocket object.
  *
  * For more information on this interface, please see
  * http://dev.w3.org/html5/websockets/#closeevent
  */
-[scriptable, uuid(f83d9d6d-6c0c-418c-b12a-438e76d5866b)]
+[scriptable, uuid(07fb24d2-a102-41a2-bdaa-eb5e0d399eba)]
 interface nsIDOMCloseEvent : nsIDOMEvent
 {
   readonly attribute boolean wasClean;
   readonly attribute unsigned short code;
   readonly attribute DOMString reason;
 
   void initCloseEvent(in DOMString aType,
                       in boolean aCanBubble,
--- a/dom/interfaces/events/nsIDOMCommandEvent.idl
+++ b/dom/interfaces/events/nsIDOMCommandEvent.idl
@@ -32,17 +32,17 @@
  * 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 "nsIDOMEvent.idl"
 
-[scriptable, uuid(37fb1798-0f76-4870-af6f-0135b4d973c8)]
+[scriptable, uuid(cfdbc647-847f-4bd9-8595-5dd0a1592b7b)]
 interface nsIDOMCommandEvent : nsIDOMEvent
 {
   readonly attribute DOMString command;
 
   void initCommandEvent(in DOMString typeArg,
                         in boolean canBubbleArg,
                         in boolean canCancelArg,
                         in DOMString command);
--- a/dom/interfaces/events/nsIDOMCompositionEvent.idl
+++ b/dom/interfaces/events/nsIDOMCompositionEvent.idl
@@ -34,17 +34,17 @@
  * 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 "nsIDOMUIEvent.idl"
 
-[scriptable, uuid(bf01c23c-cf5f-4a8e-86a7-040c6e45d685)]
+[scriptable, uuid(9244a692-2827-4f98-8335-fff958d046fc)]
 interface nsIDOMCompositionEvent : nsIDOMUIEvent
 {
   readonly attribute DOMString data;
   readonly attribute DOMString locale;
 
   void initCompositionEvent(in DOMString typeArg,
                             in boolean canBubbleArg,
                             in boolean cancelableArg,
--- a/dom/interfaces/events/nsIDOMCustomEvent.idl
+++ b/dom/interfaces/events/nsIDOMCustomEvent.idl
@@ -33,17 +33,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMEvent.idl"
 interface nsIVariant;
 
-[scriptable, uuid(e93f84c5-e72f-4429-b797-f0c28d87890f)]
+[scriptable, uuid(eee65f98-bb6f-489c-b7e3-40e5f0c90645)]
 interface nsIDOMCustomEvent : nsIDOMEvent
 {
 
   readonly attribute nsIVariant detail;
 
   void initCustomEvent(in DOMString  typeArg, 
                        in boolean    canBubbleArg, 
                        in boolean    cancelableArg, 
--- a/dom/interfaces/events/nsIDOMDataContainerEvent.idl
+++ b/dom/interfaces/events/nsIDOMDataContainerEvent.idl
@@ -34,17 +34,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMEvent.idl"
 #include "nsIVariant.idl"
 
-[scriptable, uuid(3600d66c-b9ac-4c22-b39a-d64cce619921)]
+[scriptable, uuid(ac70e6bd-1f52-45ce-9d63-0aa650480ba1)]
 interface nsIDOMDataContainerEvent : nsIDOMEvent
 {
   /**
    * Return the data associated with the given key.
    *
    * @param  key  the key
    * @return      the data associated with the key
    */
--- a/dom/interfaces/events/nsIDOMDeviceMotionEvent.idl
+++ b/dom/interfaces/events/nsIDOMDeviceMotionEvent.idl
@@ -48,17 +48,17 @@ interface nsIDOMDeviceAcceleration : nsI
 interface nsIDOMDeviceRotationRate : nsISupports
 {
   readonly attribute double alpha;
   readonly attribute double beta;
   readonly attribute double gamma;
 };
 
 
-[scriptable, uuid(66E8D2C9-0826-444C-8FB8-E509BC9615F8)]
+[scriptable, uuid(b74dfd3f-0d16-4717-aaf7-8cadfe739532)]
 interface nsIDOMDeviceMotionEvent : nsIDOMEvent
 {
   void initDeviceMotionEvent(in DOMString eventTypeArg,
                              in boolean canBubbleArg,
                              in boolean cancelableArg,
                              in nsIDOMDeviceAcceleration acceleration,
                              in nsIDOMDeviceAcceleration accelerationIncludingGravity,
                              in nsIDOMDeviceRotationRate rotationRate,
--- a/dom/interfaces/events/nsIDOMDeviceOrientationEvent.idl
+++ b/dom/interfaces/events/nsIDOMDeviceOrientationEvent.idl
@@ -31,17 +31,17 @@
  * 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 "nsIDOMEvent.idl"
 
-[scriptable, uuid(daf2d570-0ecc-4aa0-aba4-26f60dfcba6a)]
+[scriptable, uuid(a9bd91fd-c0a7-44ed-9975-5a58faba3be3)]
 interface nsIDOMDeviceOrientationEvent : nsIDOMEvent
 {
   void initDeviceOrientationEvent(in DOMString eventTypeArg,
                                   in boolean canBubbleArg,
                                   in boolean cancelableArg,
                                   in double alpha,
                                   in double beta,
                                   in double gamma,
--- a/dom/interfaces/events/nsIDOMDragEvent.idl
+++ b/dom/interfaces/events/nsIDOMDragEvent.idl
@@ -35,17 +35,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 #include "nsIDOMMouseEvent.idl"
 
 interface nsIDOMDataTransfer;
 
-[scriptable, uuid(26b40393-c383-4e9a-977f-e8e1351926eb)]
+[scriptable, uuid(74fb5f01-e473-4302-93f5-6f74bdaaddf2)]
 interface nsIDOMDragEvent : nsIDOMMouseEvent
 {
   readonly attribute nsIDOMDataTransfer dataTransfer;
 
   void initDragEvent(in DOMString typeArg,
                      in boolean canBubbleArg,
                      in boolean cancelableArg,
                      in nsIDOMWindow aView,
--- a/dom/interfaces/events/nsIDOMEvent.idl
+++ b/dom/interfaces/events/nsIDOMEvent.idl
@@ -44,17 +44,17 @@ interface nsIDOMEventTarget;
 /**
  * The nsIDOMEvent interface is the primary datatype for all events in
  * the Document Object Model.
  *
  * For more information on this interface please see 
  * http://www.w3.org/TR/DOM-Level-2-Events/
  */
 
-[scriptable, uuid(548137e8-fd2c-48c4-8635-3033f7db79e0)]
+[scriptable, uuid(e85cff74-951f-45c1-be0c-89442ea2f500)]
 interface nsIDOMEvent : nsISupports
 {
   // PhaseType
   /**
    * The current event phase is the capturing phase.
    */
   const unsigned short      CAPTURING_PHASE                = 1;
 
@@ -169,9 +169,15 @@ interface nsIDOMEvent : nsISupports
   void                      initEvent(in DOMString eventTypeArg,
                                       in boolean canBubbleArg,
                                       in boolean cancelableArg);
 
   /**
    * Used to indicate whether preventDefault() has been called for this event.
    */
   readonly attribute boolean defaultPrevented;
+
+  /**
+   * Prevents other event listeners from being triggered and,
+   * unlike Event.stopPropagation() its effect is immediate.
+   */
+  void                       stopImmediatePropagation();
 };
--- a/dom/interfaces/events/nsIDOMHashChangeEvent.idl
+++ b/dom/interfaces/events/nsIDOMHashChangeEvent.idl
@@ -29,17 +29,17 @@
  * 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 "nsIDOMEvent.idl"
 
-[scriptable, uuid(34850f11-8b88-43ce-8d55-9aa8b18753bd)]
+[scriptable, uuid(9fc4785c-b769-40e5-8f79-586e01413afd)]
 interface nsIDOMHashChangeEvent : nsIDOMEvent
 {
   readonly attribute DOMString oldURL;
   readonly attribute DOMString newURL;
 
   void initHashChangeEvent(in DOMString typeArg,
                            in boolean canBubbleArg,
                            in boolean cancelableArg,
--- a/dom/interfaces/events/nsIDOMKeyEvent.idl
+++ b/dom/interfaces/events/nsIDOMKeyEvent.idl
@@ -34,17 +34,17 @@
  * 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 "nsIDOMUIEvent.idl"
 
-[scriptable, uuid(ffcfb88a-d1d1-40b9-96e1-e338211d3511)]
+[scriptable, uuid(def974c3-b491-481b-bc67-29174af4b26a)]
 interface nsIDOMKeyEvent : nsIDOMUIEvent
 {
   const unsigned long DOM_VK_CANCEL         = 0x03;
   const unsigned long DOM_VK_HELP           = 0x06;
   const unsigned long DOM_VK_BACK_SPACE     = 0x08;
   const unsigned long DOM_VK_TAB            = 0x09;
   const unsigned long DOM_VK_CLEAR          = 0x0C;
   const unsigned long DOM_VK_RETURN         = 0x0D;
--- a/dom/interfaces/events/nsIDOMMessageEvent.idl
+++ b/dom/interfaces/events/nsIDOMMessageEvent.idl
@@ -40,17 +40,17 @@
 
 /**
  * The nsIDOMMessageEvent interface is used for server-sent events and for
  * cross-domain messaging.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#messageevent
  */
-[scriptable, uuid(9ac4fa26-4d19-4f4e-807e-b30cd0dbe56a)]