Merge mozilla-central into electrolysis.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Mon, 01 Feb 2010 15:30:27 -0500
changeset 46633 95990e1a50e5647fc6c3b3dcc73ca92c518f8251
parent 46632 4cdebce7020a1a7142845773278f78e9512240fc (current diff)
parent 37800 d5e4ed59c4504d2b88b499952b80f73af8f8bae8 (diff)
child 46634 5bc1c351482222249da2cee9903794318ff50719
push idunknown
push userunknown
push dateunknown
milestone1.9.3a1pre
Merge mozilla-central into electrolysis.
browser/app/profile/firefox.js
build/automation.py.in
content/base/src/nsGkAtomList.h
content/events/src/Makefile.in
content/events/src/nsDOMEvent.cpp
content/events/src/nsDOMEvent.h
dom/plugins/PPluginInstance.ipdl
dom/plugins/PluginInstanceChild.cpp
dom/plugins/PluginInstanceParent.cpp
dom/plugins/PluginInstanceParent.h
dom/plugins/PluginModuleChild.cpp
ipc/chromium/src/base/debug_util.cc
ipc/chromium/src/chrome/common/ipc_channel_posix.cc
ipc/ipdl/Makefile.in
modules/plugin/base/src/nsPluginHost.cpp
modules/plugin/test/testplugin/nptest.cpp
toolkit/mozapps/extensions/test/unit/test_bug541420.js
toolkit/themes/pinstripe/global/wizardOverlay.css
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
toolkit/xre/nsSigHandlers.cpp
widget/src/windows/nsWindow.cpp
widget/src/windows/nsWindow.h
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -70,19 +70,17 @@
 
 /* For documentation of the accessibility architecture, 
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 nsIStringBundle *nsAccessNode::gStringBundle = 0;
 nsIStringBundle *nsAccessNode::gKeyStringBundle = 0;
 nsIDOMNode *nsAccessNode::gLastFocusedNode = 0;
-#ifdef DEBUG
-PRBool nsAccessNode::gIsAccessibilityActive = PR_FALSE;
-#endif
+
 PRBool nsAccessNode::gIsCacheDisabled = PR_FALSE;
 PRBool nsAccessNode::gIsFormFillEnabled = PR_FALSE;
 nsAccessNodeHashtable nsAccessNode::gGlobalDocAccessibleCache;
 
 nsApplicationAccessibleWrap *nsAccessNode::gApplicationAccessible = nsnull;
 
 nsIAccessibilityService*
 nsAccessNode::GetAccService()
@@ -225,17 +223,18 @@ NS_IMETHODIMP nsAccessNode::GetOwnerWind
   if (!docAccessible)
     return NS_ERROR_FAILURE; // This node or doc accessible is shut down
   return docAccessible->GetWindowHandle(aWindow);
 }
 
 already_AddRefed<nsApplicationAccessibleWrap>
 nsAccessNode::GetApplicationAccessible()
 {
-  NS_ASSERTION(gIsAccessibilityActive, "Accessibility wasn't initialized!");
+  NS_ASSERTION(!nsAccessibilityService::gIsShutdown,
+               "Accessibility wasn't initialized!");
 
   if (!gApplicationAccessible) {
     nsApplicationAccessibleWrap::PreCreate();
 
     gApplicationAccessible = new nsApplicationAccessibleWrap();
     if (!gApplicationAccessible)
       return nsnull;
 
@@ -251,19 +250,16 @@ nsAccessNode::GetApplicationAccessible()
   }
 
   NS_ADDREF(gApplicationAccessible);   // Addref because we're a getter
   return gApplicationAccessible;
 }
 
 void nsAccessNode::InitXPAccessibility()
 {
-  NS_ASSERTION(!gIsAccessibilityActive,
-               "Accessibility was initialized already!");
-
   nsCOMPtr<nsIStringBundleService> stringBundleService =
     do_GetService(NS_STRINGBUNDLE_CONTRACTID);
   if (stringBundleService) {
     // Static variables are released in ShutdownAllXPAccessibility();
     stringBundleService->CreateBundle(ACCESSIBLE_BUNDLE_URL, 
                                       &gStringBundle);
     stringBundleService->CreateBundle(PLATFORM_KEYS_BUNDLE_URL, 
                                       &gKeyStringBundle);
@@ -274,19 +270,16 @@ void nsAccessNode::InitXPAccessibility()
   gGlobalDocAccessibleCache.Init(4);
 
   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (prefBranch) {
     prefBranch->GetBoolPref("accessibility.disablecache", &gIsCacheDisabled);
     prefBranch->GetBoolPref("browser.formfill.enable", &gIsFormFillEnabled);
   }
 
-#ifdef DEBUG
-  gIsAccessibilityActive = PR_TRUE;
-#endif
   NotifyA11yInitOrShutdown(PR_TRUE);
 }
 
 void nsAccessNode::NotifyA11yInitOrShutdown(PRBool aIsInit)
 {
   nsCOMPtr<nsIObserverService> obsService =
     do_GetService("@mozilla.org/observer-service;1");
   NS_ASSERTION(obsService, "No observer service to notify of a11y init/shutdown");
@@ -299,33 +292,28 @@ void nsAccessNode::NotifyA11yInitOrShutd
 }
 
 void nsAccessNode::ShutdownXPAccessibility()
 {
   // Called by nsAccessibilityService::Shutdown()
   // which happens when xpcom is shutting down
   // at exit of program
 
-  NS_ASSERTION(gIsAccessibilityActive, "Accessibility was shutdown already!");
-
   NS_IF_RELEASE(gStringBundle);
   NS_IF_RELEASE(gKeyStringBundle);
   NS_IF_RELEASE(gLastFocusedNode);
 
   nsApplicationAccessibleWrap::Unload();
   ClearCache(gGlobalDocAccessibleCache);
 
   // Release gApplicationAccessible after everything else is shutdown
   // so we don't accidently create it again while tearing down root accessibles
   NS_IF_RELEASE(gApplicationAccessible);
   gApplicationAccessible = nsnull;  
 
-#ifdef DEBUG
-  gIsAccessibilityActive = PR_FALSE;
-#endif
   NotifyA11yInitOrShutdown(PR_FALSE);
 }
 
 PRBool
 nsAccessNode::IsDefunct()
 {
   if (!mDOMNode)
     return PR_TRUE;
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -197,19 +197,16 @@ protected:
      * Notify global nsIObserver's that a11y is getting init'd or shutdown
      */
     static void NotifyA11yInitOrShutdown(PRBool aIsInit);
 
     // Static data, we do our own refcounting for our static data
     static nsIStringBundle *gStringBundle;
     static nsIStringBundle *gKeyStringBundle;
 
-#ifdef DEBUG
-    static PRBool gIsAccessibilityActive;
-#endif
     static PRBool gIsCacheDisabled;
     static PRBool gIsFormFillEnabled;
 
     static nsAccessNodeHashtable gGlobalDocAccessibleCache;
 
 private:
   static nsApplicationAccessibleWrap *gApplicationAccessible;
 };
--- a/accessible/src/base/nsEventShell.cpp
+++ b/accessible/src/base/nsEventShell.cpp
@@ -103,17 +103,17 @@ nsCOMPtr<nsIDOMNode> nsEventShell::sEven
 
 nsAccEventQueue::nsAccEventQueue(nsDocAccessible *aDocument):
   mProcessingStarted(PR_FALSE), mDocument(aDocument)
 {
 }
 
 nsAccEventQueue::~nsAccEventQueue()
 {
-  NS_ASSERTION(mDocument, "Queue wasn't shut down!");
+  NS_ASSERTION(!mDocument, "Queue wasn't shut down!");
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccEventQueue: nsISupports and cycle collection
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccEventQueue)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccEventQueue)
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -196,57 +196,16 @@ nsHyperTextAccessible::GetStateInternal(
   GetChildCount(&childCount);
   if (childCount > 0) {
     *aExtraState |= nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT;
   }
 
   return NS_OK;
 }
 
-void
-nsHyperTextAccessible::CacheChildren()
-{
-  PRUint32 role;
-  GetRoleInternal(&role);
-  if (role != nsIAccessibleRole::ROLE_ENTRY &&
-      role != nsIAccessibleRole::ROLE_PASSWORD_TEXT) {
-    nsAccessible::CacheChildren();
-    return;
-  }
-
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
-  if (!editor) {
-    nsAccessible::CacheChildren();
-    return;
-  }
-
-  // Special case for text entry fields, go directly to editor's root for
-  // children.
-
-  nsCOMPtr<nsIDOMElement> editorRoot;
-  editor->GetRootElement(getter_AddRefs(editorRoot));
-  nsCOMPtr<nsIDOMNode> editorRootDOMNode = do_QueryInterface(editorRoot);
-  if (!editorRootDOMNode)
-    return;
-
-  nsAccessibleTreeWalker walker(mWeakShell, editorRootDOMNode, PR_TRUE);
-
-  walker.GetFirstChild();
-  while (walker.mState.accessible) {
-    nsRefPtr<nsAccessible> acc =
-      nsAccUtils::QueryObject<nsAccessible>(walker.mState.accessible);
-
-    mChildren.AppendElement(acc);
-    acc->SetParent(this);
-
-    walker.GetNextSibling();
-  }
-}
-
 // Substring must be entirely within the same text node
 nsIntRect nsHyperTextAccessible::GetBoundsForString(nsIFrame *aFrame, PRUint32 aStartRenderedOffset,
                                                     PRUint32 aEndRenderedOffset)
 {
   nsIntRect screenRect;
   NS_ENSURE_TRUE(aFrame, screenRect);
   if (aFrame->GetType() != nsAccessibilityAtoms::textFrame) {
     // XXX fallback for non-text frames, happens for bullets right now
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -151,19 +151,16 @@ public:
                                       PRInt32 aEndHTOffset,
                                       nsIDOMNode **aStartNode,
                                       PRInt32 *aStartOffset,
                                       nsIDOMNode **aEndNode,
                                       PRInt32 *aEndOffset);
 
 protected:
 
-  // nsAccessible
-  virtual void CacheChildren();
-
   // nsHyperTextAccessible
 
   /*
    * This does the work for nsIAccessibleText::GetText[At|Before|After]Offset
    * @param aType, eGetBefore, eGetAt, eGetAfter
    * @param aBoundaryType, char/word-start/word-end/line-start/line-end/paragraph/attribute
    * @param aOffset, offset into the hypertext to start from
    * @param *aStartOffset, the resulting start offset for the returned substring
--- a/accessible/src/msaa/nsAccessNodeWrap.cpp
+++ b/accessible/src/msaa/nsAccessNodeWrap.cpp
@@ -588,19 +588,16 @@ nsAccessNodeWrap::get_localInterface(
   *localInterface = static_cast<nsIAccessNode*>(this);
   NS_ADDREF_THIS();
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return S_OK;
 }
  
 void nsAccessNodeWrap::InitAccessibility()
 {
-  NS_ASSERTION(!gIsAccessibilityActive,
-               "Accessibility was initialized already!");
-
   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (prefBranch) {
     prefBranch->GetBoolPref("accessibility.disableenumvariant", &gIsEnumVariantSupportDisabled);
   }
 
   if (!gmUserLib) {
     gmUserLib =::LoadLibraryW(L"USER32.DLL");
   }
@@ -617,18 +614,16 @@ void nsAccessNodeWrap::InitAccessibility
   nsAccessNode::InitXPAccessibility();
 }
 
 void nsAccessNodeWrap::ShutdownAccessibility()
 {
   NS_IF_RELEASE(gTextEvent);
   ::DestroyCaret();
 
-  NS_ASSERTION(gIsAccessibilityActive, "Accessibility was shutdown already!");
-
   nsAccessNode::ShutdownXPAccessibility();
 }
 
 int nsAccessNodeWrap::FilterA11yExceptions(unsigned int aCode, EXCEPTION_POINTERS *aExceptionInfo)
 {
   if (aCode == EXCEPTION_ACCESS_VIOLATION) {
 #ifdef MOZ_CRASHREPORTER
     // MSAA swallows crashes (because it is COM-based)
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -867,27 +867,30 @@ nsXULToolbarSeparatorAccessible::GetStat
   }
 
   if (aExtraState)
     *aExtraState = 0;
 
   return NS_OK;
 }
 
-/**
-  * XUL Textfield
-  */
+////////////////////////////////////////////////////////////////////////////////
+// nsXULTextFieldAccessible
+////////////////////////////////////////////////////////////////////////////////
 
 nsXULTextFieldAccessible::nsXULTextFieldAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell) :
  nsHyperTextAccessibleWrap(aNode, aShell)
 {
 }
 
 NS_IMPL_ISUPPORTS_INHERITED3(nsXULTextFieldAccessible, nsAccessible, nsHyperTextAccessible, nsIAccessibleText, nsIAccessibleEditableText)
 
+////////////////////////////////////////////////////////////////////////////////
+// nsXULTextFieldAccessible: nsIAccessible
+
 NS_IMETHODIMP nsXULTextFieldAccessible::GetValue(nsAString& aValue)
 {
   PRUint32 state;
   nsresult rv = GetStateInternal(&state, nsnull);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (state & nsIAccessibleStates::STATE_PROTECTED)    // Don't return password text!
     return NS_ERROR_FAILURE;
@@ -898,16 +901,17 @@ NS_IMETHODIMP nsXULTextFieldAccessible::
   }
   nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mDOMNode));
   if (menuList) {
     return menuList->GetLabel(aValue);
   }
   return NS_ERROR_FAILURE;
 }
 
+// nsXULTextFieldAccessible protected
 already_AddRefed<nsIDOMNode> nsXULTextFieldAccessible::GetInputField()
 {
   nsIDOMNode *inputField = nsnull;
   nsCOMPtr<nsIDOMXULTextBoxElement> textBox = do_QueryInterface(mDOMNode);
   if (textBox) {
     textBox->GetInputField(&inputField);
     return inputField;
   }
@@ -1057,8 +1061,37 @@ nsXULTextFieldAccessible::GetAllowsAnonC
 NS_IMETHODIMP nsXULTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor)
 {
   *aEditor = nsnull;
   nsCOMPtr<nsIDOMNode> inputField = GetInputField();
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(inputField));
   NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE);
   return editableElt->GetEditor(aEditor);
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// nsXULTextFieldAccessible: nsAccessible protected
+
+void
+nsXULTextFieldAccessible::CacheChildren()
+{
+  // Create child accessibles for native anonymous content of underlying HTML
+  // input element.
+  nsCOMPtr<nsIDOMNode> inputNode(GetInputField());
+  nsCOMPtr<nsIContent> inputContent(do_QueryInterface(inputNode));
+  if (!inputContent)
+    return;
+
+  nsAccessibleTreeWalker walker(mWeakShell, inputNode, PR_FALSE);
+  walker.mState.frame = inputContent->GetPrimaryFrame();
+
+  walker.GetFirstChild();
+  while (walker.mState.accessible) {
+    nsRefPtr<nsAccessible> acc =
+      nsAccUtils::QueryObject<nsAccessible>(walker.mState.accessible);
+
+    mChildren.AppendElement(acc);
+
+    acc->SetParent(this);
+
+    walker.GetNextSibling();
+  }
+}
--- a/accessible/src/xul/nsXULFormControlAccessible.h
+++ b/accessible/src/xul/nsXULFormControlAccessible.h
@@ -234,14 +234,18 @@ public:
 
   // nsAccessible
   virtual nsresult GetARIAState(PRUint32 *aState, PRUint32 *aExtraState);
   virtual nsresult GetRoleInternal(PRUint32 *aRole);
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
   virtual PRBool GetAllowsAnonChildAccessibles();
 
 protected:
+  // nsAccessible
+  virtual void CacheChildren();
+
+  // nsXULTextFieldAccessible
   already_AddRefed<nsIDOMNode> GetInputField();
 };
 
 
 #endif  
 
--- a/accessible/tests/mochitest/events/test_scroll.xul
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -1,12 +1,19 @@
 <?xml version="1.0"?>
 
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
+
+<!-- Firefox tabbrowser -->
+<?xml-stylesheet href="chrome://browser/content/browser.css"
+                 type="text/css"?>
+<!-- Seamonkey tabbrowser -->
+<?xml-stylesheet href="chrome://navigator/content/navigator.css"
+                 type="text/css"?>
+
 
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript" 
           src="chrome://mochikit/content/MochiKit/packed.js" />
--- a/accessible/tests/mochitest/name_nsRootAcc_wnd.xul
+++ b/accessible/tests/mochitest/name_nsRootAcc_wnd.xul
@@ -1,11 +1,17 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
+
+<!-- Firefox tabbrowser -->
+<?xml-stylesheet href="chrome://browser/content/browser.css"
+                 type="text/css"?>
+<!-- Seamonkey tabbrowser -->
+<?xml-stylesheet href="chrome://navigator/content/navigator.css"
+                 type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript">
   <![CDATA[
     var gOpenerWnd = window.opener.wrappedJSObject;
 
     function ok(aCond, aMsg) {
--- a/accessible/tests/mochitest/test_name.xul
+++ b/accessible/tests/mochitest/test_name.xul
@@ -142,20 +142,20 @@
 
       //////////////////////////////////////////////////////////////////////////
       // Name from the @title attribute of <toolbaritem/> (original bug 237249).
 
       // Direct child of toolbaritem.
       var textboxAcc = testName("toolbaritem_textbox", "ooospspss");
 
       // Element from anonymous content of direct child of toolbaritem.
-      var dropmarkerAcc = textboxAcc.lastChild;
-      testRole(dropmarkerAcc, ROLE_PUSHBUTTON);
-      is(dropmarkerAcc.name, "ooospspss",
-         "Wrong name for dropmarker of autocomplete textbox 'toolbaritem_textbox'.");
+      var entryAcc = textboxAcc.firstChild;
+      testRole(entryAcc, ROLE_ENTRY);
+      is(entryAcc.name, "ooospspss",
+         "Wrong name for text entry of autocomplete textbox 'toolbaritem_textbox'.");
 
       // Child from subtree of toolbaritem.
       testName("toolbaritem_hboxbutton", "ooospspss");
 
 
       //////////////////////////////////////////////////////////////////////////
       // Name from children
 
@@ -331,31 +331,31 @@
   <!-- name from children, hidden children -->
   <vbox role="listbox" tabindex="0">
     <hbox id="lb_opt1_children_hidden" role="option" tabindex="0">
       <description>i am visible</description>
       <description style="display:none">i am hidden</description>
     </hbox>
   </vbox>
 
-  </vbox>
-  </hbox>
-
   <!-- bug 441991; create name from other menuitem label listitem's own label -->
   <hbox>
-  <listbox>
-  <listitem id="li_labelledby"
-            label="The moment the event starts"
-            aria-labelledby="menuitem-DISPLAY li_labelledby"/>
-  </listbox>
-  <menulist>
-    <menupopup>
-      <menuitem id="menuitem-DISPLAY"
-                value="DISPLAY"
-                label="Show an Alert"/>
-      <menuitem id="menuitem-EMAIL"
-                value="EMAIL"
-                label="Send an E-mail"/>
-    </menupopup>
-  </menulist>
-  </hbox>  
+    <listbox>
+    <listitem id="li_labelledby"
+              label="The moment the event starts"
+              aria-labelledby="menuitem-DISPLAY li_labelledby"/>
+    </listbox>
+    <menulist>
+      <menupopup>
+        <menuitem id="menuitem-DISPLAY"
+                  value="DISPLAY"
+                  label="Show an Alert"/>
+        <menuitem id="menuitem-EMAIL"
+                  value="EMAIL"
+                  label="Send an E-mail"/>
+      </menupopup>
+    </menulist>
+  </hbox>
+
+  </vbox> <!-- close tests area -->
+  </hbox> <!-- close main area -->
 </window>
 
--- a/accessible/tests/mochitest/tree/Makefile.in
+++ b/accessible/tests/mochitest/tree/Makefile.in
@@ -61,12 +61,13 @@ include $(topsrcdir)/config/rules.mk
 		test_menu.xul \
 		test_select.html \
 		test_tabbox.xul \
 		test_tabbrowser.xul \
 		test_table.html \
 		test_tree.xul \
 		test_txtcntr.html \
 		test_txtctrl.html \
+		test_txtctrl.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/tree/test_combobox.xul
+++ b/accessible/tests/mochitest/tree/test_combobox.xul
@@ -40,16 +40,48 @@
               }
             ]
           }
         ]
       };
       testAccessibleTree("menulist", accTree);
 
       //////////////////////////////////////////////////////////////////////////
+      // editable menulist
+
+      accTree = {
+        role: ROLE_COMBOBOX,
+        children: [
+          {
+            role: ROLE_ENTRY,
+            children: []
+          },
+          {
+            role: ROLE_COMBOBOX_LIST, // context menu
+            children: []
+          },
+          {
+            role: ROLE_PUSHBUTTON, // dropmarker
+          },
+          {
+            role: ROLE_COMBOBOX_LIST, // option list
+            children: [
+              {
+                role: ROLE_COMBOBOX_OPTION
+              },
+              {
+                role: ROLE_COMBOBOX_OPTION
+              }
+            ]
+          }
+        ]
+      };
+      testAccessibleTree("menulist2", accTree);
+
+      //////////////////////////////////////////////////////////////////////////
       // textbox@type=autocomplete #1 (history)
 
       accTree = {
         role: ROLE_AUTOCOMPLETE,
         children: [
           {
             role: ROLE_ENTRY,
             children: [
@@ -117,16 +149,23 @@
     <vbox flex="1">
       <menulist id="menulist">
         <menupopup>
           <menuitem label="item"/>
           <menuitem label="item"/>
         </menupopup>
       </menulist>
 
+      <menulist id="menulist2" editable="true">
+        <menupopup>
+          <menuitem label="item"/>
+          <menuitem label="item"/>
+        </menupopup>
+      </menulist>
+
       <textbox id="autocomplete" type="autocomplete"
                autocompletesearch="history"
                value="http://localhost:8888/redirect-a11y.html"/>
 
       <textbox id="autocomplete2" type="autocomplete">
         <menupopup>
           <menuitem label="item1"/>
         </menupopup>
--- a/accessible/tests/mochitest/tree/test_groupbox.xul
+++ b/accessible/tests/mochitest/tree/test_groupbox.xul
@@ -18,17 +18,17 @@
 
   <script type="application/javascript">
   <![CDATA[
     ////////////////////////////////////////////////////////////////////////////
     // Test
 
     function doTest()
     {
-      accTree = {
+      var accTree = {
         role: ROLE_GROUPING,
         children: [
           {
             role: ROLE_LABEL,
             children: [ ]
           },
           {
             role: ROLE_CHECKBUTTON,
--- a/accessible/tests/mochitest/tree/test_txtcntr.html
+++ b/accessible/tests/mochitest/tree/test_txtcntr.html
@@ -62,28 +62,61 @@
               }
             ]
           }
         ]
       };
 
       testAccessibleTree("c3", accTree);
 
+      // contentEditable div
+      accTree = {
+        role: ROLE_SECTION,
+        children: [
+          {
+            role: ROLE_TEXT_LEAF,
+            name: "helllo "
+          },
+          {
+            role: ROLE_PARAGRAPH,
+            children: [
+              {
+                role: ROLE_TEXT_LEAF,
+                name: "blabla"
+              }
+            ]
+          },
+          {
+            role: ROLE_TEXT_LEAF,
+            name: "hello "
+          }
+        ]
+      };
+
+      testAccessibleTree("c4", accTree);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
      title="overflowed content doesn't expose child text accessibles"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306">Mozilla Bug 489306</a>
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306">
+    Mozilla Bug 489306
+  </a><br>
+  <a target="_blank"
+     title="Create child accessibles for text controls from native anonymous content"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824">
+    Mozilla Bug 542824
+  </a><br>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="c1" style="width: 100px; height: 100px; overflow: auto;">
     1hellohello 2hellohello 3hellohello 4hellohello 5hellohello 6hellohello 7hellohello
   </div>
@@ -93,10 +126,13 @@
   <div id="c3">
     Hello1<br>
     Hello2<hr>
     Hello3
     <p>
       Hello4
     </p>
   </div>
+  <div id="c4" contentEditable="true">
+    helllo <p>blabla</p> hello
+  </div>
 </body>
 </html>
--- a/accessible/tests/mochitest/tree/test_txtctrl.html
+++ b/accessible/tests/mochitest/tree/test_txtctrl.html
@@ -67,36 +67,58 @@
           {
             role: ROLE_WHITESPACE
           }
         ]
       };
 
       testAccessibleTree("txc3", accTree);
 
+      // input@type="password"
+      accTree = {
+        role: ROLE_PASSWORD_TEXT,
+        children: [
+          {
+            role: ROLE_TEXT_LEAF,
+            children: []
+          }
+        ]
+      };
+
+      testAccessibleTree("txc4", accTree);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
      title="overflowed content doesn't expose child text accessibles"
-     href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306">Mozilla Bug 489306</a>
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306">
+    Mozilla Bug 489306
+  </a><br>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824"
+     title="Create child accessibles for text controls from native anonymous content">
+    Mozilla Bug 542824
+  </a>
+
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="txc1" contentEditable="true">
     1hellohello
   </div>
   <input id="txc2" value="hello">
   <textarea id="txc3">
     hello1
     hello2
   </textarea>
+  <input id="txc4" type="password" value="hello">
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_txtctrl.xul
@@ -0,0 +1,133 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Accessible XUL textbox and textarea hierarchy tests">
+
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/common.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/role.js" />
+
+  <script type="application/javascript">
+  <![CDATA[
+    ////////////////////////////////////////////////////////////////////////////
+    // Test
+
+    function doTest()
+    {
+      // textbox
+      var accTree = {
+        role: ROLE_ENTRY,
+        children: [
+          {
+            role: ROLE_TEXT_LEAF,
+            children: []
+          }
+        ]
+      };
+      testAccessibleTree("txc1", accTree);
+
+      // number textbox
+      testAccessibleTree("txc2", accTree);
+
+      // search textbox
+      testAccessibleTree("txc3", accTree);
+
+      // timed textbox
+      testAccessibleTree("txc4", accTree);
+
+      // password textbox
+      accTree = {
+        role: ROLE_PASSWORD_TEXT,
+        children: [
+          {
+            role: ROLE_TEXT_LEAF,
+            children: []
+          }
+        ]
+      };
+
+      testAccessibleTree("txc5", accTree);
+
+      // multiline textbox
+      accTree = {
+        role: ROLE_ENTRY,
+        children: [
+          {
+            role: ROLE_TEXT_LEAF,
+            children: []
+          },
+          {
+            role: ROLE_WHITESPACE,
+            children: []
+          }
+        ]
+      };
+
+      testAccessibleTree("txc6", accTree);
+
+      // autocomplete textbox
+      accTree = {
+        role: ROLE_AUTOCOMPLETE,
+        children: [
+          {
+            role: ROLE_ENTRY,
+            children: [
+              {
+                role: ROLE_TEXT_LEAF,
+                children: []
+              }
+            ]
+          },
+          {
+            role: ROLE_COMBOBOX_LIST,
+            children: []
+          }
+        ]
+      };
+
+      testAccessibleTree("txc7", accTree);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  ]]>
+  </script>
+
+  <hbox flex="1" style="overflow: auto;">
+    <body xmlns="http://www.w3.org/1999/xhtml">
+      <a target="_blank"
+         href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824"
+         title="Create child accessibles for text controls from native anonymous content">
+        Mozilla Bug 542824
+      </a><br/>
+      <p id="display"></p>
+      <div id="content" style="display: none">
+      </div>
+      <pre id="test">
+      </pre>
+    </body>
+
+    <vbox flex="1">
+      <textbox id="txc1" value="hello"/>
+      <textbox id="txc2" type="number" value="44"/>
+      <textbox id="txc3" type="search" value="hello"/>
+      <textbox id="txc4" type="timed" value="hello"/>
+      <textbox id="txc5" type="password" value="hello"/>
+      <textbox id="txc6" multiline="true" value="hello"/>
+      <textbox id="txc7" type="autocomplete" value="hello"/>
+    </vbox>
+  </hbox>
+
+</window>
+
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4516,20 +4516,20 @@ nsBrowserAccess.prototype = {
         }
 
         if (!win) {
           // we couldn't find a suitable window, a new one needs to be opened.
           return null;
         }
 
         if (isExternal && (!aURI || aURI.spec == "about:blank")) {
-          win.BrowserOpenTab(); // this also focuses the location bar
-          win.focus();
-          newWindow = win.content;
-          break;
+          win.BrowserOpenTab(); // this also focuses the location bar
+          win.focus();
+          newWindow = win.content;
+          break;
         }
 
         let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
         let referrer = aOpener ? makeURI(aOpener.location.href) : null;
 
         // If this is an external load, we need to load a blank tab first,
         // because loadflags can't be passed to loadOneTab.
         let loadBlankFirst = !aURI || isExternal;
--- a/browser/components/places/tests/browser/browser_library_views_liveupdate.js
+++ b/browser/components/places/tests/browser/browser_library_views_liveupdate.js
@@ -41,16 +41,20 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 var gLibrary = null;
 
 function test() {
   waitForExplicitFinish();
+  // This test takes quite some time, and timeouts frequently, so we require
+  // more time to run.
+  // See Bug 525610.
+  requestLongerTimeout(2);
 
   // Sanity checks.
   ok(PlacesUtils, "PlacesUtils in context");
   ok(PlacesUIUtils, "PlacesUIUtils in context");
 
   // Open Library, we will check the left pane.
   var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
            getService(Ci.nsIWindowWatcher);
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -1392,17 +1392,17 @@ SessionStoreService.prototype = {
       try {
         var principal = SecuritySvc.getCodebasePrincipal(uri);
 
         // Using getSessionStorageForPrincipal instead of getSessionStorageForURI
         // just to be able to pass aCreate = false, that avoids creation of the
         // sessionStorage object for the page earlier than the page really
         // requires it. It was causing problems while accessing a storage when
         // a page later changed its domain.
-        storage = aDocShell.getSessionStorageForPrincipal(principal, false);
+        storage = aDocShell.getSessionStorageForPrincipal(principal, "", false);
         if (storage)
           storageItemCount = storage.length;
       }
       catch (ex) { /* sessionStorage might throw if it's turned off, see bug 458954 */ }
       if (storageItemCount == 0)
         continue;
 
       let data = storageData[domain] = {};
@@ -2249,17 +2249,17 @@ SessionStoreService.prototype = {
    * @param aStorageData
    *        Storage data to be restored
    * @param aDocShell
    *        A tab's docshell (containing the sessionStorage)
    */
   _deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
     for (let url in aStorageData) {
       let uri = IOSvc.newURI(url, null, null);
-      let storage = aDocShell.getSessionStorageForURI(uri);
+      let storage = aDocShell.getSessionStorageForURI(uri, "");
       for (let key in aStorageData[url]) {
         try {
           storage.setItem(key, aStorageData[url][key]);
         }
         catch (ex) { Cu.reportError(ex); } // throws e.g. for URIs that can't have sessionStorage
       }
     }
   },
--- a/browser/components/sessionstore/test/browser/browser_354894.js
+++ b/browser/components/sessionstore/test/browser/browser_354894.js
@@ -123,16 +123,19 @@ function browserWindowsCount(expected, m
   info(state);
   is(JSON.parse(state).windows.length, expected[1], msg + " (getBrowserState)");
 }
 
 function test() {
   browserWindowsCount(1, "Only one browser window should be open initially");
 
   waitForExplicitFinish();
+  // This test takes some time to run, and it could timeout randomly.
+  // So we require a longer timeout. See bug 528219.
+  requestLongerTimeout(2);
 
   // Some urls that might be opened in tabs and/or popups
   // Do not use about:blank:
   // That one is reserved for special purposes in the tests
   const TEST_URLS = ["about:mozilla", "about:buildconfig"];
 
   // Number of -request notifications to except
   // remember to adjust when adding new tests
--- a/browser/fuel/src/fuelApplication.js
+++ b/browser/fuel/src/fuelApplication.js
@@ -38,74 +38,64 @@
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 //=================================================
 // Singleton that holds services and utilities
 var Utilities = {
-  _bookmarks : null,
   get bookmarks() {
-    if (!this._bookmarks) {
-      this._bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-                        getService(Ci.nsINavBookmarksService);
-    }
-    return this._bookmarks;
+    let bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+                    getService(Ci.nsINavBookmarksService);
+    this.__defineGetter__("bookmarks", function() bookmarks);
+    return this.bookmarks;
   },
 
-  _livemarks : null,
   get livemarks() {
-    if (!this._livemarks) {
-      this._livemarks = Cc["@mozilla.org/browser/livemark-service;2"].
-                        getService(Ci.nsILivemarkService);
-    }
-    return this._livemarks;
+    let livemarks = Cc["@mozilla.org/browser/livemark-service;2"].
+                    getService(Ci.nsILivemarkService);
+    this.__defineGetter__("livemarks", function() livemarks);
+    return this.livemarks;
   },
 
-  _annotations : null,
   get annotations() {
-    if (!this._annotations) {
-      this._annotations = Cc["@mozilla.org/browser/annotation-service;1"].
-                          getService(Ci.nsIAnnotationService);
-    }
-    return this._annotations;
+    let annotations = Cc["@mozilla.org/browser/annotation-service;1"].
+                      getService(Ci.nsIAnnotationService);
+    this.__defineGetter__("annotations", function() annotations);
+    return this.annotations;
   },
 
-  _history : null,
   get history() {
-    if (!this._history) {
-      this._history = Cc["@mozilla.org/browser/nav-history-service;1"].
-                      getService(Ci.nsINavHistoryService);
-    }
-    return this._history;
+    let history = Cc["@mozilla.org/browser/nav-history-service;1"].
+                  getService(Ci.nsINavHistoryService);
+    this.__defineGetter__("history", function() history);
+    return this.history;
   },
 
-  _windowMediator : null,
   get windowMediator() {
-    if (!this._windowMediator) {
-      this._windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
-                             getService(Ci.nsIWindowMediator);
-    }
-    return this._windowMediator;
+    let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
+                         getService(Ci.nsIWindowMediator);
+    this.__defineGetter__("windowMediator", function() windowMediator);
+    return this.windowMediator;
   },
 
   makeURI : function(aSpec) {
     if (!aSpec)
       return null;
     var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
     return ios.newURI(aSpec, null, null);
   },
 
   free : function() {
-    this._bookmarks = null;
-    this._livemarks = null;
-    this._annotations = null;
-    this._history = null;
-    this._windowMediator = null;
+    this.bookmarks = null;
+    this.livemarks = null;
+    this.annotations = null;
+    this.history = null;
+    this.windowMediator = null;
   }
 };
 
 
 //=================================================
 // Window implementation
 function Window(aWindow) {
   this._window = aWindow;
@@ -682,39 +672,41 @@ Application.prototype = {
   classDescription: "Application",
   classID:          Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"),
   contractID:       "@mozilla.org/fuel/application;1",
 
   // redefine the default factory for XPCOMUtils
   _xpcom_factory: ApplicationFactory,
 
   // for nsISupports
-  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIApplication, Ci.extIApplication, Ci.nsIObserver, Ci.nsIClassInfo]),
+  QueryInterface : XPCOMUtils.generateQI([Ci.fuelIApplication, Ci.extIApplication,
+                                          Ci.nsIObserver, Ci.nsIClassInfo]),
 
   getInterfaces : function app_gi(aCount) {
-    var interfaces = [Ci.fuelIApplication, Ci.extIApplication, Ci.nsIObserver, Ci.nsIClassInfo];
+    var interfaces = [Ci.fuelIApplication, Ci.extIApplication, Ci.nsIObserver,
+                      Ci.nsIClassInfo];
     aCount.value = interfaces.length;
     return interfaces;
   },
 
   // for nsIObserver
   observe: function app_observe(aSubject, aTopic, aData) {
     // Call the extApplication version of this function first
     this.__proto__.__proto__.observe.call(this, aSubject, aTopic, aData);
     if (aTopic == "xpcom-shutdown") {
+      this._obs.removeObserver(this, "xpcom-shutdown");
       this._bookmarks = null;
       Utilities.free();
     }
   },
 
   get bookmarks() {
-    if (this._bookmarks == null)
-      this._bookmarks = new BookmarkRoots();
-
-    return this._bookmarks;
+    let bookmarks = new BookmarkRoots();
+    this.__defineGetter__("bookmarks", function() bookmarks);
+    return this.bookmarks;
   },
 
   get windows() {
     var win = [];
     var enum = Utilities.windowMediator.getEnumerator("navigator:browser");
 
     while (enum.hasMoreElements())
       win.push(new Window(enum.getNext()));
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1128,17 +1128,16 @@ nsEventStateManager::PreHandleEvent(nsPr
     break;
 #ifdef CLICK_HOLD_CONTEXT_MENUS
   case NS_DRAGDROP_GESTURE:
     // an external drag gesture event came in, not generated internally
     // by Gecko. Make sure we get rid of the click-hold timer.
     KillClickHoldTimer();
     break;
 #endif
-  case NS_DRAGDROP_DROP:
   case NS_DRAGDROP_OVER:
     // NS_DRAGDROP_DROP is fired before NS_DRAGDROP_DRAGDROP so send
     // the enter/exit events before NS_DRAGDROP_DROP.
     GenerateDragDropEnterExit(aPresContext, (nsGUIEvent*)aEvent);
     break;
 
   case NS_KEY_PRESS:
     {
@@ -3624,17 +3623,16 @@ nsEventStateManager::GenerateDragDropEnt
         FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_ENTER,
                             lastContent, targetContent, mCurrentTarget);
 
         mLastDragOverFrame = mCurrentTarget;
       }
     }
     break;
 
-  case NS_DRAGDROP_DROP:
   case NS_DRAGDROP_EXIT:
     {
       //This is actually the window mouse exit event.
       if ( mLastDragOverFrame ) {
         nsCOMPtr<nsIContent> lastContent;
         mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
 
         FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_LEAVE_SYNTH,
--- a/content/html/content/test/test_bug353415-2.html
+++ b/content/html/content/test/test_bug353415-2.html
@@ -20,17 +20,17 @@
 <option value="1">1</option>
 <option value="2" selected>2</option>
 <option value="3">3</option>
 <option value="4">4</option>
 </select>
 <input name="field8" value="8">
 <input name="field9" value="9">
 <input type="image" name="field10">
-<label name="field11">
+<label name="field11"></label>
 <input name="field12">
 <input type="button" name="field13" value="button">
 <input type="hidden" name="field14" value="14">
 </td>
 <input type="text" name="field1-2" value="teststring"><br>
 <input type="radio" name="field2-2" value="0" checked> 0
 <input type="radio" name="field3-2" value="1"> 1<br>
 <input type="checkbox" name="field4-2" value="1" checked> 1
@@ -40,17 +40,17 @@
 <option value="1">1</option>
 <option value="2" selected>2</option>
 <option value="3">3</option>
 <option value="4">4</option>
 </select>
 <input name="field8-2" value="8">
 <input name="field9-2" value="9">
 <input type="image" name="field10-2">
-<label name="field11-2">
+<label name="field11-2"></label>
 <input name="field12-2">
 <input type="button" name="field13-2" value="button">
 <input type="hidden" name="field14-2" value="14">
 </tr>
 </table>
 </form>
 <script>
 	document.forms[0].submit();
--- a/content/smil/SMILBoolType.cpp
+++ b/content/smil/SMILBoolType.cpp
@@ -51,26 +51,26 @@ SMILBoolType::Init(nsSMILValue& aValue) 
   aValue.mU.mBool = PR_FALSE;
   aValue.mType = this;
   return NS_OK;
 }
 
 void
 SMILBoolType::Destroy(nsSMILValue& aValue) const
 {
-  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
   aValue.mU.mBool = PR_FALSE;
   aValue.mType = &nsSMILNullType::sSingleton;
 }
 
 nsresult
 SMILBoolType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
 {
-  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types.");
-  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
   aDest.mU.mBool = aSrc.mU.mBool;
   return NS_OK;
 }
 
 nsresult
 SMILBoolType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
                   PRUint32 aCount) const
 {
@@ -94,14 +94,14 @@ nsresult
 SMILBoolType::Interpolate(const nsSMILValue& aStartVal,
                           const nsSMILValue& aEndVal,
                           double aUnitDistance,
                           nsSMILValue& aResult) const
 {
   NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
       "Trying to interpolate different types");
   NS_PRECONDITION(aStartVal.mType == this,
-      "Unexpected types for interpolation.");
-  NS_PRECONDITION(aResult.mType   == this, "Unexpected result type.");
+      "Unexpected types for interpolation");
+  NS_PRECONDITION(aResult.mType   == this, "Unexpected result type");
   return NS_ERROR_FAILURE; // bool values do not interpolate
 }
 
 } // namespace mozilla
--- a/content/smil/SMILEnumType.cpp
+++ b/content/smil/SMILEnumType.cpp
@@ -51,26 +51,26 @@ SMILEnumType::Init(nsSMILValue& aValue) 
   aValue.mU.mUint = 0;
   aValue.mType = this;
   return NS_OK;
 }
 
 void
 SMILEnumType::Destroy(nsSMILValue& aValue) const
 {
-  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
   aValue.mU.mUint = 0;
   aValue.mType = &nsSMILNullType::sSingleton;
 }
 
 nsresult
 SMILEnumType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
 {
-  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types.");
-  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
   aDest.mU.mUint = aSrc.mU.mUint;
   return NS_OK;
 }
 
 nsresult
 SMILEnumType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
                   PRUint32 aCount) const
 {
@@ -94,14 +94,14 @@ nsresult
 SMILEnumType::Interpolate(const nsSMILValue& aStartVal,
                           const nsSMILValue& aEndVal,
                           double aUnitDistance,
                           nsSMILValue& aResult) const
 {
   NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
       "Trying to interpolate different types");
   NS_PRECONDITION(aStartVal.mType == this,
-      "Unexpected types for interpolation.");
-  NS_PRECONDITION(aResult.mType   == this, "Unexpected result type.");
+      "Unexpected types for interpolation");
+  NS_PRECONDITION(aResult.mType   == this, "Unexpected result type");
   return NS_ERROR_FAILURE; // enum values do not interpolate
 }
 
 } // namespace mozilla
--- a/content/smil/nsSMILAnimationController.cpp
+++ b/content/smil/nsSMILAnimationController.cpp
@@ -77,17 +77,17 @@ nsSMILAnimationController::~nsSMILAnimat
 {
   if (mTimer) {
     mTimer->Cancel();
     mTimer = nsnull;
   }
 
   NS_ASSERTION(mAnimationElementTable.Count() == 0,
                "Animation controller shouldn't be tracking any animation"
-               " elements when it dies.");
+               " elements when it dies");
 }
 
 nsSMILAnimationController* NS_NewSMILAnimationController(nsIDocument* aDoc)
 {
   nsSMILAnimationController* animationController =
     new nsSMILAnimationController();
   NS_ENSURE_TRUE(animationController, nsnull);
 
@@ -224,17 +224,17 @@ nsSMILAnimationController::Notify(nsITim
 
   controller->Sample();
 }
 
 nsresult
 nsSMILAnimationController::StartTimer()
 {
   NS_ENSURE_TRUE(mTimer, NS_ERROR_FAILURE);
-  NS_ASSERTION(mPauseState == 0, "Starting timer but controller is paused.");
+  NS_ASSERTION(mPauseState == 0, "Starting timer but controller is paused");
 
   // Run the first sample manually
   Sample();
 
   //
   // XXX Make this self-tuning. Sounds like control theory to me and not
   // something I'm familiar with.
   //
--- a/content/smil/nsSMILAnimationFunction.cpp
+++ b/content/smil/nsSMILAnimationFunction.cpp
@@ -300,17 +300,17 @@ nsSMILAnimationFunction::ComposeResult(c
   }
 }
 
 PRInt8
 nsSMILAnimationFunction::CompareTo(const nsSMILAnimationFunction* aOther) const
 {
   NS_ENSURE_TRUE(aOther, 0);
 
-  NS_ASSERTION(aOther != this, "Trying to compare to self.");
+  NS_ASSERTION(aOther != this, "Trying to compare to self");
 
   // Inactive animations sort first
   if (!IsActiveOrFrozen() && aOther->IsActiveOrFrozen())
     return -1;
 
   if (IsActiveOrFrozen() && !aOther->IsActiveOrFrozen())
     return 1;
 
@@ -371,23 +371,23 @@ nsSMILAnimationFunction::InterpolateResu
   nsresult rv = NS_OK;
   const nsSMILTime& dur = mSimpleDuration.GetMillis();
 
   // Sanity Checks
   NS_ABORT_IF_FALSE(mSampleTime >= 0.0f, "Sample time should not be negative");
   NS_ABORT_IF_FALSE(dur >= 0.0f, "Simple duration should not be negative");
 
   if (mSampleTime >= dur || mSampleTime < 0.0f) {
-    NS_ERROR("Animation sampled outside interval.");
+    NS_ERROR("Animation sampled outside interval");
     return NS_ERROR_FAILURE;
   }
 
   if ((!IsToAnimation() && aValues.Length() < 2) ||
       (IsToAnimation()  && aValues.Length() != 1)) {
-    NS_ERROR("Unexpected number of values.");
+    NS_ERROR("Unexpected number of values");
     return NS_ERROR_FAILURE;
   }
   // End Sanity Checks
 
   double fTime = double(mSampleTime);
   double fDur = double(dur);
 
   // Get the normalised progress through the simple duration
@@ -439,18 +439,18 @@ nsSMILAnimationFunction::InterpolateResu
                                          (aValues.Length() - 1));
         from = &aValues[index];
         to = &aValues[index + 1];
         intervalProgress = simpleProgress * (aValues.Length() - 1) - index;
         ScaleIntervalProgress(intervalProgress, index, aValues.Length() - 1);
       }
     }
     if (NS_SUCCEEDED(rv)) {
-      NS_ABORT_IF_FALSE(from, "NULL from-value during interpolation.");
-      NS_ABORT_IF_FALSE(to, "NULL to-value during interpolation.");
+      NS_ABORT_IF_FALSE(from, "NULL from-value during interpolation");
+      NS_ABORT_IF_FALSE(to, "NULL to-value during interpolation");
       NS_ABORT_IF_FALSE(0.0f <= intervalProgress && intervalProgress < 1.0f,
                       "Interval progress should be in the range [0, 1)");
       rv = from->Interpolate(*to, intervalProgress, aResult);
     }
   }
 
   // Discrete-CalcMode case
   // Note: If interpolation failed (isn't supported for this type), the SVG
@@ -497,17 +497,17 @@ nsSMILAnimationFunction::AccumulateResul
 nsresult
 nsSMILAnimationFunction::ComputePacedPosition(const nsSMILValueArray& aValues,
                                               double aSimpleProgress,
                                               double& aIntervalProgress,
                                               const nsSMILValue*& aFrom,
                                               const nsSMILValue*& aTo)
 {
   NS_ASSERTION(0.0f <= aSimpleProgress && aSimpleProgress < 1.0f,
-               "aSimpleProgress is out of bounds.");
+               "aSimpleProgress is out of bounds");
   NS_ASSERTION(GetCalcMode() == CALC_PACED,
                "Calling paced-specific function, but not in paced mode");
 
   double totalDistance = ComputePacedTotalDistance(aValues);
   if (totalDistance == COMPUTE_DISTANCE_ERROR)
     return NS_ERROR_FAILURE;
 
   // total distance we should have moved at this point in time.
@@ -553,17 +553,17 @@ nsSMILAnimationFunction::ComputePacedPos
       aFrom = &aValues[i];
       aTo = &aValues[i+1];
       aIntervalProgress = remainingDist / curIntervalDist;
       return NS_OK;
     }
   }
 
   NS_NOTREACHED("shouldn't complete loop & get here -- if we do, "
-                "then aSimpleProgress was probably out of bounds.");
+                "then aSimpleProgress was probably out of bounds");
   return NS_ERROR_FAILURE;
 }
 
 /*
  * Computes the total distance to be travelled by a paced animation.
  *
  * Returns the total distance, or returns COMPUTE_DISTANCE_ERROR if
  * our values don't support distance computation.
@@ -635,18 +635,18 @@ nsSMILAnimationFunction::ScaleIntervalPr
 {
   if (GetCalcMode() != CALC_SPLINE)
     return;
 
   if (!HasAttr(nsGkAtoms::keySplines))
     return;
 
   NS_ASSERTION(aIntervalIndex < (PRUint32)mKeySplines.Length(),
-               "Invalid interval index.");
-  NS_ASSERTION(aNumIntervals >= 1, "Invalid number of intervals.");
+               "Invalid interval index");
+  NS_ASSERTION(aNumIntervals >= 1, "Invalid number of intervals");
 
   if (aIntervalIndex >= (PRUint32)mKeySplines.Length() ||
       aNumIntervals < 1)
     return;
 
   nsSMILKeySpline const &spline = mKeySplines[aIntervalIndex];
   aProgress = spline.GetSplineValue(aProgress);
 }
--- a/content/smil/nsSMILCSSValueType.cpp
+++ b/content/smil/nsSMILCSSValueType.cpp
@@ -73,17 +73,17 @@ static const nsStyleAnimation::Value
   sZeroColor(NS_RGB(0,0,0), nsStyleAnimation::Value::ColorConstructor);
 
 // Helper Methods
 // --------------
 static const nsStyleAnimation::Value*
 GetZeroValueForUnit(nsStyleAnimation::Unit aUnit)
 {
   NS_ABORT_IF_FALSE(aUnit != nsStyleAnimation::eUnit_Null,
-                    "Need non-null unit for a zero value.");
+                    "Need non-null unit for a zero value");
   switch (aUnit) {
     case nsStyleAnimation::eUnit_Coord:
       return &sZeroCoord;
     case nsStyleAnimation::eUnit_Percent:
       return &sZeroPercent;
     case nsStyleAnimation::eUnit_Float:
       return &sZeroFloat;
     case nsStyleAnimation::eUnit_Color:
@@ -271,17 +271,17 @@ nsSMILCSSValueType::Interpolate(const ns
 
   const nsStyleAnimation::Value* startCSSValue = startWrapper ?
     &startWrapper->mCSSValue :
     GetZeroValueForUnit(endWrapper->mCSSValue.GetUnit());
   if (!startCSSValue) {
     // No zero value for this unit --> doesn't support interpolation.
     return NS_ERROR_FAILURE;
   }
-  
+
   nsStyleAnimation::Value resultValue;
   if (nsStyleAnimation::Interpolate(endWrapper->mPropID, *startCSSValue,
                                     endWrapper->mCSSValue, aUnitDistance,
                                     resultValue)) {
     aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue,
                                        endWrapper->mPresContext);
     return aResult.mU.mPtr ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
   }
--- a/content/smil/nsSMILFloatType.cpp
+++ b/content/smil/nsSMILFloatType.cpp
@@ -51,26 +51,26 @@ nsSMILFloatType::Init(nsSMILValue& aValu
   aValue.mU.mDouble = 0.0;
   aValue.mType = this;
   return NS_OK;
 }
 
 void
 nsSMILFloatType::Destroy(nsSMILValue& aValue) const
 {
-  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
   aValue.mU.mDouble = 0.0;
   aValue.mType      = &nsSMILNullType::sSingleton;
 }
 
 nsresult
 nsSMILFloatType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
 {
-  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types.");
-  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
   aDest.mU.mDouble = aSrc.mU.mDouble;
   return NS_OK;
 }
 
 nsresult
 nsSMILFloatType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
                      PRUint32 aCount) const
 {
@@ -101,18 +101,18 @@ nsresult
 nsSMILFloatType::Interpolate(const nsSMILValue& aStartVal,
                              const nsSMILValue& aEndVal,
                              double aUnitDistance,
                              nsSMILValue& aResult) const
 {
   NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
       "Trying to interpolate different types");
   NS_PRECONDITION(aStartVal.mType == this,
-      "Unexpected types for interpolation.");
-  NS_PRECONDITION(aResult.mType   == this, "Unexpected result type.");
+      "Unexpected types for interpolation");
+  NS_PRECONDITION(aResult.mType   == this, "Unexpected result type");
 
   const double &startVal = aStartVal.mU.mDouble;
   const double &endVal   = aEndVal.mU.mDouble;
 
   aResult.mU.mDouble = (startVal + (endVal - startVal) * aUnitDistance);
 
   return NS_OK;
 }
--- a/content/smil/nsSMILMilestone.h
+++ b/content/smil/nsSMILMilestone.h
@@ -90,17 +90,17 @@ public:
     return mTime < aOther.mTime ||
           (mTime == aOther.mTime && mIsEnd && !aOther.mIsEnd);
   }
 
   PRBool operator<=(const nsSMILMilestone& aOther) const
   {
     return *this == aOther || *this < aOther;
   }
-  
+
   PRBool operator>=(const nsSMILMilestone& aOther) const
   {
     return !(*this < aOther);
   }
 
   nsSMILTime   mTime;  // The milestone time. This may be in container time or
                        // parent container time depending on where it is used.
   PRPackedBool mIsEnd; // PR_TRUE if this milestone corresponds to an interval
--- a/content/smil/nsSMILNullType.cpp
+++ b/content/smil/nsSMILNullType.cpp
@@ -40,41 +40,41 @@
 #include "nsSMILValue.h"
 #include "nsDebug.h"
 
 /*static*/ nsSMILNullType nsSMILNullType::sSingleton;
 
 nsresult
 nsSMILNullType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
 {
-  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types.");
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
   NS_PRECONDITION(aSrc.mType == this, "Unexpected source type");
   aDest.mU    = aSrc.mU;
   aDest.mType = &sSingleton;
   return NS_OK;
 }
 
 nsresult
 nsSMILNullType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
                     PRUint32 aCount) const
 {
-  NS_NOTREACHED("Adding NULL type.");
+  NS_NOTREACHED("Adding NULL type");
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 nsSMILNullType::ComputeDistance(const nsSMILValue& aFrom,
                                 const nsSMILValue& aTo,
                                 double& aDistance) const
 {
-  NS_NOTREACHED("Computing distance for NULL type.");
+  NS_NOTREACHED("Computing distance for NULL type");
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 nsSMILNullType::Interpolate(const nsSMILValue& aStartVal,
                             const nsSMILValue& aEndVal,
                             double aUnitDistance,
                             nsSMILValue& aResult) const
 {
-  NS_NOTREACHED("Interpolating NULL type.");
+  NS_NOTREACHED("Interpolating NULL type");
   return NS_ERROR_FAILURE;
 }
--- a/content/smil/nsSMILParserUtils.cpp
+++ b/content/smil/nsSMILParserUtils.cpp
@@ -290,17 +290,17 @@ ParseAccessKey(const nsAString& aSpec, n
   }
 
   result.mRepeatIterationOrAccessKey = c;
 
   if (*start++ != ')')
     return NS_ERROR_FAILURE;
 
   SkipBeginWsp(start, end);
-  
+
   nsresult rv = ParseOptionalOffset(Substring(start, end), result);
   if (NS_FAILED(rv))
     return rv;
 
   aResult = result;
 
   return NS_OK;
 }
@@ -416,17 +416,17 @@ ParseElementBaseTimeValueSpec(const nsAS
     result.mType = nsSMILTimeValueSpecParams::EVENT;
     result.mEventSymbol = do_GetAtom(token);
   }
 
   // We've reached the end of the token, so we should now be either looking at
   // a '+', '-', or the end.
   const PRUnichar* specEnd = aSpec.EndReading();
   SkipBeginWsp(tokenEnd, specEnd);
-  
+
   nsresult rv = ParseOptionalOffset(Substring(tokenEnd, specEnd), result);
   if (NS_SUCCEEDED(rv)) {
     aResult = result;
   }
 
   return rv;
 }
 
@@ -678,17 +678,17 @@ nsSMILParserUtils::ParseTimeValueSpecPar
   else if (StringBeginsWith(spec, ACCESSKEY_PREFIX)) {
     rv = ParseAccessKey(spec, aResult);
   }
 
   // event, syncbase, or repeat
   else {
     rv = ParseElementBaseTimeValueSpec(spec, aResult);
   }
- 
+
   return rv;
 }
 
 nsresult
 nsSMILParserUtils::ParseClockValue(const nsAString& aSpec,
                                    nsSMILTimeValue* aResult,
                                    PRUint32 aFlags,   // = 0
                                    PRBool* aIsMedia)  // = nsnull
--- a/content/smil/nsSMILRepeatCount.h
+++ b/content/smil/nsSMILRepeatCount.h
@@ -65,17 +65,17 @@ public:
 
   nsSMILRepeatCount& operator=(double aCount)
   {
     SetCount(aCount);
     return *this;
   }
   void SetCount(double aCount)
   {
-    NS_ASSERTION(aCount > 0.0, "Negative or zero repeat count.");
+    NS_ASSERTION(aCount > 0.0, "Negative or zero repeat count");
     mCount = aCount > 0.0 ? aCount : kNotSet;
   }
   void SetIndefinite() { mCount = kIndefinite; }
   void Unset() { mCount = kNotSet; }
 
 private:
   static const double kNotSet;
   static const double kIndefinite;
--- a/content/smil/nsSMILSetAnimationFunction.cpp
+++ b/content/smil/nsSMILSetAnimationFunction.cpp
@@ -104,21 +104,21 @@ nsSMILSetAnimationFunction::InterpolateR
 {
   // Sanity Checks
   const nsSMILTime& dur = mSimpleDuration.GetMillis();
   NS_ABORT_IF_FALSE(mSampleTime >= 0.0f, "Sample time should not be negative");
   NS_ABORT_IF_FALSE(dur >= 0.0f, "Simple duration should not be negative");
   NS_ABORT_IF_FALSE(IsToAnimation(), "Set element only supports to-animation");
 
   if (mSampleTime >= dur || mSampleTime < 0) {
-    NS_ERROR("Animation sampled outside interval.");
+    NS_ERROR("Animation sampled outside interval");
     return NS_ERROR_FAILURE;
   }
   if (aValues.Length() != 1) {
-    NS_ERROR("Unexpected number of values.");
+    NS_ERROR("Unexpected number of values");
     return NS_ERROR_FAILURE;
   }
   // End Sanity Checks
 
   // Always use the 'to' value (which should be first & only elem in |aValues|)
   aResult = aValues[0];
   return NS_OK;
 }
--- a/content/smil/nsSMILTimeContainer.h
+++ b/content/smil/nsSMILTimeContainer.h
@@ -247,17 +247,17 @@ protected:
    * Removes a child time container.
    */
   virtual void RemoveChild(nsSMILTimeContainer& aChild) { }
 
   /*
    * Implementation helper to update the current time.
    */
   void UpdateCurrentTime();
- 
+
   /*
    * Implementation helper to notify timed elements with dependencies that the
    * container time has changed with respect to the document time.
    */
   void NotifyTimeChange();
 
   // The parent time container, if any
   nsSMILTimeContainer* mParent;
--- a/content/smil/nsSMILTimeValueSpecParams.h
+++ b/content/smil/nsSMILTimeValueSpecParams.h
@@ -63,17 +63,17 @@ public:
     OFFSET,
     SYNCBASE,
     EVENT,
     REPEAT,
     ACCESSKEY,
     WALLCLOCK,
     INDEFINITE
   } mType;
- 
+
   // A clock value that is added to:
   // - type OFFSET: the document begin
   // - type SYNCBASE: the timebase's begin or end time
   // - type EVENT: the event time
   // - type REPEAT: the repeat time
   // - type ACCESSKEY: the keypress time
   // It is not used for WALLCLOCK or INDEFINITE times
   nsSMILTimeValue   mOffset;
--- a/content/smil/nsSMILTimedElement.cpp
+++ b/content/smil/nsSMILTimedElement.cpp
@@ -1017,17 +1017,17 @@ nsSMILTimedElement::GetNextInterval(cons
   // Calc starting point
   nsSMILTimeValue beginAfter;
   PRBool prevIntervalWasZeroDur = PR_FALSE;
   if (aPrevInterval && aPrevInterval->IsSet()) {
     beginAfter = aPrevInterval->End()->Time();
     prevIntervalWasZeroDur
       = aPrevInterval->End()->Time() == aPrevInterval->Begin()->Time();
     if (aFixedBeginTime) {
-      prevIntervalWasZeroDur &= 
+      prevIntervalWasZeroDur &=
         aPrevInterval->Begin()->Time() == aFixedBeginTime->Time();
     }
   } else {
     beginAfter.SetMillis(LL_MININT);
   }
 
   nsRefPtr<nsSMILInstanceTime> tempBegin;
   nsRefPtr<nsSMILInstanceTime> tempEnd;
@@ -1108,17 +1108,17 @@ nsSMILTimedElement::GetNextInterval(cons
     }
 
     // Check for valid interval
     if (tempEnd->Time() > zeroTime ||
        (tempBegin->Time() == zeroTime && tempEnd->Time() == zeroTime)) {
       aResult.Set(*tempBegin, *tempEnd);
       return NS_OK;
     }
-    
+
     if (mRestartMode == RESTART_NEVER) {
       // tempEnd <= 0 so we're going to loop which effectively means restarting
       return NS_ERROR_FAILURE;
     }
 
     beginAfter = tempEnd->Time();
   }
   NS_NOTREACHED("Hmm... we really shouldn't be here");
@@ -1161,20 +1161,20 @@ nsSMILTimedElement::GetNextGreaterOrEqua
  */
 nsSMILTimeValue
 nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin,
                                   const nsSMILTimeValue& aEnd) const
 {
   nsSMILTimeValue result;
 
   NS_ASSERTION(mSimpleDur.IsResolved() || mSimpleDur.IsIndefinite(),
-    "Unresolved simple duration in CalcActiveEnd.");
+    "Unresolved simple duration in CalcActiveEnd");
 
   if (!aBegin.IsResolved() && !aBegin.IsIndefinite()) {
-    NS_ERROR("Unresolved begin time passed to CalcActiveEnd.");
+    NS_ERROR("Unresolved begin time passed to CalcActiveEnd");
     result.SetIndefinite();
     return result;
   }
 
   if (mRepeatDur.IsIndefinite() || aBegin.IsIndefinite()) {
     result.SetIndefinite();
   } else {
     result = GetRepeatDuration();
--- a/content/smil/nsSMILValue.cpp
+++ b/content/smil/nsSMILValue.cpp
@@ -34,111 +34,129 @@
  * 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 "nsSMILValue.h"
 #include "nsDebug.h"
 
+//----------------------------------------------------------------------
+// Public methods
+
 nsSMILValue::nsSMILValue(const nsISMILType* aType)
-: mU(),
-  mType(&nsSMILNullType::sSingleton)
+  : mType(&nsSMILNullType::sSingleton)
 {
-  if (!aType) return;
+  if (!aType) {
+    NS_ERROR("Trying to construct nsSMILValue with null mType pointer");
+    return;
+  }
 
-  nsresult rv = aType->Init(*this);
-  NS_POSTCONDITION(mType == aType || (NS_FAILED(rv) && IsNull()),
-    "Post-condition of Init failed. nsSMILValue is invalid.");
+  InitAndCheckPostcondition(aType);
 }
 
 nsSMILValue::nsSMILValue(const nsSMILValue& aVal)
-:
-  mU(),
-  mType(&nsSMILNullType::sSingleton)
+  : mType(&nsSMILNullType::sSingleton)
 {
-  nsresult rv = aVal.mType->Init(*this);
-  NS_POSTCONDITION(mType == aVal.mType || (NS_FAILED(rv) && IsNull()),
-    "Post-condition of Init failed. nsSMILValue is invalid.");
-  if (NS_FAILED(rv)) return;
+  nsresult rv = InitAndCheckPostcondition(aVal.mType);
+  if (NS_FAILED(rv))
+    return;
+
   mType->Assign(*this, aVal);
 }
 
 const nsSMILValue&
 nsSMILValue::operator=(const nsSMILValue& aVal)
 {
   if (&aVal == this)
     return *this;
 
   if (mType != aVal.mType) {
-    mType->Destroy(*this);
-    NS_POSTCONDITION(IsNull(), "nsSMILValue not null after destroying");
-    nsresult rv = aVal.mType->Init(*this);
-    NS_POSTCONDITION(mType == aVal.mType || (NS_FAILED(rv) && IsNull()),
-      "Post-condition of Init failed. nsSMILValue is invalid.");
-    if (NS_FAILED(rv)) return *this;
+    nsresult rv = DestroyAndReinit(aVal.mType);
+    if (NS_FAILED(rv))
+      return *this; // Initialization failed; return early
   }
 
   mType->Assign(*this, aVal);
 
   return *this;
 }
 
 nsresult
 nsSMILValue::Add(const nsSMILValue& aValueToAdd, PRUint32 aCount)
 {
-  if (aValueToAdd.IsNull()) return NS_OK;
-
   if (aValueToAdd.mType != mType) {
-    NS_ERROR("Trying to add incompatible types.");
+    NS_ERROR("Trying to add incompatible types");
     return NS_ERROR_FAILURE;
   }
 
   return mType->Add(*this, aValueToAdd, aCount);
 }
 
 nsresult
 nsSMILValue::SandwichAdd(const nsSMILValue& aValueToAdd)
 {
-  if (aValueToAdd.IsNull())
-    return NS_OK;
-
   if (aValueToAdd.mType != mType) {
-    NS_ERROR("Trying to add incompatible types.");
+    NS_ERROR("Trying to add incompatible types");
     return NS_ERROR_FAILURE;
   }
 
   return mType->SandwichAdd(*this, aValueToAdd);
 }
 
 nsresult
 nsSMILValue::ComputeDistance(const nsSMILValue& aTo, double& aDistance) const
 {
   if (aTo.mType != mType) {
-    NS_ERROR("Trying to calculate distance between incompatible types.");
+    NS_ERROR("Trying to calculate distance between incompatible types");
     return NS_ERROR_FAILURE;
   }
 
   return mType->ComputeDistance(*this, aTo, aDistance);
 }
 
 nsresult
 nsSMILValue::Interpolate(const nsSMILValue& aEndVal,
                          double aUnitDistance,
                          nsSMILValue& aResult) const
 {
   if (aEndVal.mType != mType) {
-    NS_ERROR("Trying to interpolate between incompatible types.");
+    NS_ERROR("Trying to interpolate between incompatible types");
     return NS_ERROR_FAILURE;
   }
 
   if (aResult.mType != mType) {
-    aResult.mType->Destroy(aResult);
-    NS_POSTCONDITION(aResult.IsNull(), "nsSMILValue not null after destroying");
-    nsresult rv = mType->Init(aResult);
-    NS_POSTCONDITION(aResult.mType == mType
-      || (NS_FAILED(rv) && aResult.IsNull()),
-      "Post-condition of Init failed. nsSMILValue is invalid.");
-    if (NS_FAILED(rv)) return rv;
+    // Outparam has wrong type
+    nsresult rv = aResult.DestroyAndReinit(mType);
+    if (NS_FAILED(rv))
+      return rv;
   }
 
   return mType->Interpolate(*this, aEndVal, aUnitDistance, aResult);
 }
+
+//----------------------------------------------------------------------
+// Helper methods
+
+// Wrappers for nsISMILType::Init & ::Destroy that verify their postconditions
+nsresult
+nsSMILValue::InitAndCheckPostcondition(const nsISMILType* aNewType)
+{
+  nsresult rv = aNewType->Init(*this);
+  NS_ABORT_IF_FALSE(mType == aNewType || (NS_FAILED(rv) && IsNull()),
+                    "Post-condition of Init failed. nsSMILValue is invalid");
+  return rv;
+}
+                
+void
+nsSMILValue::DestroyAndCheckPostcondition()
+{
+  mType->Destroy(*this);
+  NS_ABORT_IF_FALSE(IsNull(), "Post-condition of Destroy failed. "
+                    "nsSMILValue not null after destroying");
+}
+
+nsresult
+nsSMILValue::DestroyAndReinit(const nsISMILType* aNewType)
+{
+  DestroyAndCheckPostcondition();
+  return InitAndCheckPostcondition(aNewType);
+}
--- a/content/smil/nsSMILValue.h
+++ b/content/smil/nsSMILValue.h
@@ -71,11 +71,16 @@ public:
   union {
     PRBool mBool;
     PRUint64 mUint;
     PRInt64 mInt;
     double mDouble;
     void* mPtr;
   } mU;
   const nsISMILType* mType;
+
+protected:
+  nsresult InitAndCheckPostcondition(const nsISMILType* aNewType);
+  void     DestroyAndCheckPostcondition();
+  nsresult DestroyAndReinit(const nsISMILType* aNewType);
 };
 
 #endif  // NS_SMILVALUE_H_
--- a/content/svg/content/src/nsSVGSVGElement.h
+++ b/content/svg/content/src/nsSVGSVGElement.h
@@ -219,17 +219,17 @@ protected:
   // nsSVGElement overrides
   PRBool IsEventName(nsIAtom* aName);
 
 #ifdef MOZ_SMIL
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep, PRBool aNullParent);
-#endif // MOZ_SMIL   
+#endif // MOZ_SMIL
 
   // implementation helpers:
 
   PRBool IsRoot() {
     NS_ASSERTION((IsInDoc() && !GetParent()) ==
                  (GetOwnerDoc() && (GetOwnerDoc()->GetRootContent() == this)),
                  "Can't determine if we're root");
     return IsInDoc() && !GetParent();
--- a/content/svg/content/src/nsSVGTransformSMILAttr.cpp
+++ b/content/svg/content/src/nsSVGTransformSMILAttr.cpp
@@ -58,17 +58,17 @@ nsSVGTransformSMILAttr::GetSMILType() co
 
 nsresult
 nsSVGTransformSMILAttr::ValueFromString(const nsAString& aStr,
                                      const nsISMILAnimationElement* aSrcElement,
                                      nsSMILValue& aValue) const
 {
   NS_ENSURE_TRUE(aSrcElement, NS_ERROR_FAILURE);
   NS_ASSERTION(aValue.IsNull(),
-    "aValue should have been cleared before calling ValueFromString.");
+    "aValue should have been cleared before calling ValueFromString");
 
   nsSMILValue val(&nsSVGTransformSMILType::sSingleton);
   if (val.IsNull())
     return NS_ERROR_FAILURE;
 
   const nsAttrValue* typeAttr = aSrcElement->GetAnimAttr(nsGkAtoms::type);
 
   const nsIAtom* transformType = typeAttr
@@ -152,17 +152,17 @@ nsSVGTransformSMILAttr::SetAnimValue(con
 nsresult
 nsSVGTransformSMILAttr::ParseValue(const nsAString& aSpec,
                                    const nsIAtom* aTransformType,
                                    nsSMILValue& aResult) const
 {
   nsSVGTransformSMILType* type = &nsSVGTransformSMILType::sSingleton;
   NS_ASSERTION(
       type == static_cast<nsSVGTransformSMILType const *>(aResult.mType),
-      "Unexpected type for SMIL value result.");
+      "Unexpected type for SMIL value result");
 
   // Reset the result so we can just append to it
   nsresult rv = type->Init(aResult);
   NS_ENSURE_SUCCESS(rv,rv);
 
   float params[3] = { 0.f };
   PRInt32 numParsed = ParseParameterList(aSpec, params, 3);
   nsSVGSMILTransform::TransformType transformType;
@@ -333,17 +333,17 @@ nsSVGTransformSMILAttr::AppendSVGTransfo
       }
       break;
 
     case nsIDOMSVGTransform::SVG_TRANSFORM_UNKNOWN:
       // If it's 'unknown', it's probably not initialised, so just skip it.
       return NS_OK;
 
     default:
-      NS_WARNING("Trying to convert unrecognised SVG transform type.");
+      NS_WARNING("Trying to convert unrecognised SVG transform type");
       return NS_ERROR_FAILURE;
   }
 
   if (transformType != nsSVGSMILTransform::TRANSFORM_MATRIX) {
     rv =
       type->AppendTransform(nsSVGSMILTransform(transformType, params), aValue);
   }
 
--- a/content/svg/content/src/nsSVGTransformSMILType.cpp
+++ b/content/svg/content/src/nsSVGTransformSMILType.cpp
@@ -46,17 +46,17 @@
 // nsISMILType implementation
 
 nsresult
 nsSVGTransformSMILType::Init(nsSMILValue &aValue) const
 {
   NS_PRECONDITION(aValue.mType == this || aValue.IsNull(),
                   "Unexpected value type");
   NS_ASSERTION(aValue.mType != this || aValue.mU.mPtr,
-               "Invalid nsSMILValue of SVG transform type: NULL data member.");
+               "Invalid nsSMILValue of SVG transform type: NULL data member");
 
   if (aValue.mType != this || !aValue.mU.mPtr) {
     // Different type, or no data member: allocate memory and set type
     TransformArray* transforms = new TransformArray(1);
     NS_ENSURE_TRUE(transforms, NS_ERROR_OUT_OF_MEMORY);
     aValue.mU.mPtr = transforms;
     aValue.mType = this;
   } else {
@@ -66,29 +66,29 @@ nsSVGTransformSMILType::Init(nsSMILValue
   }
 
   return NS_OK;
 }
 
 void
 nsSVGTransformSMILType::Destroy(nsSMILValue& aValue) const
 {
-  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type.");
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
   TransformArray* params = static_cast<TransformArray*>(aValue.mU.mPtr);
   delete params;
   aValue.mU.mPtr = nsnull;
   aValue.mType = &nsSMILNullType::sSingleton;
 }
 
 nsresult
 nsSVGTransformSMILType::Assign(nsSMILValue& aDest,
                                const nsSMILValue& aSrc) const
 {
-  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types.");
-  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
 
   const TransformArray* srcTransforms =
     static_cast<const TransformArray*>(aSrc.mU.mPtr);
   TransformArray* dstTransforms = static_cast<TransformArray*>(aDest.mU.mPtr);
 
   // Before we assign, ensure we have sufficient memory
   PRBool result = dstTransforms->SetCapacity(srcTransforms->Length());
   NS_ENSURE_TRUE(result,NS_ERROR_OUT_OF_MEMORY);
@@ -97,118 +97,118 @@ nsSVGTransformSMILType::Assign(nsSMILVal
 
   return NS_OK;
 }
 
 nsresult
 nsSVGTransformSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
                             PRUint32 aCount) const
 {
-  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type.");
-  NS_PRECONDITION(aDest.mType == aValueToAdd.mType, "Incompatible SMIL types.");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
+  NS_PRECONDITION(aDest.mType == aValueToAdd.mType, "Incompatible SMIL types");
 
   TransformArray& dstTransforms(*static_cast<TransformArray*>(aDest.mU.mPtr));
   const TransformArray& srcTransforms
     (*static_cast<const TransformArray*>(aValueToAdd.mU.mPtr));
 
   // We're doing a simple add here (as opposed to a sandwich add below).
   // We only do this when we're accumulating a repeat result or calculating
   // a by-animation value.
   //
   // In either case we should have 1 transform in the source array.
   NS_ASSERTION(srcTransforms.Length() == 1,
-    "Invalid source transform list to add.");
+    "Invalid source transform list to add");
 
   // And we should have 0 or 1 transforms in the dest array.
   // (We can have 0 transforms in the case of by-animation when we are
   // calculating the by-value as "0 + by". Zero being represented by an
   // nsSMILValue with an empty transform array.)
   NS_ASSERTION(dstTransforms.Length() < 2,
-    "Invalid dest transform list to add to.");
+    "Invalid dest transform list to add to");
 
   // Get the individual transforms to add
   const nsSVGSMILTransform& srcTransform = srcTransforms[0];
   if (dstTransforms.IsEmpty()) {
     nsSVGSMILTransform* result = dstTransforms.AppendElement(
       nsSVGSMILTransform(srcTransform.mTransformType));
     NS_ENSURE_TRUE(result,NS_ERROR_OUT_OF_MEMORY);
   }
   nsSVGSMILTransform& dstTransform = dstTransforms[0];
 
   // The types must be the same
   NS_ASSERTION(srcTransform.mTransformType == dstTransform.mTransformType,
-    "Trying to perform simple add of different transform types.");
+    "Trying to perform simple add of different transform types");
 
   // And it should be impossible that one of them is of matrix type
   NS_ASSERTION(
     srcTransform.mTransformType != nsSVGSMILTransform::TRANSFORM_MATRIX,
-    "Trying to perform simple add with matrix transform.");
+    "Trying to perform simple add with matrix transform");
 
   // Add the parameters
   for (int i = 0; i <= 2; ++i) {
     dstTransform.mParams[i] += srcTransform.mParams[i] * aCount;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsSVGTransformSMILType::SandwichAdd(nsSMILValue& aDest,
                                     const nsSMILValue& aValueToAdd) const
 {
-  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type.");
-  NS_PRECONDITION(aDest.mType == aValueToAdd.mType, "Incompatible SMIL types.");
+  NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
+  NS_PRECONDITION(aDest.mType == aValueToAdd.mType, "Incompatible SMIL types");
 
   // For <animateTransform> a sandwich add means a matrix post-multiplication
   // which just means to put the additional transform on the end of the array
 
   TransformArray& dstTransforms(*static_cast<TransformArray*>(aDest.mU.mPtr));
   const TransformArray& srcTransforms
     (*static_cast<const TransformArray*>(aValueToAdd.mU.mPtr));
 
   // We're only expecting to be adding 1 src transform on to the list
   NS_ASSERTION(srcTransforms.Length() == 1,
-    "Trying to do sandwich add of more than one value.");
+    "Trying to do sandwich add of more than one value");
 
   // Stick the src on the end of the array
   const nsSVGSMILTransform& srcTransform = srcTransforms[0];
   nsSVGSMILTransform* result = dstTransforms.AppendElement(srcTransform);
   NS_ENSURE_TRUE(result,NS_ERROR_OUT_OF_MEMORY);
 
   return NS_OK;
 }
 
 nsresult
 nsSVGTransformSMILType::ComputeDistance(const nsSMILValue& aFrom,
                                         const nsSMILValue& aTo,
                                         double& aDistance) const
 {
   NS_PRECONDITION(aFrom.mType == aTo.mType,
-      "Can't compute difference between different SMIL types.");
-  NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type.");
+      "Can't compute difference between different SMIL types");
+  NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
 
   const TransformArray* fromTransforms =
     static_cast<const TransformArray*>(aFrom.mU.mPtr);
   const TransformArray* toTransforms =
     static_cast<const TransformArray*>(aTo.mU.mPtr);
 
   // ComputeDistance is only used for calculating distances between single
   // values in a values array which necessarily have the same type
   //
   // So we should only have one transform in each array and they should be of
   // the same type
   NS_ASSERTION(fromTransforms->Length() == 1,
-    "Wrong number of elements in from value.");
+    "Wrong number of elements in from value");
   NS_ASSERTION(toTransforms->Length() == 1,
-    "Wrong number of elements in to value.");
+    "Wrong number of elements in to value");
 
   const nsSVGSMILTransform& fromTransform = (*fromTransforms)[0];
   const nsSVGSMILTransform& toTransform = (*toTransforms)[0];
   NS_ASSERTION(fromTransform.mTransformType == toTransform.mTransformType,
-    "Incompatible transform types to calculate distance between.");
+    "Incompatible transform types to calculate distance between");
 
   switch (fromTransform.mTransformType)
   {
     // We adopt the SVGT1.2 notions of distance here
     // See: http://www.w3.org/TR/SVGTiny12/animate.html#complexDistances
     // (As discussed in bug #469040)
     case nsSVGSMILTransform::TRANSFORM_TRANSLATE:
     case nsSVGSMILTransform::TRANSFORM_SCALE:
@@ -227,51 +227,51 @@ nsSVGTransformSMILType::ComputeDistance(
       {
         const float& a = fromTransform.mParams[0];
         const float& b = toTransform.mParams[0];
         aDistance = fabs(a-b);
       }
       break;
 
     default:
-      NS_ERROR("Got bad transform types for calculating distances.");
+      NS_ERROR("Got bad transform types for calculating distances");
       aDistance = 1.0;
       return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsSVGTransformSMILType::Interpolate(const nsSMILValue& aStartVal,
                                     const nsSMILValue& aEndVal,
                                     double aUnitDistance,
                                     nsSMILValue& aResult) const
 {
   NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
-      "Can't interpolate between different SMIL types.");
+      "Can't interpolate between different SMIL types");
   NS_PRECONDITION(aStartVal.mType == this,
-      "Unexpected type for interpolation.");
-  NS_PRECONDITION(aResult.mType == this, "Unexpected result type.");
+      "Unexpected type for interpolation");
+  NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
 
   const TransformArray& startTransforms =
     (*static_cast<const TransformArray*>(aStartVal.mU.mPtr));
   const TransformArray& endTransforms
     (*static_cast<const TransformArray*>(aEndVal.mU.mPtr));
 
   // We may have 0..n transforms in the start transform array (the base
   // value) but we should only have 1 transform in the end transform array
   NS_ASSERTION(endTransforms.Length() == 1,
-    "Invalid end-point for interpolating between transform values.");
+    "Invalid end-point for interpolating between transform values");
 
   // The end point should never be a matrix transform
   const nsSVGSMILTransform& endTransform = endTransforms[0];
   NS_ASSERTION(
     endTransform.mTransformType != nsSVGSMILTransform::TRANSFORM_MATRIX,
-    "End point for interpolation should not be a matrix transform.");
+    "End point for interpolation should not be a matrix transform");
 
   // If we have 0 or more than 1 transform in the start transform array then we
   // just interpolate from 0, 0, 0
   // Likewise, even if there's only 1 transform in the start transform array
   // then if the type of the start transform doesn't match the end then we
   // can't interpolate and should just use 0, 0, 0
   static float identityParams[3] = { 0.f };
   const float* startParams = nsnull;
@@ -311,46 +311,46 @@ nsSVGTransformSMILType::Interpolate(cons
 }
 
 //----------------------------------------------------------------------
 // Transform array accessors
 
 PRUint32
 nsSVGTransformSMILType::GetNumTransforms(const nsSMILValue& aValue) const
 {
-  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
 
   const TransformArray& transforms =
     *static_cast<const TransformArray*>(aValue.mU.mPtr);
 
   return transforms.Length();
 }
 
 const nsSVGSMILTransform*
 nsSVGTransformSMILType::GetTransformAt(PRUint32 aIndex,
                                        const nsSMILValue& aValue) const
 {
-  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
 
   const TransformArray& transforms =
     *static_cast<const TransformArray*>(aValue.mU.mPtr);
 
   if (aIndex >= transforms.Length()) {
-    NS_ERROR("Attempting to access invalid transform.");
+    NS_ERROR("Attempting to access invalid transform");
     return nsnull;
   }
 
   return &transforms[aIndex];
 }
 
 nsresult
 nsSVGTransformSMILType::AppendTransform(const nsSVGSMILTransform& aTransform,
                                         nsSMILValue& aValue) const
 {
-  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value.");
+  NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value");
 
   TransformArray& transforms = *static_cast<TransformArray*>(aValue.mU.mPtr);
 
   nsSVGSMILTransform* transform = transforms.AppendElement(aTransform);
   NS_ENSURE_TRUE(transform,NS_ERROR_OUT_OF_MEMORY);
 
   return NS_OK;
 }
--- a/content/svg/content/test/test_animLengthReadonly.xhtml
+++ b/content/svg/content/test/test_animLengthReadonly.xhtml
@@ -29,30 +29,33 @@ https://bugzilla.mozilla.org/show_bug.cg
 /* Global Variables */
 const svgns="http://www.w3.org/2000/svg";
 var svg = document.getElementById("svg");
 var circle = document.getElementById('circle');
 
 SimpleTest.waitForExplicitFinish();
 
 function main() {
+  // Start out paused at beginning of timeline
+  svg.pauseAnimations();
+  svg.setCurrentTime(0);
+
   // Sanity check: check initial values
   is(circle.cx.baseVal.value, -100);
   is(circle.cx.animVal.value, -100);
   is(circle.cy.baseVal.value, -100);
   is(circle.cy.animVal.value, -100);
 
   // (1): Check before animation that animVal's are readonly
   ok(checkReadOnly(circle.cx),
     "(1) animVal cx is editable when not animated");
   ok(checkReadOnly(circle.cy),
     "(1) animVal cy is editable when not animated");
 
   // Skip to mid-way through the animation
-  svg.pauseAnimations();
   svg.setCurrentTime(4);
 
   // (2): Check that whilst animations are active the animVal's are readonly
   ok(checkReadOnly(circle.cx),
     "(2) animVal cx is editable when animated");
   ok(checkReadOnly(circle.cy),
     "(2) animVal cy is editable when animated");
 
--- a/content/xul/templates/src/nsRDFConMemberTestNode.cpp
+++ b/content/xul/templates/src/nsRDFConMemberTestNode.cpp
@@ -38,20 +38,20 @@
 
 #include "nsRDFConMemberTestNode.h"
 #include "nsIRDFContainer.h"
 #include "nsIRDFContainerUtils.h"
 #include "nsRDFCID.h"
 #include "nsIServiceManager.h"
 #include "nsResourceSet.h"
 #include "nsString.h"
+#include "nsXULContentUtils.h"
 
 #include "prlog.h"
 #ifdef PR_LOGGING
-#include "nsXULContentUtils.h"
 extern PRLogModuleInfo* gXULTemplateLog;
 #endif
 
 nsRDFConMemberTestNode::nsRDFConMemberTestNode(TestNode* aParent,
                                                nsXULTemplateQueryProcessorRDF* aProcessor,
                                                nsIAtom *aContainerVariable,
                                                nsIAtom *aMemberVariable)
     : nsRDFTestNode(aParent),
@@ -469,16 +469,17 @@ nsRDFConMemberTestNode::FilterInstantiat
                     aInstantiations.Insert(inst, newinst);
                 }
             }
         }
 
         if (! hasContainerBinding && ! hasMemberBinding) {
             // Neither container nor member assignment!
             if (!aCantHandleYet) {
+                nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_UNBOUND);
                 return NS_ERROR_UNEXPECTED;
             }
 
             *aCantHandleYet = PR_TRUE;
             return NS_OK;
         }
 
         // finally, remove the "under specified" instantiation.
--- a/content/xul/templates/src/nsRDFPropertyTestNode.cpp
+++ b/content/xul/templates/src/nsRDFPropertyTestNode.cpp
@@ -33,22 +33,22 @@
  * 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 "nsRDFPropertyTestNode.h"
 #include "nsString.h"
+#include "nsXULContentUtils.h"
 
 #include "prlog.h"
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gXULTemplateLog;
 #include "nsIRDFLiteral.h"
-#include "nsXULContentUtils.h"
 #endif
 
 nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent,
                                              nsXULTemplateQueryProcessorRDF* aProcessor,
                                              nsIAtom* aSourceVariable,
                                              nsIRDFResource* aProperty,
                                              nsIAtom* aTargetVariable)
     : nsRDFTestNode(aParent),
@@ -331,16 +331,17 @@ nsRDFPropertyTestNode::FilterInstantiati
                 aInstantiations.Insert(inst, newinst);
             }
 
             // finally, remove the "under specified" instantiation.
             aInstantiations.Erase(inst--);
         }
         else {
             if (!aCantHandleYet) {
+                nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_UNBOUND);
                 // Neither source nor target assignment!
                 return NS_ERROR_UNEXPECTED;
             }
 
             *aCantHandleYet = PR_TRUE;
             return NS_OK;
         }
     }
--- a/content/xul/templates/src/nsXULContentBuilder.cpp
+++ b/content/xul/templates/src/nsXULContentBuilder.cpp
@@ -1206,16 +1206,19 @@ nsXULContentBuilder::CreateContainerCont
             }
         }
 
         if (removematch) {
             // remove the generated content for the existing match
             rv = ReplaceMatch(removematch->mResult, nsnull, nsnull, aElement);
             if (NS_FAILED(rv))
                 return rv;
+
+            if (mFlags & eLoggingEnabled)
+                OutputMatchToLog(resultid, removematch, PR_FALSE);
         }
 
         if (generateContent) {
             // find the rule that matches. If none match, the content does not
             // need to be generated
 
             PRInt16 ruleindex;
             nsTemplateRule* matchedrule = nsnull;
@@ -1238,16 +1241,19 @@ nsXULContentBuilder::CreateContainerCont
                 nsCOMPtr<nsIContent> action = matchedrule->GetAction();
                 BuildContentFromTemplate(action, aElement, aElement, PR_TRUE,
                                          mRefVariable == matchedrule->GetMemberVariable(),
                                          nextresult, aNotify, newmatch,
                                          aContainer, aNewIndexInContainer);
             }
         }
 
+        if (mFlags & eLoggingEnabled)
+            OutputMatchToLog(resultid, newmatch, PR_TRUE);
+
         if (prevmatch) {
             prevmatch->mNext = newmatch;
         }
         else if (!mMatchMap.Put(resultid, newmatch)) {
             nsTemplateMatch::Destroy(mPool, newmatch, PR_TRUE);
             return NS_ERROR_OUT_OF_MEMORY;
         }
 
--- a/content/xul/templates/src/nsXULContentUtils.cpp
+++ b/content/xul/templates/src/nsXULContentUtils.cpp
@@ -84,25 +84,30 @@
 #include "nsContentUtils.h"
 #include "nsIDateTimeFormat.h"
 #include "nsDateTimeFormatCID.h"
 #include "nsIScriptableDateFormat.h"
 #include "nsICollation.h"
 #include "nsCollationCID.h"
 #include "nsILocale.h"
 #include "nsILocaleService.h"
+#include "nsIConsoleService.h"
 
 static NS_DEFINE_CID(kRDFServiceCID,        NS_RDFSERVICE_CID);
 
 //------------------------------------------------------------------------
 
 nsIRDFService* nsXULContentUtils::gRDF;
 nsIDateTimeFormat* nsXULContentUtils::gFormat;
 nsICollation *nsXULContentUtils::gCollation;
 
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gXULTemplateLog;
+#endif
+
 #define XUL_RESOURCE(ident, uri) nsIRDFResource* nsXULContentUtils::ident
 #define XUL_LITERAL(ident, val) nsIRDFLiteral* nsXULContentUtils::ident
 #include "nsXULResourceList.h"
 #undef XUL_RESOURCE
 #undef XUL_LITERAL
 
 //------------------------------------------------------------------------
 // Constructors n' stuff
@@ -479,8 +484,22 @@ nsXULContentUtils::SetCommandUpdater(nsI
     if (! domelement)
         return NS_ERROR_UNEXPECTED;
 
     rv = dispatcher->AddCommandUpdater(domelement, events, targets);
     if (NS_FAILED(rv)) return rv;
 
     return NS_OK;
 }
+
+void
+nsXULContentUtils::LogTemplateError(const char* aStr)
+{
+  nsAutoString message;
+  message.AssignLiteral("Error parsing template: ");
+  message.Append(NS_ConvertUTF8toUTF16(aStr).get());
+
+  nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+  if (cs) {
+    cs->LogStringMessage(message.get());
+    PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, ("Error parsing template: %s", aStr));
+  }
+}
--- a/content/xul/templates/src/nsXULContentUtils.h
+++ b/content/xul/templates/src/nsXULContentUtils.h
@@ -56,16 +56,62 @@ class nsCString;
 class nsString;
 class nsIRDFResource;
 class nsIRDFLiteral;
 class nsIRDFService;
 class nsINameSpaceManager;
 class nsIDateTimeFormat;
 class nsICollation;
 
+// errors to pass to LogTemplateError
+#define ERROR_TEMPLATE_INVALID_QUERYPROCESSOR                           \
+        "querytype attribute doesn't specify a valid query processor"
+#define ERROR_TEMPLATE_INVALID_QUERYSET                                 \
+        "unexpected <queryset> element"
+#define ERROR_TEMPLATE_NO_MEMBERVAR                                     \
+        "no member variable found. Action body should have an element with uri attribute"
+#define ERROR_TEMPLATE_WHERE_NO_SUBJECT                                 \
+        "<where> element is missing a subject attribute"
+#define ERROR_TEMPLATE_WHERE_NO_RELATION                                \
+        "<where> element is missing a rel attribute"
+#define ERROR_TEMPLATE_WHERE_NO_VALUE                                   \
+        "<where> element is missing a value attribute"
+#define ERROR_TEMPLATE_WHERE_NO_VAR                                     \
+        "<where> element must have at least one variable as a subject or value"
+#define ERROR_TEMPLATE_BINDING_BAD_SUBJECT                              \
+        "<binding> requires a variable for its subject attribute"
+#define ERROR_TEMPLATE_BINDING_BAD_PREDICATE                            \
+        "<binding> element is missing a predicate attribute"
+#define ERROR_TEMPLATE_BINDING_BAD_OBJECT                               \
+        "<binding> requires a variable for its object attribute"
+#define ERROR_TEMPLATE_CONTENT_NOT_FIRST                                \
+        "expected <content> to be first"
+#define ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR                            \
+        "<member> requires a variable for its container attribute"
+#define ERROR_TEMPLATE_MEMBER_NOCHILDVAR                                \
+        "<member> requires a variable for its child attribute"
+#define ERROR_TEMPLATE_TRIPLE_NO_VAR                                    \
+        "<triple> should have at least one variable as a subject or object"
+#define ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT                               \
+        "<triple> requires a variable for its subject attribute"
+#define ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE                             \
+        "<triple> should have a non-variable value as a predicate"
+#define ERROR_TEMPLATE_TRIPLE_BAD_OBJECT                                \
+        "<triple> requires a variable for its object attribute"
+#define ERROR_TEMPLATE_MEMBER_UNBOUND                                   \
+        "neither container or child variables of <member> has a value"
+#define ERROR_TEMPLATE_TRIPLE_UNBOUND                                   \
+        "neither subject or object variables of <triple> has a value"
+#define ERROR_TEMPLATE_BAD_XPATH                                        \
+        "XPath expression in query could not be parsed"
+#define ERROR_TEMPLATE_BAD_ASSIGN_XPATH                                 \
+        "XPath expression in <assign> could not be parsed"
+#define ERROR_TEMPLATE_BAD_BINDING_XPATH                                \
+        "XPath expression in <binding> could not be parsed"
+
 class nsXULContentUtils
 {
 protected:
     static nsIRDFService* gRDF;
     static nsIDateTimeFormat* gFormat;
     static nsICollation *gCollation;
 
     static PRBool gDisableXULCache;
@@ -119,17 +165,23 @@ public:
     static nsresult
     GetResource(PRInt32 aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult);
 
     static nsresult
     GetResource(PRInt32 aNameSpaceID, const nsAString& aAttribute, nsIRDFResource** aResult);
 
     static nsresult
     SetCommandUpdater(nsIDocument* aDocument, nsIContent* aElement);
-    
+
+    /**
+     * Log a message to the error console
+     */
+    static void
+    LogTemplateError(const char* aMsg);
+
     static nsIRDFService*
     RDFService()
     {
         return gRDF;
     }
 
     static nsICollation*
     GetCollation();
--- a/content/xul/templates/src/nsXULTemplateBuilder.cpp
+++ b/content/xul/templates/src/nsXULTemplateBuilder.cpp
@@ -77,16 +77,17 @@
 #include "nsIXULDocument.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsIXULBuilderListener.h"
 #include "nsIRDFRemoteDataSource.h"
 #include "nsIRDFService.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIServiceManager.h"
 #include "nsISimpleEnumerator.h"
+#include "nsISupportsArray.h"
 #include "nsIMutableArray.h"
 #include "nsIURL.h"
 #include "nsIXPConnect.h"
 #include "nsContentCID.h"
 #include "nsRDFCID.h"
 #include "nsXULContentUtils.h"
 #include "nsString.h"
 #include "nsTArray.h"
@@ -96,17 +97,17 @@
 #include "nsXULElement.h"
 #include "jsapi.h"
 #include "prlog.h"
 #include "rdf.h"
 #include "pldhash.h"
 #include "plhash.h"
 #include "nsIDOMClassInfo.h"
 #include "nsPIDOMWindow.h"
-
+#include "nsIConsoleService.h" 
 #include "nsNetUtil.h"
 #include "nsXULTemplateBuilder.h"
 #include "nsXULTemplateQueryProcessorRDF.h"
 #include "nsXULTemplateQueryProcessorXML.h"
 #include "nsXULTemplateQueryProcessorStorage.h"
 
 //----------------------------------------------------------------------
 
@@ -763,31 +764,33 @@ nsXULTemplateBuilder::UpdateResultInCont
                         mMatchMap.Remove(aOldId);
                     }
                 }
 
                 if (prevmatch)
                     prevmatch->mNext = nextmatch;
 
                 removedmatch = oldmatch;
+                if (mFlags & eLoggingEnabled)
+                    OutputMatchToLog(aOldId, removedmatch, PR_FALSE);
             }
         }
     }
 
+    nsTemplateMatch *newmatch = nsnull;
     if (aNewResult) {
         // only allow a result to be inserted into containers with a matching tag
         nsIAtom* tag = aQuerySet->GetTag();
         if (aInsertionPoint && tag && tag != aInsertionPoint->Tag())
             return NS_OK;
 
         PRInt32 findpriority = aQuerySet->Priority();
 
-        nsTemplateMatch *newmatch =
-            nsTemplateMatch::Create(mPool, findpriority,
-                                    aNewResult, aInsertionPoint);
+        newmatch = nsTemplateMatch::Create(mPool, findpriority,
+                                           aNewResult, aInsertionPoint);
         if (!newmatch)
             return NS_ERROR_OUT_OF_MEMORY;
 
         nsTemplateMatch* firstmatch;
         if (mMatchMap.Get(aNewId, &firstmatch)) {
             PRBool hasEarlierActiveMatch = PR_FALSE;
 
             // Scan through the existing matches to find where the new one
@@ -969,19 +972,23 @@ nsXULTemplateBuilder::UpdateResultInCont
             }
         }
     }
 
     // The ReplaceMatch method is builder specific and removes the generated
     // content for a match.
 
     // Remove the content for a match that was active and needs to be replaced.
-    if (replacedmatch)
+    if (replacedmatch) {
         rv = ReplaceMatch(replacedmatch->mResult, nsnull, nsnull,
                           aInsertionPoint);
+
+        if (mFlags & eLoggingEnabled)
+            OutputMatchToLog(aNewId, replacedmatch, PR_FALSE);
+    }
  
     // remove a match that needs to be deleted.
     if (replacedmatchtodelete)
         nsTemplateMatch::Destroy(mPool, replacedmatchtodelete, PR_TRUE);
 
     // If the old match was active, the content for it needs to be removed.
     // If the old match was not active, it shouldn't have had any content,
     // so just pass null to ReplaceMatch. If acceptedmatch was set, then
@@ -989,16 +996,19 @@ nsXULTemplateBuilder::UpdateResultInCont
     if (oldMatchWasActive || acceptedmatch)
         rv = ReplaceMatch(oldMatchWasActive ? aOldResult : nsnull,
                           acceptedmatch, matchedrule, aInsertionPoint);
 
     // delete the old match that was replaced
     if (removedmatch)
         nsTemplateMatch::Destroy(mPool, removedmatch, PR_TRUE);
 
+    if (mFlags & eLoggingEnabled && newmatch)
+        OutputMatchToLog(aNewId, newmatch, PR_TRUE);
+
     return rv;
 }
 
 NS_IMETHODIMP
 nsXULTemplateBuilder::ResultBindingChanged(nsIXULTemplateResult* aResult)
 {
     // A binding update is used when only the values of the bindings have
     // changed, so the same rule still applies. Just synchronize the content.
@@ -1229,18 +1239,21 @@ nsXULTemplateBuilder::LoadDataSources(ns
     else if (querytype.EqualsLiteral("storage")) {
         mQueryProcessor = new nsXULTemplateQueryProcessorStorage();
         NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
     }
     else {
         nsCAutoString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX);
         AppendUTF16toUTF8(querytype, cid);
         mQueryProcessor = do_CreateInstance(cid.get(), &rv);
-        // XXXndeakin log an error here - bug 321169
-        NS_ENSURE_TRUE(mQueryProcessor, rv);
+
+        if (!mQueryProcessor) {
+            nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYPROCESSOR);
+            return rv;
+        }
     }
 
     rv = LoadDataSourceUrls(aDocument, datasources,
                             isRDFQuery, aShouldDelayBuilding);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Now set the database on the element, so that script writers can
     // access it.
@@ -1728,26 +1741,36 @@ nsXULTemplateBuilder::CompileQueries()
     // Determine if there are any special settings we need to observe
     mFlags = 0;
 
     nsAutoString flags;
     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
 
     // if the dont-test-empty flag is set, containers should not be checked to
     // see if they are empty. If dont-recurse is set, then don't process the
-    // template recursively and only show one level of results.
+    // template recursively and only show one level of results. The logging
+    // flag logs errors and results to the console, which is useful when
+    // debugging templates.
     nsWhitespaceTokenizer tokenizer(flags);
     while (tokenizer.hasMoreTokens()) {
       const nsDependentSubstring& token(tokenizer.nextToken());
       if (token.EqualsLiteral("dont-test-empty"))
         mFlags |= eDontTestEmpty;
       else if (token.EqualsLiteral("dont-recurse"))
         mFlags |= eDontRecurse;
+      else if (token.EqualsLiteral("logging"))
+        mFlags |= eLoggingEnabled;
     }
 
+#ifdef PR_LOGGING
+    // always enable logging if the debug setting is used
+    if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG))
+        mFlags |= eLoggingEnabled;
+#endif
+
     nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
     nsresult rv =
         mQueryProcessor->InitializeForBuilding(mDataSource, this, rootnode);
     if (NS_FAILED(rv))
         return rv;
 
     // Set the "container" and "member" variables, if the user has specified
     // them. The container variable may be specified with the container
@@ -1806,18 +1829,16 @@ nsresult
 nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate,
                                       nsTemplateQuerySet* aQuerySet,
                                       PRBool aIsQuerySet,
                                       PRInt32* aPriority,
                                       PRBool* aCanUseTemplate)
 {
     NS_ASSERTION(aQuerySet, "No queryset supplied");
 
-    // XXXndeakin log syntax errors
-
     nsresult rv = NS_OK;
 
     PRBool isQuerySetMode = PR_FALSE;
     PRBool hasQuerySet = PR_FALSE, hasRule = PR_FALSE, hasQuery = PR_FALSE;
 
     PRUint32 count = aTemplate->GetChildCount();
 
     for (PRUint32 i = 0; i < count; i++) {
@@ -1826,18 +1847,20 @@ nsXULTemplateBuilder::CompileTemplate(ns
 
         // don't allow more queries than can be supported
         if (*aPriority == PR_INT16_MAX)
             return NS_ERROR_FAILURE;
 
         // XXXndeakin queryset isn't a good name for this tag since it only
         //            ever contains one query
         if (!aIsQuerySet && ni->Equals(nsGkAtoms::queryset, kNameSpaceID_XUL)) {
-            if (hasRule || hasQuery)
+            if (hasRule || hasQuery) {
+              nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYSET);
               continue;
+            }
 
             isQuerySetMode = PR_TRUE;
 
             // only create a queryset for those after the first since the
             // first one is always created by CompileQueries
             if (hasQuerySet) {
                 aQuerySet = new nsTemplateQuerySet(++*aPriority);
                 if (!aQuerySet)
@@ -1868,17 +1891,20 @@ nsXULTemplateBuilder::CompileTemplate(ns
                                               kNameSpaceID_XUL,
                                               nsGkAtoms::action,
                                               getter_AddRefs(action));
 
             if (action){
                 nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
                 if (!memberVariable) {
                     memberVariable = DetermineMemberVariable(action);
-                    if (!memberVariable) continue;
+                    if (!memberVariable) {
+                        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
+                        continue;
+                    }
                 }
 
                 if (hasQuery) {
                     nsCOMPtr<nsIAtom> tag;
                     DetermineRDFQueryRef(aQuerySet->mQueryNode,
                                          getter_AddRefs(tag));
                     if (tag)
                         aQuerySet->SetTag(tag);
@@ -1994,17 +2020,20 @@ nsXULTemplateBuilder::CompileTemplate(ns
             nsCOMPtr<nsIAtom> tag;
             DetermineRDFQueryRef(aQuerySet->mQueryNode, getter_AddRefs(tag));
             if (tag)
                 aQuerySet->SetTag(tag);
 
             nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
             if (!memberVariable) {
                 memberVariable = DetermineMemberVariable(rulenode);
-                if (!memberVariable) continue;
+                if (!memberVariable) {
+                    nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
+                    continue;
+                }
             }
 
             nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
 
             rv = mQueryProcessor->CompileQuery(this, query,
                                                mRefVariable, memberVariable,
                                                getter_AddRefs(aQuerySet->mCompiledQuery));
 
@@ -2230,33 +2259,39 @@ nsXULTemplateBuilder::CompileWhereCondit
     //      startswith - subject must start with object
     //      endswith - subject must end with object
     //      contains - subject must contain object
     //    Comparisons are done as strings unless the subject is an integer.
 
     // subject
     nsAutoString subject;
     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
-    if (subject.IsEmpty())
+    if (subject.IsEmpty()) {
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_SUBJECT);
         return NS_OK;
+    }
 
     nsCOMPtr<nsIAtom> svar;
     if (subject[0] == PRUnichar('?'))
         svar = do_GetAtom(subject);
 
     nsAutoString relstring;
     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relstring);
-    if (relstring.IsEmpty())
+    if (relstring.IsEmpty()) {
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_RELATION);
         return NS_OK;
+    }
 
     // object
     nsAutoString value;
     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
-    if (value.IsEmpty())
+    if (value.IsEmpty()) {
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VALUE);
         return NS_OK;
+    }
 
     // multiple
     PRBool shouldMultiple =
       aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::multiple,
                               nsGkAtoms::_true, eCaseMatters);
 
     nsCOMPtr<nsIAtom> vvar;
     if (!shouldMultiple && (value[0] == PRUnichar('?'))) {
@@ -2283,18 +2318,17 @@ nsXULTemplateBuilder::CompileWhereCondit
         condition = new nsTemplateCondition(svar, relstring, value,
                                             shouldIgnoreCase, shouldNegate, shouldMultiple);
     }
     else if (vvar) {
         condition = new nsTemplateCondition(subject, relstring, vvar,
                                             shouldIgnoreCase, shouldNegate);
     }
     else {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] on <where> test, expected at least one variable", this));
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VAR);
         return NS_OK;
     }
 
     if (! condition)
         return NS_ERROR_OUT_OF_MEMORY;
 
     if (*aCurrentCondition) {
         (*aCurrentCondition)->SetNext(condition);
@@ -2317,34 +2351,19 @@ nsXULTemplateBuilder::CompileBindings(ns
     PRUint32 count = aBindings->GetChildCount();
 
     for (PRUint32 i = 0; i < count; ++i) {
         nsIContent *binding = aBindings->GetChildAt(i);
 
         if (binding->NodeInfo()->Equals(nsGkAtoms::binding,
                                         kNameSpaceID_XUL)) {
             rv = CompileBinding(aRule, binding);
+            if (NS_FAILED(rv))
+                return rv;
         }
-        else {
-#ifdef PR_LOGGING
-            nsAutoString tagstr;
-            binding->NodeInfo()->GetQualifiedName(tagstr);
-
-            nsCAutoString tagstrC;
-            tagstrC.AssignWithConversion(tagstr);
-            PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-                   ("xultemplate[%p] unrecognized binding <%s>",
-                    this, tagstrC.get()));
-#endif
-
-            continue;
-        }
-
-        if (NS_FAILED(rv))
-            return rv;
     }
 
     aRule->AddBindingsToQueryProcessor(mQueryProcessor);
 
     return NS_OK;
 }
 
 
@@ -2359,64 +2378,53 @@ nsXULTemplateBuilder::CompileBinding(nsT
     //            object="?var2" />
     //
     // XXXwaterson Some day it would be cool to allow the 'predicate'
     // to be bound to a variable.
 
     // subject
     nsAutoString subject;
     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
-
     if (subject.IsEmpty()) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] <binding> requires `subject'", this));
-
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
         return NS_OK;
     }
 
     nsCOMPtr<nsIAtom> svar;
     if (subject[0] == PRUnichar('?')) {
         svar = do_GetAtom(subject);
     }
     else {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] <binding> requires `subject' to be a variable", this));
-
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
         return NS_OK;
     }
 
     // predicate
     nsAutoString predicate;
     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
     if (predicate.IsEmpty()) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] <binding> requires `predicate'", this));
-
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_PREDICATE);
         return NS_OK;
     }
 
     // object
     nsAutoString object;
     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
 
     if (object.IsEmpty()) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] <binding> requires `object'", this));
-
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
         return NS_OK;
     }
 
     nsCOMPtr<nsIAtom> ovar;
     if (object[0] == PRUnichar('?')) {
         ovar = do_GetAtom(object);
     }
     else {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] <binding> requires `object' to be a variable", this));
-
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
         return NS_OK;
     }
 
     return aRule->AddBinding(svar, predicate, ovar);
 }
 
 nsresult
 nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule,
@@ -2533,8 +2541,107 @@ nsXULTemplateBuilder::GetResultResource(
         if (NS_FAILED(rv))
             return rv;
 
         return gRDFService->GetUnicodeResource(id, aResource);
     }
 
     return rv;
 }
+
+
+void
+nsXULTemplateBuilder::OutputMatchToLog(nsIRDFResource* aId,
+                                       nsTemplateMatch* aMatch,
+                                       PRBool aIsNew)
+{
+    PRInt32 priority = aMatch->QuerySetPriority() + 1;
+    PRInt32 activePriority = -1;
+
+    nsAutoString msg;
+
+    nsAutoString templateid;
+    mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::id, templateid);
+    msg.AppendLiteral("In template");
+    if (!templateid.IsEmpty()) {
+        msg.AppendLiteral(" with id ");
+        msg.Append(templateid);
+    }
+
+    nsAutoString refstring;
+    aMatch->mResult->GetBindingFor(mRefVariable, refstring);
+    if (!refstring.IsEmpty()) {
+        msg.AppendLiteral(" using ref ");
+        msg.Append(refstring);
+    }
+
+    msg.AppendLiteral("\n    ");
+
+    nsTemplateMatch* match = nsnull;
+    if (mMatchMap.Get(aId, &match)){
+        while (match) {
+            if (match == aMatch)
+                break;
+            if (match->IsActive() &&
+                match->GetContainer() == aMatch->GetContainer()) {
+                activePriority = match->QuerySetPriority() + 1;
+                break;
+            }
+            match = match->mNext;
+        }
+    }
+
+    if (aMatch->IsActive()) {
+        if (aIsNew) {
+            msg.AppendLiteral("New active result for query ");
+            msg.AppendInt(priority);
+            msg.AppendLiteral(" matching rule ");
+            msg.AppendInt(aMatch->RuleIndex() + 1);
+        }
+        else {
+            msg.AppendLiteral("Removed active result for query ");
+            msg.AppendInt(priority);
+            if (activePriority > 0) {
+                msg.AppendLiteral(" (new active query is ");
+                msg.AppendInt(activePriority);
+                msg.Append(')');
+            }
+            else {
+                msg.AppendLiteral(" (no new active query)");
+            }
+        }
+    }
+    else {
+        if (aIsNew) {
+            msg.AppendLiteral("New inactive result for query ");
+            msg.AppendInt(priority);
+            if (activePriority > 0) {
+                msg.AppendLiteral(" (overridden by query ");
+                msg.AppendInt(activePriority);
+                msg.Append(')');
+            }
+            else {
+                msg.AppendLiteral(" (didn't match a rule)");
+            }
+        }
+        else {
+            msg.AppendLiteral("Removed inactive result for query ");
+            msg.AppendInt(priority);
+            if (activePriority > 0) {
+                msg.AppendLiteral(" (active query is ");
+                msg.AppendInt(activePriority);
+                msg.Append(')');
+            }
+            else {
+                msg.AppendLiteral(" (no active query)");
+            }
+        }
+    }
+
+    nsAutoString idstring;
+    nsXULContentUtils::GetTextForNode(aId, idstring);
+    msg.AppendLiteral(": ");
+    msg.Append(idstring);
+
+    nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+    if (cs)
+      cs->LogStringMessage(msg.get());
+}
--- a/content/xul/templates/src/nsXULTemplateBuilder.h
+++ b/content/xul/templates/src/nsXULTemplateBuilder.h
@@ -415,17 +415,18 @@ protected:
     static nsIRDFService*            gRDFService;
     static nsIRDFContainerUtils*     gRDFContainerUtils;
     static nsIScriptSecurityManager* gScriptSecurityManager;
     static nsIPrincipal*             gSystemPrincipal;
     static nsIObserverService*       gObserverService;
 
     enum {
         eDontTestEmpty = (1 << 0),
-        eDontRecurse = (2 << 0)
+        eDontRecurse = (1 << 1),
+        eLoggingEnabled = (1 << 2)
     };
 
     PRInt32 mFlags;
 
     /**
      * Stack-based helper class to maintain a list of ``activated''
      * resources; i.e., resources for which we are currently building
      * content.
@@ -487,16 +488,28 @@ protected:
      * of variables that have changed.
      * @param aResult the ersult for which variable bindings has changed.
      * @param aModifiedVars the set of variables for which the bindings
      * have changed.
      */
     virtual nsresult
     SynchronizeResult(nsIXULTemplateResult* aResult) = 0;
 
+    /**
+     * Output a new match or removed match to the console.
+     *
+     * @param aId id of the result
+     * @param aMatch new or removed match
+     * @param aIsNew true for new matched, false for removed matches
+     */
+    void
+    OutputMatchToLog(nsIRDFResource* aId,
+                     nsTemplateMatch* aMatch,
+                     PRBool aIsNew);
+
     virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const
     {
     }
 
     /**
      * Document that we're observing. Weak ref!
      */
     nsIDocument* mObservedDocument;
--- a/content/xul/templates/src/nsXULTemplateQueryProcessorRDF.cpp
+++ b/content/xul/templates/src/nsXULTemplateQueryProcessorRDF.cpp
@@ -1273,17 +1273,22 @@ nsXULTemplateQueryProcessorRDF::CompileE
     TestNode* prevnode = idnode;
 
     PRUint32 count = aConditions->GetChildCount();
 
     for (PRUint32 i = 0; i < count; ++i) {
         nsIContent *condition = aConditions->GetChildAt(i);
 
         // the <content> condition should always be the first child
-        if (condition->Tag() == nsGkAtoms::content && !i) {
+        if (condition->Tag() == nsGkAtoms::content) {
+            if (i) {
+                nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_CONTENT_NOT_FIRST);
+                continue;
+            }
+
             // check for <content tag='tag'/> which indicates that matches
             // should only be generated for items inside content with that tag
             nsAutoString tagstr;
             condition->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tagstr);
 
             nsCOMPtr<nsIAtom> tag;
             if (! tagstr.IsEmpty()) {
                 tag = do_GetAtom(tagstr);
@@ -1396,56 +1401,43 @@ nsXULTemplateQueryProcessorRDF::CompileT
 
     // subject
     nsAutoString subject;
     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
 
     nsCOMPtr<nsIAtom> svar;
     nsCOMPtr<nsIRDFResource> sres;
     if (subject.IsEmpty()) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] has empty <triple> 'subject'", this));
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT);
         return NS_OK;
     }
-
     if (subject[0] == PRUnichar('?'))
         svar = do_GetAtom(subject);
     else
         gRDFService->GetUnicodeResource(subject, getter_AddRefs(sres));
 
     // predicate
     nsAutoString predicate;
     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
 
     nsCOMPtr<nsIRDFResource> pres;
-    if (predicate.IsEmpty()) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] has empty <triple> 'predicate'", this));
-
+    if (predicate.IsEmpty() || predicate[0] == PRUnichar('?')) {
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE);
         return NS_OK;
     }
-
-    if (predicate[0] == PRUnichar('?')) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] cannot handle variables in <triple> 'predicate'", this));
-
-        return NS_OK;
-    }
-
     gRDFService->GetUnicodeResource(predicate, getter_AddRefs(pres));
 
     // object
     nsAutoString object;
     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
 
     nsCOMPtr<nsIAtom> ovar;
     nsCOMPtr<nsIRDFNode> onode;
     if (object.IsEmpty()) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] has empty <triple> 'object'", this));
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_OBJECT);
         return NS_OK;
     }
 
     if (object[0] == PRUnichar('?')) {
         ovar = do_GetAtom(object);
     }
     else if (object.FindChar(':') != -1) { // XXXwaterson evil.
         // treat as resource
@@ -1468,19 +1460,17 @@ nsXULTemplateQueryProcessorRDF::CompileT
     }
     else if (svar) {
         testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, onode);
     }
     else if (ovar) {
         testnode = new nsRDFPropertyTestNode(aParentNode, this, sres, pres, ovar);
     }
     else {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] tautology in <triple> test", this));
-
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_NO_VAR);
         return NS_OK;
     }
 
     if (! testnode)
         return NS_ERROR_OUT_OF_MEMORY;
 
     // add testnode to mAllTests first. If adding to mRDFTests fails, just
     // leave it in the list so that it can be deleted later.
@@ -1509,32 +1499,28 @@ nsXULTemplateQueryProcessorRDF::CompileM
     //   <member container="?var1" child="?var2" />
     //
 
     // container
     nsAutoString container;
     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::container, container);
 
     if (!container.IsEmpty() && container[0] != PRUnichar('?')) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] on <member> test, expected 'container' attribute to name a variable", this));
-
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR);
         return NS_OK;
     }
 
     nsCOMPtr<nsIAtom> containervar = do_GetAtom(container);
 
     // child
     nsAutoString child;
     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::child, child);
 
     if (!child.IsEmpty() && child[0] != PRUnichar('?')) {
-        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
-               ("xultemplate[%p] on <member> test, expected 'child' attribute to name a variable", this));
-
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCHILDVAR);
         return NS_OK;
     }
 
     nsCOMPtr<nsIAtom> childvar = do_GetAtom(child);
 
     TestNode* testnode =
         new nsRDFConMemberTestNode(aParentNode,
                                    this,
--- a/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp
+++ b/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp
@@ -51,16 +51,17 @@
 #include "nsGkAtoms.h"
 #include "nsIServiceManager.h"
 #include "nsUnicharUtils.h"
 #include "nsIURI.h"
 #include "nsIArray.h"
 #include "nsContentUtils.h"
 #include "nsArrayUtils.h"
 #include "nsPIDOMWindow.h"
+#include "nsXULContentUtils.h"
 
 #include "nsXULTemplateBuilder.h"
 #include "nsXULTemplateQueryProcessorXML.h"
 #include "nsXULTemplateResultXML.h"
 
 NS_IMPL_ISUPPORTS1(nsXMLQuery, nsXMLQuery)
 
 //----------------------------------------------------------------------
@@ -269,17 +270,20 @@ nsXULTemplateQueryProcessorXML::CompileQ
 
     // if an expression is not specified, then the default is to
     // just take all of the children
     if (expr.IsEmpty())
         expr.AssignLiteral("*");
 
     nsCOMPtr<nsIDOMXPathExpression> compiledexpr;
     rv = CreateExpression(expr, aQueryNode, getter_AddRefs(compiledexpr));
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_FAILED(rv)) {
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_XPATH);
+        return rv;
+    }
 
     nsRefPtr<nsXMLQuery> query =
         new nsXMLQuery(this, aMemberVariable, compiledexpr);
     NS_ENSURE_TRUE(query, NS_ERROR_OUT_OF_MEMORY);
 
     PRUint32 count = content->GetChildCount();
     for (PRUint32 i = 0; i < count; ++i) {
         nsIContent *condition = content->GetChildAt(i);
@@ -292,17 +296,20 @@ nsXULTemplateQueryProcessorXML::CompileQ
             condition->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr);
 
             // ignore assignments without a variable or an expression
             if (!var.IsEmpty() && !expr.IsEmpty()) {
                 nsCOMPtr<nsIDOMNode> conditionNode =
                     do_QueryInterface(condition);
                 rv = CreateExpression(expr, conditionNode,
                                       getter_AddRefs(compiledexpr));
-                NS_ENSURE_SUCCESS(rv, rv);
+                if (NS_FAILED(rv)) {
+                    nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_ASSIGN_XPATH);
+                    return rv;
+                }
 
                 nsCOMPtr<nsIAtom> varatom = do_GetAtom(var);
 
                 rv = query->AddBinding(varatom, compiledexpr);
                 NS_ENSURE_SUCCESS(rv, rv);
             }
         }
     }
@@ -373,17 +380,20 @@ nsXULTemplateQueryProcessorXML::AddBindi
         bindings = new nsXMLBindingSet();
         if (!bindings || !mRuleToBindingsMap.Put(aRuleNode, bindings))
             return NS_ERROR_OUT_OF_MEMORY;
     }
 
     nsCOMPtr<nsIDOMXPathExpression> compiledexpr;
     nsresult rv =
         CreateExpression(aExpr, aRuleNode, getter_AddRefs(compiledexpr));
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_FAILED(rv)) {
+        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_BINDING_XPATH);
+        return NS_OK;
+    }
 
     // aRef isn't currently used for XML query processors
     return bindings->AddBinding(aVar, compiledexpr);
 }
 
 NS_IMETHODIMP
 nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports* aDatasource,
                                              const nsAString& aRefString,
--- a/content/xul/templates/src/nsXULTreeBuilder.cpp
+++ b/content/xul/templates/src/nsXULTreeBuilder.cpp
@@ -1673,16 +1673,20 @@ nsXULTreeBuilder::OpenSubtreeForQuerySet
                 IsContainerOpen(nextresult, &isOpen);
                 if (isOpen) {
                     if (open.AppendElement(count) == nsnull)
                         return NS_ERROR_OUT_OF_MEMORY;
                 }
 
                 ++count;
             }
+
+            if (mFlags & eLoggingEnabled)
+                OutputMatchToLog(resultid, newmatch, PR_TRUE);
+
         }
 
         if (prevmatch) {
             prevmatch->mNext = newmatch;
         }
         else if (!mMatchMap.Put(resultid, newmatch)) {
             nsTemplateMatch::Destroy(mPool, newmatch, PR_TRUE);
             return NS_ERROR_OUT_OF_MEMORY;
--- a/content/xul/templates/tests/chrome/Makefile.in
+++ b/content/xul/templates/tests/chrome/Makefile.in
@@ -247,12 +247,14 @@ include $(topsrcdir)/config/rules.mk
 		test_tmpl_xmlquerywithbindinginbindings.xul \
 		test_tmpl_xmlquerywithbindinginrule.xul \
 		test_tmpl_xmlquerywithsort.xul \
 		test_tmpl_xmlquerywithsortotherfield.xul \
 		test_tmpl_xmlquerywithmultiplequeries.xul \
 		test_tmpl_xmlquerywithothertypes.xul \
 		test_tmpl_xmlquerywithinlinedata.xul \
 		test_tmpl_xmlquerywithinlinedatawithmultiplequeries.xul \
+		test_tmpl_invalidqp.xul \
+		test_tmpl_errors.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
--- a/content/xul/templates/tests/chrome/templates_shared.js
+++ b/content/xul/templates/tests/chrome/templates_shared.js
@@ -46,16 +46,18 @@
  * match. This is used, for example, for xml datasources, where the ids set on
  * the generated output are pseudo-random.
  */
 
 const ZOO_NS = "http://www.some-fictitious-zoo.com/";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const debug = false;
 
+var expectedConsoleMessages = [];
+
 try {
   const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].
                 getService(Components.interfaces.nsIRDFService);
   const ContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"].
                            getService(Components.interfaces.nsIRDFContainerUtils);
 } catch(ex) { }
 
 var xmlDoc;
@@ -97,29 +99,35 @@ function test_template()
       usedds = copyRDFDataSource(root, ds);
     if (needsOpen)
       root.open = true;
     setTimeout(iterateChanged, 0, root, usedds);
   }
   else {
     if (needsOpen)
       root.open = false;
+    if (expectedConsoleMessages.length)
+      compareConsoleMessages();
     SimpleTest.finish();
   }
 }
 
 function iterateChanged(root, ds)
 {
+  Components.classes["@mozilla.org/consoleservice;1"].
+             getService(Components.interfaces.nsIConsoleService).reset();
+
   for (var c = 0; c < changes.length; c++) {
     changes[c](ds, root);
     checkResults(root, c + 1);
   }
 
   if (needsOpen)
     root.open = false;
+  compareConsoleMessages();
   SimpleTest.finish();
 }
 
 function checkResults(root, step)
 {
   var output = expectedOutput.copy();
   setForCurrentStep(output, step);
 
@@ -393,8 +401,30 @@ function treeViewToDOMInner(columns, tre
 
         i = treeViewToDOMInner(columns, innertreechildren, view, builder, i + 1, level + 1);
       }
     }
   }
 
   return i;
 }
+
+function expectConsoleMessage(ref, id, isNew, isActive, extra)
+{
+  var message = "In template with id root" +
+                (ref ? " using ref " + ref : "") + "\n    " +
+                (isNew ? "New " : "Removed ") + (isActive ? "active" : "inactive") +
+                " result for query " + extra + ": " + id;
+  expectedConsoleMessages.push(message);
+}
+
+function compareConsoleMessages()
+{
+   var consoleService = Components.classes["@mozilla.org/consoleservice;1"].
+                          getService(Components.interfaces.nsIConsoleService);
+   var out = {};
+   consoleService.getMessageArray(out, {});
+   var messages = out.value || [];
+   is(messages.length, expectedConsoleMessages.length, "correct number of logged messages");
+   for (var m = 0; m < messages.length; m++) {
+     is(messages[m].message, expectedConsoleMessages.shift(), "logged message " + (m + 1));
+   }
+}
new file mode 100644
--- /dev/null
+++ b/content/xul/templates/tests/chrome/test_tmpl_errors.xul
@@ -0,0 +1,276 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<!--
+  tests for templates with invalid syntax
+-->
+
+<window title="XUL Invalid Template Tests" width="500" height="600"
+        onload="runTest();"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
+
+<script>
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var consoleService = Components.classes["@mozilla.org/consoleservice;1"].
+                       getService(Components.interfaces.nsIConsoleService);
+
+function checkConsole(expectedError)
+{
+  var out = {};
+  consoleService.getMessageArray(out, {});
+  var messages = out.value || [];
+  is(messages[0].message, expectedError, "logged message " + expectedError);
+}
+
+// each test consists of a pre function executed before the template build, an
+// expected error message, and a post function executed after the template build
+var tests = [
+
+// <queryset> used in invalid location
+{
+  pre: function(template) template.insertBefore(document.createElement("queryset"), template.lastChild),
+  error: "Error parsing template: unexpected <queryset> element",
+  post: function(queryset) queryset.parentNode.removeChild(queryset)
+},
+
+// no member variable found
+{
+  pre: function(template) $("action").firstChild.removeAttribute("uri"),
+  error: "Error parsing template: no member variable found. Action body should have an element with uri attribute",
+  post: function() $("action").firstChild.setAttribute("uri", "?child")
+},
+
+// bad binding subject
+{
+  pre: function(template) $("binding").removeAttribute("subject"),
+  error: "Error parsing template: <binding> requires a variable for its subject attribute",
+  post: function() $("binding").setAttribute("subject", "?child"),
+},
+
+// bad binding predicate
+{
+  pre: function(template) $("binding").removeAttribute("predicate"),
+  error: "Error parsing template: <binding> element is missing a predicate attribute",
+  post: function() $("binding").setAttribute("predicate", "http://www.some-fictitious-zoo.com/rdf#name"),
+},
+
+// bad binding object
+{
+  pre: function(template) $("binding").setAttribute("object", "blah"),
+  error: "Error parsing template: <binding> requires a variable for its object attribute",
+  post: function() $("binding").setAttribute("object", "?name"),
+},
+
+// where condition missing a subject
+{
+  pre: function(template) { var rule = $("rule");
+                            var where = document.createElement("where");
+                            where.setAttribute("subject", "");
+                            where.setAttribute("rel", "equals");
+                            where.setAttribute("value", "Raven");
+                            rule.appendChild(where);
+                            return where; },
+  error: "Error parsing template: <where> element is missing a subject attribute",
+  post: function(where) { where.parentNode.removeChild(where); }
+},
+
+// where condition missing a rel
+{
+  pre: function(template) { var rule = $("rule");
+                            var where = document.createElement("where");
+                            where.setAttribute("subject", "?name");
+                            where.setAttribute("rel", "");
+                            where.setAttribute("value", "Raven");
+                            rule.appendChild(where);
+                            return where; },
+  error: "Error parsing template: <where> element is missing a rel attribute",
+  post: function(where) { where.parentNode.removeChild(where); }
+},
+
+// where condition missing a value
+{
+  pre: function(template) { var rule = $("rule");
+                            var where = document.createElement("where");
+                            where.setAttribute("subject", "?name");
+                            where.setAttribute("rel", "equals");
+                            where.setAttribute("value", "");
+                            rule.appendChild(where);
+                            return where; },
+  error: "Error parsing template: <where> element is missing a value attribute",
+  post: function(where) { where.parentNode.removeChild(where); }
+},
+
+// where condition missing a variable
+{
+  pre: function(template) { var rule = $("rule");
+                            var where = document.createElement("where");
+                            where.setAttribute("subject", "name");
+                            where.setAttribute("rel", "equals");
+                            where.setAttribute("value", "Raven");
+                            rule.appendChild(where);
+                            return where; },
+  error: "Error parsing template: <where> element must have at least one variable as a subject or value",
+  post: function(where) { where.parentNode.removeChild(where); }
+},
+
+// bad member container
+{
+  pre: function(template) $("member").setAttribute("container", "blah"),
+  error: "Error parsing template: <member> requires a variable for its container attribute",
+  post: function() $("member").setAttribute("container", "?uri"),
+},
+
+// bad member child
+{
+  pre: function(template) $("member").setAttribute("child", "blah"),
+  error: "Error parsing template: <member> requires a variable for its child attribute",
+  post: function() $("member").setAttribute("child", "?child"),
+},
+
+// bad triple subject
+{
+  pre: function(template) $("triple").removeAttribute("subject"),
+  error: "Error parsing template: <triple> requires a variable for its subject attribute",
+  post: function() $("triple").setAttribute("subject", "?child"),
+},
+
+// missing triple predicate
+{
+  pre: function(template) $("triple").removeAttribute("predicate"),
+  error: "Error parsing template: <triple> should have a non-variable value as a predicate",
+  post: function() $("triple").setAttribute("predicate", "http://www.some-fictitious-zoo.com/rdf#name"),
+},
+
+// bad triple predicate
+{
+  pre: function(template) $("triple").setAttribute("predicate", "?predicate"),
+  error: "Error parsing template: <triple> should have a non-variable value as a predicate",
+  post: function() $("triple").setAttribute("predicate", "http://www.some-fictitious-zoo.com/rdf#name"),
+},
+
+// bad triple object
+{
+  pre: function(template) $("triple").removeAttribute("object"),
+  error: "Error parsing template: <triple> requires a variable for its object attribute",
+  post: function() $("triple").setAttribute("object", "?name"),
+},
+
+// content not first element in query
+{
+  pre: function(template) { var content = $("content"); content.parentNode.appendChild(content); return content; },
+  error: "Error parsing template: expected <content> to be first",
+  post: function(content) content.parentNode.insertBefore(content, content.parentNode.firstChild),
+},
+
+// member container variable not bound
+{
+  pre: function(template) $("member").removeAttribute("container"),
+  error: "Error parsing template: neither container or child variables of <member> has a value",
+  post: function() $("member").setAttribute("container", "?uri"),
+},
+
+// neither triple subject or object variable are bound
+{
+  pre: function(template) $("triple").setAttribute("subject", "?blah"),
+  error: "Error parsing template: neither subject or object variables of <triple> has a value",
+  post: function() $("triple").setAttribute("subject", "?child"),
+},
+
+// neither triple subject or object variable are bound
+{
+  pre: function(template) { var triple = $("triple"); triple.setAttribute("subject", "blah");
+                            triple.setAttribute("object", "blah"); },
+  error: "Error parsing template: <triple> should have at least one variable as a subject or object",
+  post: function() { var triple = $("triple"); triple.setAttribute("subject", "?uri");
+                     triple.setAttribute("object", "?uri") }
+},
+
+// could not parse xml query expression
+{
+  firstXMLTest: true,
+  pre: function(template) { $("query").setAttribute("expr", "something()"); },
+  error: "Error parsing template: XPath expression in query could not be parsed",
+  post: function() { }
+},
+
+// could not parse xml assign expression
+{
+  pre: function(template) { var query = $("query");
+                            query.setAttribute("expr", "*");
+                            var assign = document.createElement("assign");
+                            assign.setAttribute("var", "?name");
+                            assign.setAttribute("expr", "something()");
+                            query.appendChild(assign);
+                            return assign; },
+  error: "Error parsing template: XPath expression in <assign> could not be parsed",
+  post: function(assign) { assign.parentNode.removeChild(assign); }
+},
+
+// could not parse xml binding expression
+{
+  pre: function(template) { $("binding").setAttribute("predicate", "something()"); },
+  error: "Error parsing template: XPath expression in <binding> could not be parsed",
+  post: function() { $("binding").setAttribute("predicate", "[name]"); },
+},
+
+];
+
+function runTest()
+{
+  var root = $("root");
+  var template = $("template");
+  while (test = tests.shift()) {
+    consoleService.reset();
+    var context = test.pre(template);
+    root.builder.rebuild();
+    checkConsole(test.error);
+    test.post(context);
+
+    // preload and set up for the xml datasource query error tests
+    if (tests.length && tests[0].firstXMLTest) {
+      var src = window.location.href.replace(/test_tmpl.*xul/, "animals.xml");
+      xmlDoc = new XMLHttpRequest();
+      xmlDoc.open("get", src, false);
+      xmlDoc.send(null);
+
+      var root = $("root");
+      root.setAttribute("querytype", "xml");
+      root.setAttribute("datasources", "animals.xml");
+      $("binding").setAttribute("predicate", "[name]");
+
+      setTimeout(runTest, 0);
+      return;
+    }
+  }
+  SimpleTest.finish();
+}
+
+]]>
+</script>
+
+<vbox id="root" datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/birds">
+<template id="template">
+  <query id="query">
+    <content id="content" uri="?uri"/>
+    <member id="member" container="?uri" child="?child"/>
+    <triple id="triple" subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/>
+  </query>
+  <rule id="rule">
+    <binding id="binding" subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/>
+    <action id="action">
+      <label uri="?child" value="?name"/>
+    </action>
+  </rule>
+</template>
+</vbox>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/content/xul/templates/tests/chrome/test_tmpl_invalidqp.xul
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<!--
+  invalid syntax - querytype="blah"
+-->
+
+<window title="XUL Template Tests" width="500" height="600"
+        onload="test_template();"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
+
+<script src="templates_shared.js"/>
+
+<script>
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var testid ="invalid syntax";
+var queryType = "rdf";
+var isTreeBuilder = false;
+var needsOpen = false;
+var notWorkingYet = false;
+var notWorkingYetDynamic = false;
+var expectedOutput = <output/>;
+
+Components.classes["@mozilla.org/consoleservice;1"].
+           getService(Components.interfaces.nsIConsoleService).reset();
+expectedConsoleMessages.push("Error parsing template: querytype attribute doesn't specify a valid query processor");
+
+var changes = [];
+]]>
+</script>
+
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.rdf"
+      ref="http://www.some-fictitious-zoo.com/birds" querytype="blah">
+<template zoo:name="Barn Owl" xmlns:zoo="http://www.some-fictitious-zoo.com/rdf#">
+<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/>
+</template>
+</vbox>
+
+</window>
--- a/content/xul/templates/tests/chrome/test_tmpl_querysettwo.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_querysettwo.xul
@@ -42,48 +42,59 @@ var changes = [
   // step 1
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('Emperor Penguin'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'birds'));
     container.AppendElement(newnode);
+    expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/emperorpenguin', true, true,
+                         '2 matching rule 1');
   },
   // step 2
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('Archaeopteryx'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'birds'));
     container.InsertElementAt(newnode, '4', true);
+    expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/archaeopteryx', true, true,
+                         '2 matching rule 1');
   },
   // step 3
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'birds/wren');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('Wren'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'birds'));
     container.InsertElementAt(newnode, '1', true);
+    expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/wren', true, true,
+                         '2 matching rule 1');
   },
   // step 4
   function(targetds, root) {
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'birds'));
     var removednode = container.RemoveElementAt('3', true);
     targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                       RDF.GetLiteral('Barn Owl'), true);
+    expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/barnowl', false, false,
+                         '2 (active query is 1)');
+    expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/barnowl', false, true,
+                         '1 (no new active query)');
   }
 ];
 ]]>
 </script>
 
-<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/birds">
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" flags="logging"
+      datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/birds">
 <template>
 <queryset>
 <query>
 <content uri="?uri"/>
 <member container="?uri" child="?animal"/>
 <triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="Barn Owl"/>
 <triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/>
 </query>
--- a/content/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivemultiplerules.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivemultiplerules.xul
@@ -116,83 +116,121 @@ var expectedOutput =
       </treerow>
     </treeitem>
     <treeitem step="3,-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false">
       <treerow>
         <treecell label="Crustaceans"/>
         <treecell/>
       </treerow>
     </treeitem>
-    <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false" open="true">
+    <treeitem step="5" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false" open="true">
       <treerow>
         <treecell label="Crustaceans"/>
         <treecell/>
       </treerow>
       <treechildren>
         <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/lobster">
           <treerow>
             <treecell/>
             <treecell label="Is this cool: Lobster?"/>
           </treerow>
         </treeitem>
+        <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/crayfish">
+          <treerow>
+            <treecell label="Crayfish"/>
+            <treecell/>
+          </treerow>
+        </treeitem>
       </treechildren>
     </treeitem>
     <treeitem id="http://www.some-fictitious-zoo.com/birds/emu">
       <treerow>
         <treecell label="Emu"/>
         <treecell/>
       </treerow>
     </treeitem>
   </treechildren>
 </output>;
 
 var changes = [
   // step 1
   function(targetds, root) {
     if (root.view && 1 < root.view.rowCount  && root.view.isContainer(1))
     root.view.toggleOpenState(1);
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/lion', true, true,
+                         '1 matching rule 1');
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/hippopotamus', true, true,
+                         '1 matching rule 2');
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/africanelephant', true, true,
+                         '1 matching rule 1');
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/llama', true, true,
+                         '1 matching rule 1');
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/polarbear', true, true,
+                         '1 matching rule 1');
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/aardvark', true, true,
+                         '1 matching rule 2');
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/ninebandedarmadillo', true, true,
+                         '1 matching rule 1');
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/gorilla', true, true,
+                         '1 matching rule 1');
   },
   // step 2
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('Koala'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'mammals'));
     container.InsertElementAt(newnode, '4', true);
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/koala', true, true,
+                         '1 matching rule 1');
   },
   // step 3
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('Lobster'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'crustaceans'));
     container.AppendElement(newnode);
   },
   // step 4
   function(targetds, root) {
-    if (root.view && 11 < root.view.rowCount  && root.view.isContainer(11))
-    root.view.toggleOpenState(11);
+    var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/crayfish');
+    targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
+                    RDF.GetLiteral('Crayfish'), true);
+    var container = ContainerUtils.MakeSeq(targetds,
+                      RDF.GetResource(ZOO_NS + 'crustaceans'));
+    container.AppendElement(newnode);
   },
   // step 5
   function(targetds, root) {
     if (root.view && 11 < root.view.rowCount  && root.view.isContainer(11))
     root.view.toggleOpenState(11);
+    expectConsoleMessage(ZOO_NS + 'crustaceans', ZOO_NS + 'crustaceans/lobster', true, true,
+                         '1 matching rule 1');
+    expectConsoleMessage(ZOO_NS + 'crustaceans', ZOO_NS + 'crustaceans/crayfish', true, true,
+                         '1 matching rule 2');
   },
   // step 6
   function(targetds, root) {
+    if (root.view && 11 < root.view.rowCount  && root.view.isContainer(11))
+    root.view.toggleOpenState(11);
+  },
+  // step 7
+  function(targetds, root) {
     if (root.view && 1 < root.view.rowCount  && root.view.isContainer(1))
     root.view.toggleOpenState(1);
   }
 ];
 ]]>
 </script>
 
-<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/marked" id="root">
+<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true"
+      flags="logging" datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/marked" id="root">
 <treecols orient="horizontal" id="treecols">
 <treecol id="treecol" primary="true" label="Name"/>
 <treecol label="Species"/>
 </treecols>
 <template id="template">
 <query>
 <content uri="?uri"/>
 <member container="?uri" child="?child"/>
--- a/content/xul/templates/tests/chrome/test_tmpl_whereequalsmultiplenegation.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_whereequalsmultiplenegation.xul
@@ -43,63 +43,74 @@ var changes = [
   // step 1
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('Arctic Hare'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'mammals'));
     container.AppendElement(newnode);
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/arctichare', true, true,
+                         '1 matching rule 1');
   },
   // step 2
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('Koala'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'mammals'));
     container.InsertElementAt(newnode, '4', true);
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/koala', true, false,
+                         '1 (didn\'t match a rule)');
   },
   // step 3
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('Zebra'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'mammals'));
     container.InsertElementAt(newnode, '1', true);
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/zebra', true, true,
+                         '1 matching rule 1');
   },
   // step 4
   function(targetds, root) {
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'mammals'));
     var removednode = container.RemoveElementAt('4', true);
     targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                       RDF.GetLiteral('African Elephant'), true);
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/africanelephant', false, true,
+                         '1 (no new active query)');
   },
   // step 5
   function(targetds, root) {
     var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant');
     targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'),
                     RDF.GetLiteral('African Elephant'), true);
     var container = ContainerUtils.MakeSeq(targetds,
                       RDF.GetResource(ZOO_NS + 'mammals'));
     container.AppendElement(newnode);
+    expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/africanelephant', true, true,
+                         '1 matching rule 1');
   },
   // step 6
   function(targetds, root) {
     targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'),
                     RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'),
                     RDF.GetLiteral('8'), true);
   }
 ];
 ]]>
 </script>
 
-<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/mammals">
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" flags="logging"
+      datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/mammals">
 <template id="template">
 <query id="query">
 <content uri="?uri"/>
 <member container="?uri" child="?animal"/>
 <triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/>
 </query>
 <rule>
 <conditions id="conditions">
--- a/content/xul/templates/tests/chrome/test_tmpl_wherenorel.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_wherenorel.xul
@@ -17,16 +17,20 @@
   <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
 
 <script src="templates_shared.js"/>
 
 <script>
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
 
+Components.classes["@mozilla.org/consoleservice;1"].
+           getService(Components.interfaces.nsIConsoleService).reset();
+expectedConsoleMessages.push("Error parsing template: <where> element is missing a rel attribute");
+
 var testid ="where - no rel";
 var queryType = "rdf";
 var isTreeBuilder = false;
 var needsOpen = false;
 var notWorkingYet = false;
 var notWorkingYetDynamic = false;
 var expectedOutput =
 <output>
--- a/content/xul/templates/tests/chrome/test_tmpl_wherenosubject.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_wherenosubject.xul
@@ -17,16 +17,20 @@
   <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
 
 <script src="templates_shared.js"/>
 
 <script>
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
 
+Components.classes["@mozilla.org/consoleservice;1"].
+           getService(Components.interfaces.nsIConsoleService).reset();
+expectedConsoleMessages.push("Error parsing template: <where> element is missing a subject attribute");
+
 var testid ="where - no subject";
 var queryType = "rdf";
 var isTreeBuilder = false;
 var needsOpen = false;
 var notWorkingYet = false;
 var notWorkingYetDynamic = false;
 var expectedOutput =
 <output>
--- a/content/xul/templates/tests/chrome/test_tmpl_wherenovalue.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_wherenovalue.xul
@@ -17,16 +17,20 @@
   <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
 
 <script src="templates_shared.js"/>
 
 <script>
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
 
+Components.classes["@mozilla.org/consoleservice;1"].
+           getService(Components.interfaces.nsIConsoleService).reset();
+expectedConsoleMessages.push("Error parsing template: <where> element is missing a value attribute");
+
 var testid ="where - no value";
 var queryType = "rdf";
 var isTreeBuilder = false;
 var needsOpen = false;
 var notWorkingYet = false;
 var notWorkingYetDynamic = false;
 var expectedOutput =
 <output>
--- a/content/xul/templates/tests/chrome/test_tmpl_xmlquerywithbindinginrule.xul
+++ b/content/xul/templates/tests/chrome/test_tmpl_xmlquerywithbindinginrule.xul
@@ -2,17 +2,17 @@
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
 
 <!--
   xml query with binding in rule
 -->
 
 <window title="XUL Template Tests" width="500" height="600"
-        onload="test_template();"
+        onload="expectLoggedMessages(); test_template();"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
 
@@ -29,21 +29,33 @@ var needsOpen = false;
 var notWorkingYet = false;
 var notWorkingYetDynamic = false;
 var expectedOutput =
 <output>
   <label anyid="true" container="true" empty="true" value="Class Reptiles"/>
   <label anyid="true" container="true" empty="true" value="Class Birds"/>
 </output>;
 
+Components.classes["@mozilla.org/consoleservice;1"].
+           getService(Components.interfaces.nsIConsoleService).reset();
+
+function expectLoggedMessages()
+{
+  // check log to ensure that two rows have been added
+  var initialNumber = Number(document.getElementById("root").firstChild.nextSibling.id.substring(3));
+  expectConsoleMessage('', 'row' + initialNumber, true, true, '1 matching rule 1');
+  expectConsoleMessage('', 'row' + (initialNumber + 1), true, true, '1 matching rule 1');
+}
+
 var changes = [];
 ]]>
 </script>
 
-<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref=".">
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root"
+      flags="logging" datasources="animals.xml" querytype="xml" ref=".">
 <template>
 <query expr="class/name"/>
 <rule id="rule">
 <binding subject="?" predicate="concat('Class ', text())" object="?text"/>
 <action>
 <label uri="?" value="?text"/>
 </action>
 </rule>
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -46,16 +46,17 @@
 #define FORCE_PR_LOG 1
 #endif
 
 #include "nsIBrowserDOMWindow.h"
 #include "nsIComponentManager.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
+#include "nsIDOM3Document.h"
 #include "nsIDOMNSDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMStorageObsolete.h"
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 #include "nsIDocumentViewer.h"
 #include "nsIDocumentLoaderFactory.h"
 #include "nsCURILoader.h"
@@ -2139,16 +2140,17 @@ GetPrincipalDomain(nsIPrincipal* aPrinci
   if (NS_FAILED(rv))
       return rv;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
+                                          const nsAString& aDocumentURI,
                                           PRBool aCreate,
                                           nsIDOMStorage** aStorage)
 {
     NS_ENSURE_ARG_POINTER(aStorage);
     *aStorage = nsnull;
 
     if (!aPrincipal)
         return NS_OK;
@@ -2160,17 +2162,19 @@ nsDocShell::GetSessionStorageForPrincipa
     if (NS_FAILED(rv))
         return rv;
 
     if (!topItem)
         return NS_ERROR_FAILURE;
 
     nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
     if (topDocShell != this)
-        return topDocShell->GetSessionStorageForPrincipal(aPrincipal, aCreate,
+        return topDocShell->GetSessionStorageForPrincipal(aPrincipal,
+                                                          aDocumentURI,
+                                                          aCreate,
                                                           aStorage);
 
     nsCAutoString currentDomain;
     rv = GetPrincipalDomain(aPrincipal, currentDomain);
     if (NS_FAILED(rv))
         return rv;
 
     if (currentDomain.IsEmpty())
@@ -2180,51 +2184,91 @@ nsDocShell::GetSessionStorageForPrincipa
         nsCOMPtr<nsIDOMStorage> newstorage =
             do_CreateInstance("@mozilla.org/dom/storage;2");
         if (!newstorage)
             return NS_ERROR_OUT_OF_MEMORY;
 
         nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
         if (!pistorage)
             return NS_ERROR_FAILURE;
-        rv = pistorage->InitAsSessionStorage(aPrincipal);
+        rv = pistorage->InitAsSessionStorage(aPrincipal, aDocumentURI);
         if (NS_FAILED(rv))
             return rv;
 
         if (!mStorages.Put(currentDomain, newstorage))
             return NS_ERROR_OUT_OF_MEMORY;
 
         newstorage.swap(*aStorage);
-        return NS_OK;
-    }
-
-    nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
-    if (piStorage) {
-        PRBool canAccess = piStorage->CanAccess(aPrincipal);
-        NS_ASSERTION(canAccess,
-                     "GetSessionStorageForPrincipal got a storage "
-                     "that could not be accessed!");
-        if (!canAccess) {
-            NS_RELEASE(*aStorage);
-            return NS_ERROR_DOM_SECURITY_ERR;
-        }
+#if defined(PR_LOGGING) && defined(DEBUG)
+        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+               ("nsDocShell[%p]: created a new sessionStorage %p",
+                this, *aStorage));
+#endif
+    }
+    else if (*aStorage) {
+      nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
+      if (piStorage) {
+          PRBool canAccess = piStorage->CanAccess(aPrincipal);
+          NS_ASSERTION(canAccess,
+                       "GetSessionStorageForPrincipal got a storage "
+                       "that could not be accessed!");
+          if (!canAccess) {
+              NS_RELEASE(*aStorage);
+              return NS_ERROR_DOM_SECURITY_ERR;
+          }
+      }
+
+#if defined(PR_LOGGING) && defined(DEBUG)
+      PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+             ("nsDocShell[%p]: returns existing sessionStorage %p",
+              this, *aStorage));
+#endif
+    }
+
+    if (aCreate) {
+        // We are asked to create a new storage object. This indicates
+        // that a new windows wants it. At this moment we "fork" the existing
+        // storage object (what it means is described in the paragraph bellow).
+        // We must create a single object per a single window to distinguish
+        // a window originating oparations on the storage object to succesfully
+        // prevent dispatch of a storage event to this same window that ivoked
+        // a change in its storage. We also do this to correctly fill
+        // documentURI property in the storage event.
+        //
+        // The difference between clone and fork is that clone creates
+        // a completelly new and independent storage, but fork only creates
+        // a new object wrapping the storage implementation and data and
+        // the forked storage then behaves completelly the same way as
+        // the storage it has been forked of, all such forked storage objects
+        // shares their state and data and change on one such object affects
+        // all others the same way.
+        nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
+        nsCOMPtr<nsIDOMStorage> fork = piStorage->Fork(aDocumentURI);
+#if defined(PR_LOGGING) && defined(DEBUG)
+        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+               ("nsDocShell[%p]: forked sessionStorage %p to %p",
+                this, *aStorage, fork.get()));
+#endif
+        fork.swap(*aStorage);
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
+                                    const nsAString& aDocumentURI,
                                     nsIDOMStorage** aStorage)
 {
-    return GetSessionStorageForURI(aURI, PR_TRUE, aStorage);
+    return GetSessionStorageForURI(aURI, aDocumentURI, PR_TRUE, aStorage);
 }
 
 nsresult
 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
+                                    const nsSubstring& aDocumentURI,
                                     PRBool aCreate,
                                     nsIDOMStorage** aStorage)
 {
     NS_ENSURE_ARG(aURI);
     NS_ENSURE_ARG_POINTER(aStorage);
 
     *aStorage = nsnull;
 
@@ -2235,17 +2279,17 @@ nsDocShell::GetSessionStorageForURI(nsIU
     NS_ENSURE_SUCCESS(rv, rv);
 
     // This is terrible hack and should go away along with this whole method.
     nsCOMPtr<nsIPrincipal> principal;
     rv = securityManager->GetCodebasePrincipal(aURI, getter_AddRefs(principal));
     if (NS_FAILED(rv))
         return rv;
 
-    return GetSessionStorageForPrincipal(principal, aCreate, aStorage);
+    return GetSessionStorageForPrincipal(principal, aDocumentURI, aCreate, aStorage);
 }
 
 nsresult
 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
                               nsIDOMStorage* aStorage)
 {
     NS_ENSURE_ARG_POINTER(aStorage);
 
@@ -2267,16 +2311,21 @@ nsDocShell::AddSessionStorage(nsIPrincip
 
             if (currentDomain.IsEmpty())
                 return NS_ERROR_FAILURE;
 
             // Do not replace an existing session storage.
             if (mStorages.GetWeak(currentDomain))
                 return NS_ERROR_NOT_AVAILABLE;
 
+#if defined(PR_LOGGING) && defined(DEBUG)
+            PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+                   ("nsDocShell[%p]: was added a sessionStorage %p",
+                    this, aStorage));
+#endif
             if (!mStorages.Put(currentDomain, aStorage))
                 return NS_ERROR_OUT_OF_MEMORY;
         }
         else {
             return topDocShell->AddSessionStorage(aPrincipal, aStorage);
         }
     }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -600,16 +600,17 @@ protected:
     // Call this when a URI load is handed to us (via OnLinkClick or
     // InternalLoad).  This makes sure that we're not inside unload, or that if
     // we are it's still OK to load this URI.
     PRBool IsOKToLoadURI(nsIURI* aURI);
     
     void ReattachEditorToWindow(nsISHEntry *aSHEntry);
 
     nsresult GetSessionStorageForURI(nsIURI* aURI,
+                                     const nsSubstring& aDocumentURI,
                                      PRBool create,
                                      nsIDOMStorage** aStorage);
 
     // helpers for executing commands
     nsresult GetControllerForCommand(const char *inCommand,
                                      nsIController** outController);
     nsresult IsCommandEnabled(const char * inCommand, PRBool* outEnabled);
     nsresult DoCommand(const char * inCommand);
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -65,17 +65,17 @@ interface nsIInputStream;
 interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 
-[scriptable, uuid(1067ee1a-08e8-455d-9b12-19eeeee56b8e)]
+[scriptable, uuid(c95eaff1-14e6-4db1-a806-46be97d5a9b6)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -425,27 +425,33 @@ interface nsIDocShell : nsISupports
    */
   void historyPurged(in long numEntries);
 
   /*
    * Retrieves the WebApps session storage object for the supplied domain.
    * If it doesn't already exist, a new one will be created.
    *
    * @param uri the uri of the storage object to retrieve
+   * @param documentURI new storage will be created with reference to this
+   *                    document.documentURI that will appear in storage event
    */
-  nsIDOMStorage getSessionStorageForURI(in nsIURI uri);
+  nsIDOMStorage getSessionStorageForURI(in nsIURI uri,
+                                        in DOMString documentURI);
 
   /*
    * Retrieves the WebApps session storage object for the supplied principal.
    *
    * @param principal returns a storage for this principal
+   * @param documentURI new storage will be created with reference to this
+   *                    document.documentURI that will appear in storage event
    * @param create If true and a session storage object doesn't
    *               already exist, a new one will be created.
    */
   nsIDOMStorage getSessionStorageForPrincipal(in nsIPrincipal principal,
+                                              in DOMString documentURI,
                                               in boolean create);
 
   /*
    * Add a WebApps session storage object to the docshell.
    *
    * @param principal the principal the storage object is associated with
    * @param storage the storage object to add
    */
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -107,13 +107,11 @@ include $(topsrcdir)/dom/dom-config.mk
 
 ifdef MOZ_JSDEBUGGER
 DEFINES += -DMOZ_JSDEBUGGER
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 ifdef MOZ_X11
-ifdef MOZ_ENABLE_GTK2
 CXXFLAGS += $(TK_CFLAGS)
 LDFLAGS += $(TK_LIBS)
 endif
-endif
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1197,16 +1197,18 @@ static nsDOMClassInfoData sClassInfoData
                            nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |
                            nsIXPCScriptable::WANT_NEWENUMERATE)
   NS_DEFINE_CLASSINFO_DATA(StorageList, nsStorageListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(StorageItem, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(StorageEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(StorageEventObsolete, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DOMParser, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(XMLSerializer, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XMLHttpProgressEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -3482,16 +3484,20 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageItem)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMToString)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(StorageEvent, nsIDOMStorageEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageEvent)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(StorageEventObsolete, nsIDOMStorageEventObsolete)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageEventObsolete)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(DOMParser, nsIDOMParser)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMParser)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMParserJS)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XMLSerializer, nsIDOMSerializer)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSerializer)
   DOM_CLASSINFO_MAP_END
--- a/dom/base/nsDOMClassInfoID.h
+++ b/dom/base/nsDOMClassInfoID.h
@@ -378,16 +378,17 @@ enum nsDOMClassInfoID {
   eDOMClassInfo_XPathResult_id,
 
   // WhatWG WebApps Objects
   eDOMClassInfo_StorageObsolete_id,
   eDOMClassInfo_Storage_id,
   eDOMClassInfo_StorageList_id,
   eDOMClassInfo_StorageItem_id,
   eDOMClassInfo_StorageEvent_id,
+  eDOMClassInfo_StorageEventObsolete_id,
 
   // DOMParser, XMLSerializer
   eDOMClassInfo_DOMParser_id,
   eDOMClassInfo_XMLSerializer_id,
 
   // XMLHttpRequest
   eDOMClassInfo_XMLHttpProgressEvent_id,
   eDOMClassInfo_XMLHttpRequest_id,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -96,28 +96,30 @@
 #include "nsIEditorDocShell.h"
 #include "nsIDocCharset.h"
 #include "nsIDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMCrypto.h"
 #include "nsIDOMDocument.h"
+#include "nsIDOM3Document.h"
 #include "nsIDOMNSDocument.h"
 #include "nsIDOMDocumentView.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMessageEvent.h"
 #include "nsIDOMPopupBlockedEvent.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsIDOMGeoGeolocation.h"
+#include "nsPIDOMStorage.h"
 #include "nsDOMString.h"
 #include "nsIEmbeddingSiteWindow2.h"
 #include "nsThreadUtils.h"
 #include "nsIEventStateManager.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsIJSContextStack.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIMarkupDocumentViewer.h"
@@ -648,19 +650,19 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mIsChrome(PR_FALSE),
     mNeedsFocus(PR_TRUE),
     mHasFocus(PR_FALSE),
     mHasAcceleration(PR_FALSE),
     mTimeoutInsertionPoint(nsnull),
     mTimeoutPublicIdCounter(1),
     mTimeoutFiringDepth(0),
     mJSObject(nsnull),
-    mPendingStorageEvents(nsnull),
     mTimeoutsSuspendDepth(0),
-    mFocusMethod(0)
+    mFocusMethod(0),
+    mPendingStorageEventsObsolete(nsnull)
 #ifdef DEBUG
     , mSetOpenerWindowCalled(PR_FALSE)
 #endif
 {
   memset(mScriptGlobals, 0, sizeof(mScriptGlobals));
   nsLayoutStatics::AddRef();
 
   // Initialize the PRCList (this).
@@ -682,16 +684,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
       if (os) {
         // Watch for online/offline status changes so we can fire events. Use
         // a strong reference.
         os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
                         PR_FALSE);
 
         // Watch for dom-storage-changed so we can fire storage
         // events. Use a strong reference.
+        os->AddObserver(mObserver, "dom-storage2-changed", PR_FALSE);
         os->AddObserver(mObserver, "dom-storage-changed", PR_FALSE);
       }
     }
   } else {
     // |this| is an outer window. Outer windows start out frozen and
     // remain frozen until they get an inner window, so freeze this
     // outer window here.
     Freeze();
@@ -768,16 +771,17 @@ nsGlobalWindow::~nsGlobalWindow()
            ("DOMWINDOW %p destroyed", this));
 #endif
 
   if (mObserver) {
     nsCOMPtr<nsIObserverService> os =
       do_GetService("@mozilla.org/observer-service;1");
     if (os) {
       os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
+      os->RemoveObserver(mObserver, "dom-storage2-changed");
       os->RemoveObserver(mObserver, "dom-storage-changed");
     }
 
     // Drop its reference to this dying window, in case for some bogus reason
     // the object stays around.
     mObserver->Forget();
     NS_RELEASE(mObserver);
   }
@@ -821,17 +825,17 @@ nsGlobalWindow::~nsGlobalWindow()
   NS_ASSERTION(!mArguments, "mArguments wasn't cleaned up properly!");
 
   CleanUp();
 
 #ifdef DEBUG
   nsCycleCollector_DEBUG_wasFreed(static_cast<nsIScriptGlobalObject*>(this));
 #endif
 
-  delete mPendingStorageEvents;
+  delete mPendingStorageEventsObsolete;
 
   nsLayoutStatics::Release();
 }
 
 // static
 void
 nsGlobalWindow::ShutDown()
 {
@@ -1725,16 +1729,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
   // Set mDocument even if this is an outer window to avoid
   // having to *always* reach into the inner window to find the
   // document.
 
   mDocument = do_QueryInterface(aDocument);
   mDoc = aDocument;
   mLocalStorage = nsnull;
+  mSessionStorage = nsnull;
 
 #ifdef DEBUG
   mLastOpenedURI = aDocument->GetDocumentURI();
 #endif
 
   if (IsOuterWindow()) {
     NS_STID_FOR_ID(st_id) {
       nsIScriptContext *langContext = GetScriptContextInternal(st_id);
@@ -5508,16 +5513,43 @@ public:
     if (mWindow)
       mWindow->ReallyCloseWindow();
     return NS_OK;
   }
 
   nsRefPtr<nsGlobalWindow> mWindow;
 };
 
+PRBool
+nsGlobalWindow::CanClose()
+{
+  if (!mDocShell)
+    return PR_TRUE;
+
+  // Ask the content viewer whether the toplevel window can close.
+  // If the content viewer returns false, it is responsible for calling
+  // Close() as soon as it is possible for the window to close.
+  // This allows us to not close the window while printing is happening.
+
+  nsCOMPtr<nsIContentViewer> cv;
+  mDocShell->GetContentViewer(getter_AddRefs(cv));
+  if (cv) {
+    PRBool canClose;
+    nsresult rv = cv->PermitUnload(PR_FALSE, &canClose);
+    if (NS_SUCCEEDED(rv) && !canClose)
+      return PR_FALSE;
+
+    rv = cv->RequestWindowClose(&canClose);
+    if (NS_SUCCEEDED(rv) && !canClose)
+      return PR_FALSE;
+  }
+
+  return PR_TRUE;
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::Close()
 {
   FORWARD_TO_OUTER(Close, (), NS_ERROR_NOT_INITIALIZED);
 
   if (IsFrame() || !mDocShell || IsInModalState()) {
     // window.close() is called on a frame in a frameset, on a window
     // that's already closed, or on a window for which there's
@@ -5537,17 +5569,16 @@ nsGlobalWindow::Close()
     // A script's popup has been blocked and we don't want
     // the window to be closed directly after this event,
     // so the user can see that there was a blocked popup.
     return NS_OK;
   }
 
   // Don't allow scripts from content to close windows
   // that were not opened by script
-  nsresult rv = NS_OK;
   if (!mHadOriginalOpener && !nsContentUtils::IsCallerTrustedForWrite()) {
     PRBool allowClose =
       nsContentUtils::GetBoolPref("dom.allow_scripts_to_close_windows",
                                   PR_TRUE);
     if (!allowClose) {
       // We're blocking the close operation
       // report localized error msg in JS console
       nsContentUtils::ReportToConsole(
@@ -5559,34 +5590,18 @@ nsGlobalWindow::Close()
           EmptyString(), 0, 0, // No source, or column/line number
           nsIScriptError::warningFlag,
           "DOM Window");  // Better name for the category?
 
       return NS_OK;
     }
   }
 
-  // Ask the content viewer whether the toplevel window can close.
-  // If the content viewer returns false, it is responsible for calling
-  // Close() as soon as it is possible for the window to close.
-  // This allows us to not close the window while printing is happening.
-
-  nsCOMPtr<nsIContentViewer> cv;
-  mDocShell->GetContentViewer(getter_AddRefs(cv));
-  if (!mInClose && !mIsClosed && cv) {
-    PRBool canClose;
-
-    rv = cv->PermitUnload(PR_FALSE, &canClose);
-    if (NS_SUCCEEDED(rv) && !canClose)
-      return NS_OK;
-
-    rv = cv->RequestWindowClose(&canClose);
-    if (NS_SUCCEEDED(rv) && !canClose)
-      return NS_OK;
-  }
+  if (!mInClose && !mIsClosed && !CanClose())
+    return NS_OK;
 
   // Fire a DOM event notifying listeners that this window is about to
   // be closed. The tab UI code may choose to cancel the default
   // action for this event, if so, we won't actually close the window
   // (since the tab UI code will close the tab in stead). Sure, this
   // could be abused by content code, but do we care? I don't think
   // so...
 
@@ -5596,16 +5611,46 @@ nsGlobalWindow::Close()
   if (!DispatchCustomEvent("DOMWindowClose")) {
     // Someone chose to prevent the default action for this event, if
     // so, let's not close this window after all...
 
     mInClose = wasInClose;
     return NS_OK;
   }
 
+  return FinalClose();
+}
+
+nsresult
+nsGlobalWindow::ForceClose()
+{
+  if (IsFrame() || !mDocShell) {
+    // This may be a frame in a frameset, or a window that's already closed.
+    // Ignore such calls.
+
+    return NS_OK;
+  }
+
+  if (mHavePendingClose) {
+    // We're going to be closed anyway; do nothing since we don't want
+    // to double-close
+    return NS_OK;
+  }
+
+  mInClose = PR_TRUE;
+
+  DispatchCustomEvent("DOMWindowClose");
+
+  return FinalClose();
+}
+
+nsresult
+nsGlobalWindow::FinalClose()
+{
+  nsresult rv;
   // Flag that we were closed.
   mIsClosed = PR_TRUE;
 
   nsCOMPtr<nsIJSContextStack> stack =
     do_GetService(sJSStackContractID);
 
   JSContext *cx = nsnull;
 
@@ -7077,34 +7122,73 @@ nsGlobalWindow::GetDocument(nsIDOMDocume
 // nsGlobalWindow::nsIDOMStorageWindow
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
 {
   FORWARD_TO_INNER(GetSessionStorage, (aSessionStorage), NS_ERROR_UNEXPECTED);
 
-  *aSessionStorage = nsnull;
-
   nsIPrincipal *principal = GetPrincipal();
   nsIDocShell* docShell = GetDocShell();
 
   if (!principal || !docShell) {
     return NS_OK;
   }
 
-  nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
-                                                        PR_TRUE,
-                                                        aSessionStorage);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!*aSessionStorage) {
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-  }
-
+  if (mSessionStorage) {
+#ifdef PR_LOGGING
+    if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+      PR_LogPrint("nsGlobalWindow %p has %p sessionStorage", this, mSessionStorage.get());
+    }
+#endif
+    nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(mSessionStorage);
+    if (piStorage) {
+      PRBool canAccess = piStorage->CanAccess(principal);
+      NS_ASSERTION(canAccess,
+                   "window %x owned sessionStorage "
+                   "that could not be accessed!");
+      if (!canAccess) {
+          mSessionStorage = nsnull;
+      }
+    }
+  }
+
+  if (!mSessionStorage) {
+    *aSessionStorage = nsnull;
+
+    nsString documentURI;
+    nsCOMPtr<nsIDOM3Document> document3 = do_QueryInterface(mDoc);
+    if (document3)
+        document3->GetDocumentURI(documentURI);
+
+    nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
+                                                          documentURI,
+                                                          PR_TRUE,
+                                                          getter_AddRefs(mSessionStorage));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef PR_LOGGING
+    if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+      PR_LogPrint("nsGlobalWindow %p tried to get a new sessionStorage %p", this, mSessionStorage.get());
+    }
+#endif
+
+    if (!mSessionStorage) {
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    }
+  }
+
+#ifdef PR_LOGGING
+    if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+      PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
+    }
+#endif
+
+  NS_ADDREF(*aSessionStorage = mSessionStorage);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetGlobalStorage(nsIDOMStorageList ** aGlobalStorage)
 {
   NS_ENSURE_ARG_POINTER(aGlobalStorage);
 
@@ -7142,17 +7226,24 @@ nsGlobalWindow::GetLocalStorage(nsIDOMSt
     nsIPrincipal *principal = GetPrincipal();
     if (!principal)
       return NS_OK;
 
     nsCOMPtr<nsIDOMStorageManager> storageManager =
       do_GetService("@mozilla.org/dom/storagemanager;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = storageManager->GetLocalStorageForPrincipal(principal, getter_AddRefs(mLocalStorage));
+    nsString documentURI;
+    nsCOMPtr<nsIDOM3Document> document3 = do_QueryInterface(mDoc);
+    if (document3)
+        document3->GetDocumentURI(documentURI);
+
+    rv = storageManager->GetLocalStorageForPrincipal(principal,
+                                                     documentURI,
+                                                     getter_AddRefs(mLocalStorage));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_ADDREF(*aLocalStorage = mLocalStorage);
   return NS_OK;
 }
 
 //*****************************************************************************
@@ -7285,31 +7376,17 @@ nsGlobalWindow::Observe(nsISupports* aSu
     return NS_OK;
   }
 
   if (IsInnerWindow() && !nsCRT::strcmp(aTopic, "dom-storage-changed")) {
     nsIPrincipal *principal;
     nsresult rv;
 
     principal = GetPrincipal();
-    if (!aData) {
-      nsIDocShell* docShell = GetDocShell();
-      if (principal && docShell) {
-        nsCOMPtr<nsIDOMStorage> storage;
-        docShell->GetSessionStorageForPrincipal(principal,
-                                                PR_FALSE,
-                                                getter_AddRefs(storage));
-
-        if (!SameCOMIdentity(storage, aSubject)) {
-          // A sessionStorage object changed, but not our session storage
-          // object.
-          return NS_OK;
-        }
-      }
-    } else if (principal) {
+    if (principal) {
       // A global storage object changed, check to see if it's one
       // this window can access.
 
       nsCOMPtr<nsIURI> codebase;
       principal->GetURI(getter_AddRefs(codebase));
 
       if (!codebase) {
         return NS_OK;
@@ -7333,33 +7410,33 @@ nsGlobalWindow::Observe(nsISupports* aSu
 
     nsAutoString domain(aData);
 
     if (IsFrozen()) {
       // This window is frozen, rather than firing the events here,
       // store the domain in which the change happened and fire the
       // events if we're ever thawed.
 
-      if (!mPendingStorageEvents) {
-        mPendingStorageEvents = new nsDataHashtable<nsStringHashKey, PRBool>;
-        NS_ENSURE_TRUE(mPendingStorageEvents, NS_ERROR_OUT_OF_MEMORY);
-
-        rv = mPendingStorageEvents->Init();
+      if (!mPendingStorageEventsObsolete) {
+        mPendingStorageEventsObsolete = new nsDataHashtable<nsStringHashKey, PRBool>;
+        NS_ENSURE_TRUE(mPendingStorageEventsObsolete, NS_ERROR_OUT_OF_MEMORY);
+
+        rv = mPendingStorageEventsObsolete->Init();
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
-      mPendingStorageEvents->Put(domain, PR_TRUE);
+      mPendingStorageEventsObsolete->Put(domain, PR_TRUE);
 
       return NS_OK;
     }
 
-    nsRefPtr<nsDOMStorageEvent> event = new nsDOMStorageEvent(domain);
+    nsRefPtr<nsDOMStorageEventObsolete> event = new nsDOMStorageEventObsolete();
     NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
 
-    rv = event->Init();
+    rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"), PR_FALSE, PR_FALSE, domain);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDocument));
 
     nsCOMPtr<nsIDOMEventTarget> target;
 
     if (htmlDoc) {
       nsCOMPtr<nsIDOMHTMLElement> body;
@@ -7368,17 +7445,109 @@ nsGlobalWindow::Observe(nsISupports* aSu
       target = do_QueryInterface(body);
     }
 
     if (!target) {
       target = this;
     }
 
     PRBool defaultActionEnabled;
-    target->DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled);
+    target->DispatchEvent((nsIDOMStorageEventObsolete *)event, &defaultActionEnabled);
+
+    return NS_OK;
+  }
+
+  if (IsInnerWindow() && !nsCRT::strcmp(aTopic, "dom-storage2-changed")) {
+    nsIPrincipal *principal;
+    nsresult rv;
+
+    nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(aSubject, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIDOMStorage> changingStorage;
+    rv = event->GetStorageArea(getter_AddRefs(changingStorage));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
+    nsPIDOMStorage::nsDOMStorageType storageType = pistorage->StorageType();
+
+    principal = GetPrincipal();
+    switch (storageType)
+    {
+    case nsPIDOMStorage::SessionStorage:
+    {
+      if (SameCOMIdentity(mSessionStorage, changingStorage)) {
+        // Do not fire any events for the same storage object, it's not shared
+        // among windows, see nsGlobalWindow::GetSessionStoarge()
+        return NS_OK;
+      }
+
+      nsCOMPtr<nsIDOMStorage> storage = mSessionStorage;
+      if (!storage) {
+        nsIDocShell* docShell = GetDocShell();
+        if (principal && docShell) {
+          // No need to pass documentURI here, it's only needed when we want
+          // to create a new storage, the third paramater would be PR_TRUE
+          docShell->GetSessionStorageForPrincipal(principal,
+                                                  EmptyString(),
+                                                  PR_FALSE,
+                                                  getter_AddRefs(storage));
+        }
+      }
+
+      if (!pistorage->IsForkOf(storage)) {
+        // This storage event is coming from a different doc shell,
+        // i.e. it is a clone, ignore this event.
+        return NS_OK;
+      }
+
+#ifdef PR_LOGGING
+      if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+        PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p", this, mSessionStorage.get(), pistorage.get());
+      }
+#endif
+
+      break;
+    }
+    case nsPIDOMStorage::LocalStorage:
+    {
+      if (SameCOMIdentity(mLocalStorage, changingStorage)) {
+        // Do not fire any events for the same storage object, it's not shared
+        // among windows, see nsGlobalWindow::GetLocalStoarge()
+        return NS_OK;
+      }
+
+      // Allow event fire only for the same principal storages
+      // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
+      nsIPrincipal *storagePrincipal = pistorage->Principal();
+      PRBool equals;
+
+      rv = storagePrincipal->Equals(principal, &equals);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (!equals)
+        return NS_OK;
+
+      break;
+    }
+    default:
+      return NS_OK;
+    }
+
+    if (IsFrozen()) {
+      // This window is frozen, rather than firing the events here,
+      // store the domain in which the change happened and fire the
+      // events if we're ever thawed.
+
+      mPendingStorageEvents.AppendElement(event);
+      return NS_OK;
+    }
+
+    PRBool defaultActionEnabled;
+    DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled);
 
     return NS_OK;
   }
 
   NS_WARNING("unrecognized topic in nsGlobalWindow::Observe");
   return NS_ERROR_FAILURE;
 }
 
@@ -7398,22 +7567,26 @@ FirePendingStorageEvents(const nsAString
   return PL_DHASH_NEXT;
 }
 
 nsresult
 nsGlobalWindow::FireDelayedDOMEvents()
 {
   FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
 
-  if (mPendingStorageEvents) {
+  for (PRUint32 i = 0; i < mPendingStorageEvents.Length(); ++i) {
+    Observe(mPendingStorageEvents[i], "dom-storage2-changed", nsnull);
+  }
+
+  if (mPendingStorageEventsObsolete) {
     // Fire pending storage events.
-    mPendingStorageEvents->EnumerateRead(FirePendingStorageEvents, this);
-
-    delete mPendingStorageEvents;
-    mPendingStorageEvents = nsnull;
+    mPendingStorageEventsObsolete->EnumerateRead(FirePendingStorageEvents, this);
+
+    delete mPendingStorageEventsObsolete;
+    mPendingStorageEventsObsolete = nsnull;
   }
 
   if (mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
   }
 
   if (mFireOfflineStatusChangeEventOnThaw) {
     mFireOfflineStatusChangeEventOnThaw = PR_FALSE;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -91,16 +91,17 @@
 #include "nsPoint.h"
 #include "nsSize.h"
 #include "nsRect.h"
 #include "mozFlushType.h"
 #include "prclist.h"
 #include "nsIDOMStorageObsolete.h"
 #include "nsIDOMStorageList.h"
 #include "nsIDOMStorageWindow.h"
+#include "nsIDOMStorageEvent.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsPIDOMEventTarget.h"
 #include "nsIArray.h"
 #include "nsIContent.h"
 
 #define DEFAULT_HOME_PAGE "www.mozilla.org"
 #define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
 
@@ -339,16 +340,19 @@ public:
                                               PRBool aClearScopeHint);
   virtual NS_HIDDEN_(void) SetOpenerWindow(nsIDOMWindowInternal *aOpener,
                                            PRBool aOriginalOpener);
   virtual NS_HIDDEN_(void) EnsureSizeUpToDate();
 
   virtual NS_HIDDEN_(void) EnterModalState();
   virtual NS_HIDDEN_(void) LeaveModalState();
 
+  virtual NS_HIDDEN_(PRBool) CanClose();
+  virtual NS_HIDDEN_(nsresult) ForceClose();
+
   virtual NS_HIDDEN_(void) SetHasOrientationEventListener();
 
   // nsIDOMViewCSS
   NS_DECL_NSIDOMVIEWCSS
 
   // nsIDOMAbstractView
   NS_DECL_NSIDOMABSTRACTVIEW
 
@@ -448,16 +452,17 @@ public:
 
   static PRBool DOMWindowDumpEnabled();
 
 protected:
   // Object Management
   virtual ~nsGlobalWindow();
   void CleanUp();
   void ClearControllers();
+  nsresult FinalClose();
 
   void FreeInnerObjects(PRBool aClearScope);
   nsGlobalWindow *CallerInnerWindow();
 
   nsresult SetNewDocument(nsIDocument *aDocument,
                           nsISupports *aState,
                           PRBool aClearScopeHint,
                           PRBool aIsInternalCall);
@@ -733,40 +738,42 @@ protected:
   // index 0->language_id 1, so index MAX-1 == language_id MAX
   nsCOMPtr<nsIScriptContext>    mScriptContexts[NS_STID_ARRAY_UBOUND];
   void *                        mScriptGlobals[NS_STID_ARRAY_UBOUND];
   nsGlobalWindowObserver*       mObserver;
 
   nsCOMPtr<nsIDOMCrypto>        mCrypto;
 
   nsCOMPtr<nsIDOMStorage>      mLocalStorage;
+  nsCOMPtr<nsIDOMStorage>      mSessionStorage;
 
   nsCOMPtr<nsISupports>         mInnerWindowHolders[NS_STID_ARRAY_UBOUND];
   nsCOMPtr<nsIPrincipal> mOpenerScriptPrincipal; // strong; used to determine
                                                  // whether to clear scope
 
   // These member variable are used only on inner windows.
   nsCOMPtr<nsIEventListenerManager> mListenerManager;
   PRCList                       mTimeouts;
   // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
   nsTimeout*                    mTimeoutInsertionPoint;
   PRUint32                      mTimeoutPublicIdCounter;
   PRUint32                      mTimeoutFiringDepth;
-  nsCOMPtr<nsIDOMStorageObsolete>       mSessionStorage;
 
   // Holder of the dummy java plugin, used to expose window.java and
   // window.packages.
   nsRefPtr<nsDummyJavaPluginOwner> mDummyJavaPluginOwner;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIDocument> mDoc;  // For fast access to principals
   JSObject* mJSObject;
 
-  nsDataHashtable<nsStringHashKey, PRBool> *mPendingStorageEvents;
+  typedef nsTArray<nsCOMPtr<nsIDOMStorageEvent> > nsDOMStorageEventArray;
+  nsDOMStorageEventArray mPendingStorageEvents;
+  nsDataHashtable<nsStringHashKey, PRBool> *mPendingStorageEventsObsolete;
 
   PRUint32 mTimeoutsSuspendDepth;
 
   // the element within the document that is currently focused when this
   // window is active
   nsCOMPtr<nsIContent>   mFocusedNode;
 
   // the method that was used to focus mFocusedNode
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -369,27 +369,23 @@ public:
   }
   void Invalidate() {
     mPtr = 0;
   }
 private:
   void *mPtr;
 };
 
-class AutoFreeJSStack {
+class nsAutoPoolRelease {
 public:
-  AutoFreeJSStack(JSContext *ctx, void *aPtr) : mContext(ctx), mStack(aPtr) {
-  }
-  JS_REQUIRES_STACK ~AutoFreeJSStack() {
-    if (mContext && mStack)
-      js_FreeStack(mContext, mStack);
-  }
+  nsAutoPoolRelease(JSArenaPool *p, void *m) : mPool(p), mMark(m) {}
+  ~nsAutoPoolRelease() { JS_ARENA_RELEASE(mPool, mMark); }
 private:
-  JSContext *mContext;
-  void *mStack;
+  JSArenaPool *mPool;
+  void *mMark;
 };
 
 // A utility function for script languages to call.  Although it looks small,
 // the use of nsIDocShell and nsPresContext triggers a huge number of
 // dependencies that most languages would not otherwise need.
 // XXXmarkh - This function is mis-placed!
 PRBool
 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
@@ -2138,34 +2134,34 @@ nsJSContext::CallEventHandler(nsISupport
 
   // check if the event handler can be run on the object in question
   rv = sSecurityManager->CheckFunctionAccess(mContext, aHandler, target);
 
   nsJSContext::TerminationFuncHolder holder(this);
 
   if (NS_SUCCEEDED(rv)) {
     // Convert args to jsvals.
-    void *mark;
     PRUint32 argc = 0;
     jsval *argv = nsnull;
 
+    js::LazilyConstructed<nsAutoPoolRelease> poolRelease;
+    js::LazilyConstructed<JSAutoTempValueRooter> tvr;
+
     // 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
     // was OK because those typically have PreCreate methods that give them the
     // right scope anyway, and we want to make sure that the arguments end up
     // in the same scope as aTarget.
     rv = ConvertSupportsTojsvals(aargv, target, &argc,
-                                 reinterpret_cast<void **>(&argv), &mark);
+                                 &argv, poolRelease, tvr);
     if (NS_FAILED(rv)) {
       stack->Pop(nsnull);
       return rv;
     }
 
-    AutoFreeJSStack stackGuard(mContext, mark); // ensure always freed.
-
     jsval funval = OBJECT_TO_JSVAL(static_cast<JSObject *>(aHandler));
     JSAutoRequest ar(mContext);
     ++mExecuteDepth;
     PRBool ok = ::JS_CallFunctionValue(mContext, target,
                                        funval, argc, argv, &rval);
     --mExecuteDepth;
 
     if (!ok) {
@@ -2650,25 +2646,26 @@ nsJSContext::InitializeExternalClasses()
   return nameSpaceManager->InitForContext(this);
 }
 
 nsresult
 nsJSContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aArgs)
 {
   PRUint32  argc;
   jsval    *argv = nsnull;
-  void *mark;
 
   JSAutoRequest ar(mContext);
 
+  js::LazilyConstructed<nsAutoPoolRelease> poolRelease;
+  js::LazilyConstructed<JSAutoTempValueRooter> tvr;
+
   nsresult rv;
   rv = ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc,
-                               reinterpret_cast<void **>(&argv), &mark);
+                               &argv, poolRelease, tvr);
   NS_ENSURE_SUCCESS(rv, rv);
-  AutoFreeJSStack stackGuard(mContext, mark); // ensure always freed.
 
   jsval vargs;
 
   // got the arguments, now attach them.
 
   // window.dialogArguments is supposed to be an array if a JS array
   // was passed to showModalDialog(), deal with that here.
   if (strcmp(aPropName, "dialogArguments") == 0 && argc <= 1) {
@@ -2685,36 +2682,34 @@ nsJSContext::SetProperty(void *aTarget, 
        NS_OK : NS_ERROR_FAILURE;
 
   return rv;
 }
 
 nsresult
 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
                                      void *aScope,
-                                     PRUint32 *aArgc, void **aArgv,
-                                     void **aMarkp)
+                                     PRUint32 *aArgc,
+                                     jsval **aArgv,
+                                     js::LazilyConstructed<nsAutoPoolRelease> &aPoolRelease,
+                                     js::LazilyConstructed<JSAutoTempValueRooter> &aRooter)
 {
   nsresult rv = NS_OK;
 
-  js_LeaveTrace(mContext);
-
   // If the array implements nsIJSArgArray, just grab the values directly.
   nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
-  if (fastArray != nsnull) {
-    *aMarkp = nsnull;
-    return fastArray->GetArgs(aArgc, aArgv);
-  }
+  if (fastArray != nsnull)
+    return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
+
   // Take the slower path converting each item.
   // Handle only nsIArray and nsIVariant.  nsIArray is only needed for
   // SetProperty('arguments', ...);
 
   *aArgv = nsnull;
   *aArgc = 0;
-  *aMarkp = nsnull;
 
   nsIXPConnect *xpc = nsContentUtils::XPConnect();
   NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
 
   if (!aArgs)
     return NS_OK;
   PRUint32 argCtr, argCount;
   // This general purpose function may need to convert an arg array
@@ -2725,19 +2720,26 @@ nsJSContext::ConvertSupportsTojsvals(nsI
     rv = argsArray->GetLength(&argCount);
     NS_ENSURE_SUCCESS(rv, rv);
     if (argCount == 0)
       return NS_OK;
   } else {
     argCount = 1; // the nsISupports which is not an array
   }
 
-  jsval *argv = js_AllocStack(mContext, argCount, aMarkp);
+  void *mark = JS_ARENA_MARK(&mContext->tempPool);
+  jsval *argv;
+  JS_ARENA_ALLOCATE_CAST(argv, jsval *, &mContext->tempPool,
+                         argCount * sizeof(jsval));
   NS_ENSURE_TRUE(argv, NS_ERROR_OUT_OF_MEMORY);
 
+  /* Use the caller's auto guards to release and unroot. */
+  aPoolRelease.construct(&mContext->tempPool, mark);
+  aRooter.construct(mContext, argCount, argv);
+
   if (argsArray) {
     for (argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
       nsCOMPtr<nsISupports> arg;
       jsval *thisval = argv + argCtr;
       argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
                                 getter_AddRefs(arg));
       if (!arg) {
         *thisval = JSVAL_NULL;
@@ -2776,20 +2778,18 @@ nsJSContext::ConvertSupportsTojsvals(nsI
     nsCOMPtr<nsIVariant> variant(do_QueryInterface(aArgs));
     if (variant)
       rv = xpc->VariantToJS(mContext, (JSObject *)aScope, variant, argv);
     else {
       NS_ERROR("Not an array, not an interface?");
       rv = NS_ERROR_UNEXPECTED;
     }
   }
-  if (NS_FAILED(rv)) {
-    js_FreeStack(mContext, *aMarkp);
+  if (NS_FAILED(rv))
     return rv;
-  }
   *aArgv = argv;
   *aArgc = argCount;
   return NS_OK;
 }
 
 // This really should go into xpconnect somewhere...
 nsresult
 nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv)
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -43,16 +43,19 @@
 #include "jsapi.h"
 #include "nsIObserver.h"
 #include "nsIXPCScriptNotify.h"
 #include "prtime.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsScriptNameSpaceManager.h"
 
 class nsIXPConnectJSObjectHolder;
+class nsAutoPoolRelease;
+class JSAutoTempValueRooter;
+namespace js { template <class> class LazilyConstructed; }
 
 class nsJSContext : public nsIScriptContext,
                     public nsIXPCScriptNotify
 {
 public:
   nsJSContext(JSRuntime *aRuntime);
   virtual ~nsJSContext();
 
@@ -202,21 +205,22 @@ public:
   static void CCIfUserInactive();
 
   static void FireGCTimer(PRBool aLoadInProgress);
 
 protected:
   nsresult InitializeExternalClasses();
 
   // Helper to convert xpcom datatypes to jsvals.
-  JS_FORCES_STACK nsresult ConvertSupportsTojsvals(nsISupports *aArgs,
-                                                   void *aScope,
-                                                   PRUint32 *aArgc,
-                                                   void **aArgv,
-                                                   void **aMarkp);
+  nsresult ConvertSupportsTojsvals(nsISupports *aArgs,
+                                   void *aScope,
+                                   PRUint32 *aArgc,
+                                   jsval **aArgv,
+                                   js::LazilyConstructed<nsAutoPoolRelease> &aPoolRelease,
+                                   js::LazilyConstructed<JSAutoTempValueRooter> &aRooter);
 
   nsresult AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv);
 
   // given an nsISupports object (presumably an event target or some other
   // DOM object), get (or create) the JSObject wrapping it.
   nsresult JSObjectFromInterface(nsISupports *aSup, void *aScript, 
                                  JSObject **aRet);
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -369,16 +369,19 @@ public:
 
   /**
    * Callback for notifying a window about a modal dialog being
    * opened/closed with the window as a parent.
    */
   virtual void EnterModalState() = 0;
   virtual void LeaveModalState() = 0;
 
+  virtual PRBool CanClose() = 0;
+  virtual nsresult ForceClose() = 0;
+
   void SetModalContentWindow(PRBool aIsModalContentWindow)
   {
     mIsModalContentWindow = aIsModalContentWindow;
   }
 
   PRBool IsModalContentWindow() const
   {
     return mIsModalContentWindow;
--- a/dom/interfaces/storage/Makefile.in
+++ b/dom/interfaces/storage/Makefile.in
@@ -54,14 +54,15 @@ XPIDLSRCS =                             
        nsIDOMToString.idl                      \
        nsIDOMStorageManager.idl                \
        $(NULL)
 
 SDK_XPIDLSRCS =                      \
         nsIDOMStorage.idl        \
         nsIDOMStorageObsolete.idl\
         nsIDOMStorageEvent.idl   \
+        nsIDOMStorageEventObsolete.idl \
         nsIDOMStorageItem.idl    \
         nsIDOMStorageList.idl    \
         nsIDOMStorageWindow.idl  \
        $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/interfaces/storage/nsIDOMStorageEvent.idl
+++ b/dom/interfaces/storage/nsIDOMStorageEvent.idl
@@ -35,31 +35,62 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 #include "nsIDOMEvent.idl"
 
 /**
  * Interface for a client side storage. See
- * http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side
+ * http://dev.w3.org/html5/webstorage/#the-storage-event
  * for more information.
  *
  * Event sent to a window when a storage area changes.
  */
 
-[scriptable, uuid(8c021c75-f6b6-493c-914e-0b6c57ccc88d)]
+interface nsIDOMStorage;
+
+[scriptable, uuid(AE0CB688-68B3-4fb3-9A11-2DA8E620E808)]
 interface nsIDOMStorageEvent : nsIDOMEvent
 {
   /**
-   * Domain of the storage area which changed, or #session for
-   * session storage.
+   * Attribute represents the key being changed. The key attribute is null
+   * when change has been invoked by the storage clear() method.
    */
-  readonly attribute DOMString domain;
+  readonly attribute DOMString key;
+
+  /**
+   * The original value of the key. The oldValue is null when the change
+   * has been invoked by storage clear() method or the key has been newly
+   * added and therefor doesn't have any previous value.
+   */
+  readonly attribute DOMString oldValue;
 
   /**
-   * Initialize a storage event.
+   * The new value of the key. The newValue is null when the change
+   * has been invoked by storage clear() method or the key has been removed
+   * from the storage.
+   */
+  readonly attribute DOMString newValue;
+
+  /**
+   * Represents the address of the document whose key changed.
+   */
+  readonly attribute DOMString url;
+
+  /**
+   * Represents the Storage object that was affected.
+   */
+  readonly attribute nsIDOMStorage storageArea;
+
+  /**
+   * Initialize the event in a manner analogous to the similarly-named method
+   * in the DOM Events interfaces.
    */
   void initStorageEvent(in DOMString typeArg, 
                         in boolean canBubbleArg, 
                         in boolean cancelableArg, 
-                        in DOMString domainArg);
+                        in DOMString keyArg,
+                        in DOMString oldValueArg,
+                        in DOMString newValueArg,
+                        in DOMString urlArg,
+                        in nsIDOMStorage storageAreaArg);
 };
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/storage/nsIDOMStorageEventObsolete.idl
@@ -0,0 +1,65 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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
+ *   Neil Deakin <enndeakin@sympatico.ca>
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "domstubs.idl"
+#include "nsIDOMEvent.idl"
+
+/**
+ * Interface for a client side storage. See
+ * http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side
+ * for more information.
+ *
+ * Event sent to a window when a storage area changes.
+ */
+
+[scriptable, uuid(c0178907-847d-41c0-8a62-31301bb946fa)]
+interface nsIDOMStorageEventObsolete : nsIDOMEvent
+{
+  /**
+   * Domain of the storage area which changed, or #session for
+   * session storage.
+   */
+  readonly attribute DOMString domain;
+
+  /**
+   * Initialize a storage event.
+   */
+  void initStorageEvent(in DOMString typeArg,
+                        in boolean canBubbleArg,
+                        in boolean cancelableArg,
+                        in DOMString domainArg);
+};
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -35,17 +35,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIDOMStorage;
 interface nsIPrincipal;
 
-[scriptable, uuid(c8e54beb-48f3-4538-a0ce-d6229f4d8f45)]
+[scriptable, uuid(fd91ec36-7da8-43bb-b8f2-4b57a862a467)]
 interface nsIDOMStorageManager : nsISupports
 {
   /**
    * Return the amount of disk space used by a domain.  Usage is checked
    * against the domain of the page that set the key (the owner domain), not
    * the domain of the storage object.
    *
    * @param aOwnerDomain The domain to check.
@@ -59,10 +59,11 @@ interface nsIDOMStorageManager : nsISupp
    */
   void clearOfflineApps();
 
   /**
    * Returns instance of localStorage object for aURI's origin.
    * This method ensures there is always only a single instance
    * for a single origin.
    */
-  nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal);
+  nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal,
+                                            in DOMString aDocumentURI);
 };
--- a/dom/interfaces/storage/nsPIDOMStorage.h
+++ b/dom/interfaces/storage/nsPIDOMStorage.h
@@ -42,32 +42,48 @@
 
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 class nsIDOMStorageObsolete;
 class nsIURI;
 class nsIPrincipal;
 
+// {BAFFCEB1-FD40-4ea9-8378-3509DD79204A}
 #define NS_PIDOMSTORAGE_IID                                 \
-  { 0x5ffbee8d, 0x9a86, 0x4a57,                           \
-      { 0x8c, 0x63, 0x76, 0x56, 0x18, 0x9c, 0xb2, 0xbc } }
+  { 0xbaffceb1, 0xfd40, 0x4ea9,  \
+    { 0x83, 0x78, 0x35, 0x9, 0xdd, 0x79, 0x20, 0x4a } }
 
 class nsPIDOMStorage : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
 
-  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal) = 0;
-  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal) = 0;
+  typedef enum {
+    Unknown = 0,
+    GlobalStorage = 1,
+    LocalStorage = 2,
+    SessionStorage = 3
+  } nsDOMStorageType;
+
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI) = 0;
+  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI) = 0;
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded) = 0;
 
   virtual already_AddRefed<nsIDOMStorage> Clone() = 0;
+  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI) = 0;
+  virtual PRBool IsForkOf(nsIDOMStorage* aThat) = 0;
 
   virtual nsTArray<nsString> *GetKeys() = 0;
 
   virtual nsIPrincipal* Principal() = 0;
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal) = 0;
+
+  virtual nsDOMStorageType StorageType() = 0;
+
+  virtual void BroadcastChangeNotification(const nsSubstring &aKey,
+                                           const nsSubstring &aOldValue,
+                                           const nsSubstring &aNewValue) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMStorage, NS_PIDOMSTORAGE_IID)
 
 #endif // __nsPIDOMStorage_h_
--- a/dom/plugins/ChildAsyncCall.cpp
+++ b/dom/plugins/ChildAsyncCall.cpp
@@ -59,16 +59,16 @@ ChildAsyncCall::Cancel()
   mFunc = NULL;
   mData = NULL;
 }
 
 NS_IMETHODIMP
 ChildAsyncCall::Run()
 {
   if (mFunc) {
+    mInstance->mPendingAsyncCalls.RemoveElement(this);
     mFunc(mData);
-    mInstance->mPendingAsyncCalls.RemoveElement(this);
   }
   return NS_OK;
 }
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/Makefile.in
+++ b/dom/plugins/Makefile.in
@@ -118,9 +118,11 @@ LOCAL_INCLUDES = \
   $(NULL)
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 endif
 
 include $(topsrcdir)/config/rules.mk
 
+CXXFLAGS += $(TK_CFLAGS)
+
 DEFINES += -DFORCE_PR_LOG
--- a/dom/plugins/NPEventX11.h
+++ b/dom/plugins/NPEventX11.h
@@ -37,16 +37,21 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_plugins_NPEventX11_h
 #define mozilla_dom_plugins_NPEventX11_h 1
 
 #if defined(MOZ_WIDGET_GTK2)
 #  include <gdk/gdkx.h>
+#elif defined(MOZ_WIDGET_QT)
+// X11/X.h has #define CursorShape 0, but Qt's qnamespace.h defines
+//   enum CursorShape { ... }.  Good times!
+#  undef CursorShape
+#  include <QX11Info>
 #else
 #  error Implement me for your toolkit
 #endif
 
 #include "npapi.h"
 
 namespace mozilla {
 
@@ -109,28 +114,34 @@ struct ParamTraits<mozilla::plugins::NPR
         aLog->append(L"(XEvent)");
     }
 
 private:
     static Display* GetXDisplay(const XAnyEvent& ev)
     {
         // TODO: get Display* from Window in |ev|
 
-        // FIXME: do this using Xlib, don't use Gdk
-        
+        // FIXME: do this using Xlib
+#if defined(MOZ_WIDGET_GTK2)
         return GDK_DISPLAY();
+#elif defined(MOZ_WIDGET_QT)
+        return QX11Info::display();
+#endif
     }
 
     static Display* GetXDisplay(const XErrorEvent& ev)
     {
         // TODO: get Display* from Window in |ev|
 
-        // FIXME: do this using Xlib, don't use Gdk
-        
+        // FIXME: do this using Xlib
+#if defined(MOZ_WIDGET_GTK2)
         return GDK_DISPLAY();
+#elif defined(MOZ_WIDGET_QT)
+        return QX11Info::display();
+#endif
     }
 
     static void SetXDisplay(XEvent& ev)
     {
         if (ev.type >= KeyPress) {
             ev.xany.display = GetXDisplay(ev.xany);
         }
         else {
--- a/dom/plugins/PluginInstanceChild.cpp
+++ b/dom/plugins/PluginInstanceChild.cpp
@@ -471,24 +471,26 @@ PluginInstanceChild::AnswerNPP_SetWindow
     mWindow.type = aWindow.type;
 
     mWsInfo.colormap = aWindow.colormap;
     if (!XVisualIDToInfo(mWsInfo.display, aWindow.visualID,
                          &mWsInfo.visual, &mWsInfo.depth))
         return false;
 
     if (aWindow.type == NPWindowTypeWindow) {
+#ifdef MOZ_WIDGET_GTK2
         if (GdkWindow* socket_window = gdk_window_lookup(aWindow.window)) {
             // A GdkWindow for the socket already exists.  Need to
             // workaround https://bugzilla.gnome.org/show_bug.cgi?id=607061
             // See wrap_gtk_plug_embedded in PluginModuleChild.cpp.
             g_object_set_data(G_OBJECT(socket_window),
                               "moz-existed-before-set-window",
                               GUINT_TO_POINTER(1));
         }
+#endif
     }
 
     *rv = mPluginIface->setwindow(&mData, &mWindow);
 
 #elif defined(OS_WIN)
     switch (aWindow.type) {
       case NPWindowTypeWindow:
       {
--- a/dom/plugins/PluginInstanceParent.cpp
+++ b/dom/plugins/PluginInstanceParent.cpp
@@ -70,16 +70,21 @@ PluginInstanceParent::PluginInstancePare
 #endif // defined(XP_WIN)
 {
 }
 
 PluginInstanceParent::~PluginInstanceParent()
 {
     if (mNPP)
         mNPP->pdata = NULL;
+
+#if defined(OS_WIN)
+    NS_ASSERTION(!(mPluginHWND || mPluginWndProc),
+        "Subclass was not reset correctly before the dtor was reached!");
+#endif
 }
 
 bool
 PluginInstanceParent::Init()
 {
     return !!mScriptableObjects.Init();
 }
 
@@ -92,16 +97,29 @@ ActorCollect(const void* aKey,
 {
     nsTArray<PluginScriptableObjectParent*>* objects =
         reinterpret_cast<nsTArray<PluginScriptableObjectParent*>*>(aUserData);
     return objects->AppendElement(aData) ? PL_DHASH_NEXT : PL_DHASH_STOP;
 }
 
 } // anonymous namespace
 
+void
+PluginInstanceParent::ActorDestroy(ActorDestroyReason why)
+{
+#if defined(OS_WIN)
+    if (why == AbnormalShutdown) {
+        // If the plugin process crashes, this is the only
+        // chance we get to destroy resources.
+        SharedSurfaceRelease();
+        UnsubclassPluginWindow();
+    }
+#endif
+}
+
 NPError
 PluginInstanceParent::Destroy()
 {
     NPError retval;
     if (!CallNPP_Destroy(&retval)) {
         NS_WARNING("Failed to send message!");
         return NPERR_GENERIC_ERROR;
     }
--- a/dom/plugins/PluginInstanceParent.h
+++ b/dom/plugins/PluginInstanceParent.h
@@ -68,16 +68,18 @@ public:
                          NPP npp,
                          const NPNetscapeFuncs* npniface);
 
     virtual ~PluginInstanceParent();
 
     bool Init();
     NPError Destroy();
 
+    NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why);
+
     virtual PPluginScriptableObjectParent*
     AllocPPluginScriptableObject();
 
     virtual bool
     AnswerPPluginScriptableObjectConstructor(PPluginScriptableObjectParent* aActor);
 
     virtual bool
     DeallocPPluginScriptableObject(PPluginScriptableObjectParent* aObject);
--- a/dom/plugins/PluginLibrary.h
+++ b/dom/plugins/PluginLibrary.h
@@ -61,17 +61,17 @@ public:
   virtual bool HasRequiredFunctions() = 0;
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
   virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) = 0;
 #else
   virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) = 0;
 #endif
   virtual nsresult NP_Shutdown(NPError* error) = 0;
-  virtual nsresult NP_GetMIMEDescription(char** mimeDesc) = 0;
+  virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) = 0;
   virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
                                void *aValue, NPError* error) = 0;
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_OS2)
   virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) = 0;
 #endif
   virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
                            uint16_t mode, int16_t argc, char* argn[],
                            char* argv[], NPSavedData* saved,
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -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 "mozilla/plugins/PluginModuleChild.h"
 
-#ifdef OS_LINUX
+#ifdef MOZ_WIDGET_GTK2
 #include <gtk/gtk.h>
 #endif
 
 #include "nsILocalFile.h"
 
 #include "pratom.h"
 #include "nsDebug.h"
 #include "nsCOMPtr.h"
@@ -172,17 +172,17 @@ PluginModuleChild::Init(const std::strin
 #else
 
 #  error Please copy the initialization code from nsNPAPIPlugin.cpp
 
 #endif
     return true;
 }
 
-#if defined(OS_LINUX)
+#if defined(MOZ_WIDGET_GTK2)
 typedef void (*GObjectDisposeFn)(GObject*);
 typedef void (*GtkPlugEmbeddedFn)(GtkPlug*);
 
 static GObjectDisposeFn real_gtk_plug_dispose;
 static GtkPlugEmbeddedFn real_gtk_plug_embedded;
 
 static void
 undo_bogus_unref(gpointer data, GObject* object, gboolean is_last_ref) {
@@ -229,17 +229,17 @@ wrap_gtk_plug_embedded(GtkPlug* plug) {
     }
 }
 #endif
 
 bool
 PluginModuleChild::InitGraphics()
 {
     // FIXME/cjones: is this the place for this?
-#if defined(OS_LINUX)
+#if defined(MOZ_WIDGET_GTK2)
     gtk_init(0, 0);
 
     // GtkPlug is a static class so will leak anyway but this ref makes sure.
     gpointer gtk_plug_class = g_type_class_ref(GTK_TYPE_PLUG);
 
     // The dispose method is a good place to hook into the destruction process
     // because the reference count should be 1 the last time dispose is
     // called.  (Toggle references wouldn't detect if the reference count
@@ -248,16 +248,17 @@ PluginModuleChild::InitGraphics()
     NS_ABORT_IF_FALSE(*dispose != wrap_gtk_plug_dispose,
                       "InitGraphics called twice");
     real_gtk_plug_dispose = *dispose;
     *dispose = wrap_gtk_plug_dispose;
 
     GtkPlugEmbeddedFn* embedded = &GTK_PLUG_CLASS(gtk_plug_class)->embedded;
     real_gtk_plug_embedded = *embedded;
     *embedded = wrap_gtk_plug_embedded;
+#elif defined(MOZ_WIDGET_QT)
 #else
     // may not be necessary on all platforms
 #endif
 
     return true;
 }
 
 bool
--- a/dom/plugins/PluginModuleChild.h
+++ b/dom/plugins/PluginModuleChild.h
@@ -78,19 +78,16 @@
 #else
 #define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (* _name)
 #endif
 
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_GETENTRYPOINTS) (NPPluginFuncs* pCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGININIT) (const NPNetscapeFuncs* pCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT) (const NPNetscapeFuncs* pCallbacks, NPPluginFuncs* fCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) (void);
-#ifdef XP_MACOSX
-typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_MAIN) (NPNetscapeFuncs* nCallbacks, NPPluginFuncs* pCallbacks, NPP_ShutdownProcPtr* unloadProcPtr);
-#endif
 
 namespace mozilla {
 namespace plugins {
 
 class PluginScriptableObjectChild;
 class PluginInstanceChild;
 
 class PluginModuleChild : public PPluginModuleChild
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -642,21 +642,21 @@ PluginModuleParent::NP_Shutdown(NPError*
     // plugin dso will have been unloaded on the other side by the
     // CallNP_Shutdown() message
     Close();
 
     return ok ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
-PluginModuleParent::NP_GetMIMEDescription(char** mimeDesc)
+PluginModuleParent::NP_GetMIMEDescription(const char** mimeDesc)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
-    *mimeDesc = (char*)"application/x-foobar";
+    *mimeDesc = "application/x-foobar";
     return NS_OK;
 }
 
 nsresult
 PluginModuleParent::NP_GetValue(void *future, NPPVariable aVariable,
                                    void *aValue, NPError* error)
 {
     PR_LOG(gPluginLog, PR_LOG_WARNING, ("%s Not implemented, requested variable %i", __FUNCTION__,
--- a/dom/plugins/PluginModuleParent.h
+++ b/dom/plugins/PluginModuleParent.h
@@ -195,17 +195,17 @@ private:
     virtual bool HasRequiredFunctions();
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error);
 #else
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
 #endif
     virtual nsresult NP_Shutdown(NPError* error);
-    virtual nsresult NP_GetMIMEDescription(char** mimeDesc);
+    virtual nsresult NP_GetMIMEDescription(const char** mimeDesc);
     virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
                                  void *aValue, NPError* error);
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_OS2)
     virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error);
 #endif
     virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
                              uint16_t mode, int16_t argc, char* argn[],
                              char* argv[], NPSavedData* saved,
--- a/dom/plugins/PluginPRLibrary.h
+++ b/dom/plugins/PluginPRLibrary.h
@@ -160,17 +160,17 @@ public:
             if (!pfNP_Shutdown)
                 return NS_ERROR_FAILURE;
             *error = pfNP_Shutdown();
         }
 
         return NS_OK;
     }
 
-    virtual nsresult NP_GetMIMEDescription(char** mimeDesc) {
+    virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) {
         if (mNP_GetMIMEDescription) {
             *mimeDesc = mNP_GetMIMEDescription();
         }
         else {
             NP_GetMIMEDescriptionFunc pfNP_GetMIMEDescription =
                 (NP_GetMIMEDescriptionFunc)
                 PR_FindFunctionSymbol(mLibrary, "NP_GetMIMEDescription");
             if (!pfNP_GetMIMEDescription) {
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -59,16 +59,17 @@
 #include "nsIPermission.h"
 #include "nsIPermissionManager.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsIJSContextStack.h"
 #include "nsIPrivateBrowsingService.h"
 #include "nsDOMString.h"
 #include "nsNetCID.h"
+#include "nsIProxyObjectManager.h"
 
 static const PRUint32 ASK_BEFORE_ACCEPT = 1;
 static const PRUint32 ACCEPT_SESSION = 2;
 static const PRUint32 BEHAVIOR_REJECT = 2;
 
 static const PRUint32 DEFAULT_QUOTA = 5 * 1024;
 // Be generous with offline apps by default...
 static const PRUint32 DEFAULT_OFFLINE_APP_QUOTA = 200 * 1024;
@@ -431,28 +432,29 @@ nsDOMStorageManager::ClearOfflineApps()
     nsTArray<nsString> domains;
     rv = GetOfflineDomains(domains);
     NS_ENSURE_SUCCESS(rv, rv);
     return nsDOMStorage::gStorageDB->RemoveOwners(domains, PR_TRUE, PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsDOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal *aPrincipal,
+                                                 const nsSubstring &aDocumentURI,
                                                  nsIDOMStorage **aResult)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
   *aResult = nsnull;
 
   nsresult rv;
 
   nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
   if (!storage)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  rv = storage->InitAsLocalStorage(aPrincipal);
+  rv = storage->InitAsLocalStorage(aPrincipal, aDocumentURI);
   if (NS_FAILED(rv))
     return rv;
 
   *aResult = storage.get();
   storage.forget();
 
   return NS_OK;
 }
@@ -543,34 +545,36 @@ NS_NewDOMStorage2(nsISupports* aOuter, R
     return NS_ERROR_OUT_OF_MEMORY;
 
   return storage->QueryInterface(aIID, aResult);
 }
 
 nsDOMStorage::nsDOMStorage()
   : mUseDB(PR_FALSE)
   , mSessionOnly(PR_TRUE)
-  , mLocalStorage(PR_FALSE)
   , mItemsCached(PR_FALSE)
+  , mStorageType(nsPIDOMStorage::Unknown)
+  , mEventBroadcaster(nsnull)
 {
   mSecurityChecker = this;
   mItems.Init(8);
   if (nsDOMStorageManager::gStorageManager)
     nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
 }
 
 nsDOMStorage::nsDOMStorage(nsDOMStorage& aThat)
   : mUseDB(PR_FALSE) // Any clone is not using the database
   , mSessionOnly(PR_TRUE)
-  , mLocalStorage(PR_FALSE) // Any clone is not a localStorage
   , mItemsCached(PR_FALSE)
   , mDomain(aThat.mDomain)
+  , mStorageType(aThat.mStorageType)
 #ifdef MOZ_STORAGE
   , mScopeDBKey(aThat.mScopeDBKey)
 #endif
+  , mEventBroadcaster(nsnull)
 {
   mSecurityChecker = this;
   mItems.Init(8);
 
   if (nsDOMStorageManager::gStorageManager)
     nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
 }
 
@@ -605,68 +609,74 @@ GetDomainURI(nsIPrincipal *aPrincipal, P
   if (!innerURI)
     return NS_ERROR_UNEXPECTED;
   innerURI.forget(_domain);
 
   return NS_OK;
 }
 
 nsresult
-nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal)
+nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
 {
   nsCOMPtr<nsIURI> domainURI;
   nsresult rv = GetDomainURI(aPrincipal, PR_TRUE, getter_AddRefs(domainURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // No need to check for a return value. If this would fail we would not get
   // here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
   // nsDOMStorage::CanUseStorage before we query the storage manager for a new
   // sessionStorage. It calls GetAsciiHost on innermost URI. If it fails, we
   // won't get to InitAsSessionStorage.
   domainURI->GetAsciiHost(mDomain);
 
+  mDocumentURI = aDocumentURI;
+
 #ifdef MOZ_STORAGE
   mUseDB = PR_FALSE;
   mScopeDBKey.Truncate();
   mQuotaDomainDBKey.Truncate();
 #endif
+
+  mStorageType = SessionStorage;
   return NS_OK;
 }
 
 nsresult
-nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal)
+nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
 {
   nsCOMPtr<nsIURI> domainURI;
   nsresult rv = GetDomainURI(aPrincipal, PR_FALSE, getter_AddRefs(domainURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // No need to check for a return value. If this would fail we would not get
   // here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
   // nsDOMStorage::CanUseStorage before we query the storage manager for a new
   // localStorage. It calls GetAsciiHost on innermost URI. If it fails, we won't
   // get to InitAsLocalStorage. Actually, mDomain will get replaced with
   // mPrincipal in bug 455070. It is not even used for localStorage.
   domainURI->GetAsciiHost(mDomain);
 
+  mDocumentURI = aDocumentURI;
+
 #ifdef MOZ_STORAGE
   nsDOMStorageDBWrapper::CreateOriginScopeDBKey(domainURI, mScopeDBKey);
 
   // XXX Bug 357323, we have to solve the issue how to define
   // origin for file URLs. In that case CreateOriginScopeDBKey
   // fails (the result is empty) and we must avoid database use
   // in that case because it produces broken entries w/o owner.
   mUseDB = !mScopeDBKey.IsEmpty();
 
   nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(mDomain,
       PR_TRUE, PR_FALSE, mQuotaDomainDBKey);
   nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(mDomain,
       PR_TRUE, PR_TRUE, mQuotaETLDplus1DomainDBKey);
 #endif
 
-  mLocalStorage = PR_TRUE;
+  mStorageType = LocalStorage;
   return NS_OK;
 }
 
 nsresult
 nsDOMStorage::InitAsGlobalStorage(const nsACString &aDomainDemanded)
 {
   mDomain = aDomainDemanded;
 #ifdef MOZ_STORAGE
@@ -679,16 +689,19 @@ nsDOMStorage::InitAsGlobalStorage(const 
   if (!(mUseDB = !mScopeDBKey.IsEmpty()))
     mScopeDBKey.AppendLiteral(":");
 
   nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomainDemanded,
       PR_TRUE, PR_FALSE, mQuotaDomainDBKey);
   nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomainDemanded,
       PR_TRUE, PR_TRUE, mQuotaETLDplus1DomainDBKey);
 #endif
+
+  mStorageType = GlobalStorage;
+  mEventBroadcaster = this;
   return NS_OK;
 }
 
 static PLDHashOperator
 CopyStorageItems(nsSessionStorageEntry* aEntry, void* userArg)
 {
   nsDOMStorage* newstorage = static_cast<nsDOMStorage*>(userArg);
 
@@ -990,26 +1003,28 @@ NS_IMETHODIMP
 nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
 {
   if (!CacheStoragePermissions())
     return NS_ERROR_DOM_SECURITY_ERR;
 
   if (aKey.IsEmpty())
     return NS_OK;
 
+  nsString oldValue;
+  SetDOMStringToNull(oldValue);
+
   nsresult rv;
   nsRefPtr<nsDOMStorageItem> newitem = nsnull;
   nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
   if (entry) {
     if (entry->mItem->IsSecure() && !IsCallerSecure()) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
-    if (!UseDB()) {
-      entry->mItem->SetValueInternal(aData);
-    }
+    oldValue = entry->mItem->GetValueInternal();
+    entry->mItem->SetValueInternal(aData);
   }
   else {
     newitem = new nsDOMStorageItem(this, aKey, aData, IsCallerSecure());
     if (!newitem)
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (UseDB()) {
@@ -1018,31 +1033,31 @@ nsDOMStorage::SetItem(const nsAString& a
   }
 
   if (newitem) {
     entry = mItems.PutEntry(aKey);
     NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
     entry->mItem = newitem;
   }
 
-  // SetDBValue already calls BroadcastChangeNotification so don't do it again
-  if (!UseDB())
-    BroadcastChangeNotification();
+  if ((oldValue != aData || mStorageType == GlobalStorage) && mEventBroadcaster)
+    mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, aData);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
 {
   if (!CacheStoragePermissions())
     return NS_ERROR_DOM_SECURITY_ERR;
 
   if (aKey.IsEmpty())
     return NS_OK;
 
+  nsString oldValue;
   nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
 
   if (entry && entry->mItem->IsSecure() && !IsCallerSecure()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   if (UseDB()) {
 #ifdef MOZ_STORAGE
@@ -1051,36 +1066,41 @@ NS_IMETHODIMP nsDOMStorage::RemoveItem(c
 
     nsAutoString value;
     PRBool secureItem;
     rv = GetDBValue(aKey, value, &secureItem);
     if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
       return NS_OK;
     NS_ENSURE_SUCCESS(rv, rv);
 
+    oldValue = value;
+
     rv = gStorageDB->RemoveKey(this, aKey, !IsOfflineAllowed(mDomain),
                                aKey.Length() + value.Length());
     NS_ENSURE_SUCCESS(rv, rv);
 
     mItemsCached = PR_FALSE;
-
-    BroadcastChangeNotification();
 #endif
   }
   else if (entry) {
     // clear string as StorageItems may be referencing this item
+    oldValue = entry->mItem->GetValueInternal();
     entry->mItem->ClearValue();
-
-    BroadcastChangeNotification();
   }
 
   if (entry) {
     mItems.RawRemoveEntry(entry);
   }
 
+  if ((!oldValue.IsEmpty() && mStorageType != GlobalStorage) && mEventBroadcaster) {
+    nsAutoString nullString;
+    SetDOMStringToNull(nullString);
+    mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, nullString);
+  }
+
   return NS_OK;
 }
 
 PR_STATIC_CALLBACK(PLDHashOperator)
 CheckSecure(nsSessionStorageEntry* aEntry, void* userArg)
 {
   PRBool* secure = (PRBool*)userArg;
   if (aEntry->mItem->IsSecure()) {
@@ -1095,16 +1115,18 @@ nsresult
 nsDOMStorage::Clear()
 {
   if (!CacheStoragePermissions())
     return NS_ERROR_DOM_SECURITY_ERR;
 
   if (UseDB())
     CacheKeysFromDB();
 
+  PRInt32 oldCount = mItems.Count();
+
   PRBool foundSecureItem = PR_FALSE;
   mItems.EnumerateEntries(CheckSecure, &foundSecureItem);
 
   if (foundSecureItem && !IsCallerSecure()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
 #ifdef MOZ_STORAGE
@@ -1113,17 +1135,22 @@ nsDOMStorage::Clear()
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = gStorageDB->ClearStorage(this);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 #endif
 
   mItems.Clear();
-  BroadcastChangeNotification();
+
+  if (oldCount && mEventBroadcaster) {
+    nsAutoString nullString;
+    SetDOMStringToNull(nullString);
+    mEventBroadcaster->BroadcastChangeNotification(nullString, nullString, nullString);
+  }
 
   return NS_OK;
 }
 
 nsresult
 nsDOMStorage::InitDB()
 {
 #ifdef MOZ_STORAGE
@@ -1183,17 +1210,17 @@ nsDOMStorage::GetDBValue(const nsAString
     return NS_OK;
 
   nsresult rv = InitDB();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString value;
   rv = gStorageDB->GetKeyValue(this, aKey, value, aSecure);
 
-  if (rv == NS_ERROR_DOM_NOT_FOUND_ERR && mLocalStorage) {
+  if (rv == NS_ERROR_DOM_NOT_FOUND_ERR && mStorageType != GlobalStorage) {
     SetDOMStringToNull(aValue);
   }
 
   if (NS_FAILED(rv))
     return rv;
 
   if (!IsCallerSecure() && *aSecure) {
     return NS_ERROR_DOM_SECURITY_ERR;
@@ -1245,17 +1272,16 @@ nsDOMStorage::SetDBValue(const nsAString
     }
 
     nsCOMPtr<nsIObserverService> os =
       do_GetService("@mozilla.org/observer-service;1");
     os->NotifyObservers(window, "dom-storage-warn-quota-exceeded",
                         NS_ConvertUTF8toUTF16(mDomain).get());
   }
 
-  BroadcastChangeNotification();
 #endif
 
   return NS_OK;
 }
 
 nsresult
 nsDOMStorage::SetSecure(const nsAString& aKey, PRBool aSecure)
 {
@@ -1296,16 +1322,29 @@ nsDOMStorage::ClearAll()
 
 already_AddRefed<nsIDOMStorage>
 nsDOMStorage::Clone()
 {
   NS_ASSERTION(PR_FALSE, "Old DOMStorage doesn't implement cloning");
   return nsnull;
 }
 
+already_AddRefed<nsIDOMStorage>
+nsDOMStorage::Fork(const nsSubstring &aDocumentURI)
+{
+  NS_ASSERTION(PR_FALSE, "Old DOMStorage doesn't implement forking");
+  return nsnull;
+}
+
+PRBool nsDOMStorage::IsForkOf(nsIDOMStorage* aThat)
+{
+  NS_ASSERTION(PR_FALSE, "Old DOMStorage doesn't implement forking");
+  return PR_FALSE;
+}
+
 struct KeysArrayBuilderStruct
 {
   PRBool callerIsSecure;
   nsTArray<nsString> *keys;
 };
 
 static PLDHashOperator
 KeysArrayBuilder(nsSessionStorageEntry* aEntry, void* userArg)
@@ -1366,32 +1405,40 @@ nsDOMStorage::CanAccess(nsIPrincipal *aP
   nsCOMPtr<nsIURI> unused;
   nsresult rv = GetPrincipalURIAndHost(aPrincipal,
                                        getter_AddRefs(unused), domain);
   NS_ENSURE_SUCCESS(rv, PR_FALSE);
 
   return domain.Equals(mDomain);
 }
 
+nsPIDOMStorage::nsDOMStorageType
+nsDOMStorage::StorageType()
+{
+  return mStorageType;
+}
+
 void
-nsDOMStorage::BroadcastChangeNotification()
+nsDOMStorage::BroadcastChangeNotification(const nsSubstring &aKey,
+                                          const nsSubstring &aOldValue,
+                                          const nsSubstring &aNewValue)
 {
   nsresult rv;
   nsCOMPtr<nsIObserverService> observerService =
     do_GetService("@mozilla.org/observer-service;1", &rv);
   if (NS_FAILED(rv)) {
     return;
   }
 
   // Fire off a notification that a storage object changed. If the
   // storage object is a session storage object, we don't pass a
   // domain, but if it's a global storage object we do.
   observerService->NotifyObservers((nsIDOMStorageObsolete *)this,
                                    "dom-storage-changed",
-                                   UseDB() ? NS_ConvertUTF8toUTF16(mDomain).get() : nsnull);
+                                   NS_ConvertUTF8toUTF16(mDomain).get());
 }
 
 //
 // nsDOMStorage2
 //
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorage2)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStorage2)
@@ -1417,38 +1464,42 @@ nsDOMStorage2::nsDOMStorage2()
 nsDOMStorage2::nsDOMStorage2(nsDOMStorage2& aThat)
 {
   mStorage = new nsDOMStorage(*aThat.mStorage.get());
   mStorage->mSecurityChecker = mStorage;
   mPrincipal = aThat.mPrincipal;
 }
 
 nsresult
-nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal)
+nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
 {
   mStorage = new nsDOMStorage();
   if (!mStorage)
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Leave security checks only for domain (nsDOMStorage implementation)
   mStorage->mSecurityChecker = mStorage;
   mPrincipal = aPrincipal;
-  return mStorage->InitAsSessionStorage(aPrincipal);
+  mDocumentURI = aDocumentURI;
+
+  return mStorage->InitAsSessionStorage(aPrincipal, aDocumentURI);
 }
 
 nsresult
-nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal)
+nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
 {
   mStorage = new nsDOMStorage();
   if (!mStorage)
     return NS_ERROR_OUT_OF_MEMORY;
 
   mStorage->mSecurityChecker = this;
   mPrincipal = aPrincipal;
-  return mStorage->InitAsLocalStorage(aPrincipal);
+  mDocumentURI = aDocumentURI;
+
+  return mStorage->InitAsLocalStorage(aPrincipal, aDocumentURI);
 }
 
 nsresult
 nsDOMStorage2::InitAsGlobalStorage(const nsACString &aDomainDemanded)
 {
   NS_ASSERTION(PR_FALSE, "Should not initialize nsDOMStorage2 as global storage.");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
@@ -1461,16 +1512,51 @@ nsDOMStorage2::Clone()
     return nsnull;
 
   storage->mStorage->CloneFrom(mStorage);
   NS_ADDREF(storage);
 
   return storage;
 }
 
+already_AddRefed<nsIDOMStorage>
+nsDOMStorage2::Fork(const nsSubstring &aDocumentURI)
+{
+  nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
+  if (!storage)
+    return nsnull;
+
+  nsresult rv = storage->InitAsSessionStorageFork(mPrincipal, aDocumentURI, mStorage);
+  if (NS_FAILED(rv))
+    return nsnull;
+
+  nsIDOMStorage* result = static_cast<nsIDOMStorage*>(storage.get());
+  storage.forget();
+  return result;
+}
+
+PRBool nsDOMStorage2::IsForkOf(nsIDOMStorage* aThat)
+{
+  if (!aThat)
+    return PR_FALSE;
+
+  nsDOMStorage2* storage = static_cast<nsDOMStorage2*>(aThat);
+  return mStorage == storage->mStorage;
+}
+
+nsresult
+nsDOMStorage2::InitAsSessionStorageFork(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI, nsIDOMStorageObsolete* aStorage)
+{
+  mPrincipal = aPrincipal;
+  mDocumentURI = aDocumentURI;
+  mStorage = static_cast<nsDOMStorage*>(aStorage);
+
+  return NS_OK;
+}
+
 nsTArray<nsString> *
 nsDOMStorage2::GetKeys()
 {
   return mStorage->GetKeys();
 }
 
 nsIPrincipal*
 nsDOMStorage2::Principal()
@@ -1492,16 +1578,66 @@ nsDOMStorage2::CanAccess(nsIPrincipal *a
   PRBool subsumes;
   nsresult rv = aPrincipal->Subsumes(mPrincipal, &subsumes);
   if (NS_FAILED(rv))
     return PR_FALSE;
 
   return subsumes;
 }
 
+nsPIDOMStorage::nsDOMStorageType
+nsDOMStorage2::StorageType()
+{
+  if (mStorage)
+    return mStorage->StorageType();
+
+  return nsPIDOMStorage::Unknown;
+}
+
+void
+nsDOMStorage2::BroadcastChangeNotification(const nsSubstring &aKey,
+                                          const nsSubstring &aOldValue,
+                                          const nsSubstring &aNewValue)
+{
+  nsresult rv;
+  nsCOMPtr<nsIDOMStorageEvent> event = new nsDOMStorageEvent();
+  rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
+                               PR_FALSE,
+                               PR_FALSE,
+                               aKey,
+                               aOldValue,
+                               aNewValue,
+                               mDocumentURI,
+                               static_cast<nsIDOMStorage*>(this));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> observerService =
+    do_GetService("@mozilla.org/observer-service;1", &rv);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> observerServiceProxy;
+  rv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
+                            NS_GET_IID(nsIObserverService),
+                            observerService,
+                            NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
+                            getter_AddRefs(observerServiceProxy));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  // Fire off a notification that a storage object changed.
+  observerServiceProxy->NotifyObservers(event,
+                                        "dom-storage2-changed",
+                                        nsnull);
+}
+
 NS_IMETHODIMP
 nsDOMStorage2::GetLength(PRUint32 *aLength)
 {
   return mStorage->GetLength(aLength);
 }
 
 NS_IMETHODIMP
 nsDOMStorage2::Key(PRUint32 aIndex, nsAString& aKey)
@@ -1513,28 +1649,31 @@ NS_IMETHODIMP
 nsDOMStorage2::GetItem(const nsAString& aKey, nsAString &aData)
 {
   return mStorage->GetItem(aKey, aData);
 }
 
 NS_IMETHODIMP
 nsDOMStorage2::SetItem(const nsAString& aKey, const nsAString& aData)
 {
+  mStorage->mEventBroadcaster = this;
   return mStorage->SetItem(aKey, aData);
 }
 
 NS_IMETHODIMP
 nsDOMStorage2::RemoveItem(const nsAString& aKey)
 {
+  mStorage->mEventBroadcaster = this;
   return mStorage->RemoveItem(aKey);
 }
 
 NS_IMETHODIMP
 nsDOMStorage2::Clear()
 {
+  mStorage->mEventBroadcaster = this;
   return mStorage->Clear();
 }
 
 //
 // nsDOMStorageList
 //
 
 NS_INTERFACE_MAP_BEGIN(nsDOMStorageList)
@@ -1837,41 +1976,101 @@ NS_INTERFACE_MAP_BEGIN(nsDOMStorageEvent
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEvent)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMStorageEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMStorageEvent, nsDOMEvent)
 
 
+/* readonly attribute DOMString key; */
+NS_IMETHODIMP nsDOMStorageEvent::GetKey(nsAString & aKey)
+{
+  aKey = mKey;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString oldValue; */
+NS_IMETHODIMP nsDOMStorageEvent::GetOldValue(nsAString & aOldValue)
+{
+  aOldValue = mOldValue;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString newValue; */
+NS_IMETHODIMP nsDOMStorageEvent::GetNewValue(nsAString & aNewValue)
+{
+  aNewValue = mNewValue;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString url; */
+NS_IMETHODIMP nsDOMStorageEvent::GetUrl(nsAString & aUrl)
+{
+  aUrl = mUrl;
+  return NS_OK;
+}
+
+/* readonly attribute nsIDOMStorage storageArea; */
+NS_IMETHODIMP nsDOMStorageEvent::GetStorageArea(nsIDOMStorage * *aStorageArea)
+{
+  NS_ENSURE_ARG_POINTER(aStorageArea);
+
+  NS_ADDREF(*aStorageArea = mStorageArea);
+  return NS_OK;
+}
+
+/* void initStorageEvent (in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString urlArg, in nsIDOMStorage storageAreaArg); */
+NS_IMETHODIMP nsDOMStorageEvent::InitStorageEvent(const nsAString & typeArg,
+                                                  PRBool canBubbleArg,
+                                                  PRBool cancelableArg,
+                                                  const nsAString & keyArg,
+                                                  const nsAString & oldValueArg,
+                                                  const nsAString & newValueArg,
+                                                  const nsAString & urlArg,
+                                                  nsIDOMStorage *storageAreaArg)
+{
+  nsresult rv;
+
+  rv = InitEvent(typeArg, canBubbleArg, cancelableArg);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mKey = keyArg;
+  mOldValue = oldValueArg;
+  mNewValue = newValueArg;
+  mUrl = urlArg;
+  mStorageArea = storageAreaArg;
+
+  return NS_OK;
+}
+
+// Obsolete globalStorage event
+
+// QueryInterface implementation for nsDOMStorageEventObsolete
+NS_INTERFACE_MAP_BEGIN(nsDOMStorageEventObsolete)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEventObsolete)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEventObsolete)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
+
+NS_IMPL_ADDREF_INHERITED(nsDOMStorageEventObsolete, nsDOMEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMStorageEventObsolete, nsDOMEvent)
+
+
 NS_IMETHODIMP
-nsDOMStorageEvent::GetDomain(nsAString& aDomain)
+nsDOMStorageEventObsolete::GetDomain(nsAString& aDomain)
 {
   // mDomain will be #session for session storage for events that fire
   // due to a change in a session storage object.
   aDomain = mDomain;
 
   return NS_OK;
 }
 
-nsresult
-nsDOMStorageEvent::Init()
-{
-  nsresult rv = InitEvent(NS_LITERAL_STRING("storage"), PR_TRUE, PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // This init method is only called by native code, so set the
-  // trusted flag here.
-  SetTrusted(PR_TRUE);
-
-  return NS_OK;
-}
-
 NS_IMETHODIMP
-nsDOMStorageEvent::InitStorageEvent(const nsAString& aTypeArg,
+nsDOMStorageEventObsolete::InitStorageEvent(const nsAString& aTypeArg,
                                     PRBool aCanBubbleArg,
                                     PRBool aCancelableArg,
                                     const nsAString& aDomainArg)
 {
   nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDomain = aDomainArg;
--- a/dom/src/storage/nsDOMStorage.h
+++ b/dom/src/storage/nsDOMStorage.h
@@ -50,16 +50,17 @@
 #include "nsIPermissionManager.h"
 #include "nsInterfaceHashtable.h"
 #include "nsVoidArray.h"
 #include "nsTArray.h"
 #include "nsPIDOMStorage.h"
 #include "nsIDOMToString.h"
 #include "nsDOMEvent.h"
 #include "nsIDOMStorageEvent.h"
+#include "nsIDOMStorageEventObsolete.h"
 #include "nsIDOMStorageManager.h"
 #include "nsCycleCollectionParticipant.h"
 
 #ifdef MOZ_STORAGE
 #include "nsDOMStorageDBWrapper.h"
 #endif
 
 #define IS_PERMISSION_ALLOWED(perm) \
@@ -140,23 +141,29 @@ public:
   // nsIDOMStorageObsolete
   NS_DECL_NSIDOMSTORAGEOBSOLETE
 
   // Helpers for implementing nsIDOMStorage
   nsresult GetItem(const nsAString& key, nsAString& aData);
   nsresult Clear();
 
   // nsPIDOMStorage
-  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal);
-  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal);
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI);
+  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI);
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded);
   virtual already_AddRefed<nsIDOMStorage> Clone();
+  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
+  virtual PRBool IsForkOf(nsIDOMStorage* aThat);
   virtual nsTArray<nsString> *GetKeys();
   virtual nsIPrincipal* Principal();
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal);
+  virtual nsDOMStorageType StorageType();
+  virtual void BroadcastChangeNotification(const nsSubstring &aKey,
+                                           const nsSubstring &aOldValue,
+                                           const nsSubstring &aNewValue);
 
   // If true, the contents of the storage should be stored in the
   // database, otherwise this storage should act like a session
   // storage.
   // This call relies on mSessionOnly, and should only be used
   // after a CacheStoragePermissions() call.  See the comments
   // for mSessionOnly below.
   PRBool UseDB() {
@@ -213,53 +220,55 @@ protected:
   friend class nsDOMStorageManager;
   friend class nsDOMStorage2;
 
   static nsresult InitDB();
 
   // cache the keys from the database for faster lookup
   nsresult CacheKeysFromDB();
 
-  void BroadcastChangeNotification();
-
   PRBool CanAccessSystem(nsIPrincipal *aPrincipal);
 
   // true if the storage database should be used for values
   PRPackedBool mUseDB;
 
+  // domain this store is associated with
+  nsCString mDomain;
+
+  // document URI of the document this storage is bound to
+  nsString mDocumentURI;
+
   // true if the preferences indicates that this storage should be
   // session only. This member is updated by
   // CacheStoragePermissions(), using the current principal.
   // CacheStoragePermissions() must be called at each entry point to
   // make sure this stays up to date.
   PRPackedBool mSessionOnly;
 
   // true if this storage was initialized as a localStorage object.  localStorage
   // objects are scoped to scheme/host/port in the database, while globalStorage
   // objects are scoped just to host.  this flag also tells the manager to map
   // this storage also in mLocalStorages hash table.
-  PRPackedBool mLocalStorage;
+  nsDOMStorageType mStorageType;
 
   // true if items from the database are cached
   PRPackedBool mItemsCached;
 
-  // domain this store is associated with
-  nsCString mDomain;
-
   // the key->value item pairs
   nsTHashtable<nsSessionStorageEntry> mItems;
 
   // keys are used for database queries.
   // see comments of the getters bellow.
   nsCString mScopeDBKey;
   nsCString mQuotaETLDplus1DomainDBKey;
   nsCString mQuotaDomainDBKey;
 
   friend class nsIDOMStorage2;
   nsPIDOMStorage* mSecurityChecker;
+  nsPIDOMStorage* mEventBroadcaster;
 
 public:
   // e.g. "moc.rab.oof.:" or "moc.rab.oof.:http:80" depending
   // on association with a domain (globalStorage) or
   // an origin (localStorage).
   nsCString& GetScopeDBKey() {return mScopeDBKey;}
 
   // e.g. "moc.rab.%" - reversed eTLD+1 subpart of the domain or
@@ -283,29 +292,42 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage)
 
   nsDOMStorage2(nsDOMStorage2& aThat);
   nsDOMStorage2();
 
   NS_DECL_NSIDOMSTORAGE
 
   // nsPIDOMStorage
-  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal);
-  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal);
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI);
+  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI);
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded);
   virtual already_AddRefed<nsIDOMStorage> Clone();
+  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
+  virtual PRBool IsForkOf(nsIDOMStorage* aThat);
   virtual nsTArray<nsString> *GetKeys();
   virtual nsIPrincipal* Principal();
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal);
+  virtual nsDOMStorageType StorageType();
+  virtual void BroadcastChangeNotification(const nsSubstring &aKey,
+                                           const nsSubstring &aOldValue,
+                                           const nsSubstring &aNewValue);
+
+  nsresult InitAsSessionStorageFork(nsIPrincipal *aPrincipal,
+                                    const nsSubstring &aDocumentURI,
+                                    nsIDOMStorageObsolete* aStorage);
 
 private:
   // storages bound to an origin hold the principal to
   // make security checks against it
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
+  // Needed for the storage event, this is address of the document this storage
+  // is bound to
+  nsString mDocumentURI;
   nsRefPtr<nsDOMStorage> mStorage;
 };
 
 class nsDOMStorageList : public nsIDOMStorageList
 {
 public:
   nsDOMStorageList()
   {
@@ -417,38 +439,57 @@ protected:
   // object where this item came from.
   nsRefPtr<nsDOMStorage> mStorage;
 };
 
 class nsDOMStorageEvent : public nsDOMEvent,
                           public nsIDOMStorageEvent
 {
 public:
-  nsDOMStorageEvent(const nsAString& aDomain)
-    : nsDOMEvent(nsnull, nsnull), mDomain(aDomain)
+  nsDOMStorageEvent()
+    : nsDOMEvent(nsnull, nsnull)
   {
-    if (aDomain.IsEmpty()) {
-      // An empty domain means this event is for a session sotrage
-      // object change. Store #session as the domain.
-
-      mDomain = NS_LITERAL_STRING("#session");
-    }
   }
 
   virtual ~nsDOMStorageEvent()
   {
   }
 
   nsresult Init();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMSTORAGEEVENT
   NS_FORWARD_NSIDOMEVENT(nsDOMEvent::)
 
 protected:
+  nsString mKey;
+  nsString mOldValue;
+  nsString mNewValue;
+  nsString mUrl;
+  nsCOMPtr<nsIDOMStorage> mStorageArea;
+};
+
+class nsDOMStorageEventObsolete : public nsDOMEvent,
+                          public nsIDOMStorageEventObsolete
+{
+public:
+  nsDOMStorageEventObsolete()
+    : nsDOMEvent(nsnull, nsnull)
+  {
+  }
+
+  virtual ~nsDOMStorageEventObsolete()
+  {
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSTORAGEEVENTOBSOLETE
+  NS_FORWARD_NSIDOMEVENT(nsDOMEvent::)
+
+protected:
   nsString mDomain;
 };
 
 NS_IMETHODIMP
 NS_NewDOMStorage(nsISupports* aOuter, REFNSIID aIID, void** aResult);
 
 NS_IMETHODIMP
 NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult);
--- a/dom/src/threads/nsDOMThreadService.cpp
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -992,18 +992,17 @@ nsDOMThreadService::CreateJSContext()
 
   static JSSecurityCallbacks securityCallbacks = {
     nsDOMWorkerSecurityManager::JSCheckAccess,
     nsDOMWorkerSecurityManager::JSTranscodePrincipals,
     nsDOMWorkerSecurityManager::JSFindPrincipal
   };
   JS_SetContextSecurityCallbacks(cx, &securityCallbacks);
 
-  static JSDebugHooks debugHooks;
-  JS_SetContextDebugHooks(cx, &debugHooks);
+  JS_ClearContextDebugHooks(cx);
 
   nsresult rv = nsContentUtils::XPConnect()->
     SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
   NS_ENSURE_SUCCESS(rv, nsnull);
 
   PRUint32 stackDummy;
   jsuword stackLimit, currentStackAddr = (jsuword)&stackDummy;
 
--- a/dom/tests/browser/browser_focus_steal_from_chrome.js
+++ b/dom/tests/browser/browser_focus_steal_from_chrome.js
@@ -46,47 +46,62 @@ function test() {
            "<label id='target'><input></label></body>",
       tagName: "INPUT", methodName: "click event on the label element" },
   ];
 
   let testingIndex = -1;
   let canRetry;
   let callback;
   let loadedCount;
+  let setFocusToChrome;
 
   function runNextTest() {
     if (++testingIndex >= testingList.length) {
       // cleaning-up...
       gBrowser.addTab();
       for (let i = 0; i < tabs.length; i++) {
         gBrowser.removeTab(tabs[i]);
       }
       finish();
       return;
     }
     callback = doTest1;
-    loadTestPage();
+    loadTestPage(false);
   }
 
-  function loadTestPage() {
+  function loadTestPage(aSetFocusToChrome) {
     loadedCount = 0;
     canRetry = 10;
+    setFocusToChrome = aSetFocusToChrome;
     // Set the focus to the contents.
     tabs[0].linkedBrowser.focus();
-    for (let i = 0; i < tabs.length; i++) {
-      tabs[i].linkedBrowser.addEventListener("load", onLoad, true);
-      tabs[i].linkedBrowser.loadURI(testingList[testingIndex].uri);
-    }
+    // Load on the tabs
+    tabs[0].linkedBrowser.addEventListener("load", onLoadForegroundTab, true);
+    tabs[0].linkedBrowser.loadURI(testingList[testingIndex].uri);
+    tabs[1].linkedBrowser.addEventListener("load", onLoadBackgroundTab, true);
+    tabs[1].linkedBrowser.loadURI(testingList[testingIndex].uri);
   }
 
-  function onLoad() {
+  function onLoadForegroundTab() {
+    tabs[0].linkedBrowser.removeEventListener("load", onLoadForegroundTab, true);
+    if (setFocusToChrome) {
+      // Set focus to a chrome element before the loaded content tries to move
+      // focus.
+      BrowserSearch.searchBar.focus();
+    }
+    onLoadComplete();
+  }
+
+  function onLoadBackgroundTab() {
+    tabs[1].linkedBrowser.removeEventListener("load", onLoadBackgroundTab, true);
+    onLoadComplete();
+  }
+
+  function onLoadComplete() {
     if (++loadedCount == tabs.length) {
-      for (let i = 0; i < tabs.length; i++) {
-        tabs[i].linkedBrowser.removeEventListener("load", onLoad, true);
-      }
       setTimeout(callback, 20);
     }
   }
 
   function isPrepared() {
     for (let i = 0; i < tabs.length; i++) {
       if (tabs[i].linkedBrowser.contentDocument.activeElement.tagName !=
             testingList[testingIndex].tagName) {
@@ -122,55 +137,26 @@ function test() {
        " element is not active by the " + testingList[testingIndex].methodName +
        " (Test1: content can steal focus)");
     isnot(fm.focusedElement, e,
           "the " + testingList[testingIndex].tagName +
           " element is focused by the " + testingList[testingIndex].methodName +
           " (Test1: content can steal focus)");
 
     callback = doTest2;
-    loadTestPage();
-
-    // Set focus to chrome element before onload events of the loading contents.
-    BrowserSearch.searchBar.focus();
+    loadTestPage(true);
   }
 
 
   function doTest2() {
     if (canRetry-- > 0 && !isPrepared()) {
       setTimeout(callback, 10); // retry
       return;
     }
 
-    // XXX puts some useful information for bug 534420
-    let activeWindow = fm.activeWindow;
-    let focusedWindow = fm.focusedWindow;
-    ok(activeWindow, "We're not active");
-    ok(focusedWindow, "There is no focused window");
-    is(activeWindow, window.top, "our window isn't active");
-    let searchbar = BrowserSearch.searchBar;
-    let focusedElement = fm.focusedElement;
-    if (searchbar) {
-      let principal = searchbar.nodePrincipal;
-      ok(principal, "principal is null");
-      info("search bar: tagName=" + searchbar.tagName + " id=" + searchbar.id);
-      ok(secMan.isSystemPrincipal(principal), "search bar isn't chrome");
-    } else {
-      info("search bar is NULL!!");
-    }
-    if (focusedElement) {
-      let principal = focusedElement.nodePrincipal;
-      ok(principal, "principal is null");
-      info("focusedElement: tagName=" + focusedElement.tagName +
-           " id=" + focusedElement.id);
-      ok(secMan.isSystemPrincipal(principal), "focusedElement isn't chrome");
-    } else {
-      info("focusedElement is NULL!!");
-    }
-
     // The contents shouldn't be able to steal the focus from chrome.
 
     // in foreground tab
     let e = tabs[0].linkedBrowser.contentDocument.activeElement;
     is(e.tagName, testingList[testingIndex].tagName,
        "the foreground tab's " + testingList[testingIndex].tagName +
        " element is not active by the " + testingList[testingIndex].methodName +
        " (Test2: content can NOT steal focus)");
--- a/dom/tests/mochitest/Makefile.in
+++ b/dom/tests/mochitest/Makefile.in
@@ -50,12 +50,13 @@ DIRS	+= \
 	ajax \
 	bugs \
 	chrome \
 	general \
 	whatwg \
 	geolocation \
 	localstorage \
 	sessionstorage \
+	storageevent \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
--- a/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
+++ b/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
@@ -15,17 +15,17 @@ function startTest()
     .getService(Components.interfaces.nsIIOService);
   var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
     .getService(Components.interfaces.nsIScriptSecurityManager);
   var dsm = Components.classes["@mozilla.org/dom/storagemanager;1"]
     .getService(Components.interfaces.nsIDOMStorageManager);
 
   var uri = ios.newURI(url, "", null);
   var principal = ssm.getCodebasePrincipal(uri);
-  var storage = dsm.getLocalStorageForPrincipal(principal);
+  var storage = dsm.getLocalStorageForPrincipal(principal, "");
   
   storage.setItem("chromekey", "chromevalue");
   
   var aframe = document.getElementById("aframe");
   aframe.onload = function()
   {
     is(storage.getItem("chromekey"), "chromevalue");
     is(aframe.contentDocument.getElementById("data").innerHTML, "chromevalue");
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/Makefile.in
@@ -0,0 +1,71 @@
+#
+# ***** 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) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Jan Bambas <honzab@firemni.cz>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir	= dom/tests/mochitest/storageevent
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES	= \
+    frameGlobalStorageMaster.html \
+    frameGlobalStorageSlaveEqual.html \
+    frameGlobalStorageSlaveNotEqual.html \
+    frameLocalStorageMaster.html \
+    frameLocalStorageSlaveEqual.html \
+    frameLocalStorageSlaveNotEqual.html \
+    frameSessionStorageMasterEqual.html \
+    frameSessionStorageMasterNotEqual.html \
+    frameSessionStorageSlaveEqual.html \
+    frameSessionStorageSlaveNotEqual.html \
+    test_storageGlobalStorageEventCheckNoPropagation.html \
+    test_storageGlobalStorageEventCheckPropagation.html \
+    test_storageLocalStorageEventCheckNoPropagation.html \
+    test_storageLocalStorageEventCheckPropagation.html \
+    test_storageSessionStorageEventCheckPropagation.html \
+    test_storageSessionStorageEventCheckNoPropagation.html \
+    interOriginFrame.js \
+    interOriginTest2.js \
+    $(NULL)
+
+libs::	$(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameGlobalStorageMaster.html
@@ -0,0 +1,53 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>master frame for event storage propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 1;
+var gotEvent = false;
+
+function bind()
+{
+  document.body.addEventListener("storage", function(event)
+  {
+    gotEvent = true;
+  }, false);
+}
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 1:
+      var storage = globalStorage[location.host];
+      storage.setItem("X", "1");
+      storage.setItem("X", "2");
+      storage.setItem("X", "2");
+      storage.removeItem("X");
+      storage.removeItem("X");
+      storage.removeItem("Y");
+      storage.setItem("X", "2");
+      break;
+
+    case 3:
+      todo(gotEvent, false, "Expected no events");
+      return finishTest();
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="bind(); postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameGlobalStorageSlaveEqual.html
@@ -0,0 +1,56 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+
+var events = [];
+
+function bind()
+{
+  document.body.addEventListener("storage", function(event)
+  {
+    events.push(event);
+  }, false);
+}
+
+function doStep()
+{
+  function checkEvent()
+  {
+    var event = events.shift();
+    ok(event);
+    if (!event)
+      return;
+    is(event.domain, "example.com");
+  }
+
+  switch (currentStep)
+  {
+    case 2:
+      is(events.length, 4, "Expected 5 events");
+      checkEvent();
+      checkEvent();
+      checkEvent();
+      checkEvent();
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="bind(); postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameGlobalStorageSlaveNotEqual.html
@@ -0,0 +1,42 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+var gotEvent = false;
+
+function bind()
+{
+  document.body.addEventListener("storage", function(event)
+  {
+    gotEvent = true;
+  }, false);
+}
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 2:
+      is(gotEvent, false, "Expected no events");
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="bind(); postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameLocalStorageMaster.html
@@ -0,0 +1,63 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>master frame for event storage propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 1;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 1:
+      // Must not fire (storage must be clear!)
+      localStorage.clear();
+      // Must fire X:null->'1'
+      localStorage.setItem("X", "1");
+      // Must fire X:'1'->'2'
+      localStorage.setItem("X", "2");
+      // Must not fire
+      localStorage.setItem("X", "2");
+      // Must fire X:'2'->null
+      localStorage.removeItem("X");
+      // Must not fire
+      localStorage.removeItem("X");
+      // Must not fire
+      localStorage.removeItem("Y");
+      // Must fire X:null->'2' (we need something in the storage)
+      localStorage.setItem("X", "2");
+      // Must fire null:null->null (one item has been erased)
+      localStorage.clear();
+      // Must not fire
+      localStorage.clear();
+      break;
+
+    // Wait some time to let the async event be propagated
+    case 11:
+      is(gotEvent, false, "Expected no events");
+      return finishTest();
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameLocalStorageSlaveEqual.html
@@ -0,0 +1,55 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+
+var events = [];
+
+window.addEventListener("storage", function(event)
+{
+  events.push(event);
+}, false);
+
+function doStep()
+{
+  function checkEvent(expectedKey, expectedOldValue, expectedNewValue)
+  {
+    var event = events.shift();
+    is(event.key, expectedKey, "key name check");
+    is(event.oldValue, expectedOldValue, "old value check");
+    is(event.newValue, expectedNewValue, "new value check");
+    is(event.url, "http://example.com/tests/dom/tests/mochitest/storageevent/frameLocalStorageMaster.html");
+    ok(event.storageArea);
+  }
+
+  switch (currentStep)
+  {
+    case 10:
+      is(events.length, 5, "Expected 5 events");
+      checkEvent("X", null, "1");
+      checkEvent("X", "1", "2");
+      checkEvent("X", "2", null);
+      checkEvent("X", null, "2");
+      checkEvent(null, null, null);
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameLocalStorageSlaveNotEqual.html
@@ -0,0 +1,39 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 10:
+      is(gotEvent, false, "Expected no events");
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameSessionStorageMasterEqual.html
@@ -0,0 +1,67 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>master frame for event storage propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 1;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    // In step 2 we instantiate sessionStorage in the other frame
+    case 1:
+      // Must not fire (storage must be clear!)
+      sessionStorage.clear();
+      // Must fire X:null->'1'
+      sessionStorage.setItem("X", "1");
+      // Must fire X:'1'->'2'
+      sessionStorage.setItem("X", "2");
+      // Must not fire
+      sessionStorage.setItem("X", "2");
+      // Must fire X:'2'->null
+      sessionStorage.removeItem("X");
+      // Must not fire
+      sessionStorage.removeItem("X");
+      // Must not fire
+      sessionStorage.removeItem("Y");
+      // Must fire X:null->'2' (we need something in the storage)
+      sessionStorage.setItem("X", "2");
+      // Must fire null:null->null (one item has been erased)
+      sessionStorage.clear();
+      // Must not fire
+      sessionStorage.clear();
+      break;
+
+    // Wait some time to let the async event be propagated
+    case 11:
+      is(gotEvent, false, "Expected no events");
+      return finishTest();
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+  <iframe src="http://example.com:80/tests/dom/tests/mochitest/storageevent/frameSessionStorageSlaveEqual.html"
+          name="slaveFrame">
+  </iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameSessionStorageMasterNotEqual.html
@@ -0,0 +1,66 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>master frame for event storage propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 1;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 1:
+      // Must not fire (storage must be clear!)
+      sessionStorage.clear();
+      // Must fire X:null->'1'
+      sessionStorage.setItem("X", "1");
+      // Must fire X:'1'->'2'
+      sessionStorage.setItem("X", "2");
+      // Must not fire
+      sessionStorage.setItem("X", "2");
+      // Must fire X:'2'->null
+      sessionStorage.removeItem("X");
+      // Must not fire
+      sessionStorage.removeItem("X");
+      // Must not fire
+      sessionStorage.removeItem("Y");
+      // Must fire X:null->'2' (we need something in the storage)
+      sessionStorage.setItem("X", "2");
+      // Must fire null:null->null (one item has been erased)
+      sessionStorage.clear();
+      // Must not fire
+      sessionStorage.clear();
+      break;
+
+    // Wait some time to let the async event be propagated
+    case 11:
+      is(gotEvent, false, "Expected no events");
+      return finishTest();
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+  <iframe src="http://example.com:80/tests/dom/tests/mochitest/storageevent/frameSessionStorageSlaveNotEqual.html"
+          name="slaveFrame">
+  </iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameSessionStorageSlaveEqual.html
@@ -0,0 +1,59 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+
+var events = [];
+
+window.addEventListener("storage", function(event)
+{
+  events.push(event);
+}, false);
+
+function doStep()
+{
+  function checkEvent(expectedKey, expectedOldValue, expectedNewValue)
+  {
+    var event = events.shift();
+    ok(event, "Event is present");
+    if (!event)
+      return;
+
+    is(event.key, expectedKey, "key name check");
+    is(event.oldValue, expectedOldValue, "old value check");
+    is(event.newValue, expectedNewValue, "new value check");
+    is(event.url, "http://example.com/tests/dom/tests/mochitest/storageevent/frameSessionStorageMasterEqual.html");
+    ok(event.storageArea);
+  }
+
+  switch (currentStep)
+  {
+    case 10:
+      is(events.length, 5, "Expected 5 events");
+      checkEvent("X", null, "1");
+      checkEvent("X", "1", "2");
+      checkEvent("X", "2", null);
+      checkEvent("X", null, "2");
+      checkEvent(null, null, null);
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameSessionStorageSlaveNotEqual.html
@@ -0,0 +1,39 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 10:
+      is(gotEvent, false, "Expected no events");
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/interOriginFrame.js
@@ -0,0 +1,57 @@
+function postMsg(message)
+{
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
+  var l = parent.window.location;
+  parent.postMessage(message, l.protocol + "//" + l.host);
+}
+
+window.addEventListener("message", onMessageReceived, false);
+
+function onMessageReceived(event)
+{
+  if (event.data == "step") {
+    var performed = false;
+    try {
+      performed = doStep();
+    }
+    catch (ex) {
+      postMsg("FAILURE: exception threw at "+ location +":\n" + ex);
+      finishTest();
+    }
+
+    if (performed)
+      postMsg("perf");
+
+    return;
+  }
+
+  if (parent)
+    postMsg(event.data);
+}
+
+function ok(a, message)
+{
+  if (!a)
+    postMsg("FAILURE: " + message);
+  else
+    postMsg(message);
+}
+
+function is(a, b, message)
+{
+  if (a != b)
+    postMsg("FAILURE: " + message + ", expected "+b+" got "+a);
+  else
+    postMsg(message + ", expected "+b+" got "+a);
+}
+
+function todo(a, b, message)
+{
+  postMsg("TODO: " + message + ", expected "+b+" got "+a);
+}
+
+function finishTest()
+{
+  postMsg("done");
+  return false;
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/interOriginTest2.js
@@ -0,0 +1,58 @@
+var frameLoadsPending = 2;
+
+var callMasterFrame = true;
+var testDone = false;
+
+var masterFrameOrigin = "";
+var slaveFrameOrigin = "";
+
+var failureRegExp = new RegExp("^FAILURE");
+var todoRegExp = new RegExp("^TODO");
+
+const framePath = "/tests/dom/tests/mochitest/storageevent/";
+
+window.addEventListener("message", onMessageReceived, false);
+
+function onMessageReceived(event)
+{
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
+
+  switch (event.data)
+  {
+    // Indication of the frame onload event
+    case "frame loaded":
+      if (--frameLoadsPending)
+        break;
+
+      // Just fall through...
+
+    // Indication of successfully finished step of a test
+    case "perf":
+      if (callMasterFrame)
+        masterFrame.postMessage("step", masterFrameOrigin);
+      else if (slaveFrame)
+        slaveFrame.postMessage("step", slaveFrameOrigin);
+      else if (masterFrame.slaveFrame)
+        masterFrame.slaveFrame.postMessage("step", slaveFrameOrigin);
+      callMasterFrame = !callMasterFrame;
+      break;
+
+    // Indication of all test parts finish (from any of the frames)
+    case "done":
+      if (testDone)
+        break;
+
+      testDone = true;
+      SimpleTest.finish();
+      break;
+
+    // Any other message indicates error, succes or todo message of a test
+    default:
+      if (event.data.match(todoRegExp))
+        SimpleTest.todo(false, event.data);
+      else
+        SimpleTest.ok(!event.data.match(failureRegExp), event.data);
+      break;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageGlobalStorageEventCheckNoPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.org:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameGlobalStorageMaster.html";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameGlobalStorageSlaveNotEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageGlobalStorageEventCheckPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.com:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameGlobalStorageMaster.html";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameGlobalStorageSlaveEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageLocalStorageEventCheckNoPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.org:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameLocalStorageMaster.html";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameLocalStorageSlaveNotEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageLocalStorageEventCheckPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.com:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameLocalStorageMaster.html";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameLocalStorageSlaveEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageSessionStorageEventCheckNoPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+var slaveFrame = null;
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.org:80";
+  slaveFrameOrigin = "http://example.com:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameSessionStorageMasterNotEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageSessionStorageEventCheckPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+var slaveFrame = null;
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.com:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameSessionStorageMasterEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+</body>
+</html>
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -156,30 +156,46 @@ NS_IMETHODIMP nsPlaintextEditor::Init(ns
     nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
   
     // Init the base editor
     res = nsEditor::Init(aDoc, aPresShell, aRoot, aSelCon, aFlags);
   }
 
   // check the "single line editor newline handling"
   // and "caret behaviour in selection" prefs
+  GetDefaultEditorPrefs(mNewlineHandling, mCaretStyle);
+
+  if (NS_FAILED(rulesRes)) return rulesRes;
+  return res;
+}
+
+// static
+void
+nsPlaintextEditor::GetDefaultEditorPrefs(PRInt32 &aNewlineHandling,
+                                         PRInt32 &aCaretStyle)
+{
+  // set default values
+  aNewlineHandling = nsIPlaintextEditor::eNewlinesPasteToFirst;
+#ifdef XP_WIN
+  aCaretStyle = 1;
+#else
+  aCaretStyle = 0;
+#endif
+
   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (prefBranch)
   {
     prefBranch->GetIntPref("editor.singleLine.pasteNewlines",
-                           &mNewlineHandling);
-    prefBranch->GetIntPref("layout.selection.caret_style", &mCaretStyle);
+                           &aNewlineHandling);
+    prefBranch->GetIntPref("layout.selection.caret_style", &aCaretStyle);
 #ifdef XP_WIN
-    if (mCaretStyle == 0)
-      mCaretStyle = 1;
+    if (aCaretStyle == 0)
+      aCaretStyle = 1;
 #endif
   }
-
-  if (NS_FAILED(rulesRes)) return rulesRes;
-  return res;
 }
 
 void 
 nsPlaintextEditor::BeginEditorInit()
 {
   mInitTriggerCounter++;
 }
 
--- a/editor/libeditor/text/nsPlaintextEditor.h
+++ b/editor/libeditor/text/nsPlaintextEditor.h
@@ -171,16 +171,19 @@ public:
   /**
    * Extends the selection for given deletion operation
    * If done, also update aAction to what's actually left to do after the
    * extension.
    */
   nsresult ExtendSelectionForDelete(nsISelection* aSelection,
                                     nsIEditor::EDirection *aAction);
 
+  static void GetDefaultEditorPrefs(PRInt32 &aNewLineHandling,
+                                    PRInt32 &aCaretStyle);
+
 protected:
 
   NS_IMETHOD  InitRules();
   void        BeginEditorInit();
   nsresult    EndEditorInit();
 
   // Create the event listeners for the editor to install.
   virtual nsresult CreateEventListeners();
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -550,16 +550,85 @@ GetTextNode(nsISelection *selection, nsE
     mEditor->GetTextLength(&txtLen);                                   \
     NS_ASSERTION(mPasswordText.Length() == txtLen,                     \
                  "password length not equal to number of asterisks");  \
   }
 #else
 #define ASSERT_PASSWORD_LENGTHS_EQUAL()
 #endif
 
+// static
+void
+nsTextEditRules::HandleNewLines(nsString &aString,
+                                PRInt32 aNewlineHandling)
+{
+  if (aNewlineHandling < 0) {
+    PRInt32 caretStyle;
+    nsPlaintextEditor::GetDefaultEditorPrefs(aNewlineHandling, caretStyle);
+  }
+
+  switch(aNewlineHandling)
+  {
+  case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
+    // Strip trailing newlines first so we don't wind up with trailing spaces
+    aString.Trim(CRLF, PR_FALSE, PR_TRUE);
+    aString.ReplaceChar(CRLF, ' ');
+    break;
+  case nsIPlaintextEditor::eNewlinesStrip:
+    aString.StripChars(CRLF);
+    break;
+  case nsIPlaintextEditor::eNewlinesPasteToFirst:
+  default:
+    {
+      PRInt32 firstCRLF = aString.FindCharInSet(CRLF);
+
+      // we get first *non-empty* line.
+      PRInt32 offset = 0;
+      while (firstCRLF == offset)
+      {
+        offset++;
+        firstCRLF = aString.FindCharInSet(CRLF, offset);
+      }
+      if (firstCRLF > 0)
+        aString.Truncate(firstCRLF);
+      if (offset > 0)
+        aString.Cut(0, offset);
+    }
+    break;
+  case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
+    aString.Trim(CRLF, PR_TRUE, PR_TRUE);
+    aString.ReplaceChar(CRLF, ',');
+    break;
+  case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
+    {
+      // find each newline, and strip all the whitespace before
+      // and after it
+      PRInt32 firstCRLF = aString.FindCharInSet(CRLF);
+      while (firstCRLF >= 0)
+      {
+        PRUint32 wsBegin = firstCRLF, wsEnd = firstCRLF + 1;
+        // look backwards for the first non-whitespace char
+        while (wsBegin > 0 && NS_IS_SPACE(aString[wsBegin - 1]))
+          --wsBegin;
+        while (wsEnd < aString.Length() && NS_IS_SPACE(aString[wsEnd]))
+          ++wsEnd;
+        // now cut this range out of the string
+        aString.Cut(wsBegin, wsEnd - wsBegin);
+        // look for another CR or LF
+        firstCRLF = aString.FindCharInSet(CRLF);
+      }
+    }
+    break;
+  case nsIPlaintextEditor::eNewlinesPasteIntact:
+    // even if we're pasting newlines, don't paste leading/trailing ones
+    aString.Trim(CRLF, PR_TRUE, PR_TRUE);
+    break;
+  }
+}
+
 nsresult
 nsTextEditRules::WillInsertText(PRInt32          aAction,
                                 nsISelection *aSelection, 
                                 PRBool          *aCancel,
                                 PRBool          *aHandled,
                                 const nsAString *inString,
                                 nsAString *outString,
                                 PRInt32          aMaxLength)
@@ -634,73 +703,17 @@ nsTextEditRules::WillInsertText(PRInt32 
   // 3. strip newlines
   // 4. replace with commas
   // 5. strip newlines and surrounding whitespace
   // So find out what we're expected to do:
   if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
   {
     nsAutoString tString(*outString);
 
-    switch(mEditor->mNewlineHandling)
-    {
-    case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
-      // Strip trailing newlines first so we don't wind up with trailing spaces
-      tString.Trim(CRLF, PR_FALSE, PR_TRUE);
-      tString.ReplaceChar(CRLF, ' ');
-      break;
-    case nsIPlaintextEditor::eNewlinesStrip:
-      tString.StripChars(CRLF);
-      break;
-    case nsIPlaintextEditor::eNewlinesPasteToFirst:
-    default:
-      {
-        PRInt32 firstCRLF = tString.FindCharInSet(CRLF);
-
-        // we get first *non-empty* line.
-        PRInt32 offset = 0;
-        while (firstCRLF == offset)
-        {
-          offset++;
-          firstCRLF = tString.FindCharInSet(CRLF, offset);
-        }
-        if (firstCRLF > 0)
-          tString.Truncate(firstCRLF);
-        if (offset > 0)
-          tString.Cut(0, offset);
-      }
-      break;
-    case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
-      tString.Trim(CRLF, PR_TRUE, PR_TRUE);
-      tString.ReplaceChar(CRLF, ',');
-      break;
-    case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
-      {
-        // find each newline, and strip all the whitespace before
-        // and after it
-        PRInt32 firstCRLF = tString.FindCharInSet(CRLF);
-        while (firstCRLF >= 0)
-        {
-          PRUint32 wsBegin = firstCRLF, wsEnd = firstCRLF + 1;
-          // look backwards for the first non-whitespace char
-          while (wsBegin > 0 && NS_IS_SPACE(tString[wsBegin - 1]))
-            --wsBegin;
-          while (wsEnd < tString.Length() && NS_IS_SPACE(tString[wsEnd]))
-            ++wsEnd;
-          // now cut this range out of the string
-          tString.Cut(wsBegin, wsEnd - wsBegin);
-          // look for another CR or LF
-          firstCRLF = tString.FindCharInSet(CRLF);
-        }
-      }
-      break;
-    case nsIPlaintextEditor::eNewlinesPasteIntact:
-      // even if we're pasting newlines, don't paste leading/trailing ones
-      tString.Trim(CRLF, PR_TRUE, PR_TRUE);
-      break;
-    }
+    HandleNewLines(tString, mEditor->mNewlineHandling);
 
     outString->Assign(tString);
   }
 
   if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
   {
     // manage the password buffer
     mPasswordText.Insert(*outString, start);
@@ -1480,16 +1493,17 @@ nsresult nsTextEditRules::HideLastPWInpu
   
   nodeAsText->ReplaceData(mLastStart, mLastLength, hiddenText);
   selection->Collapse(selNode, start);
   if (start != end)
     selection->Extend(selNode, end);
   return NS_OK;
 }
 
+// static
 nsresult
 nsTextEditRules::FillBufWithPWChars(nsAString *aOutString, PRInt32 aLength)
 {
   if (!aOutString) {return NS_ERROR_NULL_POINTER;}
 
   // change the output to the platform password character
   PRUnichar passwordChar = PRUnichar('*');
   nsCOMPtr<nsILookAndFeel> lookAndFeel = do_GetService(kLookAndFeelCID);
--- a/editor/libeditor/text/nsTextEditRules.h
+++ b/editor/libeditor/text/nsTextEditRules.h
@@ -108,16 +108,52 @@ public:
     kDecreaseZIndex      = 3017,
     kIncreaseZIndex      = 3018
 
   };
   
 public:
   nsresult ResetIMETextPWBuf();
 
+  /**
+   * Handles the newline characters either according to aNewLineHandling
+   * or to the default system prefs if aNewLineHandling is negative.
+   *
+   * @param aString the string to be modified in place.
+   * @param aNewLineHandling determine the desired type of newline handling:
+   *        * negative values:
+   *          handle newlines according to platform defaults.
+   *        * nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
+   *          replace newlines with spaces.
+   *        * nsIPlaintextEditor::eNewlinesStrip:
+   *          remove newlines from the string.
+   *        * nsIPlaintextEditor::eNewlinesReplaceWithCommas:
+   *          replace newlines with commas.
+   *        * nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
+   *          collapse newlines and surrounding whitespace characters and
+   *          remove them from the string.
+   *        * nsIPlaintextEditor::eNewlinesPasteIntact:
+   *          only remove the leading and trailing newlines.
+   *        * nsIPlaintextEditor::eNewlinesPasteToFirst or any other value:
+   *          remove the first newline and all characters following it.
+   */
+  static void HandleNewLines(nsString &aString, PRInt32 aNewLineHandling);
+
+  /**
+   * Prepare a string buffer for being displayed as the contents of a password
+   * field.  This function uses the platform-specific character for representing
+   * characters entered into password fields.
+   *
+   * @param aOutString the output string.  When this function returns,
+   *        aOutString will contain aLength password characters.
+   * @param aLength the number of password characters that aOutString should
+   *        contain.
+   */
+  static nsresult FillBufWithPWChars(nsAString *aOutString, PRInt32 aLength);
+
 protected:
 
   // nsTextEditRules implementation methods
   nsresult WillInsertText(  PRInt32          aAction,
                             nsISelection *aSelection, 
                             PRBool          *aCancel,
                             PRBool          *aHandled,
                             const nsAString *inString,
@@ -180,20 +216,16 @@ protected:
   nsresult CreateBogusNodeIfNeeded(nsISelection *aSelection);
 
   /** returns a truncated insertion string if insertion would place us
       over aMaxLength */
   nsresult TruncateInsertionIfNeeded(nsISelection             *aSelection, 
                                      const nsAString          *aInString,
                                      nsAString                *aOutString,
                                      PRInt32                   aMaxLength);
-  
-  /** Echo's the insertion text into the password buffer, and converts
-      insertion text to '*'s */                                        
-  nsresult FillBufWithPWChars(nsAString *aOutString, PRInt32 aLength);
 
   /** Remove IME composition text from password buffer */
   nsresult RemoveIMETextFromPWBuf(PRUint32 &aStart, nsAString *aIMEString);
 
   nsresult CreateMozBR(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outBRNode);
 
   nsresult CheckBidiLevelForDeletion(nsISelection         *aSelection,
                                      nsIDOMNode           *aSelNode, 
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -90,18 +90,16 @@
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 #include "nsIWidget.h"
 #include "nsFocusManager.h"
 
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 
-#include "jsinterp.h" // for js_AllocStack() and js_FreeStack()
-
 #ifdef USEWEAKREFS
 #include "nsIWeakReference.h"
 #endif
 
 static const char *sJSStackContractID="@mozilla.org/js/xpc/ContextStack;1";
 
 /****************************************************************
  ******************** nsWatcherWindowEntry **********************
@@ -945,17 +943,18 @@ nsWindowWatcher::OpenWindowJSInternal(ns
   // Copy the current session storage for the current domain.
   nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aParent);
   nsIDocShell* parentDocShell = nsnull;
   if (piWindow)
     parentDocShell = piWindow->GetDocShell();
 
   if (subjectPrincipal && parentDocShell) {
     nsCOMPtr<nsIDOMStorage> storage;
-    parentDocShell->GetSessionStorageForPrincipal(subjectPrincipal, PR_FALSE,
+    parentDocShell->GetSessionStorageForPrincipal(subjectPrincipal,
+                                                  EmptyString(), PR_FALSE,
                                                   getter_AddRefs(storage));
     nsCOMPtr<nsPIDOMStorage> piStorage =
       do_QueryInterface(storage);
     if (piStorage){
       storage = piStorage->Clone();
       newDocShell->AddSessionStorage(
         piStorage->Principal(),
         storage);
--- a/gfx/qcms/Makefile.in
+++ b/gfx/qcms/Makefile.in
@@ -11,16 +11,19 @@ LIBXUL_LIBRARY = 1
 GRE_MODULE      = 1
 DIST_INSTALL = 1
 
 EXPORTS      = qcms.h qcmstypes.h
 
 CSRCS = iccread.c transform.c
 
 ifeq (86,$(findstring 86,$(OS_TEST)))
+ifeq ($(OS_ARCH)_$(OS_TEST),WINNT_x86_64)
+	CSRCS += transform-sse2.c
+else
 	CSRCS += transform-sse2.c transform-sse1.c
 ifdef GNU_CC
 	SSE1_FLAGS=-msse
 	SSE2_FLAGS=-msse2
 else
 ifeq ($(SOLARIS_SUNPRO_CC),1)
 ifneq (64,$(findstring 64,$(OS_TEST)))
 	SSE1_FLAGS=-xarch=sse
@@ -32,16 +35,17 @@ else
 	SSE2_FLAGS= -xO4
 endif
 else
 	SSE1_FLAGS=
 	SSE2_FLAGS=
 endif
 endif
 endif
+endif
 
 FORCE_STATIC_LIB = 1
 # This library is used by other shared libs
 FORCE_USE_PIC = 1
 
 include $(topsrcdir)/config/rules.mk
 
 CFLAGS          += -DMOZ_QCMS
--- a/gfx/qcms/transform.c
+++ b/gfx/qcms/transform.c
@@ -1221,23 +1221,26 @@ qcms_transform* qcms_transform_create(
 	    if (precache) {
 #ifdef X86
 		    if (sse_version_available() >= 2) {
 			    if (in_type == QCMS_DATA_RGB_8)
 				    transform->transform_fn = qcms_transform_data_rgb_out_lut_sse2;
 			    else
 				    transform->transform_fn = qcms_transform_data_rgba_out_lut_sse2;
 
+#if !(defined(_MSC_VER) && defined(_M_AMD64))
+                    /* Microsoft Compiler for x64 doesn't support MMX.
+                     * SSE code uses MMX so that we disable on x64 */
 		    } else
 		    if (sse_version_available() >= 1) {
 			    if (in_type == QCMS_DATA_RGB_8)
 				    transform->transform_fn = qcms_transform_data_rgb_out_lut_sse1;
 			    else
 				    transform->transform_fn = qcms_transform_data_rgba_out_lut_sse1;
-
+#endif
 		    } else
 #endif
 		    {
 			    if (in_type == QCMS_DATA_RGB_8)
 				    transform->transform_fn = qcms_transform_data_rgb_out_lut_precache;
 			    else
 				    transform->transform_fn = qcms_transform_data_rgba_out_lut_precache;
 		    }
--- a/gfx/thebes/public/gfxFont.h
+++ b/gfx/thebes/public/gfxFont.h
@@ -241,16 +241,17 @@ public:
     gfxSparseBitSet  mCharacterMap;
     gfxUserFontData* mUserFontData;
 
 protected:
     friend class gfxPlatformFontList;
     friend class gfxMacPlatformFontList;
     friend class gfxFcFontEntry;
     friend class gfxFontFamily;
+    friend class gfxSingleFaceMacFontFamily;
 
     gfxFontEntry() :
         mItalic(PR_FALSE), mFixedPitch(PR_FALSE),
         mIsProxy(PR_FALSE), mIsValid(PR_TRUE), 
         mIsBadUnderlineFont(PR_FALSE),
         mIsUserFont(PR_FALSE),
         mStandardFace(PR_FALSE),
         mSymbolFont(PR_FALSE),
@@ -279,27 +280,25 @@ struct FontSearch {
         mCh(aCharacter), mFontToMatch(aFont), mMatchRank(0) {
     }
     const PRUint32         mCh;
     gfxFont*               mFontToMatch;
     PRInt32                mMatchRank;
     nsRefPtr<gfxFontEntry> mBestMatch;
 };
 
-// helper class for adding other family names back into font cache
-class AddOtherFamilyNameFunctor;
-
 class gfxFontFamily {
 public:
     THEBES_INLINE_DECL_REFCOUNTING(gfxFontFamily)
 
     gfxFontFamily(const nsAString& aName) :
         mName(aName),
         mOtherFamilyNamesInitialized(PR_FALSE),
         mHasOtherFamilyNames(PR_FALSE),
+        mFaceNamesInitialized(PR_FALSE),
         mHasStyles(PR_FALSE),
         mIsSimpleFamily(PR_FALSE),
         mIsBadUnderlineFamily(PR_FALSE)
         { }
 
     virtual ~gfxFontFamily() { }
 
     const nsString& Name() { return mName; }
@@ -323,17 +322,22 @@ public:
     gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle, 
                                    PRBool& aNeedsBold);
 
     // iterates over faces looking for a match with a given characters
     // used as part of the font fallback process
     void FindFontForChar(FontSearch *aMatchData);
 
     // read in other family names, if any, and use functor to add each into cache
-    virtual void ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor);
+    virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
+
+    // read in other localized family names, fullnames and Postscript names
+    // for all faces and append to lookup tables
+    virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
+                               PRBool aNeedFullnamePostscriptNames);
 
     // find faces belonging to this family (platform implementations override this;
     // should be made pure virtual once all subclasses have been updated)
     virtual void FindStyleVariations() { }
 
     // search for a specific face using the Postscript name
     gfxFontEntry* FindFont(const nsAString& aPostscriptName);
 
@@ -349,48 +353,51 @@ public:
     // mark this family as being in the "bad" underline offset blacklist
     void SetBadUnderlineFamily() {
         mIsBadUnderlineFamily = PR_TRUE;
         if (mHasStyles) {
             SetBadUnderlineFonts();
         }
     }
 
+    PRBool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; }
+
     // sort available fonts to put preferred (standard) faces towards the end
     void SortAvailableFonts();
 
     // check whether the family fits into the simple 4-face model,
     // so we can use simplified style-matching;
     // if so set the mIsSimpleFamily flag (defaults to False before we've checked)
     void CheckForSimpleFamily();
 
 protected:
     // fills in an array with weights of faces that match style,
     // returns whether any matching entries found
     virtual PRBool FindWeightsForStyle(gfxFontEntry* aFontsForWeights[],
                                        PRBool anItalic, PRInt16 aStretch);
 
-    PRBool ReadOtherFamilyNamesForFace(AddOtherFamilyNameFunctor& aOtherFamilyFunctor,
-                                       gfxFontEntry *aFontEntry,
+    PRBool ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
+                                       nsTArray<PRUint8>& aNameTable,
                                        PRBool useFullName = PR_FALSE);
 
     // set whether this font family is in "bad" underline offset blacklist.
     void SetBadUnderlineFonts() {
         PRUint32 i, numFonts = mAvailableFonts.Length();
         for (i = 0; i < numFonts; i++) {
             if (mAvailableFonts[i]) {
                 mAvailableFonts[i]->mIsBadUnderlineFont = PR_TRUE;
             }
         }
     }
 
     nsString mName;
     nsTArray<nsRefPtr<gfxFontEntry> >  mAvailableFonts;
     PRPackedBool mOtherFamilyNamesInitialized;
     PRPackedBool mHasOtherFamilyNames;
+    PRPackedBool mFaceNamesInitialized;
     PRPackedBool mHasStyles;
     PRPackedBool mIsSimpleFamily;
     PRPackedBool mIsBadUnderlineFamily;
 
     enum {
         // for "simple" families, the faces are stored in mAvailableFonts
         // with fixed positions:
         kRegularFaceIndex    = 0,
--- a/gfx/thebes/src/gfxCoreTextFonts.cpp
+++ b/gfx/thebes/src/gfxCoreTextFonts.cpp
@@ -580,38 +580,38 @@ gfxCoreTextFont::SetGlyphsFromRun(gfxTex
     // and so allocating a new array and copying data with CTRunGetGlyphs
     // will be extremely rare.
     // If this were not the case, we could use an nsAutoTArray<> to
     // try and avoid the heap allocation for small runs.
     // It's possible that some future change to CoreText will mean that
     // CTRunGetGlyphsPtr fails more often; if this happens, nsAutoTArray<>
     // may become an attractive option.
     glyphs = ::CTRunGetGlyphsPtr(aCTRun);
-    if (glyphs == NULL) {
-        glyphsArray = new CGGlyph[numGlyphs];
+    if (!glyphs) {
+        glyphsArray = new (std::nothrow) CGGlyph[numGlyphs];
         if (!glyphsArray)
             return NS_ERROR_OUT_OF_MEMORY;
         ::CTRunGetGlyphs(aCTRun, ::CFRangeMake(0, 0), glyphsArray.get());
         glyphs = glyphsArray.get();
     }
 
     positions = ::CTRunGetPositionsPtr(aCTRun);
-    if (positions == NULL) {
-        positionsArray = new CGPoint[numGlyphs];
+    if (!positions) {
+        positionsArray = new (std::nothrow) CGPoint[numGlyphs];
         if (!positionsArray)
             return NS_ERROR_OUT_OF_MEMORY;
         ::CTRunGetPositions(aCTRun, ::CFRangeMake(0, 0), positionsArray.get());
         positions = positionsArray.get();
     }
 
     // Remember that the glyphToChar indices relate to the CoreText line
     // not to the beginning of the textRun, the font run, or the stringRange of the glyph run
     glyphToChar = ::CTRunGetStringIndicesPtr(aCTRun);
-    if (glyphToChar == NULL) {
-        glyphToCharArray = new CFIndex[numGlyphs];
+    if (!glyphToChar) {
+        glyphToCharArray = new (std::nothrow) CFIndex[numGlyphs];
         if (!glyphToCharArray)
             return NS_ERROR_OUT_OF_MEMORY;
         ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get());
         glyphToChar = glyphToCharArray.get();
     }
 
     double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0), NULL, NULL, NULL);
 
@@ -631,20 +631,20 @@ gfxCoreTextFont::SetGlyphsFromRun(gfxTex
 
     static const PRInt32 NO_GLYPH = -1;
     nsAutoTArray<PRInt32,SMALL_GLYPH_RUN> charToGlyphArray;
     if (!charToGlyphArray.SetLength(stringRange.length))
         return NS_ERROR_OUT_OF_MEMORY;
     PRInt32 *charToGlyph = charToGlyphArray.Elements();
     for (PRInt32 offset = 0; offset < stringRange.length; ++offset)
         charToGlyph[offset] = NO_GLYPH;
-    for (PRInt32 g = 0; g < numGlyphs; ++g) {
-        PRInt32 loc = glyphToChar[g] - stringRange.location;
+    for (PRInt32 i = 0; i < numGlyphs; ++i) {
+        PRInt32 loc = glyphToChar[i] - stringRange.location;
         if (loc >= 0 && loc < stringRange.length) {
-            charToGlyph[loc] = g;
+            charToGlyph[loc] = i;
         }
     }
 
     // Find character and glyph clumps that correspond, allowing for ligatures,
     // indic reordering, split glyphs, etc.
     //
     // The idea is that we'll find a character sequence starting at the first char of stringRange,
     // and extend it until it includes the character associated with the first glyph;
@@ -661,26 +661,31 @@ gfxCoreTextFont::SetGlyphsFromRun(gfxTex
     PRInt32 glyphStart = 0; // looking for a clump that starts at this glyph index
     PRInt32 charStart = isLTR ?
         0 : stringRange.length-1; // and this char index (in the stringRange of the glyph run)
 
     while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for
 
         PRBool inOrder = PR_TRUE;
         PRInt32 charEnd = glyphToChar[glyphStart] - stringRange.location;
+        NS_ASSERTION(charEnd >= 0 && charEnd < stringRange.length,
+                     "glyph-to-char mapping points outside string range");
         PRInt32 glyphEnd = glyphStart;
         PRInt32 charLimit = isLTR ? stringRange.length : -1;
         do {
             // This is normally executed once for each iteration of the outer loop,
             // but in unusual cases where the character/glyph association is complex,
             // the initial character range might correspond to a non-contiguous
             // glyph range with "holes" in it. If so, we will repeat this loop to
             // extend the character range until we have a contiguous glyph sequence.
+            NS_ASSERTION((direction > 0 && charEnd < charLimit) ||
+                         (direction < 0 && charEnd > charLimit),
+                         "no characters left in range?");
             charEnd += direction;
-            while (charEnd != charLimit && charToGlyph[charEnd - stringRange.location] == NO_GLYPH) {
+            while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
                 charEnd += direction;
             }
 
             // find the maximum glyph index covered by the clump so far
             for (PRInt32 i = charStart; i != charEnd; i += direction) {
                 if (charToGlyph[i] != NO_GLYPH) {
                     glyphEnd = PR_MAX(glyphEnd, charToGlyph[i] + 1); // update extent of glyph range
                 }
--- a/gfx/thebes/src/gfxFT2FontList.cpp
+++ b/gfx/thebes/src/gfxFT2FontList.cpp
@@ -116,20 +116,24 @@ gfxFT2FontList::AppendFacesFromFontFile(
             FontEntry* fe = FontEntry::CreateFontEntryFromFace(face);
             if (fe) {
                 NS_ConvertUTF8toUTF16 name(face->family_name);
                 BuildKeyNameFromFontName(name);       
                 gfxFontFamily *family = mFontFamilies.GetWeak(name);
                 if (!family) {
                     family = new gfxFontFamily(name);
                     mFontFamilies.Put(name, family);
+                    if (mBadUnderlineFamilyNames.GetEntry(name))
+                        family->SetBadUnderlineFamily();
                 }
                 family->AddFontEntry(fe);
                 fe->SetFamily(family);
                 family->SetHasStyles(PR_TRUE);
+                if (family->IsBadUnderlineFamily())
+                    fe->mIsBadUnderlineFont = PR_TRUE;
 #ifdef PR_LOGGING
                 if (LOG_ENABLED()) {
                     LOG(("(fontinit) added (%s) to family (%s)"
                          " with style: %s weight: %d stretch: %d",
                          NS_ConvertUTF16toUTF8(fe->Name()).get(), 
                          NS_ConvertUTF16toUTF8(family->Name()).get(), 
                          fe->IsItalic() ? "italic" : "normal",
                          fe->Weight(), fe->Stretch()));
@@ -186,30 +190,20 @@ gfxFT2FontList::FindFonts()
                 FindClose(handle);
         }
     }
 }
 
 void
 gfxFT2FontList::InitFontList()
 {
-    mFontFamilies.Clear();
-    mOtherFamilyNames.Clear();
-    mOtherFamilyNamesInitialized = PR_FALSE;
-    mPrefFonts.Clear();
-    CancelLoader();
-
-    // initialize ranges of characters for which system-wide font search should be skipped
-    mCodepointsWithNoFonts.reset();
-    mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
-    mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
-
+    // reset font lists
+    gfxPlatformFontList::InitFontList();
+    
     FindFonts();
-
-    InitBadUnderlineList();
 }
 
 struct FullFontNameSearch {
     FullFontNameSearch(const nsAString& aFullName)
         : mFullName(aFullName), mFontEntry(nsnull)
     { }
 
     nsString     mFullName;
--- a/gfx/thebes/src/gfxFont.cpp
+++ b/gfx/thebes/src/gfxFont.cpp
@@ -149,18 +149,17 @@ gfxFontFamily::SortAvailableFonts()
     mAvailableFonts.Sort(FontEntryStandardFaceComparator());
 }
 
 PRBool
 gfxFontFamily::HasOtherFamilyNames()
 {
     // need to read in other family names to determine this
     if (!mOtherFamilyNamesInitialized) {
-        AddOtherFamilyNameFunctor addOtherNames(gfxPlatformFontList::PlatformFontList());
-        ReadOtherFamilyNames(addOtherNames);  // sets mHasOtherFamilyNames
+        ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList());  // sets mHasOtherFamilyNames
     }
     return mHasOtherFamilyNames;
 }
 
 gfxFontEntry*
 gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle, PRBool& aNeedsBold)
 {
     if (!mHasStyles)
@@ -495,31 +494,24 @@ gfxFontFamily::FindFontForChar(FontSearc
                 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) 
         {
             aMatchData->mBestMatch = fe;
             aMatchData->mMatchRank = rank;
         }
     }
 }
 
-
 // returns true if other names were found, false otherwise
 PRBool
-gfxFontFamily::ReadOtherFamilyNamesForFace(AddOtherFamilyNameFunctor& aOtherFamilyFunctor,
-                                           gfxFontEntry *aFontEntry,
+gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
+                                           nsTArray<PRUint8>& aNameTable,
                                            PRBool useFullName)
 {
-    const PRUint32 kNAME = TRUETYPE_TAG('n','a','m','e');
-
-    nsAutoTArray<PRUint8,8192> buffer;
-    if (aFontEntry->GetFontTable(kNAME, buffer) != NS_OK)
-        return PR_FALSE;
-
-    const PRUint8 *nameData = buffer.Elements();
-    PRUint32 dataLength = buffer.Length();
+    const PRUint8 *nameData = aNameTable.Elements();
+    PRUint32 dataLength = aNameTable.Length();
     const gfxFontUtils::NameHeader *nameHeader =
         reinterpret_cast<const gfxFontUtils::NameHeader*>(nameData);
 
     PRUint32 nameCount = nameHeader->count;
     if (nameCount * sizeof(gfxFontUtils::NameRecord) > dataLength) {
         NS_WARNING("invalid font (name records)");
         return PR_FALSE;
     }
@@ -546,58 +538,132 @@ gfxFontFamily::ReadOtherFamilyNamesForFa
             PRBool ok = gfxFontUtils::DecodeFontName(nameData + stringsBase + nameOff,
                                                      nameLen,
                                                      PRUint32(nameRecord->platformID),
                                                      PRUint32(nameRecord->encodingID),
                                                      PRUint32(nameRecord->languageID),
                                                      otherFamilyName);
             // add if not same as canonical family name
             if (ok && otherFamilyName != mName) {
-                aOtherFamilyFunctor(this, otherFamilyName);
+                aPlatformFontList->AddOtherFamilyName(this, otherFamilyName);
                 foundNames = PR_TRUE;
             }
         }
     }
 
     return foundNames;
 }
 
 
 void
-gfxFontFamily::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor)
+gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
 {
     if (mOtherFamilyNamesInitialized) 
         return;
     mOtherFamilyNamesInitialized = PR_TRUE;
 
     FindStyleVariations();
 
     // read in other family names for the first face in the list
-    PRUint32 numFonts = mAvailableFonts.Length();
-    PRUint32 i;
+    PRUint32 i, numFonts = mAvailableFonts.Length();
+    const PRUint32 kNAME = TRUETYPE_TAG('n','a','m','e');
+    nsAutoTArray<PRUint8,8192> buffer;
+
     for (i = 0; i < numFonts; ++i) {
-        if (!mAvailableFonts[i])
+        gfxFontEntry *fe = mAvailableFonts[i];
+        if (!fe)
             continue;
-        mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor,
-                                                           mAvailableFonts[i].get());
+
+        if (fe->GetFontTable(kNAME, buffer) != NS_OK)
+            continue;
+
+        mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
+                                                           buffer);
         break;
     }
 
     // read in other names for the first face in the list with the assumption
     // that if extra names don't exist in that face then they don't exist in
     // other faces for the same font
-    if (mHasOtherFamilyNames) {
-        // read in names for all faces, needed to catch cases where fonts have
-        // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
-        for ( ; i < numFonts; i++) {
-            if (!mAvailableFonts[i])
-                continue;
-            ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, mAvailableFonts[i].get());
+    if (!mHasOtherFamilyNames) 
+        return;
+
+    // read in names for all faces, needed to catch cases where fonts have
+    // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
+    for ( ; i < numFonts; i++) {
+        gfxFontEntry *fe = mAvailableFonts[i];
+        if (!fe)
+            continue;
+
+        if (fe->GetFontTable(kNAME, buffer) != NS_OK)
+            continue;
+
+        ReadOtherFamilyNamesForFace(aPlatformFontList, buffer);
+    }
+}
+
+void
+gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, 
+                             PRBool aNeedFullnamePostscriptNames)
+{
+    // if all needed names have already been read, skip
+    if (mOtherFamilyNamesInitialized &&
+        (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
+        return;
+
+    FindStyleVariations();
+
+    PRUint32 i, numFonts = mAvailableFonts.Length();
+    const PRUint32 kNAME = TRUETYPE_TAG('n','a','m','e');
+    nsAutoTArray<PRUint8,8192> buffer;
+    nsAutoString fullname, psname;
+
+    PRBool firstTime = PR_TRUE, readAllFaces = PR_FALSE;
+    for (i = 0; i < numFonts; ++i) {
+        gfxFontEntry *fe = mAvailableFonts[i];
+        if (!fe)
+            continue;
+
+        if (fe->GetFontTable(kNAME, buffer) != NS_OK)
+            continue;
+
+        if (aNeedFullnamePostscriptNames) {
+            if (gfxFontUtils::ReadCanonicalName(
+                    buffer, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
+            {
+                aPlatformFontList->AddFullname(fe, fullname);
+            }
+
+            if (gfxFontUtils::ReadCanonicalName(
+                    buffer, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
+            {
+                aPlatformFontList->AddPostscriptName(fe, psname);
+            }
         }
+
+       if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
+           PRBool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList,
+                                                               buffer);
+
+           // if the first face has a different name, scan all faces, otherwise
+           // assume the family doesn't have other names
+           if (firstTime && foundOtherName) {
+               mHasOtherFamilyNames = PR_TRUE;
+               readAllFaces = PR_TRUE;
+           }
+           firstTime = PR_FALSE;
+       }
+
+       // if not reading in any more names, skip other faces
+       if (!readAllFaces && !aNeedFullnamePostscriptNames)
+           break;
     }
+
+    mFaceNamesInitialized = PR_TRUE;
+    mOtherFamilyNamesInitialized = PR_TRUE;
 }
 
 
 gfxFontEntry*
 gfxFontFamily::FindFont(const nsAString& aPostscriptName)
 {
     // find the font using a simple linear search
     PRUint32 numFonts = mAvailableFonts.Length();
@@ -3245,23 +3311,30 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun
     while (iter.NextRun()) {
         gfxFont *font = iter.GetGlyphRun()->mFont;
         NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
 #ifdef DEBUG
         lastFont = font;
         PRUint32 end = iter.GetStringEnd();
 #endif
         PRUint32 start = iter.GetStringStart();
-        // These assertions are probably not needed; it's possible for us to assign
+
+        // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
+        // Although it's unusual (and not desirable), it's possible for us to assign
         // different fonts to a base character and a following diacritic.
-        // View http://www.alanwood.net/unicode/cyrillic.html on OS X 10.5 for an example.
-        NS_ASSERTION(aSource->IsClusterStart(start),
-                     "Started word in the middle of a cluster...");
-        NS_ASSERTION(end == aSource->GetLength() || aSource->IsClusterStart(end),
-                     "Ended word in the middle of a cluster...");
+        // Example on OSX 10.5/10.6 with default fonts installed:
+        //     data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
+        //                    &#x043E;&#x0486;&#x20;&#x043E;&#x0486;
+        // This means the rendering of the cluster will probably not be very good,
+        // but it's the best we can do for now if the specified font only covered the
+        // initial base character and not its applied marks.
+        NS_WARN_IF_FALSE(aSource->IsClusterStart(start),
+                         "Started font run in the middle of a cluster");
+        NS_WARN_IF_FALSE(end == aSource->GetLength() || aSource->IsClusterStart(end),
+                         "Ended font run in the middle of a cluster");
 
         nsresult rv = AddGlyphRun(font, start - aStart + aDest);
         if (NS_FAILED(rv))
             return;
     }
 }
 
 void
--- a/gfx/thebes/src/gfxGDIFontList.cpp
+++ b/gfx/thebes/src/gfxGDIFontList.cpp
@@ -433,17 +433,17 @@ GDIFontFamily::FamilyAddStylesProc(const
         if (fe->mWeight == logFont.lfWeight &&
             fe->mItalic == (logFont.lfItalic == 0xFF)) {
             // update the charset bit here since this could be different
             fe->mCharset.set(metrics.tmCharSet);
             return 1; 
         }
     }
 
-    fe = GDIFontEntry::CreateFontEntry(ff->mName, feType, (logFont.lfItalic == 0xFF),
+    fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName), feType, (logFont.lfItalic == 0xFF),
                                        (PRUint16) (logFont.lfWeight), nsnull);
     if (!fe)
         return 1;
 
     ff->mAvailableFonts.AppendElement(fe);
     fe->SetFamily(ff);
 
     // mark the charset bit
@@ -587,43 +587,34 @@ gfxGDIFontList::GetFontSubstitutes()
 
 void
 gfxGDIFontList::InitFontList()
 {
     gfxFontCache *fc = gfxFontCache::GetCache();
     if (fc)
         fc->AgeAllGenerations();
 
-    mFontFamilies.Clear();
-    mOtherFamilyNames.Clear();
-    mOtherFamilyNamesInitialized = PR_FALSE;
-    mPrefFonts.Clear();
+    // reset font lists
+    gfxPlatformFontList::InitFontList();
+    
     mFontSubstitutes.Clear();
     mNonExistingFonts.Clear();
-    CancelLoader();
-
-    // initialize ranges of characters for which system-wide font search should be skipped
-    mCodepointsWithNoFonts.reset();
-    mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
-    mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
 
     // iterate over available families
     LOGFONTW logfont;
     memset(&logfont, 0, sizeof(logfont));
     logfont.lfCharSet = DEFAULT_CHARSET;
 
     AutoDC hdc;
     int result = EnumFontFamiliesExW(hdc.GetDC(), &logfont,
                                      (FONTENUMPROCW)&EnumFontFamExProc,
                                      0, 0);
 
     GetFontSubstitutes();
 
-    InitBadUnderlineList();
-
     StartLoader(kDelayBeforeLoadingFonts, kIntervalBetweenLoadingFonts);
 }
     
 int CALLBACK
 gfxGDIFontList::EnumFontFamExProc(ENUMLOGFONTEXW *lpelfe,
                                       NEWTEXTMETRICEXW *lpntme,
                                       DWORD fontType,
                                       LPARAM lParam)
@@ -638,82 +629,57 @@ gfxGDIFontList::EnumFontFamExProc(ENUMLO
     BuildKeyNameFromFontName(name);
 
     gfxGDIFontList *fontList = PlatformFontList();
 
     if (!fontList->mFontFamilies.GetWeak(name)) {
         nsDependentString faceName(lf.lfFaceName);
         nsRefPtr<gfxFontFamily> family = new GDIFontFamily(faceName);
         fontList->mFontFamilies.Put(name, family);
+        if (fontList->mBadUnderlineFamilyNames.GetEntry(name))
+            family->SetBadUnderlineFamily();
     }
 
     return 1;
 }
 
 gfxFontEntry* 
 gfxGDIFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                 const nsAString& aFullname)
 {
-    LOGFONTW logFont;
-    memset(&logFont, 0, sizeof(LOGFONTW));
-    logFont.lfCharSet = DEFAULT_CHARSET;
-    PRUint32 namelen = PR_MIN(aFullname.Length(), LF_FACESIZE - 1);
-    ::memcpy(logFont.lfFaceName,
-             nsPromiseFlatString(aFullname).get(),
-             namelen * sizeof(PRUnichar));
-    logFont.lfFaceName[namelen] = 0;
-
-    AutoDC dc;
-    ::SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
-
-    AutoSelectFont font(dc.GetDC(), &logFont);
-    if (!font.IsValid())
-        return nsnull;
-
-    // fetch fullname from name table (Windows takes swapped tag order)
-    const PRUint32 kNameTag = NS_SWAP32(TRUETYPE_TAG('n','a','m','e'));
-    nsAutoString fullName;
+    PRBool found;
+    gfxFontEntry *lookup;
 
-    {
-        DWORD len = ::GetFontData(dc.GetDC(), kNameTag, 0, nsnull, 0);
-        if (len == GDI_ERROR || len == 0) // not a truetype font --
-            return nsnull;                // so just ignore
-    
-        nsAutoTArray<PRUint8,1024> nameData;
-        if (!nameData.AppendElements(len))
-            return nsnull;
-        PRUint8 *nameTable = nameData.Elements();
-    
-        DWORD newLen = ::GetFontData(dc.GetDC(), kNameTag, 0, nameTable, len);
-        if (newLen != len)
-            return nsnull;
-    
-        nsresult rv;
-        rv = gfxFontUtils::ReadCanonicalName(nameData, 
-                                             gfxFontUtils::NAME_ID_FULL,
-                                             fullName);
-        if (NS_FAILED(rv))
-            return nsnull;
+    // initialize name lookup tables if needed
+    if (!mFaceNamesInitialized) {
+        InitFaceNameLists();
     }
 
-    // reject if different from canonical fullname
-    if (!aFullname.Equals(fullName))
+    // lookup in name lookup tables, return null if not found
+    if (!(lookup = mPostscriptNames.GetWeak(aFullname, &found)) &&
+        !(lookup = mFullnames.GetWeak(aFullname, &found))) 
+    {
         return nsnull;
+    }
 
-    // create a new font entry
+    // create a new font entry with the proxy entry style characteristics
     PRUint16 w = (aProxyEntry->mWeight == 0 ? 400 : aProxyEntry->mWeight);
     PRBool isCFF = PR_FALSE; // jtdfix -- need to determine this
     
-    gfxFontEntry *fe = GDIFontEntry::CreateFontEntry(aFullname, 
+    // use the face name from the lookup font entry, which will be the localized
+    // face name which GDI mapping tables use (e.g. with the system locale set to
+    // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
+    // 'Arial Vet' which can be used as a key in GDI font lookups).
+    gfxFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(), 
         gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, 
         PRUint32(aProxyEntry->mItalic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL), 
         w, nsnull);
         
     if (!fe)
-        return fe;
+        return nsnull;
 
     fe->mIsUserFont = PR_TRUE;
     return fe;
 }
 
 void gfxGDIFontList::InitializeFontEmbeddingProcs()
 {
     HMODULE fontlib = LoadLibraryW(L"t2embed.dll");
--- a/gfx/thebes/src/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/src/gfxMacPlatformFontList.mm
@@ -543,17 +543,17 @@ public:
     gfxSingleFaceMacFontFamily(nsAString& aName) :
         gfxFontFamily(aName)
     {}
 
     virtual ~gfxSingleFaceMacFontFamily() {}
 
     virtual void LocalizedName(nsAString& aLocalizedName);
 
-    virtual void ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor);
+    virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
 };
 
 void
 gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
 {
     if (!HasOtherFamilyNames()) {
         aLocalizedName = mName;
         return;
@@ -570,33 +570,43 @@ gfxSingleFaceMacFontFamily::LocalizedNam
         }
     }
 
     // failed to get localized name, just use the canonical one
     aLocalizedName = mName;
 }
 
 void
-gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor)
+gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
 {
     if (mOtherFamilyNamesInitialized)
         return;
 
-    mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor,
-                                                       mAvailableFonts[0].get(),
+    gfxFontEntry *fe = mAvailableFonts[0];
+    if (!fe)
+        return;
+
+    const PRUint32 kNAME = TRUETYPE_TAG('n','a','m','e');
+    nsAutoTArray<PRUint8,8192> buffer;
+
+    if (fe->GetFontTable(kNAME, buffer) != NS_OK)
+        return;
+
+    mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
+                                                       buffer,
                                                        PR_TRUE);
     mOtherFamilyNamesInitialized = PR_TRUE;
 }
 
 
 /* gfxMacPlatformFontList */
 #pragma mark-
 
 gfxMacPlatformFontList::gfxMacPlatformFontList() :
-    mATSGeneration(PRUint32(kATSGenerationInitial))
+    gfxPlatformFontList(PR_FALSE), mATSGeneration(PRUint32(kATSGenerationInitial))
 {
     ::ATSFontNotificationSubscribe(ATSNotification,
                                    kATSFontNotifyOptionDefault,
                                    (void*)this, nsnull);
 
     // this should always be available (though we won't actually fail if it's missing,
     // we'll just end up doing a search and then caching the new result instead)
     mReplacementCharFallbackFamily = NS_LITERAL_STRING("Lucida Grande");
@@ -613,27 +623,19 @@ gfxMacPlatformFontList::InitFontList()
 
     // need to ignore notifications after adding each font
     if (mATSGeneration == currentGeneration)
         return;
 
     mATSGeneration = currentGeneration;
     PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit) updating to generation: %d", mATSGeneration));
 
-    mFontFamilies.Clear();
-    mOtherFamilyNames.Clear();
-    mOtherFamilyNamesInitialized = PR_FALSE;
-    mPrefFonts.Clear();
-    CancelLoader();
-
-    // initialize ranges of characters for which system-wide font search should be skipped
-    mCodepointsWithNoFonts.reset();
-    mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
-    mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
-
+    // reset font lists
+    gfxPlatformFontList::InitFontList();
+    
     // iterate over available families
     NSEnumerator *families = [[sFontManager availableFontFamilies]
                               objectEnumerator];  // returns "canonical", non-localized family name
 
     nsAutoString availableFamilyName;
 
     NSString *availableFamily = nil;
     while ((availableFamily = [families nextObject])) {
@@ -643,16 +645,20 @@ gfxMacPlatformFontList::InitFontList()
 
         // create a family entry
         gfxFontFamily *familyEntry = new gfxMacFontFamily(availableFamilyName);
         if (!familyEntry) break;
 
         // add the family entry to the hash table
         ToLowerCase(availableFamilyName);
         mFontFamilies.Put(availableFamilyName, familyEntry);
+
+        // check the bad underline blacklist
+        if (mBadUnderlineFamilyNames.GetEntry(availableFamilyName))
+            familyEntry->SetBadUnderlineFamily();
     }
 
     InitSingleFaceList();
 
     // to avoid full search of font name tables, seed the other names table with localized names from
     // some of the prefs fonts which are accessed via their localized names.  changes in the pref fonts will only cause
     // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
     PreloadNamesList();
@@ -665,18 +671,16 @@ gfxMacPlatformFontList::InitFontList()
         EliminateDuplicateFaces(NS_LITERAL_STRING("Helvetica"));
 
         // Cocoa reports that Courier and Monaco are not fixed-pitch fonts
         // so explicitly tweak these settings
         SetFixedPitch(NS_LITERAL_STRING("Courier"));
         SetFixedPitch(NS_LITERAL_STRING("Monaco"));
     }
 
-    InitBadUnderlineList();
-
     // start the delayed cmap loader
     StartLoader(kDelayBeforeLoadingCmaps, kIntervalBetweenLoadingCmaps);
 }
 
 void
 gfxMacPlatformFontList::InitSingleFaceList()
 {
     nsAutoTArray<nsString, 10> singleFaceFonts;
--- a/gfx/thebes/src/gfxPlatformFontList.cpp
+++ b/gfx/thebes/src/gfxPlatformFontList.cpp
@@ -98,39 +98,70 @@ gfxFontListPrefObserver::Observe(nsISupp
     NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), "invalid topic");
     // XXX this could be made to only clear out the cache for the prefs that were changed
     // but it probably isn't that big a deal.
     gfxPlatformFontList::PlatformFontList()->ClearPrefFonts();
     return NS_OK;
 }
 
 
-gfxPlatformFontList::gfxPlatformFontList()
-    : mStartIndex(0), mIncrement(kNumFontsPerSlice), mNumFamilies(0)
+gfxPlatformFontList::gfxPlatformFontList(PRBool aNeedFullnamePostscriptNames)
+    : mNeedFullnamePostscriptNames(aNeedFullnamePostscriptNames),
+      mStartIndex(0), mIncrement(kNumFontsPerSlice), mNumFamilies(0)
 {
     mFontFamilies.Init(100);
     mOtherFamilyNames.Init(30);
     mOtherFamilyNamesInitialized = PR_FALSE;
+
+    if (mNeedFullnamePostscriptNames) {
+        mFullnames.Init(100);
+        mPostscriptNames.Init(100);
+    }
+    mFaceNamesInitialized = PR_FALSE;
+
     mPrefFonts.Init(10);
 
+    mBadUnderlineFamilyNames.Init(10);
+    LoadBadUnderlineList();
+
     // pref changes notification setup
     gfxFontListPrefObserver *observer = new gfxFontListPrefObserver();
     if (observer) {
         nsCOMPtr<nsIPrefBranch2> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
         if (pref) {
             pref->AddObserver("font.", observer, PR_FALSE);
             pref->AddObserver("font.name-list.", observer, PR_FALSE);
             pref->AddObserver("intl.accept_languages", observer, PR_FALSE);  // hmmmm...
         } else {
             delete observer;
         }
     }
 }
 
 void
+gfxPlatformFontList::InitFontList()
+{
+    mFontFamilies.Clear();
+    mOtherFamilyNames.Clear();
+    mOtherFamilyNamesInitialized = PR_FALSE;
+    if (mNeedFullnamePostscriptNames) {
+        mFullnames.Clear();
+        mPostscriptNames.Clear();
+    }
+    mFaceNamesInitialized = PR_FALSE;
+    mPrefFonts.Clear();
+    CancelLoader();
+
+    // initialize ranges of characters for which system-wide font search should be skipped
+    mCodepointsWithNoFonts.reset();
+    mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
+    mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
+}
+
+void
 gfxPlatformFontList::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
 {
     aResult = aKeyName;
     ToLowerCase(aResult);
 }
 
 void 
 gfxPlatformFontList::InitOtherFamilyNames()
@@ -142,30 +173,37 @@ gfxPlatformFontList::InitOtherFamilyName
 }
                                                          
 PLDHashOperator PR_CALLBACK
 gfxPlatformFontList::InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
                                               nsRefPtr<gfxFontFamily>& aFamilyEntry,
                                               void* userArg)
 {
     gfxPlatformFontList *fc = static_cast<gfxPlatformFontList*>(userArg);
-    AddOtherFamilyNameFunctor addOtherNames(fc);
-    aFamilyEntry->ReadOtherFamilyNames(addOtherNames);
+    aFamilyEntry->ReadOtherFamilyNames(fc);
     return PL_DHASH_NEXT;
 }
 
 void
-gfxPlatformFontList::ReadOtherFamilyNamesForFamily(const nsAString& aFamilyName)
+gfxPlatformFontList::InitFaceNameLists()
 {
-    gfxFontFamily *familyEntry = FindFamily(aFamilyName);
+    mFaceNamesInitialized = PR_TRUE;
+
+    // iterate over all font families and read in other family names
+    mFontFamilies.Enumerate(gfxPlatformFontList::InitFaceNameListsProc, this);
+}
 
-    if (familyEntry) {
-        AddOtherFamilyNameFunctor addOtherNames(this);
-        familyEntry->ReadOtherFamilyNames(addOtherNames);
-    }
+PLDHashOperator PR_CALLBACK
+gfxPlatformFontList::InitFaceNameListsProc(nsStringHashKey::KeyType aKey,
+                                           nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                                           void* userArg)
+{
+    gfxPlatformFontList *fc = static_cast<gfxPlatformFontList*>(userArg);
+    aFamilyEntry->ReadFaceNames(fc, fc->NeedFullnamePostscriptNames());
+    return PL_DHASH_NEXT;
 }
 
 void
 gfxPlatformFontList::PreloadNamesList()
 {
     nsAutoTArray<nsString, 10> preloadFonts;
     gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
 
@@ -173,18 +211,17 @@ gfxPlatformFontList::PreloadNamesList()
     for (PRUint32 i = 0; i < numFonts; i++) {
         PRBool found;
         nsAutoString key;
         GenerateFontListKey(preloadFonts[i], key);
         
         // only search canonical names!
         gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key, &found);
         if (familyEntry) {
-            AddOtherFamilyNameFunctor addOtherNames(this);
-            familyEntry->ReadOtherFamilyNames(addOtherNames);
+            familyEntry->ReadOtherFamilyNames(this);
         }
     }
 
 }
 
 void 
 gfxPlatformFontList::SetFixedPitch(const nsAString& aFamilyName)
 {
@@ -197,29 +234,25 @@ gfxPlatformFontList::SetFixedPitch(const
     PRUint32 i, numFonts = fontlist.Length();
 
     for (i = 0; i < numFonts; i++) {
         fontlist[i]->mFixedPitch = 1;
     }
 }
 
 void
-gfxPlatformFontList::InitBadUnderlineList()
+gfxPlatformFontList::LoadBadUnderlineList()
 {
     nsAutoTArray<nsString, 10> blacklist;
     gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
     PRUint32 numFonts = blacklist.Length();
     for (PRUint32 i = 0; i < numFonts; i++) {
-        PRBool found;
         nsAutoString key;
         GenerateFontListKey(blacklist[i], key);
-
-        gfxFontFamily *familyEntry = mFontFamilies.GetWeak(key, &found);
-        if (familyEntry)
-            familyEntry->SetBadUnderlineFamily();
+        mBadUnderlineFamilyNames.PutEntry(key);
     }
 }
 
 PRBool 
 gfxPlatformFontList::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName)
 {
     gfxFontFamily *family = FindFamily(aFontName);
     if (family) {
@@ -428,16 +461,44 @@ gfxPlatformFontList::AddOtherFamilyName(
     PRBool found;
     GenerateFontListKey(aOtherFamilyName, key);
 
     if (!mOtherFamilyNames.GetWeak(key, &found)) {
         mOtherFamilyNames.Put(key, aFamilyEntry);
         PR_LOG(gFontListLog, PR_LOG_DEBUG, ("(fontlist-otherfamily) canonical family: %s, other family: %s\n", 
                                             NS_ConvertUTF16toUTF8(aFamilyEntry->Name()).get(), 
                                             NS_ConvertUTF16toUTF8(aOtherFamilyName).get()));
+        if (mBadUnderlineFamilyNames.GetEntry(key))
+            aFamilyEntry->SetBadUnderlineFamily();
+    }
+}
+
+void
+gfxPlatformFontList::AddFullname(gfxFontEntry *aFontEntry, nsAString& aFullname)
+{
+    PRBool found;
+
+    if (!mFullnames.GetWeak(aFullname, &found)) {
+        mFullnames.Put(aFullname, aFontEntry);
+        PR_LOG(gFontListLog, PR_LOG_DEBUG, ("(fontlist-fullname) name: %s, fullname: %s\n", 
+                                            NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(), 
+                                            NS_ConvertUTF16toUTF8(aFullname).get()));
+    }
+}
+
+void
+gfxPlatformFontList::AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName)
+{
+    PRBool found;
+
+    if (!mPostscriptNames.GetWeak(aPostscriptName, &found)) {
+        mPostscriptNames.Put(aPostscriptName, aFontEntry);
+        PR_LOG(gFontListLog, PR_LOG_DEBUG, ("(fontlist-postscript) name: %s, psname: %s\n", 
+                                            NS_ConvertUTF16toUTF8(aFontEntry->Name()).get(), 
+                                            NS_ConvertUTF16toUTF8(aPostscriptName).get()));
     }
 }
 
 PRBool
 gfxPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
 {
     aFamilyName.Truncate();
     ResolveFontName(aFontName, aFamilyName);
@@ -453,28 +514,27 @@ gfxPlatformFontList::InitLoader()
 }
 
 PRBool 
 gfxPlatformFontList::RunLoader()
 {
     PRUint32 i, endIndex = (mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies);
 
     // for each font family, load in various font info
-    AddOtherFamilyNameFunctor addOtherNames(this);
     for (i = mStartIndex; i < endIndex; i++) {
         gfxFontFamily* familyEntry = mFontFamiliesToLoad[i];
 
         // find all faces that are members of this family
         familyEntry->FindStyleVariations();
 
         // load the cmaps
         familyEntry->ReadCMAP();
 
-        // read in other family names
-        familyEntry->ReadOtherFamilyNames(addOtherNames);
+        // read in face names
+        familyEntry->ReadFaceNames(this, mNeedFullnamePostscriptNames);
 
         // check whether the family can be considered "simple" for style matching
         familyEntry->CheckForSimpleFamily();
     }
 
     mStartIndex += mIncrement;
     if (mStartIndex < mNumFamilies)
         return PR_FALSE;
--- a/gfx/thebes/src/gfxPlatformFontList.h
+++ b/gfx/thebes/src/gfxPlatformFontList.h
@@ -90,18 +90,26 @@ public:
 
     gfxFontFamily* FindFamily(const nsAString& aFamily);
 
     gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, PRBool& aNeedsBold);
 
     PRBool GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> > *array);
     void SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> >& array);
 
+    // name lookup table methods
+
     void AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName);
 
+    void AddFullname(gfxFontEntry *aFontEntry, nsAString& aFullname);
+
+    void AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName);
+
+    PRBool NeedFullnamePostscriptNames() { return mNeedFullnamePostscriptNames; }
+
     // pure virtual functions, to be provided by concrete subclasses
 
     // get the system default font
     virtual gfxFontEntry* GetDefaultFont(const gfxFontStyle* aStyle,
                                          PRBool& aNeedsBold) = 0;
 
     // look up a font by name on the host platform
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
@@ -113,96 +121,97 @@ public:
                                            const PRUint8 *aFontData,
                                            PRUint32 aLength) = 0;
 
     // get the standard family name on the platform for a given font name
     // (platforms may override, eg Mac)
     virtual PRBool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 
 protected:
-    gfxPlatformFontList();
+    gfxPlatformFontList(PRBool aNeedFullnamePostscriptNames = PR_TRUE);
 
     static gfxPlatformFontList *sPlatformFontList;
 
     static PLDHashOperator FindFontForCharProc(nsStringHashKey::KeyType aKey,
                                                nsRefPtr<gfxFontFamily>& aFamilyEntry,
                                                void* userArg);
 
-    // initialize font lists [pure virtual]
-    virtual void InitFontList() = 0;
-
-    // read secondary family names
-    void ReadOtherFamilyNamesForFamily(const nsAString& aFamilyName);
+    // initialize font lists
+    virtual void InitFontList();
 
     // separate initialization for reading in name tables, since this is expensive
     void InitOtherFamilyNames();
 
+    static PLDHashOperator InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
+                                                    nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                                                    void* userArg);
+
+    // read in all fullname/Postscript names for all font faces
+    void InitFaceNameLists();
+
+    static PLDHashOperator InitFaceNameListsProc(nsStringHashKey::KeyType aKey,
+                                                 nsRefPtr<gfxFontFamily>& aFamilyEntry,
+                                                 void* userArg);
+
     // commonly used fonts for which the name table should be loaded at startup
     virtual void PreloadNamesList();
 
-    // initialize the bad underline blacklist from pref.
-    virtual void InitBadUnderlineList();
+    // load the bad underline blacklist from pref.
+    void LoadBadUnderlineList();
 
     // explicitly set fixed-pitch flag for all faces
     void SetFixedPitch(const nsAString& aFamilyName);
 
-    static PLDHashOperator InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
-                                                    nsRefPtr<gfxFontFamily>& aFamilyEntry,
-                                                    void* userArg);
-
     void GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult);
 
     static PLDHashOperator
         HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey,
                                 nsRefPtr<gfxFontFamily>& aFamilyEntry,
                                 void* aUserArg);
 
     // gfxFontInfoLoader overrides, used to load in font cmaps
     virtual void InitLoader();
     virtual PRBool RunLoader();
     virtual void FinishLoader();
 
-    // canonical family name ==> family entry (unique, one name per family entry)
-    nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mFontFamilies;    
+      // canonical family name ==> family entry (unique, one name per family entry)
+    nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mFontFamilies;
+  
+    // flag set after InitOtherFamilyNames is called upon first name lookup miss
+    PRPackedBool mOtherFamilyNamesInitialized;
+
+    // other family name ==> family entry (not unique, can have multiple names per
+      // family entry, only names *other* than the canonical names are stored here)
+    nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mOtherFamilyNames;
 
-    // other family name ==> family entry (not unique, can have multiple names per 
-    // family entry, only names *other* than the canonical names are stored here)
-    nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mOtherFamilyNames;    
+    // flag set after fullname and Postcript name lists are populated
+    PRPackedBool mFaceNamesInitialized;
+
+    // whether these are needed for a given platform
+    PRPackedBool mNeedFullnamePostscriptNames;
+
+    // fullname ==> font entry (unique, one name per font entry)
+    nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> mFullnames;
+
+    // Postscript name ==> font entry (unique, one name per font entry)
+    nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> mPostscriptNames;
 
     // cached pref font lists
     // maps list of family names ==> array of family entries, one per lang group
     nsDataHashtable<nsUint32HashKey, nsTArray<nsRefPtr<gfxFontFamily> > > mPrefFonts;
 
     // when system-wide font lookup fails for a character, cache it to skip future searches
     gfxSparseBitSet mCodepointsWithNoFonts;
 
     // the family to use for U+FFFD fallback, to avoid expensive search every time
     // on pages with lots of problems
     nsString mReplacementCharFallbackFamily;
 
-    // flag set after InitOtherFamilyNames is called upon first name lookup miss
-    PRPackedBool mOtherFamilyNamesInitialized;
+    nsTHashtable<nsStringHashKey> mBadUnderlineFamilyNames;
 
     // data used as part of the font cmap loading process
     nsTArray<nsRefPtr<gfxFontFamily> > mFontFamiliesToLoad;
     PRUint32 mStartIndex;
     PRUint32 mIncrement;
     PRUint32 mNumFamilies;
 };
 
-
-// helper class for adding other family names back into font cache
-class AddOtherFamilyNameFunctor 
-{
-public:
-    AddOtherFamilyNameFunctor(gfxPlatformFontList *aFontList) :
-        mFontList(aFontList)
-    {}
-
-    void operator() (gfxFontFamily *aFamilyEntry, nsAString& aOtherName) {
-        mFontList->AddOtherFamilyName(aFamilyEntry, aOtherName);
-    }
-
-    gfxPlatformFontList *mFontList;
-};
-
-
 #endif /* GFXPLATFORMFONTLIST_H_ */
--- a/intl/uconv/src/charsetalias.properties
+++ b/intl/uconv/src/charsetalias.properties
@@ -292,16 +292,17 @@ utf8=UTF-8
 #
 # Aliases for Shift_JIS
 #
 x-sjis=Shift_JIS
 shift-jis=Shift_JIS
 ms_kanji=Shift_JIS
 csshiftjis=Shift_JIS
 windows-31j=Shift_JIS
+cp932=Shift_JIS
 #
 # Aliases for EUC_JP
 #
 cseucjpkdfmtjapanese=EUC-JP
 x-euc-jp=EUC-JP
 #
 # Aliases for ISO-2022-JP
 #
--- a/ipc/chromium/Makefile.in
+++ b/ipc/chromium/Makefile.in
@@ -241,24 +241,18 @@ ifdef OS_LINUX # {
 
 CPPSRCS += \
   atomicops_internals_x86_gcc.cc \
   base_paths_linux.cc \
   data_pack.cc \
   file_util_linux.cc \
   file_version_info_linux.cc \
   idle_timer_none.cc \
-  message_pump_glib.cc \
   process_util_linux.cc \
   time_posix.cc \
-  chrome_paths_linux.cc \
-  owned_widget_gtk.cc \
-  platform_util_linux.cc \
-  transport_dib_linux.cc \
-  x11_util.cc \
   $(NULL)
 
 endif # } OS_LINUX
 
 # libevent
 
 ifdef OS_POSIX # {
 
--- a/ipc/chromium/chromium-config.mk
+++ b/ipc/chromium/chromium-config.mk
@@ -101,16 +101,15 @@ OS_POSIX = 1
 DEFINES += \
   -DOS_LINUX=1 \
   -DOS_POSIX=1 \
   $(NULL)
 
 # NB: to stop gcc warnings about exporting template instantiation
 OS_CXXFLAGS := $(filter-out -pedantic,$(OS_CXXFLAGS))
 
-# TODO support !GTK
-OS_CXXFLAGS += $(MOZ_GTK2_CFLAGS)
-OS_CFLAGS += $(MOZ_GTK2_CFLAGS)
+OS_CXXFLAGS += $(TK_CFLAGS)
+OS_CFLAGS += $(TK_CFLAGS)
 
 endif # }
 endif # }
 
 endif # }
\ No newline at end of file
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -14,17 +14,17 @@
 #include "base/thread_local.h"
 
 #if defined(OS_MACOSX)
 #include "base/message_pump_mac.h"
 #endif
 #if defined(OS_POSIX)
 #include "base/message_pump_libevent.h"
 #endif
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) && !defined(CHROMIUM_MOZILLA_BUILD)
 #include "base/message_pump_glib.h"
 #endif
 
 #ifdef CHROMIUM_MOZILLA_BUILD
 #include "MessagePump.h"
 #endif
 
 using base::Time;
@@ -107,17 +107,17 @@ MessageLoop::MessageLoop(Type type)
   } else {
     DCHECK(type_ == TYPE_UI);
     pump_ = new base::MessagePumpForUI();
   }
 #elif defined(OS_POSIX)
   if (type_ == TYPE_UI) {
 #if defined(OS_MACOSX)
     pump_ = base::MessagePumpMac::Create();
-#elif defined(OS_LINUX)
+#elif defined(OS_LINUX) && !defined(CHROMIUM_MOZILLA_BUILD)
     pump_ = new base::MessagePumpForUI();
 #endif  // OS_LINUX
   } else if (type_ == TYPE_IO) {
     pump_ = new base::MessagePumpLibevent();
   } else {
     pump_ = new base::MessagePumpDefault();
   }
 #endif  // OS_POSIX
--- a/ipc/chromium/src/chrome/common/chrome_paths.cc
+++ b/ipc/chromium/src/chrome/common/chrome_paths.cc
@@ -27,16 +27,17 @@ bool GetGearsPluginPathFromCommandLine(F
   *path = FilePath::FromWStringHack(plugin_path);
   return !plugin_path.empty();
 #else
   return false;
 #endif
 }
 
 bool PathProvider(int key, FilePath* result) {
+#ifndef CHROMIUM_MOZILLA_BUILD
   // Some keys are just aliases...
   switch (key) {
     case chrome::DIR_APP:
       return PathService::Get(base::DIR_MODULE, result);
     case chrome::DIR_LOGS:
 #ifndef NDEBUG
       return PathService::Get(chrome::DIR_USER_DATA, result);
 #else
@@ -206,16 +207,17 @@ bool PathProvider(int key, FilePath* res
       return false;
   }
 
   if (create_dir && !file_util::PathExists(cur) &&
       !file_util::CreateDirectory(cur))
     return false;
 
   *result = cur;
+#endif
   return true;
 }
 
 // This cannot be done as a static initializer sadly since Visual Studio will
 // eliminate this object file if there is no direct entry point into it.
 void RegisterPathProvider() {
   PathService::RegisterProvider(PathProvider, PATH_START, PATH_END);
 }
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -99,8 +99,12 @@ include $(topsrcdir)/config/rules.mk
 export:: $(ALL_IPDLSRCS)
 	$(PYTHON) $(topsrcdir)/config/pythonpath.py	\
 	  -I$(topsrcdir)/other-licenses/ply		\
 	  $(srcdir)/ipdl.py				\
 	  --outheaders-dir=_ipdlheaders			\
 	  --outcpp-dir=.				\
 	  $(IPDLDIRS:%=-I$(topsrcdir)/%)		\
 	  $^
+
+# We #include some things in the dom/plugins/ directory that rely on
+# toolkit libraries.
+CXXFLAGS    += $(TK_CFLAGS)
--- a/js/src/find-child.py
+++ b/js/src/find-child.py
@@ -14,16 +14,17 @@
 # conversion's filemap) and connects them to the current tip of the
 # destination repo.
 #
 
 from mercurial import ui, hg
 from hgext.convert.filemap import filemapper
 from optparse import OptionParser
 
+import sys
 
 parser = OptionParser()
 
 parser.add_option("-s", "--src", dest="src",
                   help="source repository", metavar="REPO")
 
 parser.add_option("-d", "--dst", dest="dst",
                   help="destination repository", metavar="REPO")
@@ -58,8 +59,11 @@ while len(revs) != 0:
     tmp = revs
     revs = []
     for child in tmp:
         for f in child.files():
             if (not fm) or fm(f):
                 u.write("%s %s\n" % (child.hex(), dst_tip))
                 exit(0);
         revs.extend(child.children())
+
+sys.stderr.write("No candidate child found in source repository\n")
+exit(1)
--- a/js/src/imacros.c.out
+++ b/js/src/imacros.c.out
@@ -911,16 +911,17 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
     0,  /* JSOP_GETUPVAR_DBG */
     0,  /* JSOP_CALLUPVAR_DBG */
     0,  /* JSOP_DEFFUN_DBGFC */
     0,  /* JSOP_DEFLOCALFUN_DBGFC */
     0,  /* JSOP_LAMBDA_DBGFC */
     3,  /* JSOP_CONCATN */
     0,  /* JSOP_SETMETHOD */
     0,  /* JSOP_INITMETHOD */
+    0,  /* JSOP_UNBRAND */
     0,  /* JSOP_SHARPINIT */
 };
 #define JSOP_IS_IMACOP(x) (0 \
  || x == JSOP_BITOR \
  || x == JSOP_BITXOR \
  || x == JSOP_BITAND \
  || x == JSOP_EQ \
  || x == JSOP_NE \
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -93,16 +93,18 @@
 #if JS_HAS_FILE_OBJECT
 #include "jsfile.h"
 #endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
+using namespace js;
+
 #ifdef HAVE_VA_LIST_AS_ARRAY
 #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap))
 #else
 #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
 #endif
 
 #if defined(JS_THREADSAFE)
 #define CHECK_REQUEST(cx)                                                   \
@@ -329,17 +331,17 @@ JS_PushArgumentsVA(JSContext *cx, void *
         /*
          * Count non-space non-star characters as individual jsval arguments.
          * This may over-allocate stack, but we'll fix below.
          */
         if (isspace(c) || c == '*')
             continue;
         argc++;
     }
-    js_LeaveTrace(cx);
+    LeaveTrace(cx);
     sp = js_AllocStack(cx, argc, markp);
     if (!sp)
         return NULL;
     argv = sp;
     while ((c = *format++) != '\0') {
         if (isspace(c) || c == '*')
             continue;
         switch (c) {
@@ -964,17 +966,17 @@ JS_EndRequest(JSContext *cx)
 #ifdef JS_THREADSAFE
     JSRuntime *rt;
 
     CHECK_REQUEST(cx);
     JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
     JS_ASSERT(cx->requestDepth > 0);
     JS_ASSERT(cx->outstandingRequests > 0);
     if (cx->requestDepth == 1) {
-        js_LeaveTrace(cx);  /* for GC safety */
+        LeaveTrace(cx);  /* for GC safety */
 
         /* Lock before clearing to interlock with ClaimScope, in jslock.c. */
         rt = cx->runtime;
         JS_LOCK_GC(rt);
         cx->requestDepth = 0;
         cx->outstandingRequests--;
 
         js_ShareWaitingTitles(cx);
@@ -2041,17 +2043,17 @@ JS_SetExtraGCRoots(JSRuntime *rt, JSTrac
     rt->gcExtraRootsData = data;
 }
 
 JS_PUBLIC_API(void)
 JS_TraceRuntime(JSTracer *trc)
 {
     JSBool allAtoms = trc->context->runtime->gcKeepAtoms != 0;
 
-    js_LeaveTrace(trc->context);
+    LeaveTrace(trc->context);
     js_TraceRuntime(trc, allAtoms);
 }
 
 #ifdef DEBUG
 
 #ifdef HAVE_XPCONNECT
 #include "dump_xpc.h"
 #endif
@@ -2447,17 +2449,17 @@ extern JS_PUBLIC_API(JSBool)
 JS_IsGCMarkingTracer(JSTracer *trc)
 {
     return IS_GC_MARKING_TRACER(trc);
 }
 
 JS_PUBLIC_API(void)
 JS_GC(JSContext *cx)
 {
-    js_LeaveTrace(cx);
+    LeaveTrace(cx);
 
     /* Don't nuke active arenas if executing or compiling. */
     if (cx->stackPool.current == &cx->stackPool.first)
         JS_FinishArenaPool(&cx->stackPool);
     if (cx->tempPool.current == &cx->tempPool.first)
         JS_FinishArenaPool(&cx->tempPool);
     js_GC(cx, GC_NORMAL);
 }
@@ -2598,17 +2600,17 @@ JS_GetGCParameter(JSRuntime *rt, JSGCPar
     }
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32 value)
 {
     JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES);
 #ifdef JS_TRACER
-    js_SetMaxCodeCacheBytes(cx, value);
+    SetMaxCodeCacheBytes(cx, value);
 #endif
 }
 
 JS_PUBLIC_API(uint32)
 JS_GetGCParameterForThread(JSContext *cx, JSGCParamKey key)
 {
     JS_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES);
 #ifdef JS_TRACER
@@ -2617,17 +2619,17 @@ JS_GetGCParameterForThread(JSContext *cx
     return 0;
 #endif
 }
 
 JS_PUBLIC_API(void)
 JS_FlushCaches(JSContext *cx)
 {
 #ifdef JS_TRACER
-    js_FlushJITCache(cx);
+    FlushJITCache(cx);
 #endif
 }
 
 JS_PUBLIC_API(intN)
 JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer)
 {
     return js_ChangeExternalStringFinalizer(NULL, finalizer);
 }
@@ -5168,69 +5170,42 @@ JS_IsRunning(JSContext *cx)
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
 #ifdef JS_TRACER
     JS_ASSERT_IF(JS_TRACE_MONITOR(cx).tracecx == cx, cx->fp);
 #endif
     return cx->fp != NULL;
 }
 
+
+
 JS_PUBLIC_API(JSBool)
 JS_IsConstructing(JSContext *cx)
 {
-#ifdef JS_TRACER
-    if (JS_ON_TRACE(cx)) {
-        JS_ASSERT(cx->bailExit);
-        return *cx->bailExit->pc == JSOP_NEW;
-    }
-#endif
-
-    JSStackFrame *fp = js_GetTopStackFrame(cx);
-    return fp && (fp->flags & JSFRAME_CONSTRUCTING);
-}
-
-JS_FRIEND_API(JSBool)
-JS_IsAssigning(JSContext *cx)
-{
-    JSStackFrame *fp;
-
-    fp = js_GetScriptedCaller(cx, NULL);
-    if (!fp || !fp->regs)
-        return JS_FALSE;
-    return (js_CodeSpec[*fp->regs->pc].format & JOF_ASSIGNING) != 0;
+    return