Merge mozilla-central to tracemonkey.
authorRobert Sayre <sayrer@gmail.com>
Wed, 24 Nov 2010 14:00:42 -0800
changeset 58295 9123f97f059c4aba1af51ea6b657be1d21d45ea1
parent 58294 ad4b7fa4e68d9c607a810944c15da1e670b14786 (current diff)
parent 58184 b5aa5e3dc568443dc18e35f62af77e68d2d90bc1 (diff)
child 58296 1b24f8e54d1b9ea9d9f1b9a46e0b1eceafdfe649
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
milestone2.0b8pre
Merge mozilla-central to tracemonkey.
accessible/tests/mochitest/name.css
accessible/tests/mochitest/name.xbl
accessible/tests/mochitest/name_nsRootAcc_wnd.xul
accessible/tests/mochitest/namerules.xml
accessible/tests/mochitest/test_name.html
accessible/tests/mochitest/test_name.xul
accessible/tests/mochitest/test_name_button.html
accessible/tests/mochitest/test_name_link.html
accessible/tests/mochitest/test_name_markup.html
accessible/tests/mochitest/test_name_nsRootAcc.xul
content/media/test/test_timeupdate_seek.html
gfx/cairo/cairo/src/cairo-ddraw-private.h
gfx/cairo/cairo/src/cairo-ddraw-surface.c
gfx/harfbuzz/NEWS
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-ft.cc
gfx/thebes/gfxDDrawSurface.cpp
gfx/thebes/gfxDDrawSurface.h
js/src/jsapi.h
js/src/jstracer.cpp
layout/reftests/editor/spellcheck-1.html
layout/reftests/editor/spellcheck-ref.html
toolkit/themes/gnomestripe/mozapps/extensions/go-back.png
toolkit/themes/gnomestripe/mozapps/extensions/rating-unrated.png
toolkit/themes/gnomestripe/mozapps/extensions/utilities.png
toolkit/themes/gnomestripe/mozapps/extensions/warning-stripes.png
toolkit/themes/pinstripe/mozapps/extensions/go-back.png
toolkit/themes/pinstripe/mozapps/extensions/rating-unrated.png
toolkit/themes/pinstripe/mozapps/extensions/warning-stripes.png
toolkit/themes/winstripe/mozapps/extensions/go-back.png
toolkit/themes/winstripe/mozapps/extensions/rating-unrated.png
toolkit/themes/winstripe/mozapps/extensions/warning-stripes.png
--- a/accessible/src/base/AccIterator.cpp
+++ b/accessible/src/base/AccIterator.cpp
@@ -132,8 +132,120 @@ RelatedAccIterator::Next()
       nsAccessible* related = GetAccService()->GetAccessible(provider->mContent);
       if (related)
         return related;
     }
   }
 
   return nsnull;
 }
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLLabelIterator
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLLabelIterator::
+  HTMLLabelIterator(nsDocAccessible* aDocument, nsIContent* aElement,
+                    LabelFilter aFilter) :
+  mRelIter(aDocument, aElement, nsAccessibilityAtoms::_for),
+  mElement(aElement), mLabelFilter(aFilter)
+{
+}
+
+nsAccessible*
+HTMLLabelIterator::Next()
+{
+  // Get either <label for="[id]"> element which explicitly points to given
+  // element, or <label> ancestor which implicitly point to it.
+  nsAccessible* label = nsnull;
+  while ((label = mRelIter.Next())) {
+    if (label->GetContent()->Tag() == nsAccessibilityAtoms::label)
+      return label;
+  }
+
+  if (mLabelFilter == eSkipAncestorLabel)
+    return nsnull;
+
+  // Go up tree get name of ancestor label if there is one (an ancestor <label>
+  // implicitly points to us). Don't go up farther than form or body element.
+  nsIContent* walkUpContent = mElement;
+  while ((walkUpContent = walkUpContent->GetParent()) &&
+         walkUpContent->Tag() != nsAccessibilityAtoms::form &&
+         walkUpContent->Tag() != nsAccessibilityAtoms::body) {
+    if (walkUpContent->Tag() == nsAccessibilityAtoms::label) {
+      // Prevent infinite loop.
+      mLabelFilter = eSkipAncestorLabel;
+      return GetAccService()->GetAccessible(walkUpContent);
+    }
+  }
+
+  return nsnull;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLOutputIterator
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLOutputIterator::
+HTMLOutputIterator(nsDocAccessible* aDocument, nsIContent* aElement) :
+  mRelIter(aDocument, aElement, nsAccessibilityAtoms::_for)
+{
+}
+
+nsAccessible*
+HTMLOutputIterator::Next()
+{
+  nsAccessible* output = nsnull;
+  while ((output = mRelIter.Next())) {
+    if (output->GetContent()->Tag() == nsAccessibilityAtoms::output)
+      return output;
+  }
+
+  return nsnull;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// XULLabelIterator
+////////////////////////////////////////////////////////////////////////////////
+
+XULLabelIterator::
+  XULLabelIterator(nsDocAccessible* aDocument, nsIContent* aElement) :
+  mRelIter(aDocument, aElement, nsAccessibilityAtoms::control)
+{
+}
+
+nsAccessible*
+XULLabelIterator::Next()
+{
+  nsAccessible* label = nsnull;
+  while ((label = mRelIter.Next())) {
+    if (label->GetContent()->Tag() == nsAccessibilityAtoms::label)
+      return label;
+  }
+
+  return nsnull;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// XULDescriptionIterator
+////////////////////////////////////////////////////////////////////////////////
+
+XULDescriptionIterator::
+  XULDescriptionIterator(nsDocAccessible* aDocument, nsIContent* aElement) :
+  mRelIter(aDocument, aElement, nsAccessibilityAtoms::control)
+{
+}
+
+nsAccessible*
+XULDescriptionIterator::Next()
+{
+  nsAccessible* descr = nsnull;
+  while ((descr = mRelIter.Next())) {
+    if (descr->GetContent()->Tag() == nsAccessibilityAtoms::description)
+      return descr;
+  }
+
+  return nsnull;
+}
--- a/accessible/src/base/AccIterator.h
+++ b/accessible/src/base/AccIterator.h
@@ -126,9 +126,105 @@ private:
   RelatedAccIterator& operator = (const RelatedAccIterator&);
 
   nsIAtom* mRelAttr;
   nsDocAccessible::AttrRelProviderArray* mProviders;
   nsIContent* mBindingParent;
   PRUint32 mIndex;
 };
 
+
+/**
+ * Used to iterate through HTML labels associated with the given element.
+ */
+class HTMLLabelIterator
+{
+public:
+  enum LabelFilter {
+    eAllLabels,
+    eSkipAncestorLabel
+  };
+
+  HTMLLabelIterator(nsDocAccessible* aDocument, nsIContent* aElement,
+                    LabelFilter aFilter = eAllLabels);
+
+  /**
+   * Return next label accessible associated with the given element.
+   */
+  nsAccessible* Next();
+
+private:
+  HTMLLabelIterator();
+  HTMLLabelIterator(const HTMLLabelIterator&);
+  HTMLLabelIterator& operator = (const HTMLLabelIterator&);
+
+  RelatedAccIterator mRelIter;
+  nsIContent* mElement;
+  LabelFilter mLabelFilter;
+};
+
+
+/**
+ * Used to iterate through HTML outputs associated with the given element.
+ */
+class HTMLOutputIterator
+{
+public:
+  HTMLOutputIterator(nsDocAccessible* aDocument, nsIContent* aElement);
+
+  /**
+   * Return next output accessible associated with the given element.
+   */
+  nsAccessible* Next();
+
+private:
+  HTMLOutputIterator();
+  HTMLOutputIterator(const HTMLOutputIterator&);
+  HTMLOutputIterator& operator = (const HTMLOutputIterator&);
+
+  RelatedAccIterator mRelIter;
+};
+
+
+/**
+ * Used to iterate through XUL labels associated with the given element.
+ */
+class XULLabelIterator
+{
+public:
+  XULLabelIterator(nsDocAccessible* aDocument, nsIContent* aElement);
+
+  /**
+   * Return next label accessible associated with the given element.
+   */
+  nsAccessible* Next();
+
+private:
+  XULLabelIterator();
+  XULLabelIterator(const XULLabelIterator&);
+  XULLabelIterator& operator = (const XULLabelIterator&);
+
+  RelatedAccIterator mRelIter;
+};
+
+
+/**
+ * Used to iterate through XUL descriptions associated with the given element.
+ */
+class XULDescriptionIterator
+{
+public:
+  XULDescriptionIterator(nsDocAccessible* aDocument, nsIContent* aElement);
+
+  /**
+   * Return next description accessible associated with the given element.
+   */
+  nsAccessible* Next();
+
+private:
+  XULDescriptionIterator();
+  XULDescriptionIterator(const XULDescriptionIterator&);
+  XULDescriptionIterator& operator = (const XULDescriptionIterator&);
+
+  RelatedAccIterator mRelIter;
+};
+
 #endif
--- a/accessible/src/base/nsAccDocManager.cpp
+++ b/accessible/src/base/nsAccDocManager.cpp
@@ -209,16 +209,20 @@ nsAccDocManager::OnStateChange(nsIWebPro
       loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE) {
 
     // Fire reload event.
     nsRefPtr<AccEvent> reloadEvent =
       new AccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, docAcc);
     nsEventShell::FireEvent(reloadEvent);
   }
 
+  // Mark the document accessible as loading, if it stays alive then we'll mark
+  // it as loaded when we receive proper notification.
+  docAcc->MarkAsLoading();
+
   // Fire state busy change event. Use delayed event since we don't care
   // actually if event isn't delivered when the document goes away like a shot.
   nsRefPtr<AccEvent> stateEvent =
     new AccStateChangeEvent(document, nsIAccessibleStates::STATE_BUSY,
                             PR_FALSE, PR_TRUE);
   docAcc->FireDelayedAccessibleEvent(stateEvent);
 
   return NS_OK;
@@ -305,43 +309,41 @@ nsAccDocManager::HandleEvent(nsIDOMEvent
   }
 
   // XXX: handle error pages loading separately since they get neither
   // webprogress notifications nor 'pageshow' event.
   if (type.EqualsLiteral("DOMContentLoaded") &&
       nsCoreUtils::IsErrorPage(document)) {
     NS_LOG_ACCDOCLOAD2("handled 'DOMContentLoaded' event", document)
     HandleDOMDocumentLoad(document,
-                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE,
-                          PR_TRUE);
+                          nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
   }
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccDocManager private
 
 void
 nsAccDocManager::HandleDOMDocumentLoad(nsIDocument *aDocument,
-                                       PRUint32 aLoadEventType,
-                                       PRBool aMarkAsLoaded)
+                                       PRUint32 aLoadEventType)
 {
   // Document accessible can be created before we were notified the DOM document
   // was loaded completely. However if it's not created yet then create it.
   nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
   if (!docAcc) {
     docAcc = CreateDocOrRootAccessible(aDocument);
     NS_ASSERTION(docAcc, "Can't create document accessible!");
     if (!docAcc)
       return;
   }
 
-  if (aMarkAsLoaded)
-    docAcc->MarkAsLoaded();
+  // Mark the document as loaded to drop off the busy state flag on it.
+  docAcc->MarkAsLoaded();
 
   // Do not fire document complete/stop events for root chrome document
   // accessibles and for frame/iframe documents because
   // a) screen readers start working on focus event in the case of root chrome
   // documents
   // b) document load event on sub documents causes screen readers to act is if
   // entire page is reloaded.
   if (!IsEventTargetDocument(aDocument))
--- a/accessible/src/base/nsAccDocManager.h
+++ b/accessible/src/base/nsAccDocManager.h
@@ -111,23 +111,19 @@ private:
 private:
   /**
    * Create an accessible document if it was't created and fire accessibility
    * events if needed.
    *
    * @param  aDocument       [in] loaded DOM document
    * @param  aLoadEventType  [in] specifies the event type to fire load event,
    *                           if 0 then no event is fired
-   * @param  aMarkAsLoaded   [in] indicates whether we should mark forcedly
-   *                           an accessible document as loaded (used for error
-   *                           pages only which do not get 'pageshow' event)
    */
   void HandleDOMDocumentLoad(nsIDocument *aDocument,
-                             PRUint32 aLoadEventType,
-                             PRBool aMarkAsLoaded = PR_FALSE);
+                             PRUint32 aLoadEventType);
 
   /**
    * Return true if accessibility events accompanying document accessible
    * loading should be fired.
    *
    * The rules are: do not fire events for root chrome document accessibles and
    * for sub document accessibles (like HTML frame of iframe) of the loading
    * document accessible.
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -289,25 +289,21 @@ NS_IMETHODIMP nsAccessible::GetDescripti
       GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby,
                              description);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (description.IsEmpty()) {
       PRBool isXUL = mContent->IsXUL();
       if (isXUL) {
         // Try XUL <description control="[id]">description text</description>
-        nsIContent *descriptionContent =
-          nsCoreUtils::FindNeighbourPointingToNode(mContent,
-                                                   nsAccessibilityAtoms::control,
-                                                   nsAccessibilityAtoms::description);
-
-        if (descriptionContent) {
-          // We have a description content node
+        XULDescriptionIterator iter(GetDocAccessible(), mContent);
+        nsAccessible* descr = nsnull;
+        while ((descr = iter.Next())) {
           nsTextEquivUtils::
-            AppendTextEquivFromContent(this, descriptionContent, &description);
+            AppendTextEquivFromContent(this, descr->GetContent(), &description);
         }
       }
       if (description.IsEmpty()) {
         nsIAtom *descAtom = isXUL ? nsAccessibilityAtoms::tooltiptext :
                                     nsAccessibilityAtoms::title;
         if (mContent->GetAttr(kNameSpaceID_None, descAtom, description)) {
           nsAutoString name;
           GetName(name);
@@ -388,21 +384,33 @@ nsAccessible::GetKeyboardShortcut(nsAStr
 {
   aAccessKey.Truncate();
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   PRUint32 key = nsCoreUtils::GetAccessKeyFor(mContent);
   if (!key && mContent->IsElement()) {
-    // Copy access key from label node unless it is labeled
-    // via an ancestor <label>, in which case that would be redundant
-    nsCOMPtr<nsIContent> labelContent(nsCoreUtils::GetLabelContent(mContent));
-    if (labelContent && !nsCoreUtils::IsAncestorOf(labelContent, mContent))
-      key = nsCoreUtils::GetAccessKeyFor(labelContent);
+    nsAccessible* label = nsnull;
+
+    // Copy access key from label node.
+    if (mContent->IsHTML()) {
+      // Unless it is labeled via an ancestor <label>, in which case that would
+      // be redundant.
+      HTMLLabelIterator iter(GetDocAccessible(), mContent,
+                             HTMLLabelIterator::eSkipAncestorLabel);
+      label = iter.Next();
+
+    } else if (mContent->IsXUL()) {
+      XULLabelIterator iter(GetDocAccessible(), mContent);
+      label = iter.Next();
+    }
+
+    if (label)
+      key = nsCoreUtils::GetAccessKeyFor(label->GetContent());
   }
 
   if (!key)
     return NS_OK;
 
   nsAutoString accesskey(key);
 
   // Append the modifiers in reverse order, result: Control+Alt+Shift+Meta+<key>
@@ -1132,31 +1140,33 @@ nsAccessible::TakeFocus()
     fm->SetFocus(element, 0);
 
   return NS_OK;
 }
 
 nsresult
 nsAccessible::GetHTMLName(nsAString& aLabel)
 {
-  nsIContent *labelContent = nsCoreUtils::GetHTMLLabelContent(mContent);
-  if (labelContent) {
-    nsAutoString label;
-    nsresult rv =
-      nsTextEquivUtils::AppendTextEquivFromContent(this, labelContent, &label);
+  nsAutoString label;
+
+  nsAccessible* labelAcc = nsnull;
+  HTMLLabelIterator iter(GetDocAccessible(), mContent);
+  while ((labelAcc = iter.Next())) {
+    nsresult rv = nsTextEquivUtils::
+      AppendTextEquivFromContent(this, labelAcc->GetContent(), &label);
     NS_ENSURE_SUCCESS(rv, rv);
 
     label.CompressWhitespace();
-    if (!label.IsEmpty()) {
-      aLabel = label;
-      return NS_OK;
-    }
   }
 
-  return nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
+  if (label.IsEmpty())
+    return nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
+
+  aLabel = label;
+  return NS_OK;
 }
 
 /**
   * 3 main cases for XUL Controls to be labeled
   *   1 - control contains label="foo"
   *   2 - control has, as a child, a label element
   *        - label has either value="foo" or children
   *   3 - non-child label contains control="controlID"
@@ -1193,27 +1203,29 @@ nsAccessible::GetXULName(nsAString& aLab
         }
       }
     }
   }
 
   // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
   if (NS_FAILED(rv) || label.IsEmpty()) {
     label.Truncate();
-    nsIContent *labelContent =
-      nsCoreUtils::FindNeighbourPointingToNode(mContent,
-                                               nsAccessibilityAtoms::control,
-                                               nsAccessibilityAtoms::label);
-
-    nsCOMPtr<nsIDOMXULLabelElement> xulLabel(do_QueryInterface(labelContent));
-    // Check if label's value attribute is used
-    if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(label)) && label.IsEmpty()) {
-      // If no value attribute, a non-empty label must contain
-      // children that define its text -- possibly using HTML
-      nsTextEquivUtils::AppendTextEquivFromContent(this, labelContent, &label);
+
+    nsAccessible* labelAcc = nsnull;
+    XULLabelIterator iter(GetDocAccessible(), mContent);
+    while ((labelAcc = iter.Next())) {
+      nsCOMPtr<nsIDOMXULLabelElement> xulLabel =
+        do_QueryInterface(labelAcc->GetContent());
+      // Check if label's value attribute is used
+      if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(label)) && label.IsEmpty()) {
+        // If no value attribute, a non-empty label must contain
+        // children that define its text -- possibly using HTML
+        nsTextEquivUtils::
+          AppendTextEquivFromContent(this, labelAcc->GetContent(), &label);
+      }
     }
   }
 
   // XXX If CompressWhiteSpace worked on nsAString we could avoid a copy
   label.CompressWhitespace();
   if (!label.IsEmpty()) {
     aLabel = label;
     return NS_OK;
@@ -2063,38 +2075,53 @@ nsAccessible::GetRelationByType(PRUint32
 
   case nsIAccessibleRelation::RELATION_LABELLED_BY:
     {
       rv = nsRelUtils::
         AddTargetFromIDRefsAttr(aRelationType, aRelation, mContent,
                                 nsAccessibilityAtoms::aria_labelledby);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (rv != NS_OK_NO_RELATION_TARGET)
-        return NS_OK; // XXX bug 381599, avoid performance problems
-
-      return nsRelUtils::
-        AddTargetFromContent(aRelationType, aRelation,
-                             nsCoreUtils::GetLabelContent(mContent));
+      nsAccessible* label = nsnull;
+      if (mContent->IsHTML()) {
+        HTMLLabelIterator iter(GetDocAccessible(), mContent);
+        while ((label = iter.Next())) {
+          rv = nsRelUtils::AddTarget(aRelationType, aRelation, label);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+        return rv;
+      }
+
+      if (mContent->IsXUL()) {
+        XULLabelIterator iter(GetDocAccessible(), mContent);
+        while ((label = iter.Next())) {
+          rv = nsRelUtils::AddTarget(aRelationType, aRelation, label);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+      }
+      return rv;
     }
 
   case nsIAccessibleRelation::RELATION_DESCRIBED_BY:
     {
       rv = nsRelUtils::
         AddTargetFromIDRefsAttr(aRelationType, aRelation, mContent,
                                 nsAccessibilityAtoms::aria_describedby);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (rv != NS_OK_NO_RELATION_TARGET)
-        return NS_OK; // XXX bug 381599, avoid performance problems
-
-      return nsRelUtils::
-        AddTargetFromNeighbour(aRelationType, aRelation, mContent,
-                               nsAccessibilityAtoms::control,
-                               nsAccessibilityAtoms::description);
+      if (mContent->IsXUL()) {
+        XULDescriptionIterator iter(GetDocAccessible(), mContent);
+        nsAccessible* descr = nsnull;
+        while ((descr = iter.Next())) {
+          rv = nsRelUtils::AddTarget(aRelationType, aRelation, descr);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+      }
+
+      return rv;
     }
 
   case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR:
     {
       RelatedAccIterator iter(GetDocAccessible(), mContent,
                               nsAccessibilityAtoms::aria_describedby);
 
       nsAccessible* related = nsnull;
@@ -2180,23 +2207,24 @@ nsAccessible::GetRelationByType(PRUint32
 
   case nsIAccessibleRelation::RELATION_CONTROLLER_FOR:
     {
       nsresult rv = nsRelUtils::
         AddTargetFromIDRefsAttr(aRelationType, aRelation, mContent,
                                 nsAccessibilityAtoms::aria_controls);
       NS_ENSURE_SUCCESS(rv,rv);
 
-      if (rv != NS_OK_NO_RELATION_TARGET)
-        return NS_OK; // XXX bug 381599, avoid performance problems      
-
-      return nsRelUtils::
-        AddTargetFromNeighbour(aRelationType, aRelation, mContent,
-                               nsAccessibilityAtoms::_for,
-                               nsAccessibilityAtoms::output);
+      HTMLOutputIterator iter(GetDocAccessible(), mContent);
+      nsAccessible* related = nsnull;
+      while ((related = iter.Next())) {
+        rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      return rv;
     }
 
   case nsIAccessibleRelation::RELATION_FLOWS_TO:
     {
       return nsRelUtils::
         AddTargetFromIDRefsAttr(aRelationType, aRelation, mContent,
                                 nsAccessibilityAtoms::aria_flowto);
     }
--- a/accessible/src/base/nsCoreUtils.cpp
+++ b/accessible/src/base/nsCoreUtils.cpp
@@ -456,29 +456,16 @@ nsCoreUtils::GetDocShellTreeItemFor(nsIN
   nsIDocShellTreeItem *docShellTreeItem = nsnull;
   if (container)
     CallQueryInterface(container, &docShellTreeItem);
 
   return docShellTreeItem;
 }
 
 PRBool
-nsCoreUtils::IsDocumentBusy(nsIDocument *aDocument)
-{
-  nsCOMPtr<nsISupports> container = aDocument->GetContainer();
-  nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
-  if (!docShell)
-    return PR_TRUE;
-
-  PRUint32 busyFlags = 0;
-  docShell->GetBusyFlags(&busyFlags);
-  return (busyFlags != nsIDocShell::BUSY_FLAGS_NONE);
-}
-
-PRBool
 nsCoreUtils::IsRootDocument(nsIDocument *aDocument)
 {
   nsCOMPtr<nsISupports> container = aDocument->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
     do_QueryInterface(container);
   NS_ASSERTION(docShellTreeItem, "No document shell for document!");
 
   nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
@@ -581,284 +568,29 @@ nsCoreUtils::IsXLink(nsIContent *aConten
   if (!aContent)
     return PR_FALSE;
 
   return aContent->AttrValueIs(kNameSpaceID_XLink, nsAccessibilityAtoms::type,
                                nsAccessibilityAtoms::simple, eCaseMatters) &&
          aContent->HasAttr(kNameSpaceID_XLink, nsAccessibilityAtoms::href);
 }
 
-nsIContent*
-nsCoreUtils::FindNeighbourPointingToNode(nsIContent *aForNode, 
-                                         nsIAtom *aRelationAttr,
-                                         nsIAtom *aTagName,
-                                         PRUint32 aAncestorLevelsToSearch)
-{
-  return FindNeighbourPointingToNode(aForNode, &aRelationAttr, 1, aTagName, aAncestorLevelsToSearch);
-}
-
-nsIContent*
-nsCoreUtils::FindNeighbourPointingToNode(nsIContent *aForNode, 
-                                         nsIAtom **aRelationAttrs,
-                                         PRUint32 aAttrNum,
-                                         nsIAtom *aTagName,
-                                         PRUint32 aAncestorLevelsToSearch)
-{
-  nsAutoString controlID;
-  if (!nsCoreUtils::GetID(aForNode, controlID)) {
-    if (!aForNode->IsInAnonymousSubtree())
-      return nsnull;
-
-    aForNode->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::anonid, controlID);
-    if (controlID.IsEmpty())
-      return nsnull;
-  }
-
-  // Look for label in subtrees of nearby ancestors
-  nsCOMPtr<nsIContent> binding(aForNode->GetBindingParent());
-  PRUint32 count = 0;
-  nsIContent *labelContent = nsnull;
-  nsIContent *prevSearched = nsnull;
-
-  while (!labelContent && ++count <= aAncestorLevelsToSearch &&
-         (aForNode = aForNode->GetParent()) != nsnull) {
-
-    if (aForNode == binding) {
-      // When we reach the binding parent, make sure to check
-      // all of its anonymous child subtrees
-      nsCOMPtr<nsIDocument> doc = aForNode->GetCurrentDoc();
-      nsCOMPtr<nsIDOMDocumentXBL> xblDoc(do_QueryInterface(doc));
-      if (!xblDoc)
-        return nsnull;
-
-      nsCOMPtr<nsIDOMNodeList> nodes;
-      nsCOMPtr<nsIDOMElement> forElm(do_QueryInterface(aForNode));
-      xblDoc->GetAnonymousNodes(forElm, getter_AddRefs(nodes));
-      if (!nodes)
-        return nsnull;
-
-      PRUint32 length;
-      nsresult rv = nodes->GetLength(&length);
-      if (NS_FAILED(rv))
-        return nsnull;
-
-      for (PRUint32 index = 0; index < length && !labelContent; index++) {
-        nsCOMPtr<nsIDOMNode> node;
-        rv = nodes->Item(index, getter_AddRefs(node));
-        if (NS_FAILED(rv))
-          return nsnull;
-
-        nsCOMPtr<nsIContent> content = do_QueryInterface(node);
-        if (!content)
-          return nsnull;
-
-        if (content != prevSearched) {
-          labelContent = FindDescendantPointingToID(&controlID, content,
-                                                    aRelationAttrs, aAttrNum,
-                                                    nsnull, aTagName);
-        }
-      }
-      break;
-    }
-
-    labelContent = FindDescendantPointingToID(&controlID, aForNode,
-                                              aRelationAttrs, aAttrNum,
-                                              prevSearched, aTagName);
-    prevSearched = aForNode;
-  }
-
-  return labelContent;
-}
-
-// Pass in aAriaProperty = null and aRelationAttr == nsnull if any <label> will do
-nsIContent*
-nsCoreUtils::FindDescendantPointingToID(const nsString *aId,
-                                        nsIContent *aLookContent,
-                                        nsIAtom **aRelationAttrs,
-                                        PRUint32 aAttrNum,
-                                        nsIContent *aExcludeContent,
-                                        nsIAtom *aTagType)
-{
-  // Surround id with spaces for search
-  nsCAutoString idWithSpaces(' ');
-  LossyAppendUTF16toASCII(*aId, idWithSpaces);
-  idWithSpaces += ' ';
-  return FindDescendantPointingToIDImpl(idWithSpaces, aLookContent,
-                                        aRelationAttrs, aAttrNum,
-                                        aExcludeContent, aTagType);
-}
-
-nsIContent*
-nsCoreUtils::FindDescendantPointingToID(const nsString *aId,
-                                        nsIContent *aLookContent,
-                                        nsIAtom *aRelationAttr,
-                                        nsIContent *aExcludeContent,
-                                        nsIAtom *aTagType)
-{
-  return FindDescendantPointingToID(aId, aLookContent, &aRelationAttr, 1, aExcludeContent, aTagType);
-}
-
-nsIContent*
-nsCoreUtils::FindDescendantPointingToIDImpl(nsCString& aIdWithSpaces,
-                                            nsIContent *aLookContent,
-                                            nsIAtom **aRelationAttrs,
-                                            PRUint32 aAttrNum,
-                                            nsIContent *aExcludeContent,
-                                            nsIAtom *aTagType)
-{
-  NS_ENSURE_TRUE(aLookContent, nsnull);
-  NS_ENSURE_TRUE(aRelationAttrs && *aRelationAttrs, nsnull);
-
-  if (!aTagType || aLookContent->Tag() == aTagType) {
-    // Tag matches
-    // Check for ID in the attributes aRelationAttrs, which can be a list
-    for (PRUint32 i = 0; i < aAttrNum; i++) {
-      nsAutoString idList;
-      if (aLookContent->GetAttr(kNameSpaceID_None, aRelationAttrs[i], idList)) {
-        idList.Insert(' ', 0);  // Surround idlist with spaces for search
-        idList.Append(' ');
-        // idList is now a set of id's with spaces around each,
-        // and id also has spaces around it.
-        // If id is a substring of idList then we have a match
-        if (idList.Find(aIdWithSpaces) != -1) {
-          return aLookContent;
-        }
-      }
-    }
-    if (aTagType) {
-      // Don't bother to search descendants of an element with matching tag.
-      // That would be like looking for a nested <label> or <description>
-      return nsnull;
-    }
-  }
-
-  // Recursively search descendants for match
-  PRUint32 count  = 0;
-  nsIContent *child;
-  nsIContent *labelContent = nsnull;
-
-  while ((child = aLookContent->GetChildAt(count++)) != nsnull) {
-    if (child != aExcludeContent) {
-      labelContent = FindDescendantPointingToIDImpl(aIdWithSpaces, child,
-                                                    aRelationAttrs, aAttrNum,
-                                                    aExcludeContent, aTagType);
-      if (labelContent) {
-        return labelContent;
-      }
-    }
-  }
-  return nsnull;
-}
-
-nsIContent*
-nsCoreUtils::GetLabelContent(nsIContent *aForNode)
-{
-  if (aForNode->IsXUL())
-    return FindNeighbourPointingToNode(aForNode, nsAccessibilityAtoms::control,
-                                       nsAccessibilityAtoms::label);
-
-  return GetHTMLLabelContent(aForNode);
-}
-
-nsIContent*
-nsCoreUtils::GetHTMLLabelContent(nsIContent *aForNode)
-{
-  // Get either <label for="[id]"> element which explictly points to aForNode,
-  // or <label> ancestor which implicitly point to it.
-  nsIContent *walkUpContent = aForNode;
-  
-  // Go up tree get name of ancestor label if there is one. Don't go up farther
-  // than form element.
-  while ((walkUpContent = walkUpContent->GetParent()) != nsnull) {
-    nsIAtom *tag = walkUpContent->Tag();
-    if (tag == nsAccessibilityAtoms::label)
-      return walkUpContent;  // An ancestor <label> implicitly points to us
-
-    if (tag == nsAccessibilityAtoms::form ||
-        tag == nsAccessibilityAtoms::body) {
-      // Reached top ancestor in form
-      // There can be a label targeted at this control using the 
-      // for="control_id" attribute. To save computing time, only 
-      // look for those inside of a form element
-      nsAutoString forId;
-      if (!GetID(aForNode, forId))
-        break;
-
-      // Actually we'll be walking down the content this time, with a depth first search
-      return FindDescendantPointingToID(&forId, walkUpContent,
-                                        nsAccessibilityAtoms::_for);
-    }
-  }
-
-  return nsnull;
-}
-
 void
 nsCoreUtils::GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
                             nsAString& aLanguage)
 {
   aLanguage.Truncate();
 
   nsIContent *walkUp = aContent;
   while (walkUp && walkUp != aRootContent &&
          !walkUp->GetAttr(kNameSpaceID_None,
                           nsAccessibilityAtoms::lang, aLanguage))
     walkUp = walkUp->GetParent();
 }
 
-void
-nsCoreUtils::GetElementsHavingIDRefsAttr(nsIContent *aRootContent,
-                                         nsIContent *aContent,
-                                         nsIAtom *aIDRefsAttr,
-                                         nsIArray **aElements)
-{
-  *aElements = nsnull;
-
-  nsAutoString id;
-  if (!GetID(aContent, id))
-    return;
-
-  nsCAutoString idWithSpaces(' ');
-  LossyAppendUTF16toASCII(id, idWithSpaces);
-  idWithSpaces += ' ';
-
-  nsCOMPtr<nsIMutableArray> elms = do_CreateInstance(NS_ARRAY_CONTRACTID);
-  if (!elms)
-    return;
-
-  GetElementsHavingIDRefsAttrImpl(aRootContent, idWithSpaces, aIDRefsAttr,
-                                  elms);
-  NS_ADDREF(*aElements = elms);
-}
-
-void
-nsCoreUtils::GetElementsHavingIDRefsAttrImpl(nsIContent *aRootContent,
-                                             nsCString& aIdWithSpaces,
-                                             nsIAtom *aIDRefsAttr,
-                                             nsIMutableArray *aElements)
-{
-  PRUint32 childCount = aRootContent->GetChildCount();
-  for (PRUint32 index = 0; index < childCount; index++) {
-    nsIContent* child = aRootContent->GetChildAt(index);
-    nsAutoString idList;
-    if (child->GetAttr(kNameSpaceID_None, aIDRefsAttr, idList)) {
-      idList.Insert(' ', 0);  // Surround idlist with spaces for search
-      idList.Append(' ');
-      // idList is now a set of id's with spaces around each, and id also has
-      // spaces around it. If id is a substring of idList then we have a match.
-      if (idList.Find(aIdWithSpaces) != -1) {
-        aElements->AppendElement(child, PR_FALSE);
-        continue; // Do not search inside children.
-      }
-    }
-    GetElementsHavingIDRefsAttrImpl(child, aIdWithSpaces,
-                                    aIDRefsAttr, aElements);
-  }
-}
-
 already_AddRefed<nsIDOMCSSStyleDeclaration>
 nsCoreUtils::GetComputedStyleDeclaration(const nsAString& aPseudoElt,
                                          nsIContent *aContent)
 {
   nsIContent* content = GetDOMElementFor(aContent);
   if (!content)
     return nsnull;
 
--- a/accessible/src/base/nsCoreUtils.h
+++ b/accessible/src/base/nsCoreUtils.h
@@ -219,21 +219,16 @@ public:
 
   /**
    * Return document shell tree item for the given DOM node.
    */
   static already_AddRefed<nsIDocShellTreeItem>
     GetDocShellTreeItemFor(nsINode *aNode);
 
   /**
-   * Return true if document is loading.
-   */
-  static PRBool IsDocumentBusy(nsIDocument *aDocument);
-
-  /**
    * Return true if the given document is root document.
    */
   static PRBool IsRootDocument(nsIDocument *aDocument);
 
   /**
    * Return true if the given document is content document (not chrome).
    */
   static PRBool IsContentDocument(nsIDocument *aDocument);
@@ -301,123 +296,23 @@ public:
    * @param aContent     [in] the given node
    * @param aRootContent [in] container of the given node
    * @param aLanguage    [out] language
    */
   static void GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
                              nsAString& aLanguage);
 
   /**
-   * Return the array of elements having IDRefs that points to the given node.
-   *
-   * @param  aRootContent  [in] root element to search inside
-   * @param  aContent      [in] an element having ID attribute
-   * @param  aIDRefsAttr   [in] IDRefs attribute
-   * @param  aElements     [out] result array of elements
-   */
-  static void GetElementsHavingIDRefsAttr(nsIContent *aRootContent,
-                                          nsIContent *aContent,
-                                          nsIAtom *aIDRefsAttr,
-                                          nsIArray **aElements);
-
-  /**
-   * Helper method for GetElementsHavingIDRefsAttr.
-   */
-  static void GetElementsHavingIDRefsAttrImpl(nsIContent *aRootContent,
-                                              nsCString& aIdWithSpaces,
-                                              nsIAtom *aIDRefsAttr,
-                                              nsIMutableArray *aElements);
-
-  /**
    * Return computed styles declaration for the given node.
    */
   static already_AddRefed<nsIDOMCSSStyleDeclaration>
     GetComputedStyleDeclaration(const nsAString& aPseudoElt,
                                 nsIContent *aContent);
 
   /**
-   * Search element in neighborhood of the given element by tag name and
-   * attribute value that equals to ID attribute of the given element.
-   * ID attribute can be either 'id' attribute or 'anonid' if the element is
-   * anonymous.
-   * The first matched content will be returned.
-   *
-   * @param aForNode - the given element the search is performed for
-   * @param aRelationAttrs - an array of attributes, element is attribute name of searched element, ignored if aAriaProperty passed in
-   * @param aAttrNum - how many attributes in aRelationAttrs
-   * @param aTagName - tag name of searched element, or nsnull for any -- ignored if aAriaProperty passed in
-   * @param aAncestorLevelsToSearch - points how is the neighborhood of the
-   *                                  given element big.
-   */
-  static nsIContent *FindNeighbourPointingToNode(nsIContent *aForNode,
-                                                 nsIAtom **aRelationAttrs, 
-                                                 PRUint32 aAttrNum,
-                                                 nsIAtom *aTagName = nsnull,
-                                                 PRUint32 aAncestorLevelsToSearch = 5);
-
-  /**
-   * Overloaded version of FindNeighbourPointingToNode to accept only one
-   * relation attribute.
-   */
-  static nsIContent *FindNeighbourPointingToNode(nsIContent *aForNode,
-                                                 nsIAtom *aRelationAttr, 
-                                                 nsIAtom *aTagName = nsnull,
-                                                 PRUint32 aAncestorLevelsToSearch = 5);
-
-  /**
-   * Search for element that satisfies the requirements in subtree of the given
-   * element. The requirements are tag name, attribute name and value of
-   * attribute.
-   * The first matched content will be returned.
-   *
-   * @param aId - value of searched attribute
-   * @param aLookContent - element that search is performed inside
-   * @param aRelationAttrs - an array of searched attributes
-   * @param aAttrNum - how many attributes in aRelationAttrs
-   * @param                 if both aAriaProperty and aRelationAttrs are null, then any element with aTagType will do
-   * @param aExcludeContent - element that is skiped for search
-   * @param aTagType - tag name of searched element, by default it is 'label' --
-   *                   ignored if aAriaProperty passed in
-   */
-  static nsIContent *FindDescendantPointingToID(const nsString *aId,
-                                                nsIContent *aLookContent,
-                                                nsIAtom **aRelationAttrs,
-                                                PRUint32 aAttrNum = 1,
-                                                nsIContent *aExcludeContent = nsnull,
-                                                nsIAtom *aTagType = nsAccessibilityAtoms::label);
-
-  /**
-   * Overloaded version of FindDescendantPointingToID to accept only one
-   * relation attribute.
-   */
-  static nsIContent *FindDescendantPointingToID(const nsString *aId,
-                                                nsIContent *aLookContent,
-                                                nsIAtom *aRelationAttr,
-                                                nsIContent *aExcludeContent = nsnull,
-                                                nsIAtom *aTagType = nsAccessibilityAtoms::label);
-
-  // Helper for FindDescendantPointingToID(), same args
-  static nsIContent *FindDescendantPointingToIDImpl(nsCString& aIdWithSpaces,
-                                                    nsIContent *aLookContent,
-                                                    nsIAtom **aRelationAttrs,
-                                                    PRUint32 aAttrNum = 1,
-                                                    nsIContent *aExcludeContent = nsnull,
-                                                    nsIAtom *aTagType = nsAccessibilityAtoms::label);
-
-  /**
-   * Return the label element for the given DOM element.
-   */
-  static nsIContent *GetLabelContent(nsIContent *aForNode);
-
-  /**
-   * Return the HTML label element for the given HTML element.
-   */
-  static nsIContent *GetHTMLLabelContent(nsIContent *aForNode);
-
-  /**
    * Return box object for XUL treechildren element by tree box object.
    */
   static already_AddRefed<nsIBoxObject>
     GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj);
 
   /**
    * Return tree box object from any levels DOMNode under the XUL tree.
    */
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -86,17 +86,19 @@ namespace dom = mozilla::dom;
 PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0;
 
 static nsIAtom** kRelationAttrs[] =
 {
   &nsAccessibilityAtoms::aria_labelledby,
   &nsAccessibilityAtoms::aria_describedby,
   &nsAccessibilityAtoms::aria_owns,
   &nsAccessibilityAtoms::aria_controls,
-  &nsAccessibilityAtoms::aria_flowto
+  &nsAccessibilityAtoms::aria_flowto,
+  &nsAccessibilityAtoms::_for,
+  &nsAccessibilityAtoms::control
 };
 
 static const PRUint32 kRelationAttrsLen = NS_ARRAY_LENGTH(kRelationAttrs);
 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/desctructor
 
 nsDocAccessible::
@@ -312,17 +314,17 @@ nsDocAccessible::GetStateInternal(PRUint
     // XXX Need to invent better check to see if doc is focusable,
     // which it should be if it is scrollable. A XUL document could be focusable.
     // See bug 376803.
     *aState |= nsIAccessibleStates::STATE_FOCUSABLE;
     if (gLastFocusedNode == mDocument)
       *aState |= nsIAccessibleStates::STATE_FOCUSED;
   }
 
-  if (nsCoreUtils::IsDocumentBusy(mDocument)) {
+  if (!mIsLoaded) {
     *aState |= nsIAccessibleStates::STATE_BUSY;
     if (aExtraState) {
       *aExtraState |= nsIAccessibleStates::EXT_STATE_STALE;
     }
   }
  
   nsIFrame* frame = GetFrame();
   while (frame != nsnull && !frame->HasView()) {
@@ -937,17 +939,17 @@ NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(ns
 NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsDocAccessible)
 
 void
 nsDocAccessible::AttributeWillChange(nsIDocument *aDocument,
                                      dom::Element* aElement,
                                      PRInt32 aNameSpaceID,
                                      nsIAtom* aAttribute, PRInt32 aModType)
 {
-  // XXX TODO: bugs 381599 (partially fixed by 573469), 467143, 472142, 472143.
+  // XXX TODO: bugs 467143, 472142, 472143.
   // Here we will want to cache whatever state we are potentially interested in,
   // such as the existence of aria-pressed for button (so we know if we need to
   // newly expose it as a toggle button) etc.
 
   // Update dependent IDs cache.
   if (aModType == nsIDOMMutationEvent::MODIFICATION ||
       aModType == nsIDOMMutationEvent::REMOVAL) {
     nsAccessible* accessible =
@@ -1396,30 +1398,32 @@ nsDocAccessible::BindToDocument(nsAccess
   aAccessible->SetRoleMapEntry(aRoleMapEntry);
   AddDependentIDsFor(aAccessible);
   return true;
 }
 
 void
 nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
 {
+  NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
+               "Unbinding the unbound accessible!");
+
   // Remove an accessible from node-to-accessible map if it exists there.
   if (aAccessible->IsPrimaryForNode() &&
       mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
     mNodeToAccessibleMap.Remove(aAccessible->GetNode());
 
-  RemoveDependentIDsFor(aAccessible);
-
-#ifdef DEBUG
-  NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
-               "Unbinding the unbound accessible!");
-#endif
+  if (!aAccessible->IsDefunct())
+    RemoveDependentIDsFor(aAccessible);
 
   void* uniqueID = aAccessible->UniqueID();
+
+  NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
   aAccessible->Shutdown();
+
   mAccessibleCache.Remove(uniqueID);
 }
 
 void
 nsDocAccessible::UpdateTree(nsIContent* aContainerNode,
                             nsIContent* aStartNode,
                             nsIContent* aEndNode,
                             PRBool aIsInsert)
@@ -1444,26 +1448,24 @@ nsDocAccessible::UpdateTree(nsIContent* 
   nsAccessible* container = nsnull;
   if (aIsInsert) {
     container = aContainerNode ?
       GetAccService()->GetAccessibleOrContainer(aContainerNode, mWeakShell) :
       this;
 
     // The document children were changed; the root content might be affected.
     if (container == this) {
+      // If new root content has been inserted then update it.
       nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocument);
+      if (rootContent && rootContent != mContent)
+        mContent = rootContent;
 
-      // No root content (for example HTML document element was inserted but no
-      // body). Nothing to update.
-      if (!rootContent)
-        return;
-
-      // New root content has been inserted, update it and update the tree.
-      if (rootContent != mContent)
-        mContent = rootContent;
+      // Continue to update the tree even if we don't have root content.
+      // For example, elements may be inserted under the document element while
+      // there is no HTML body element.
     }
 
     // XXX: Invalidate parent-child relations for container accessible and its
     // children because there's no good way to find insertion point of new child
     // accessibles into accessible tree. We need to invalidate children even
     // there's no inserted accessibles in the end because accessible children
     // are created while parent recaches child accessibles.
     container->InvalidateChildren();
@@ -1618,27 +1620,55 @@ nsDocAccessible::NotifyOfCachingEnd(nsAc
     mInvalidationList.Clear();
 
     mCacheRoot = nsnull;
     mIsPostCacheProcessing = PR_FALSE;
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// nsAccessible protected
+
+void
+nsDocAccessible::CacheChildren()
+{
+  // Search for accessible children starting from the document element since
+  // some web pages tend to insert elements under it rather than document body.
+  nsAccTreeWalker walker(mWeakShell, mDocument->GetRootElement(),
+                         GetAllowsAnonChildAccessibles());
+
+  nsRefPtr<nsAccessible> child;
+  while ((child = walker.GetNextChild()) && AppendChild(child));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
 void
 nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider,
                                     nsIAtom* aRelAttr)
 {
   for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
     nsIAtom* relAttr = *kRelationAttrs[idx];
     if (aRelAttr && aRelAttr != relAttr)
       continue;
 
+    if (relAttr == nsAccessibilityAtoms::_for) {
+      if (!aRelProvider->GetContent()->IsHTML() ||
+          aRelProvider->GetContent()->Tag() != nsAccessibilityAtoms::label &&
+          aRelProvider->GetContent()->Tag() != nsAccessibilityAtoms::output)
+        continue;
+
+    } else if (relAttr == nsAccessibilityAtoms::control) {
+      if (!aRelProvider->GetContent()->IsXUL() ||
+          aRelProvider->GetContent()->Tag() != nsAccessibilityAtoms::label &&
+          aRelProvider->GetContent()->Tag() != nsAccessibilityAtoms::description)
+        continue;
+    }
+
     IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
     while (true) {
       const nsDependentSubstring id = iter.NextID();
       if (id.IsEmpty())
         break;
 
       AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
       if (!providers) {
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -133,21 +133,24 @@ public:
    */
   PRBool IsContentLoaded() const
   {
     return mDocument && mDocument->IsVisible() &&
       (mDocument->IsShowing() || mIsLoaded);
   }
 
   /**
-   * Marks as loaded, used for error pages as workaround since they do not
+   * Marks this document as loaded or loading, used to expose busy state.
+   * The loaded flag has special meaning for error pages and used as workaround
+   * to make IsContentLoaded() return correct result since these pages do not
    * receive pageshow event and as consequence nsIDocument::IsShowing() returns
    * false.
    */
   void MarkAsLoaded() { mIsLoaded = PR_TRUE; }
+  void MarkAsLoading() { mIsLoaded = PR_FALSE; }
 
   /**
    * Return a native window handler or pointer depending on platform.
    */
   virtual void* GetNativeWindow() const;
 
   /**
    * Return the parent document.
@@ -261,16 +264,20 @@ public:
    * for referred content by related accessible. Keep the caching root and
    * these related nodes to invalidate their containers after root caching.
    */
   void NotifyOfCachingStart(nsAccessible* aAccessible);
   void NotifyOfCachingEnd(nsAccessible* aAccessible);
 
 protected:
 
+  // nsAccessible
+  virtual void CacheChildren();
+
+  // nsDocAccessible
     virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
     virtual nsresult AddEventListeners();
     virtual nsresult RemoveEventListeners();
     void AddScrollListener();
     void RemoveScrollListener();
 
   /**
    * Append the given document accessible to this document's child document
@@ -410,17 +417,17 @@ protected:
    */
   class AttrRelProvider
   {
   public:
     AttrRelProvider(nsIAtom* aRelAttr, nsIContent* aContent) :
       mRelAttr(aRelAttr), mContent(aContent) { }
 
     nsIAtom* mRelAttr;
-    nsIContent* mContent;
+    nsCOMPtr<nsIContent> mContent;
 
   private:
     AttrRelProvider();
     AttrRelProvider(const AttrRelProvider&);
     AttrRelProvider& operator =(const AttrRelProvider&);
   };
 
   /**
--- a/accessible/src/base/nsRelUtils.cpp
+++ b/accessible/src/base/nsRelUtils.cpp
@@ -140,51 +140,8 @@ nsRelUtils::AddTargetFromIDRefsAttr(PRUi
   IDRefsIterator iter(aContent, aAttr);
   while ((refElm = iter.NextElem())) {
     rv = AddTargetFromContent(aRelationType, aRelation, refElm);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return rv;
 }
-
-nsresult
-nsRelUtils::AddTargetFromNeighbour(PRUint32 aRelationType,
-                                   nsIAccessibleRelation **aRelation,
-                                   nsIContent *aContent,
-                                   nsIAtom *aNeighboutAttr,
-                                   nsIAtom *aNeighboutTagName)
-{
-  return AddTargetFromContent(
-    aRelationType, aRelation,
-    nsCoreUtils::FindNeighbourPointingToNode(aContent, aNeighboutAttr,
-                                             aNeighboutTagName));
-}
-
-nsresult
-nsRelUtils::AddTargetFromChildrenHavingIDRefsAttr(PRUint32 aRelationType,
-                                                  nsIAccessibleRelation **aRelation,
-                                                  nsIContent *aRootContent,
-                                                  nsIContent *aContent,
-                                                  nsIAtom *aIDRefsAttr)
-{
-  nsCOMPtr<nsIArray> elms;
-  nsCoreUtils::GetElementsHavingIDRefsAttr(aRootContent, aContent, aIDRefsAttr,
-                                           getter_AddRefs(elms));
-  if (!elms)
-    return NS_OK_NO_RELATION_TARGET;
-
-  PRUint32 count = 0;
-  nsresult rv = elms->GetLength(&count);
-  if (NS_FAILED(rv) || count == 0)
-    return NS_OK_NO_RELATION_TARGET;
-
-  nsCOMPtr<nsIContent> content;
-  for (PRUint32 idx = 0; idx < count; idx++) {
-    content = do_QueryElementAt(elms, idx, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = AddTargetFromContent(aRelationType, aRelation, content);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return NS_OK;
-}
--- a/accessible/src/base/nsRelUtils.h
+++ b/accessible/src/base/nsRelUtils.h
@@ -115,52 +115,16 @@ public:
    * @param  aContent       [in] node having the given IDRefs attribute
    * @param  aAttr          [in] IDRefs attribute
    */
   static nsresult AddTargetFromIDRefsAttr(PRUint32 aRelationType,
                                           nsIAccessibleRelation **aRelation,
                                           nsIContent *aContent, nsIAtom *aAttr);
 
   /**
-   * Create the relation if the given relation is null and add the target to it
-   * found in neighbour tree.
-   *
-   * @param  aRelationType      [in] relation type
-   * @param  aRelation          [in, out] relation object
-   * @param  aContent           [in] node defining neighbour tree
-   * @param  aNeighboutAttr     [in] IDRef attribute of the node in neighbour
-   *                            tree pointing to node defining neighbour tree
-   * @param  aNeighboutTagName  [in, optional] tag name of the node in neighbour
-   *                            tree having IDRef attribute pointed by previous
-   *                            argument
-   */
-  static nsresult AddTargetFromNeighbour(PRUint32 aRelationType,
-                                         nsIAccessibleRelation **aRelation,
-                                         nsIContent *aContent,
-                                         nsIAtom *aNeighboutAttr,
-                                         nsIAtom *aNeighboutTagName = nsnull);
-
-  /**
-   * Create the relation if the given relation is null and add the targets to it
-   * that have IDRefs attribute pointing to the given node.
-   *
-   * @param  aRelationType  [in] relation type
-   * @param  aRelation      [in, out] relation object
-   * @param  aRootContent   [in] root node we search inside of
-   * @param  aContent       [in] node having ID
-   * @param  aIDRefsAttr    [in] IDRefs attribute
-   */
-  static nsresult
-    AddTargetFromChildrenHavingIDRefsAttr(PRUint32 aRelationType,
-                                          nsIAccessibleRelation **aRelation,
-                                          nsIContent *aRootContent,
-                                          nsIContent *aContent,
-                                          nsIAtom *aIDRefsAttr);
-
-  /**
    * Query nsAccessibleRelation from the given nsIAccessibleRelation.
    */
   static already_AddRefed<nsAccessibleRelation>
   QueryAccRelation(nsIAccessibleRelation *aRelation)
   {
     nsAccessibleRelation* relation = nsnull;
     if (aRelation)
       CallQueryInterface(aRelation, &relation);
--- a/accessible/src/base/nsTextEquivUtils.cpp
+++ b/accessible/src/base/nsTextEquivUtils.cpp
@@ -411,17 +411,17 @@ nsTextEquivUtils::IsWhitespace(PRUnichar
     aChar == '\r' || aChar == '\t' || aChar == 0xa0;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Name rules to role map.
 
 PRUint32 nsTextEquivUtils::gRoleToNameRulesMap[] =
 {
-  eNoRule,           // ROLE_NOTHING
+  eFromSubtreeIfRec, // ROLE_NOTHING
   eNoRule,           // ROLE_TITLEBAR
   eNoRule,           // ROLE_MENUBAR
   eNoRule,           // ROLE_SCROLLBAR
   eNoRule,           // ROLE_GRIP
   eNoRule,           // ROLE_SOUND
   eNoRule,           // ROLE_CURSOR
   eNoRule,           // ROLE_CARET
   eNoRule,           // ROLE_ALERT
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -42,16 +42,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible
 
 DIRS	= \
   actions \
   attributes \
   events \
   hyperlink \
+  name \
   relations \
   selectable \
   states \
   table \
   text \
   tree \
   treeupdate \
   $(null)
@@ -67,22 +68,18 @@ include $(topsrcdir)/config/rules.mk
 		longdesc_src.html \
 		actions.js \
 		attributes.js \
 		common.js \
 		editabletext.js \
 		events.js \
 		grid.js \
 		layout.js \
-		name.css \
 		name.js \
-		name.xbl \
-		name_nsRootAcc_wnd.xul \
-		namerules.xml \
- 		nsIAccessible_selects.js \
+		nsIAccessible_selects.js \
 		relations.js \
 		role.js \
 		selectable.js \
 		states.js \
 		table.js \
 		value.js \
 		test_aria_activedescendant.html \
 		test_aria_role_article.html \
@@ -96,22 +93,17 @@ include $(topsrcdir)/config/rules.mk
 		test_descr.html \
 		test_editabletext_1.html \
 		test_editabletext_2.html \
 		test_elm_landmarks.html \
 		test_elm_listbox.xul \
 	$(warning   test_elm_media.html temporarily disabled) \
 		test_elm_nsApplicationAcc.html \
 		test_elm_plugin.html \
-		test_name.html \
-		test_name.xul \
-		test_name_button.html \
-		test_name_link.html \
-		test_name_markup.html \
-		test_name_nsRootAcc.xul \
+		test_keys.html \
 	$(warning test_nsIAccessible_comboboxes.xul temporarily disabled) \
  		test_nsIAccessible_selects.html \
 		test_nsIAccessible_focus.html \
 		test_nsIAccessibleDocument.html \
 		test_nsIAccessibleHyperText.html \
 		test_nsIAccessibleImage.html \
 		test_nsIAccessNode_utils.html \
 		test_nsOuterDocAccessible.html \
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -46,16 +46,17 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		docload_wnd.html \
 		docload_wnd.xul \
 		focus.html \
 		scroll.html \
 		test_aria_alert.html \
+		test_aria_menu.html \
 		test_aria_statechange.html \
 		test_attrs.html \
 		test_caretmove.html \
 		test_coalescence.html \
 		test_contextmenu.html \
 		test_docload.html \
 		test_docload.xul \
 		test_dragndrop.html \
--- a/accessible/tests/mochitest/events/docload_wnd.xul
+++ b/accessible/tests/mochitest/events/docload_wnd.xul
@@ -23,16 +23,23 @@
     function ok(aCond, aMsg) {
       gOpenerWnd.SimpleTest.ok(aCond, aMsg);
     }
 
     function is(aExpected, aActual, aMsg) {
       gOpenerWnd.SimpleTest.is(aExpected, aActual, aMsg);
     }
 
+    function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState,
+                        aAbsentExtraState)
+    {
+      gOpenerWnd.testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState,
+                            aAbsentExtraState);
+    }
+
     var invokerChecker = gOpenerWnd.invokerChecker;
 
     const STATE_BUSY = gOpenerWnd.STATE_BUSY;
     const EVENT_DOCUMENT_LOAD_COMPLETE =
       gOpenerWnd.EVENT_DOCUMENT_LOAD_COMPLETE;
     const EVENT_DOCUMENT_RELOAD = gOpenerWnd.EVENT_DOCUMENT_RELOAD;
     const EVENT_DOCUMENT_LOAD_STOPPED =
       gOpenerWnd.EVENT_DOCUMENT_LOAD_STOPPED;
@@ -86,16 +93,19 @@
         }
 
         if (!event)
           return;
 
         is(event.state, STATE_BUSY, "Wrong state of statechange event.");
         is(event.isEnabled(), aIsEnabled,
            "Wrong value of state of statechange event");
+
+        testStates(event.accessible, (aIsEnabled ? STATE_BUSY : 0), 0,
+                   (aIsEnabled ? 0 : STATE_BUSY), 0);
       }
     }
 
     function documentReloadChecker(aIsFromUserInput)
     {
       this.type = EVENT_DOCUMENT_RELOAD;
       this.__defineGetter__("target", getDocument);
 
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_menu.html
@@ -0,0 +1,88 @@
+<html>
+
+<head>
+  <title>ARIA menu events testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    function showMenu(aID, aViaDisplayStyle)
+    {
+      this.DOMNode = getNode(aID);
+
+      this.invoke = function showMenu_invoke()
+      {
+        if (aViaDisplayStyle)
+          this.DOMNode.style.display = "block";
+        else
+          this.DOMNode.style.visibility = "visible";
+      };
+
+      this.getID = function showMenu_getID()
+      {
+        return "Show ARIA menu " + aID + " by " +
+          (aViaDisplayStyle ? "display" : "visibility") + " style tricks";
+      };
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    var gQueue = null;
+
+    //gA11yEventDumpID = "eventdump";
+
+    function doTests()
+    {
+      gQueue = new eventQueue(EVENT_MENUPOPUP_START);
+
+      gQueue.push(new showMenu("menu1", true));
+      gQueue.push(new showMenu("menu2", false));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=606207"
+     title="Dojo dropdown buttons are broken">
+    Mozilla Bug 606207
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="menu1" role="menu" style="display: none;">
+    <div role="menuitem">menuitem1.1</div>
+    <div role="menuitem">menuitem1.2</div>
+  </div>
+  <div id="menu2" role="menu" style="visibility: hidden;">
+    <div role="menuitem">menuitem2.1</div>
+    <div role="menuitem">menuitem2.2</div>
+  </div>
+
+  <div id="eventdump"></div>
+
+</body>
+</html>
--- a/accessible/tests/mochitest/events/test_docload.xul
+++ b/accessible/tests/mochitest/events/test_docload.xul
@@ -13,16 +13,18 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
   <![CDATA[
     // var gA11yEventDumpID = "eventdump"; // debug stuff
 
     function doTest()
     {
--- a/accessible/tests/mochitest/events/test_statechange.html
+++ b/accessible/tests/mochitest/events/test_statechange.html
@@ -55,17 +55,17 @@
     }
 
     function invalidInput(aNodeOrID)
     {
       this.DOMNode = getNode(aNodeOrID);
 
       this.invoke = function invalidInput_invoke() {
         // Note: this should fire an EVENT_STATE_CHANGE
-        this.DOMNode.value = "I am too long";
+        this.DOMNode.value = "I am not an email";
       };
 
       this.check = function invalidInput_check() {
         testStates(aNodeOrID, STATE_INVALID);
       };
 
       this.getID = function invalidInput_getID() {
         return prettyName(aNodeOrID) + " became invalid";
@@ -83,17 +83,17 @@
     {
       gQueue = new eventQueue(nsIAccessibleEvent.EVENT_STATE_CHANGE);
 
       // Test delayed editable state change
       var doc = document.getElementById("iframe").contentDocument;
       gQueue.push(new makeEditableDoc(doc));
 
       // invalid state change
-      gQueue.push(new invalidInput("maxlength"));
+      gQueue.push(new invalidInput("email"));
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -115,13 +115,13 @@
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="testContainer">
     <iframe id="iframe"></iframe>
   </div>
 
-  <input id="maxlength" maxlength="1">
+  <input id="email" type='email'>
 
   <div id="eventdump"></div>
 </body>
 </html>
--- a/accessible/tests/mochitest/name.js
+++ b/accessible/tests/mochitest/name.js
@@ -1,284 +1,19 @@
+/**
+ * Test accessible name for the given accessible identifier.
+ */
 function testName(aAccOrElmOrID, aName, aMsg)
 {
   var msg = aMsg ? aMsg : "";
 
   var acc = getAccessible(aAccOrElmOrID);
   if (!acc)
     return;
 
   var txtID = prettyName(aAccOrElmOrID);
   try {
     is(acc.name, aName, msg + "Wrong name of the accessible for " + txtID);
   } catch (e) {
     ok(false, msg + "Can't get name of the accessible for " + txtID);
   }
   return acc;
 }
-
-////////////////////////////////////////////////////////////////////////////////
-// Name tests described by "namerules.xml" file.
-
-var gNameRulesFileURL = "namerules.xml";
-
-var gRuleDoc = null;
-
-/**
- * Start name tests. Run through markup elements and test names for test
- * element (see namerules.xml for details).
- */
-function testNames()
-{
-  var request = new XMLHttpRequest();
-  request.open("get", gNameRulesFileURL, false);
-  request.send();
-
-  gRuleDoc = request.responseXML;
-
-  var markupElms = evaluateXPath(gRuleDoc, "//rules/rulesample/markup");
-  gTestIterator.iterateMarkups(markupElms);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Private section.
-
-/**
- * Helper class to interate through name tests.
- */
-var gTestIterator =
-{
-  iterateMarkups: function gTestIterator_iterateMarkups(aMarkupElms)
-  {
-    this.markupElms = aMarkupElms;
-
-    this.iterateNext();
-  },
-
-  iterateRules: function gTestIterator_iterateRules(aElm, aContainer, aRuleElms)
-  {
-    this.ruleElms = aRuleElms;
-    this.elm = aElm;
-    this.container = aContainer;
-
-    this.iterateNext();
-  },
-
-  iterateNext: function gTestIterator_iterateNext()
-  {
-    if (this.markupIdx == -1) {
-      this.markupIdx++;
-      testNamesForMarkup(this.markupElms[this.markupIdx]);
-      return;
-    }
-
-    this.ruleIdx++;
-    if (this.ruleIdx == this.ruleElms.length) {
-      this.markupIdx++;
-      if (this.markupIdx == this.markupElms.length) {
-        SimpleTest.finish();
-        return;
-      }
-
-      document.body.removeChild(this.container);
-
-      this.ruleIdx = -1;
-      testNamesForMarkup(this.markupElms[this.markupIdx]);
-      return;
-    }
-
-    testNameForRule(this.elm, this.ruleElms[this.ruleIdx]);
-  },
-
-  markupElms: null,
-  markupIdx: -1,
-  ruleElms: null,
-  ruleIdx: -1,
-  elm: null,
-  container: null
-};
-
-/**
- * Process every 'markup' element and test names for it. Used by testNames
- * function.
- */
-function testNamesForMarkup(aMarkupElm)
-{
-  var div = document.createElement("div");
-  div.setAttribute("id", "test");
-
-  var child = aMarkupElm.firstChild;
-  while (child) {
-    var newChild = document.importNode(child, true);
-    div.appendChild(newChild);
-    child = child.nextSibling;
-  }
-
-  waitForEvent(EVENT_REORDER, document, testNamesForMarkupRules,
-                null, aMarkupElm, div);
-
-  document.body.appendChild(div);
-}
-
-function testNamesForMarkupRules(aMarkupElm, aContainer)
-{
-  ensureAccessibleTree(aContainer);
-
-  var serializer = new XMLSerializer();
-
-  var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref");
-  var elms = evaluateXPath(document, expr, htmlDocResolver);
-
-  var ruleId = aMarkupElm.getAttribute("ruleset");
-  var ruleElms = getRuleElmsByRulesetId(ruleId);
-
-  gTestIterator.iterateRules(elms[0], aContainer, ruleElms);
-}
-
-/**
- * Test name for current rule and current 'markup' element. Used by
- * testNamesForMarkup function.
- */
-function testNameForRule(aElm, aRuleElm)
-{
-  if (aRuleElm.hasAttribute("attr"))
-    testNameForAttrRule(aElm, aRuleElm);
-  else if (aRuleElm.hasAttribute("elm") && aRuleElm.hasAttribute("elmattr"))
-    testNameForElmRule(aElm, aRuleElm);
-  else if (aRuleElm.getAttribute("fromsubtree") == "true")
-    testNameForSubtreeRule(aElm, aRuleElm);
-}
-
-function testNameForAttrRule(aElm, aRule)
-{
-  var name = "";
-
-  var attr = aRule.getAttribute("attr");
-  var attrValue = aElm.getAttribute(attr);
-
-  var type = aRule.getAttribute("type");
-  if (type == "string") {
-    name = attrValue;
-
-  } else if (type == "ref") {
-    var ids = attrValue.split(/\s+/);
-    for (var idx = 0; idx < ids.length; idx++) {
-      var labelElm = getNode(ids[idx]);
-      if (name != "")
-        name += " ";
-
-      name += labelElm.getAttribute("a11yname");
-    }
-  }
-
-  var msg = "Attribute '" + attr + "' test. ";
-  testName(aElm, name, msg);
-  aElm.removeAttribute(attr);
-
-  gTestIterator.iterateNext();
-}
-
-function testNameForElmRule(aElm, aRule)
-{  
-  var elm = aRule.getAttribute("elm");
-  var elmattr = aRule.getAttribute("elmattr");
-
-  var filter = {
-    acceptNode: function filter_acceptNode(aNode)
-    {
-      if (aNode.localName == this.mLocalName &&
-          aNode.getAttribute(this.mAttrName) == this.mAttrValue)
-        return NodeFilter.FILTER_ACCEPT;
-
-      return NodeFilter.FILTER_SKIP;
-    },
-
-    mLocalName: elm,
-    mAttrName: elmattr,
-    mAttrValue: aElm.getAttribute("id")
-  };
-
-  var treeWalker = document.createTreeWalker(document.body,
-                                             NodeFilter.SHOW_ELEMENT,
-                                             filter, false);
-  var labelElm = treeWalker.nextNode();
-  var msg = "Element '" + elm + "' test.";
-  testName(aElm, labelElm.getAttribute("a11yname"), msg);
-
-  var parentNode = labelElm.parentNode;
-  waitForEvent(EVENT_REORDER, parentNode,
-               gTestIterator.iterateNext, gTestIterator);
-
-  parentNode.removeChild(labelElm);
-}
-
-function testNameForSubtreeRule(aElm, aRule)
-{
-  var msg = "From subtree test.";
-  testName(aElm, aElm.getAttribute("a11yname"), msg);
-
-  waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator);
-
-  while (aElm.firstChild)
-    aElm.removeChild(aElm.firstChild);
-}
-
-/**
- * Return array of 'rule' elements. Used in conjunction with
- * getRuleElmsFromRulesetElm() function.
- */
-function getRuleElmsByRulesetId(aRulesetId)
-{
-  var expr = "//rules/ruledfn/ruleset[@id='" + aRulesetId + "']";
-  var rulesetElm = evaluateXPath(gRuleDoc, expr);
-  return getRuleElmsFromRulesetElm(rulesetElm[0]);
-}
-
-function getRuleElmsFromRulesetElm(aRulesetElm)
-{
-  var rulesetId = aRulesetElm.getAttribute("ref");
-  if (rulesetId)
-    return getRuleElmsByRulesetId(rulesetId);
-
-  var ruleElms = [];
-
-  var child = aRulesetElm.firstChild;
-  while (child) {
-    if (child.localName == "ruleset")
-      ruleElms = ruleElms.concat(getRuleElmsFromRulesetElm(child));
-    if (child.localName == "rule")
-      ruleElms.push(child);
-
-    child = child.nextSibling;
-  }
-
-  return ruleElms;
-}
-
-/**
- * Helper method to evaluate xpath expression.
- */
-function evaluateXPath(aNode, aExpr, aResolver)
-{
-  var xpe = new XPathEvaluator();
-
-  var resolver = aResolver;
-  if (!resolver) {
-    var node = aNode.ownerDocument == null ?
-      aNode.documentElement : aNode.ownerDocument.documentElement;
-    resolver = xpe.createNSResolver(node);
-  }
-
-  var result = xpe.evaluate(aExpr, aNode, resolver, 0, null);
-  var found = [];
-  var res;
-  while (res = result.iterateNext())
-    found.push(res);
-
-  return found;
-}
-
-function htmlDocResolver(aPrefix) {
-  var ns = {
-    'html' : 'http://www.w3.org/1999/xhtml'
-  };
-  return ns[aPrefix] || null;
-}
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/name/Makefile.in
@@ -0,0 +1,63 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Alexander Surkov <surkov.alexander@gmail.com> (original author)
+#
+# 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  = accessible/name
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES =\
+		general.css \
+		general.xbl \
+		markup.js \
+		nsRootAcc_wnd.xul \
+		test_button.html \
+		test_general.html \
+		test_general.xul \
+		test_link.html \
+		test_markup.html \
+		test_nsRootAcc.xul \
+		markuprules.xml \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
rename from accessible/tests/mochitest/name.css
rename to accessible/tests/mochitest/name/general.css
--- a/accessible/tests/mochitest/name.css
+++ b/accessible/tests/mochitest/name/general.css
@@ -1,11 +1,11 @@
 box.first {
-  -moz-binding: url('name.xbl#first');
+  -moz-binding: url('general.xbl#first');
 }
 
 .second {
-  -moz-binding: url('name.xbl#second');
+  -moz-binding: url('general.xbl#second');
 }
 
 .third {
-  -moz-binding: url('name.xbl#third');
+  -moz-binding: url('general.xbl#third');
 }
rename from accessible/tests/mochitest/name.xbl
rename to accessible/tests/mochitest/name/general.xbl
copy from accessible/tests/mochitest/name.js
copy to accessible/tests/mochitest/name/markup.js
--- a/accessible/tests/mochitest/name.js
+++ b/accessible/tests/mochitest/name/markup.js
@@ -1,29 +1,12 @@
-function testName(aAccOrElmOrID, aName, aMsg)
-{
-  var msg = aMsg ? aMsg : "";
-
-  var acc = getAccessible(aAccOrElmOrID);
-  if (!acc)
-    return;
+////////////////////////////////////////////////////////////////////////////////
+// Name tests described by "markuprules.xml" file.
 
-  var txtID = prettyName(aAccOrElmOrID);
-  try {
-    is(acc.name, aName, msg + "Wrong name of the accessible for " + txtID);
-  } catch (e) {
-    ok(false, msg + "Can't get name of the accessible for " + txtID);
-  }
-  return acc;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Name tests described by "namerules.xml" file.
-
-var gNameRulesFileURL = "namerules.xml";
+var gNameRulesFileURL = "markuprules.xml";
 
 var gRuleDoc = null;
 
 /**
  * Start name tests. Run through markup elements and test names for test
  * element (see namerules.xml for details).
  */
 function testNames()
rename from accessible/tests/mochitest/namerules.xml
rename to accessible/tests/mochitest/name/markuprules.xml
rename from accessible/tests/mochitest/name_nsRootAcc_wnd.xul
rename to accessible/tests/mochitest/name/nsRootAcc_wnd.xul
rename from accessible/tests/mochitest/test_name_button.html
rename to accessible/tests/mochitest/name/test_button.html
--- a/accessible/tests/mochitest/test_name_button.html
+++ b/accessible/tests/mochitest/name/test_button.html
@@ -5,19 +5,19 @@
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
-          src="common.js"></script>
+          src="../common.js"></script>
   <script type="application/javascript"
-          src="name.js"></script>
+          src="../name.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       // html:button, aria-label
       testName("btn_aria_label", "button label");
 
       // html:button, aria-labelledby
rename from accessible/tests/mochitest/test_name.html
rename to accessible/tests/mochitest/name/test_general.html
--- a/accessible/tests/mochitest/test_name.html
+++ b/accessible/tests/mochitest/name/test_general.html
@@ -6,19 +6,19 @@
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
-          src="common.js"></script>
+          src="../common.js"></script>
   <script type="application/javascript"
-          src="name.js"></script>
+          src="../name.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       // aria-label
 
       // Simple label provided via ARIA
       testName("btn_simple_aria_label", "I am a button");
@@ -104,35 +104,41 @@
       // Note: the name contains the content of the button.
       testName("btn_label_inside", "text10text");
 
       // The label element and the button are placed in the same form. Gets
       // the name from the label subtree.
       testName("btn_label_inform", "in form");
 
       // The label element is placed outside of form where the button is.
-      // Do not take into account the label.
-      testName("btn_label_outform", "12");
+      // Take into account the label.
+      testName("btn_label_outform", "out form");
 
       // The label element and the button are in the same document. Gets the
       // name from the label subtree.
       testName("btn_label_indocument", "in document");
 
+      // Multiple label elements for single button
+      testName("btn_label_multi", "label1label2");
+
 
       //////////////////////////////////////////////////////////////////////////
       // name from children
 
       // ARIA role button is presented allowing the name calculation from
       // children.
       testName("btn_children", "14");
 
       // ARIA role option is presented allowing the name calculation from
       // visible children (bug 443081).
       testName("lb_opt1_children_hidden", "i am visible");
 
+      // Get the name from subtree of menuitem crossing role nothing to get
+      // the name from its children.
+      testName("tablemenuitem", "menuitem 1");
 
       //////////////////////////////////////////////////////////////////////////
       // title attribute
 
       // If nothing is left. Let's try title attribute.
       testName("btn_title", "title");
 
       //////////////////////////////////////////////////////////////////////////
@@ -355,27 +361,41 @@
   <form>
     <button id="btn_label_outform">12</button>
   </form>
 
   <!-- label element, label and the button are in the same document -->
   <label for="btn_label_indocument">in document</label>
   <button id="btn_label_indocument">13</button>
 
+  <!-- multiple label elements for single button -->
+  <label for="btn_label_multi">label1</label>
+  <label for="btn_label_multi">label2</label>
+  <button id="btn_label_multi">button</button>
+
   <!-- name from children -->
   <span id="btn_children" role="button">14</span>
 
   <!-- name from children, hidden children -->
   <div role="listbox" tabindex="0">
     <div id="lb_opt1_children_hidden" role="option" tabindex="0">
       <span>i am visible</span>
       <span style="display:none">i am hidden</span>
     </div>
   </div>
 
+  <table role="menu">
+    <tr role="menuitem" id="tablemenuitem">
+      <td>menuitem 1</td>
+    </tr>
+    <tr role="menuitem">
+      <td>menuitem 2</td>
+    </tr>
+  </table>
+
   <!-- name from title attribute -->
   <span id="btn_title" role="group" title="title">15</span>
 
   <!-- A textarea nested in a label with a text child (bug #453371). -->
   <form>
     <label>Story
       <textarea id="textareawithchild" name="name">Foo</textarea>
       is ended.
rename from accessible/tests/mochitest/test_name.xul
rename to accessible/tests/mochitest/name/test_general.xul
--- a/accessible/tests/mochitest/test_name.xul
+++ b/accessible/tests/mochitest/name/test_general.xul
@@ -1,30 +1,30 @@
 <?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"?>
-<?xml-stylesheet href="name.css"
+<?xml-stylesheet href="general.css"
                  type="text/css"?>
 
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="Accessibility Name Calculating Test.">
 
   <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 type="application/javascript"
-          src="common.js"></script>
+          src="../common.js"></script>
   <script type="application/javascript"
-          src="role.js"></script>
+          src="../role.js"></script>
   <script type="application/javascript"
-          src="name.js"></script>
+          src="../name.js"></script>
 
   <script type="application/javascript">
   <![CDATA[
     function doTest()
     {
       // aria-label
 
       // Simple label provided via ARIA
@@ -101,16 +101,19 @@
       testName("btn_label_1", "label1");
 
       // The label is on 1st, the button is on 5th level relative common parent.
       testName("btn_label_2", "label2");
 
       // The label and button are siblings.
       testName("btn_label_3", "label3");
 
+      // Multiple labels for single button: XUL button takes the last one.
+      testName("btn_label_4", "label5");
+
 
       //////////////////////////////////////////////////////////////////////////
       // Name from the label element in anonymous content (see bug 362365).
 
       // Get the name from anonymous label element for anonymous textbox
       // (@anonid is used).
       var ID = "box_label_anon1";
       var box1Acc = testName(ID, null);
@@ -291,16 +294,20 @@
           <box>
             <button id="btn_label_2"/>
           </box>
         </box>
       </box>
     </box>
     <label control="btn_label_3">label3</label>
     <button id="btn_label_3"/>
+
+    <label control="btn_label_4">label4</label>
+    <label control="btn_label_4">label5</label>
+    <button id="btn_label_4"/>
   </hbox>
 
   <!-- label element, anonymous content -->
   <box id="box_label_anon1"
        class="first"
        role="group"/>
 
   <box id="box_label_anon2" 
rename from accessible/tests/mochitest/test_name_link.html
rename to accessible/tests/mochitest/name/test_link.html
--- a/accessible/tests/mochitest/test_name_link.html
+++ b/accessible/tests/mochitest/name/test_link.html
@@ -7,19 +7,19 @@
         type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
-          src="common.js"></script>
+          src="../common.js"></script>
   <script type="application/javascript"
-          src="name.js"></script>
+          src="../name.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       // aria-label
       testName("aria_label", "anchor label");
 
       // aria-labelledby
rename from accessible/tests/mochitest/test_name_markup.html
rename to accessible/tests/mochitest/name/test_markup.html
--- a/accessible/tests/mochitest/test_name_markup.html
+++ b/accessible/tests/mochitest/name/test_markup.html
@@ -5,21 +5,24 @@
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
   <script type="application/javascript"
-          src="common.js"></script>
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
   <script type="application/javascript"
-          src="events.js"></script>
+          src="../name.js"></script>
+
   <script type="application/javascript"
-          src="name.js"></script>
+          src="markup.js"></script>
 
   <script type="application/javascript">
     // gA11yEventDumpID = "eventdump";
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(testNames);
   </script>
 
rename from accessible/tests/mochitest/test_name_nsRootAcc.xul
rename to accessible/tests/mochitest/name/test_nsRootAcc.xul
--- a/accessible/tests/mochitest/test_name_nsRootAcc.xul
+++ b/accessible/tests/mochitest/name/test_nsRootAcc.xul
@@ -9,21 +9,21 @@
   <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 type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
-          src="common.js"></script>
+          src="../common.js"></script>
   <script type="application/javascript"
-          src="role.js"></script>
+          src="../role.js"></script>
   <script type="application/javascript"
-          src="events.js"></script>
+          src="../events.js"></script>
 
   <script type="application/javascript">
   <![CDATA[
     // var gA11yEventDumpID = "eventdump"; // debug stuff
 
     function doTest()
     {
       // Actually, just disable this test everywhere -- bug 586818.
@@ -31,17 +31,17 @@
       return;
 
       if (LINUX) {
         todo(false, "Enable test on Linux - see bug 525175.");
         SimpleTest.finish();
         return;
       }
 
-      var w = window.openDialog("name_nsRootAcc_wnd.xul",
+      var w = window.openDialog("nsRootAcc_wnd.xul",
                                 "nsRootAcc_name_test", 
                                 "chrome,width=600,height=600");
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(doTest);
   ]]>
   </script>
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -18,16 +18,22 @@
 
   <script type="application/javascript">
     function doTest()
     {
       // html:label@for
       testRelation("label1", RELATION_LABEL_FOR, "checkbox1");
       testRelation("checkbox1", RELATION_LABELLED_BY, "label1");
 
+      // html:label@for, multiple
+      testRelation("label1_1", RELATION_LABEL_FOR, "checkbox1_1");
+      testRelation("label1_2", RELATION_LABEL_FOR, "checkbox1_1");
+      testRelation("checkbox1_1", RELATION_LABELLED_BY,
+                   [ "label1_1", "label1_2" ]);
+
       // aria-labelledby
       testRelation("label2", RELATION_LABEL_FOR, "checkbox2");
       testRelation("checkbox2", RELATION_LABELLED_BY, "label2");
 
       // aria-labelledby, multiple relations
       testRelation("label3", RELATION_LABEL_FOR, "checkbox3");
       testRelation("label4", RELATION_LABEL_FOR, "checkbox3");
       testRelation("checkbox3", RELATION_LABELLED_BY, ["label3", "label4"]);
@@ -85,18 +91,19 @@
       testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
       testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
 
       // 'default button' relation
       testRelation("input", RELATION_DEFAULT_BUTTON, "submit");
 
       // output 'for' relations
       testRelation("output", RELATION_CONTROLLED_BY, ["input", "input2"]);
-      testRelation("input", RELATION_CONTROLLER_FOR, "output");
-      testRelation("input2", RELATION_CONTROLLER_FOR, "output");
+      testRelation("output2", RELATION_CONTROLLED_BY, ["input", "input2"]);
+      testRelation("input", RELATION_CONTROLLER_FOR, ["output", "output2"]);
+      testRelation("input2", RELATION_CONTROLLER_FOR, ["output", "output2"]);
 
       // 'described by'/'description for' relation for html:table and
       // html:caption
       testRelation("caption", RELATION_DESCRIPTION_FOR, "table");
       testRelation("table", RELATION_DESCRIBED_BY, "caption");
 
       // 'labelled by'/'label for' relation for html:fieldset and
       // html:legend
@@ -141,16 +148,20 @@
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <label id="label1" for="checkbox1">label</label>
   <input id="checkbox1" />
 
+  <label id="label1_1" for="checkbox1_1">label</label>
+  <label id="label1_2" for="checkbox1_1">label</label>
+  <input id="checkbox1_1" />
+
   <span id="label2">label</span>
   <span role="checkbox" id="checkbox2" aria-labelledby="label2"></span>
 
   <span id="label3">label1</span>
   <span id="label4">label2</span>
   <span role="checkbox" id="checkbox3" aria-labelledby="label3 label4"></span>
 
   <span id="descr1">description</span>
@@ -202,16 +213,17 @@
   <span id="flowfrom1">flow from</span>
   <span id="flowfrom2">flow from</span>
 
   <form id="form">
     <input id="input" />
     <input id="input2" />
     <input type="submit" id="submit" />
     <output id="output" style="display:block" for="input input2"></output>
+    <output id="output2" for="input input2"></output>
   </form>
 
   <table id="table">
     <caption id="caption">tabple caption</caption>
     <tr>
       <td>cell1</td><td>cell2</td>
     </tr>
   </table>
--- a/accessible/tests/mochitest/relations/test_general.xul
+++ b/accessible/tests/mochitest/relations/test_general.xul
@@ -21,16 +21,22 @@
   <script type="application/javascript">
   <![CDATA[
     function doTest()
     {
       // xul:label@control
       testRelation("label1", RELATION_LABEL_FOR, "checkbox1");
       testRelation("checkbox1", RELATION_LABELLED_BY, "label1");
 
+      // xul:label@control, multiple
+      testRelation("label1_1", RELATION_LABEL_FOR, "checkbox1_1");
+      testRelation("label1_2", RELATION_LABEL_FOR, "checkbox1_1");
+      testRelation("checkbox1_1", RELATION_LABELLED_BY,
+                   [ "label1_1", "label1_2" ]);
+
       // aria-labelledby
       testRelation("label2", RELATION_LABEL_FOR, "checkbox2");
       testRelation("checkbox2", RELATION_LABELLED_BY, "label2");
 
       // aria-labelledby, multiple relations
       testRelation("label3", RELATION_LABEL_FOR, "checkbox3");
       testRelation("label4", RELATION_LABEL_FOR, "checkbox3");
       testRelation("checkbox3", RELATION_LABELLED_BY, ["label3", "label4"]);
@@ -43,16 +49,22 @@
       testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox5");
       testRelation("descr3", RELATION_DESCRIPTION_FOR, "checkbox5");
       testRelation("checkbox5", RELATION_DESCRIBED_BY, ["descr2", "descr3"]);
 
       // xul:description@control
       testRelation("descr4", RELATION_DESCRIPTION_FOR, "checkbox6");
       testRelation("checkbox6", RELATION_DESCRIBED_BY, "descr4");
 
+      // xul:description@control, multiple
+      testRelation("descr5", RELATION_DESCRIPTION_FOR, "checkbox7");
+      testRelation("descr6", RELATION_DESCRIPTION_FOR, "checkbox7");
+      testRelation("checkbox7", RELATION_DESCRIBED_BY,
+                   [ "descr5", "descr6" ]);
+
       // aria_owns, multiple relations
       testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
 
       // 'node child of' relation for outlineitem role
       testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
@@ -140,16 +152,20 @@
       </div>
       <pre id="test">
       </pre>
     </body>
 
     <label id="label1" control="checkbox1">label</label>
     <checkbox id="checkbox1"/>
 
+    <label id="label1_1" control="checkbox1_1">label</label>
+    <label id="label1_2" control="checkbox1_1">label</label>
+    <checkbox id="checkbox1_1"/>
+
     <description id="label2">label</description>
     <description role="checkbox" id="checkbox2" aria-labelledby="label2"/>
 
     <description id="label3">label</description>
     <description id="label4">label</description>
     <description role="checkbox" id="checkbox3"
                  aria-labelledby="label3 label4"/>
 
@@ -159,16 +175,20 @@
     <description id="descr2">label</description>
     <description id="descr3">label</description>
     <description role="checkbox" id="checkbox5"
                  aria-describedby="descr2 descr3"/>
 
     <description id="descr4" control="checkbox6">description</description>
     <checkbox id="checkbox6"/>
 
+    <description id="descr5" control="checkbox7">description</description>
+    <description id="descr6" control="checkbox7">description</description>
+    <checkbox id="checkbox7"/>
+
     <description role="treeitem" id="treeitem1">Yellow</description>
     <description role="treeitem" id="treeitem2">Orange</description>
     <vbox id="tree" role="tree" aria-owns="treeitem1 treeitem2">
       <description role="treeitem" id="treeitem3">Blue</description>
       <description role="treeitem" id="treeitem4" aria-level="1">Green</description>
       <description role="treeitem" id="treeitem5" aria-level="2">Light green</description>
     </vbox>
 
--- a/accessible/tests/mochitest/states/test_inputs.html
+++ b/accessible/tests/mochitest/states/test_inputs.html
@@ -33,27 +33,40 @@
       testStates(never_required[i], 0, 0, STATE_REQUIRED);
     }
 
     // inherited 'unavailable' state
     testStates("f", STATE_UNAVAILABLE);
     testStates("f_input", STATE_UNAVAILABLE);
     testStates("f_input_disabled", STATE_UNAVAILABLE);
 
+    /**
+     * maxlength doesn't make the element invalid until bug 613016 and bug 613019
+     * are fixed. Commenting out related lines and adding a todo to make sure
+     * it will be uncommented as soon as possible.
+     */
+    var todoInput = document.createElement("input");
+    todoInput.maxLength = '2';
+    todoInput.value = 'foo';
+    todo(!todoInput.validity.valid,
+         "input should be invalid because of maxlength");
+
     // invalid/valid state
-    var invalid = ["maxlength","pattern","email","url"];
-    document.getElementById("maxlength").value = "i am too long";
+    //var invalid = ["maxlength","pattern","email","url"];
+    //document.getElementById("maxlength").value = "i am too long";
+    var invalid = ["pattern","email","url"];
     for (i in invalid) {
       testStates(invalid[i], STATE_INVALID);
       testStates(invalid[i] + "2", 0, 0, STATE_INVALID);
     }
 
     // invalid/valid state
-    var invalid = ["maxlength","pattern","email","url"];
-    document.getElementById("maxlength").value = "i am too long";
+    //var invalid = ["maxlength","pattern","email","url"];
+    //document.getElementById("maxlength").value = "i am too long";
+    var invalid = ["pattern","email","url"];
     for (i in invalid) {
       testStates(invalid[i], STATE_INVALID);
       testStates(invalid[i] + "2", 0, 0, STATE_INVALID);
     }
 
     SimpleTest.finish();
   }
 
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/test_keys.html
@@ -0,0 +1,59 @@
+<html>
+
+<head>
+  <title>Keyboard shortcuts tests</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="common.js"></script>
+
+  <script type="application/javascript">
+    function testKeyboardShortcut(aAccOrElmOrID, aKey)
+    {
+      var acc = getAccessible(aAccOrElmOrID);
+      if (!acc)
+        return;
+
+      is(acc.keyboardShortcut, aKey,
+         "Wrong keyboard shortcut on " + prettyName(aAccOrElmOrID));
+    }
+
+    function doTest()
+    {
+      testKeyboardShortcut("input1", "");
+      testKeyboardShortcut("input2", "Alt+Shift+b");
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=381599"
+     title="Inverse relations cache">
+    Mozilla Bug 381599
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <label accesskey="a">
+    <input id="input1"/>
+  </label>
+  <label accesskey="b" for="input2">
+  <input id="input2"/>
+</body>
+</html>
--- a/accessible/tests/mochitest/text/Makefile.in
+++ b/accessible/tests/mochitest/text/Makefile.in
@@ -41,13 +41,16 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/text
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
+		doc.html \
+		test_doc.html \
 		test_singleline.html \
+		test_whitespaces.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/text/doc.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="application/javascript">
+    document.documentElement.appendChild(document.createTextNode("outbody"));
+  </script>
+</head>
+<body>inbody</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_doc.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>nsIAccessibleText getText related function tests for document accessible</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../text.js"></script>
+  <script type="application/javascript">
+    
+    function doTest()
+    {
+      testText([getNode("iframe").contentDocument], 0, 14, "outbody inbody");
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="Elements appended outside the body aren't accessible"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <iframe id="iframe" src="doc.html"></iframe>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_whitespaces.html
@@ -0,0 +1,630 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>getText... methods tests on string with whitespaces for plain text containers</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="../common.js"></script>
+
+  <script type="application/javascript"
+          src="../text.js"></script>
+  <script type="application/javascript">
+    
+    function doTest()
+    {
+      // Tests for elements rendering the original sring
+      var IDs = ["input", "div", "editable", "textarea"];
+  
+      // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n
+      //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
+
+      ////////////////////////////////////////////////////////////////////////
+      // getText
+
+      testText(IDs, 0, 1, "B");
+      testText(IDs, 5, 6, " ");
+      testText(IDs, 9, 11, "  ");
+      testText(IDs, 16, 19, "   ");
+      testText(IDs, 0, 22, "Brave Sir  Robin   ran");
+
+      ////////////////////////////////////////////////////////////////////////
+      // getTextAfterOffset
+
+      // BOUNDARY_CHAR
+      testTextAfterOffset(0, BOUNDARY_CHAR, "r", 1, 2,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(1, BOUNDARY_CHAR, "a", 2, 3,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(4, BOUNDARY_CHAR, " ", 5, 6,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(5, BOUNDARY_CHAR, "S", 6, 7,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(8, BOUNDARY_CHAR, " ", 9, 10,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(9, BOUNDARY_CHAR, " ", 10, 11,
+                          "input", kOk, kTodo, kTodo,
+                          "div", kOk, kTodo, kTodo,
+                          "editable", kOk, kTodo, kTodo,
+                          "textarea", kOk, kTodo, kTodo);
+      testTextAfterOffset(10, BOUNDARY_CHAR, "R", 11, 12,
+			  "input", kTodo, kTodo, kTodo,
+			  "div", kTodo, kTodo, kTodo,
+			  "editable", kTodo, kTodo, kTodo,
+			  "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(15, BOUNDARY_CHAR, " ", 16, 17,
+			  "input", kTodo, kTodo, kTodo,
+			  "div", kTodo, kTodo, kTodo,
+			  "editable", kTodo, kTodo, kTodo,
+			  "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(16, BOUNDARY_CHAR, " ", 17, 18,
+			  "input", kOk, kTodo, kTodo,
+			  "div", kOk, kTodo, kTodo,
+			  "editable", kOk, kTodo, kTodo,
+			  "textarea", kOk, kTodo, kTodo);
+      testTextAfterOffset(17, BOUNDARY_CHAR, " ", 18, 19,
+			  "input", kOk, kTodo, kTodo,
+			  "div", kOk, kTodo, kTodo,
+			  "editable", kOk, kTodo, kTodo,
+			  "textarea", kOk, kTodo, kTodo);
+      testTextAfterOffset(18, BOUNDARY_CHAR, "r", 19, 20,
+			  "input", kTodo, kTodo, kTodo,
+			  "div", kTodo, kTodo, kTodo,
+			  "editable", kTodo, kTodo, kTodo,
+			  "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_WORD_START
+      testTextAfterOffset(0, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(5, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(6, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(9, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(10, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(11, BOUNDARY_WORD_START, "ran", 19, 22,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(16, BOUNDARY_WORD_START, "ran", 19, 22,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(18, BOUNDARY_WORD_START, "ran", 19, 22,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(19, BOUNDARY_WORD_START, "", 22, 22,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_WORD_END
+      testTextAfterOffset(0, BOUNDARY_WORD_END, " Sir", 5, 9,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(4, BOUNDARY_WORD_END, " Sir", 5, 9,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(5, BOUNDARY_WORD_END, " Sir", 5, 9,
+                          "input", kOk, kOk, kOk,
+                          "div", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(6, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(8, BOUNDARY_WORD_END, "   Robin", 9, 16,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(9, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                          "input", kOk, kOk, kOk,
+                          "div", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(10, BOUNDARY_WORD_END, "   ran", 16, 22,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(11, BOUNDARY_WORD_END, "   ran", 16, 22,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(15, BOUNDARY_WORD_END, "   ran", 16, 22,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(16, BOUNDARY_WORD_END, "   ran", 16, 22,
+                          "input", kOk, kOk, kOk,
+                          "div", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(17, BOUNDARY_WORD_END, "", 22, 22,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(18, BOUNDARY_WORD_END, "", 22, 22,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(19, BOUNDARY_WORD_END, "", 22, 22,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(21, BOUNDARY_WORD_END, "", 22, 22,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(22, BOUNDARY_WORD_END, "", 22, 22,
+                          "input", kOk, kTodo, kTodo,
+                          "div", kOk, kTodo, kTodo,
+                          "editable", kOk, kTodo, kTodo,
+                          "textarea", kTodo, kOk, kTodo);
+
+      ////////////////////////////////////////////////////////////////////////
+      // getTextBeforeOffset
+
+      // BOUNDARY_CHAR
+      testTextBeforeOffset(0, BOUNDARY_CHAR, "", 0, 0, 
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(1, BOUNDARY_CHAR, "B", 0, 1,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(6, BOUNDARY_CHAR, " ", 5, 6,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(10, BOUNDARY_CHAR, " ", 9, 10,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(11, BOUNDARY_CHAR, " ", 10, 11,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(17, BOUNDARY_CHAR, " ", 16, 17,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(19, BOUNDARY_CHAR, " ", 18, 19,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+
+      // BOUNDARY_WORD_START
+      testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(6, BOUNDARY_WORD_START, "Brave ", 0, 6,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(9, BOUNDARY_WORD_START, "Brave ", 0, 6,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(10, BOUNDARY_WORD_START, "Brave ", 0, 6,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(11, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(15, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(16, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(17, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(18, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(19, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(20, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(21, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_WORD_END
+      testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(4, BOUNDARY_WORD_END, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(6, BOUNDARY_WORD_END, "Brave", 0, 5,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(7, BOUNDARY_WORD_END, "Brave", 0, 5,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(8, BOUNDARY_WORD_END, "Brave", 0, 5,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(9, BOUNDARY_WORD_END, "Brave", 0, 5,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(10, BOUNDARY_WORD_END, " Sir", 5, 9,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(11, BOUNDARY_WORD_END, " Sir", 5, 9,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(15, BOUNDARY_WORD_END, " Sir", 5, 9,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(16, BOUNDARY_WORD_END, " Sir", 5, 9,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(17, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(18, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(19, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(21, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(22, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+
+      ////////////////////////////////////////////////////////////////////////
+      // getTextAtOffset
+
+      // BOUNDARY_CHAR
+      testTextAtOffset(0, BOUNDARY_CHAR, "B", 0, 1,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(1, BOUNDARY_CHAR, "r", 1, 2,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(5, BOUNDARY_CHAR, " ", 5, 6,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(9, BOUNDARY_CHAR, " ", 9, 10,
+                        "input", kOk, kOk, kOk,
+                        "div", kOk, kOk, kOk,
+                        "editable", kOk, kOk, kOk,
+                        "textarea", kOk, kOk, kOk);
+      testTextAtOffset(10, BOUNDARY_CHAR, " ", 10, 11,
+                        "input", kOk, kOk, kOk,
+                        "div", kOk, kOk, kOk,
+                        "editable", kOk, kOk, kOk,
+                        "textarea", kOk, kOk, kOk);
+      testTextAtOffset(17, BOUNDARY_CHAR, " ", 17, 18,
+                        "input", kOk, kOk, kOk,
+                        "div", kOk, kOk, kOk,
+                        "editable", kOk, kOk, kOk,
+                        "textarea", kOk, kOk, kOk);
+
+      // BOUNDARY_WORD_START
+      testTextAtOffset(0, BOUNDARY_WORD_START, "Brave ", 0, 6,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(5, BOUNDARY_WORD_START, "Brave ", 0, 6,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(6, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(8, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(9, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(10, BOUNDARY_WORD_START, "Sir  ", 6, 11,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(11, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(15, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(16, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(17, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(18, BOUNDARY_WORD_START, "Robin   ", 11, 19,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(19, BOUNDARY_WORD_START, "ran", 19, 22,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(21, BOUNDARY_WORD_START, "ran", 19, 22,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(22, BOUNDARY_WORD_START, "ran", 19, 22,
+                       "input", kTodo, kTodo, kTodo,
+                       "div", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kOk, kTodo);
+
+      // BOUNDARY_WORD_END
+      testTextAtOffset(0, BOUNDARY_WORD_END, "Brave", 0, 5,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(4, BOUNDARY_WORD_END, "Brave", 0, 5,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(5, BOUNDARY_WORD_END, "Brave", 0, 5,
+                       "input", kTodo, kTodo, kTodo,
+                       "div", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(6, BOUNDARY_WORD_END, " Sir", 5, 9,
+                        "input", kOk, kOk, kOk,
+                        "div", kOk, kOk, kOk,
+                        "editable", kOk, kOk, kOk,
+                        "textarea", kOk, kOk, kOk);
+      testTextAtOffset(8, BOUNDARY_WORD_END, " Sir", 5, 9,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(9, BOUNDARY_WORD_END, " Sir", 5, 9,
+                       "input", kTodo, kTodo, kTodo,
+                       "div", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(10, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(11, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(15, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(16, BOUNDARY_WORD_END, "  Robin", 9, 16,
+                       "input", kTodo, kTodo, kTodo,
+                       "div", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(17, BOUNDARY_WORD_END, "   ran", 16, 22,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(18, BOUNDARY_WORD_END, "   ran", 16, 22,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(19, BOUNDARY_WORD_END, "   ran", 16, 22,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(20, BOUNDARY_WORD_END, "   ran", 16, 22,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(21, BOUNDARY_WORD_END, "   ran", 16, 22,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(22, BOUNDARY_WORD_END, "   ran", 16, 22,
+                       "input", kTodo, kTodo, kTodo,
+                       "div", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="getText... methods tests on string with whitespaces for plain text containers"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=610568">Mozilla Bug 610568</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  <input id="input" value="Brave Sir  Robin   ran"/>
+  <div id="div">Brave Sir  Robin   ran</div>
+  <div id="editable" contenteditable="true">Brave Sir  Robin   ran</div>
+  <textarea id="textarea" cols="300">Brave Sir  Robin   ran</textarea>
+  </pre>
+
+</body>
+</html>
--- a/accessible/tests/mochitest/tree/Makefile.in
+++ b/accessible/tests/mochitest/tree/Makefile.in
@@ -41,24 +41,26 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/tree
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
-  $(warning test_applicationacc.xul temporarily disabled, see bug 561508) \
+		dockids.html \
+	$(warning test_applicationacc.xul temporarily disabled, see bug 561508) \
 		test_aria_globals.html \
 		test_aria_imgmap.html \
 		test_button.xul \
 		test_colorpicker.xul \
 		test_combobox.xul \
 		test_cssoverflow.html \
 		test_dochierarchy.html \
+		test_dockids.html \
 		test_filectrl.html \
 		test_formctrl.html \
 		test_formctrl.xul \
 		test_gencontent.html \
 		test_groupbox.xul \
 		test_iframe.html \
 		test_img.html \
 		test_list.html \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/tree/dockids.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html>
+  <head>
+    <link rel="next" href="http://www.mozilla.org">
+    <style>
+      head, link, a { display: block; }
+      link:after { content: "Link to " attr(href); }
+    </style>
+    <script>
+      window.onload = function() {
+        document.documentElement.appendChild(document.createElement("input"));
+
+        var l = document.createElement("link");
+        l.href = "http://www.mozilla.org";
+        l.textContent = "Another ";
+        document.documentElement.appendChild(l);
+
+        l = document.createElement("a");
+        l.href = "http://www.mozilla.org";
+        l.textContent = "Yet another link to mozilla";
+        document.documentElement.appendChild(l);
+      }
+    </script>
+  </head>
+  <body>
+    Hey, I'm a <body> with three links that are not inside me and an input
+    that's not inside me.
+  </body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_dockids.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Test document hierarchy</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+
+  <script type="application/javascript">
+  function doTest()
+  {
+    var tree =
+     { DOCUMENT: [
+       { PARAGRAPH: [ // head
+         { PARAGRAPH: [ // link
+           { STATICTEXT: [] }, // generated content
+           { STATICTEXT: [] } // generated content
+         ] }
+       ] },
+       { TEXT_LEAF: [ ] }, // body text
+       { ENTRY: [ // input under document element
+         { TEXT_LEAF: [ ] }
+       ] },
+       { PARAGRAPH: [ // link under document element
+         { TEXT_LEAF: [ ] }, // link content
+         { STATICTEXT: [ ] }, // generated content
+         { STATICTEXT: [ ] } // generated content
+       ] },
+       { LINK: [ // anchor under document element
+         { TEXT_LEAF: [ ] } // anchor content
+       ] },
+     ] };
+    testAccessibleTree(getNode("iframe").contentDocument, tree);
+
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  addA11yLoadEvent(doTest);
+  </script>
+</head>
+
+<body>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887"
+     title="Elements appended outside the body aren't accessible">
+    Mozilla Bug 608887
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <iframe src="dockids.html" id="iframe"></iframe>
+</body>
+</html>
--- a/accessible/tests/mochitest/treeupdate/test_doc.html
+++ b/accessible/tests/mochitest/treeupdate/test_doc.html
@@ -299,16 +299,51 @@
       }
 
       this.getID = function removeBodyFromIFrameDoc_getID()
       {
         return "remove body element";
       }
     }
 
+    function insertElmUnderDocElmWhileBodyMissed(aID)
+    {
+      this.docNode = getDocNode(aID);
+      this.inputNode = getDocNode(aID).createElement("input");
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_SHOW, this.inputNode),
+        new invokerChecker(EVENT_REORDER, this.docNode)
+      ];
+
+      this.invoke = function invoke()
+      {
+        this.docNode.documentElement.appendChild(this.inputNode);
+      }
+
+      this.finalCheck = function finalCheck()
+      {
+        var tree =
+          { DOCUMENT: [
+            { ENTRY: [
+              { TEXT_LEAF: [ ] }
+            ] }
+          ] };
+        testAccessibleTree(this.docNode, tree);
+
+        // Remove aftermath of this test before next test starts.
+        this.docNode.documentElement.removeChild(this.inputNode);
+      }
+
+      this.getID = function getID()
+      {
+        return "Insert element under document element while body is missed.";
+      }
+    }
+
     function insertBodyToIFrameDoc(aID)
     {
       this.__proto__ = new rootContentInserted(aID, "Yo ho ho i butylka roma!");
 
       this.invoke = function insertBodyToIFrameDoc_invoke()
       {
         // Insert body element.
         var docNode = getDocNode(aID);
@@ -338,30 +373,34 @@
       gQueue.push(new writeIFrameDoc("iframe"));
       gQueue.push(new replaceIFrameHTMLElm("iframe"));
       gQueue.push(new replaceIFrameBody("iframe"));
       gQueue.push(new openIFrameDoc("iframe"));
       gQueue.push(new closeIFrameDoc("iframe"));
       gQueue.push(new removeHTMLFromIFrameDoc("iframe"));
       gQueue.push(new insertHTMLToIFrameDoc("iframe"));
       gQueue.push(new removeBodyFromIFrameDoc("iframe"));
+      gQueue.push(new insertElmUnderDocElmWhileBodyMissed("iframe"));
       gQueue.push(new insertBodyToIFrameDoc("iframe"));
 
       gQueue.invoke(); // SimpleTest.finish() will be called in the end
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
      title="Update accessible tree when root element is changed"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=606082">Mozilla Bug 606082</a>
+  <a target="_blank"
+     title="Elements inserted outside the body aren't accessible"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <iframe id="iframe"></iframe>
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1032,14 +1032,21 @@ pref("services.sync.prefs.sync.signon.re
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
 // Disable the error console and inspector
 pref("devtools.errorconsole.enabled", false);
 pref("devtools.inspector.enabled", false);
 
+// The last Web Console height. This is initially 0 which means that the Web
+// Console will use the default height next time it shows.
+// Change to -1 if you do not want the Web Console to remember its last height.
+pref("devtools.hud.height", 0);
+
 // Whether the character encoding menu is under the main Firefox button. This
 // preference is a string so that localizers can alter it.
 pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
 
+// Allow using tab-modal prompts when possible.
+pref("prompts.tab_modal.enabled", true);
 // Whether the Panorama should animate going in/out of tabs
 pref("browser.panorama.animate_zoom", true);
--- a/browser/base/Makefile.in
+++ b/browser/base/Makefile.in
@@ -78,13 +78,16 @@ endif
 ifneq (,$(filter windows cocoa gtk2, $(MOZ_WIDGET_TOOLKIT)))
 ifneq ($(OS_ARCH),WINCE)
 DEFINES += -DCONTEXT_COPY_IMAGE_CONTENTS=1
 endif
 endif
 
 ifneq (,$(filter windows, $(MOZ_WIDGET_TOOLKIT)))
 DEFINES += -DCAN_DRAW_IN_TITLEBAR=1
+endif
+
+ifneq (,$(filter windows gtk2, $(MOZ_WIDGET_TOOLKIT)))
 DEFINES += -DMENUBAR_CAN_AUTOHIDE=1
 endif
 
 libs::
 	$(NSINSTALL) $(srcdir)/content/tabview/modules/* $(FINAL_TARGET)/modules/tabview
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -11,17 +11,17 @@ tabbrowser {
 
 .tabbrowser-tabs {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
 }
 
 #tabbrowser-tabs:not([overflow="true"]) + #new-tab-button,
 #tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
 #TabsToolbar[currentset]:not([currentset*="tabbrowser-tabs,new-tab-button"]) > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
-#navigator-toolbox[customizing="true"] > #TabsToolbar > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+#TabsToolbar[customizing="true"] > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
   visibility: collapse;
 }
 
 .tabbrowser-tab {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
 }
 
 .tabbrowser-tab:not([pinned]) {
@@ -140,19 +140,27 @@ toolbar[mode="icons"] > #reload-button[d
 }
 
 .menuitem-iconic-tooltip,
 .menuitem-tooltip[type="checkbox"],
 .menuitem-tooltip[type="radio"] {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-iconic-tooltip");
 }
 
+%ifdef MENUBAR_CAN_AUTOHIDE
+%ifndef CAN_DRAW_IN_TITLEBAR
+#appmenu-toolbar-button > .toolbarbutton-text {
+  display: -moz-box;
+}
+%endif
+
 #appmenu_offlineModeRecovery:not([checked=true]) {
   display: none;
 }
+%endif
 
 /* ::::: location bar ::::: */
 #urlbar {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
 /* Some child nodes want to be ordered based on the locale's direction, while
    everything else should be ltr. */
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3429,22 +3429,16 @@ function BrowserCustomizeToolbar()
   if (splitter)
     splitter.parentNode.removeChild(splitter);
 
   CombinedStopReload.uninit();
 
   PlacesToolbarHelper.customizeStart();
   BookmarksMenuButton.customizeStart();
 
-  let addonBar = document.getElementById("addon-bar");
-  if (addonBar.collapsed) {
-    addonBar.wasCollapsed = addonBar.collapsed;
-    addonBar.collapsed = false;
-  }
-
   var customizeURL = "chrome://global/content/customizeToolbar.xul";
   gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false);
 
   if (gCustomizeSheet) {
     var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
     var panel = document.getElementById("customizeToolbarSheetPopup");
     sheetFrame.hidden = false;
     sheetFrame.toolbox = gNavToolbox;
@@ -3493,22 +3487,16 @@ function BrowserToolboxCustomizeDone(aTo
 #ifndef XP_MACOSX
     updateEditUIVisibility();
 #endif
   }
 
   PlacesToolbarHelper.customizeDone();
   BookmarksMenuButton.customizeDone();
 
-  let addonBar = document.getElementById("addon-bar");
-  if (addonBar.wasCollapsed === true) {
-    addonBar.collapsed = true;
-    delete addonBar.wasCollapsed;
-  }
-
   // The url bar splitter state is dependent on whether stop/reload
   // and the location bar are combined, so we need this ordering
   CombinedStopReload.init();
   UpdateUrlbarSearchSplitterState();
 
   // Update the urlbar
   if (gURLBar) {
     URLBarSetURI();
@@ -4745,16 +4733,19 @@ function updateAppButtonDisplay() {
 
 #ifdef CAN_DRAW_IN_TITLEBAR
   document.getElementById("titlebar").hidden = !displayAppButton;
 
   if (displayAppButton)
     document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
   else
     document.documentElement.removeAttribute("chromemargin");
+#else
+  document.getElementById("appmenu-toolbar-button").hidden =
+    !displayAppButton;
 #endif
 }
 #endif
 
 #ifdef CAN_DRAW_IN_TITLEBAR
 function onTitlebarMaxClick() {
   if (window.windowState == window.STATE_MAXIMIZED)
     window.restore();
@@ -7433,16 +7424,23 @@ let DownloadMonitorPanel = {
 
 function getNotificationBox(aWindow) {
   var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
   if (foundBrowser)
     return gBrowser.getNotificationBox(foundBrowser)
   return null;
 };
 
+function getTabModalPromptBox(aWindow) {
+  var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
+  if (foundBrowser)
+    return gBrowser.getTabModalPromptBox(foundBrowser);
+  return null;
+};
+
 /* DEPRECATED */
 function getBrowser() gBrowser;
 function getNavToolbox() gNavToolbox;
 
 let gPrivateBrowsingUI = {
   _privateBrowsingService: null,
   _searchBarValue: null,
   _findBarValue: null,
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -759,26 +759,47 @@
                          tooltip="bhTooltip" popupsinherittooltip="true"
                          context="placesContext"/>
             </toolbarbutton>
           </hbox>
         </hbox>
       </toolbaritem>
     </toolbar>
 
+#ifdef MENUBAR_CAN_AUTOHIDE
+#ifndef CAN_DRAW_IN_TITLEBAR
+#define APPMENU_ON_TABBAR
+#endif
+#endif
+
+
     <toolbar id="TabsToolbar"
              fullscreentoolbar="true"
              customizable="true"
              mode="icons" lockmode="true"
              iconsize="small" defaulticonsize="small" lockiconsize="true"
              aria-label="&tabsToolbar.label;"
              context="toolbar-context-menu"
+#ifdef APPMENU_ON_TABBAR
+             defaultset="appmenu-toolbar-button,tabbrowser-tabs,new-tab-button,alltabs-button,tabview-button,tabs-closebutton"
+#else
              defaultset="tabbrowser-tabs,new-tab-button,alltabs-button,tabview-button,tabs-closebutton"
+#endif
              collapsed="true">
 
+#ifdef APPMENU_ON_TABBAR
+      <toolbarbutton id="appmenu-toolbar-button"
+                     class="chromeclass-toolbar-additional"
+                     type="menu"
+                     label="&brandShortName;"
+                     tooltiptext="&appMenuButton.tooltip;">
+#include browser-appmenu.inc
+      </toolbarbutton>
+#endif
+
       <tabs id="tabbrowser-tabs"
             class="tabbrowser-tabs"
             tabbrowser="content"
             flex="1"
             setfocus="false"
             tooltip="tabbrowser-tab-tooltip">
         <tab class="tabbrowser-tab" selected="true" fadein="true"/>
       </tabs>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -285,16 +285,68 @@
         <parameter name="aBrowser"/>
         <body>
           <![CDATA[
             return (aBrowser || this.mCurrentBrowser).parentNode.parentNode;
           ]]>
         </body>
       </method>
 
+      <method name="getTabModalPromptBox">
+        <parameter name="aBrowser"/>
+        <body>
+          <![CDATA[
+            const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+            let browser = (aBrowser || this.mCurrentBrowser);
+            let stack = browser.parentNode;
+            let self = this;
+
+            let promptBox = {
+              appendPrompt : function(args, onCloseCallback) {
+                let count = browser.getAttribute("tabmodalPromptShowing");
+                if (count)
+                    count = parseInt(count) + 1;
+                else
+                    count = 1;
+                browser.setAttribute("tabmodalPromptShowing", count);
+
+                let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
+                stack.appendChild(newPrompt);
+                newPrompt.clientTop; // style flush to assure binding is attached
+
+                let tab = self._getTabForContentWindow(browser.contentWindow);
+                newPrompt.init(args, tab, onCloseCallback);
+                return newPrompt;
+              },
+
+              removePrompt : function(aPrompt) {
+                let count = parseInt(browser.getAttribute("tabmodalPromptShowing"));
+                count--;
+                if (count)
+                    browser.setAttribute("tabmodalPromptShowing", count);
+                else
+                    browser.removeAttribute("tabmodalPromptShowing");
+                stack.removeChild(aPrompt);
+              },
+
+              listPrompts : function(aPrompt) {
+                let prompts = [];
+                let els = stack.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
+                // NodeList --> real JS array
+                for (let i = 0; i < els.length; i++)
+                  prompts.push(els[i]);
+                return prompts;
+              },
+            };
+
+            return promptBox;
+          ]]>
+        </body>
+      </method>
+
       <method name="_callProgressListeners">
         <parameter name="aBrowser"/>
         <parameter name="aMethod"/>
         <parameter name="aArguments"/>
         <parameter name="aCallGlobalListeners"/>
         <parameter name="aCallTabsListeners"/>
         <body><![CDATA[
           var rv = true;
@@ -2725,22 +2777,30 @@
 
           if (pinnedOnly)
             this.tabbrowser.tabContainer.setAttribute("pinnedonly", "true");
           else
             this.tabbrowser.tabContainer.removeAttribute("pinnedonly");
 
           var scrollButtonWidth = (this.getAttribute("overflow") != "true" || pinnedOnly) ? 0 :
                                   this.mTabstrip._scrollButtonDown.scrollWidth;
+          var paddingStart = this.mTabstrip.scrollboxPaddingStart;
+
           for (var i = this.tabbrowser._numPinnedTabs - 1; i >= 0; i--) {
             let tab = this.childNodes[i];
             width += pinnedOnly ? 0 : tab.scrollWidth;
-            tab.style.MozMarginStart = - (width + scrollButtonWidth) + "px";
+            if (this.getAttribute("overflow") != "true")
+              tab.style.MozMarginStart = - (width + scrollButtonWidth) + "px";
+            else
+              tab.style.MozMarginStart = - (width + scrollButtonWidth + paddingStart) + "px";
           }
-          this.style.MozMarginStart = width + "px";
+          if (width == 0 || this.getAttribute("overflow") != "true")
+            this.style.MozMarginStart = width + "px";
+          else
+            this.style.MozMarginStart = width + paddingStart + "px";
           this.mTabstrip.ensureElementIsVisible(this.selectedItem, false);
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           switch (aEvent.type) {
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -423,17 +423,21 @@ GroupItem.prototype = Utils.extend(new I
   // Function: getContentBounds
   // Returns a <Rect> for the groupItem's content area (which doesn't include the title, etc).
   getContentBounds: function GroupItem_getContentBounds() {
     var box = this.getBounds();
     var titleHeight = this.$titlebar.height();
     box.top += titleHeight;
     box.height -= titleHeight;
 
-    box.width -= this.$appTabTray.width();
+    var appTabTrayWidth = this.$appTabTray.width();
+    box.width -= appTabTrayWidth;
+    if (UI.rtl) {
+      box.left += appTabTrayWidth;
+    }
 
     // Make the computed bounds' "padding" and new tab button margin actually be
     // themeable --OR-- compute this from actual bounds. Bug 586546
     box.inset(6, 6);
     box.height -= 33; // For new tab button
 
     return box;
   },
--- a/browser/base/content/test/browser_bug599325.js
+++ b/browser/base/content/test/browser_bug599325.js
@@ -1,56 +1,37 @@
 function test() {
   waitForExplicitFinish();
-  
-  // test the main (normal) browser window
-  testCustomize(window, testChromeless);
-}
 
-function testChromeless() {
-  // test a chromeless window
-  var newWin = openDialog("chrome://browser/content/", "_blank",
-                      "chrome,dialog=no,toolbar=no", "about:blank");
-  ok(newWin, "got new window");
-
-  function runWindowTest() {
-    function finalize() {
-      newWin.removeEventListener("load", runWindowTest, false);
-      newWin.close();
-      finish();
-    }
-    testCustomize(newWin, finalize);
-  }
-
-  newWin.addEventListener("load", runWindowTest, false);
+  testCustomize(window, finish);
 }
 
 function testCustomize(aWindow, aCallback) {
   var addonBar = aWindow.document.getElementById("addon-bar");
   ok(addonBar, "got addon bar");
-  is(addonBar.collapsed, true, "addon bar initially disabled");
+  ok(!isElementVisible(addonBar), "addon bar initially hidden");
 
   // Launch toolbar customization
   // ctEl is either iframe that contains the customize sheet, or the dialog
   var ctEl = aWindow.BrowserCustomizeToolbar();
 
-  is(addonBar.collapsed, false,
-     "file menu is not collapsed during toolbar customization");
-
   aWindow.gNavToolbox.addEventListener("beforecustomization", function () {
     aWindow.gNavToolbox.removeEventListener("beforecustomization", arguments.callee, false);
     executeSoon(ctInit);
   }, false);
 
   function ctInit() {
+    ok(isElementVisible(addonBar),
+       "add-on bar is visible during toolbar customization");
+
     // Close toolbar customization
     closeToolbarCustomization(aWindow, ctEl);
 
-    is(addonBar.getAttribute("collapsed"), "true",
-       "addon bar is collapsed after toolbar customization");
+    ok(!isElementVisible(addonBar),
+       "addon bar is hidden after toolbar customization");
 
     if (aCallback)
       aCallback();
   }
 }
 
 function closeToolbarCustomization(aWindow, aCTWindow) {
   // Force the cleanup code to be run now instead of onunload.
--- a/browser/base/content/test/browser_overflowScroll.js
+++ b/browser/base/content/test/browser_overflowScroll.js
@@ -41,39 +41,43 @@ function runOverflowTests(aEvent) {
 
   tabstrip.removeEventListener("overflow", runOverflowTests, false);
 
   var upButton = tabstrip._scrollButtonUp;
   var downButton = tabstrip._scrollButtonDown;
   var element;
 
   gBrowser.selectedTab = firstScrollable();
-  isLeft(firstScrollable(), "Selecting the first tab scrolls it into view");
+  ok(left(scrollbox) <= left(firstScrollable()), "Selecting the first tab scrolls it into view " +
+     "(" + left(scrollbox) + " <= " + left(firstScrollable()) + ")");
 
   element = nextRightElement();
   EventUtils.synthesizeMouse(downButton, 1, 1, {});
   isRight(element, "Scrolled one tab to the right with a single click");
 
   gBrowser.selectedTab = tabs[tabs.length - 1];
-  isRight(gBrowser.selectedTab, "Selecting the last tab scrolls it into view");
+  ok(right(gBrowser.selectedTab) <= right(scrollbox), "Selecting the last tab scrolls it into view " +
+     "(" + right(gBrowser.selectedTab) + " <= " + right(scrollbox) + ")");
 
   element = nextLeftElement();
   EventUtils.synthesizeMouse(upButton, 1, 1, {});
   isLeft(element, "Scrolled one tab to the left with a single click");
 
   element = elementFromPoint(left(scrollbox) - width(scrollbox));
   EventUtils.synthesizeMouse(upButton, 1, 1, {clickCount: 2});
   isLeft(element, "Scrolled one page of tabs with a double click");
 
   EventUtils.synthesizeMouse(upButton, 1, 1, {clickCount: 3});
-  isLeft(firstScrollable(), "Scrolled to the start with a triple click");
+  var firstScrollableLeft = left(firstScrollable());
+  ok(left(scrollbox) <= firstScrollableLeft, "Scrolled to the start with a triple click " +
+     "(" + left(scrollbox) + " <= " + firstScrollableLeft + ")");
 
   for (var i = 2; i; i--)
     EventUtils.synthesizeMouseScroll(scrollbox, 1, 1, {axis: "horizontal", delta: -1});
-  isLeft(firstScrollable(), "Remained at the start with the mouse wheel");
+  is(left(firstScrollable()), firstScrollableLeft, "Remained at the start with the mouse wheel");
 
   element = nextRightElement();
   EventUtils.synthesizeMouseScroll(scrollbox, 1, 1, {axis: "horizontal", delta: 1});
   isRight(element, "Scrolled one tab to the right with the mouse wheel");
 
   while (tabs.length > 1)
     gBrowser.removeTab(tabs[0]);
 
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -125,34 +125,21 @@
                                                 "anonid", "textbox-input-box");
         var cxmenu = document.getAnonymousElementByAttribute(textBox,
                                             "anonid", "input-box-contextmenu");
         var pasteAndGo;
         cxmenu.addEventListener("popupshowing", function() {
           if (!pasteAndGo)
             return;
           var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
-          var couldBeURL = controller.isCommandEnabled("cmd_paste");
-          if (couldBeURL) {
-            let cbSvc = Cc["@mozilla.org/widget/clipboard;1"].
-                          getService(Ci.nsIClipboard);
-            let xferable = Cc["@mozilla.org/widget/transferable;1"].
-                            createInstance(Ci.nsITransferable);
-            xferable.addDataFlavor("text/unicode");
-            cbSvc.getData(xferable, cbSvc.kGlobalClipboard);
-            let data = {};
-            xferable.getTransferData("text/unicode", data, {});
-            data = data.value.QueryInterface(Ci.nsISupportsString).data;
-            try {
-              makeURI(data);
-            } catch (ex) { // clipboard data is not a URL
-              couldBeURL = false;
-            }
-          }
-          pasteAndGo.hidden = !couldBeURL;
+          var enabled = controller.isCommandEnabled("cmd_paste");
+          if (enabled)
+            pasteAndGo.removeAttribute("disabled");
+          else
+            pasteAndGo.setAttribute("disabled", "true");
         }, false);
 
         var insertLocation = cxmenu.firstChild;
         while (insertLocation.nextSibling &&
                insertLocation.getAttribute("cmd") != "cmd_paste")
           insertLocation = insertLocation.nextSibling;
         if (insertLocation) {
           pasteAndGo = document.createElement("menuitem");
@@ -623,81 +610,103 @@
                                                 "over-link-path-label");
       ]]></field>
 
       <field name="_textboxContainer" readonly="true"><![CDATA[
         document.getAnonymousElementByAttribute(this, "anonid",
                                                 "textbox-container");
       ]]></field>
 
-      <field name="_overLinkInDelay" readonly="true"><![CDATA[
+      <field name="_overLinkIntervalDelay" readonly="true"><![CDATA[
         100
       ]]></field>
 
-      <field name="_overLinkOutDelay" readonly="true"><![CDATA[
-        100
-      ]]></field>
-
-      <field name="_overLinkDelayTimer"><![CDATA[
+      <field name="_overLinkInterval"><![CDATA[
         null
       ]]></field>
 
       <method name="setOverLink">
         <parameter name="aURL"/>
         <body><![CDATA[
-          this._cancelOverLinkDelayTimer();
+          // NOTE: This method is called many times in a row very quickly when
+          // the user mouses over a bookmarks menu, tabs menu, or long list of
+          // links in a page, or leaves the cursor over a page with many links
+          // while scrolling.  Therefore it's important that it be fast.  Don't
+          // regress performance when you modify it!
 
           // Hide the over-link immediately if necessary.
-          if (!aURL && (XULBrowserWindow.hideOverLinkImmediately ||
-                        this._hideOverLinkImmediately)) {
+          if ((!aURL && (XULBrowserWindow.hideOverLinkImmediately ||
+                         this._hideOverLinkImmediately)) ||
+              this.focused) {
+            this._clearOverLinkInterval();
             this._setOverLinkState(null);
             return;
           }
 
-          // If aURL is falsey, fade it out after a delay.  This happens on
-          // mouseout for example.
-          if (!aURL) {
-            this._overLinkDelayTimer = setTimeout(function overLinkOut(self) {
-              self._overLinkDelayTimer = null;
-              self._setOverLinkState("fade-out");
-            }, this._overLinkOutDelay, this);
-            return;
-          }
-
-          // If it's in transition, update and show it immediately.
-          if (this._overLinkTransitioning) {
+          // Update and show it immediately if it's in transition.
+          if (aURL && this._overLinkTransitioning) {
+            this._clearOverLinkInterval();
             this._updateOverLink(aURL);
             this._setOverLinkState("showing");
             return;
           }
 
-          // Delay the update if it's hidden or shown.  We delay when shown in
-          // part for performance reasons: otherwise mousing over the bookmarks
-          // menu for example is very laggy.
-          this._overLinkDelayTimer = setTimeout(function overLinkIn(self) {
-            self._overLinkDelayTimer = null;
-            self._updateOverLink(aURL);
+          this._overLinkURL = aURL;
+          this._seenNewOverLink = true;
+
+          // If the user is continually mousing over links, make this method
+          // basically a no-op.
+          if (this._overLinkInterval)
+            return;
+
+          // Otherwise, the user has just moused over or focused a single link.
+          // Start the interval and signal that we should potentially show the
+          // over-link the first time it fires by unsetting _seenNewOverLink.
+          this._seenNewOverLink = false;
+          this._overLinkInterval = setInterval(this._overLinkIntervalCallback,
+                                               this._overLinkIntervalDelay,
+                                               this);
+        ]]></body>
+      </method>
+
+      <method name="_overLinkIntervalCallback">
+        <parameter name="self"/>
+        <body><![CDATA[
+          // If the user is still mousing over links, bail out early.
+          if (self._seenNewOverLink) {
+            self._seenNewOverLink = false;
+            return;
+          }
+
+          // Otherwise, the user has stopped over a link.  Clear the interval
+          // and update the over-link.
+          self._clearOverLinkInterval();
+          if (self._overLinkURL) {
+            self._updateOverLink(self._overLinkURL);
             self._setOverLinkState("fade-in");
-          }, this._overLinkInDelay, this);
+          }
+          else {
+            self._setOverLinkState("fade-out");
+          }
         ]]></body>
       </method>
 
       <method name="_hideOverLink">
         <body><![CDATA[
           this._hideOverLinkImmediately = true;
           this.setOverLink("");
           this._hideOverLinkImmediately = false;
         ]]></body>
       </method>
 
-      <method name="_cancelOverLinkDelayTimer">
+      <method name="_clearOverLinkInterval">
         <body><![CDATA[
-          if (this._overLinkDelayTimer) {
-            clearTimeout(this._overLinkDelayTimer);
-            this._overLinkDelayTimer = null;
+          if (this._overLinkInterval) {
+            clearInterval(this._overLinkInterval);
+            this._overLinkInterval = null;
           }
         ]]></body>
       </method>
 
       <method name="_setOverLinkState">
         <parameter name="aVal"/>
         <body><![CDATA[
           switch (aVal) {
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -2438,17 +2438,17 @@ SessionStoreService.prototype = {
           --unhiddenTabs;
           continue;
         }
         ++t;
       }
 
       // Determine if we can optimize & load visible tabs first
       let maxVisibleTabs = Math.ceil(tabbrowser.tabContainer.mTabstrip.scrollClientSize /
-                                     aTabs[unhiddenTabs - 1].clientWidth);
+                                     aTabs[unhiddenTabs - 1].getBoundingClientRect().width);
 
       // make sure we restore visible tabs first, if there are enough
       if (maxVisibleTabs < unhiddenTabs && aSelectTab > 1) {
         let firstVisibleTab = 0;
         if (unhiddenTabs - maxVisibleTabs > aSelectTab) {
           // aSelectTab is leftmost since we scroll to it when possible
           firstVisibleTab = aSelectTab - 1;
         } else {
--- a/browser/components/sessionstore/test/browser/browser_480148.js
+++ b/browser/components/sessionstore/test/browser/browser_480148.js
@@ -127,17 +127,19 @@ function test() {
           is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
           finish();
         }
       },
 
       handleLoad: function (aEvent) {
         let _this = this;
         executeSoon(function () {
-          _this.window.resizeTo(_this.windowWidth, _this.window.outerHeight);
+          let extent = _this.window.outerWidth - _this.window.gBrowser.tabContainer.mTabstrip.scrollClientSize;
+          let windowWidth = _this.tabbarWidth + extent;
+          _this.window.resizeTo(windowWidth, _this.window.outerHeight);
           ss.setWindowState(_this.window, JSON.stringify(_this.state), true);
         });
       },
 
       // Implement nsIDOMEventListener for handling various window and tab events
       handleEvent: function (aEvent) {
         switch (aEvent.type) {
           case "load":
@@ -146,17 +148,17 @@ function test() {
           case "SSTabRestoring":
             this.handleSSTabRestoring(aEvent);
             break;
         }
       },
 
       // setup and actually run the test
       run: function () {
-        this.windowWidth = Math.floor((this.numTabsToShow - 0.5) * tabMinWidth);
+        this.tabbarWidth = Math.floor((this.numTabsToShow - 0.5) * tabMinWidth);
         this.window = openDialog(location, "_blank", "chrome,all,dialog=no");
         this.window.addEventListener("SSTabRestoring", this, false);
         this.window.addEventListener("load", this, false);
       }
     };
     test.run();
   }
 
--- a/browser/locales/shipped-locales
+++ b/browser/locales/shipped-locales
@@ -18,16 +18,17 @@ es-AR
 es-ES
 et
 eu
 fi
 fr
 fy-NL
 ga-IE
 gd
+gu-IN
 he
 hu
 hy-AM
 id
 is
 it
 ja linux win32
 ja-JP-mac osx
@@ -36,23 +37,26 @@ ku
 lg
 lt
 lv
 mk
 nb-NO
 nl
 nn-NO
 nso
+or
 pa-IN
 pl
 pt-BR
 pt-PT
 rm
 ro
 ru
+si
 sk
 son
 sq
 sv-SE
+th
 tr
 uk
 zh-CN
 zh-TW
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -232,68 +232,77 @@ menuitem.bookmark-item {
   list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
 }
 
 /* Stock icons for the menu bar items */
 menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
   -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
 }
 
+#appmenu_newNavigator,
 #placesContext_open\:newwindow,
 #menu_newNavigator,
 #context-openlink,
 #context-openframe {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 80px 16px 64px);
 }
 
+#appmenu_newTab,
+#appmenu_newTab_popup,
 #placesContext_open\:newtab,
 #placesContext_openContainer\:tabs,
 #menu_newNavigatorTab,
 #context-openlinkintab,
 #context-openframeintab {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 64px 16px 48px);
 }
 
+#appmenu_openFile,
 #menu_openFile {
   list-style-image: url("moz-icon://stock/gtk-open?size=menu");
 }
 
 #menu_close {
   list-style-image: url("moz-icon://stock/gtk-close?size=menu");
 }
 
 #context-media-play {
   list-style-image: url("moz-icon://stock/gtk-media-play?size=menu");
 }
 
 #context-media-pause {
   list-style-image: url("moz-icon://stock/gtk-media-pause?size=menu");
 }
 
+#appmenu_savePage,
 #menu_savePage,
 #context-savelink,
 #context-saveimage,
 #context-savevideo,
 #context-saveaudio,
 #context-savepage,
 #context-saveframe {
   list-style-image: url("moz-icon://stock/gtk-save-as?size=menu");
 }
 
+#appmenu_printPreview,
 #menu_printPreview {
   list-style-image: url("moz-icon://stock/gtk-print-preview?size=menu");
 }
 
+#appmenu_print,
+#appmenu_print_popup,
 #menu_print,
 #context-printframe {
   list-style-image: url("moz-icon://stock/gtk-print?size=menu");
 }
 
+#appmenu-quit,
 #menu_FileQuitItem {
   list-style-image: url("moz-icon://stock/gtk-quit?size=menu");
 }
 
 #menu_undo,
 #context-undo {
   list-style-image: url("moz-icon://stock/gtk-undo?size=menu");
 }
@@ -366,24 +375,27 @@ menuitem:not([type]):not(.menuitem-toolt
   list-style-image: url("moz-icon://stock/gtk-delete?size=menu&state=disabled");
 }
 
 #menu_selectAll,
 #context-selectall {
   list-style-image: url("moz-icon://stock/gtk-select-all?size=menu");
 }
 
+#appmenu_find,
 #menu_find {
   list-style-image: url("moz-icon://stock/gtk-find?size=menu");
 }
 
 #menu_find[disabled] {
   list-style-image: url("moz-icon://stock/gtk-find?size=menu&state=disabled");
 }
 
+#appmenu_customize,
+#appmenu_preferences,
 #menu_preferences {
   list-style-image: url("moz-icon://stock/gtk-preferences?size=menu");
 }
 
 #menu_stop,
 #context-stop {
   list-style-image: url("moz-icon://stock/gtk-stop?size=menu");
 }
@@ -457,21 +469,25 @@ menuitem:not([type]):not(.menuitem-toolt
 #context-forward[disabled]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu&state=disabled");
 }
 
 #historyMenuHome {
   list-style-image: url("moz-icon://stock/gtk-home?size=menu");
 }
 
+#appmenu_history,
+#appmenu_showAllHistory,
 #menu_showAllHistory {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 32px 16px 16px);
 }
 
+#appmenu_bookmarks,
+#appmenu_showAllBookmarks,
 #bookmarksShowAll {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 48px 16px 32px);
 }
 
 #subscribeToPageMenuitem:not([disabled]),
 #subscribeToPageMenupopup,
 #BMB_subscribeToPageMenuitem:not([disabled]),
@@ -487,39 +503,44 @@ menuitem:not([type]):not(.menuitem-toolt
 #BMB_bookmarkThisPage {
   list-style-image: url("chrome://browser/skin/places/starPage.png");
 }
 
 #BMB_unsortedBookmarks {
   list-style-image: url("chrome://browser/skin/places/unsortedBookmarks.png");
 }
 
+#appmenu_downloads,
 #menu_openDownloads {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 16px 16px 0px);
 }
 
 #menu_pageInfo,
 #context-viewinfo,
 #context-viewframeinfo {
   list-style-image: url("moz-icon://stock/gtk-info?size=menu");
 }
 
 #placesContext_show\:info {
   list-style-image: url("moz-icon://stock/gtk-properties?size=menu");
 }
 
+#appmenu_sanitizeHistory,
 #sanitizeItem {
   list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
 }
 
+#appmenu_help,
+#appmenu_openHelp,
 #menu_openHelp {
   list-style-image: url("moz-icon://stock/gtk-help?size=menu");
 }
 
+#appmenu_about,
 #aboutName {
   list-style-image: url("moz-icon://stock/gtk-about?size=menu");
 }
 
 #javascriptConsole {
   list-style-image: url("chrome://global/skin/console/console.png");
 }
 
@@ -1688,16 +1709,42 @@ toolbar[mode="text"] toolbarbutton.chevr
   -moz-margin-start: 36px;
   -moz-margin-end: 0;
 }
 
 .allTabs-preview-label {
   -moz-transform: translate(0, 2px);
 }
 
+/* Application menu toolbar button */
+
+#appmenu-toolbar-button > .toolbarbutton-text {
+  margin-top: -2px !important;
+  margin-bottom: -2px !important;
+}
+#appmenuSecondaryPane {
+  -moz-border-start: 1px solid ThreeDShadow;
+}
+#appmenuSecondaryPane-spacer {
+  min-height: 1em;
+}
+#appmenu-cut {
+  list-style-image: url("moz-icon://stock/gtk-cut?size=menu");
+}
+#appmenu-copy {
+  list-style-image: url("moz-icon://stock/gtk-copy?size=menu");
+}
+#appmenu-paste {
+  list-style-image: url("moz-icon://stock/gtk-paste?size=menu");
+}
+#wrapper-appmenu-toolbar-button,
+.appmenu-edit-button[disabled="true"] {
+  opacity: .3;
+}
+
 /* Inspector / Highlighter */
 
 #highlighter-panel {
   -moz-appearance: none;
   -moz-window-shadow: none;
   background: -moz-linear-gradient(top -1deg, #ffdd88, #ffeeaa);
   border: none;
   opacity: 0.35;
@@ -1726,8 +1773,12 @@ panel[dimmed="true"] {
 }
 
 /* Remove all borders from statusbarpanel children of
    the statusbar. */
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
+
+browser[tabmodalPromptShowing] {
+  filter: url("chrome://browser/skin/effects.svg#blurAndDesaturate");
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/gnomestripe/browser/effects.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+# ***** 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 effects.svg.
+#
+# The Initial Developer of the Original Code is the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Justin Dolske <dolske@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+         "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0">
+    <filter id="blurAndDesaturate">
+        <feGaussianBlur stdDeviation="2"/>
+        <feColorMatrix type="saturate" values="0.7"/>
+    </filter>
+</svg>
--- a/browser/themes/gnomestripe/browser/jar.mn
+++ b/browser/themes/gnomestripe/browser/jar.mn
@@ -7,16 +7,17 @@ browser.jar:
 * skin/classic/browser/aboutSessionRestore.css        (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutCertError.css             (aboutCertError.css)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                    (browser.css)
+* skin/classic/browser/effects.svg                    (effects.svg)
 * skin/classic/browser/engineManager.css              (engineManager.css)
   skin/classic/browser/fullscreen-video.css
   skin/classic/browser/inspector.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Go-arrow.png
   skin/classic/browser/identity.png
   skin/classic/browser/Info.png
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -2226,8 +2226,12 @@ panel[dimmed="true"] {
 }
 
 /* Remove all borders from statusbarpanel children of
    the statusbar. */
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
+
+browser[tabmodalPromptShowing] {
+  filter: url("chrome://browser/skin/effects.svg#blurAndDesaturate");
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/pinstripe/browser/effects.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+# ***** 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 effects.svg.
+#
+# The Initial Developer of the Original Code is the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Justin Dolske <dolske@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+         "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0">
+    <filter id="blurAndDesaturate">
+        <feGaussianBlur stdDeviation="2"/>
+        <feColorMatrix type="saturate" values="0.7"/>
+    </filter>
+</svg>
--- a/browser/themes/pinstripe/browser/jar.mn
+++ b/browser/themes/pinstripe/browser/jar.mn
@@ -6,16 +6,17 @@ browser.jar:
 * skin/classic/browser/aboutSessionRestore.css              (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutCertError.css                   (aboutCertError.css)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                          (browser.css)
+* skin/classic/browser/effects.svg                          (effects.svg)
 * skin/classic/browser/engineManager.css                    (engineManager.css)
   skin/classic/browser/fullscreen-video.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Go-arrow.png
   skin/classic/browser/home.png
   skin/classic/browser/hud-panel.png
   skin/classic/browser/hud-style-button-middle-background.png
--- a/browser/themes/winstripe/browser/browser-aero.css
+++ b/browser/themes/winstripe/browser/browser-aero.css
@@ -1,14 +1,14 @@
 %define WINSTRIPE_AERO
 %include browser.css
 %undef WINSTRIPE_AERO
 
 %define customToolbarColor hsl(214,44%,87%)
-%define glassToolbarBorderColor rgb(40%,40%,40%)
+%define glassToolbarBorderColor rgba(10%,10%,10%,.4)
 %define glassActiveBorderColor rgb(37, 44, 51)
 %define glassInactiveBorderColor rgb(102, 102, 102)
 
 @media all and (-moz-windows-default-theme) {
   #navigator-toolbox > toolbar:not(:-moz-lwtheme) {
     background-color: @customToolbarColor@;
   }
 
@@ -125,16 +125,17 @@
 
   #main-window[sizemode="normal"]:not([inFullscreen="true"]) #navigator-toolbox[tabsontop="true"] > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(:-moz-lwtheme) {
     border-left: 1px solid @glassToolbarBorderColor@;
     border-right: 1px solid @glassToolbarBorderColor@;
   }
   #navigator-toolbox,
   #navigator-toolbox > toolbar {
     border-color: @glassToolbarBorderColor@ !important;
+    background-clip: padding-box;
   }
 
   #main-window[sizemode="normal"]:not([inFullscreen="true"]) #navigator-toolbox[tabsontop="true"] > #nav-bar:not(:-moz-lwtheme),
   #main-window[sizemode="normal"]:not([inFullscreen="true"]) #navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + toolbar:not(:-moz-lwtheme),
   #main-window[sizemode="normal"]:not([inFullscreen="true"]) #navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme),
   #main-window[sizemode="normal"]:not([inFullscreen="true"]) #navigator-toolbox:not([tabsontop="true"]) > #PersonalToolbar:not(:-moz-lwtheme) {
     border-top-left-radius: 3.5px;
     border-top-right-radius: 3.5px;
@@ -184,17 +185,18 @@
   #urlbar[focused="true"],
   .searchbar-textbox[focused="true"] {
     background-color: white;
   }
 
   .tabbrowser-tab:not(:-moz-lwtheme):not([selected="true"]),
   .tabs-newtab-button:not(:-moz-lwtheme) {
     background-image: -moz-linear-gradient(hsla(214,15%,80%,.8), hsla(214,15%,65%,.8) 50%);
-    text-shadow: 0 1px 0 rgba(255,255,255,.4);
+    text-shadow: none;
+    color: black;
   }
 
   .tabbrowser-tab:not(:-moz-lwtheme):not([selected="true"]):hover,
   .tabs-newtab-button:not(:-moz-lwtheme):hover {
     background-image: -moz-linear-gradient(hsla(214,15%,90%,.8), hsla(214,15%,75%,.8) 50%);
   }
 
   #allTabs-panel,
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -189,16 +189,20 @@
   background: transparent;
   border-radius: 3px;
 }
 
 .appmenu-edit-button[disabled="true"] {
   opacity: .3;
 }
 
+#appmenuPrimaryPane {
+  -moz-border-end: 1px solid ThreeDShadow;
+}
+
 @media all and (-moz-windows-default-theme) {
   #appmenu-popup {
     -moz-appearance: none;
     background: white;
     border: 1px solid rgba(0,0,0,.5);
   }
   #appmenuPrimaryPane {
     -moz-margin-end: -1px;
@@ -650,17 +654,18 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 
 #forward-button {
   -moz-image-region: rect(0, 36px, 18px, 18px);
   border-left: none;
   -moz-margin-start: 0;
 }
 
 #back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
-#forward-button:-moz-locale-dir(rtl) {
+#forward-button:-moz-locale-dir(rtl),
+#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-text {
   -moz-transform: scaleX(-1);
 }
 
 #back-button:-moz-locale-dir(ltr) {
   border-top-right-radius: 0;
   border-bottom-right-radius: 0;
 }
 
@@ -2111,8 +2116,12 @@ panel[dimmed="true"] {
 }
 
 /* Remove all borders from statusbarpanel children of
    the statusbar. */
 #status-bar > statusbarpanel {
   border-width: 0;
   -moz-appearance: none;
 }
+
+browser[tabmodalPromptShowing] {
+  filter: url("chrome://browser/skin/effects.svg#blurAndDesaturate");
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/winstripe/browser/effects.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+# ***** 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 effects.svg.
+#
+# The Initial Developer of the Original Code is the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Justin Dolske <dolske@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+         "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0">
+    <filter id="blurAndDesaturate">
+        <feGaussianBlur stdDeviation="2"/>
+        <feColorMatrix type="saturate" values="0.7"/>
+    </filter>
+</svg>
--- a/browser/themes/winstripe/browser/jar.mn
+++ b/browser/themes/winstripe/browser/jar.mn
@@ -11,16 +11,17 @@ browser.jar:
         skin/classic/browser/aboutCertError.css                      (aboutCertError.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/aboutSyncTabs.css
 #endif
         skin/classic/browser/actionicon-tab.png
         skin/classic/browser/appmenu-icons.png
         skin/classic/browser/appmenu-dropmarker.png
 *       skin/classic/browser/browser.css                             (browser.css)
+*       skin/classic/browser/effects.svg                             (effects.svg)
 *       skin/classic/browser/engineManager.css                       (engineManager.css)
         skin/classic/browser/fullscreen-video.css
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png                                (Info.png)
         skin/classic/browser/identity.png                            (identity.png)
         skin/classic/browser/keyhole-forward-mask.svg
         skin/classic/browser/KUI-background.png
@@ -123,16 +124,17 @@ browser.jar:
         skin/classic/aero/browser/aboutCertError.css                 (aboutCertError.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
         skin/classic/aero/browser/actionicon-tab.png                 (actionicon-tab.png)
         skin/classic/aero/browser/appmenu-dropmarker.png
         skin/classic/aero/browser/appmenu-icons.png
 *       skin/classic/aero/browser/browser.css                        (browser-aero.css)
+*       skin/classic/aero/browser/effects.svg                        (effects.svg)
 *       skin/classic/aero/browser/engineManager.css                  (engineManager.css)
         skin/classic/aero/browser/fullscreen-video.css
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/keyhole-forward-mask.svg
         skin/classic/aero/browser/KUI-background.png
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -587,18 +587,16 @@ STATIC_LIBIDL = @STATIC_LIBIDL@
 
 MOZ_NATIVE_MAKEDEPEND	= @SYSTEM_MAKEDEPEND@
 
 export CL_INCLUDES_PREFIX = @CL_INCLUDES_PREFIX@
 
 MOZ_AUTO_DEPS	= @MOZ_AUTO_DEPS@
 COMPILER_DEPEND = @COMPILER_DEPEND@
 MDDEPDIR        := @MDDEPDIR@
-CC_WRAPPER = @CC_WRAPPER@
-CXX_WRAPPER = @CXX_WRAPPER@
 
 MOZ_DEMANGLE_SYMBOLS = @MOZ_DEMANGLE_SYMBOLS@
 
 # XXX - these need to be cleaned up and have real checks added -cls
 CM_BLDTYPE=dbg
 AWT_11=1
 OS_TARGET=@OS_TARGET@
 OS_ARCH=@OS_ARCH@
--- a/config/config.mk
+++ b/config/config.mk
@@ -165,16 +165,29 @@ JEMALLOC_LIBS = $(MKSHLIB_FORCE_ALL) $(c
 # If we are linking jemalloc into a program, we want the jemalloc symbols
 # to be exported
 ifneq (,$(SIMPLE_PROGRAMS)$(PROGRAM))
 JEMALLOC_LIBS += $(MOZ_JEMALLOC_STANDALONE_GLUE_LDOPTS)
 endif
 endif
 endif
 
+ifndef NO_CXX_WRAPPER
+ifdef _MSC_VER
+ifndef .PYMAKE
+CC_WRAPPER = $(PYTHON) -O $(topsrcdir)/build/cl.py
+CXX_WRAPPER = $(PYTHON) -O $(topsrcdir)/build/cl.py
+else
+PYCOMMANDPATH += $(topsrcdir)/build
+CC_WRAPPER = %cl InvokeClWithDependencyGeneration
+CXX_WRAPPER = %cl InvokeClWithDependencyGeneration
+endif # .PYMAKE
+endif # _MSC_VER
+endif # NO_CXX_WRAPPER
+
 CC := $(CC_WRAPPER) $(CC)
 CXX := $(CXX_WRAPPER) $(CXX)
 
 # determine debug-related options
 _DEBUG_CFLAGS :=
 _DEBUG_LDFLAGS :=
 
 ifdef MOZ_DEBUG
--- a/configure.in
+++ b/configure.in
@@ -647,82 +647,77 @@ case "$target" in
         _MSC_VER=${_CC_MAJOR_VERSION}${_CC_MINOR_VERSION}
 
         CXX_VERSION=`"${CXX}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
         _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | $AWK -F\. '{ print $1 }'`
 
         if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then
             AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.])
         fi
-        if test "$_CC_MAJOR_VERSION" = "13"; then
-            _CC_SUITE=7
-        elif test "$_CC_MAJOR_VERSION" = "14"; then
+
+        if test "$_CC_MAJOR_VERSION" = "14"; then
+            dnl Require VC8SP1 or newer.
+            dnl VC8 is 14.00.50727.42, VC8SP1 is 14.00.50727.762.
+            if test "$_CC_RELEASE" -lt 50727 -o \
+                    \( "$_CC_RELEASE" -eq 50727 -a "$_CC_BUILD" -lt 762 \); then
+              AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. You probably need to install Service Pack 1 of Visual Studio 2005. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
+            fi
+
             _CC_SUITE=8
             CXXFLAGS="$CXXFLAGS -Zc:wchar_t-"
-            dnl -DYNAMICBASE is only supported on VC8SP1 or newer,
-            dnl so be very specific here!
-            dnl VC8 is 14.00.50727.42, VC8SP1 is 14.00.50727.762
-            if test $_CC_RELEASE -gt 50727; then
-               _USE_DYNAMICBASE=1
-            elif test $_CC_BUILD -ge 762; then
-               _USE_DYNAMICBASE=1
-            fi
             AC_DEFINE(_CRT_SECURE_NO_DEPRECATE)
             AC_DEFINE(_CRT_NONSTDC_NO_DEPRECATE)
         elif test "$_CC_MAJOR_VERSION" = "15"; then
             _CC_SUITE=9
             CXXFLAGS="$CXXFLAGS -Zc:wchar_t-"
-            _USE_DYNAMICBASE=1
             AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
             AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
         elif test "$_CC_MAJOR_VERSION" = "16"; then
             _CC_SUITE=10
             CXXFLAGS="$CXXFLAGS -Zc:wchar_t-"
-            _USE_DYNAMICBASE=1
             AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
             AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
         else
-            AC_MSG_ERROR([This version of the MSVC compiler, $CC_VERSION , is unsupported.])
+            AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
         fi
 
         _MOZ_RTTI_FLAGS_ON='-GR'
         _MOZ_RTTI_FLAGS_OFF='-GR-'
         _MOZ_EXCEPTIONS_FLAGS_ON='-EHsc'
         _MOZ_EXCEPTIONS_FLAGS_OFF=''
 
         if test -n "$WIN32_REDIST_DIR"; then
             WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd`
         fi
-	
-        # bug #249782
-        # ensure that mt.exe is Microsoft (R) Manifest Tool and not magnetic tape manipulation utility (or something else)
-        if test "$_CC_SUITE" -ge "8"; then
-                changequote(,)
-                _MSMT_VER_FILTER='s|.* \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p'
-                changequote([,])
-
-                MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'`
-                if test -n "$MSMT_TOOL"; then
-                        MSMANIFEST_TOOL_VERSION=`echo ${MSMT_TOOL}|sed -ne "$_MSMT_VER_FILTER"`
-                        if test -z "$MSMANIFEST_TOOL_VERSION"; then
-                                AC_MSG_WARN([Unknown version of the Microsoft (R) Manifest Tool.])
-                        fi
-                        MSMANIFEST_TOOL=1
-                        unset MSMT_TOOL
-                else
-                        AC_MSG_ERROR([Microsoft (R) Manifest Tool must be in your \$PATH.])
-                fi
+
+        dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool',
+        dnl not something else like "magnetic tape manipulation utility".
+        MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'`
+        if test -z "$MSMT_TOOL"; then
+          AC_MSG_ERROR([Microsoft (R) Manifest Tool must be in your \$PATH.])
         fi
 
+        changequote(,)
+        _MSMT_VER_FILTER='s|.* \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p'
+        changequote([,])
+        MSMANIFEST_TOOL_VERSION=`echo ${MSMT_TOOL}|sed -ne "$_MSMT_VER_FILTER"`
+        if test -z "$MSMANIFEST_TOOL_VERSION"; then
+          AC_MSG_WARN([Unknown version of the Microsoft (R) Manifest Tool.])
+        fi
+
+        MSMANIFEST_TOOL=1
+        unset MSMT_TOOL
+
         # Check linker version
         _LD_FULL_VERSION=`"${LD}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
         _LD_MAJOR_VERSION=`echo ${_LD_FULL_VERSION} | $AWK -F\. '{ print $1 }'`
         if test "$_LD_MAJOR_VERSION" != "$_CC_SUITE"; then
             AC_MSG_ERROR([The linker major version, $_LD_FULL_VERSION,  does not match the compiler suite version, $_CC_SUITE.])
         fi
+
         INCREMENTAL_LINKER=1
 
         # Check midl version
         _MIDL_FULL_VERSION=`"${MIDL}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
         _MIDL_MAJOR_VERSION=`echo ${_MIDL_FULL_VERSION} | $AWK -F\. '{ print $1 }'`
         _MIDL_MINOR_VERSION=`echo ${_MIDL_FULL_VERSION} | $AWK -F\. '{ print $2 }'`
         _MIDL_REV_VERSION=`echo ${_MIDL_FULL_VERSION} | $AWK -F\. '{ print $3 }'`
         # Add flags if necessary
@@ -2425,19 +2420,17 @@ ia64*-hpux*)
             dnl of using WARNINGS_AS_ERRORS in some modules, combined
             dnl with the linker doing most of the work in the whole-program
             dnl optimization/PGO case. I think it's probably a compiler bug,
             dnl but we work around it here.
             PROFILE_USE_CFLAGS="-GL -wd4624 -wd4952"
             dnl XXX: should be -LTCG:PGOPTIMIZE, but that fails on libxul.
             dnl Probably also a compiler bug, but what can you do?
             PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE"
-            if test -n "$_USE_DYNAMICBASE"; then
-               LDFLAGS="$LDFLAGS -DYNAMICBASE"
-            fi
+            LDFLAGS="$LDFLAGS -DYNAMICBASE"
         fi
     fi
     MOZ_JPEG_LIBS='$(call EXPAND_LIBNAME_PATH,jpeg32$(VERSION_NUMBER),$(DEPTH)/jpeg)'
     MOZ_PNG_LIBS='$(call EXPAND_LIBNAME_PATH,png,$(DEPTH)/modules/libimg/png)'
     AC_DEFINE(HAVE_SNPRINTF)
     AC_DEFINE(_WINDOWS)
     AC_DEFINE(WIN32)
     AC_DEFINE(XP_WIN)
@@ -2488,17 +2481,17 @@ ia64*-hpux*)
     fi
     MOZ_TOOLS_DIR=`$CYGPATH_W $MOZ_TOOLS_DIR | $CYGPATH_S`
     ;;
     esac 
 
 
     case "$host_os" in
     cygwin*|msvc*|mks*)
-        AC_MSG_WARN([Using a cygwin build environment is unsupported. Configure cannot check for the presence of necessary headers. Please upgrade to MozillaBuild; see http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites])
+        AC_MSG_WARN([Using a cygwin build environment is unsupported. Configure cannot check for the presence of necessary headers. Please upgrade to MozillaBuild; see https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
         ;;
 
     *)
         AC_CHECK_HEADERS(oleacc.idl)
 
         AC_LANG_SAVE
         AC_LANG_CPLUSPLUS
         AC_CHECK_HEADERS(atlbase.h)
@@ -6491,38 +6484,28 @@ MOZ_ARG_DISABLE_BOOL(mathml,
 [  --disable-mathml        Disable MathML support],
     MOZ_MATHML=,
     MOZ_MATHML=1 )
 if test "$MOZ_MATHML"; then
   AC_DEFINE(MOZ_MATHML)
 fi
 
 dnl ========================================================
-dnl SVG
-dnl ========================================================
-MOZ_ARG_DISABLE_BOOL(svg,
-[  --disable-svg           Disable SVG support],
-    MOZ_SVG=,
-    MOZ_SVG=1 )
-if test -n "$MOZ_SVG"; then
-  AC_DEFINE(MOZ_SVG)
-fi
+dnl Keeping AC_DEFINE(MOZ_SVG) for the moment in case of something needs it.
+dnl ========================================================
+AC_DEFINE(MOZ_SVG)
 
 dnl ========================================================
 dnl SMIL
 dnl ========================================================
 MOZ_SMIL=1
 MOZ_ARG_DISABLE_BOOL(smil,
 [  --disable-smil          Disable SMIL animation support],
     MOZ_SMIL=,
     MOZ_SMIL=1 )
-# Automatically disable SMIL if SVG is disabled
-if test -z "$MOZ_SVG"; then
-  MOZ_SMIL=
-fi
 if test -n "$MOZ_SMIL"; then
   AC_DEFINE(MOZ_SMIL)
 fi
 
 dnl ========================================================
 dnl Build Freetype in the tree
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(tree-freetype,
@@ -7484,16 +7467,17 @@ MOZ_ARG_ENABLE_BOOL(wrap-malloc,
 [  --enable-wrap-malloc    Wrap malloc calls (gnu linker only)],
     _WRAP_MALLOC=1,
     _WRAP_MALLOC= )
 
 if test -n "$_WRAP_MALLOC"; then
     if test "$GNU_CC"; then
     WRAP_MALLOC_CFLAGS="${LDFLAGS} ${WRAP_MALLOC_CFLAGS} -Wl,--wrap -Wl,malloc -Wl,--wrap -Wl,calloc -Wl,--wrap -Wl,valloc -Wl,--wrap -Wl,free -Wl,--wrap -Wl,realloc -Wl,--wrap -Wl,memalign -Wl,--wrap -Wl,__builtin_new -Wl,--wrap -Wl,__builtin_vec_new -Wl,--wrap -Wl,__builtin_delete -Wl,--wrap -Wl,__builtin_vec_delete -Wl,--wrap -Wl,PR_Free -Wl,--wrap -Wl,PR_Malloc -Wl,--wrap -Wl,PR_Calloc -Wl,--wrap -Wl,PR_Realloc -Wl,--wrap -Wl,strdup -Wl,--wrap -Wl,strndup -Wl,--wrap -Wl,posix_memalign"
     MKSHLIB='$(CXX) $(DSO_LDOPTS) $(WRAP_MALLOC_CFLAGS) $(WRAP_MALLOC_LIB) -o $@'
+    MKCSHLIB='$(CC) $(DSO_LDOPTS) $(WRAP_MALLOC_CFLAGS) $(WRAP_MALLOC_LIB) -o $@'
     fi
 fi
 
 dnl ========================================================
 dnl = Location of malloc wrapper lib
 dnl ========================================================
 MOZ_ARG_WITH_STRING(wrap-malloc,
 [  --with-wrap-malloc=DIR  Location of malloc wrapper library],
@@ -8167,31 +8151,25 @@ else
     changequote(,)
     CL_INCLUDES_PREFIX=`"${CC}" -showIncludes -c -Fonul dummy-hello.c 2>&1 | sed -ne 's/^\([^:]*:[^:]*:\).*stdio.h$/\1/p'`
     changequote([,])
     if test -z "$CL_INCLUDES_PREFIX"; then
         AC_MSG_ERROR([Cannot find cl -showIncludes prefix.])
     fi
     AC_SUBST(CL_INCLUDES_PREFIX)
     rm -f dummy-hello.c
-    _topsrcdirwin=`cd \`dirname $0\`; pwd -W`
     dnl cl.py provides dependency generation for MSVC
-    CC_WRAPPER="$PYTHON -O $_topsrcdirwin/build/cl.py"
-    CXX_WRAPPER="$PYTHON -O $_topsrcdirwin/build/cl.py"
     COMPILER_DEPEND=1
   fi
 fi
 fi # MOZ_AUTO_DEPS
 MDDEPDIR='.deps'
 AC_SUBST(MOZ_AUTO_DEPS)
 AC_SUBST(COMPILER_DEPEND)
 AC_SUBST(MDDEPDIR)
-AC_SUBST(CC_WRAPPER)
-AC_SUBST(CXX_WRAPPER)
-
 
 dnl ========================================================
 dnl =
 dnl = Static Build Options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Static build options)
 
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1703,16 +1703,22 @@ public:
   /**
    * Flushes the layout tree (recursively)
    *
    * @param aWindow the window the flush should start at
    *
    */
   static void FlushLayoutForTree(nsIDOMWindow* aWindow);
 
+  /**
+   * Returns true if content with the given principal is allowed to use XUL
+   * and XBL and false otherwise.
+   */
+  static bool AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal);
+
 private:
 
   static PRBool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static nsIDOMScriptObjectFactory *GetDOMScriptObjectFactory();
 
@@ -1792,16 +1798,17 @@ private:
   static PRUint32 sRemovableScriptBlockerCount;
   static nsCOMArray<nsIRunnable>* sBlockedScriptRunners;
   static PRUint32 sRunnersCountAtFirstBlocker;
   static PRUint32 sScriptBlockerCountWhereRunnersPrevented;
 
   static nsIInterfaceRequestor* sSameOriginChecker;
 
   static PRBool sIsHandlingKeyBoardEvent;
+  static PRBool sAllowXULXBL_for_file;
 };
 
 #define NS_HOLD_JS_OBJECTS(obj, clazz)                                         \
   nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz),        \
                                 &NS_CYCLE_COLLECTION_NAME(clazz))
 
 #define NS_DROP_JS_OBJECTS(obj, clazz)                                         \
   nsContentUtils::DropJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz))
--- a/content/base/public/nsIFrameMessageManager.idl
+++ b/content/base/public/nsIFrameMessageManager.idl
@@ -93,16 +93,22 @@ interface nsIContentFrameMessageManager 
    * The top level docshell or null.
    */
   readonly attribute nsIDocShell docShell;
 
   /**
    * Print a string to stdout.
    */
   void dump(in DOMString aStr);
+
+  /**
+   * If leak detection is enabled, print a note to the leak log that this
+   * process will intentionally crash.
+   */
+  void privateNoteIntentionalCrash();
 };
 
 [uuid(9c48d557-92fe-4edb-95fc-bfe97e77bdc3)]
 interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
 {
   [notxpcom] nsIContent getOwnerContent();
 };
 
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -48,16 +48,17 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 const CSP_VIOLATION_TOPIC = "csp-on-violate-policy";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/CSPUtils.jsm");
 
 /* ::::: Policy Parsing & Data structures :::::: */
 
 function ContentSecurityPolicy() {
   CSPdebug("CSP CREATED");
   this._isInitialized = false;
   this._reportOnlyMode = false;
@@ -65,19 +66,16 @@ function ContentSecurityPolicy() {
 
   // default options "wide open" since this policy will be intersected soon
   this._policy._allowInlineScripts = true;
   this._policy._allowEval = true;
 
   this._requestHeaders = []; 
   this._request = "";
   CSPdebug("CSP POLICY INITED TO 'allow *'");
-
-  this._observerService = Cc['@mozilla.org/observer-service;1']
-                            .getService(Ci.nsIObserverService);
 }
 
 /*
  * Set up mappings from nsIContentPolicy content types to CSP directives.
  */
 {
   let cp = Ci.nsIContentPolicy;
   let csp = ContentSecurityPolicy;
@@ -126,45 +124,27 @@ ContentSecurityPolicy.prototype = {
 
   get policy () {
     return this._policy.toString();
   },
 
   get allowsInlineScript() {
     // trigger automatic report to go out when inline scripts are disabled.
     if (!this._policy.allowsInlineScripts) {
-      var violation = 'violated base restriction: Inline Scripts will not execute';
-      // gotta wrap the violation string, since it's sent out to observers as
-      // an nsISupports.
-      let wrapper = Cc["@mozilla.org/supports-cstring;1"]
-                      .createInstance(Ci.nsISupportsCString);
-      wrapper.data = violation;
-      this._observerService.notifyObservers(
-                              wrapper,
-                              CSP_VIOLATION_TOPIC,
-                              'inline script base restriction');
-      this.sendReports('self', violation);
+      this._asyncReportViolation('self','inline script base restriction',
+                                 'violated base restriction: Inline Scripts will not execute');
     }
     return this._reportOnlyMode || this._policy.allowsInlineScripts;
   },
 
   get allowsEval() {
     // trigger automatic report to go out when eval and friends are disabled.
     if (!this._policy.allowsEvalInScripts) {
-      var violation = 'violated base restriction: Code will not be created from strings';
-      // gotta wrap the violation string, since it's sent out to observers as
-      // an nsISupports.
-      let wrapper = Cc["@mozilla.org/supports-cstring;1"]
-                      .createInstance(Ci.nsISupportsCString);
-      wrapper.data = violation;
-      this._observerService.notifyObservers(
-                              wrapper,
-                              CSP_VIOLATION_TOPIC,
-                              'eval script base restriction');
-      this.sendReports('self', violation);
+      this._asyncReportViolation('self','eval script base restriction',
+                                 'violated base restriction: Code will not be created from strings');
     }
     return this._reportOnlyMode || this._policy.allowsEvalInScripts;
   },
 
   set reportOnlyMode(val) {
     this._reportOnlyMode = val;
   },
 
@@ -364,22 +344,19 @@ ContentSecurityPolicy.prototype = {
     for (let i in ancestors) {
       let ancestor = ancestors[i].prePath;
       if (!this._policy.permits(ancestor, cspContext)) {
         // report the frame-ancestor violation
         let directive = this._policy._directives[cspContext];
         let violatedPolicy = (directive._isImplicit
                                 ? 'allow' : 'frame-ancestors ')
                                 + directive.toString();
-        // send an nsIURI object to the observers (more interesting than a string)
-        this._observerService.notifyObservers(
-                                ancestors[i],
-                                CSP_VIOLATION_TOPIC, 
-                                violatedPolicy);
-        this.sendReports(ancestors[i].asciiSpec, violatedPolicy);
+
+        this._asyncReportViolation(ancestors[i], violatedPolicy);
+
         // need to lie if we are testing in report-only mode
         return this._reportOnlyMode;
       }
     }
     return true;
   },
 
   /**
@@ -422,21 +399,17 @@ ContentSecurityPolicy.prototype = {
     // If the result is *NOT* ACCEPT, then send report
     if (res != Ci.nsIContentPolicy.ACCEPT) { 
       CSPdebug("blocking request for " + aContentLocation.asciiSpec);
       try {
         let directive = this._policy._directives[cspContext];
         let violatedPolicy = (directive._isImplicit
                                 ? 'allow' : cspContext)
                                 + ' ' + directive.toString();
-        this._observerService.notifyObservers(
-                                aContentLocation,
-                                CSP_VIOLATION_TOPIC, 
-                                violatedPolicy);
-        this.sendReports(aContentLocation, violatedPolicy);
+        this._asyncReportViolation(aContentLocation, violatedPolicy);
       } catch(e) {
         CSPdebug('---------------- ERROR: ' + e);
       }
     }
 
     return (this._reportOnlyMode ? Ci.nsIContentPolicy.ACCEPT : res);
   },
   
@@ -448,11 +421,51 @@ ContentSecurityPolicy.prototype = {
                              aMimeType,
                              aExtra) {
     // frame-ancestors check is done outside the ContentPolicy
     var res = Ci.nsIContentPolicy.ACCEPT;
     CSPdebug("shouldProcess aContext=" + aContext);
     return res;
   },
 
+  /**
+   * Asynchronously notifies any nsIObservers listening to the CSP violation
+   * topic that a violation occurred.  Also triggers report sending.  All
+   * asynchronous on the main thread.
+   *
+   * @param blockedContentSource
+   *        Either a CSP Source (like 'self', as string) or nsIURI: the source
+   *        of the violation.
+   * @param violatedDirective
+   *        the directive that was violated (string).
+   * @param observerSubject
+   *        optional, subject sent to the nsIObservers listening to the CSP
+   *        violation topic.
+   */
+  _asyncReportViolation:
+  function(blockedContentSource, violatedDirective, observerSubject) {
+    // if optional observerSubject isn't specified, default to the source of
+    // the violation.
+    if (!observerSubject)
+      observerSubject = blockedContentSource;
+
+    // gotta wrap things that aren't nsISupports, since it's sent out to
+    // observers as such.  Objects that are not nsISupports are converted to
+    // strings and then wrapped into a nsISupportsCString.
+    if (!(observerSubject instanceof Ci.nsISupports)) {
+      let d = observerSubject;
+      observerSubject = Cc["@mozilla.org/supports-cstring;1"]
+                          .createInstance(Ci.nsISupportsCString);
+      observerSubject.data = d;
+    }
+
+    var reportSender = this;
+    Services.tm.mainThread.dispatch(
+      function() {
+        Services.obs.notifyObservers(observerSubject,
+                                     CSP_VIOLATION_TOPIC,
+                                     violatedDirective);
+        reportSender.sendReports(blockedContentSource, violatedDirective);
+      }, Ci.nsIThread.DISPATCH_NORMAL);
+  },
 };
 
 var NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentSecurityPolicy]);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -259,16 +259,17 @@ nsCOMArray<nsIRunnable>* nsContentUtils:
 PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
 PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
 
 nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService;
 JSRuntime *nsAutoGCRoot::sJSScriptRuntime;
 
 PRBool nsContentUtils::sIsHandlingKeyBoardEvent = PR_FALSE;
+PRBool nsContentUtils::sAllowXULXBL_for_file = PR_FALSE;
 
 PRBool nsContentUtils::sInitialized = PR_FALSE;
 
 nsRefPtrHashtable<nsPrefObserverHashKey, nsPrefOldCallback>
   *nsContentUtils::sPrefCallbackTable = nsnull;
 
 static PLDHashTable sEventListenerManagersHash;
 
@@ -501,16 +502,19 @@ nsContentUtils::Init()
 
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   sBlockedScriptRunners = new nsCOMArray<nsIRunnable>;
   NS_ENSURE_TRUE(sBlockedScriptRunners, NS_ERROR_OUT_OF_MEMORY);
 
+  nsContentUtils::AddBoolPrefVarCache("dom.allow_XUL_XBL_for_file",
+                                      &sAllowXULXBL_for_file);
+
   sInitialized = PR_TRUE;
 
   return NS_OK;
 }
 
 bool nsContentUtils::sImgLoaderInitialized;
 
 void
@@ -4936,22 +4940,19 @@ nsContentUtils::SetDataTransferInEvent(n
   NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
 
   nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
   dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
   if (!initialDataTransfer) {
     // A dataTransfer won't exist when a drag was started by some other
     // means, for instance calling the drag service directly, or a drag
     // from another application. In either case, a new dataTransfer should
-    // be created that reflects the data. Pass true to the constructor for
-    // the aIsExternal argument, so that only system access is allowed.
-    PRUint32 action = 0;
-    dragSession->GetDragAction(&action);
+    // be created that reflects the data.
     initialDataTransfer =
-      new nsDOMDataTransfer(aDragEvent->message, action);
+      new nsDOMDataTransfer(aDragEvent->message);
     NS_ENSURE_TRUE(initialDataTransfer, NS_ERROR_OUT_OF_MEMORY);
 
     // now set it in the drag session so we don't need to create it again
     dragSession->SetDataTransfer(initialDataTransfer);
   }
 
   // each event should use a clone of the original dataTransfer.
   nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS =
@@ -6411,16 +6412,30 @@ nsContentUtils::LayerManagerForDocument(
       }
     }
   }
 
   nsRefPtr<LayerManager> manager = new BasicLayerManager();
   return manager.forget();
 }
 
+bool
+nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal)
+{
+  if (IsSystemPrincipal(aPrincipal)) {
+    return true;
+  }
+  
+  nsCOMPtr<nsIURI> princURI;
+  aPrincipal->GetURI(getter_AddRefs(princURI));
+  
+  return princURI &&
+         ((sAllowXULXBL_for_file && SchemeIs(princURI, "file")) ||
+          IsSitePermAllow(princURI, "allowXULXBL"));
+}
 
 NS_IMPL_ISUPPORTS1(nsIContentUtils, nsIContentUtils)
 
 PRBool
 nsIContentUtils::IsSafeToRunScript()
 {
   return nsContentUtils::IsSafeToRunScript();
 }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3873,29 +3873,17 @@ nsScriptLoader*
 nsDocument::ScriptLoader()
 {
   return mScriptLoader;
 }
 
 PRBool
 nsDocument::InternalAllowXULXBL()
 {
-  if (nsContentUtils::IsSystemPrincipal(NodePrincipal())) {
-    mAllowXULXBL = eTriTrue;
-    return PR_TRUE;
-  }
-  
-  nsCOMPtr<nsIURI> princURI;
-  NodePrincipal()->GetURI(getter_AddRefs(princURI));
-  if (!princURI) {
-    mAllowXULXBL = eTriFalse;
-    return PR_FALSE;
-  }
-
-  if (nsContentUtils::IsSitePermAllow(princURI, "allowXULXBL")) {
+  if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
     mAllowXULXBL = eTriTrue;
     return PR_TRUE;
   }
 
   mAllowXULXBL = eTriFalse;
   return PR_FALSE;
 }
 
--- a/content/base/src/nsFileDataProtocolHandler.cpp
+++ b/content/base/src/nsFileDataProtocolHandler.cpp
@@ -86,16 +86,32 @@ nsFileDataProtocolHandler::RemoveFileDat
     gFileDataTable->Remove(aUri);
     if (gFileDataTable->Count() == 0) {
       delete gFileDataTable;
       gFileDataTable = nsnull;
     }
   }
 }
 
+nsIPrincipal*
+nsFileDataProtocolHandler::GetFileDataEntryPrincipal(nsACString& aUri)
+{
+  if (!gFileDataTable) {
+    return nsnull;
+  }
+  
+  FileDataInfo* res;
+  gFileDataTable->Get(aUri, &res);
+  if (!res) {
+    return nsnull;
+  }
+
+  return res->mPrincipal;
+}
+
 static FileDataInfo*
 GetFileDataInfo(const nsACString& aUri)
 {
   NS_ASSERTION(StringBeginsWith(aUri,
                                 NS_LITERAL_CSTRING(FILEDATA_SCHEME ":")),
                "Bad URI");
   
   if (!gFileDataTable) {
--- a/content/base/src/nsFileDataProtocolHandler.h
+++ b/content/base/src/nsFileDataProtocolHandler.h
@@ -56,16 +56,17 @@ public:
   nsFileDataProtocolHandler() {}
   virtual ~nsFileDataProtocolHandler() {}
 
   // Methods for managing uri->file mapping
   static void AddFileDataEntry(nsACString& aUri,
 			       nsIDOMBlob* aFile,
                                nsIPrincipal* aPrincipal);
   static void RemoveFileDataEntry(nsACString& aUri);
+  static nsIPrincipal* GetFileDataEntryPrincipal(nsACString& aUri);
   
 };
 
 #define NS_FILEDATAPROTOCOLHANDLER_CID \
 { 0xb43964aa, 0xa078, 0x44b2, \
   { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
 
 #endif /* nsFileDataProtocolHandler_h___ */
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -82,16 +82,18 @@
 #include "nsDOMError.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsISHistory.h"
 #include "nsISHistoryInternal.h"
 #include "nsIDocShellHistory.h"
 #include "nsIDOMNSHTMLDocument.h"
 #include "nsIXULWindow.h"
+#include "nsIEditor.h"
+#include "nsIEditorDocShell.h"
 
 #include "nsLayoutUtils.h"
 #include "nsIView.h"
 #include "nsPLDOMEvent.h"
 
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
@@ -681,16 +683,23 @@ nsFrameLoader::Show(PRInt32 marginWidth,
     nsCOMPtr<nsIDOMNSHTMLDocument> doc =
       do_QueryInterface(presShell->GetDocument());
 
     if (doc) {
       nsAutoString designMode;
       doc->GetDesignMode(designMode);
 
       if (designMode.EqualsLiteral("on")) {
+        // Hold on to the editor object to let the document reattach to the
+        // same editor object, instead of creating a new one.
+        nsCOMPtr<nsIEditorDocShell> editorDocshell = do_QueryInterface(mDocShell);
+        nsCOMPtr<nsIEditor> editor;
+        nsresult rv = editorDocshell->GetEditor(getter_AddRefs(editor));
+        NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
         doc->SetDesignMode(NS_LITERAL_STRING("off"));
         doc->SetDesignMode(NS_LITERAL_STRING("on"));
       }
     }
   }
 
   mInShow = PR_FALSE;
   if (mHideCalled) {
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -303,16 +303,22 @@ NS_IMETHODIMP
 nsFrameMessageManager::Dump(const nsAString& aStr)
 {
   fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout);
   fflush(stdout);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsFrameMessageManager::PrivateNoteIntentionalCrash()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 nsFrameMessageManager::GetContent(nsIDOMWindow** aContent)
 {
   *aContent = nsnull;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetDocShell(nsIDocShell** aDocShell)
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -253,19 +253,17 @@ GK_ATOM(controls, "controls")
 #endif
 GK_ATOM(coords, "coords")
 GK_ATOM(copy, "copy")
 GK_ATOM(copyOf, "copy-of")
 GK_ATOM(count, "count")
 GK_ATOM(crop, "crop")
 GK_ATOM(curpos, "curpos")
 GK_ATOM(current, "current")
-#ifdef MOZ_MEDIA
 GK_ATOM(currentloop, "currentloop")
-#endif
 GK_ATOM(cycler, "cycler")
 GK_ATOM(data, "data")
 GK_ATOM(datalist, "datalist")
 GK_ATOM(dataType, "data-type")
 GK_ATOM(datasources, "datasources")
 GK_ATOM(datetime, "datetime")
 GK_ATOM(dblclick, "dblclick")
 GK_ATOM(dd, "dd")
@@ -583,16 +581,17 @@ GK_ATOM(modifiers, "modifiers")
 GK_ATOM(monochrome, "monochrome")
 GK_ATOM(mousedown, "mousedown")
 GK_ATOM(mousemove, "mousemove")
 GK_ATOM(mouseout, "mouseout")
 GK_ATOM(mouseover, "mouseover")
 GK_ATOM(mousethrough, "mousethrough")
 GK_ATOM(mouseup, "mouseup")
 GK_ATOM(moz_opaque, "moz-opaque")
+GK_ATOM(moz_action_hint, "mozactionhint")
 GK_ATOM(x_moz_errormessage, "x-moz-errormessage")
 GK_ATOM(msthemecompatible, "msthemecompatible")
 GK_ATOM(multicol, "multicol")
 GK_ATOM(multiple, "multiple")
 GK_ATOM(name, "name")
 GK_ATOM(_namespace, "namespace")
 GK_ATOM(namespaceAlias, "namespace-alias")
 GK_ATOM(namespaceUri, "namespace-uri")
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -175,16 +175,22 @@ nsInProcessTabChildGlobal::GetContent(ns
 
 NS_IMETHODIMP
 nsInProcessTabChildGlobal::GetDocShell(nsIDocShell** aDocShell)
 {
   NS_IF_ADDREF(*aDocShell = mDocShell);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsInProcessTabChildGlobal::PrivateNoteIntentionalCrash()
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 void
 nsInProcessTabChildGlobal::Disconnect()
 {
   // Let the frame scripts know the child is being closed. We do any other
   // cleanup after the event has been fired. See DelayedDisconnect
   nsContentUtils::AddScriptRunner(
      NS_NewRunnableMethod(this, &nsInProcessTabChildGlobal::DelayedDisconnect)
   );
--- a/content/base/src/nsInProcessTabChildGlobal.h
+++ b/content/base/src/nsInProcessTabChildGlobal.h
@@ -72,16 +72,17 @@ public:
                            : NS_ERROR_NULL_POINTER;
   }
   NS_IMETHOD GetContent(nsIDOMWindow** aContent);
   NS_IMETHOD GetDocShell(nsIDocShell** aDocShell);
   NS_IMETHOD Dump(const nsAString& aStr)
   {
     return mMessageManager ? mMessageManager->Dump(aStr) : NS_OK;
   }
+  NS_IMETHOD PrivateNoteIntentionalCrash();
   NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER
 
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
   NS_IMETHOD AddEventListener(const nsAString& aType,
                               nsIDOMEventListener* aListener,
                               PRBool aUseCapture)
   {
     // By default add listeners only for trusted events!
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -95,16 +95,17 @@
 #include "nsIWindowWatcher.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsIConsoleService.h"
 #include "nsIChannelPolicy.h"
 #include "nsChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "jstypedarray.h"
+#include "nsStringBuffer.h"
 
 #define LOAD_STR "load"
 #define ERROR_STR "error"
 #define ABORT_STR "abort"
 #define LOADSTART_STR "loadstart"
 #define PROGRESS_STR "progress"
 #define UPLOADPROGRESS_STR "uploadprogress"
 #define READYSTATE_STR "readystatechange"
@@ -1169,23 +1170,24 @@ nsXMLHttpRequest::ConvertBodyToText(nsAS
     return rv;
 
   const char * inBuffer = mResponseBody.get();
   PRInt32 outBufferLength;
   rv = decoder->GetMaxLength(inBuffer, dataLen, &outBufferLength);
   if (NS_FAILED(rv))
     return rv;
 
-  PRUnichar * outBuffer =
-    static_cast<PRUnichar*>(nsMemory::Alloc((outBufferLength + 1) *
-                                               sizeof(PRUnichar)));
-  if (!outBuffer) {
+  nsStringBuffer* buf =
+    nsStringBuffer::Alloc((outBufferLength + 1) * sizeof(PRUnichar));
+  if (!buf) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  PRUnichar* outBuffer = static_cast<PRUnichar*>(buf->Data());
+
   PRInt32 totalChars = 0,
           outBufferIndex = 0,
           outLen = outBufferLength;
 
   do {
     PRInt32 inBufferLength = dataLen;
     rv = decoder->Convert(inBuffer,
                           &inBufferLength,
@@ -1207,20 +1209,28 @@ nsXMLHttpRequest::ConvertBodyToText(nsAS
         inBufferLength++;
       }
 
       inBuffer = &inBuffer[inBufferLength];
       dataLen -= inBufferLength;
     }
   } while ( NS_FAILED(rv) && (dataLen > 0) );
 
-  mResponseBodyUnicode.Assign(outBuffer, totalChars);
+  // Use the string buffer if it is small, or doesn't contain
+  // too much extra data.
+  if (outBufferLength < 127 ||
+      (outBufferLength * 0.9) < totalChars) {
+    outBuffer[totalChars] = PRUnichar(0);
+    // Move ownership to mResponseBodyUnicode.
+    buf->ToString(totalChars, mResponseBodyUnicode, PR_TRUE);
+  } else {
+    mResponseBodyUnicode.Assign(outBuffer, totalChars);
+    buf->Release();
+  }
   aOutBuffer = mResponseBodyUnicode;
-  nsMemory::Free(outBuffer);
-
   return NS_OK;
 }
 
 /* readonly attribute AString responseText; */
 NS_IMETHODIMP nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
 {
   nsresult rv = NS_OK;
 
--- a/content/base/test/test_fileapi_slice.html
+++ b/content/base/test/test_fileapi_slice.html
@@ -192,56 +192,56 @@ function imageLoadHandler(event) {
 
   testHasRun();
 }
 
 // image in the middle
 var imgfile = createFileWithData(testBinaryData + fileData + testBinaryData);
 is(imgfile.size, size + testBinaryData.length * 2, "correct file size");
 var img = new Image;
-img.src = createBlobURL(imgfile.slice(testBinaryData.length, size));
+img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, size));
 img.onload = imageLoadHandler;
 expectedTestCount++;
 
 // image at start
 var imgfile = createFileWithData(fileData + testBinaryData);
 is(imgfile.size, size + testBinaryData.length, "correct file size");
 var img = new Image;
-img.src = createBlobURL(imgfile.slice(0, size));
+img.src = URL.createObjectURL(imgfile.slice(0, size));
 img.onload = imageLoadHandler;
 expectedTestCount++;
 
 // image at end
 var imgfile = createFileWithData(testBinaryData + fileData);
 is(imgfile.size, size + testBinaryData.length, "correct file size");
 var img = new Image;
-img.src = createBlobURL(imgfile.slice(testBinaryData.length, size));
+img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, size));
 img.onload = imageLoadHandler;
 expectedTestCount++;
 
 // image past end
 var imgfile = createFileWithData(testBinaryData + fileData);
 is(imgfile.size, size + testBinaryData.length, "correct file size");
 var img = new Image;
-img.src = createBlobURL(imgfile.slice(testBinaryData.length, size + 1000));
+img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, size + 1000));
 img.onload = imageLoadHandler;
 expectedTestCount++;
 
 
 // Utility functions
 function testFile(file, contents, test) {
   // Load file using FileReader
   var r = new FileReader();
   r.onload = getFileReaderLoadHandler(contents, contents.length, "FileReader.readAsBinaryString of " + test);
   r.readAsBinaryString(file);
   expectedTestCount++;
 
-  // Load file using createBlobURL and XMLHttpRequest
+  // Load file using URL.createObjectURL and XMLHttpRequest
   var xhr = new XMLHttpRequest;
-  xhr.open("GET", createBlobURL(file));
+  xhr.open("GET", URL.createObjectURL(file));
   xhr.onload = getXHRLoadHandler(contents, contents.length, false,
                                  "XMLHttpRequest load of " + test);
   xhr.overrideMimeType('text/plain; charset=x-user-defined');
   xhr.send();
   expectedTestCount++;
 
   // Send file to server using FormData and XMLHttpRequest
   xhr = new XMLHttpRequest();
--- a/content/base/test/test_mozfiledataurl.html
+++ b/content/base/test/test_mozfiledataurl.html
@@ -17,17 +17,17 @@
 </p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.8">
 
 try {
-  createBlobURL(undefined);
+  URL.createObjectURL(undefined);
 } catch(e) { }
 
 window.addEventListener("message", function(e) {
   gen.send(JSON.parse(e.data));
 }, false);
 
 const innerSameSiteURI = "file_mozfiledataurl_inner.html";
 const innerCrossSiteURI = "http://example.com/tests/content/base/test/file_mozfiledataurl_inner.html"
@@ -39,38 +39,38 @@ function runTest() {
   inner = document.getElementById('inner');
   img = document.getElementById('img');
   audio = document.getElementById('audio');
   iframe = document.getElementById('iframe');
   inner.onload = function() { gen.send("inner loaded"); };
 
   // Attempt to load a image in this document
   var file = getFile("file_mozfiledataurl_img.jpg");
-  var fileurl = createBlobURL(file);
+  var fileurl = URL.createObjectURL(file);
   img.src = fileurl;
   var e = (yield);
   is(e.type, "load", "loaded successfully");
   is(img.width, 120, "correct width");
   is(img.height, 90, "correct height");
 
   // Revoke url and attempt to load a image in this document
   img.src = "file_mozfiledataurl_img.jpg";
   is((yield).type, "load", "successfull reset image");
-  revokeBlobURL(fileurl);
+  URL.revokeObjectURL(fileurl);
   todo(false, "urls need to act like 404s, not fail to parse");
 /*  img.src = fileurl;
   var e = (yield);
   is(e.type, "error", "failed successfully");
   isnot(img.width, 120, "correct error width");
   isnot(img.height, 90, "correct error height");
 */
   // Generate new fileurl and make sure it's different from the old
   var oldFileurl = fileurl;
-  var fileurl = createBlobURL(file);
-  isnot(fileurl, oldFileurl, "createBlobURL generated the same url twice");
+  var fileurl = URL.createObjectURL(file);
+  isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice");
 
   // Attempt to load an image in a different same-origin document
   inner.src = innerSameSiteURI;
   yield;
   inner.contentWindow.postMessage(JSON.stringify({img:fileurl}), "*");
   var res = (yield);
   is(res.type, "load", "loaded successfully");
   is(res.width, 120, "correct width");
@@ -82,36 +82,36 @@ function runTest() {
   inner.contentWindow.postMessage(JSON.stringify({img:fileurl}), "*");
   var res = (yield);
   is(res.type, "error", "failed successfully");
   isnot(res.width, 120, "correct error width");
   isnot(res.height, 90, "correct error height");
 
   // Attempt to load an audio in this document
   var file = getFile("file_mozfiledataurl_audio.ogg");
-  var fileurl = createBlobURL(file);
+  var fileurl = URL.createObjectURL(file);
   audio.src = fileurl;
   var e = (yield);
   is(e.type, "canplay", "loaded successfully");
 
   // Revoke url and attempt to load a audio in this document
   audio.src = "file_mozfiledataurl_audio.ogg";
   is((yield).type, "canplay", "successfully reset audio");
-  revokeBlobURL(fileurl);
+  URL.revokeObjectURL(fileurl);
   todo(false, "urls need to act like 404s, not fail to parse");
 /*  img.src = fileurl;
   var e = (yield);
   is(e.type, "error", "failed successfully");
   isnot(img.width, 120, "correct error width");
   isnot(img.height, 90, "correct error height");
 */
   // Generate new fileurl and make sure it's different from the old
   var oldFileurl = fileurl;
-  var fileurl = createBlobURL(file);
-  isnot(fileurl, oldFileurl, "createBlobURL generated the same url twice");
+  var fileurl = URL.createObjectURL(file);
+  isnot(fileurl, oldFileurl, "URL.createObjectURL generated the same url twice");
 
   // Attempt to load an audio in a different same-origin document
   inner.src = innerSameSiteURI;
   yield;
   inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*");
   var res = (yield);
   is(res.type, "canplay", "loaded successfully");
   
@@ -131,17 +131,17 @@ function runTest() {
      "iframe loaded successfully");
   is(iframe.contentDocument.getElementById("img").width, 120,
      "image in iframe width");
   is(iframe.contentDocument.getElementById("img").height, 90,
      "image in iframe height");
 
   // Attempt to load a HTML document in an iframe in this document, using file url
   file = getFile("file_mozfiledataurl_doc.html");
-  fileurl = createBlobURL(file);
+  fileurl = URL.createObjectURL(file);
   iframe.src = fileurl;
   yield;
   is(iframe.contentDocument.getElementsByTagName("p")[0].textContent,
      "This here is a document!",
      "iframe loaded successfully");
   isnot(iframe.contentDocument.getElementById("img").width, 120,
         "failed image in iframe width");
   isnot(iframe.contentDocument.getElementById("img").height, 90,
@@ -167,17 +167,17 @@ function runTest() {
   inner.src = innerCrossSiteURI;
   is((yield), "inner loaded", "correct gen.next()");
   inner.contentWindow.postMessage(JSON.stringify({iframe:fileurl}), "*");
   var res = (yield);
   is(res.type, "error", "load failed successfully");
 
   // Attempt to load file url using XHR
   file = getFile("file_mozfiledataurl_text.txt");
-  fileurl = createBlobURL(file);
+  fileurl = URL.createObjectURL(file);
   xhr = new XMLHttpRequest;
   xhr.onload = function() { gen.send("XHR finished"); };
   xhr.open("GET", fileurl);
   xhr.send();
   is((yield), "XHR finished", "correct gen.next()");
   xhr.responseText == "Yarr, here be plaintext file, ya landlubber\n";
 
   // Attempt to load file url using XHR in inner document
--- a/content/events/public/nsEventStates.h
+++ b/content/events/public/nsEventStates.h
@@ -33,16 +33,18 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsEventStates_h__
 #define nsEventStates_h__
 
+#include "nsDebug.h"
+
 /**
  * nsEventStates is the class used to represent the event states of nsIContent
  * instances. These states are calculated by IntrinsicState() and
  * ContentStatesChanged() has to be called when one of them changes thus
  * informing the layout/style engine of the change.
  * Event states are associated with pseudo-classes.
  */
 class nsEventStates
@@ -252,15 +254,19 @@ private:
 // Handler for the content has crashed
 #define NS_EVENT_STATE_HANDLER_CRASHED NS_DEFINE_EVENT_STATE_MACRO(28)
 // Content has focus and should show a ring.
 #define NS_EVENT_STATE_FOCUSRING     NS_DEFINE_EVENT_STATE_MACRO(29)
 // Content shows its placeholder
 #define NS_EVENT_STATE_MOZ_PLACEHOLDER NS_DEFINE_EVENT_STATE_MACRO(30)
 // Content is a submit control and the form isn't valid.
 #define NS_EVENT_STATE_MOZ_SUBMITINVALID NS_DEFINE_EVENT_STATE_MACRO(31)
+// UI friendly version of :invalid pseudo-class.
+#define NS_EVENT_STATE_MOZ_UI_INVALID NS_DEFINE_EVENT_STATE_MACRO(32)
+// UI friendly version of :valid pseudo-class.
+#define NS_EVENT_STATE_MOZ_UI_VALID NS_DEFINE_EVENT_STATE_MACRO(33)
 
 /**
  * NOTE: do not go over 63 without updating nsEventStates::InternalType!
  */
 
 #endif // nsEventStates_h__
 
--- a/content/events/src/nsDOMDataTransfer.cpp
+++ b/content/events/src/nsDOMDataTransfer.cpp
@@ -79,31 +79,27 @@ nsDOMDataTransfer::nsDOMDataTransfer()
     mReadOnly(PR_FALSE),
     mIsExternal(PR_FALSE),
     mUserCancelled(PR_FALSE),
     mDragImageX(0),
     mDragImageY(0)
 {
 }
 
-nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType, PRUint32 aAction)
+nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType)
   : mEventType(aEventType),
     mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
+    mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
     mCursorState(PR_FALSE),
     mReadOnly(PR_TRUE),
     mIsExternal(PR_TRUE),
     mUserCancelled(PR_FALSE),
     mDragImageX(0),
     mDragImageY(0)
 {
-  mEffectAllowed = aAction &
-                   (nsIDragService::DRAGDROP_ACTION_COPY |
-                    nsIDragService::DRAGDROP_ACTION_LINK |
-                    nsIDragService::DRAGDROP_ACTION_MOVE);
-
   CacheExternalFormats();
 }
 
 nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType,
                                      const PRUint32 aEffectAllowed,
                                      PRBool aCursorState,
                                      PRBool aIsExternal,
                                      PRBool aUserCancelled,
--- a/content/events/src/nsDOMDataTransfer.h
+++ b/content/events/src/nsDOMDataTransfer.h
@@ -88,17 +88,17 @@ protected:
   // default constructor used for the dragstart/draggesture event and
   // synthetic events
   nsDOMDataTransfer();
 
   // this constructor must only be used to create a dataTransfer for a drag
   // that was started without using a data transfer, either an external drag,
   // that is, a drag where the source is another application, or a drag
   // started by calling the drag service directly.
-  nsDOMDataTransfer(PRUint32 aEventType, PRUint32 aAction);
+  nsDOMDataTransfer(PRUint32 aEventType);
 
   // this constructor is used only by the Clone method to copy the fields as
   // needed to a new data transfer.
   nsDOMDataTransfer(PRUint32 aEventType,
                     const PRUint32 aEffectAllowed,
                     PRBool aCursorState,
                     PRBool aIsExternal,
                     PRBool aUserCancelled,
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2785,16 +2785,17 @@ nsEventStateManager::DecideGestureEvent(
       }
     } //scrollableFrame
   } //ancestor chain
 
   aEvent->displayPanFeedback = displayPanFeedback;
   aEvent->panDirection = panDirection;
 }
 
+#ifdef XP_MACOSX
 static bool
 NodeAllowsClickThrough(nsINode* aNode)
 {
   while (aNode) {
     if (aNode->IsElement() && aNode->AsElement()->IsXUL()) {
       mozilla::dom::Element* element = aNode->AsElement();
       static nsIContent::AttrValuesArray strings[] =
         {&nsGkAtoms::always, &nsGkAtoms::never, nsnull};
@@ -2805,16 +2806,17 @@ NodeAllowsClickThrough(nsINode* aNode)
         case 1:
           return false;
       }
     }
     aNode = nsContentUtils::GetCrossDocParentNode(aNode);
   }
   return true;
 }
+#endif
 
 NS_IMETHODIMP
 nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
                                      nsEvent *aEvent,
                                      nsIFrame* aTargetFrame,
                                      nsEventStatus* aStatus,
                                      nsIView* aView)
 {
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -80,17 +80,17 @@ nsresult
 nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
 {
   NS_ENSURE_ARG_POINTER(aPresContext);
   if (aPresContext != sPresContext)
     return NS_OK;
   nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
   if (widget) {
     PRUint32 newState = GetNewIMEState(sPresContext, nsnull);
-    SetIMEState(newState, widget);
+    SetIMEState(newState, nsnull, widget);
   }
   sContent = nsnull;
   sPresContext = nsnull;
   OnTextStateBlur(nsnull, nsnull);
   return NS_OK;
 }
 
 nsresult
@@ -105,17 +105,17 @@ nsIMEStateManager::OnRemoveContent(nsPre
 
   // Current IME transaction should commit
   nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
   if (widget) {
     nsresult rv = widget->CancelIMEComposition();
     if (NS_FAILED(rv))
       widget->ResetInputState();
     PRUint32 newState = GetNewIMEState(sPresContext, nsnull);
-    SetIMEState(newState, widget);
+    SetIMEState(newState, nsnull, widget);
   }
 
   sContent = nsnull;
   sPresContext = nsnull;
 
   return NS_OK;
 }
 
@@ -134,22 +134,23 @@ nsIMEStateManager::OnChangeFocus(nsPresC
   if (aPresContext == sPresContext && aContent == sContent) {
     // actual focus isn't changing, but if IME enabled state is changing,
     // we should do it.
     PRUint32 newEnabledState = newState & nsIContent::IME_STATUS_MASK_ENABLED;
     if (newEnabledState == 0) {
       // the enabled state isn't changing, we should do nothing.
       return NS_OK;
     }
-    PRUint32 enabled;
-    if (NS_FAILED(widget->GetIMEEnabled(&enabled))) {
+    nsIWidget_MOZILLA_2_0_BRANCH* widget2 = static_cast<nsIWidget_MOZILLA_2_0_BRANCH*>(widget.get());
+    IMEContext context;
+    if (!widget2 || NS_FAILED(widget2->GetInputMode(context))) {
       // this platform doesn't support IME controlling
       return NS_OK;
     }
-    if (enabled ==
+    if (context.mStatus ==
         nsContentUtils::GetWidgetStatusFromIMEStatus(newEnabledState)) {
       // the enabled state isn't changing.
       return NS_OK;
     }
   }
 
   // Current IME transaction should commit
   if (sPresContext) {
@@ -159,62 +160,63 @@ nsIMEStateManager::OnChangeFocus(nsPresC
     else
       oldWidget = GetWidget(sPresContext);
     if (oldWidget)
       oldWidget->ResetInputState();
   }
 
   if (newState != nsIContent::IME_STATUS_NONE) {
     // Update IME state for new focus widget
-    SetIMEState(newState, widget);
+    SetIMEState(newState, aContent, widget);
   }
 
   sPresContext = aPresContext;
   sContent = aContent;
 
   return NS_OK;
 }
 
 void
 nsIMEStateManager::OnInstalledMenuKeyboardListener(PRBool aInstalling)
 {
   sInstalledMenuKeyboardListener = aInstalling;
   OnChangeFocus(sPresContext, sContent);
 }
 
 void
-nsIMEStateManager::UpdateIMEState(PRUint32 aNewIMEState)
+nsIMEStateManager::UpdateIMEState(PRUint32 aNewIMEState, nsIContent* aContent)
 {
   if (!sPresContext) {
     NS_WARNING("ISM doesn't know which editor has focus");
     return;
   }
   NS_PRECONDITION(aNewIMEState != 0, "aNewIMEState doesn't specify new state.");
   nsCOMPtr<nsIWidget> widget = GetWidget(sPresContext);
   if (!widget) {
     NS_WARNING("focused widget is not found");
     return;
   }
 
   // Don't update IME state when enabled state isn't actually changed.
-  PRUint32 currentEnabledState;
-  nsresult rv = widget->GetIMEEnabled(&currentEnabledState);
+  nsIWidget_MOZILLA_2_0_BRANCH* widget2 = static_cast<nsIWidget_MOZILLA_2_0_BRANCH*>(widget.get());
+  IMEContext context;
+  nsresult rv = widget2->GetInputMode(context);
   if (NS_FAILED(rv)) {
     return; // This platform doesn't support controling the IME state.
   }
   PRUint32 newEnabledState = aNewIMEState & nsIContent::IME_STATUS_MASK_ENABLED;
-  if (currentEnabledState ==
+  if (context.mStatus ==
         nsContentUtils::GetWidgetStatusFromIMEStatus(newEnabledState)) {
     return;
   }
 
   // commit current composition
   widget->ResetInputState();
 
-  SetIMEState(aNewIMEState, widget);
+  SetIMEState(aNewIMEState, aContent, widget);
 }
 
 PRUint32
 nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
                                   nsIContent*    aContent)
 {
   // On Printing or Print Preview, we don't need IME.
   if (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
@@ -256,22 +258,38 @@ public:
   }
 
 private:
   PRUint32 mState;
 };
 
 void
 nsIMEStateManager::SetIMEState(PRUint32 aState,
+                               nsIContent* aContent,
                                nsIWidget* aWidget)
 {
   if (aState & nsIContent::IME_STATUS_MASK_ENABLED) {
-    PRUint32 state =
-      nsContentUtils::GetWidgetStatusFromIMEStatus(aState);
-    aWidget->SetIMEEnabled(state);
+    nsIWidget_MOZILLA_2_0_BRANCH* widget2 = static_cast<nsIWidget_MOZILLA_2_0_BRANCH*>(aWidget);
+    if (!widget2)
+      return;
+
+    PRUint32 state = nsContentUtils::GetWidgetStatusFromIMEStatus(aState);
+    IMEContext context;
+    context.mStatus = state;
+    
+    if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML &&
+        (aContent->Tag() == nsGkAtoms::input ||
+         aContent->Tag() == nsGkAtoms::textarea)) {
+      aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type,
+                        context.mHTMLInputType);
+      aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
+                        context.mActionHint);
+    }
+
+    widget2->SetInputMode(context);
 
     nsContentUtils::AddScriptRunner(new IMEEnabledStateChangedEvent(state));
   }
   if (aState & nsIContent::IME_STATUS_MASK_OPENED) {
     PRBool open = !!(aState & nsIContent::IME_STATUS_OPEN);
     aWidget->SetIMEOpenState(open);
   }
 }
--- a/content/events/src/nsIMEStateManager.h
+++ b/content/events/src/nsIMEStateManager.h
@@ -81,20 +81,21 @@ public:
   static nsresult GetFocusSelectionAndRoot(nsISelection** aSel,
                                            nsIContent** aRoot);
   // This method updates the current IME state.  However, if the enabled state
   // isn't changed by the new state, this method does nothing.
   // Note that this method changes the IME state of the active element in the
   // widget.  So, the caller must have focus.
   // aNewIMEState must have an enabled state of nsIContent::IME_STATUS_*.
   // And optionally, it can have an open state of nsIContent::IME_STATUS_*.
-  static void UpdateIMEState(PRUint32 aNewIMEState);
+  static void UpdateIMEState(PRUint32 aNewIMEState, nsIContent* aContent);
 
 protected:
-  static void SetIMEState(PRUint32 aState, nsIWidget* aWidget);
+  static void SetIMEState(PRUint32 aState, nsIContent* aContent,
+                          nsIWidget* aWidget);
   static PRUint32 GetNewIMEState(nsPresContext* aPresContext,
                                  nsIContent* aContent);
 
   static nsIWidget* GetWidget(nsPresContext* aPresContext);
 
   static nsIContent*    sContent;
   static nsPresContext* sPresContext;
   static PRBool         sInstalledMenuKeyboardListener;
new file mode 100644
--- /dev/null
+++ b/content/html/content/crashtests/613027.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  var a = document.createElementNS("http://www.w3.org/1999/xhtml", "fieldset");
+  var b = document.createElementNS("http://www.w3.org/1999/xhtml", "legend");
+  var c = document.createElementNS("http://www.w3.org/1999/xhtml", "input");
+
+  a.appendChild(b);
+  a.appendChild(c);
+  a.removeChild(b);
+  c.expandoQ = a;
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/content/html/content/crashtests/crashtests.list
+++ b/content/html/content/crashtests/crashtests.list
@@ -17,8 +17,9 @@ load 515829-2.html
 load 570566-1.html
 load 571428-1.html
 load 580507-1.xhtml
 load 590387.html
 load 596785-1.html
 load 596785-2.html
 load 606430-1.html
 load 602117.html
+load 613027.html
--- a/content/html/content/public/nsFormSubmission.h
+++ b/content/html/content/public/nsFormSubmission.h
@@ -157,19 +157,22 @@ public:
 
   virtual ~nsEncodingFormSubmission();
 
   /**
    * Encode a Unicode string to bytes using the encoder (or just copy the input
    * if there is no encoder).
    * @param aStr the string to encode
    * @param aResult the encoded string [OUT]
+   * @param aHeaderEncode If true, turns all linebreaks into spaces and escapes
+   *                      all quotes
    * @throws an error if UnicodeToNewBytes fails
    */
-  nsresult EncodeVal(const nsAString& aStr, nsACString& aResult);
+  nsresult EncodeVal(const nsAString& aStr, nsCString& aResult,
+                     bool aHeaderEncode);
 
 private:
   // The encoder that will encode Unicode names and values
   nsCOMPtr<nsISaveAsCharset> mEncoder;
 };
 
 /**
  * Handle multipart/form-data encoding, which does files as well as normal
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -44,31 +44,34 @@
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMRange.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsILoadGroup.h"
 #include "nsIObserver.h"
 #include "ImageLayers.h"
-
 #include "nsAudioStream.h"
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef PRUint16 nsMediaNetworkState;
 typedef PRUint16 nsMediaReadyState;
 
 class nsHTMLMediaElement : public nsGenericHTMLElement,
                            public nsIObserver
 {
   typedef mozilla::layers::ImageContainer ImageContainer;
 
 public:
+
+  typedef mozilla::TimeStamp TimeStamp;
+  typedef mozilla::TimeDuration TimeDuration;
+
   enum CanPlayStatus {
     CANPLAY_NO,
     CANPLAY_MAYBE,
     CANPLAY_YES
   };
 
   nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                      mozilla::dom::FromParser aFromParser = mozilla::dom::NOT_FROM_PARSER);
@@ -334,16 +337,24 @@ public:
   virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) = 0;
 
   /**
    * Sets the required request headers on the HTTP channel for
    * video or audio requests.
    */
   void SetRequestHeaders(nsIHttpChannel* aChannel);
 
+  /**
+   * Fires a timeupdate event. If aPeriodic is PR_TRUE, the event will only
+   * be fired if we've not fired a timeupdate event (for any reason) in the
+   * last 250ms, as required by the spec when the current time is periodically
+   * increasing during playback.
+   */
+  void FireTimeUpdate(PRBool aPeriodic);
+
 protected:
   class MediaLoadListener;
 
   /**
    * Changes mHasPlayedOrSeeked to aValue. If mHasPlayedOrSeeked changes
    * we'll force a reflow so that the video frame gets reflowed to reflect
    * the poster hiding or showing immediately.
    */
@@ -586,16 +597,24 @@ protected:
   // PRELOAD_UNDEFINED, its value is changed by calling
   // UpdatePreloadAction().
   PreloadAction mPreloadAction;
 
   // Size of the media. Updated by the decoder on the main thread if
   // it changes. Defaults to a width and height of -1 inot set.
   nsIntSize mMediaSize;
 
+  // Time that the last timeupdate event was fired. Read/Write from the
+  // main thread only.
+  TimeStamp mTimeUpdateTime;
+
+  // Media 'currentTime' value when the last timeupdate event occurred.
+  // Read/Write from the main thread only.
+  float mLastCurrentTime;
+
   nsRefPtr<gfxASurface> mPrintSurface;
 
   // Reference to the source element last returned by GetNextSource().
   // This is the child source element which we're trying to load from.
   nsCOMPtr<nsIContent> mSourceLoadCandidate;
 
   // An audio stream for writing audio directly from JS.
   nsRefPtr<nsAudioStream> mAudioStream;
--- a/content/html/content/src/nsFormSubmission.cpp
+++ b/content/html/content/src/nsFormSubmission.cpp
@@ -386,17 +386,17 @@ nsFSURLEncoded::URLEncode(const nsAStrin
   // convert to CRLF breaks
   PRUnichar* convertedBuf =
     nsLinebreakConverter::ConvertUnicharLineBreaks(PromiseFlatString(aStr).get(),
                                                    nsLinebreakConverter::eLinebreakAny,
                                                    nsLinebreakConverter::eLinebreakNet);
   NS_ENSURE_TRUE(convertedBuf, NS_ERROR_OUT_OF_MEMORY);
 
   nsCAutoString encodedBuf;
-  nsresult rv = EncodeVal(nsDependentString(convertedBuf), encodedBuf);
+  nsresult rv = EncodeVal(nsDependentString(convertedBuf), encodedBuf, false);
   nsMemory::Free(convertedBuf);
   NS_ENSURE_SUCCESS(rv, rv);
 
   char* escapedBuf = nsEscape(encodedBuf.get(), url_XPAlphas);
   NS_ENSURE_TRUE(escapedBuf, NS_ERROR_OUT_OF_MEMORY);
   aEncoded.Adopt(escapedBuf);
 
   return NS_OK;
@@ -436,26 +436,26 @@ nsFSMultipartFormData::GetSubmissionBody
 }
 
 nsresult
 nsFSMultipartFormData::AddNameValuePair(const nsAString& aName,
                                         const nsAString& aValue)
 {
   nsCString valueStr;
   nsCAutoString encodedVal;
-  nsresult rv = EncodeVal(aValue, encodedVal);
+  nsresult rv = EncodeVal(aValue, encodedVal, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   valueStr.Adopt(nsLinebreakConverter::
                  ConvertLineBreaks(encodedVal.get(),
                                    nsLinebreakConverter::eLinebreakAny,
                                    nsLinebreakConverter::eLinebreakNet));
 
   nsCAutoString nameStr;
-  rv = EncodeVal(aName, nameStr);
+  rv = EncodeVal(aName, nameStr, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Make MIME block for name/value pair
 
   // XXX: name parameter should be encoded per RFC 2231
   // RFC 2388 specifies that RFC 2047 be used, but I think it's not 
   // consistent with MIME standard.
   mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
@@ -468,44 +468,42 @@ nsFSMultipartFormData::AddNameValuePair(
 }
 
 nsresult
 nsFSMultipartFormData::AddNameFilePair(const nsAString& aName,
                                        nsIDOMBlob* aBlob)
 {
   // Encode the control name
   nsCAutoString nameStr;
-  nsresult rv = EncodeVal(aName, nameStr);
+  nsresult rv = EncodeVal(aName, nameStr, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCString filenameStr;
-  nsAutoString contentType;
+  nsCString filename, contentType;
   nsCOMPtr<nsIInputStream> fileStream;
   if (aBlob) {
     // Get and encode the filename
-    nsAutoString filename;
+    nsAutoString filename16;
     nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
     if (file) {
-      rv = file->GetName(filename);
+      rv = file->GetName(filename16);
       NS_ENSURE_SUCCESS(rv, rv);
     }
-    nsCAutoString encodedFileName;
-    rv = EncodeVal(filename, encodedFileName);
+    rv = EncodeVal(filename16, filename, true);
     NS_ENSURE_SUCCESS(rv, rv);
   
-    filenameStr.Adopt(nsLinebreakConverter::
-                      ConvertLineBreaks(encodedFileName.get(),
+    // Get content type
+    nsAutoString contentType16;
+    rv = aBlob->GetType(contentType16);
+    if (NS_FAILED(rv) || contentType16.IsEmpty()) {
+      contentType16.AssignLiteral("application/octet-stream");
+    }
+    contentType.Adopt(nsLinebreakConverter::
+                      ConvertLineBreaks(NS_ConvertUTF16toUTF8(contentType16).get(),
                                         nsLinebreakConverter::eLinebreakAny,
-                                        nsLinebreakConverter::eLinebreakNet));
-  
-    // Get content type
-    rv = aBlob->GetType(contentType);
-    if (NS_FAILED(rv) || contentType.IsEmpty()) {
-      contentType.AssignLiteral("application/octet-stream");
-    }
+                                        nsLinebreakConverter::eLinebreakSpace));
   
     // Get input stream
     rv = aBlob->GetInternalStream(getter_AddRefs(fileStream));
     NS_ENSURE_SUCCESS(rv, rv);
     if (fileStream) {
       // Create buffered stream (for efficiency)
       nsCOMPtr<nsIInputStream> bufferedStream;
       rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
@@ -526,20 +524,19 @@ nsFSMultipartFormData::AddNameFilePair(c
   mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
                  + NS_LITERAL_CSTRING(CRLF);
   // XXX: name/filename parameter should be encoded per RFC 2231
   // RFC 2388 specifies that RFC 2047 be used, but I think it's not 
   // consistent with the MIME standard.
   mPostDataChunk +=
          NS_LITERAL_CSTRING("Content-Disposition: form-data; name=\"")
        + nameStr + NS_LITERAL_CSTRING("\"; filename=\"")
-       + filenameStr + NS_LITERAL_CSTRING("\"" CRLF)
-       + NS_LITERAL_CSTRING("Content-Type: ");
-  AppendUTF16toUTF8(contentType, mPostDataChunk);
-  mPostDataChunk += NS_LITERAL_CSTRING(CRLF CRLF);
+       + filename + NS_LITERAL_CSTRING("\"" CRLF)
+       + NS_LITERAL_CSTRING("Content-Type: ")
+       + contentType + NS_LITERAL_CSTRING(CRLF CRLF);
 
   // Add the file to the stream
   if (fileStream) {
     // We need to dump the data up to this point into the POST data stream here,
     // since we're about to add the file input stream
     AddPostDataStream();
 
     mPostDataStream->AppendStream(fileStream);
@@ -664,20 +661,30 @@ nsFSTextPlain::GetEncodedSubmission(nsIU
     nsCString escapedBody;
     escapedBody.Adopt(escapedBuf);
 
     path += NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody;
 
     rv = aURI->SetPath(path);
 
   } else {
-    // Create data stream
+    // Create data stream.
+    // We do want to send the data through the charset encoder and we want to
+    // normalize linebreaks to use the "standard net" format (\r\n), but we
+    // don't want to perform any other encoding. This means that names and
+    // values which contains '=' or newlines are potentially ambigiously
+    // encoded, but that how text/plain is specced.
+    nsCString cbody;
+    EncodeVal(mBody, cbody, false);
+    cbody.Adopt(nsLinebreakConverter::
+                ConvertLineBreaks(cbody.get(),
+                                  nsLinebreakConverter::eLinebreakAny,
+                                  nsLinebreakConverter::eLinebreakNet));
     nsCOMPtr<nsIInputStream> bodyStream;
-    rv = NS_NewStringInputStream(getter_AddRefs(bodyStream),
-                                          mBody);
+    rv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), cbody);
     if (!bodyStream) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Create mime stream with headers and such
     nsCOMPtr<nsIMIMEInputStream> mimeStream
         = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -725,27 +732,40 @@ nsEncodingFormSubmission::nsEncodingForm
 }
 
 nsEncodingFormSubmission::~nsEncodingFormSubmission()
 {
 }
 
 // i18n helper routines
 nsresult
-nsEncodingFormSubmission::EncodeVal(const nsAString& aStr, nsACString& aOut)
+nsEncodingFormSubmission::EncodeVal(const nsAString& aStr, nsCString& aOut,
+                                    bool aHeaderEncode)
 {
-  if (mEncoder) {
+  if (mEncoder && !aStr.IsEmpty()) {
     aOut.Truncate();
-    return aStr.IsEmpty() ? NS_OK :
-           mEncoder->Convert(PromiseFlatString(aStr).get(),
-                             getter_Copies(aOut));
+    nsresult rv = mEncoder->Convert(PromiseFlatString(aStr).get(),
+                                    getter_Copies(aOut));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  else {
+    // fall back to UTF-8
+    CopyUTF16toUTF8(aStr, aOut);
   }
 
-  // fall back to UTF-8
-  CopyUTF16toUTF8(aStr, aOut);
+  if (aHeaderEncode) {
+    aOut.Adopt(nsLinebreakConverter::
+               ConvertLineBreaks(aOut.get(),
+                                 nsLinebreakConverter::eLinebreakAny,
+                                 nsLinebreakConverter::eLinebreakSpace));
+    aOut.ReplaceSubstring(NS_LITERAL_CSTRING("\""),
+                          NS_LITERAL_CSTRING("\\\""));
+  }
+
+
   return NS_OK;
 }
 
 // --------------------------------------------------------------------------
 
 static void
 GetSubmitCharset(nsGenericHTMLElement* aForm,
                  nsACString& oCharset)
--- a/content/html/content/src/nsHTMLButtonElement.cpp
+++ b/content/html/content/src/nsHTMLButtonElement.cpp
@@ -648,20 +648,22 @@ nsHTMLButtonElement::AfterSetAttr(PRInt3
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::type) {
       if (!aValue) {
         mType = kButtonDefaultType->value;
       }
 
       UpdateBarredFromConstraintValidation();
       states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID |
                 NS_EVENT_STATE_MOZ_SUBMITINVALID;
     } else if (aName == nsGkAtoms::disabled) {
       UpdateBarredFromConstraintValidation();
-      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     }
 
     if (aNotify && !states.IsEmpty()) {
       nsIDocument* doc = GetCurrentDoc();
       if (doc) {
         MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
         doc->ContentStatesChanged(this, nsnull, states);
       }
@@ -701,17 +703,18 @@ nsHTMLButtonElement::RestoreState(nsPres
 }
 
 nsEventStates
 nsHTMLButtonElement::IntrinsicState() const
 {
   nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
 
   if (IsCandidateForConstraintValidation()) {
-    state |= IsValid() ? NS_EVENT_STATE_VALID : NS_EVENT_STATE_INVALID;
+    state |= IsValid() ? NS_EVENT_STATE_VALID | NS_EVENT_STATE_MOZ_UI_VALID
+                       : NS_EVENT_STATE_INVALID | NS_EVENT_STATE_MOZ_UI_INVALID;
   }
 
   if (mForm && !mForm->GetValidity() && IsSubmitControl()) {
     state |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
   }
 
   return state;
 }
@@ -722,17 +725,19 @@ NS_IMETHODIMP
 nsHTMLButtonElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
     doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID);
+                                            NS_EVENT_STATE_VALID |
+                                            NS_EVENT_STATE_MOZ_UI_INVALID |
+                                            NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 void
 nsHTMLButtonElement::UpdateBarredFromConstraintValidation()
 {
@@ -741,12 +746,13 @@ nsHTMLButtonElement::UpdateBarredFromCon
                                     IsDisabled());
 }
 
 void
 nsHTMLButtonElement::FieldSetDisabledChanged(nsEventStates aStates, PRBool aNotify)
 {
   UpdateBarredFromConstraintValidation();
 
-  aStates |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+  aStates |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+             NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
   nsGenericHTMLFormElement::FieldSetDisabledChanged(aStates, aNotify);
 }
 
--- a/content/html/content/src/nsHTMLFieldSetElement.cpp
+++ b/content/html/content/src/nsHTMLFieldSetElement.cpp
@@ -61,33 +61,34 @@ nsHTMLFieldSetElement::~nsHTMLFieldSetEl
   PRUint32 length = mDependentElements.Length();
   for (PRUint32 i=0; i<length; ++i) {
     mDependentElements[i]->ForgetFieldSet(this);
   }
 }
 
 // nsISupports
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHTMLFieldSetElement)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLFieldSetElement,
+                                                nsGenericHTMLFormElement)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mElements)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLFieldSetElement)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLFieldSetElement,
                                                   nsGenericHTMLFormElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mElements, nsIDOMNodeList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(nsHTMLFieldSetElement, nsGenericElement)
 NS_IMPL_RELEASE_INHERITED(nsHTMLFieldSetElement, nsGenericElement)
 
 DOMCI_NODE_DATA(HTMLFieldSetElement, nsHTMLFieldSetElement)
 
 // QueryInterface implementation for nsHTMLFieldSetElement
-NS_INTERFACE_TABLE_HEAD(nsHTMLFieldSetElement)
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLFieldSetElement)
   NS_HTML_CONTENT_INTERFACE_TABLE2(nsHTMLFieldSetElement,
                                    nsIDOMHTMLFieldSetElement,
                                    nsIConstraintValidation)
   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLFieldSetElement,
                                                nsGenericHTMLFormElement)
 NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLFieldSetElement)
 
 NS_IMPL_ELEMENT_CLONE(nsHTMLFieldSetElement)
--- a/content/html/content/src/nsHTMLFieldSetElement.h
+++ b/content/html/content/src/nsHTMLFieldSetElement.h
@@ -50,17 +50,17 @@ class nsHTMLFieldSetElement : public nsG
 {
 public:
   using nsIConstraintValidation::GetValidationMessage;
 
   nsHTMLFieldSetElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~nsHTMLFieldSetElement();
 
   // nsISupports
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE(nsGenericHTMLFormElement::)
 
   // nsIDOMElement
   NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLFormElement::)
 
   // nsIDOMHTMLElement
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -260,17 +260,18 @@ nsHTMLFormElement::nsHTMLFormElement(alr
     mNotifiedObserversResult(PR_FALSE),
     mSubmitPopupState(openAbused),
     mSubmitInitiatedFromUserInput(PR_FALSE),
     mPendingSubmission(nsnull),
     mSubmittingRequest(nsnull),
     mDefaultSubmitElement(nsnull),
     mFirstSubmitInElements(nsnull),
     mFirstSubmitNotInElements(nsnull),
-    mInvalidElementsCount(0)
+    mInvalidElementsCount(0),
+    mEverTriedInvalidSubmit(false)
 {
 }
 
 nsHTMLFormElement::~nsHTMLFormElement()
 {
   if (mControls) {
     mControls->DropFormReference();
   }
@@ -472,16 +473,20 @@ MarkOrphans(const nsTArray<nsGenericHTML
 
 static void
 CollectOrphans(nsINode* aRemovalRoot, nsTArray<nsGenericHTMLFormElement*> aArray
 #ifdef DEBUG
                , nsIDOMHTMLFormElement* aThisForm
 #endif
                )
 {
+  // Prepare document update batch.
+  nsIDocument* doc = aArray.IsEmpty() ? nsnull : aArray[0]->GetCurrentDoc();
+  MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+
   // Walk backwards so that if we remove elements we can just keep iterating
   PRUint32 length = aArray.Length();
   for (PRUint32 i = length; i > 0; --i) {
     nsGenericHTMLFormElement* node = aArray[i-1];
 
     // Now if MAYBE_ORPHAN_FORM_ELEMENT is not set, that would mean that the
     // node is in fact a descendant of the form and hence should stay in the
     // form.  If it _is_ set, then we need to check whether the node is a
@@ -490,24 +495,29 @@ CollectOrphans(nsINode* aRemovalRoot, ns
 #ifdef DEBUG
     PRBool removed = PR_FALSE;
 #endif
     if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) {
       node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
       if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) {
         node->ClearForm(PR_TRUE);
 
-        // When submit controls have no more form, they need to be updated.
+        // When a form control loses its form owner, :-moz-ui-invalid and
+        // :-moz-ui-valid might not apply any more.
+        nsEventStates states = NS_EVENT_STATE_MOZ_UI_VALID |
+                               NS_EVENT_STATE_MOZ_UI_INVALID;
+
+        // In addition, submit controls shouldn't have
+        // NS_EVENT_STATE_MOZ_SUBMITINVALID applying if they do not have a form.
         if (node->IsSubmitControl()) {
-          nsIDocument* doc = node->GetCurrentDoc();
-          if (doc) {
-            MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-            doc->ContentStatesChanged(node, nsnull,
-                                      NS_EVENT_STATE_MOZ_SUBMITINVALID);
-          }
+          states |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
+        }
+
+        if (doc) {
+          doc->ContentStatesChanged(node, nsnull, states);
         }
 #ifdef DEBUG
         removed = PR_TRUE;
 #endif
       }
     }
 
 #ifdef DEBUG
@@ -1619,20 +1629,16 @@ nsHTMLFormElement::CheckFormValidity(nsI
 
   // Hold a reference to the elements so they can't be deleted while calling
   // the invalid events.
   for (PRUint32 i = 0; i < len; ++i) {
     static_cast<nsGenericHTMLElement*>(sortedControls[i])->AddRef();
   }
 
   for (PRUint32 i = 0; i < len; ++i) {
-    if (!sortedControls[i]->IsSubmittableControl()) {
-      continue;
-    }
-
     nsCOMPtr<nsIConstraintValidation> cvElmt =
       do_QueryInterface((nsGenericHTMLElement*)sortedControls[i]);
     if (cvElmt && cvElmt->IsCandidateForConstraintValidation() &&
         !cvElmt->IsValid()) {
       ret = PR_FALSE;
       PRBool defaultAction = PR_TRUE;
       nsContentUtils::DispatchTrustedEvent(sortedControls[i]->GetOwnerDoc(),
                                            static_cast<nsIContent*>(sortedControls[i]),
@@ -1696,28 +1702,61 @@ nsHTMLFormElement::CheckValidFormSubmiss
   // Do not check form validity if there is no observer for
   // NS_INVALIDFORMSUBMIT_SUBJECT.
   if (NS_SUCCEEDED(rv) && hasObserver) {
     nsCOMPtr<nsIMutableArray> invalidElements =
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!CheckFormValidity(invalidElements.get())) {
+      // For the first invalid submission, we should update element states.
+      // We have to do that _before_ calling the observers so we are sure they
+      // will not interfere (like focusing the element).
+      if (!mEverTriedInvalidSubmit) {
+        mEverTriedInvalidSubmit = true;
+
+        nsIDocument* doc = GetCurrentDoc();
+        if (doc) {
+          /*
+           * We are going to call ContentStatesChanged assuming elements want to
+           * be notified because we can't know.
+           * Submissions shouldn't happen during parsing so it _should_ be safe.
+           */
+
+          MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+
+          for (PRUint32 i = 0, length = mControls->mElements.Length();
+               i < length; ++i) {
+            doc->ContentStatesChanged(mControls->mElements[i], nsnull,
+                                      NS_EVENT_STATE_MOZ_UI_VALID |
+                                      NS_EVENT_STATE_MOZ_UI_INVALID);
+          }
+
+          // Because of backward compatibility, <input type='image'> is not in
+          // elements but can be invalid.
+          // TODO: should probably be removed when bug 606491 will be fixed.
+          for (PRUint32 i = 0, length = mControls->mNotInElements.Length();
+               i < length; ++i) {
+            doc->ContentStatesChanged(mControls->mNotInElements[i], nsnull,
+                                      NS_EVENT_STATE_MOZ_UI_VALID |
+                                      NS_EVENT_STATE_MOZ_UI_INVALID);
+          }
+        }
+      }
+
       nsCOMPtr<nsISupports> inst;
       nsCOMPtr<nsIFormSubmitObserver> observer;
       PRBool more = PR_TRUE;
       while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
         theEnum->GetNext(getter_AddRefs(inst));
         observer = do_QueryInterface(inst);
 
         if (observer) {
-          rv = observer->
-            NotifyInvalidSubmit(this,
-                                static_cast<nsIArray*>(invalidElements));
-          NS_ENSURE_SUCCESS(rv, rv);
+          observer->NotifyInvalidSubmit(this,
+                                        static_cast<nsIArray*>(invalidElements));
         }
       }
 
       // The form is invalid. Observers have been alerted. Do not submit.
       return false;
     }
   } else {
     NS_WARNING("There is no observer for \"invalidformsubmit\". \
--- a/content/html/content/src/nsHTMLFormElement.h
+++ b/content/html/content/src/nsHTMLFormElement.h
@@ -291,16 +291,25 @@ public:
   /**
    * Walk over the form elements and call SubmitNamesValues() on them to get
    * their data pumped into the FormSubmitter.
    *
    * @param aFormSubmission the form submission object
    */
   nsresult WalkFormElements(nsFormSubmission* aFormSubmission);
 
+  /**
+   * Whether the submission of this form has been ever prevented because of
+   * being invalid.
+   *
+   * @return Whether the submission of this form has been prevented because of
+   * being invalid.
+   */
+  bool HasEverTriedInvalidSubmit() const { return mEverTriedInvalidSubmit; }
+
 protected:
   class RemoveElementRunnable;
   friend class RemoveElementRunnable;
 
   class RemoveElementRunnable : public nsRunnable {
   public:
     RemoveElementRunnable(nsHTMLFormElement* aForm)
       : mForm(aForm)
@@ -436,16 +445,22 @@ protected:
 
   /**
    * Number of invalid and candidate for constraint validation elements in the
    * form the last time UpdateValidity has been called.
    * @note Should only be used by UpdateValidity() and GetValidity()!
    */
   PRInt32 mInvalidElementsCount;
 
+  /**
+   * Whether the submission of this form has been ever prevented because of
+   * being invalid.
+   */
+  bool mEverTriedInvalidSubmit;
+
 protected:
   /** Detection of first form to notify observers */
   static PRBool gFirstFormSubmitted;
   /** Detection of first password input to initialize the password manager */
   static PRBool gPasswordManagerInitialized;
 };
 
 #endif // nsHTMLFormElement_h__
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -110,17 +110,16 @@
 #include "nsIPopupWindowManager.h"
 #include "nsGlobalWindow.h"
 
 // input type=image
 #include "nsImageLoadingContent.h"
 #include "nsIDOMWindowInternal.h"
 
 #include "mozAutoDocUpdate.h"
-#include "nsHTMLFormElement.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 
 #include "nsTextEditRules.h"
 
 // JS headers are needed for the pattern attribute.
 #include "jsapi.h"
@@ -436,17 +435,17 @@ AsyncClickHandler::Run()
     }
   }
 
   // Set new selected files
   if (newFiles.Count()) {
     // The text control frame (if there is one) isn't going to send a change
     // event because it will think this is done by a script.
     // So, we can safely send one by ourself.
-    mInput->SetFiles(newFiles);
+    mInput->SetFiles(newFiles, true);
     nsContentUtils::DispatchTrustedEvent(mInput->GetOwnerDoc(),
                                          static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
                                          NS_LITERAL_STRING("change"), PR_FALSE,
                                          PR_FALSE);
   }
 
   return NS_OK;
 }
@@ -621,16 +620,18 @@ nsHTMLInputElement::nsHTMLInputElement(a
                                        FromParser aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mType(kInputDefaultType->value),
     mBitField(0)
 {
   SET_BOOLBIT(mBitField, BF_PARSER_CREATING, aFromParser);
   SET_BOOLBIT(mBitField, BF_INHIBIT_RESTORATION,
       aFromParser & mozilla::dom::FROM_PARSER_FRAGMENT);
+  SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, PR_TRUE);
+  SET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI, PR_TRUE);
   mInputData.mState = new nsTextEditorState(this);
   NS_ADDREF(mInputData.mState);
   
   if (!gUploadLastDir)
     nsHTMLInputElement::InitUploadLastDir();
 }
 
 nsHTMLInputElement::~nsHTMLInputElement()
@@ -716,17 +717,17 @@ nsHTMLInputElement::Clone(nsINodeInfo *a
 
   switch (mType) {
     case NS_FORM_INPUT_EMAIL:
     case NS_FORM_INPUT_SEARCH:
     case NS_FORM_INPUT_TEXT:
     case NS_FORM_INPUT_PASSWORD:
     case NS_FORM_INPUT_TEL:
     case NS_FORM_INPUT_URL:
-      if (GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)) {
+      if (GetValueChanged()) {
         // We don't have our default value anymore.  Set our value on
         // the clone.
         // XXX GetValue should be const
         nsAutoString value;
         const_cast<nsHTMLInputElement*>(this)->GetValue(value);
         // SetValueInternal handles setting the VALUE_CHANGED bit for us
         it->SetValueInternal(value, PR_FALSE, PR_TRUE);
       }
@@ -817,18 +818,17 @@ nsHTMLInputElement::AfterSetAttr(PRInt32
       AddedToRadioGroup();
     }
 
     // If @value is changed and BF_VALUE_CHANGED is false, @value is the value
     // of the element so, if the value of the element is different than @value,
     // we have to re-set it. This is only the case when GetValueMode() returns
     // VALUE_MODE_VALUE.
     if (aName == nsGkAtoms::value &&
-        !GET_BOOLBIT(mBitField, BF_VALUE_CHANGED) &&
-        GetValueMode() == VALUE_MODE_VALUE) {
+        !GetValueChanged() && GetValueMode() == VALUE_MODE_VALUE) {
       SetDefaultValueAsValue();
     }
 
     //
     // Checked must be set no matter what type of control it is, since
     // GetChecked() must reflect the new value
     if (aName == nsGkAtoms::checked &&
         !GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED)) {
@@ -896,38 +896,43 @@ nsHTMLInputElement::AfterSetAttr(PRInt32
                 NS_EVENT_STATE_SUPPRESSED |
                 NS_EVENT_STATE_LOADING |
                 NS_EVENT_STATE_MOZ_READONLY |
                 NS_EVENT_STATE_MOZ_READWRITE |
                 NS_EVENT_STATE_REQUIRED |
                 NS_EVENT_STATE_OPTIONAL |
                 NS_EVENT_STATE_VALID |
                 NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID |
+                NS_EVENT_STATE_MOZ_UI_INVALID |
                 NS_EVENT_STATE_INDETERMINATE |
                 NS_EVENT_STATE_MOZ_PLACEHOLDER |
                 NS_EVENT_STATE_MOZ_SUBMITINVALID;
     }
 
     if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
         aName == nsGkAtoms::readonly) {
       UpdateValueMissingValidityState();
 
       // This *has* to be called *after* validity has changed.
       if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
         UpdateBarredFromConstraintValidation();
       }
 
       states |= NS_EVENT_STATE_REQUIRED | NS_EVENT_STATE_OPTIONAL |
-                NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+                NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     } else if (MaxLengthApplies() && aName == nsGkAtoms::maxlength) {
       UpdateTooLongValidityState();
-      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     } else if (aName == nsGkAtoms::pattern) {
       UpdatePatternMismatchValidityState();
-      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     }
 
     if (aNotify) {
       nsIDocument* doc = GetCurrentDoc();
 
       if (aName == nsGkAtoms::type) {
         UpdateEditableState();
       } else if (IsSingleLineTextControl(PR_FALSE) && aName == nsGkAtoms::readonly) {
@@ -1071,17 +1076,17 @@ nsHTMLInputElement::SetValue(const nsASt
         // setting the value of a "FILE" input widget requires the
         // UniversalFileRead privilege
         return NS_ERROR_DOM_SECURITY_ERR;
       }
       const PRUnichar *name = PromiseFlatString(aValue).get();
       return MozSetFileNameArray(&name, 1);
     }
     else {
-      ClearFiles();
+      ClearFiles(true);
     }
   }
   else {
     SetValueInternal(aValue, PR_FALSE, PR_TRUE);
   }
 
   return NS_OK;
 }
@@ -1165,17 +1170,17 @@ nsHTMLInputElement::MozSetFileNameArray(
       nsCOMPtr<nsIDOMFile> domFile = new nsDOMFile(file);
       files.AppendObject(domFile);
     } else {
       continue; // Not much we can do if the file doesn't exist
     }
 
   }
 
-  SetFiles(files);
+  SetFiles(files, true);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::MozIsTextField(PRBool aExcludePassword, PRBool* aResult)
 {
   *aResult = IsSingleLineTextControl(aExcludePassword);
@@ -1325,34 +1330,38 @@ nsHTMLInputElement::GetDisplayFileName(n
     }
     else {
       aValue.Append(NS_LITERAL_STRING(", ") + str);
     }
   }
 }
 
 void
-nsHTMLInputElement::SetFiles(const nsCOMArray<nsIDOMFile>& aFiles)
+nsHTMLInputElement::SetFiles(const nsCOMArray<nsIDOMFile>& aFiles,
+                             bool aSetValueChanged)
 {
   mFiles.Clear();
   mFiles.AppendObjects(aFiles);
 
   // No need to flush here, if there's no frame at this point we
   // don't need to force creation of one just to tell it about this
   // new value.  We just want the display to update as needed.
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
   if (formControlFrame) {
     nsAutoString readableValue;
     GetDisplayFileName(readableValue);
     formControlFrame->SetFormProperty(nsGkAtoms::value, readableValue);
   }
 
   UpdateFileList();
 
-  SetValueChanged(PR_TRUE);
+  if (aSetValueChanged) {
+    SetValueChanged(PR_TRUE);
+  }
+
   UpdateAllValidityStates(PR_TRUE);
 }
 
 const nsCOMArray<nsIDOMFile>&
 nsHTMLInputElement::GetFiles()
 {
   return mFiles;
 }
@@ -1425,22 +1434,35 @@ nsHTMLInputElement::SetValueInternal(con
   return nsGenericHTMLFormElement::SetAttr(kNameSpaceID_None,
                                            nsGkAtoms::value, aValue,
                                            PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::SetValueChanged(PRBool aValueChanged)
 {
+  PRBool valueChangedBefore = GetValueChanged();
+
   SET_BOOLBIT(mBitField, BF_VALUE_CHANGED, aValueChanged);
+
   if (!aValueChanged) {
     if (!IsSingleLineTextControl(PR_FALSE)) {
       FreeData();
     }
   }
+
+  if (valueChangedBefore != aValueChanged) {
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc) {
+      mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_UI_VALID |
+                                              NS_EVENT_STATE_MOZ_UI_INVALID);
+    }
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsHTMLInputElement::GetChecked(PRBool* aChecked)
 {
   *aChecked = GetChecked();
   return NS_OK;
@@ -1466,24 +1488,31 @@ nsHTMLInputElement::DoSetCheckedChanged(
   } else {
     SetCheckedChangedInternal(aCheckedChanged);
   }
 }
 
 void
 nsHTMLInputElement::SetCheckedChangedInternal(PRBool aCheckedChanged)
 {
+  PRBool checkedChangedBefore = GetCheckedChanged();
+
   SET_BOOLBIT(mBitField, BF_CHECKED_CHANGED, aCheckedChanged);
-}
-
-
-PRBool
-nsHTMLInputElement::GetCheckedChanged()
-{
-  return GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED);
+
+  // This method can't be called when we are not authorized to notify
+  // so we do not need a aNotify parameter.
+  if (checkedChangedBefore != aCheckedChanged) {
+    nsIDocument* document = GetCurrentDoc();
+    if (document) {
+      mozAutoDocUpdate upd(document, UPDATE_CONTENT_STATE, PR_TRUE);
+      document->ContentStatesChanged(this, nsnull,
+                                     NS_EVENT_STATE_MOZ_UI_VALID |
+                                     NS_EVENT_STATE_MOZ_UI_INVALID);
+    }
+  }
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::SetChecked(PRBool aChecked)
 {
   return DoSetChecked(aChecked, PR_TRUE, PR_TRUE);
 }
 
@@ -2076,26 +2105,49 @@ SelectTextFieldOnFocus()
 
 nsresult
 nsHTMLInputElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   if (!aVisitor.mPresContext) {
     return NS_OK;
   }
 
-  if (PlaceholderApplies() &&
-      HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
-      // TODO: checking if the value is empty could be a good idea but we do not
+  if (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
+      aVisitor.mEvent->message == NS_BLUR_CONTENT) {
+    nsEventStates states;
+
+    if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) {
+      // If the invalid UI is shown, we should show it while focusing (and
+      // update). Otherwise, we should not.
+      SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI,
+                  !IsValid() && ShouldShowInvalidUI());
+
+      // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
+      // UI while typing.
+      SET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI, ShouldShowValidUI());
+
+      // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor
+      // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
+    } else { // NS_BLUR_CONTENT
+      SET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI, PR_TRUE);
+      SET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI, PR_TRUE);
+      states |= NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
+    }
+
+    if (PlaceholderApplies() &&
+        HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
+        // TODO: checking if the value is empty could be a good idea but we do not
       // have a simple way to do that, see bug 585100
-      (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
-       aVisitor.mEvent->message == NS_BLUR_CONTENT)) {
+      states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
+    }
+
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER);
+      doc->ContentStatesChanged(this, nsnull, states);
     }
   }
 
   // ignore the activate event fired by the "Browse..." button
   // (file input controls fire their own) (bug 500885)
   if (mType == NS_FORM_INPUT_FILE) {
     nsCOMPtr<nsIContent> maybeButton =
       do_QueryInterface(aVisitor.mEvent->originalTarget);
@@ -2649,17 +2701,17 @@ nsHTMLInputElement::ParseAttribute(PRInt
         // That way the logic in SetValueInternal() will work right (that logic
         // makes assumptions about our frame based on mType, but we won't have
         // had time to recreate frames yet -- that happens later in the
         // SetAttr() process).
         if (newType == NS_FORM_INPUT_FILE || mType == NS_FORM_INPUT_FILE) {
           // This call isn't strictly needed any more since we'll never
           // confuse values and filenames. However it's there for backwards
           // compat.
-          ClearFiles();
+          ClearFiles(false);
         }
 
         HandleTypeChange(newType);
       }
 
       return success;
     }
     if (aAttribute == nsGkAtoms::width) {
@@ -2979,17 +3031,17 @@ nsHTMLInputElement::Reset()
   switch (GetValueMode()) {
     case VALUE_MODE_VALUE:
       return SetDefaultValueAsValue();
     case VALUE_MODE_DEFAULT_ON:
       PRBool resetVal;
       GetDefaultChecked(&resetVal);
       return DoSetChecked(resetVal, PR_TRUE, PR_FALSE);
     case VALUE_MODE_FILENAME:
-      ClearFiles();
+      ClearFiles(false);
       return NS_OK;
     case VALUE_MODE_DEFAULT:
     default:
       return NS_OK;
   }
 }
 
 NS_IMETHODIMP
@@ -3145,17 +3197,17 @@ nsHTMLInputElement::SaveState()
       break;
     case NS_FORM_INPUT_EMAIL:
     case NS_FORM_INPUT_SEARCH:
     case NS_FORM_INPUT_TEXT:
     case NS_FORM_INPUT_TEL:
     case NS_FORM_INPUT_URL:
     case NS_FORM_INPUT_HIDDEN:
       {
-        if (GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)) {
+        if (GetValueChanged()) {
           inputState = new nsHTMLInputElementState();
           if (!inputState) {
             return NS_ERROR_OUT_OF_MEMORY;
           }
 
           nsAutoString value;
           GetValue(value);
           rv = nsLinebreakConverter::ConvertStringLineBreaks(
@@ -3269,17 +3321,39 @@ nsHTMLInputElement::IntrinsicState() con
 
   if (DoesRequiredApply() && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
     state |= NS_EVENT_STATE_REQUIRED;
   } else {
     state |= NS_EVENT_STATE_OPTIONAL;
   }
 
   if (IsCandidateForConstraintValidation()) {
-    state |= IsValid() ? NS_EVENT_STATE_VALID : NS_EVENT_STATE_INVALID;
+    if (IsValid()) {
+      state |= NS_EVENT_STATE_VALID;
+    } else {
+      state |= NS_EVENT_STATE_INVALID;
+
+      if (GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI) &&
+          ShouldShowInvalidUI()) {
+        state |= NS_EVENT_STATE_MOZ_UI_INVALID;
+      }
+    }
+
+    // :-moz-ui-valid applies if all of the following conditions are true:
+    // 1. The element is not focused, or had either :-moz-ui-valid or
+    //    :-moz-ui-invalid applying before it was focused ;
+    // 2. The element is either valid or isn't allowed to have
+    //    :-moz-ui-invalid applying ;
+    // 3. The rules to have :-moz-ui-valid applying are fulfilled
+    //    (see ShouldShowValidUI()).
+    if (GET_BOOLBIT(mBitField, BF_CAN_SHOW_VALID_UI) &&
+        (IsValid() || !GET_BOOLBIT(mBitField, BF_CAN_SHOW_INVALID_UI)) &&
+        ShouldShowValidUI()) {
+      state |= NS_EVENT_STATE_MOZ_UI_VALID;
+    }
   }
 
   if (PlaceholderApplies() && HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
       !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
     // TODO: we really need a GetValue(...) const method, see bug 585097
     nsTextEditorState* edState = GetEditorState();
     nsAutoString value;
 
@@ -3329,17 +3403,17 @@ nsHTMLInputElement::RestoreState(nsPresS
       case NS_FORM_INPUT_HIDDEN:
         {
           SetValueInternal(inputState->GetValue(), PR_FALSE, PR_TRUE);
           break;
         }
       case NS_FORM_INPUT_FILE:
         {
           const nsCOMArray<nsIDOMFile>& files = inputState->GetFiles();
-          SetFiles(files);
+          SetFiles(files, true);
           break;
         }
     }
   }
 
   if (aState->IsDisabledSet()) {
     SetDisabled(aState->GetDisabled());
   }
@@ -3355,20 +3429,20 @@ nsHTMLInputElement::AllowDrop()
   return mType != NS_FORM_INPUT_FILE;
 }
 
 /*
  * Radio group stuff
  */
 
 void
-nsHTMLInputElement::AddedToRadioGroup(PRBool aNotify)
+nsHTMLInputElement::AddedToRadioGroup()
 {
   // Make sure not to notify if we're still being created by the parser
-  aNotify = aNotify && !GET_BOOLBIT(mBitField, BF_PARSER_CREATING);
+  PRBool notify = !GET_BOOLBIT(mBitField, BF_PARSER_CREATING);
 
   //
   //  If the input element is not in a form and
   //  not in a document, we just need to return.
   //
   if (!mForm && !(IsInDoc() && GetParent())) {
     return;
   }
@@ -3380,30 +3454,30 @@ nsHTMLInputElement::AddedToRadioGroup(PR
   if (GetChecked()) {
     //
     // If it is checked, call "RadioSetChecked" to perform the selection/
     // deselection ritual.  This has the side effect of repainting the
     // radio button, but as adding a checked radio button into the group
     // should not be that common an occurrence, I think we can live with
     // that.
     //
-    RadioSetChecked(aNotify);
+    RadioSetChecked(notify);
   }
 
   //
   // For integrity purposes, we have to ensure that "checkedChanged" is
   // the same for this new element as for all the others in the group
   //
   PRBool checkedChanged = GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED);
   nsCOMPtr<nsIRadioVisitor> visitor;
   nsresult rv = NS_GetRadioGetCheckedChangedVisitor(&checkedChanged, this,
                                            getter_AddRefs(visitor));
   if (NS_FAILED(rv)) { return; }
   
-  VisitGroup(visitor, aNotify);
+  VisitGroup(visitor, notify);
   SetCheckedChangedInternal(checkedChanged);
   
   //
   // Add the radio to the radio group container.
   //
   nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
   if (container) {
     nsAutoString name;
@@ -3686,28 +3760,30 @@ NS_IMETHODIMP
 nsHTMLInputElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
     doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID);
+                                            NS_EVENT_STATE_VALID |
+                                            NS_EVENT_STATE_MOZ_UI_INVALID |
+                                            NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 PRBool
 nsHTMLInputElement::IsTooLong()
 {
   if (!MaxLengthApplies() ||
       !HasAttr(kNameSpaceID_None, nsGkAtoms::maxlength) ||
-      !GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)) {
+      !GetValueChanged()) {
     return PR_FALSE;
   }
 
   PRInt32 maxLength = -1;
   GetMaxLength(&maxLength);
 
   // Maxlength of -1 means parsing error.
   if (maxLength == -1) {
@@ -3819,17 +3895,20 @@ nsHTMLInputElement::HasPatternMismatch()
   }
 
   return !IsPatternMatching(value, pattern, doc);
 }
 
 void
 nsHTMLInputElement::UpdateTooLongValidityState()
 {
+  // TODO: this code will be re-enabled with bug 613016 and bug 613019.
+#if 0
   SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong());
+#endif
 }
 
 void
 nsHTMLInputElement::UpdateValueMissingValidityState()
 {
   SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
 }
 
@@ -3854,17 +3933,18 @@ nsHTMLInputElement::UpdateAllValiditySta
   UpdateTypeMismatchValidityState();
   UpdatePatternMismatchValidityState();
 
   if (validBefore != IsValid() && aNotify) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
       doc->ContentStatesChanged(this, nsnull,
-                                NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID);
+                                NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID);
     }
   }
 }
 
 void
 nsHTMLInputElement::UpdateBarredFromConstraintValidation()
 {
   SetBarredFromConstraintValidation(mType == NS_FORM_INPUT_HIDDEN ||
@@ -4337,17 +4417,17 @@ nsHTMLInputElement::GetDefaultValueFromC
       SanitizeValue(aValue);
     }
   }
 }
 
 NS_IMETHODIMP_(PRBool)
 nsHTMLInputElement::ValueChanged() const
 {
-  return GET_BOOLBIT(mBitField, BF_VALUE_CHANGED);
+  return GetValueChanged();
 }
 
 NS_IMETHODIMP_(void)
 nsHTMLInputElement::GetTextEditorValue(nsAString& aValue,
                                        PRBool aIgnoreWrap) const
 {
   nsTextEditorState *state = GetEditorState();
   if (state) {
@@ -4393,17 +4473,18 @@ nsHTMLInputElement::OnValueChanged(PRBoo
 }
 
 void
 nsHTMLInputElement::FieldSetDisabledChanged(nsEventStates aStates, PRBool aNotify)
 {
   UpdateValueMissingValidityState();
   UpdateBarredFromConstraintValidation();
 
-  aStates |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+  aStates |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+             NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
   nsGenericHTMLFormElement::FieldSetDisabledChanged(aStates, aNotify);
 }
 
 PRInt32
 nsHTMLInputElement::GetFilterFromAccept()
 {
   NS_ASSERTION(HasAttr(kNameSpaceID_None, nsGkAtoms::accept),
                "You should not call GetFileFiltersFromAccept if the element"
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -40,37 +40,39 @@
 #define nsHTMLInputElement_h__
 
 #include "nsGenericHTMLElement.h"
 #include "nsImageLoadingContent.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsITextControlElement.h"
 #include "nsIPhonetic.h"
 #include "nsIDOMNSEditableElement.h"
-
 #include "nsTextEditorState.h"
 #include "nsCOMPtr.h"
 #include "nsIConstraintValidation.h"
 #include "nsDOMFile.h"
+#include "nsHTMLFormElement.h" // for ShouldShowInvalidUI()
 
 //
 // Accessors for mBitField
 //
 #define BF_DISABLED_CHANGED 0
 #define BF_HANDLING_CLICK 1
 #define BF_VALUE_CHANGED 2
 #define BF_CHECKED_CHANGED 3
 #define BF_CHECKED 4
 #define BF_HANDLING_SELECT_EVENT 5
 #define BF_SHOULD_INIT_CHECKED 6
 #define BF_PARSER_CREATING 7
 #define BF_IN_INTERNAL_ACTIVATE 8
 #define BF_CHECKED_IS_TOGGLED 9
 #define BF_INDETERMINATE 10
 #define BF_INHIBIT_RESTORATION 11
+#define BF_CAN_SHOW_INVALID_UI 12
+#define BF_CAN_SHOW_VALID_UI 13
 
 #define GET_BOOLBIT(bitfield, field) (((bitfield) & (0x01 << (field))) \
                                         ? PR_TRUE : PR_FALSE)
 #define SET_BOOLBIT(bitfield, field, b) ((b) \
                                         ? ((bitfield) |=  (0x01 << (field))) \
                                         : ((bitfield) &= ~(0x01 << (field))))
 
 class nsDOMFileList;
@@ -211,21 +213,23 @@ public:
   NS_IMETHOD_(void) UpdatePlaceholderText(PRBool aNotify);
   NS_IMETHOD_(void) SetPlaceholderClass(PRBool aVisible, PRBool aNotify);
   NS_IMETHOD_(void) InitializeKeyboardEventListeners();
   NS_IMETHOD_(void) OnValueChanged(PRBool aNotify);
 
   // nsIFileControlElement
   void GetDisplayFileName(nsAString& aFileName) const;
   const nsCOMArray<nsIDOMFile>& GetFiles();
-  void SetFiles(const nsCOMArray<nsIDOMFile>& aFiles);
+  void SetFiles(const nsCOMArray<nsIDOMFile>& aFiles, bool aSetValueChanged);
 
   void SetCheckedChangedInternal(PRBool aCheckedChanged);
-  PRBool GetCheckedChanged();
-  void AddedToRadioGroup(PRBool aNotify = PR_TRUE);
+  PRBool GetCheckedChanged() const {
+    return GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED);
+  }
+  void AddedToRadioGroup();
   void WillRemoveFromRadioGroup();
   /**
    * Get the radio group container for this button (form or document)
    * @return the radio group container (or null if no form or document)
    */
   virtual already_AddRefed<nsIRadioGroupContainer> GetRadioGroupContainer();
 
  /**
@@ -362,26 +366,19 @@ protected:
   static PRBool IsPatternMatching(nsAString& aValue, nsAString& aPattern,
                                   nsIDocument* aDocument);
 
   // Helper method
   nsresult SetValueInternal(const nsAString& aValue,
                             PRBool aUserInput,
                             PRBool aSetValueChanged);
 
-  void ClearFiles() {
+  void ClearFiles(bool aSetValueChanged) {
     nsCOMArray<nsIDOMFile> files;
-    SetFiles(files);
-  }
-
-  void SetSingleFile(nsIDOMFile* aFile) {
-    nsCOMArray<nsIDOMFile> files;
-    nsCOMPtr<nsIDOMFile> file = aFile;
-    files.AppendObject(file);
-    SetFiles(files);
+    SetFiles(files, aSetValueChanged);
   }
 
   nsresult SetIndeterminateInternal(PRBool aValue,
                                     PRBool aShouldInvalidate);
 
   nsresult GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
 
   /**
@@ -535,16 +532,87 @@ protected:
 
   /**
    * Set the current default value to the value of the input element.
    * @note You should not call this method if GetValueMode() doesn't return
    * VALUE_MODE_VALUE.
    */
   nsresult SetDefaultValueAsValue();
 
+  /**
+   * Returns whether the value has been changed since the element has been created.
+   * @return Whether the value has been changed since the element has been created.
+   */
+  PRBool GetValueChanged() const {
+    return GET_BOOLBIT(mBitField, BF_VALUE_CHANGED);
+  }
+
+  /**
+   * Return if an invalid element should have a specific UI for being invalid
+   * (with :-moz-ui-invalid pseudo-class.
+   *
+   * @return Whether the invalid elemnet should have a UI for being invalid.
+   * @note The caller has to be sure the element is invalid before calling.
+   */
+  bool ShouldShowInvalidUI() const {
+    NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
+                             "element is valid!");
+
+    /**
+     * Always show the invalid UI if:
+     * - the form has already tried to be submitted but was invalid;
+     * - the element is suffering from a custom error;
+     * - the element has had its value changed
+     *
+     * Otherwise, show the invalid UI if the element's value has been changed.
+     */
+    if ((mForm && mForm->HasEverTriedInvalidSubmit()) ||
+        GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
+      return true;
+    }
+
+    switch (GetValueMode()) {
+      case VALUE_MODE_DEFAULT:
+        return true;
+      case VALUE_MODE_DEFAULT_ON:
+        return GetCheckedChanged();
+      case VALUE_MODE_VALUE:
+      case VALUE_MODE_FILENAME:
+        return GetValueChanged();
+      default:
+        NS_NOTREACHED("We should not be there: there are no other modes.");
+        return false;
+    }
+  }
+
+  /**
+   * Return whether an element should show the valid UI.
+   *
+   * @return Whether the valid UI should be shown.
+   * @note This doesn't take into account the validity of the element.
+   */
+  bool ShouldShowValidUI() const {
+    if (mForm && mForm->HasEverTriedInvalidSubmit()) {
+      return true;
+    }
+
+    switch (GetValueMode()) {
+      case VALUE_MODE_DEFAULT:
+        return true;
+      case VALUE_MODE_DEFAULT_ON:
+        return GetCheckedChanged();
+      case VALUE_MODE_VALUE:
+      case VALUE_MODE_FILENAME:
+        return GetValueChanged();
+      default:
+        NS_NOTREACHED("We should not be there: there are no other modes.");
+        return false;
+    }
+  }
+
   nsCOMPtr<nsIControllers> mControllers;
 
   /**
    * The type of this input (<input type=...>) as an integer.
    * @see nsIFormControl.h (specifically NS_FORM_INPUT_*)
    */
   PRUint8                  mType;
   /**
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -119,16 +119,19 @@ static PRLogModuleInfo* gMediaElementEve
 
 #include "nsIContentSecurityPolicy.h"
 #include "nsIChannelPolicy.h"
 #include "nsChannelPolicy.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
+// Number of milliseconds between timeupdate events as defined by spec
+#define TIMEUPDATE_MS 250
+
 // Under certain conditions there may be no-one holding references to
 // a media element from script, DOM parent, etc, but the element may still
 // fire meaningful events in the future so we can't destroy it yet:
 // 1) If the element is delaying the load event (or would be, if it were
 // in a document), then events up to loadeddata or error could be fired,
 // so we need to stay alive.
 // 2) If the element is not paused and playback has not ended, then
 // we will (or might) play, sending timeupdate and ended events and possibly
@@ -503,17 +506,17 @@ void nsHTMLMediaElement::AbortExistingLo
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
     mPaused = PR_TRUE;
 
     if (fireTimeUpdate) {
       // Since we destroyed the decoder above, the current playback position
       // will now be reported as 0. The playback position was non-zero when
       // we destroyed the decoder, so fire a timeupdate event so that the
       // change will be reflected in the controls.
-      DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
+      FireTimeUpdate(PR_FALSE);
     }
     DispatchEvent(NS_LITERAL_STRING("emptied"));
   }
 
   // We may have changed mPaused, mAutoplaying, mNetworkState and other
   // things which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
@@ -1143,17 +1146,17 @@ NS_IMETHODIMP nsHTMLMediaElement::Pause(
 
   PRBool oldPaused = mPaused;
   mPaused = PR_TRUE;
   mAutoplaying = PR_FALSE;
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
   if (!oldPaused) {
-    DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
+    FireTimeUpdate(PR_FALSE);
     DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
   }
 
   return NS_OK;
 }
 
 /* attribute float volume; */
 NS_IMETHODIMP nsHTMLMediaElement::GetVolume(float *aVolume)
@@ -1261,16 +1264,17 @@ nsHTMLMediaElement::nsHTMLMediaElement(a
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mChannels(0),
     mRate(0),
     mPreloadAction(PRELOAD_UNDEFINED),
     mMediaSize(-1,-1),
+    mLastCurrentTime(0.0),
     mAllowAudioData(PR_FALSE),
     mBegun(PR_FALSE),
     mLoadedFirstFrame(PR_FALSE),
     mAutoplaying(PR_TRUE),
     mAutoplayEnabled(PR_TRUE),
     mPaused(PR_TRUE),
     mMuted(PR_FALSE),
     mPlayingBeforeSeek(PR_FALSE),
@@ -1370,16 +1374,17 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
   // TODO: If the playback has ended, then the user agent must set
   // seek to the effective start.
   // TODO: The playback rate must be set to the default playback rate.
   if (mPaused) {
     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
     switch (mReadyState) {
     case nsIDOMHTMLMediaElement::HAVE_METADATA:
     case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
+      FireTimeUpdate(PR_FALSE);
       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
       break;
     case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
     case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
       DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
       break;
     }
   }
@@ -2010,23 +2015,24 @@ void nsHTMLMediaElement::Error(PRUint16 
 }
 
 void nsHTMLMediaElement::PlaybackEnded()
 {
   NS_ASSERTION(mDecoder->IsEnded(), "Decoder fired ended, but not in ended state");
   // We changed the state of IsPlaybackEnded which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
+  FireTimeUpdate(PR_FALSE);
   DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
 }
 
 void nsHTMLMediaElement::SeekStarted()
 {
   DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
-  DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
+  FireTimeUpdate(PR_FALSE);
 }
 
 void nsHTMLMediaElement::SeekCompleted()
 {
   mPlayingBeforeSeek = PR_FALSE;
   SetPlayedOrSeeked(PR_TRUE);
   DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
   // We changed whether we're seeking so we need to AddRemoveSelfReference
@@ -2071,16 +2077,17 @@ void nsHTMLMediaElement::UpdateReadyStat
     // a chance to run.
     // The arrival of more data can't change us out of this readyState.
     return;
   }
 
   if (aNextFrame != NEXT_FRAME_AVAILABLE) {
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
     if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
+      FireTimeUpdate(PR_FALSE);
       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
       mWaitingFired = PR_TRUE;
     }
     return;
   }
 
   // Now see if we should set HAVE_ENOUGH_DATA.
   // If it's something we don't know the size of, then we can't
@@ -2565,8 +2572,30 @@ void nsHTMLMediaElement::SetRequestHeade
   SetAcceptHeader(aChannel);
 
   // Set the Referer header
   nsIDocument* doc = GetOwnerDoc();
   if (doc) {
     aChannel->SetReferrer(doc->GetDocumentURI());
   }
 }
+
+void nsHTMLMediaElement::FireTimeUpdate(PRBool aPeriodic)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+
+  TimeStamp now = TimeStamp::Now();
+  float time = 0;
+  GetCurrentTime(&time);
+
+  // Fire a timupdate event if this is not a periodic update (i.e. it's a
+  // timeupdate event mandated by the spec), or if it's a periodic update
+  // and TIMEUPDATE_MS has passed since the last timeupdate event fired and
+  // the time has changed.
+  if (!aPeriodic ||
+      (mLastCurrentTime != time &&
+       (mTimeUpdateTime.IsNull() ||
+        now - mTimeUpdateTime >= TimeDuration::FromMilliseconds(TIMEUPDATE_MS)))) {
+    DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
+    mTimeUpdateTime = now;
+    mLastCurrentTime = time;
+  }
+}
--- a/content/html/content/src/nsHTMLOutputElement.cpp
+++ b/content/html/content/src/nsHTMLOutputElement.cpp
@@ -155,17 +155,19 @@ NS_IMETHODIMP
 nsHTMLOutputElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
     doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID);
+                                            NS_EVENT_STATE_VALID |
+                                            NS_EVENT_STATE_MOZ_UI_INVALID |
+                                            NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLOutputElement::Reset()
 {
@@ -199,17 +201,18 @@ nsHTMLOutputElement::ParseAttribute(PRIn
 
 nsEventStates
 nsHTMLOutputElement::IntrinsicState() const
 {
   nsEventStates states = nsGenericHTMLFormElement::IntrinsicState();
 
   // We don't have to call IsCandidateForConstraintValidation()
   // because <output> can't be barred from constraint validation.
-  states |= IsValid() ? NS_EVENT_STATE_VALID : NS_EVENT_STATE_INVALID;
+  states |= IsValid() ? NS_EVENT_STATE_VALID | NS_EVENT_STATE_MOZ_UI_VALID
+                      : NS_EVENT_STATE_INVALID | NS_EVENT_STATE_MOZ_UI_INVALID;
 
   return states;
 }
 
 NS_IMETHODIMP
 nsHTMLOutputElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
   return nsGenericHTMLFormElement::GetForm(aForm);
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -96,17 +96,17 @@ nsSafeOptionListMutation::nsSafeOptionLi
     mTopLevelMutation = !select->mMutating;
     if (mTopLevelMutation) {
       select->mMutating = PR_TRUE;
     } else {
       // This is very unfortunate, but to handle mutation events properly,
       // option list must be up-to-date before inserting or removing options.
       // Fortunately this is called only if mutation event listener
       // adds or removes options.
-      select->RebuildOptionsArray();
+      select->RebuildOptionsArray(aNotify);
     }
     nsresult rv;
     if (aKid) {
       rv = mSelect->WillAddOptions(aKid, aParent, aIndex, aNotify);
     } else {
       rv = mSelect->WillRemoveOptions(aParent, aIndex, aNotify);
     }
     mNeedsRebuild = NS_FAILED(rv);
@@ -114,17 +114,17 @@ nsSafeOptionListMutation::nsSafeOptionLi
 }
 
 nsSafeOptionListMutation::~nsSafeOptionListMutation()
 {
   if (mSelect) {
     nsHTMLSelectElement* select =
       static_cast<nsHTMLSelectElement*>(mSelect.get());
     if (mNeedsRebuild || (mTopLevelMutation && mGuard.Mutated(1))) {
-      select->RebuildOptionsArray();
+      select->RebuildOptionsArray(PR_TRUE);
     }
     if (mTopLevelMutation) {
       select->mMutating = PR_FALSE;
     }
 #ifdef DEBUG
     select->VerifyOptionsArray();
 #endif
   }
@@ -143,16 +143,20 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER
 nsHTMLSelectElement::nsHTMLSelectElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                                          FromParser aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mOptions(new nsHTMLOptionCollection(this)),
     mIsDoneAddingChildren(!aFromParser),
     mDisabledChanged(PR_FALSE),
     mMutating(PR_FALSE),
     mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
+    mSelectionHasChanged(PR_FALSE),
+    mDefaultSelectionSet(PR_FALSE),
+    mCanShowInvalidUI(PR_TRUE),
+    mCanShowValidUI(PR_TRUE),
     mNonOptionChildren(0),
     mOptGroupCount(0),
     mSelectedIndex(-1)
 {
   // FIXME: Bug 328908, set mOptions in an Init function and get rid of null
   // checks.
 
   // DoneAddingChildren() will be called later if it's from the parser,
@@ -205,17 +209,19 @@ NS_IMETHODIMP
 nsHTMLSelectElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
     doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID);
+                                            NS_EVENT_STATE_VALID |
+                                            NS_EVENT_STATE_MOZ_UI_INVALID |
+                                            NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLSelectElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
@@ -260,16 +266,17 @@ nsHTMLSelectElement::InsertOptionsIntoLi
   nsresult rv = InsertOptionsIntoListRecurse(aOptions, &insertIndex, aDepth);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Deal with the selected list
   if (insertIndex - aListIndex) {
     // Fix the currently selected index
     if (aListIndex <= mSelectedIndex) {
       mSelectedIndex += (insertIndex - aListIndex);
+      SetSelectionChanged(PR_TRUE, aNotify);
     }
 
     // Get the frame stuff for notification. No need to flush here
     // since if there's no frame for the select yet the select will
     // get into the right state once it's created.
     nsISelectControlFrame* selectFrame = nsnull;
     nsWeakFrame weakSelectFrame;
     PRBool didGetFrame = PR_FALSE;
@@ -335,37 +342,40 @@ nsHTMLSelectElement::RemoveOptionsFromLi
       }
     }
 
     // Fix the selected index
     if (aListIndex <= mSelectedIndex) {
       if (mSelectedIndex < (aListIndex+numRemoved)) {
         // aListIndex <= mSelectedIndex < aListIndex+numRemoved
         // Find a new selected index if it was one of the ones removed.
-        FindSelectedIndex(aListIndex);
+        FindSelectedIndex(aListIndex, aNotify);
       } else {
         // Shift the selected index if something in front of it was removed
         // aListIndex+numRemoved <= mSelectedIndex
         mSelectedIndex -= numRemoved;
+        SetSelectionChanged(PR_TRUE, aNotify);
       }
     }
 
     // Select something in case we removed the selected option on a
     // single select
     if (!CheckSelectSomething(aNotify) && mSelectedIndex == -1) {
       // Update the validity state in case of we've just removed the last
       // option.
       UpdateValueMissingValidityState();
 
       if (aNotify) {
         nsIDocument* doc = GetCurrentDoc();
         if (doc) {
           MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
           doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
-                                                  NS_EVENT_STATE_INVALID);
+                                                  NS_EVENT_STATE_INVALID |
+                                                  NS_EVENT_STATE_MOZ_UI_INVALID |
+                                                  NS_EVENT_STATE_MOZ_UI_VALID);
         }
       }
     }
   }
 
   return NS_OK;
 }
 
@@ -803,35 +813,43 @@ nsHTMLSelectElement::SetLength(PRUint32 
 NS_IMETHODIMP
 nsHTMLSelectElement::GetSelectedIndex(PRInt32* aValue)
 {
   *aValue = mSelectedIndex;
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsHTMLSelectElement::SetSelectedIndex(PRInt32 aIndex)
+nsresult
+nsHTMLSelectElement::SetSelectedIndexInternal(PRInt32 aIndex, PRBool aNotify)
 {
   PRInt32 oldSelectedIndex = mSelectedIndex;
 
   nsresult rv = SetOptionsSelectedByIndex(aIndex, aIndex, PR_TRUE,
-                                          PR_TRUE, PR_TRUE, PR_TRUE, nsnull);
+                                          PR_TRUE, PR_TRUE, aNotify, nsnull);
 
   if (NS_SUCCEEDED(rv)) {
     nsISelectControlFrame* selectFrame = GetSelectFrame();
     if (selectFrame) {
       rv = selectFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex);
     }
   }
 
+  SetSelectionChanged(PR_TRUE, aNotify);
+
   return rv;
 }
 
 NS_IMETHODIMP
+nsHTMLSelectElement::SetSelectedIndex(PRInt32 aIndex)
+{
+  return SetSelectedIndexInternal(aIndex, PR_TRUE);
+}
+
+NS_IMETHODIMP
 nsHTMLSelectElement::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
                                     PRInt32 aStartIndex, PRBool aForward,
                                     PRInt32* aIndex)
 {
   nsCOMPtr<nsINode> option = do_QueryInterface(aOption);
   return mOptions->GetOptionIndex(option->AsElement(), aStartIndex, aForward, aIndex);
 }
 
@@ -851,18 +869,19 @@ nsHTMLSelectElement::OnOptionSelected(ns
                                       PRInt32 aIndex,
                                       PRBool aSelected,
                                       PRBool aChangeOptionState,
                                       PRBool aNotify)
 {
   // Set the selected index
   if (aSelected && (aIndex < mSelectedIndex || mSelectedIndex < 0)) {
     mSelectedIndex = aIndex;
+    SetSelectionChanged(PR_TRUE, aNotify);
   } else if (!aSelected && aIndex == mSelectedIndex) {
-    FindSelectedIndex(aIndex+1);
+    FindSelectedIndex(aIndex+1, aNotify);
   }
 
   if (aChangeOptionState) {
     // Tell the option to get its bad self selected
     nsCOMPtr<nsIDOMNode> option;
     Item(aIndex, getter_AddRefs(option));
     if (option) {
       nsRefPtr<nsHTMLOptionElement> optionElement = 
@@ -877,30 +896,34 @@ nsHTMLSelectElement::OnOptionSelected(ns
   }
 
   UpdateValueMissingValidityState();
   if (aNotify) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
       doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
-                                              NS_EVENT_STATE_INVALID);
+                                              NS_EVENT_STATE_INVALID |
+                                              NS_EVENT_STATE_MOZ_UI_INVALID |
+                                              NS_EVENT_STATE_MOZ_UI_VALID);
     }
   }
 }
 
 void
-nsHTMLSelectElement::FindSelectedIndex(PRInt32 aStartIndex)
+nsHTMLSelectElement::FindSelectedIndex(PRInt32 aStartIndex, PRBool aNotify)
 {
   mSelectedIndex = -1;
+  SetSelectionChanged(PR_TRUE, aNotify);
   PRUint32 len;
   GetLength(&len);
   for (PRInt32 i=aStartIndex; i<(PRInt32)len; i++) {
     if (IsOptionSelectedByIndex(i)) {
       mSelectedIndex = i;
+      SetSelectionChanged(PR_TRUE, aNotify);
       break;
     }
   }
 }
 
 // XXX Consider splitting this into two functions for ease of reading:
 // SelectOptionsByIndex(startIndex, endIndex, clearAll, checkDisabled)
 //   startIndex, endIndex - the range of options to turn on
@@ -1224,17 +1247,17 @@ nsHTMLSelectElement::SetValue(const nsAS
         nsCOMPtr<nsIDOMHTMLOptionElement> option = do_QueryInterface(node);
 
         if (option) {
           nsAutoString optionVal;
 
           option->GetValue(optionVal);
 
           if (optionVal.Equals(aValue)) {
-            SetSelectedIndex((PRInt32)i);
+            SetSelectedIndexInternal((PRInt32)i, PR_TRUE);
 
             break;
           }
         }
       }
     }
   }
 
@@ -1309,26 +1332,28 @@ nsHTMLSelectElement::SelectSomething(PRB
 
   PRUint32 count;
   GetLength(&count);
   for (PRUint32 i=0; i<count; i++) {
     PRBool disabled;
     nsresult rv = IsOptionDisabled(i, &disabled);
 
     if (NS_FAILED(rv) || !disabled) {
-      rv = SetSelectedIndex(i);
+      rv = SetSelectedIndexInternal(i, aNotify);
       NS_ENSURE_SUCCESS(rv, PR_FALSE);
 
       UpdateValueMissingValidityState();
       if (aNotify) {
         nsIDocument* doc = GetCurrentDoc();
         if (doc) {
           MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
           doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
-                                                  NS_EVENT_STATE_INVALID);
+                                                  NS_EVENT_STATE_INVALID |
+                                                  NS_EVENT_STATE_MOZ_UI_INVALID |
+                                                  NS_EVENT_STATE_MOZ_UI_VALID);
         }
       }
 
       return PR_TRUE;
     }
   }
 
   return PR_FALSE;
@@ -1368,20 +1393,22 @@ nsresult
 nsHTMLSelectElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                   const nsAString* aValue, PRBool aNotify)
 {
   nsEventStates states;
 
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::disabled) {
       UpdateBarredFromConstraintValidation();
-      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     } else if (aName == nsGkAtoms::required) {
       UpdateValueMissingValidityState();
-      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     }
   }
 
   if (aNotify && !states.IsEmpty()) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
       doc->ContentStatesChanged(this, nsnull, states);
@@ -1400,17 +1427,17 @@ nsHTMLSelectElement::UnsetAttr(PRInt32 a
       aAttribute == nsGkAtoms::multiple) {
     // We're changing from being a multi-select to a single-select.
     // Make sure we only have one option selected before we do that.
     // Note that this needs to come before we really unset the attr,
     // since SetOptionsSelectedByIndex does some bail-out type
     // optimization for cases when the select is not multiple that
     // would lead to only a single option getting deselected.
     if (mSelectedIndex >= 0) {
-      SetSelectedIndex(mSelectedIndex);
+      SetSelectedIndexInternal(mSelectedIndex, aNotify);
     }
   }
 
   nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID, aAttribute,
                                                     aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aNotify && aNameSpaceID == kNameSpaceID_None &&
@@ -1418,22 +1445,16 @@ nsHTMLSelectElement::UnsetAttr(PRInt32 a
     // We might have become a combobox; make sure _something_ gets
     // selected in that case
     CheckSelectSomething(aNotify);
   }
 
   return rv;
 }
 
-PRBool
-nsHTMLSelectElement::IsDoneAddingChildren()
-{
-  return mIsDoneAddingChildren;
-}
-
 nsresult
 nsHTMLSelectElement::DoneAddingChildren(PRBool aHaveNotified)
 {
   mIsDoneAddingChildren = PR_TRUE;
 
   nsISelectControlFrame* selectFrame = GetSelectFrame();
 
   // If we foolishly tried to restore before we were done adding
@@ -1457,16 +1478,18 @@ nsHTMLSelectElement::DoneAddingChildren(
   // must be selected)
   if (!CheckSelectSomething(PR_FALSE)) {
     // If an option has @selected set, it will be selected during parsing but
     // with an empty value. We have to make sure the select element updates it's
     // validity state to take this into account.
     UpdateValueMissingValidityState();
   }
 
+  mDefaultSelectionSet = PR_TRUE;
+
   return NS_OK;
 }
 
 PRBool
 nsHTMLSelectElement::ParseAttribute(PRInt32 aNamespaceID,
                                     nsIAtom* aAttribute,
                                     const nsAString& aValue,
                                     nsAttrValue& aResult)
@@ -1538,23 +1561,73 @@ nsHTMLSelectElement::PreHandleEvent(nsEv
         uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED) {
       return NS_OK;
     }
   }
 
   return nsGenericHTMLFormElement::PreHandleEvent(aVisitor);
 }
 
+nsresult
+nsHTMLSelectElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
+{
+  if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) {
+    // If the invalid UI is shown, we should show it while focused and
+    // update the invalid/valid UI.
+    mCanShowInvalidUI = !IsValid() && ShouldShowInvalidUI();
+
+    // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
+    // UI while focused.
+    mCanShowValidUI = ShouldShowValidUI();
+
+    // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor
+    // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
+  } else if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
+    mCanShowInvalidUI = PR_TRUE;
+    mCanShowValidUI = PR_TRUE;
+
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc) {
+      MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_UI_VALID |
+                                              NS_EVENT_STATE_MOZ_UI_INVALID);
+    }
+  }
+
+  return nsGenericHTMLFormElement::PostHandleEvent(aVisitor);
+}
+
 nsEventStates
 nsHTMLSelectElement::IntrinsicState() const
 {
   nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
 
   if (IsCandidateForConstraintValidation()) {
-    state |= IsValid() ? NS_EVENT_STATE_VALID : NS_EVENT_STATE_INVALID;
+    if (IsValid()) {
+      state |= NS_EVENT_STATE_VALID;
+    } else {
+      state |= NS_EVENT_STATE_INVALID;
+
+      if (mCanShowInvalidUI && ShouldShowInvalidUI()) {
+        state |= NS_EVENT_STATE_MOZ_UI_INVALID;
+      }
+    }
+
+    // :-moz-ui-valid applies if all the following are true:
+    // 1. The element is not focused, or had either :-moz-ui-valid or
+    //    :-moz-ui-invalid applying before it was focused ;
+    // 2. The element is either valid or isn't allowed to have
+    //    :-moz-ui-invalid applying ;
+    // 3. The rules to have :-moz-ui-valid applying are fulfilled
+    //    (see ShouldShowValidUI()).
+    if (mCanShowValidUI &&
+        (IsValid() || !mCanShowInvalidUI) &&
+        ShouldShowValidUI()) {
+      state |= NS_EVENT_STATE_MOZ_UI_VALID;
+    }
   }
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
     state |= NS_EVENT_STATE_REQUIRED;
   } else {
     state |= NS_EVENT_STATE_OPTIONAL;
   }
 
@@ -1689,16 +1762,18 @@ nsHTMLSelectElement::Reset()
 
   //
   // If nothing was selected and it's not multiple, select something
   //
   if (numSelected == 0 && IsCombobox()) {
     SelectSomething(PR_TRUE);
   }
 
+  SetSelectionChanged(PR_FALSE, PR_TRUE);
+
   //
   // Let the frame know we were reset
   //
   // Don't flush, if there's no frame yet it won't care about us being
   // reset even if we forced it to be created now.
   //
   DispatchContentReset();
 
@@ -1816,21 +1891,21 @@ AddOptionsRecurse(nsIContent* aRoot, nsH
     }
     else if (IsOptGroup(child)) {
       AddOptionsRecurse(child, aArray);
     }
   }
 }
 
 void
-nsHTMLSelectElement::RebuildOptionsArray()
+nsHTMLSelectElement::RebuildOptionsArray(PRBool aNotify)
 {
   mOptions->Clear();
   AddOptionsRecurse(this, mOptions);
-  FindSelectedIndex(0);
+  FindSelectedIndex(0, aNotify);
 }
 
 bool
 nsHTMLSelectElement::IsValueMissing()
 {
   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
     return false;
   }
@@ -2225,12 +2300,33 @@ nsHTMLSelectElement::UpdateBarredFromCon
   SetBarredFromConstraintValidation(IsDisabled());
 }
 
 void
 nsHTMLSelectElement::FieldSetDisabledChanged(nsEventStates aStates, PRBool aNotify)
 {
   UpdateBarredFromConstraintValidation();
 
-  aStates |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+  aStates |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+             NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
   nsGenericHTMLFormElement::FieldSetDisabledChanged(aStates, aNotify);
 }
 
+void
+nsHTMLSelectElement::SetSelectionChanged(PRBool aValue, PRBool aNotify)
+{
+  if (!mDefaultSelectionSet) {
+    return;
+  }
+
+  PRBool previousSelectionChangedValue = mSelectionHasChanged;
+  mSelectionHasChanged = aValue;
+
+  if (aNotify && mSelectionHasChanged != previousSelectionChangedValue) {
+    nsIDocument* doc = GetCurrentDoc();
+    if (doc) {
+      MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
+      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_UI_INVALID |
+                                              NS_EVENT_STATE_MOZ_UI_VALID);
+    }
+  }
+}
+
--- a/content/html/content/src/nsHTMLSelectElement.h
+++ b/content/html/content/src/nsHTMLSelectElement.h
@@ -56,16 +56,17 @@
 
 // PresState
 #include "nsXPCOM.h"
 #include "nsPresState.h"
 #include "nsIComponentManager.h"
 #include "nsCheapSets.h"
 #include "nsLayoutErrors.h"
 #include "nsHTMLOptionElement.h"
+#include "nsHTMLFormElement.h"
 
 class nsHTMLSelectElement;
 
 /**
  * The collection of options in the select (what you get back when you do
  * select.options in DOM)
  */
 class nsHTMLOptionCollection: public nsIDOMHTMLOptionsCollection,
@@ -262,16 +263,17 @@ public:
   // nsIDOMHTMLSelectElement
   NS_DECL_NSIDOMHTMLSELECTELEMENT
 
   // nsIDOMHTMLSelectElement_Mozilla_2_0_Branch
   NS_DECL_NSIDOMHTMLSELECTELEMENT_MOZILLA_2_0_BRANCH
 
   // nsIContent
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
+  virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
   virtual PRBool IsHTMLFocusable(PRBool aWithMouse, PRBool *aIsFocusable, PRInt32 *aTabIndex);
   virtual nsresult InsertChildAt(nsIContent* aKid, PRUint32 aIndex,
                                  PRBool aNotify);
   virtual nsresult RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationEvent = PR_TRUE);
 
   // Overriden nsIFormControl methods
   NS_IMETHOD_(PRUint32) GetType() const { return NS_FORM_SELECT; }
@@ -296,17 +298,19 @@ public:
   virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                  const nsAString* aValue, PRBool aNotify);
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                 const nsAString* aValue, PRBool aNotify);
   virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                              PRBool aNotify);
   
   virtual nsresult DoneAddingChildren(PRBool aHaveNotified);
-  virtual PRBool IsDoneAddingChildren();
+  virtual PRBool IsDoneAddingChildren() {
+    return mIsDoneAddingChildren;
+  }
 
   virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                               PRInt32 aModType) const;
@@ -343,17 +347,17 @@ protected:
    * @return whether the option at the index is selected
    */
   PRBool IsOptionSelectedByIndex(PRInt32 aIndex);
   /**
    * Starting with (and including) aStartIndex, find the first selected index
    * and set mSelectedIndex to it.
    * @param aStartIndex the index to start with
    */
-  void FindSelectedIndex(PRInt32 aStartIndex);
+  void FindSelectedIndex(PRInt32 aStartIndex, PRBool aNotify);
   /**
    * Select some option if possible (generally the first non-disabled option).
    * @return true if something was selected, false otherwise
    */
   PRBool SelectSomething(PRBool aNotify);
   /**
    * Call SelectSomething(), but only if nothing is selected
    * @see SelectSomething()
@@ -491,41 +495,95 @@ protected:
    * Helper method for dispatching ContentReset notifications to list
    * and combo box frames.
    */
   void DispatchContentReset();
 
   /**
    * Rebuilds the options array from scratch as a fallback in error cases.
    */
-  void RebuildOptionsArray();
+  void RebuildOptionsArray(PRBool aNotify);
 
 #ifdef DEBUG
   void VerifyOptionsArray();
 #endif
 
   virtual PRBool AcceptAutofocus() const
   {
     return PR_TRUE;
   }
 
+  nsresult SetSelectedIndexInternal(PRInt32 aIndex, PRBool aNotify);
+
+  void SetSelectionChanged(PRBool aValue, PRBool aNotify);
+
+  /**
+   * Return whether an invalid element should have a specific UI for being invalid
+   * (with :-moz-ui-invalid pseudo-class).
+   *
+   * @return Whether the invalid element should have a UI for being invalid.
+   * @note The caller has to be sure the element is invalid before calling.
+   */
+  bool ShouldShowInvalidUI() const {
+    NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
+                             "element is valid!");
+
+    /**
+     * Always show the invalid UI if:
+     * - the form has already tried to be submitted but was invalid;
+     * - the element is suffering from a custom error;
+     *
+     * Otherwise, show the invalid UI if the selection has been changed.
+     */
+    return mSelectionHasChanged ||
+           (mForm && mForm->HasEverTriedInvalidSubmit()) ||
+           GetValidityState(VALIDITY_STATE_CUSTOM_ERROR);
+  }
+
+  /**
+   * Return whether an element should show the valid UI.
+   *
+   * @return Whether the valid UI should be shown.
+   * @note This doesn't take into account the validity of the element.
+   */
+  bool ShouldShowValidUI() const {
+    return mSelectionHasChanged ||
+           (mForm && mForm->HasEverTriedInvalidSubmit());
+  }
   /** The options[] array */
   nsRefPtr<nsHTMLOptionCollection> mOptions;
   /** false if the parser is in the middle of adding children. */
   PRPackedBool    mIsDoneAddingChildren;
   /** true if our disabled state has changed from the default **/
   PRPackedBool    mDisabledChanged;
   /** true if child nodes are being added or removed.
    *  Used by nsSafeOptionListMutation.
    */
   PRPackedBool    mMutating;
   /**
    * True if DoneAddingChildren will get called but shouldn't restore state.
    */
   PRPackedBool    mInhibitStateRestoration;
+  /**
+   * True if the selection has changed since the element's creation.
+   */
+  PRPackedBool    mSelectionHasChanged;
+  /**
+   * True if the default selected option has been set.
+   */
+  PRPackedBool    mDefaultSelectionSet;
+  /**
+   * True if :-moz-ui-invalid can be shown.
+   */
+  PRPackedBool    mCanShowInvalidUI;
+  /**
+   * True if :-moz-ui-valid can be shown.
+   */
+  PRPackedBool    mCanShowValidUI;
+
   /** The number of non-options as children of the select */
   PRUint32  mNonOptionChildren;
   /** The number of optgroups anywhere under the select */
   PRUint32  mOptGroupCount;
   /**
    * The current selected index for selectedIndex (will be the first selected
    * index if multiple are selected)
    */
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -73,16 +73,17 @@
 #include "nsLayoutUtils.h"
 #include "nsLayoutErrors.h"
 #include "nsStubMutationObserver.h"
 #include "nsDOMError.h"
 #include "mozAutoDocUpdate.h"
 #include "nsISupportsPrimitives.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIConstraintValidation.h"
+#include "nsHTMLFormElement.h"
 
 #include "nsTextEditorState.h"
 
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kXULControllersCID,  NS_XULCONTROLLERS_CID);
 
 #define NS_NO_CONTENT_DISPATCH (1 << 0)
@@ -232,19 +233,24 @@ protected:
   PRPackedBool             mHandlingSelect;
   /** Whether or not we are done adding children (always PR_TRUE if not
       created by a parser */
   PRPackedBool             mDoneAddingChildren;
   /** Whether state restoration should be inhibited in DoneAddingChildren. */
   PRPackedBool             mInhibitStateRestoration;
   /** Whether our disabled state has changed from the default **/
   PRPackedBool             mDisabledChanged;
+  /** Whether we should make :-moz-ui-invalid apply on the element. **/
+  PRPackedBool             mCanShowInvalidUI;
+  /** Whether we should make :-moz-ui-valid apply on the element. **/
+  PRPackedBool             mCanShowValidUI;
+
   /** The state of the text editor (selection controller and the editor) **/
   nsRefPtr<nsTextEditorState> mState;
-  
+
   NS_IMETHOD SelectAll(nsPresContext* aPresContext);
   /**
    * Get the value, whether it is from the content or the frame.
    * @param aValue the value [out]
    * @param aIgnoreWrap whether to ignore the wrap attribute when getting the
    *        value.  If this is true, linebreaks will not be inserted even if
    *        wrap=hard.
    */
@@ -265,16 +271,49 @@ protected:
    * parent; we should only respond to the change if aContent is non-anonymous.
    */
   void ContentChanged(nsIContent* aContent);
 
   virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName,
                                 const nsAString* aValue, PRBool aNotify);
 
   /**
+   * Return if an invalid element should have a specific UI for being invalid
+   * (with :-moz-ui-invalid pseudo-class.
+   *
+   * @return Whether the invalid elemnet should have a UI for being invalid.
+   * @note The caller has to be sure the element is invalid before calling.
+   */
+  bool ShouldShowInvalidUI() const {
+    NS_ASSERTION(!IsValid(), "You should not call ShouldShowInvalidUI if the "
+                             "element is valid!");
+
+    /**
+     * Always show the invalid UI if:
+     * - the form has already tried to be submitted but was invalid;
+     * - the element is suffering from a custom error;
+     *
+     * Otherwise, show the invalid UI if the element's value has been changed.
+     */
+
+    return (mForm && mForm->HasEverTriedInvalidSubmit()) ||
+           mValueChanged || GetValidityState(VALIDITY_STATE_CUSTOM_ERROR);
+  }
+
+  /**
+   * Return whether an element should show the valid UI.
+   *
+   * @return Whether the valid UI should be shown.
+   * @note This doesn't take into account the validity of the element.
+   */
+  bool ShouldShowValidUI() const {
+    return (mForm && mForm->HasEverTriedInvalidSubmit()) || mValueChanged;
+  }
+
+  /**
    * Get the mutable state of the element.
    */
   PRBool IsMutable() const;
 };
 
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
 
@@ -282,16 +321,18 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER
 nsHTMLTextAreaElement::nsHTMLTextAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                                              FromParser aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mValueChanged(PR_FALSE),
     mHandlingSelect(PR_FALSE),
     mDoneAddingChildren(!aFromParser),
     mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
     mDisabledChanged(PR_FALSE),
+    mCanShowInvalidUI(PR_TRUE),
+    mCanShowValidUI(PR_TRUE),
     mState(new nsTextEditorState(this))
 {
   AddMutationObserver(this);
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLTextAreaElement)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLTextAreaElement,
@@ -556,26 +597,35 @@ nsHTMLTextAreaElement::SetUserInput(cons
   }
   SetValueInternal(aValue, PR_TRUE);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLTextAreaElement::SetValueChanged(PRBool aValueChanged)
 {
+  PRBool previousValue = mValueChanged;
+
   mValueChanged = aValueChanged;
   if (!aValueChanged && !mState->IsEmpty()) {
     mState->EmptyValue();
   }
 
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
+  if (mValueChanged != previousValue) {
+    nsEventStates states = NS_EVENT_STATE_MOZ_UI_VALID |
+                           NS_EVENT_STATE_MOZ_UI_INVALID;
+
+    if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
+      states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
+    }
+
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER);
+      doc->ContentStatesChanged(this, nsnull, states);
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue)
@@ -716,25 +766,47 @@ nsHTMLTextAreaElement::PreHandleEvent(ns
 
 nsresult
 nsHTMLTextAreaElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   if (aVisitor.mEvent->message == NS_FORM_SELECTED) {
     mHandlingSelect = PR_FALSE;
   }
 
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
+  if (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
+      aVisitor.mEvent->message == NS_BLUR_CONTENT) {
+    nsEventStates states;
+
+    if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) {
+      // If the invalid UI is shown, we should show it while focusing (and
+      // update). Otherwise, we should not.
+      mCanShowInvalidUI = !IsValid() && ShouldShowInvalidUI();
+
+      // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
+      // UI while typing.
+      mCanShowValidUI = ShouldShowValidUI();
+
+      // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor
+      // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
+    } else { // NS_BLUR_CONTENT
+      mCanShowInvalidUI = PR_TRUE;
+      mCanShowValidUI = PR_TRUE;
+      states |= NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
+    }
+
+    if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
       // TODO: checking if the value is empty could be a good idea but we do not
       // have a simple way to do that, see bug 585100
-      (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
-       aVisitor.mEvent->message == NS_BLUR_CONTENT)) {
+      states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
+    }
+
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER);
+      doc->ContentStatesChanged(this, nsnull, states);
     }
   }
 
   // Reset the flag for other content besides this text field
   aVisitor.mEvent->flags |= (aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH)
     ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE;
 
   return NS_OK;
@@ -907,17 +979,18 @@ nsHTMLTextAreaElement::SubmitNamesValues
   if (IsDisabled()) {
     return NS_OK;
   }
 
   //
   // Get the name (if no name, no submit)
   //
   nsAutoString name;
-  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
+  GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
+  if (name.IsEmpty()) {
     return NS_OK;
   }
 
   //
   // Get the value
   //
   nsAutoString value;
   GetValueInternal(value, PR_FALSE);
@@ -998,17 +1071,42 @@ nsHTMLTextAreaElement::IntrinsicState() 
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
     state |= NS_EVENT_STATE_REQUIRED;
   } else {
     state |= NS_EVENT_STATE_OPTIONAL;
   }
 
   if (IsCandidateForConstraintValidation()) {
-    state |= IsValid() ? NS_EVENT_STATE_VALID : NS_EVENT_STATE_INVALID;
+    if (IsValid()) {
+      state |= NS_EVENT_STATE_VALID;
+    } else {
+      state |= NS_EVENT_STATE_INVALID;
+      // NS_EVENT_STATE_MOZ_UI_INVALID always apply if the element suffers from
+      // VALIDITY_STATE_CUSTOM_ERROR.
+      // Otherwise, it applies if the value has been modified.
+      // NS_EVENT_STATE_MOZ_UI_INVALID always applies if the form submission has
+      // been tried while invalid.
+      if (mCanShowInvalidUI && ShouldShowInvalidUI()) {
+        state |= NS_EVENT_STATE_MOZ_UI_INVALID;
+      }
+    }
+
+    // :-moz-ui-valid applies if all the following are true:
+    // 1. The element is not focused, or had either :-moz-ui-valid or
+    //    :-moz-ui-invalid applying before it was focused ;
+    // 2. The element is either valid or isn't allowed to have
+    //    :-moz-ui-invalid applying ;
+    // 3. The rules to have :-moz-ui-valid applying are fulfilled
+    //    (see ShouldShowValidUI()).
+    if (mCanShowValidUI &&
+        (IsValid() || !mCanShowInvalidUI) &&
+        ShouldShowValidUI()) {
+      state |= NS_EVENT_STATE_MOZ_UI_VALID;
+    }
   }
 
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
       !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
     nsAutoString value;
     GetValueInternal(value, PR_TRUE);
     if (value.IsEmpty()) {
       state |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
@@ -1119,20 +1217,22 @@ nsHTMLTextAreaElement::AfterSetAttr(PRIn
       UpdateValueMissingValidityState();
 
       // This *has* to be called *after* validity has changed.
       if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
         UpdateBarredFromConstraintValidation();
       }
 
       states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID |
                 NS_EVENT_STATE_MOZ_SUBMITINVALID;
     } else if (aName == nsGkAtoms::maxlength) {
       UpdateTooLongValidityState();
-      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     }
 
     if (aNotify) {
       nsIDocument* doc = GetCurrentDoc();
 
       if (aName == nsGkAtoms::readonly) {
         UpdateEditableState();
         states |= NS_EVENT_STATE_MOZ_READONLY | NS_EVENT_STATE_MOZ_READWRITE;
@@ -1175,17 +1275,19 @@ NS_IMETHODIMP
 nsHTMLTextAreaElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
     doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID);
+                                            NS_EVENT_STATE_VALID |
+                                            NS_EVENT_STATE_MOZ_UI_INVALID |
+                                            NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 PRBool
 nsHTMLTextAreaElement::IsTooLong()
 {
@@ -1218,17 +1320,20 @@ nsHTMLTextAreaElement::IsValueMissing() 
   GetValueInternal(value, PR_TRUE);
 
   return value.IsEmpty();
 }
 
 void
 nsHTMLTextAreaElement::UpdateTooLongValidityState()
 {
+  // TODO: this code will be re-enabled with bug 613016 and bug 613019.
+#if 0
   SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong());
+#endif
 }
 
 void
 nsHTMLTextAreaElement::UpdateValueMissingValidityState()
 {
   SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
 }
 
@@ -1390,17 +1495,18 @@ nsHTMLTextAreaElement::OnValueChanged(PR
   // Update the validity state
   PRBool validBefore = IsValid();
   UpdateTooLongValidityState();
   UpdateValueMissingValidityState();
 
   if (aNotify) {
     nsEventStates states;
     if (validBefore != IsValid()) {
-      states |= (NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID);
+      states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     }
 
     if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)
         && !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
       states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
     }
 
     if (!states.IsEmpty()) {
@@ -1414,12 +1520,13 @@ nsHTMLTextAreaElement::OnValueChanged(PR
 }
 
 void
 nsHTMLTextAreaElement::FieldSetDisabledChanged(nsEventStates aStates, PRBool aNotify)
 {
   UpdateValueMissingValidityState();
   UpdateBarredFromConstraintValidation();
 
-  aStates |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
+  aStates |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+             NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
   nsGenericHTMLFormElement::FieldSetDisabledChanged(aStates, aNotify);
 }
 
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -1631,17 +1631,17 @@ nsTextEditorState::GetMaxLength(PRInt32*
   }
 
   return PR_FALSE;
 }
 
 void
 nsTextEditorState::GetValue(nsAString& aValue, PRBool aIgnoreWrap) const
 {
-  if (mEditor && mBoundFrame) {
+  if (mEditor && mBoundFrame && (mEditorInitialized || !IsSingleLineTextControl())) {
     PRBool canCache = aIgnoreWrap && !IsSingleLineTextControl();
     if (canCache && !mCachedValue.IsEmpty()) {
       aValue = mCachedValue;
       return;
     }
 
     aValue.Truncate(); // initialize out param
 
@@ -1708,18 +1708,32 @@ nsTextEditorState::SetValue(const nsAStr
     // PrepareEditor cannot be called prematurely.
     nsAutoScriptBlocker scriptBlocker;
 
     PRBool fireChangeEvent = mBoundFrame->GetFireChangeEventState();
     if (aUserInput) {
       mBoundFrame->SetFireChangeEventState(PR_TRUE);
     }
 
+    NS_ASSERTION(mEditorInitialized || mInitializing,
+      "We should never try to use the editor if we're not initialized unless we're being initialized");
+
     nsAutoString currentValue;
-    mBoundFrame->GetText(currentValue);
+    if (!mEditorInitialized && IsSingleLineTextControl()) {
+      // Grab the current value directly from the text node to make sure that we
+      // deal with stale data correctly.
+      NS_ASSERTION(mRootNode, "We should have a root node here");
+      nsIContent *textContent = mRootNode->GetChildAt(0);
+      nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(textContent);
+      if (textNode) {
+        textNode->GetData(currentValue);
+      }
+    } else {
+      mBoundFrame->GetText(currentValue);
+    }
 
     nsWeakFrame weakFrame(mBoundFrame);
 
     // this is necessary to avoid infinite recursion
     if (!currentValue.Equals(aValue))
     {
       nsTextControlFrame::ValueSetter valueSetter(mBoundFrame,
                                                   mBoundFrame->mFocusedValue.Equals(currentValue));
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -237,12 +237,19 @@ include $(topsrcdir)/config/rules.mk
 		test_bug556007.html \
 		test_bug606817.html \
 		test_bug297761.html \
 		file_bug297761.html \
 		test_bug607145.html \
 		test_bug601061.html \
 		test_bug596511.html \
 		reflect.js \
+		test_bug611189.html \
+		test_bug613113.html \
+		test_bug605124-1.html \
+		test_bug605124-2.html \
+		test_bug605125-1.html \
+		test_bug605125-2.html \
+		test_bug612730.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
--- a/content/html/content/test/form_submit_server.sjs
+++ b/content/html/content/test/form_submit_server.sjs
@@ -9,23 +9,22 @@ function utf8decode(s) {
 
 function utf8encode(s) {
   return unescape(encodeURIComponent(s));
 }
 
 function handleRequest(request, response)
 {
   var bodyStream = new BinaryInputStream(request.bodyInputStream);
-  var bodyBytes = [];
   var result = [];
+  var requestBody = "";
   while ((bodyAvail = bodyStream.available()) > 0)
-    Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
+    requestBody += bodyStream.readBytes(bodyAvail);
 
   if (request.method == "POST") {
-    var requestBody = String.fromCharCode.apply(null, bodyBytes);
 
     var contentTypeParams = {};
     request.getHeader("Content-Type").split(/\s*\;\s*/).forEach(function(s) {
       if (s.indexOf('=') >= 0) {
         let [name, value] = s.split('=');
         contentTypeParams[name] = value;
       }
       else {
@@ -49,35 +48,23 @@ function handleRequest(request, response
           // We're assuming UTF8 for now
 	  body = utf8decode(body);
 	}
 	result.push({ headers: headers, body: body});
       });
     }
     if (contentTypeParams[''] == "text/plain" &&
         request.queryString == "plain") {
-      requestBody.split("\r\n").slice(0, -1).forEach(function (s) {
-        let index = s.indexOf("=");
-        result.push({ name: s.substr(0, index),
-                      value: s.substr(index + 1) });
-      });
+      result = requestBody;
     }
     if (contentTypeParams[''] == "application/x-www-form-urlencoded" &&
         request.queryString == "url") {
-      requestBody.split("&").forEach(function (s) {
-        let index = s.indexOf("=");
-        result.push({ name: unescape(s.substr(0, index)),
-                      value: unescape(s.substr(index + 1)) });
-      });
+      result = requestBody;
     }
   }
   else if (request.method == "GET") {
-    request.queryString.split("&").forEach(function (s) {
-      let index = s.indexOf("=");
-      result.push({ name: unescape(s.substr(0, index)),
-                    value: unescape(s.substr(index + 1)) });
-    });
+    result = request.queryString;
   }
 
   // Send response body
   response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
   response.write(utf8encode(JSON.stringify(result)));
 }
--- a/content/html/content/test/test_bug345624-2.html
+++ b/content/html/content/test/test_bug345624-2.html
@@ -49,28 +49,28 @@ function checkTooLongValidity(element)
   ok(!element.validity.tooLong,
     "Element should not be too long when maxlength > value length");
   is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
      "rgb(0, 255, 0)", ":valid pseudo-class should apply");
 
   ok(element.validity.valid, "Element should be valid");
 
   element.maxLength = 2;
-  ok(element.validity.tooLong,
-    "Element should be too long when maxlength < value length");
-  is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
-     "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+  todo(element.validity.tooLong,
+       "Element should be too long when maxlength < value length");
+  todo_is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
+          "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
 
-  ok(!element.validity.valid,
-    "Element should not be valid when it is too long");
+  todo(!element.validity.valid,
+       "Element should not be valid when it is too long");
 
-  is(element.validationMessage,
-    "Please shorten this text to 2 characters or less (you are currently using 3 characters).",
-    "The validation message text is not correct");
-  ok(!element.checkValidity(), "The element should not be valid");
+  todo(element.validationMessage,
+       "Please shorten this text to 2 characters or less (you are currently using 3 characters).",
+       "The validation message text is not correct");
+  todo(!element.checkValidity(), "The element should not be valid");
   element.setCustomValidity("custom message");
   is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
      "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
   is(element.validationMessage, "custom message",
     "Custom message should be shown instead of too long one");
 }
 
 checkTooLongValidity(document.getElementById('i'));
--- a/content/html/content/test/test_bug346485.html
+++ b/content/html/content/test/test_bug346485.html
@@ -3,21 +3,27 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=346485
 -->
 <head>
   <title>Test for Bug 346485</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+    frameLoaded = function() {
+      is(frames['submit_frame'].location.href, "about:blank",
+         "Blank frame loaded");
+    }
+  </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=346485">Mozilla Bug 346485</a>
 <p id="display"></p>
-<iframe name="submit_frame" onload="checkFormSubmission();" style="visibility: hidden;"></iframe>
+<iframe name="submit_frame" onload="frameLoaded()" style="visibility: hidden;"></iframe>
 <div id="content" style="display: none">
   <form id='f' method='get' target='submit_frame' action='foo'>
     <input name='a' id='a'>
     <input name='b' id='b'>
     <output id='o' for='a b' name='output-name'>tulip</output>
   </form>
 </div>
 <pre id="test">
@@ -121,16 +127,18 @@ function checkHtmlForIDLAttribute(elemen
 
 function submitForm()
 {
   // Setting the values for the submit.
   document.getElementById('o').value = 'foo';
   document.getElementById('a').value = 'afield';
   document.getElementById('b').value = 'bfield';
 
+  frameLoaded = checkFormSubmission;
+
   // This will call checkFormSubmission() which is going to call ST.finish().
   document.getElementById('f').submit();
 }
 
 function checkFormSubmission()
 {
   /**
    * All elements values have been set just before the submission.
@@ -140,40 +148,41 @@ function checkFormSubmission()
 
   is(frames['submit_frame'].location.href,
     'http://mochi.test:8888/tests/content/html/content/test/foo?a=afield&b=bfield',
      "The output element value should not be submitted");
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
-
-var o = document.getElementsByTagName('output');
-is(o.length, 1, "There should be one output element");
+addLoadEvent(function() {
+  var o = document.getElementsByTagName('output');
+  is(o.length, 1, "There should be one output element");
 
-o = o[0];
-ok(o instanceof HTMLOutputElement,
-  "The output should be instance of HTMLOutputElement");
+  o = o[0];
+  ok(o instanceof HTMLOutputElement,
+    "The output should be instance of HTMLOutputElement");
 
-o = document.getElementById('o');
-ok(o instanceof HTMLOutputElement,
-  "The output should be instance of HTMLOutputElement");
+  o = document.getElementById('o');
+  ok(o instanceof HTMLOutputElement,
+    "The output should be instance of HTMLOutputElement");
 
-is(o.type, "output", "Output type IDL attribute should be 'output'");
+  is(o.type, "output", "Output type IDL attribute should be 'output'");
 
-checkNameAttribute(o);
+  checkNameAttribute(o);
 
-checkValueAndDefaultValueIDLAttribute(o);
+  checkValueAndDefaultValueIDLAttribute(o);
 
-checkValueModeFlag(o);
+  checkValueModeFlag(o);
 
-checkDescendantChanged(o);
+  checkDescendantChanged(o);
 
-checkFormIDLAttribute(o);
+  checkFormIDLAttribute(o);
 
-checkHtmlForIDLAttribute(o);
+  checkHtmlForIDLAttribute(o);
 
-submitForm();
+  submitForm();
+});
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/test/test_bug558788-1.html
+++ b/content/html/content/test/test_bug558788-1.html
@@ -35,18 +35,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 var gContent = document.getElementById('content');
 
 function checkValidApplies(elmt)
 {
   is(window.getComputedStyle(elmt, null).getPropertyValue('background-color'),
      "rgb(0, 255, 0)", ":valid pseudo-class should apply");
 }
 
-function checkInvalidApplies(elmt)
+function checkInvalidApplies(elmt, aTodo)
 {
+  if (aTodo) {
+    todo_is(window.getComputedStyle(elmt, null).getPropertyValue('background-color'),
+            "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+    return;
+  }
   is(window.getComputedStyle(elmt, null).getPropertyValue('background-color'),
      "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
 }
 
 function checkMissing(elementName)
 {
   var element = document.createElement(elementName);
   element.required = true;
@@ -65,17 +70,17 @@ function checkMissing(elementName)
 }
 
 function checkTooLong(elementName)
 {
   var element = document.createElement(elementName);
   element.value = "foo";
   element.maxLength = 2;
   gContent.appendChild(element);
-  checkInvalidApplies(element);
+  checkInvalidApplies(element, true);
 
   element.focus();
 
   synthesizeKey("VK_BACK_SPACE", {});
   checkValidApplies(element);
   gContent.removeChild(element);
 }
 
--- a/content/html/content/test/test_bug558788-2.html
+++ b/content/html/content/test/test_bug558788-2.html
@@ -40,30 +40,30 @@ var validElementsDescription = [
   /* <input maxlength='3'> */
   [ "input", null, null, null, null, "3" ],
   /* <input maxlength='3'> */
   [ "input", null, "foo", null, null, "3" ],
   /* <textarea></textarea> */
   [ "textarea", null, null, null, null, null ],
   /* <textarea required>foo</textarea> */
   [ "textarea", null, "foo", true, null, null ],
+  /* <input maxlength='3' value='foo'> */
+  [ "input", null, "foobar", null, null, "3", "foo" ],
 ];
 
 var invalidElementsDescription = [
   /* element type value required pattern maxlength valid-value */
   /* <input required> */
   [ "input", null, null, true, null, null, "foo" ],
   /* <input type='email' value='foo'> */
   [ "input", "email", "foo", null, null, null, "foo@mozilla.org" ],
   /* <input type='url' value='foo'> */
   [ "input", "url", "foo", null, null, null, "http://mozilla.org" ],
   /* <input pattern='\\d\\d' value='foo'> */
   [ "input", null, "foo", null, "\\d\\d", null, "42" ],
-  /* <input maxlength='3' value='foo'> */
-  [ "input", null, "foobar", null, null, "3", "foo" ],
   /* <textarea required></textarea> */
   [ "textarea", null, null, true, null, null, "foo" ],
 ];
 
 var validElements = [];
 var invalidElements = [];
 
 function appendElements(aElementsDesc, aElements)
--- a/content/html/content/test/test_bug561634.html
+++ b/content/html/content/test/test_bug561634.html
@@ -22,47 +22,34 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 561634 **/
 
 function checkEmptyForm()
 {
   ok(document.forms[0].checkValidity(), "An empty form is valid");
 }
 
-function checkInvalidNonSubmittable()
-{
-  var f = document.forms[0];
-  var o = document.createElement('output');
-  var fs = document.createElement('fieldset');
-
-  f.appendChild(o);
-  f.appendChild(fs);
-
-  o.setCustomValidity("foo");
-  fs.setCustomValidity("foo");
-
-  ok(f.checkValidity(),
-     "A form with invalid non-submittable elements is valid");
-
-  f.removeChild(o);
-  f.removeChild(fs);
-}
-
 function checkBarredFromConstraintValidation()
 {
   var f = document.forms[0];
+  var fs = document.createElement('fieldset');
   var i = document.createElement('input');
+
+  f.appendChild(fs);
   i.type = 'hidden';
   f.appendChild(i);
 
+  fs.setCustomValidity("foo");
   i.setCustomValidity("foo");
+
   ok(f.checkValidity(),
-     "A form with invalid submittable element barred from constraint validation is valid");
+     "A form with invalid element barred from constraint validation should be valid");
 
   f.removeChild(i);
+  f.removeChild(fs);
 }
 
 function checkValid()
 {
   var f = document.forms[0];
   var i = document.createElement('input');
   f.appendChild(i);
 
@@ -127,17 +114,16 @@ function checkInvalidEvent()
        "invalid event should not be fired on valid elements");
   });
 
   f.removeChild(i2);
   f.removeChild(i);
 }
 
 checkEmptyForm();
-checkInvalidNonSubmittable();
 checkBarredFromConstraintValidation();
 checkValid();
 checkInvalid();
 checkInvalidEvent();
 
 </script>
 </pre>
 </body>
--- a/content/html/content/test/test_bug561640.html
+++ b/content/html/content/test/test_bug561640.html
@@ -31,19 +31,19 @@ function checkValid(elmt)
 {
   ok(!elmt.validity.tooLong, "element should not be too long");
   is(window.getComputedStyle(elmt, null).getPropertyValue('background-color'),
      "rgb(0, 255, 0)", ":valid pseudo-class should apply");
 }
 
 function checkInvalid(elmt)
 {
-  ok(elmt.validity.tooLong, "element should be too long");
-  is(window.getComputedStyle(elmt, null).getPropertyValue('background-color'),
-     "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+  todo(elmt.validity.tooLong, "element should be too long");
+  todo_is(window.getComputedStyle(elmt, null).getPropertyValue('background-color'),
+          "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
 }
 
 for each (var elmtName in elements) {
   var elmt = document.createElement(elmtName);
   content.appendChild(elmt);
 
   if (elmtName == 'textarea') {
     elmt.textContent = 'foo';
--- a/content/html/content/test/test_bug598643.html
+++ b/content/html/content/test/test_bug598643.html
@@ -59,34 +59,44 @@ var types = [
 
 var input = document.createElement("input");
 input.maxLength = 1;
 input.value = "foo";
 
 // Too long types.
 for each (type in types[0]) {
   input.type = type
-  ok(!input.validity.valid, "the element should be invalid [type=" + type + "]");
-  ok(input.validity.tooLong,
-     "the element should suffer from being too long [type=" + type + "]");
+  if (type == 'email') {
+    input.value = "foo@bar.com";
+  } else if (type == 'url') {
+    input.value = 'http://foo.org';
+  }
+
+  todo(!input.validity.valid, "the element should be invalid [type=" + type + "]");
+  todo(input.validity.tooLong,
+       "the element should suffer from being too long [type=" + type + "]");
+
+  if (type == 'email' || type == 'url') {
+    input.value = 'foo';
+  }
 }
 
 // Not too long types.
 for each (type in types[1]) {
   input.type = type
   ok(input.validity.valid, "the element should be valid [type=" + type + "]");
   ok(!input.validity.tooLong,
      "the element shouldn't suffer from being too long [type=" + type + "]");
 }
 
 // Not too long types but TODO.
 for each (type in types[2]) {
   input.type = type
-  todo(input.validity.valid, "the element should be valid [type=" + type + "]");
-  todo(!input.validity.tooLong,
+  ok(input.validity.valid, "the element should be valid [type=" + type + "]");
+  ok(!input.validity.tooLong,
      "the element shouldn't suffer from being too long [type=" + type + "]");
 }
 
 testFileControl(input);
 
 </script>
 </pre>
 </body>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug605124-1.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605124
+-->
+<head>
+  <title>Test for Bug 605124</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605124">Mozilla Bug 605124</a>
+<p id="display"></p>
+<div id="content">
+  <form>
+    <textarea required></textarea>
+    <input required>
+    <select required></select>
+    <button type='submit'></button>
+  </form>
+
+  <table>
+    <form>
+    <tr>
+      <textarea required></textarea>
+      <input required>
+      <select required></select>
+      <button type='submit'></button>
+    </tr>
+    </form>
+  </table>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605124 **/
+
+function checkPseudoClass(aElement, aExpected)
+{
+  is(aElement.mozMatchesSelector(":-moz-ui-invalid"), aExpected,
+     "mozMatchesSelector(':-moz-ui-invalid') should return " + aExpected + " for " + aElement);
+}
+
+netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+var os = Components.classes['@mozilla.org/observer-service;1']
+                   .getService(Components.interfaces.nsIObserverService);
+var observers = os.enumerateObservers("invalidformsubmit");
+
+if (observers.hasMoreElements()) {
+  var content = document.getElementById('content');
+  var textarea = document.getElementsByTagName('textarea')[0];
+  var input = document.getElementsByTagName('input')[0];
+  var select = document.getElementsByTagName('select')[0];
+  var button = document.getElementsByTagName('button')[0];
+  var form = document.forms[0];
+
+  checkPseudoClass(textarea, false);
+  checkPseudoClass(input, false);
+  checkPseudoClass(select, false);
+
+  // Try to submit.
+  button.click();
+  checkPseudoClass(textarea, true);
+  checkPseudoClass(input, true);
+  checkPseudoClass(select, true);
+
+  // No longer in the form.
+  content.appendChild(textarea);
+  content.appendChild(input);
+  content.appendChild(select);
+  checkPseudoClass(textarea, false);
+  checkPseudoClass(input, false);
+  checkPseudoClass(select, false);
+
+  // Back in the form.
+  form.appendChild(textarea);
+  form.appendChild(input);
+  form.appendChild(select);
+  checkPseudoClass(textarea, true);
+  checkPseudoClass(input, true);
+  checkPseudoClass(select, true);
+
+  /* Case when elements get orphaned. */
+  var textarea = document.getElementsByTagName('textarea')[1];
+  var input = document.getElementsByTagName('input')[1];
+  var select = document.getElementsByTagName('select')[1];
+  var button = document.getElementsByTagName('button')[1];
+  var form = document.forms[1];
+
+  // Try to submit.
+  button.click();
+  checkPseudoClass(textarea, true);
+  checkPseudoClass(input, true);
+  checkPseudoClass(select, true);
+
+  // Remove the form.
+  document.getElementsByTagName('table')[0].removeChild(form);
+  checkPseudoClass(textarea, false);
+  checkPseudoClass(input, false);
+  checkPseudoClass(select, false);
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug605124-2.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605124
+-->
+<head>
+  <title>Test for Bug 605124</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605124">Mozilla Bug 605124</a>
+<p id="display"></p>
+<div id="content">
+  <input required>
+  <textarea required></textarea>
+  <select required>
+    <option value="">foo</option>
+    <option>bar</option>
+  </select>
+  <select multiple required>
+    <option value="">foo</option>
+    <option>bar</option>
+  </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605124 **/
+
+function checkPseudoClass(aElement, aExpected)
+{
+  is(aElement.mozMatchesSelector(":-moz-ui-invalid"), aExpected,
+     "mozMatchesSelector(':-moz-ui-invalid') should return " + aExpected + " for " + aElement);
+}
+
+function checkElement(aElement)
+{
+  checkPseudoClass(aElement, false);
+
+  // Focusing while :-moz-ui-invalid doesn't apply,
+  // the pseudo-class should not apply while typing.
+  aElement.focus();
+  checkPseudoClass(aElement, false);
+  // with keys
+  synthesizeKey('f', {});
+  checkPseudoClass(aElement, false);
+  synthesizeKey('VK_BACK_SPACE', {});
+  checkPseudoClass(aElement, false);
+  // with .value
+  aElement.value = 'f';
+  checkPseudoClass(aElement, false);
+  aElement.value = '';
+  checkPseudoClass(aElement, false);
+
+  aElement.blur();
+  checkPseudoClass(aElement, true);
+
+  // Focusing while :-moz-ui-invalid applies,
+  // the pseudo-class should apply while typing if appropriate.
+  aElement.focus();
+  checkPseudoClass(aElement, true);
+  // with keys
+  synthesizeKey('f', {});
+  checkPseudoClass(aElement, false);
+  synthesizeKey('VK_BACK_SPACE', {});
+  checkPseudoClass(aElement, true);
+  // with .value
+  aElement.value = 'f';
+  checkPseudoClass(aElement, false);
+  aElement.value = '';
+  checkPseudoClass(aElement, true);
+}
+
+function checkSelectElement(aElement)
+{
+  checkPseudoClass(aElement, false);
+
+  // Focusing while :-moz-ui-invalid doesn't apply,
+  // the pseudo-class should not apply while changing selection.
+  aElement.focus();
+  checkPseudoClass(aElement, false);
+
+  aElement.selectedIndex = 1;
+  checkPseudoClass(aElement, false);
+  aElement.selectedIndex = 0;
+  checkPseudoClass(aElement, false);
+
+  aElement.blur();
+  checkPseudoClass(aElement, true);
+
+  // Focusing while :-moz-ui-invalid applies,
+  // the pseudo-class should apply while changing selection if appropriate.
+  aElement.focus();
+  checkPseudoClass(aElement, true);
+
+  aElement.selectedIndex = 1;
+  checkPseudoClass(aElement, false);
+  aElement.selectedIndex = 0;
+  checkPseudoClass(aElement, true);
+}
+
+checkElement(document.getElementsByTagName('input')[0]);
+checkElement(document.getElementsByTagName('textarea')[0]);
+checkSelectElement(document.getElementsByTagName('select')[0]);
+checkSelectElement(document.getElementsByTagName('select')[1]);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug605125-1.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605125
+-->
+<head>
+  <title>Test for Bug 605125</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605125">Mozilla Bug 605125</a>
+<p id="display"></p>
+<div id="content">
+  <form id='f1'>
+    <textarea></textarea>
+    <input>
+    <button type='submit'></button>
+    <select></select>
+  </form>
+
+  <table>
+    <form id='f2'>
+    <tr>
+      <textarea></textarea>
+      <input>
+      <button type='submit'></button>
+      <select></select>
+    </tr>
+    </form>
+  </table>
+  <input form='f1' required>
+  <input form='f2' required>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605125 **/
+
+/**
+ * NOTE: this test is very similar to 605124-1.html.
+ */
+
+function checkPseudoClass(aElement, aExpected)
+{
+  is(aElement.mozMatchesSelector(":-moz-ui-valid"), aExpected,
+     "mozMatchesSelector(':-moz-ui-valid') should return " + aExpected + " for " + aElement);
+}
+
+netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+var os = Components.classes['@mozilla.org/observer-service;1']
+                   .getService(Components.interfaces.nsIObserverService);
+var observers = os.enumerateObservers("invalidformsubmit");
+
+if (observers.hasMoreElements()) {
+  var content = document.getElementById('content');
+  var textarea = document.getElementsByTagName('textarea')[0];
+  var input = document.getElementsByTagName('input')[0];
+  var button = document.getElementsByTagName('button')[0];
+  var select = document.getElementsByTagName('select')[0];
+  var form = document.forms[0];
+
+  checkPseudoClass(textarea, false);
+  checkPseudoClass(input, false);
+  checkPseudoClass(select, false);
+
+  // Try to submit.
+  button.click();
+  checkPseudoClass(textarea, true);
+  checkPseudoClass(input, true);
+  checkPseudoClass(select, true);
+
+  // No longer in the form.
+  content.appendChild(textarea);
+  content.appendChild(input);
+  content.appendChild(select);
+  checkPseudoClass(textarea, false);
+  checkPseudoClass(input, false);
+  checkPseudoClass(select, false);
+
+  // Back in the form.
+  form.appendChild(textarea);
+  form.appendChild(input);
+  form.appendChild(select);
+  checkPseudoClass(textarea, true);
+  checkPseudoClass(input, true);
+  checkPseudoClass(select, true);
+
+  /* Case when elements get orphaned. */
+  var textarea = document.getElementsByTagName('textarea')[1];
+  var input = document.getElementsByTagName('input')[1];
+  var button = document.getElementsByTagName('button')[1];
+  var select = document.getElementsByTagName('select')[1];
+  var form = document.forms[1];
+
+  // Try to submit.
+  button.click();
+  checkPseudoClass(textarea, true);
+  checkPseudoClass(input, true);
+  checkPseudoClass(select, true);
+
+  // Remove the form.
+  document.getElementsByTagName('table')[0].removeChild(form);
+  checkPseudoClass(textarea, false);
+  checkPseudoClass(input, false);
+  checkPseudoClass(select, false);
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug605125-2.html
@@ -0,0 +1,146 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605125
+-->
+<head>
+  <title>Test for Bug 605125</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605125">Mozilla Bug 605125</a>
+<p id="display"></p>
+<div id="content">
+  <input>
+  <textarea></textarea>
+  <select>
+    <option value="">foo</option>
+    <option>bar</option>
+  </select>
+  <select multiple>
+    <option value="">foo</option>
+    <option>bar</option>
+  </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605125 **/
+
+function checkPseudoClass(aElement, aExpected)
+{
+  is(aElement.mozMatchesSelector(":-moz-ui-valid"), aExpected,
+     "mozMatchesSelector(':-moz-ui-valid') should return " + aExpected + " for " + aElement);
+}
+
+function checkElement(aElement)
+{
+  checkPseudoClass(aElement, false);
+
+  // Focusing while :-moz-ui-valid doesn't apply,
+  // the pseudo-class should not apply while typing.
+  aElement.focus();
+  checkPseudoClass(aElement, false);
+  // with keys
+  synthesizeKey('f', {});
+  checkPseudoClass(aElement, false);
+  synthesizeKey('VK_BACK_SPACE', {});
+  checkPseudoClass(aElement, false);
+  // with .value
+  aElement.value = 'f';
+  checkPseudoClass(aElement, false);
+  aElement.value = '';
+  checkPseudoClass(aElement, false);
+
+  aElement.blur();
+  checkPseudoClass(aElement, true);
+
+  // Focusing while :-moz-ui-valid applies,
+  // the pseudo-class should apply while typing if appropriate.
+  aElement.focus();
+  checkPseudoClass(aElement, true);
+  // with keys
+  synthesizeKey('f', {});
+  checkPseudoClass(aElement, true);
+  synthesizeKey('VK_BACK_SPACE', {});
+  checkPseudoClass(aElement, true);
+  // with .value
+  aElement.value = 'f';
+  checkPseudoClass(aElement, true);
+  aElement.value = '';