merge m-c to fx-team
authorRob Campbell <rcampbell@mozilla.com>
Sat, 05 Nov 2011 10:11:34 -0300
changeset 79824 b78ada5e3d701458bd1d53bdec12dbc925d6dd41
parent 79823 49b98a76f55b8c3a1b85f397a3f4238c40f9e160 (current diff)
parent 79815 1a4de3faac0597de429a8f161a8b1a405d5efa75 (diff)
child 79825 e697147121b3258da64e1cea557bc43ded0987ce
child 79828 56e7358abdb3c4f8bb82b064bf749081b181f802
push id21426
push userrcampbell@mozilla.com
push dateSat, 05 Nov 2011 13:11:53 +0000
treeherdermozilla-central@b78ada5e3d70 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge 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.
+   */