about:startup - merge vlad's review comments with the trunk
authorDaniel Brooks <db48x@db48x.net>
Tue, 23 Nov 2010 09:49:19 -0600
changeset 58873 c5ecb4498216eaf08d538b39ba157d95597b1e73
parent 58872 97a4aa8ca939962635f391aef5b612eadefcbb7a (current diff)
parent 58026 e6dadb378a1a7ec25079718c4cf4247d4fbb7c66 (diff)
child 58874 292bd8571d7c4ea5bc730ebab1c7e6139bf9bfdc
push idunknown
push userunknown
push dateunknown
milestone2.0b8pre
about:startup - merge vlad's review comments with the trunk
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
accessible/tests/mochitest/treeupdate/test_tableinsubtree.html
browser/base/content/browser.js
caps/tests/browser/Makefile.in
caps/tests/browser/browser_bug571289.js
content/base/public/nsIScriptLoader.idl
content/base/test/test_bug308484.html
content/canvas/test/webgl/conformance/00_test_list.txt
content/canvas/test/webgl/conformance/context-attributes.html
content/svg/content/src/nsSVGPathSeg.cpp
content/svg/content/src/nsSVGPathSeg.h
content/svg/content/src/nsSVGPathSegList.cpp
content/svg/content/src/nsSVGPathSegList.h
dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
editor/libeditor/html/tests/browserscope/lib/browserscope/LICENSE
editor/libeditor/html/tests/browserscope/lib/browserscope/README
editor/libeditor/html/tests/browserscope/lib/browserscope/README.Mozilla
editor/libeditor/html/tests/browserscope/lib/browserscope/currentStatus.js
editor/libeditor/html/tests/browserscope/lib/browserscope/current_revision
editor/libeditor/html/tests/browserscope/lib/browserscope/richtext/editable.html
editor/libeditor/html/tests/browserscope/lib/browserscope/richtext/js/range.js
editor/libeditor/html/tests/browserscope/lib/browserscope/richtext/richtext.html
editor/libeditor/html/tests/browserscope/lib/browserscope/update_from_upstream
editor/libeditor/html/tests/browserscope/test_browserscope.html
editor/libeditor/html/tests/test_bug601881.html
extensions/cookie/test/unit/test_bug481775.js
gfx/harfbuzz/NEWS
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-ft.cc
gfx/ycbcr/bug572034_mac_64bit.patch
gfx/ycbcr/bug577645_movntq.patch
gfx/ycbcr/bustage.patch
gfx/ycbcr/export.patch
gfx/ycbcr/picture_region.patch
gfx/ycbcr/remove_scale.patch
gfx/ycbcr/row_c_fix.patch
gfx/ycbcr/win64_mac64.patch
gfx/ycbcr/yuv_row_linux.cpp
gfx/ycbcr/yuv_row_mac.cpp
gfx/ycbcr/yv24.patch
js/src/tests/ecma_5/misc/explicit-undefined-optional-argument.diff
media/libtheora/bug559343.patch
media/libtheora/include/theora/config.h
media/libtheora/lib/cpu.c
media/libtheora/lib/cpu.h
media/libtheora/lib/encint.h
media/libtheora/lib/encoder_disabled.c
media/libtheora/lib/enquant.h
media/libtheora/lib/huffenc.h
media/libtheora/lib/x86/mmxfrag.h
media/libtheora/lib/x86_vc/mmxfrag.h
media/libvorbis/bug487519.patch
media/libvpx/frame_buf_ref.patch
media/libvpx/reduce-warnings-1.patch
media/libvpx/subpixel-qword.patch
media/libvpx/vp8/common/segmentation_common.h
media/libvpx/vp8/decoder/demode.c
media/libvpx/vp8/decoder/demode.h
modules/freetype2/Makefile.in
modules/plugin/test/crashtests/522512-1.html
testing/mochitest/runtests.py
testing/mochitest/runtests.py.in
toolkit/components/console/hudservice/tests/browser/browser_webconsole_get_display_by_uri_spec.js
toolkit/locales/en-US/chrome/global-region/region.dtd
toolkit/locales/jar.mn
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/update/test/unit/data/aus-0110_general.mar
toolkit/mozapps/update/test/unit/data/aus-0110_general_ref_image.png
toolkit/mozapps/update/test/unit/data/aus-0111_general.mar
toolkit/mozapps/update/test/unit/data/aus-0111_general_ref_image.png
toolkit/themes/pinstripe/global/jar.mn
toolkit/themes/winstripe/global/jar.mn
toolkit/xre/nsAppRunner.cpp
uriloader/exthandler/tests/unit_ipc/disable_t_encoding.js
--- a/accessible/public/nsIAccessibleRole.idl
+++ b/accessible/public/nsIAccessibleRole.idl
@@ -777,14 +777,20 @@ interface nsIAccessibleRole : nsISupport
   const unsigned long ROLE_GRID_CELL = 121;
 
   /**
    * Represents an embedded object. It is used for html:object or html:embed.
    */
   const unsigned long ROLE_EMBEDDED_OBJECT = 122;
 
   /**
+   * A note. Originally intended to be hidden until activated, but now also used
+   * for things like html 'aside'.
+   */
+  const unsigned long ROLE_NOTE = 123;
+
+  /**
    * It's not role actually. This constant is important to help ensure
    * nsRoleMap's are synchronized.
    */
-  const unsigned long ROLE_LAST_ENTRY = 123;
+  const unsigned long ROLE_LAST_ENTRY = 124;
 };
 
--- a/accessible/src/atk/nsRoleMap.h
+++ b/accessible/src/atk/nsRoleMap.h
@@ -164,11 +164,12 @@ static const PRUint32 atkRoleMap[] = {
     ATK_ROLE_MENU_ITEM,           // nsIAccessibleRole::ROLE_COMBOBOX_OPTION      115
     ATK_ROLE_IMAGE,               // nsIAccessibleRole::ROLE_IMAGE_MAP            116
     ATK_ROLE_LIST_ITEM,           // nsIAccessibleRole::ROLE_OPTION               117
     ATK_ROLE_LIST_ITEM,           // nsIAccessibleRole::ROLE_RICH_OPTION          118
     ATK_ROLE_LIST,                // nsIAccessibleRole::ROLE_LISTBOX              119
     ATK_ROLE_UNKNOWN,             // nsIAccessibleRole::ROLE_FLAT_EQUATION        120
     ATK_ROLE_TABLE_CELL,          // nsIAccessibleRole::ROLE_GRID_CELL            121
     ATK_ROLE_PANEL,               // nsIAccessibleRole::ROLE_EMBEDDED_OBJECT      122
+    ATK_ROLE_SECTION,             // nsIAccessibleRole::ROLE_NOTE                 123
     kROLE_ATK_LAST_ENTRY          // nsIAccessibleRole::ROLE_LAST_ENTRY
 };
 
--- a/accessible/src/base/AccIterator.cpp
+++ b/accessible/src/base/AccIterator.cpp
@@ -32,20 +32,22 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "AccIterator.h"
 
+#include "nsAccessibilityService.h"
 #include "nsAccessible.h"
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsAccIterator
+// AccIterator
+////////////////////////////////////////////////////////////////////////////////
 
 AccIterator::AccIterator(nsAccessible *aAccessible,
                          filters::FilterFuncPtr aFilterFunc,
                          IterationType aIterationType) :
   mFilterFunc(aFilterFunc), mIsDeep(aIterationType != eFlatNav)
 {
   mState = new IteratorState(aAccessible);
 }
@@ -88,8 +90,162 @@ AccIterator::GetNext()
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccIterator::IteratorState
 
 AccIterator::IteratorState::IteratorState(nsAccessible *aParent,
                                           IteratorState *mParentState) :
   mParent(aParent), mIndex(0), mParentState(mParentState)
 {
 }
+
+
+////////////////////////////////////////////////////////////////////////////////
+// RelatedAccIterator
+////////////////////////////////////////////////////////////////////////////////
+
+RelatedAccIterator::
+  RelatedAccIterator(nsDocAccessible* aDocument, nsIContent* aDependentContent,
+                     nsIAtom* aRelAttr) :
+  mRelAttr(aRelAttr), mProviders(nsnull), mBindingParent(nsnull), mIndex(0)
+{
+  mBindingParent = aDependentContent->GetBindingParent();
+  nsIAtom* IDAttr = mBindingParent ?
+    nsAccessibilityAtoms::anonid : aDependentContent->GetIDAttributeName();
+
+  nsAutoString id;
+  if (aDependentContent->GetAttr(kNameSpaceID_None, IDAttr, id))
+    mProviders = aDocument->mDependentIDsHash.Get(id);
+}
+
+nsAccessible*
+RelatedAccIterator::Next()
+{
+  if (!mProviders)
+    return nsnull;
+
+  while (mIndex < mProviders->Length()) {
+    nsDocAccessible::AttrRelProvider* provider = (*mProviders)[mIndex++];
+
+    // Return related accessible for the given attribute and if the provider
+    // content is in the same binding in the case of XBL usage.
+    if (provider->mRelAttr == mRelAttr &&
+        (!mBindingParent ||
+         mBindingParent == provider->mContent->GetBindingParent())) {
+      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
@@ -35,16 +35,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsAccIterator_h_
 #define nsAccIterator_h_
 
 #include "filters.h"
 #include "nscore.h"
+#include "nsDocAccessible.h"
 
 /**
  * Allows to iterate through accessible children or subtree complying with
  * filter function.
  */
 class AccIterator
 {
 public:
@@ -88,9 +89,142 @@ private:
     IteratorState *mParentState;
   };
 
   filters::FilterFuncPtr mFilterFunc;
   PRBool mIsDeep;
   IteratorState *mState;
 };
 
+
+/**
+ * Allows to traverse through related accessibles that are pointing to the given
+ * dependent accessible by relation attribute.
+ */
+class RelatedAccIterator
+{
+public:
+  /**
+   * Constructor.
+   *
+   * @param aDocument         [in] the document accessible the related
+   * &                         accessibles belong to.
+   * @param aDependentContent [in] the content of dependent accessible that
+   *                           relations were requested for
+   * @param aRelAttr          [in] relation attribute that relations are
+   *                           pointed by
+   */
+  RelatedAccIterator(nsDocAccessible* aDocument, nsIContent* aDependentContent,
+                     nsIAtom* aRelAttr);
+
+  /**
+   * Return next related accessible for the given dependent accessible.
+   */
+  nsAccessible* Next();
+
+private:
+  RelatedAccIterator();
+  RelatedAccIterator(const RelatedAccIterator&);
+  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))
@@ -451,37 +453,38 @@ nsAccDocManager::CreateDocOrRootAccessib
   }
 
   if (!outerDocAcc)
     return nsnull;
 
   // We only create root accessibles for the true root, otherwise create a
   // doc accessible.
   nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell));
-  nsDocAccessible *docAcc = isRootDoc ?
+  nsRefPtr<nsDocAccessible> docAcc = isRootDoc ?
     new nsRootAccessibleWrap(aDocument, rootElm, weakShell) :
     new nsDocAccessibleWrap(aDocument, rootElm, weakShell);
 
-  if (!docAcc)
+  // Cache the document accessible into document cache.
+  if (!docAcc || !mDocAccessibleCache.Put(aDocument, docAcc))
     return nsnull;
 
-  // Cache and addref document accessible.
-  if (!mDocAccessibleCache.Put(aDocument, docAcc)) {
-    delete docAcc;
+  // Bind the document accessible into tree.
+  if (!outerDocAcc->AppendChild(docAcc)) {
+    mDocAccessibleCache.Remove(aDocument);
     return nsnull;
   }
 
-  // XXX: ideally we should initialize an accessible and then put it into tree,
-  // we can't since document accessible fires reorder event on its container
-  // while initialized.
-  if (!outerDocAcc->AppendChild(docAcc) ||
-      !GetAccService()->InitAccessible(docAcc, nsAccUtils::GetRoleMapEntry(aDocument))) {
+  // Initialize the document accessible. Note, Init() should be called after
+  // the document accessible is bound to the tree.
+  if (!docAcc->Init()) {
+    docAcc->Shutdown();
     mDocAccessibleCache.Remove(aDocument);
     return nsnull;
   }
+  docAcc->SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aDocument));
 
   NS_LOG_ACCDOCCREATE("document creation finished", aDocument)
 
   AddListeners(aDocument, isRootDoc);
   return docAcc;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- 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/nsAccTreeWalker.cpp
+++ b/accessible/src/base/nsAccTreeWalker.cpp
@@ -108,26 +108,26 @@ nsAccTreeWalker::GetNextChildInternal(PR
   PRUint32 length = 0;
   if (mState->childList)
     mState->childList->GetLength(&length);
 
   while (mState->childIdx < length) {
     nsIContent* childNode = mState->childList->GetNodeAt(mState->childIdx);
     mState->childIdx++;
 
-    PRBool isHidden = PR_FALSE;
+    bool isSubtreeHidden = false;
     nsRefPtr<nsAccessible> accessible =
       GetAccService()->GetOrCreateAccessible(childNode, presShell, mWeakShell,
-                                             &isHidden);
+                                             &isSubtreeHidden);
 
     if (accessible)
       return accessible.forget();
 
     // Walk down into subtree to find accessibles.
-    if (!isHidden) {
+    if (!isSubtreeHidden) {
       if (!PushState(childNode))
         break;
 
       accessible = GetNextChildInternal(PR_TRUE);
       if (accessible)
         return accessible.forget();
     }
   }
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -77,17 +77,16 @@
 /* For documentation of the accessibility architecture, 
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 nsIStringBundle *nsAccessNode::gStringBundle = 0;
 nsIStringBundle *nsAccessNode::gKeyStringBundle = 0;
 nsINode *nsAccessNode::gLastFocusedNode = nsnull;
 
-PRBool nsAccessNode::gIsCacheDisabled = PR_FALSE;
 PRBool nsAccessNode::gIsFormFillEnabled = PR_FALSE;
 
 nsApplicationAccessible *nsAccessNode::gApplicationAccessible = nsnull;
 
 /*
  * Class nsAccessNode
  */
  
@@ -212,17 +211,16 @@ void nsAccessNode::InitXPAccessibility()
     stringBundleService->CreateBundle(PLATFORM_KEYS_BUNDLE_URL, 
                                       &gKeyStringBundle);
   }
 
   nsAccessibilityAtoms::AddRefAtoms();
 
   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (prefBranch) {
-    prefBranch->GetBoolPref("accessibility.disablecache", &gIsCacheDisabled);
     prefBranch->GetBoolPref("browser.formfill.enable", &gIsFormFillEnabled);
   }
 
   NotifyA11yInitOrShutdown(PR_TRUE);
 }
 
 // nsAccessNode protected static
 void nsAccessNode::NotifyA11yInitOrShutdown(PRBool aIsInit)
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -151,17 +151,17 @@ public:
     return DOMNode;
   }
 
   /**
    * Return DOM node associated with the accessible.
    */
   virtual nsINode* GetNode() const { return mContent; }
   nsIContent* GetContent() const { return mContent; }
-  nsIDocument* GetDocumentNode() const
+  virtual nsIDocument* GetDocumentNode() const
     { return mContent ? mContent->GetOwnerDoc() : nsnull; }
 
   /**
    * Return node type information of DOM node associated with the accessible.
    */
   PRBool IsContent() const
   {
     return GetNode() && GetNode()->IsNodeOfType(nsINode::eCONTENT);
@@ -207,17 +207,16 @@ protected:
      * Notify global nsIObserver's that a11y is getting init'd or shutdown
      */
     static void NotifyA11yInitOrShutdown(PRBool aIsInit);
 
     // Static data, we do our own refcounting for our static data
     static nsIStringBundle *gStringBundle;
     static nsIStringBundle *gKeyStringBundle;
 
-    static PRBool gIsCacheDisabled;
     static PRBool gIsFormFillEnabled;
 
 private:
   static nsApplicationAccessible *gApplicationAccessible;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsAccessNode,
                               NS_ACCESSNODE_IMPL_CID)
--- a/accessible/src/base/nsAccessibilityAtomList.h
+++ b/accessible/src/base/nsAccessibilityAtomList.h
@@ -95,28 +95,32 @@ ACCESSIBILITY_ATOM(tableOuterFrame, "Tab
 ACCESSIBILITY_ATOM(tableRowGroupFrame, "TableRowGroupFrame")
 ACCESSIBILITY_ATOM(tableRowFrame, "TableRowFrame")
 
   // Alphabetical list of tag names
 ACCESSIBILITY_ATOM(a, "a")
 ACCESSIBILITY_ATOM(abbr, "abbr")
 ACCESSIBILITY_ATOM(acronym, "acronym")
 ACCESSIBILITY_ATOM(area, "area")
+ACCESSIBILITY_ATOM(article, "article") // HTML landmark
+ACCESSIBILITY_ATOM(aside, "aside") // HTML landmark
 ACCESSIBILITY_ATOM(autocomplete, "autocomplete")
 ACCESSIBILITY_ATOM(blockquote, "blockquote")
 ACCESSIBILITY_ATOM(br, "br")
 ACCESSIBILITY_ATOM(body, "body")
 ACCESSIBILITY_ATOM(caption, "caption") // XUL
 ACCESSIBILITY_ATOM(choices, "choices") // XForms
 ACCESSIBILITY_ATOM(description, "description")    // XUL
 ACCESSIBILITY_ATOM(dd, "dd")
 ACCESSIBILITY_ATOM(div, "div")
 ACCESSIBILITY_ATOM(dl, "dl")
 ACCESSIBILITY_ATOM(dt, "dt")
+ACCESSIBILITY_ATOM(footer, "footer") // HTML landmark
 ACCESSIBILITY_ATOM(form, "form")
+ACCESSIBILITY_ATOM(header, "header") // HTML landmark
 ACCESSIBILITY_ATOM(h1, "h1")
 ACCESSIBILITY_ATOM(h2, "h2")
 ACCESSIBILITY_ATOM(h3, "h3")
 ACCESSIBILITY_ATOM(h4, "h4")
 ACCESSIBILITY_ATOM(h5, "h5")
 ACCESSIBILITY_ATOM(h6, "h6")
 ACCESSIBILITY_ATOM(item, "item") // XForms
 ACCESSIBILITY_ATOM(itemset, "itemset") // XForms
@@ -130,16 +134,17 @@ ACCESSIBILITY_ATOM(listcell, "listcell")
 ACCESSIBILITY_ATOM(listcols, "listcols") // XUL
 ACCESSIBILITY_ATOM(listcol, "listcol") // XUL
 ACCESSIBILITY_ATOM(listhead, "listhead") // XUL
 ACCESSIBILITY_ATOM(listheader, "listheader") // XUL
 ACCESSIBILITY_ATOM(map, "map")
 ACCESSIBILITY_ATOM(math, "math")
 ACCESSIBILITY_ATOM(menupopup, "menupopup")     // XUL
 ACCESSIBILITY_ATOM(object, "object")
+ACCESSIBILITY_ATOM(nav, "nav") // HTML landmark
 ACCESSIBILITY_ATOM(ol, "ol")
 ACCESSIBILITY_ATOM(optgroup, "optgroup")
 ACCESSIBILITY_ATOM(option, "option")
 ACCESSIBILITY_ATOM(output, "output")
 ACCESSIBILITY_ATOM(panel, "panel") // XUL
 ACCESSIBILITY_ATOM(q, "q")
 ACCESSIBILITY_ATOM(select, "select")
 ACCESSIBILITY_ATOM(select1, "select1") // XForms
@@ -191,16 +196,17 @@ ACCESSIBILITY_ATOM(longDesc, "longdesc")
 ACCESSIBILITY_ATOM(max, "max") // XUL
 ACCESSIBILITY_ATOM(maxpos, "maxpos") // XUL
 ACCESSIBILITY_ATOM(minpos, "minpos") // XUL
 ACCESSIBILITY_ATOM(_moz_menuactive, "_moz-menuactive") // XUL
 ACCESSIBILITY_ATOM(multiline, "multiline") // XUL
 ACCESSIBILITY_ATOM(name, "name")
 ACCESSIBILITY_ATOM(onclick, "onclick")
 ACCESSIBILITY_ATOM(popup, "popup")
+ACCESSIBILITY_ATOM(placeholder, "placeholder")
 ACCESSIBILITY_ATOM(readonly, "readonly")
 ACCESSIBILITY_ATOM(scope, "scope") // HTML table
 ACCESSIBILITY_ATOM(seltype, "seltype") // XUL listbox
 ACCESSIBILITY_ATOM(simple, "simple") // XLink
 ACCESSIBILITY_ATOM(src, "src")
 ACCESSIBILITY_ATOM(selected, "selected")
 ACCESSIBILITY_ATOM(summary, "summary")
 ACCESSIBILITY_ATOM(tabindex, "tabindex")
@@ -284,8 +290,9 @@ ACCESSIBILITY_ATOM(containerBusy, "conta
 ACCESSIBILITY_ATOM(containerLive, "container-live")
 ACCESSIBILITY_ATOM(containerLiveRole, "container-live-role")
 ACCESSIBILITY_ATOM(containerRelevant, "container-relevant")
 ACCESSIBILITY_ATOM(level, "level")
 ACCESSIBILITY_ATOM(live, "live")
 ACCESSIBILITY_ATOM(lineNumber, "line-number")
 ACCESSIBILITY_ATOM(posinset, "posinset") 
 ACCESSIBILITY_ATOM(setsize, "setsize")
+ACCESSIBILITY_ATOM(xmlroles, "xml-roles")
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -472,64 +472,78 @@ nsAccessibilityService::CreateHTMLCaptio
 }
 
 void
 nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
                                              nsIContent* aContainer,
                                              nsIContent* aStartChild,
                                              nsIContent* aEndChild)
 {
-#ifdef DEBUG_A11Y
+#ifdef DEBUG_CONTENTMUTATION
   nsAutoString tag;
   aStartChild->Tag()->ToString(tag);
-  nsIAtom* id = aStartChild->GetID();
-  nsCAutoString strid;
-  if (id)
-    id->ToUTF8String(strid);
+
+  nsIAtom* atomid = aStartChild->GetID();
+  nsCAutoString id;
+  if (atomid)
+    atomid->ToUTF8String(id);
+
   nsAutoString ctag;
-  aContainer->Tag()->ToString(ctag);
-  nsIAtom* cid = aContainer->GetID();
-  nsCAutoString strcid;
-  if (cid)
-    cid->ToUTF8String(strcid);
+  nsCAutoString cid;
+  nsIAtom* catomid = nsnull;
+  if (aContainer) {
+    aContainer->Tag()->ToString(ctag);
+    catomid = aContainer->GetID();
+    if (catomid)
+      catomid->ToUTF8String(cid);
+  }
+
   printf("\ncontent inserted: %s@id='%s', container: %s@id='%s', end node: %p\n\n",
-         NS_ConvertUTF16toUTF8(tag).get(), strid.get(),
-         NS_ConvertUTF16toUTF8(ctag).get(), strcid.get(), aEndChild);
+         NS_ConvertUTF16toUTF8(tag).get(), id.get(),
+         NS_ConvertUTF16toUTF8(ctag).get(), cid.get(), aEndChild);
 #endif
 
-  // XXX: bug 606082. aContainer is null when root element is inserted into
-  // document, we need to handle this and update the tree, also we need to
-  // update a content node of the document accessible.
-  if (aContainer) {
-    nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
-    if (docAccessible)
-      docAccessible->UpdateTree(aContainer, aStartChild, aEndChild, PR_TRUE);
-  }
+  nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
+  if (docAccessible)
+    docAccessible->UpdateTree(aContainer, aStartChild, aEndChild, PR_TRUE);
 }
 
 void
 nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
                                        nsIContent* aContainer,
                                        nsIContent* aChild)
 {
-#ifdef DEBUG_A11Y
-  nsAutoString id;
-  aChild->Tag()->ToString(id);
-  printf("\ncontent removed: %s\n", NS_ConvertUTF16toUTF8(id).get());
+#ifdef DEBUG_CONTENTMUTATION
+  nsAutoString tag;
+  aChild->Tag()->ToString(tag);
+
+  nsIAtom* atomid = aChild->GetID();
+  nsCAutoString id;
+  if (atomid)
+    atomid->ToUTF8String(id);
+
+  nsAutoString ctag;
+  nsCAutoString cid;
+  nsIAtom* catomid = nsnull;
+  if (aContainer) {
+    aContainer->Tag()->ToString(ctag);
+    catomid = aContainer->GetID();
+    if (catomid)
+      catomid->ToUTF8String(cid);
+  }
+
+  printf("\ncontent removed: %s@id='%s', container: %s@id='%s'\n\n",
+           NS_ConvertUTF16toUTF8(tag).get(), id.get(),
+           NS_ConvertUTF16toUTF8(ctag).get(), cid.get());
 #endif
 
-  // XXX: bug 606082. aContainer is null when root element is inserted into
-  // document, we need to handle this and update the tree, perhaps destroy
-  // the document accessible.
-  if (aContainer) {
-    nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
-    if (docAccessible)
-      docAccessible->UpdateTree(aContainer, aChild, aChild->GetNextSibling(),
-                                PR_FALSE);
-  }
+  nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
+  if (docAccessible)
+    docAccessible->UpdateTree(aContainer, aChild, aChild->GetNextSibling(),
+                              PR_FALSE);
 }
 
 void
 nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
 {
   // Presshell destruction will automatically destroy shells for descendant
   // documents, so no need to worry about those. Just shut down the accessible
   // for this one document. That keeps us from having bad behavior in case of
@@ -818,77 +832,51 @@ nsAccessibilityService::GetCachedAccessi
 
   nsAccessible *accessible = nsnull;
   while (!(accessible = GetCachedAccessible(currNode, weakShell)) &&
          (currNode = currNode->GetNodeParent()));
 
   return accessible;
 }
 
-PRBool
-nsAccessibilityService::InitAccessible(nsAccessible *aAccessible,
-                                       nsRoleMapEntry *aRoleMapEntry)
-{
-  if (!aAccessible)
-    return PR_FALSE;
-
-  // Add to cache an accessible, etc.
-  if (!aAccessible->Init()) {
-    NS_ERROR("Failed to initialize an accessible!");
-
-    aAccessible->Shutdown();
-    return PR_FALSE;
-  }
-
-  NS_ASSERTION(aAccessible->IsInCache(),
-               "Initialized accessible not in the cache!");
-
-  aAccessible->SetRoleMapEntry(aRoleMapEntry);
-  return PR_TRUE;
-}
-
 static PRBool HasRelatedContent(nsIContent *aContent)
 {
   nsAutoString id;
   if (!aContent || !nsCoreUtils::GetID(aContent, id) || id.IsEmpty()) {
     return PR_FALSE;
   }
 
-  nsIAtom *relationAttrs[] = {nsAccessibilityAtoms::aria_labelledby,
-                              nsAccessibilityAtoms::aria_describedby,
-                              nsAccessibilityAtoms::aria_owns,
-                              nsAccessibilityAtoms::aria_controls,
-                              nsAccessibilityAtoms::aria_flowto};
-  if (nsCoreUtils::FindNeighbourPointingToNode(aContent, relationAttrs,
-                                               NS_ARRAY_LENGTH(relationAttrs))) {
+  // If the given ID is referred by relation attribute then create an accessible
+  // for it. Take care of HTML elements only for now.
+  if (aContent->IsHTML() &&
+      nsAccUtils::GetDocAccessibleFor(aContent)->IsDependentID(id))
     return PR_TRUE;
-  }
 
   nsIContent *ancestorContent = aContent;
   while ((ancestorContent = ancestorContent->GetParent()) != nsnull) {
     if (ancestorContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_activedescendant)) {
         // ancestor has activedescendant property, this content could be active
       return PR_TRUE;
     }
   }
 
   return PR_FALSE;
 }
 
 already_AddRefed<nsAccessible>
 nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
                                               nsIPresShell* aPresShell,
                                               nsIWeakReference* aWeakShell,
-                                              PRBool* aIsHidden)
+                                              bool* aIsSubtreeHidden)
 {
   if (!aPresShell || !aWeakShell || !aNode || gIsShutdown)
     return nsnull;
 
-  if (aIsHidden)
-    *aIsHidden = PR_FALSE;
+  if (aIsSubtreeHidden)
+    *aIsSubtreeHidden = false;
 
   // Check to see if we already have an accessible for this node in the cache.
   nsAccessible *cachedAccessible = GetCachedAccessible(aNode, aWeakShell);
   if (cachedAccessible) {
     NS_ADDREF(cachedAccessible);
     return cachedAccessible;
   }
 
@@ -919,55 +907,63 @@ nsAccessibilityService::GetOrCreateAcces
     return nsnull;
 
   // Frames can be deallocated when we flush layout, or when we call into code
   // that can flush layout, either directly, or via DOM manipulation, or some
   // CSS styles like :hover. We use the weak frame checks to avoid calling
   // methods on a dead frame pointer.
   nsWeakFrame weakFrame = content->GetPrimaryFrame();
 
-  // Check frame to see if it is hidden.
-  if (!weakFrame.GetFrame()) {
-    if (aIsHidden)
-      *aIsHidden = PR_TRUE;
+  // Check frame and its visibility. Note, hidden frame allows visible
+  // elements in subtree.
+  if (!weakFrame.GetFrame() || !weakFrame->GetStyleVisibility()->IsVisible()) {
+    if (aIsSubtreeHidden && !weakFrame.GetFrame())
+      *aIsSubtreeHidden = true;
 
     return nsnull;
   }
 
   if (weakFrame.GetFrame()->GetContent() != content) {
     // Not the main content for this frame. This happens because <area>
     // elements return the image frame as their primary frame. The main content
     // for the image frame is the image content. If the frame is not an image
     // frame or the node is not an area element then null is returned.
     // This setup will change when bug 135040 is fixed.
     nsAccessible* areaAcc = GetAreaAccessible(weakFrame.GetFrame(),
                                               aNode, aWeakShell);
     NS_IF_ADDREF(areaAcc);
     return areaAcc;
   }
 
+  nsDocAccessible* docAcc =
+    GetAccService()->GetDocAccessible(aNode->GetOwnerDoc());
+  if (!docAcc) {
+    NS_NOTREACHED("No document for accessible being created!");
+    return nsnull;
+  }
+
   // Attempt to create an accessible based on what we know.
   nsRefPtr<nsAccessible> newAcc;
   if (content->IsNodeOfType(nsINode::eTEXT)) {
     // --- Create HTML for visible text frames ---
     nsIFrame* f = weakFrame.GetFrame();
     if (f && f->IsEmpty()) {
       nsAutoString renderedWhitespace;
       f->GetRenderedText(&renderedWhitespace, nsnull, nsnull, 0, 1);
       if (renderedWhitespace.IsEmpty()) {
         // Really empty -- nothing is rendered
-        if (aIsHidden)
-          *aIsHidden = PR_TRUE;
+        if (aIsSubtreeHidden)
+          *aIsSubtreeHidden = true;
 
         return nsnull;
       }
     }
     if (weakFrame.IsAlive()) {
       newAcc = weakFrame.GetFrame()->CreateAccessible();
-      if (InitAccessible(newAcc, nsnull))
+      if (docAcc->BindToDocument(newAcc, nsnull))
         return newAcc.forget();
       return nsnull;
     }
 
     return nsnull;
   }
 
   PRBool isHTML = content->IsHTML();
@@ -978,24 +974,24 @@ nsAccessibilityService::GetOrCreateAcces
     // suppose it is used for links grouping. Otherwise we think it is used in
     // conjuction with HTML image element and in this case we don't create any
     // accessible for it and don't walk into it. The accessibles for HTML area
     // (nsHTMLAreaAccessible) the map contains are attached as children of the
     // appropriate accessible for HTML image (nsHTMLImageAccessible).
     nsAutoString name;
     content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::name, name);
     if (!name.IsEmpty()) {
-      if (aIsHidden)
-        *aIsHidden = PR_TRUE;
+      if (aIsSubtreeHidden)
+        *aIsSubtreeHidden = true;
 
       return nsnull;
     }
 
     newAcc = new nsHyperTextAccessibleWrap(content, aWeakShell);
-    if (InitAccessible(newAcc, nsAccUtils::GetRoleMapEntry(aNode)))
+    if (docAcc->BindToDocument(newAcc, nsAccUtils::GetRoleMapEntry(aNode)))
       return newAcc.forget();
     return nsnull;
   }
 
   nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(aNode);
   if (roleMapEntry && !nsCRT::strcmp(roleMapEntry->roleString, "presentation") &&
       !content->IsFocusable()) { // For presentation only
     // Only create accessible for role of "presentation" if it is focusable --
@@ -1103,28 +1099,28 @@ nsAccessibilityService::GetOrCreateAcces
       newAcc = CreateHTMLAccessibleByMarkup(weakFrame.GetFrame(), content,
                                             aWeakShell);
 
       if (!newAcc) {
         // Do not create accessible object subtrees for non-rendered table
         // captions. This could not be done in
         // nsTableCaptionFrame::GetAccessible() because the descendants of
         // the table caption would still be created. By setting
-        // *aIsHidden = PR_TRUE we ensure that no descendant accessibles are
-        // created.
+        // *aIsSubtreeHidden = true we ensure that no descendant accessibles
+        // are created.
         nsIFrame* f = weakFrame.GetFrame();
         if (!f) {
           f = aPresShell->GetRealPrimaryFrameFor(content);
         }
         if (f->GetType() == nsAccessibilityAtoms::tableCaptionFrame &&
            f->GetRect().IsEmpty()) {
           // XXX This is not the ideal place for this code, but right now there
           // is no better place:
-          if (aIsHidden)
-            *aIsHidden = PR_TRUE;
+          if (aIsSubtreeHidden)
+            *aIsSubtreeHidden = true;
 
           return nsnull;
         }
 
         // Try using frame to do it.
         newAcc = f->CreateAccessible();
       }
     }
@@ -1172,17 +1168,17 @@ nsAccessibilityService::GetOrCreateAcces
       newAcc = new nsHyperTextAccessibleWrap(content, aWeakShell);
     }
     else {  // XUL, SVG, MathML etc.
       // Interesting generic non-HTML container
       newAcc = new nsAccessibleWrap(content, aWeakShell);
     }
   }
 
-  if (InitAccessible(newAcc, roleMapEntry))
+  if (docAcc->BindToDocument(newAcc, roleMapEntry))
     return newAcc.forget();
   return nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService private
 
 PRBool
@@ -1329,32 +1325,39 @@ nsAccessibilityService::GetAreaAccessibl
     return nsnull;
 
   nsCOMPtr<nsIDOMHTMLAreaElement> areaElmt = do_QueryInterface(aAreaNode);
   if (!areaElmt)
     return nsnull;
 
   // Try to get image map accessible from the global cache or create it
   // if failed.
-  nsRefPtr<nsAccessible> imageAcc =
-    GetCachedAccessible(aImageFrame->GetContent(), aWeakShell);
-  if (!imageAcc) {
-    imageAcc = CreateHTMLImageAccessible(aImageFrame->GetContent(),
-                                         aImageFrame->PresContext()->PresShell());
+  nsRefPtr<nsAccessible> image = GetCachedAccessible(aImageFrame->GetContent(),
+                                                     aWeakShell);
+  if (!image) {
+    image = CreateHTMLImageAccessible(aImageFrame->GetContent(),
+                                      aImageFrame->PresContext()->PresShell());
 
-    if (!InitAccessible(imageAcc, nsnull))
+    nsDocAccessible* document =
+      GetAccService()->GetDocAccessible(aAreaNode->GetOwnerDoc());
+    if (!document) {
+      NS_NOTREACHED("No document for accessible being created!");
+      return nsnull;
+    }
+
+    if (!document->BindToDocument(image, nsnull))
       return nsnull;
   }
 
   if (aImageAccessible)
-    *aImageAccessible = imageAcc;
+    *aImageAccessible = image;
 
   // Make sure <area> accessible children of the image map are cached so
   // that they should be available in global cache.
-  imageAcc->EnsureChildren();
+  image->EnsureChildren();
 
   return GetCachedAccessible(aAreaNode, aWeakShell);
 }
 
 already_AddRefed<nsAccessible>
 nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
                                                nsIWeakReference* aWeakShell)
 {
@@ -1752,17 +1755,17 @@ nsAccessible*
 nsAccessibilityService::AddNativeRootAccessible(void* aAtkAccessible)
  {
 #ifdef MOZ_ACCESSIBILITY_ATK
   nsApplicationAccessible* applicationAcc =
     nsAccessNode::GetApplicationAccessible();
   if (!applicationAcc)
     return nsnull;
 
-  nsNativeRootAccessibleWrap* nativeRootAcc =
+  nsRefPtr<nsNativeRootAccessibleWrap> nativeRootAcc =
      new nsNativeRootAccessibleWrap((AtkObject*)aAtkAccessible);
   if (!nativeRootAcc)
     return nsnull;
 
   if (applicationAcc->AppendChild(nativeRootAcc))
     return nativeRootAcc;
 #endif
 
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -106,19 +106,19 @@ public:
     CreateHyperTextAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
   virtual already_AddRefed<nsAccessible>
     CreateOuterDocAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
 
   virtual nsAccessible* AddNativeRootAccessible(void* aAtkAccessible);
   virtual void RemoveNativeRootAccessible(nsAccessible* aRootAccessible);
 
   virtual void ContentRangeInserted(nsIPresShell* aPresShell,
-                                      nsIContent* aContainer,
-                                      nsIContent* aStartChild,
-                                      nsIContent* aEndChild);
+                                    nsIContent* aContainer,
+                                    nsIContent* aStartChild,
+                                    nsIContent* aEndChild);
 
   virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer,
                               nsIContent* aChild);
 
   virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget);
 
   virtual void PresShellDestroyed(nsIPresShell* aPresShell);
 
@@ -133,26 +133,26 @@ public:
    * Return true if accessibility service has been shutdown.
    */
   static PRBool IsShutdown() { return gIsShutdown; }
 
   /**
    * Return an accessible for the given DOM node from the cache or create new
    * one.
    *
-   * @param  aNode       [in] the given node
-   * @param  aPresShell  [in] the pres shell of the node
-   * @param  aWeakShell  [in] the weak shell for the pres shell
-   * @param  aIsHidden   [out, optional] indicates whether the node's frame is
-   *                       hidden
+   * @param  aNode             [in] the given node
+   * @param  aPresShell        [in] the pres shell of the node
+   * @param  aWeakShell        [in] the weak shell for the pres shell
+   * @param  aIsSubtreeHidden  [out, optional] indicates whether the node's
+   *                             frame and its subtree is hidden
    */
   already_AddRefed<nsAccessible>
     GetOrCreateAccessible(nsINode* aNode, nsIPresShell* aPresShell,
                           nsIWeakReference* aWeakShell,
-                          PRBool* aIsHidden = nsnull);
+                          bool* aIsSubtreeHidden = nsnull);
 
   /**
    * Return an accessible for the given DOM node.
    */
   nsAccessible* GetAccessible(nsINode* aNode);
 
   /**
    * Return an accessible for a DOM node in the given presshell.
@@ -197,29 +197,16 @@ public:
    * @param aDOMNode    [in] the DOM node to get an accessible for
    */
   inline nsAccessible* GetCachedContainerAccessible(nsINode *aNode)
   {
     return aNode ?
       GetCachedAccessibleOrContainer(aNode->GetNodeParent()) : nsnull;
   }
 
-  /**
-   * Initialize an accessible and cache it. The method should be called for
-   * every created accessible.
-   *
-   * @param  aAccessible    [in] accessible to initialize.
-   * @param  aRoleMapEntry  [in] the role map entry role the ARIA role or nsnull
-   *                          if none
-   *
-   * @return true if the accessible was initialized, otherwise false
-   */
-  PRBool InitAccessible(nsAccessible *aAccessible,
-                        nsRoleMapEntry *aRoleMapEntry);
-
 protected:
   /**
    * Return an accessible for the DOM node in the given presentation shell if
    * the accessible already exists, otherwise null.
    *
    * @param  aNode       [in] the DOM node to get an access node for
    * @param  aPresShell  [in] the presentation shell which contains layout info
    *                       for the DOM node
--- 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>
@@ -462,19 +470,16 @@ nsAccessible::GetPreviousSibling(nsIAcce
 
   /* readonly attribute nsIAccessible firstChild; */
 NS_IMETHODIMP
 nsAccessible::GetFirstChild(nsIAccessible **aFirstChild) 
 {
   NS_ENSURE_ARG_POINTER(aFirstChild);
   *aFirstChild = nsnull;
 
-  if (gIsCacheDisabled)
-    InvalidateChildren();
-
   PRInt32 childCount = GetChildCount();
   NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
 
   if (childCount > 0)
     NS_ADDREF(*aFirstChild = GetChildAt(0));
 
   return NS_OK;
 }
@@ -1135,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"
@@ -1196,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;
@@ -2035,100 +2044,124 @@ nsAccessible::GetRelationByType(PRUint32
   NS_ENSURE_ARG_POINTER(aRelation);
   *aRelation = nsnull;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   // Relationships are defined on the same content node that the role would be
   // defined on.
-  nsresult rv;
+  nsresult rv = NS_OK_NO_RELATION_TARGET;
   switch (aRelationType)
   {
   case nsIAccessibleRelation::RELATION_LABEL_FOR:
     {
+      RelatedAccIterator iter(GetDocAccessible(), mContent,
+                              nsAccessibilityAtoms::aria_labelledby);
+
+      nsAccessible* related = nsnull;
+      while ((related = iter.Next())) {
+        rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
       if (mContent->Tag() == nsAccessibilityAtoms::label) {
         nsIAtom *IDAttr = mContent->IsHTML() ?
           nsAccessibilityAtoms::_for : nsAccessibilityAtoms::control;
         rv = nsRelUtils::
           AddTargetFromIDRefAttr(aRelationType, aRelation, mContent, IDAttr);
         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::aria_labelledby);
+      return rv;
     }
 
   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:
     {
-      rv = nsRelUtils::
-        AddTargetFromNeighbour(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
+      RelatedAccIterator iter(GetDocAccessible(), mContent,
+                              nsAccessibilityAtoms::aria_describedby);
+
+      nsAccessible* related = nsnull;
+      while ((related = iter.Next())) {
+        rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
 
       if (mContent->Tag() == nsAccessibilityAtoms::description &&
           mContent->IsXUL()) {
         // This affectively adds an optional control attribute to xul:description,
         // which only affects accessibility, by allowing the description to be
         // tied to a control.
         return nsRelUtils::
           AddTargetFromIDRefAttr(aRelationType, aRelation, mContent,
                                  nsAccessibilityAtoms::control);
       }
 
-      return NS_OK;
+      return rv;
     }
 
   case nsIAccessibleRelation::RELATION_NODE_CHILD_OF:
     {
-      rv = nsRelUtils::
-        AddTargetFromNeighbour(aRelationType, aRelation, mContent,
-                               nsAccessibilityAtoms::aria_owns);
-      NS_ENSURE_SUCCESS(rv, rv);
-
+      RelatedAccIterator iter(GetDocAccessible(), mContent,
+                              nsAccessibilityAtoms::aria_owns);
+
+      nsAccessible* related = nsnull;
+      while ((related = iter.Next())) {
+        rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      // Got relation from aria-owns, don't calculate it from native markup.
       if (rv != NS_OK_NO_RELATION_TARGET)
-        return NS_OK; // XXX bug 381599, avoid performance problems
+        return NS_OK;
 
       // This is an ARIA tree or treegrid that doesn't use owns, so we need to
       // get the parent the hard way.
       if (mRoleMapEntry &&
           (mRoleMapEntry->role == nsIAccessibleRole::ROLE_OUTLINEITEM ||
            mRoleMapEntry->role == nsIAccessibleRole::ROLE_ROW)) {
 
         AccGroupInfo* groupInfo = GetGroupInfo();
@@ -2151,54 +2184,67 @@ nsAccessible::GetRelationByType(PRUint32
         if (view) {
           nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
           if (scrollFrame || view->GetWidget() || !frame->GetParent()) {
             return nsRelUtils::AddTarget(aRelationType, aRelation, GetParent());
           }
         }
       }
 
-      return NS_OK;
+      return rv;
     }
 
   case nsIAccessibleRelation::RELATION_CONTROLLED_BY:
     {
-      return nsRelUtils::
-        AddTargetFromNeighbour(aRelationType, aRelation, mContent,
-                               nsAccessibilityAtoms::aria_controls);
+      RelatedAccIterator iter(GetDocAccessible(), mContent,
+                              nsAccessibilityAtoms::aria_controls);
+
+      nsAccessible* related = nsnull;
+      while ((related = iter.Next())) {
+        rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      return rv;
     }
 
   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);
     }
 
   case nsIAccessibleRelation::RELATION_FLOWS_FROM:
     {
-      return nsRelUtils::
-        AddTargetFromNeighbour(aRelationType, aRelation, mContent,
-                               nsAccessibilityAtoms::aria_flowto);
+      RelatedAccIterator iter(GetDocAccessible(), mContent,
+                              nsAccessibilityAtoms::aria_flowto);
+
+      nsAccessible* related = nsnull;
+      while ((related = iter.Next())) {
+        rv = nsRelUtils::AddTarget(aRelationType, aRelation, related);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      return rv;
     }
 
   case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON:
     {
       if (mContent->IsHTML()) {
         // HTML form controls implements nsIFormControl interface.
         nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
         if (control) {
@@ -2619,29 +2665,16 @@ nsAccessible::AppendTextTo(nsAString& aT
   }
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode public methods
 
-PRBool
-nsAccessible::Init()
-{
-  if (!nsAccessNodeWrap::Init())
-    return PR_FALSE;
-
-  nsDocAccessible* document =
-    GetAccService()->GetDocAccessible(mContent->GetOwnerDoc());
-  NS_ASSERTION(document, "Cannot cache new nsAccessible!");
-
-  return document ? document->CacheAccessible(this) : PR_FALSE;
-}
-
 void
 nsAccessible::Shutdown()
 {
   // Invalidate the child count and pointers to other accessibles, also make
   // sure none of its children point to this parent
   InvalidateChildren();
   if (mParent)
     mParent->RemoveChild(this);
@@ -2728,64 +2761,77 @@ nsAccessible::InvalidateChildren()
   mEmbeddedObjCollector = nsnull;
   mChildren.Clear();
   mChildrenFlags = eChildrenUninitialized;
 }
 
 PRBool
 nsAccessible::AppendChild(nsAccessible* aChild)
 {
+  if (!aChild)
+    return PR_FALSE;
+
   if (!mChildren.AppendElement(aChild))
     return PR_FALSE;
 
   if (!nsAccUtils::IsEmbeddedObject(aChild))
     mChildrenFlags = eMixedChildren;
 
   aChild->BindToParent(this, mChildren.Length() - 1);
   return PR_TRUE;
 }
 
 PRBool
 nsAccessible::InsertChildAt(PRUint32 aIndex, nsAccessible* aChild)
 {
+  if (!aChild)
+    return PR_FALSE;
+
   if (!mChildren.InsertElementAt(aIndex, aChild))
     return PR_FALSE;
 
-  for (PRUint32 idx = aIndex + 1; idx < mChildren.Length(); idx++)
-    mChildren[idx]->mIndexInParent++;
+  for (PRUint32 idx = aIndex + 1; idx < mChildren.Length(); idx++) {
+    NS_ASSERTION(mChildren[idx]->mIndexInParent == idx - 1, "Accessible child index doesn't match");
+    mChildren[idx]->mIndexInParent = idx;
+  }
 
   if (nsAccUtils::IsText(aChild))
     mChildrenFlags = eMixedChildren;
 
   mEmbeddedObjCollector = nsnull;
 
   aChild->BindToParent(this, aIndex);
   return PR_TRUE;
 }
 
 PRBool
 nsAccessible::RemoveChild(nsAccessible* aChild)
 {
-  if (aChild->mParent != this || aChild->mIndexInParent == -1)
+  if (!aChild)
     return PR_FALSE;
 
-  if (aChild->mIndexInParent >= mChildren.Length() ||
-      mChildren[aChild->mIndexInParent] != aChild) {
+  PRInt32 index = aChild->mIndexInParent;
+  if (aChild->mParent != this || index == -1)
+    return PR_FALSE;
+
+  if (index >= mChildren.Length() || mChildren[index] != aChild) {
     NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
     aChild->UnbindFromParent();
     return PR_FALSE;
   }
 
-  for (PRUint32 idx = aChild->mIndexInParent + 1; idx < mChildren.Length(); idx++)
-    mChildren[idx]->mIndexInParent--;
-
-  mChildren.RemoveElementAt(aChild->mIndexInParent);
-  mEmbeddedObjCollector = nsnull;
+  for (PRUint32 idx = index + 1; idx < mChildren.Length(); idx++) {
+    NS_ASSERTION(mChildren[idx]->mIndexInParent == idx, "Accessible child index doesn't match");
+    mChildren[idx]->mIndexInParent = idx - 1;
+  }
 
   aChild->UnbindFromParent();
+  mChildren.RemoveElementAt(index);
+  mEmbeddedObjCollector = nsnull;
+
   return PR_TRUE;
 }
 
 nsAccessible*
 nsAccessible::GetParent()
 {
   if (mParent)
     return mParent;
@@ -2897,30 +2943,16 @@ nsAccessible::GetIndexOfEmbeddedChild(ns
       mEmbeddedObjCollector = new EmbeddedObjCollector(this);
     return mEmbeddedObjCollector ?
       mEmbeddedObjCollector->GetIndexAt(aChild) : -1;
   }
 
   return GetIndexOf(aChild);
 }
 
-#ifdef DEBUG
-PRBool
-nsAccessible::IsInCache()
-{
-  nsDocAccessible *docAccessible =
-    GetAccService()->GetDocAccessible(mContent->GetOwnerDoc());
-  if (docAccessible)
-    return docAccessible->GetCachedAccessibleByUniqueID(UniqueID()) ? PR_TRUE : PR_FALSE;
-
-  return PR_FALSE;
-}
-#endif
-
-
 ////////////////////////////////////////////////////////////////////////////////
 // HyperLinkAccessible methods
 
 bool
 nsAccessible::IsHyperLink()
 {
   // Every embedded accessible within hypertext accessible implements
   // hyperlink interface.
@@ -3181,18 +3213,27 @@ nsAccessible::EnsureChildren()
     return PR_TRUE;
   }
 
   if (mChildrenFlags != eChildrenUninitialized)
     return PR_FALSE;
 
   // State is embedded children until text leaf accessible is appended.
   mChildrenFlags = eEmbeddedChildren; // Prevent reentry
+
+  // Notify the document about caching status.
+  nsDocAccessible* document = GetDocAccessible();
+  if (document)
+    document->NotifyOfCachingStart(this);
+
   CacheChildren();
 
+  if (document)
+    document->NotifyOfCachingEnd(this);
+
   return PR_FALSE;
 }
 
 nsAccessible*
 nsAccessible::GetSiblingAtOffset(PRInt32 aOffset, nsresult* aError)
 {
   if (IsDefunct()) {
     if (aError)
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -46,34 +46,31 @@
 #include "nsIAccessibleSelectable.h"
 #include "nsIAccessibleValue.h"
 #include "nsIAccessibleRole.h"
 #include "nsIAccessibleStates.h"
 
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "nsRefPtrHashtable.h"
-#include "nsDataHashtable.h"
 
 class AccGroupInfo;
 class EmbeddedObjCollector;
 class nsAccessible;
 class AccEvent;
 struct nsRoleMapEntry;
 
 struct nsRect;
 class nsIContent;
 class nsIFrame;
 class nsIAtom;
 class nsIView;
 
 typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsAccessible>
   nsAccessibleHashtable;
-typedef nsDataHashtable<nsPtrHashKey<const nsINode>, nsAccessible*>
-  NodeToAccessibleMap;
 
 // see nsAccessible::GetAttrValue
 #define NS_OK_NO_ARIA_VALUE \
 NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 0x21)
 
 // see nsAccessible::GetNameInternal
 #define NS_OK_EMPTY_NAME \
 NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 0x23)
@@ -108,17 +105,16 @@ public:
   NS_DECL_NSIACCESSIBLEHYPERLINK
   NS_DECL_NSIACCESSIBLESELECTABLE
   NS_DECL_NSIACCESSIBLEVALUE
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCESSIBLE_IMPL_IID)
 
   //////////////////////////////////////////////////////////////////////////////
   // nsAccessNode
 
-  virtual PRBool Init();
   virtual void Shutdown();
 
   //////////////////////////////////////////////////////////////////////////////
   // Public methods
 
   /**
    * Returns the accessible name specified by ARIA.
    */
@@ -293,23 +289,16 @@ public:
     return mParent ?
       mParent->mChildren.SafeElementAt(mIndexInParent - 1, nsnull).get() : nsnull;
   }
   PRUint32 GetCachedChildCount() const { return mChildren.Length(); }
   nsAccessible* GetCachedChildAt(PRUint32 aIndex) const { return mChildren.ElementAt(aIndex); }
   PRBool AreChildrenCached() const { return mChildrenFlags != eChildrenUninitialized; }
   bool IsBoundToParent() const { return mParent; }
 
-#ifdef DEBUG
-  /**
-   * Return true if the access node is cached.
-   */
-  PRBool IsInCache();
-#endif
-
   //////////////////////////////////////////////////////////////////////////////
   // Miscellaneous methods
 
   /**
    * Handle accessible event, i.e. process it, notifies observers and fires
    * platform specific event.
    */
   virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
@@ -436,17 +425,17 @@ protected:
   /**
    * Cache accessible children.
    */
   virtual void CacheChildren();
 
   /**
    * Set accessible parent and index in parent.
    */
-  void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
+  virtual void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
   void UnbindFromParent();
 
   /**
    * Return sibling accessible at the given offset.
    */
   virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
                                            nsresult *aError = nsnull);
 
--- a/accessible/src/base/nsBaseWidgetAccessible.cpp
+++ b/accessible/src/base/nsBaseWidgetAccessible.cpp
@@ -202,23 +202,16 @@ nsLinkableAccessible::GetKeyboardShortcu
     return actionAcc->GetKeyboardShortcut(aKeyboardShortcut);
 
   return nsAccessible::GetKeyboardShortcut(aKeyboardShortcut);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsLinkableAccessible. nsAccessNode
 
-PRBool
-nsLinkableAccessible::Init()
-{
-  CacheActionContent();
-  return nsAccessibleWrap::Init();
-}
-
 void
 nsLinkableAccessible::Shutdown()
 {
   mActionContent = nsnull;
   nsAccessibleWrap::Shutdown();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -237,21 +230,29 @@ nsLinkableAccessible::GetAnchorURI(PRUin
         return link->GetAnchorURI(aAnchorIndex);
     }
   }
 
   return nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsLinkableAccessible
+// nsLinkableAccessible: nsAccessible protected
 
 void
-nsLinkableAccessible::CacheActionContent()
+nsLinkableAccessible::BindToParent(nsAccessible* aParent,
+                                   PRUint32 aIndexInParent)
 {
+  nsAccessibleWrap::BindToParent(aParent, aIndexInParent);
+
+  // Cache action content.
+  mActionContent = nsnull;
+  mIsLink = PR_FALSE;
+  mIsOnclick = PR_FALSE;
+
   nsIContent* walkUpContent = mContent;
   PRBool isOnclick = nsCoreUtils::HasClickListener(walkUpContent);
 
   if (isOnclick) {
     mActionContent = walkUpContent;
     mIsOnclick = PR_TRUE;
     return;
   }
@@ -271,16 +272,19 @@ nsLinkableAccessible::CacheActionContent
     if (isOnclick) {
       mActionContent = walkUpContent;
       mIsOnclick = PR_TRUE;
       return;
     }
   }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// nsLinkableAccessible: protected
+
 nsAccessible *
 nsLinkableAccessible::GetActionAccessible() const
 {
   // Return accessible for the action content if it's different from node of
   // this accessible. If the action accessible is not null then it is used to
   // redirect methods calls otherwise we use method implementation from the
   // base class.
   if (!mActionContent || mContent == mActionContent)
--- a/accessible/src/base/nsBaseWidgetAccessible.h
+++ b/accessible/src/base/nsBaseWidgetAccessible.h
@@ -92,36 +92,35 @@ public:
   NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
   NS_IMETHOD GetValue(nsAString& _retval);
   NS_IMETHOD TakeFocus();
   NS_IMETHOD GetKeyboardShortcut(nsAString& _retval);
 
   // nsAccessNode
-  virtual PRBool Init();
   virtual void Shutdown();
 
   // nsAccessible
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
 
   // HyperLinkAccessible
   virtual already_AddRefed<nsIURI> GetAnchorURI(PRUint32 aAnchorIndex);
 
 protected:
+  // nsAccessible
+  virtual void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
+
+  // nsLinkableAccessible
+
   /**
    * Return an accessible for cached action node.
    */
   nsAccessible *GetActionAccessible() const;
 
-  /**
-   * Cache action node.
-   */
-  virtual void CacheActionContent();
-
   nsCOMPtr<nsIContent> mActionContent;
   PRPackedBool mIsLink;
   PRPackedBool mIsOnclick;
 };
 
 /**
  * A simple accessible that gets its enumerated role passed into constructor.
  */ 
--- a/accessible/src/base/nsCoreUtils.cpp
+++ b/accessible/src/base/nsCoreUtils.cpp
@@ -42,17 +42,16 @@
 
 #include "nsAccessNode.h"
 
 #include "nsIDocument.h"
 #include "nsIDOMAbstractView.h"
 #include "nsIDOM3Node.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentView.h"
-#include "nsIDOMDocumentXBL.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMViewCSS.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDocShell.h"
@@ -457,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;
@@ -582,345 +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::GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr,
-                                     nsIArray **aRefElements)
-{
-  *aRefElements = nsnull;
-
-  nsAutoString ids;
-  if (!aContent->GetAttr(kNameSpaceID_None, aAttr, ids))
-    return;
-
-  ids.CompressWhitespace(PR_TRUE, PR_TRUE);
-
-  nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(aContent->GetOwnerDoc());
-  NS_ASSERTION(document, "The given node is not in document!");
-  if (!document)
-    return;
-
-  nsCOMPtr<nsIDOMDocumentXBL> xblDocument;
-  if (aContent->IsInAnonymousSubtree())
-    xblDocument = do_QueryInterface(document);
-
-  nsCOMPtr<nsIMutableArray> refElms = do_CreateInstance(NS_ARRAY_CONTRACTID);
-
-  while (!ids.IsEmpty()) {
-    nsAutoString id;
-    PRInt32 idLength = ids.FindChar(' ');
-    NS_ASSERTION(idLength != 0,
-                 "Should not be 0 because of CompressWhitespace() call above");
-
-    if (idLength == kNotFound) {
-      id = ids;
-      ids.Truncate();
-    } else {
-      id = Substring(ids, 0, idLength);
-      ids.Cut(0, idLength + 1);
-    }
-
-    // If content is anonymous subtree then use "anonid" attribute to get
-    // elements, otherwise search elements in DOM by ID attribute.
-    nsCOMPtr<nsIDOMElement> refElement;
-    if (xblDocument) {
-      nsCOMPtr<nsIDOMElement> elm =
-        do_QueryInterface(aContent->GetBindingParent());
-      xblDocument->GetAnonymousElementByAttribute(elm,
-                                                  NS_LITERAL_STRING("anonid"),
-                                                  id,
-                                                  getter_AddRefs(refElement));
-    } else {
-      document->GetElementById(id, getter_AddRefs(refElement));
-    }
-
-    if (!refElement)
-      continue;
-
-    refElms->AppendElement(refElement, PR_FALSE);
-  }
-
-  NS_ADDREF(*aRefElements = refElms);
-  return;
-}
-
-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;
 
@@ -1160,8 +830,83 @@ nsAccessibleDOMStringList::GetLength(PRU
 
 NS_IMETHODIMP
 nsAccessibleDOMStringList::Contains(const nsAString& aString, PRBool *aResult)
 {
   *aResult = mNames.Contains(aString);
 
   return NS_OK;
 }
+
+
+////////////////////////////////////////////////////////////////////////////////
+// IDRefsIterator
+////////////////////////////////////////////////////////////////////////////////
+
+IDRefsIterator::IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr) :
+  mCurrIdx(0)
+{
+  if (!aContent->IsInDoc() ||
+      !aContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs))
+    return;
+
+  if (aContent->IsInAnonymousSubtree()) {
+    mXBLDocument = do_QueryInterface(aContent->GetOwnerDoc());
+    mBindingParent = do_QueryInterface(aContent->GetBindingParent());
+  } else {
+    mDocument = aContent->GetOwnerDoc();
+  }
+}
+
+const nsDependentSubstring
+IDRefsIterator::NextID()
+{
+  for (; mCurrIdx < mIDs.Length(); mCurrIdx++) {
+    if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
+      break;
+  }
+
+  if (mCurrIdx >= mIDs.Length())
+    return nsDependentSubstring();
+
+  nsAString::index_type idStartIdx = mCurrIdx;
+  while (++mCurrIdx < mIDs.Length()) {
+    if (NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
+      break;
+  }
+
+  return Substring(mIDs, idStartIdx, mCurrIdx++ - idStartIdx);
+}
+
+nsIContent*
+IDRefsIterator::NextElem()
+{
+  while (true) {
+    const nsDependentSubstring id = NextID();
+    if (id.IsEmpty())
+      break;
+
+    nsIContent* refContent = GetElem(id);
+    if (refContent)
+      return refContent;
+  }
+
+  return nsnull;
+}
+
+nsIContent*
+IDRefsIterator::GetElem(const nsDependentSubstring& aID)
+{
+  if (mXBLDocument) {
+    // If content is anonymous subtree then use "anonid" attribute to get
+    // elements, otherwise search elements in DOM by ID attribute.
+
+    nsCOMPtr<nsIDOMElement> refElm;
+    mXBLDocument->GetAnonymousElementByAttribute(mBindingParent,
+                                                 NS_LITERAL_STRING("anonid"),
+                                                 aID,
+                                                 getter_AddRefs(refElm));
+    nsCOMPtr<nsIContent> refContent = do_QueryInterface(refElm);
+    return refContent;
+  }
+
+  return mDocument->GetElementById(aID);
+}
--- a/accessible/src/base/nsCoreUtils.h
+++ b/accessible/src/base/nsCoreUtils.h
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsCoreUtils_h_
 #define nsCoreUtils_h_
 
 #include "nsAccessibilityAtoms.h"
 
+#include "nsIDOMDocumentXBL.h"
 #include "nsIDOMNode.h"
 #include "nsIContent.h"
 #include "nsIBoxObject.h"
 #include "nsITreeBoxObject.h"
 #include "nsITreeColumns.h"
 
 #include "nsIFrame.h"
 #include "nsIDocShellTreeItem.h"
@@ -218,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);
@@ -300,134 +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 the given node is referred to by its
-   * IDRefs attribute.
-   *
-   * @param aContent     [in] the given node
-   * @param aAttr        [in] IDRefs attribute on the given node
-   * @param aRefElements [out] result array of elements
-   */
-  static void GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr,
-                                      nsIArray **aRefElements);
-
-  /**
-   * 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.
    */
@@ -510,10 +395,44 @@ public:
   PRBool Add(const nsAString& aName) {
     return mNames.AppendElement(aName) != nsnull;
   }
 
 private:
   nsTArray<nsString> mNames;
 };
 
+/**
+ * Used to iterate through IDs or elements pointed by IDRefs attribute. Note,
+ * any method used to iterate through IDs or elements moves iterator to next
+ * position.
+ */
+class IDRefsIterator
+{
+public:
+  IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr);
+
+  /**
+   * Return next ID.
+   */
+  const nsDependentSubstring NextID();
+
+  /**
+   * Return next element.
+   */
+  nsIContent* NextElem();
+
+  /**
+   * Return the element with the given ID.
+   */
+  nsIContent* GetElem(const nsDependentSubstring& aID);
+
+private:
+  nsString mIDs;
+  nsAString::index_type mCurrIdx;
+
+  nsIDocument* mDocument;
+  nsCOMPtr<nsIDOMDocumentXBL> mXBLDocument;
+  nsCOMPtr<nsIDOMElement> mBindingParent;
+};
+
 #endif
 
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -80,26 +80,40 @@
 
 namespace dom = mozilla::dom;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Static member initialization
 
 PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0;
 
+static nsIAtom** kRelationAttrs[] =
+{
+  &nsAccessibilityAtoms::aria_labelledby,
+  &nsAccessibilityAtoms::aria_describedby,
+  &nsAccessibilityAtoms::aria_owns,
+  &nsAccessibilityAtoms::aria_controls,
+  &nsAccessibilityAtoms::aria_flowto,
+  &nsAccessibilityAtoms::_for,
+  &nsAccessibilityAtoms::control
+};
+
+static const PRUint32 kRelationAttrsLen = NS_ARRAY_LENGTH(kRelationAttrs);
 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/desctructor
 
 nsDocAccessible::
   nsDocAccessible(nsIDocument *aDocument, nsIContent *aRootContent,
                   nsIWeakReference *aShell) :
   nsHyperTextAccessibleWrap(aRootContent, aShell),
-  mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE)
+  mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE),
+  mCacheRoot(nsnull), mIsPostCacheProcessing(PR_FALSE)
 {
+  mDependentIDsHash.Init();
   // XXX aaronl should we use an algorithm for the initial cache size?
   mAccessibleCache.Init(kDefaultCacheSize);
   mNodeToAccessibleMap.Init(kDefaultCacheSize);
 
   // For GTK+ native window, we do nothing here.
   if (!mDocument)
     return;
 
@@ -129,16 +143,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   }
 
   CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventQueue)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
+  tmp->mDependentIDsHash.Clear();
   tmp->mNodeToAccessibleMap.Clear();
   ClearCache(tmp->mAccessibleCache);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible)
   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsDocAccessible)
   NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
@@ -150,18 +165,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
 
   nsresult status;
   if (!foundInterface) {
     // HTML document accessible must inherit from nsHyperTextAccessible to get
     // support text interfaces. XUL document accessible doesn't need this.
     // However at some point we may push <body> to implement the interfaces and
     // return nsDocAccessible to inherit from nsAccessibleWrap.
 
-    nsCOMPtr<nsIDOMXULDocument> xulDoc(do_QueryInterface(mDocument));
-    if (xulDoc)
+    if (mDocument && mDocument->IsXUL())
       status = nsAccessible::QueryInterface(aIID, (void**)&foundInterface);
     else
       status = nsHyperTextAccessible::QueryInterface(aIID,
                                                      (void**)&foundInterface);
   } else {
     NS_ADDREF(foundInterface);
     status = NS_OK;
   }
@@ -280,33 +294,37 @@ nsDocAccessible::GetStateInternal(PRUint
 
   if (IsDefunct()) {
     if (aExtraState)
       *aExtraState = nsIAccessibleStates::EXT_STATE_DEFUNCT;
 
     return NS_OK_DEFUNCT_OBJECT;
   }
 
-  if (aExtraState)
-    *aExtraState = 0;
+  if (aExtraState) {
+    // The root content of the document might be removed so that mContent is
+    // out of date.
+    *aExtraState = (mContent->GetCurrentDoc() == mDocument) ?
+      0 : nsIAccessibleStates::EXT_STATE_STALE;
+  }
 
 #ifdef MOZ_XUL
   nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
   if (!xulDoc)
 #endif
   {
     // 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()) {
@@ -590,41 +608,16 @@ nsDocAccessible::GetCachedAccessible(nsI
   nsAccessible* parent(accessible->GetCachedParent());
   if (parent)
     parent->TestChildCache(accessible);
 #endif
 
   return accessible;
 }
 
-// nsDocAccessible public method
-PRBool
-nsDocAccessible::CacheAccessible(nsAccessible* aAccessible)
-{
-  if (aAccessible->IsPrimaryForNode() &&
-      !mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible))
-    return PR_FALSE;
-
-  return mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible);
-}
-
-// nsDocAccessible public method
-void
-nsDocAccessible::ShutdownAccessible(nsAccessible *aAccessible)
-{
-  // Remove an accessible from node to accessible map if it is presented there.
-  if (aAccessible->IsPrimaryForNode() &&
-      mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
-    mNodeToAccessibleMap.Remove(aAccessible->GetNode());
-
-  void* uniqueID = aAccessible->UniqueID();
-  aAccessible->Shutdown();
-  mAccessibleCache.Remove(uniqueID);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode
 
 PRBool
 nsDocAccessible::Init()
 {
   NS_LOG_ACCDOCCREATE_FOR("document initialize", mDocument, this)
 
@@ -670,24 +663,27 @@ nsDocAccessible::Shutdown()
   if (mParent) {
     nsDocAccessible* parentDocument = mParent->GetDocAccessible();
     if (parentDocument)
       parentDocument->RemoveChildDocument(this);
 
     mParent->RemoveChild(this);
   }
 
-  PRUint32 childDocCount = mChildDocuments.Length();
-  for (PRUint32 idx = 0; idx < childDocCount; idx++)
+  // Walk the array backwards because child documents remove themselves from the
+  // array as they are shutdown.
+  PRInt32 childDocCount = mChildDocuments.Length();
+  for (PRInt32 idx = childDocCount - 1; idx >= 0; idx--)
     mChildDocuments[idx]->Shutdown();
 
   mChildDocuments.Clear();
 
   mWeakShell = nsnull;  // Avoid reentrancy
 
+  mDependentIDsHash.Clear();
   mNodeToAccessibleMap.Clear();
   ClearCache(mAccessibleCache);
 
   nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
   mDocument = nsnull;
 
   nsHyperTextAccessibleWrap::Shutdown();
 
@@ -943,30 +939,49 @@ 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 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 =
+      GetAccService()->GetAccessibleInWeakShell(aElement, mWeakShell);
+    if (accessible)
+      RemoveDependentIDsFor(accessible, aAttribute);
+  }
 }
 
 void
 nsDocAccessible::AttributeChanged(nsIDocument *aDocument,
                                   dom::Element* aElement,
                                   PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                                   PRInt32 aModType)
 {
   AttributeChangedImpl(aElement, aNameSpaceID, aAttribute);
 
+  // Update dependent IDs cache.
+  if (aModType == nsIDOMMutationEvent::MODIFICATION ||
+      aModType == nsIDOMMutationEvent::ADDITION) {
+    nsAccessible* accessible =
+      GetAccService()->GetAccessibleInWeakShell(aElement, mWeakShell);
+
+    if (accessible)
+      AddDependentIDsFor(accessible, aAttribute);
+  }
+
   // If it was the focused node, cache the new state
   if (aElement == gLastFocusedNode) {
     nsAccessible *focusedAccessible = GetAccService()->GetAccessible(aElement);
     if (focusedAccessible)
       gLastFocusedAccessiblesState = nsAccUtils::State(focusedAccessible);
   }
 }
 
@@ -1038,18 +1053,31 @@ nsDocAccessible::AttributeChangedImpl(ns
   if (aNameSpaceID == kNameSpaceID_None) {
     // Check for hyphenated aria-foo property?
     if (StringBeginsWith(nsDependentAtomString(aAttribute),
                          NS_LITERAL_STRING("aria-"))) {
       ARIAAttributeChanged(aContent, aAttribute);
     }
   }
 
-  if (aAttribute == nsAccessibilityAtoms::role ||
-      aAttribute == nsAccessibilityAtoms::href ||
+  if (aAttribute == nsAccessibilityAtoms::role) {
+    if (mContent == aContent) {
+      // It is common for js libraries to set the role of the body element after
+      // the doc has loaded. In this case we just update the role map entry. 
+      SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aContent));
+    }
+    else {
+      // Recreate the accessible when role is changed because we might require a
+      // different accessible class for the new role or the accessible may
+      // expose a different sets of interfaces (COM restriction).
+      RecreateAccessible(aContent);
+    }
+  }
+
+  if (aAttribute == nsAccessibilityAtoms::href ||
       aAttribute == nsAccessibilityAtoms::onclick) {
     // Not worth the expense to ensure which namespace these are in
     // It doesn't kill use to recreate the accessible even if the attribute was used
     // in the wrong namespace or an element that doesn't support it
     RecreateAccessible(aContent);
     return;
   }
   
@@ -1234,22 +1262,27 @@ void nsDocAccessible::ContentAppended(ns
 {
 }
 
 void nsDocAccessible::ContentStatesChanged(nsIDocument* aDocument,
                                            nsIContent* aContent1,
                                            nsIContent* aContent2,
                                            nsEventStates aStateMask)
 {
-  if (!aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
-    return;
+  if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
+    nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
+    nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
   }
 
-  nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
-  nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
+  if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
+    nsRefPtr<AccEvent> event =
+      new AccStateChangeEvent(aContent1, nsIAccessibleStates::STATE_INVALID,
+                              PR_FALSE, PR_TRUE);
+    FireDelayedAccessibleEvent(event);
+   }
 }
 
 void nsDocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
                                             nsEventStates aStateMask)
 {
 }
 
 void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument,
@@ -1329,44 +1362,124 @@ nsDocAccessible::GetCachedAccessibleByUn
     child = childDocument->GetCachedAccessibleByUniqueIDInSubtree(aUniqueID);
     if (child)
       return child;
   }
 
   return nsnull;
 }
 
+bool
+nsDocAccessible::BindToDocument(nsAccessible* aAccessible,
+                                nsRoleMapEntry* aRoleMapEntry)
+{
+  if (!aAccessible)
+    return false;
+
+  // Put into DOM node cache.
+  if (aAccessible->IsPrimaryForNode() &&
+      !mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible))
+    return false;
+
+  // Put into unique ID cache.
+  if (!mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible)) {
+    if (aAccessible->IsPrimaryForNode())
+      mNodeToAccessibleMap.Remove(aAccessible->GetNode());
+
+    return false;
+  }
+
+  // Initialize the accessible.
+  if (!aAccessible->Init()) {
+    NS_ERROR("Failed to initialize an accessible!");
+
+    UnbindFromDocument(aAccessible);
+    return false;
+  }
+
+  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());
+
+  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)
 {
   // Content change notification mostly are async, thus we can't detect whether
   // these actions are from user. This information is used to fire or do not
   // fire events to avoid events that are generated because of document loading.
   // Since this information may be not correct then we need to fire some events
   // regardless the document loading state.
 
+  // Update the whole tree of this document accessible when the container is
+  // null (document element is inserted or removed).
+
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   nsIEventStateManager* esm = presShell->GetPresContext()->EventStateManager();
   PRBool fireAllEvents = PR_TRUE;//IsContentLoaded() || esm->IsHandlingUserInputExternal();
 
-  // We don't create new accessibles on content removal.
-  nsAccessible* container = aIsInsert ?
-    GetAccService()->GetAccessibleOrContainer(aContainerNode, mWeakShell) :
-    GetAccService()->GetCachedAccessibleOrContainer(aContainerNode);
+  // XXX: bug 608887 reconsider accessible tree update logic because
+  // 1) elements appended outside the HTML body don't get accessibles;
+  // 2) the document having elements that should be accessible may function
+  // without body.
+  nsAccessible* container = nsnull;
+  if (aIsInsert) {
+    container = aContainerNode ?
+      GetAccService()->GetAccessibleOrContainer(aContainerNode, mWeakShell) :
+      this;
 
-  if (aIsInsert) {
+    // 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;
+
+      // 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();
+
+  } else {
+    // Don't create new accessibles on content removal.
+    container = aContainerNode ?
+      GetAccService()->GetCachedAccessibleOrContainer(aContainerNode) :
+      this;
   }
 
   EIsFromUserInput fromUserInput = esm->IsHandlingUserInputExternal() ?
     eFromUserInput : eNoUserInput;
 
   // Update the accessible tree in the case of content removal and fire events
   // if allowed.
   PRUint32 updateFlags =
@@ -1473,20 +1586,169 @@ nsDocAccessible::RecreateAccessible(nsIN
       new AccEvent(nsIAccessibleEvent::EVENT_REORDER, parent->GetNode(),
                    eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
 
     if (reorderEvent)
       FireDelayedAccessibleEvent(reorderEvent);
   }
 }
 
+void
+nsDocAccessible::NotifyOfCachingStart(nsAccessible* aAccessible)
+{
+  if (!mCacheRoot)
+    mCacheRoot = aAccessible;
+}
+
+void
+nsDocAccessible::NotifyOfCachingEnd(nsAccessible* aAccessible)
+{
+  if (mCacheRoot == aAccessible && !mIsPostCacheProcessing) {
+    // Allow invalidation list insertions while container children are recached.
+    mIsPostCacheProcessing = PR_TRUE;
+
+    // Invalidate children of container accessible for each element in
+    // invalidation list.
+    for (PRUint32 idx = 0; idx < mInvalidationList.Length(); idx++) {
+      nsIContent* content = mInvalidationList[idx];
+      nsAccessible* container =
+        GetAccService()->GetCachedContainerAccessible(content);
+      container->InvalidateChildren();
+
+      // Make sure we keep children updated. While we're inside of caching loop
+      // then we must exist it with cached children.
+      container->EnsureChildren();
+    }
+    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) {
+        providers = new AttrRelProviderArray();
+        if (providers) {
+          if (!mDependentIDsHash.Put(id, providers)) {
+            delete providers;
+            providers = nsnull;
+          }
+        }
+      }
+
+      if (providers) {
+        AttrRelProvider* provider =
+          new AttrRelProvider(relAttr, aRelProvider->GetContent());
+        if (provider) {
+          providers->AppendElement(provider);
+
+          // We've got here during the children caching. If the referenced
+          // content is not accessible then store it to pend its container
+          // children invalidation (this happens immediately after the caching
+          // is finished).
+          nsIContent* dependentContent = iter.GetElem(id);
+          if (dependentContent && !GetCachedAccessible(dependentContent)) {
+            mInvalidationList.AppendElement(dependentContent);
+          }
+        }
+      }
+    }
+
+    // If the relation attribute is given then we don't have anything else to
+    // check.
+    if (aRelAttr)
+      break;
+  }
+}
+
+void
+nsDocAccessible::RemoveDependentIDsFor(nsAccessible* aRelProvider,
+                                       nsIAtom* aRelAttr)
+{
+  for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
+    nsIAtom* relAttr = *kRelationAttrs[idx];
+    if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
+      continue;
+
+    IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
+    while (true) {
+      const nsDependentSubstring id = iter.NextID();
+      if (id.IsEmpty())
+        break;
+
+      AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
+      if (providers) {
+        for (PRUint32 jdx = 0; jdx < providers->Length(); ) {
+          AttrRelProvider* provider = (*providers)[jdx];
+          if (provider->mRelAttr == relAttr &&
+              provider->mContent == aRelProvider->GetContent())
+            providers->RemoveElement(provider);
+          else
+            jdx++;
+        }
+        if (providers->Length() == 0)
+          mDependentIDsHash.Remove(id);
+      }
+    }
+
+    // If the relation attribute is given then we don't have anything else to
+    // check.
+    if (aRelAttr)
+      break;
+  }
+}
+
+void
 nsDocAccessible::FireValueChangeForTextFields(nsAccessible *aAccessible)
 {
   if (aAccessible->Role() != nsIAccessibleRole::ROLE_ENTRY)
     return;
 
   // Dependent value change event for text changes in textfields
   nsRefPtr<AccEvent> valueChangeEvent =
     new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible,
@@ -1718,30 +1980,26 @@ nsDocAccessible::UncacheChildrenInSubtre
   if (aRoot->IsPrimaryForNode() &&
       mNodeToAccessibleMap.Get(aRoot->GetNode()) == aRoot)
     mNodeToAccessibleMap.Remove(aRoot->GetNode());
 }
 
 void
 nsDocAccessible::ShutdownChildrenInSubtree(nsAccessible* aAccessible)
 {
-#ifdef DEBUG
-  nsAccessible* incache = mAccessibleCache.GetWeak(aAccessible->UniqueID());
-#endif
-
   // Traverse through children and shutdown them before this accessible. When
   // child gets shutdown then it removes itself from children array of its
   //parent. Use jdx index to process the cases if child is not attached to the
   // parent and as result doesn't remove itself from its children.
   PRUint32 count = aAccessible->GetCachedChildCount();
   for (PRUint32 idx = 0, jdx = 0; idx < count; idx++) {
     nsAccessible* child = aAccessible->GetCachedChildAt(jdx);
     if (!child->IsBoundToParent()) {
       NS_ERROR("Parent refers to a child, child doesn't refer to parent!");
       jdx++;
     }
 
     ShutdownChildrenInSubtree(child);
   }
 
-  ShutdownAccessible(aAccessible);
+  UnbindFromDocument(aAccessible);
 }
 
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -39,16 +39,18 @@
 #ifndef _nsDocAccessible_H_
 #define _nsDocAccessible_H_
 
 #include "nsIAccessibleDocument.h"
 
 #include "nsHyperTextAccessibleWrap.h"
 #include "nsEventShell.h"
 
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
 #include "nsIDocument.h"
 #include "nsIDocumentObserver.h"
 #include "nsIEditor.h"
 #include "nsIObserver.h"
 #include "nsIScrollPositionListener.h"
 #include "nsITimer.h"
 #include "nsIWeakReference.h"
 #include "nsCOMArray.h"
@@ -103,16 +105,17 @@ public:
   NS_DECL_NSIDOCUMENTOBSERVER
 
   // nsAccessNode
   virtual PRBool Init();
   virtual void Shutdown();
   virtual nsIFrame* GetFrame();
   virtual PRBool IsDefunct();
   virtual nsINode* GetNode() const { return mDocument; }
+  virtual nsIDocument* GetDocumentNode() const { return mDocument; }
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
   virtual nsresult GetARIAState(PRUint32 *aState, PRUint32 *aExtraState);
 
   virtual void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry);
 
@@ -130,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.
@@ -205,28 +211,38 @@ public:
 
   /**
    * Return the cached accessible by the given unique ID looking through
    * this and nested documents.
    */
   nsAccessible* GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID);
 
   /**
-   * Cache the accessible.
-   *
-   * @param  aAccessible  [in] accessible to cache
+   * Return true if the given ID is referred by relation attribute.
    *
-   * @return true if accessible being cached, otherwise false
+   * @note Different elements may share the same ID if they are hosted inside
+   *       XBL bindings. Be careful the result of this method may be  senseless
+   *       while it's called for XUL elements (where XBL is used widely).
    */
-  PRBool CacheAccessible(nsAccessible *aAccessible);
+  PRBool IsDependentID(const nsAString& aID) const
+    { return mDependentIDsHash.Get(aID, nsnull); }
 
   /**
-   * Shutdown the accessible and remove it from document cache.
+   * Initialize the newly created accessible and put it into document caches.
+   *
+   * @param  aAccessible    [in] created accessible
+   * @param  aRoleMapEntry  [in] the role map entry role the ARIA role or nsnull
+   *                          if none
    */
-  void ShutdownAccessible(nsAccessible *aAccessible);
+  bool BindToDocument(nsAccessible* aAccessible, nsRoleMapEntry* aRoleMapEntry);
+
+  /**
+   * Remove from document and shutdown the given accessible.
+   */
+  void UnbindFromDocument(nsAccessible* aAccessible);
 
   /**
    * Process the event when the queue of pending events is untwisted. Fire
    * accessible events as result of the processing.
    */
   void ProcessPendingEvent(AccEvent* aEvent);
 
   /**
@@ -235,18 +251,33 @@ public:
   void UpdateTree(nsIContent* aContainerNode, nsIContent* aStartChildNode,
                   nsIContent* aEndChildNode, PRBool aIsInsert);
 
   /**
    * Recreate an accessible, results in hide/show events pair.
    */
   void RecreateAccessible(nsINode* aNode);
 
+  /**
+   * Used to notify the document that the accessible caching is started or
+   * finished.
+   *
+   * While children are cached we may encounter the case there's no accessible
+   * 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
@@ -261,16 +292,38 @@ protected:
    * Remove the given document accessible from this document's child document
    * accessibles.
    */
   void RemoveChildDocument(nsDocAccessible* aChildDocument)
   {
     mChildDocuments.RemoveElement(aChildDocument);
   }
 
+  /**
+   * Add dependent IDs pointed by accessible element by relation attribute to
+   * cache. If the relation attribute is missed then all relation attributes
+   * are checked.
+   *
+   * @param aRelProvider [in] accessible that element has relation attribute
+   * @param aRelAttr     [in, optional] relation attribute
+   */
+  void AddDependentIDsFor(nsAccessible* aRelProvider,
+                          nsIAtom* aRelAttr = nsnull);
+
+  /**
+   * Remove dependent IDs pointed by accessible element by relation attribute
+   * from cache. If the relation attribute is absent then all relation
+   * attributes are checked.
+   *
+   * @param aRelProvider [in] accessible that element has relation attribute
+   * @param aRelAttr     [in, optional] relation attribute
+   */
+  void RemoveDependentIDsFor(nsAccessible* aRelProvider,
+                             nsIAtom* aRelAttr = nsnull);
+
     static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure);
 
     /**
      * Fires accessible events when attribute is changed.
      *
      * @param aContent - node that attribute is changed for
      * @param aNameSpaceID - namespace of changed attribute
      * @param aAttribute - changed attribute
@@ -294,24 +347,16 @@ protected:
      * @param aIsInserted  the flag pointed whether removed or inserted
      *                     characters should be cause of event
      */
     void FireTextChangeEventForText(nsIContent *aContent,
                                     CharacterDataChangeInfo* aInfo,
                                     PRBool aIsInserted);
 
   /**
-   * Used to define should the event be fired on a delay.
-   */
-  enum EEventFiringType {
-    eNormalEvent,
-    eDelayedEvent
-  };
-
-  /**
    * Fire a value change event for the the given accessible if it is a text
    * field (has a ROLE_ENTRY).
    */
   void FireValueChangeForTextFields(nsAccessible *aAccessible);
 
   /**
    * Helper for UpdateTree() method. Go down to DOM subtree and updates
    * accessible tree. Return one of these flags.
@@ -341,17 +386,18 @@ protected:
    *                      child/parent refs in
    */
   void ShutdownChildrenInSubtree(nsAccessible *aAccessible);
 
   /**
    * Cache of accessibles within this document accessible.
    */
   nsAccessibleHashtable mAccessibleCache;
-  NodeToAccessibleMap mNodeToAccessibleMap;
+  nsDataHashtable<nsPtrHashKey<const nsINode>, nsAccessible*>
+    mNodeToAccessibleMap;
 
     nsCOMPtr<nsIDocument> mDocument;
     nsCOMPtr<nsITimer> mScrollWatchTimer;
     PRUint16 mScrollPositionChangedTicks; // Used for tracking scroll events
 
 protected:
 
   nsRefPtr<nsAccEventQueue> mEventQueue;
@@ -360,14 +406,51 @@ protected:
    * Specifies if the document was loaded, used for error pages only.
    */
   PRPackedBool mIsLoaded;
 
     static PRUint32 gLastFocusedAccessiblesState;
     static nsIAtom *gLastFocusedFrameType;
 
   nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments;
+
+  /**
+   * A storage class for pairing content with one of its relation attributes.
+   */
+  class AttrRelProvider
+  {
+  public:
+    AttrRelProvider(nsIAtom* aRelAttr, nsIContent* aContent) :
+      mRelAttr(aRelAttr), mContent(aContent) { }
+
+    nsIAtom* mRelAttr;
+    nsCOMPtr<nsIContent> mContent;
+
+  private:
+    AttrRelProvider();
+    AttrRelProvider(const AttrRelProvider&);
+    AttrRelProvider& operator =(const AttrRelProvider&);
+  };
+
+  /**
+   * The cache of IDs pointed by relation attributes.
+   */
+  typedef nsTArray<nsAutoPtr<AttrRelProvider> > AttrRelProviderArray;
+  nsClassHashtable<nsStringHashKey, AttrRelProviderArray> mDependentIDsHash;
+
+  friend class RelatedAccIterator;
+
+  /**
+   * Used for our caching algorithm. We store the root of the tree that needs
+   * caching, the list of nodes that should be invalidated, and whether we are
+   * processing the invalidation list.
+   *
+   * @see NotifyOfCachingStart/NotifyOfCachingEnd
+   */
+  nsAccessible* mCacheRoot;
+  nsTArray<nsIContent*> mInvalidationList;
+  PRBool mIsPostCacheProcessing;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsDocAccessible,
                               NS_DOCACCESSIBLE_IMPL_CID)
 
-#endif  
+#endif
--- a/accessible/src/base/nsOuterDocAccessible.cpp
+++ b/accessible/src/base/nsOuterDocAccessible.cpp
@@ -191,18 +191,23 @@ nsOuterDocAccessible::InvalidateChildren
   // accessible is created and appended as a child when it's requested.
 
   mChildrenFlags = eChildrenUninitialized;
 }
 
 PRBool
 nsOuterDocAccessible::AppendChild(nsAccessible *aAccessible)
 {
-  NS_ASSERTION(!mChildren.Length(),
-               "Previous child document of outerdoc accessible wasn't removed!");
+  // We keep showing the old document for a bit after creating the new one,
+  // and while building the new DOM and frame tree. That's done on purpose
+  // to avoid weird flashes of default background color.
+  // The old viewer will be destroyed after the new one is created.
+  // For a11y, it should be safe to shut down the old document now.
+  if (mChildren.Length())
+    mChildren[0]->Shutdown();
 
   if (!nsAccessible::AppendChild(aAccessible))
     return PR_FALSE;
 
   NS_LOG_ACCDOCCREATE("append document to outerdoc",
                       aAccessible->GetDocumentNode())
   NS_LOG_ACCDOCCREATE_ACCADDRESS("outerdoc", this)
 
--- a/accessible/src/base/nsRelUtils.cpp
+++ b/accessible/src/base/nsRelUtils.cpp
@@ -129,73 +129,19 @@ nsRelUtils::AddTargetFromIDRefAttr(PRUin
   return AddTargetFromContent(aRelationType, aRelation, refContent);
 }
 
 nsresult
 nsRelUtils::AddTargetFromIDRefsAttr(PRUint32 aRelationType,
                                     nsIAccessibleRelation **aRelation,
                                     nsIContent *aContent, nsIAtom *aAttr)
 {
-  nsCOMPtr<nsIArray> refElms;
-  nsCoreUtils::GetElementsByIDRefsAttr(aContent, aAttr, getter_AddRefs(refElms));
-
-  if (!refElms)
-    return NS_OK_NO_RELATION_TARGET;
+  nsresult rv = NS_OK_NO_RELATION_TARGET;
 
-  PRUint32 count = 0;
-  nsresult rv = refElms->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(refElms, idx, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = AddTargetFromContent(aRelationType, aRelation, content);
+  nsIContent* refElm = nsnull;
+  IDRefsIterator iter(aContent, aAttr);
+  while ((refElm = iter.NextElem())) {
+    rv = AddTargetFromContent(aRelationType, aRelation, refElm);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  return NS_OK;
-}
-
-nsresult
-nsRelUtils::AddTargetFromNeighbour(PRUint32 aRelationType,
-                                   nsIAccessibleRelation **aRelation,
-                                   nsIContent *aContent,
-                                   nsIAtom *aNeighboutAttr,
-                                   nsIAtom *aNeighboutTagName)
-{
-  return AddTargetFromContent(
-    aRelationType, aRelation,
-    nsCoreUtils::FindNeighbourPointingToNode(aContent, aNeighboutAttr,
-                                             aNeighboutTagName));
+  return rv;
 }
-
-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
@@ -87,36 +87,24 @@ nsTextEquivUtils::GetTextEquivFromIDRefs
                                          nsAString& aTextEquiv)
 {
   aTextEquiv.Truncate();
 
   nsIContent* content = aAccessible->GetContent();
   if (!content)
     return NS_OK;
 
-  nsCOMPtr<nsIArray> refElms;
-  nsCoreUtils::GetElementsByIDRefsAttr(content, aIDRefsAttr,
-                                       getter_AddRefs(refElms));
-
-  if (!refElms)
-    return NS_OK;
-
-  PRUint32 count = 0;
-  nsresult rv = refElms->GetLength(&count);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIContent> refContent;
-  for (PRUint32 idx = 0; idx < count; idx++) {
-    refContent = do_QueryElementAt(refElms, idx, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
+  nsIContent* refContent = nsnull;
+  IDRefsIterator iter(content, aIDRefsAttr);
+  while ((refContent = iter.NextElem())) {
     if (!aTextEquiv.IsEmpty())
       aTextEquiv += ' ';
 
-    rv = AppendTextEquivFromContent(aAccessible, refContent, &aTextEquiv);
+    nsresult rv = AppendTextEquivFromContent(aAccessible, refContent,
+                                             &aTextEquiv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsTextEquivUtils::AppendTextEquivFromContent(nsAccessible *aInitiatorAcc,
@@ -232,21 +220,16 @@ nsTextEquivUtils::AppendFromAccessibleCh
 
   return rv;
 }
 
 nsresult
 nsTextEquivUtils::AppendFromAccessible(nsAccessible *aAccessible,
                                        nsAString *aString)
 {
-  // Ignore hidden accessible for name computation.
-  nsIFrame* frame = aAccessible->GetFrame();
-  if (!frame || !frame->GetStyleVisibility()->IsVisible())
-    return NS_OK;
-
   //XXX: is it necessary to care the accessible is not a document?
   if (aAccessible->IsContent()) {
     nsresult rv = AppendTextEquivFromTextContent(aAccessible->GetContent(),
                                                  aString);
     if (rv != NS_OK_NO_NAME_CLAUSE_HANDLED)
       return rv;
   }
 
@@ -428,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/src/html/nsHTMLFormControlAccessible.cpp
+++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp
@@ -407,26 +407,34 @@ nsresult
 nsHTMLTextFieldAccessible::GetNameInternal(nsAString& aName)
 {
   nsresult rv = nsAccessible::GetNameInternal(aName);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!aName.IsEmpty())
     return NS_OK;
 
-  if (!mContent->GetBindingParent())
+  if (mContent->GetBindingParent())
+  {
+    // XXX: bug 459640
+    // There's a binding parent.
+    // This means we're part of another control, so use parent accessible for name.
+    // This ensures that a textbox inside of a XUL widget gets
+    // an accessible name.
+    nsAccessible* parent = GetParent();
+    parent->GetName(aName);
+  }
+
+  if (!aName.IsEmpty())
     return NS_OK;
 
-  // XXX: bug 459640
-  // There's a binding parent.
-  // This means we're part of another control, so use parent accessible for name.
-  // This ensures that a textbox inside of a XUL widget gets
-  // an accessible name.
-  nsAccessible* parent = GetParent();
-  return parent ? parent->GetName(aName) : NS_OK;
+  // text inputs and textareas might have useful placeholder text
+  mContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::placeholder, aName);
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLTextFieldAccessible::GetValue(nsAString& _retval)
 {
   PRUint32 state;
   nsresult rv = GetStateInternal(&state, nsnull);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/accessible/src/html/nsHTMLImageMapAccessible.cpp
+++ b/accessible/src/html/nsHTMLImageMapAccessible.cpp
@@ -35,16 +35,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHTMLImageMapAccessible.h"
 
 #include "nsAccUtils.h"
+#include "nsDocAccessible.h"
 
 #include "nsIDOMHTMLCollection.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLAreaElement.h"
 #include "nsIFrame.h"
 #include "nsIImageFrame.h"
 #include "nsIImageMap.h"
@@ -109,41 +110,35 @@ nsHTMLImageMapAccessible::CacheChildren(
   if (!mMapElement)
     return;
 
   nsCOMPtr<nsIDOMHTMLCollection> mapAreas;
   mMapElement->GetAreas(getter_AddRefs(mapAreas));
   if (!mapAreas)
     return;
 
+  nsDocAccessible* document = GetDocAccessible();
+
   PRUint32 areaCount = 0;
   mapAreas->GetLength(&areaCount);
 
   for (PRUint32 areaIdx = 0; areaIdx < areaCount; areaIdx++) {
     nsCOMPtr<nsIDOMNode> areaNode;
     mapAreas->Item(areaIdx, getter_AddRefs(areaNode));
     if (!areaNode)
       return;
 
     nsCOMPtr<nsIContent> areaContent(do_QueryInterface(areaNode));
-    nsRefPtr<nsAccessible> areaAcc =
+    nsRefPtr<nsAccessible> area =
       new nsHTMLAreaAccessible(areaContent, mWeakShell);
-    if (!areaAcc)
-      return;
 
-    if (!areaAcc->Init()) {
-      areaAcc->Shutdown();
+    if (!document->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent)) ||
+        !AppendChild(area)) {
       return;
     }
-
-    // We must respect ARIA on area elements (for the canvas map technique)
-    areaAcc->SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(areaContent));
-
-    if (!AppendChild(areaAcc))
-      return;
   }
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLAreaAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -687,31 +687,27 @@ nsHTMLComboboxAccessible::CacheChildren(
 
   nsIFrame *listFrame = comboFrame->GetDropDown();
   if (!listFrame)
     return;
 
   if (!mListAccessible) {
     mListAccessible = 
       new nsHTMLComboboxListAccessible(mParent, mContent, mWeakShell);
-    if (!mListAccessible)
-      return;
 
     // Initialize and put into cache.
-    if (!mListAccessible->Init()) {
-      mListAccessible->Shutdown();
+    if (!GetDocAccessible()->BindToDocument(mListAccessible, nsnull))
       return;
-    }
   }
 
-  AppendChild(mListAccessible);
-
-  // Cache combobox option accessibles so that we build complete accessible tree
-  // for combobox.
-  mListAccessible->EnsureChildren();
+  if (AppendChild(mListAccessible)) {
+    // Cache combobox option accessibles so that we build complete accessible
+    // tree for combobox.
+    mListAccessible->EnsureChildren();
+  }
 }
 
 void
 nsHTMLComboboxAccessible::Shutdown()
 {
   nsAccessibleWrap::Shutdown();
 
   if (mListAccessible) {
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -303,44 +303,37 @@ nsHTMLTableCellAccessible::GetCellIndexe
   return cellLayout->GetCellIndexes(aRowIndex, aColIndex);
 }
 
 nsresult
 nsHTMLTableCellAccessible::GetHeaderCells(PRInt32 aRowOrColumnHeaderCell,
                                           nsIArray **aHeaderCells)
 {
   // Get header cells from @header attribute.
-  nsCOMPtr<nsIArray> headerCellElms;
-  nsCoreUtils::GetElementsByIDRefsAttr(mContent, nsAccessibilityAtoms::headers,
-                                       getter_AddRefs(headerCellElms));
-
-  if (headerCellElms) {
+  IDRefsIterator iter(mContent, nsAccessibilityAtoms::headers);
+  nsIContent* headerCellElm = iter.NextElem();
+  if (headerCellElm) {
     nsresult rv = NS_OK;
     nsCOMPtr<nsIMutableArray> headerCells =
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    PRUint32 count = 0;
-    rv = headerCellElms->GetLength(&count);
-    if (NS_SUCCEEDED(rv) && count > 0) {
-      nsCOMPtr<nsIContent> headerCellContent;
-      for (PRUint32 idx = 0; idx < count; idx++) {
-        headerCellContent = do_QueryElementAt(headerCellElms, idx, &rv);
-        nsAccessible *headerCell =
-          GetAccService()->GetAccessibleInWeakShell(headerCellContent, mWeakShell);
+    do {
+      nsAccessible* headerCell =
+        GetAccService()->GetAccessibleInWeakShell(headerCellElm, mWeakShell);
 
-        if (headerCell &&
-            (aRowOrColumnHeaderCell == nsAccUtils::eRowHeaderCells &&
-             headerCell->Role() == nsIAccessibleRole::ROLE_ROWHEADER ||
-             aRowOrColumnHeaderCell == nsAccUtils::eColumnHeaderCells &&
-             headerCell->Role() == nsIAccessibleRole::ROLE_COLUMNHEADER))
-          headerCells->AppendElement(static_cast<nsIAccessible*>(headerCell),
-                                     PR_FALSE);
+      if (headerCell &&
+          (aRowOrColumnHeaderCell == nsAccUtils::eRowHeaderCells &&
+              headerCell->Role() == nsIAccessibleRole::ROLE_ROWHEADER ||
+              aRowOrColumnHeaderCell == nsAccUtils::eColumnHeaderCells &&
+              headerCell->Role() == nsIAccessibleRole::ROLE_COLUMNHEADER)) {
+        headerCells->AppendElement(static_cast<nsIAccessible*>(headerCell),
+                                   PR_FALSE);
       }
-    }
+    } while ((headerCellElm = iter.NextElem()));
 
     NS_ADDREF(*aHeaderCells = headerCells);
     return NS_OK;
   }
 
   // Otherwise calculate header cells from hierarchy (see 11.4.3 "Algorithm to
   // find heading information" of w3c HTML 4.01).
   nsCOMPtr<nsIAccessibleTable> table = GetTableAccessible();
--- a/accessible/src/html/nsHTMLTextAccessible.cpp
+++ b/accessible/src/html/nsHTMLTextAccessible.cpp
@@ -254,18 +254,17 @@ nsHTMLOutputAccessible::GetAttributesInt
 
 nsHTMLLIAccessible::
   nsHTMLLIAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
   nsHyperTextAccessibleWrap(aContent, aShell)
 {
   nsBlockFrame* blockFrame = do_QueryFrame(GetFrame());
   if (blockFrame && !blockFrame->BulletIsEmptyExternal()) {
     mBulletAccessible = new nsHTMLListBulletAccessible(mContent, mWeakShell);
-    if (mBulletAccessible)
-      mBulletAccessible->Init();
+    GetDocAccessible()->BindToDocument(mBulletAccessible, nsnull);
   }
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLLIAccessible, nsHyperTextAccessible)
 
 void
 nsHTMLLIAccessible::Shutdown()
 {
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -129,28 +129,41 @@ nsresult nsHyperTextAccessible::QueryInt
 PRUint32
 nsHyperTextAccessible::NativeRole()
 {
   nsIAtom *tag = mContent->Tag();
 
   if (tag == nsAccessibilityAtoms::form)
     return nsIAccessibleRole::ROLE_FORM;
 
-  if (tag == nsAccessibilityAtoms::div ||
-      tag == nsAccessibilityAtoms::blockquote)
+  if (tag == nsAccessibilityAtoms::article ||
+      tag == nsAccessibilityAtoms::blockquote ||
+      tag == nsAccessibilityAtoms::div ||
+      tag == nsAccessibilityAtoms::nav)
     return nsIAccessibleRole::ROLE_SECTION;
 
   if (tag == nsAccessibilityAtoms::h1 ||
       tag == nsAccessibilityAtoms::h2 ||
       tag == nsAccessibilityAtoms::h3 ||
       tag == nsAccessibilityAtoms::h4 ||
       tag == nsAccessibilityAtoms::h5 ||
       tag == nsAccessibilityAtoms::h6)
     return nsIAccessibleRole::ROLE_HEADING;
 
+  // Deal with html landmark elements
+  if (tag == nsAccessibilityAtoms::header)
+    return nsIAccessibleRole::ROLE_HEADER;
+
+  if (tag == nsAccessibilityAtoms::footer)
+    return nsIAccessibleRole::ROLE_FOOTER;
+
+  if (tag == nsAccessibilityAtoms::aside)
+    return nsIAccessibleRole::ROLE_NOTE;
+
+  // Treat block frames as paragraphs
   nsIFrame *frame = GetFrame();
   if (frame && frame->GetType() == nsAccessibilityAtoms::blockFrame &&
       frame->GetContent()->Tag() != nsAccessibilityAtoms::input) {
     // An html:input @type="file" is the only input that is exposed as a
     // blockframe. It must be exposed as ROLE_TEXT_CONTAINER for JAWS.
     return nsIAccessibleRole::ROLE_PARAGRAPH;
   }
 
@@ -1192,16 +1205,34 @@ nsHyperTextAccessible::GetAttributesInte
     if (lineNumber >= 1) {
       nsAutoString strLineNumber;
       strLineNumber.AppendInt(lineNumber);
       nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::lineNumber,
                              strLineNumber);
     }
   }
 
+  // For the html landmark elements we expose them like we do aria landmarks to
+  // make AT navigation schemes "just work".
+  if (mContent->Tag() == nsAccessibilityAtoms::nav)
+    nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
+                           NS_LITERAL_STRING("navigation"));
+  else if (mContent->Tag() == nsAccessibilityAtoms::header) 
+    nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
+                           NS_LITERAL_STRING("banner"));
+  else if (mContent->Tag() == nsAccessibilityAtoms::footer) 
+    nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
+                           NS_LITERAL_STRING("contentinfo"));
+  else if (mContent->Tag() == nsAccessibilityAtoms::article) 
+    nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
+                           NS_LITERAL_STRING("main"));
+  else if (mContent->Tag() == nsAccessibilityAtoms::aside) 
+    nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles,
+                           NS_LITERAL_STRING("note"));
+
   return  NS_OK;
 }
 
 /*
  * Given an offset, the x, y, width, and height values are filled appropriately.
  */
 NS_IMETHODIMP nsHyperTextAccessible::GetCharacterExtents(PRInt32 aOffset, PRInt32 *aX, PRInt32 *aY,
                                                          PRInt32 *aWidth, PRInt32 *aHeight,
--- a/accessible/src/mac/nsRoleMap.h
+++ b/accessible/src/mac/nsRoleMap.h
@@ -160,10 +160,11 @@ static const NSString* AXRoles [] = {
   NSAccessibilityMenuItemRole,                  // ROLE_COMBOBOX_OPTION
   NSAccessibilityImageRole,                     // ROLE_IMAGE_MAP
   NSAccessibilityRowRole,                       // ROLE_OPTION
   NSAccessibilityRowRole,                       // ROLE_RICH_OPTION
   NSAccessibilityListRole,                      // ROLE_LISTBOX
   NSAccessibilityUnknownRole,                   // ROLE_FLAT_EQUATION
   NSAccessibilityGroupRole,                     // ROLE_GRID_CELL
   NSAccessibilityGroupRole,                     // ROLE_EMBEDDED_OBJECT
+  NSAccessibilityGroupRole,                     // ROLE_NOTE
   @"ROLE_LAST_ENTRY"                            // ROLE_LAST_ENTRY. bogus role that will never be shown (just marks the end of this array)!
 };
--- a/accessible/src/msaa/nsRoleMap.h
+++ b/accessible/src/msaa/nsRoleMap.h
@@ -438,12 +438,15 @@ static const WindowsRoleMapItem gWindows
   { ROLE_SYSTEM_EQUATION, ROLE_SYSTEM_EQUATION },
   
   // nsIAccessibleRole::ROLE_GRID_CELL
   { ROLE_SYSTEM_CELL, ROLE_SYSTEM_CELL },
 
   // nsIAccessibleRole::ROLE_EMBEDDED_OBJECT
   { USE_ROLE_STRING, IA2_ROLE_EMBEDDED_OBJECT },
 
+  // nsIAccessibleRole::ROLE_NOTE
+  { USE_ROLE_STRING, IA2_ROLE_NOTE },
+
   // nsIAccessibleRole::ROLE_LAST_ENTRY
   { ROLE_WINDOWS_LAST_ENTRY, ROLE_WINDOWS_LAST_ENTRY }
 };
 
--- a/accessible/src/xul/nsXULColorPickerAccessible.cpp
+++ b/accessible/src/xul/nsXULColorPickerAccessible.cpp
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXULColorPickerAccessible.h"
 
 #include "nsAccUtils.h"
 #include "nsAccTreeWalker.h"
 #include "nsCoreUtils.h"
+#include "nsDocAccessible.h"
 
 #include "nsIDOMElement.h"
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULColorPickerTileAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -166,10 +167,13 @@ nsXULColorPickerAccessible::CacheChildre
   while ((child = walker.GetNextChild())) {
     PRUint32 role = child->Role();
 
     // Get an accessbile for menupopup or panel elements.
     if (role == nsIAccessibleRole::ROLE_ALERT) {
       AppendChild(child);
       return;
     }
+
+    // Unbind rejected accessibles from the document.
+    GetDocAccessible()->UnbindFromDocument(child);
   }
 }
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -38,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXULFormControlAccessible.h"
 
 #include "nsAccessibilityAtoms.h"
 #include "nsAccUtils.h"
 #include "nsAccTreeWalker.h"
 #include "nsCoreUtils.h"
+#include "nsDocAccessible.h"
 #include "nsRelUtils.h"
 
 // NOTE: alphabetically ordered
 #include "nsHTMLFormControlAccessible.h"
 #include "nsXULMenuAccessible.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIDOMXULButtonElement.h"
@@ -217,16 +218,20 @@ nsXULButtonAccessible::CacheChildren()
       // Get an accessbile for menupopup or panel elements.
       menupopupAccessible.swap(child);
 
     } else if (isMenuButton && role == nsIAccessibleRole::ROLE_PUSHBUTTON) {
       // Button type="menu-button" contains a real button. Get an accessible
       // for it. Ignore dropmarker button what is placed as a last child.
       buttonAccessible.swap(child);
       break;
+
+    } else {
+      // Unbind rejected accessible from document.
+      GetDocAccessible()->UnbindFromDocument(child);
     }
   }
 
   if (!menupopupAccessible)
     return;
 
   AppendChild(menupopupAccessible);
   if (buttonAccessible)
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -467,61 +467,58 @@ nsXULTreeAccessible::GetTreeItemAccessib
     return nsnull;
 
   PRInt32 rowCount = 0;
   nsresult rv = mTreeView->GetRowCount(&rowCount);
   if (NS_FAILED(rv) || aRow >= rowCount)
     return nsnull;
 
   void *key = reinterpret_cast<void*>(aRow);
-  nsRefPtr<nsAccessible> accessible = mAccessibleCache.GetWeak(key);
-
-  if (!accessible) {
-    accessible = CreateTreeItemAccessible(aRow);
-    if (!accessible)
-      return nsnull;
+  nsAccessible* cachedTreeItem = mAccessibleCache.GetWeak(key);
+  if (cachedTreeItem)
+    return cachedTreeItem;
 
-    if (!accessible->Init()) {
-      accessible->Shutdown();
-      return nsnull;
+  nsRefPtr<nsAccessible> treeItem = CreateTreeItemAccessible(aRow);
+  if (treeItem) {
+    if (mAccessibleCache.Put(key, treeItem)) {
+      if (GetDocAccessible()->BindToDocument(treeItem, nsnull))
+        return treeItem;
+
+      mAccessibleCache.Remove(key);
     }
-
-    if (!mAccessibleCache.Put(key, accessible))
-      return nsnull;
   }
 
-  return accessible;
+  return nsnull;
 }
 
 void
 nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
 {
   if (IsDefunct())
     return;
 
   // Do not invalidate the cache if rows have been inserted.
   if (aCount > 0)
     return;
 
+  nsDocAccessible* document = GetDocAccessible();
+
   // Fire destroy event for removed tree items and delete them from caches.
   for (PRInt32 rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) {
 
     void* key = reinterpret_cast<void*>(rowIdx);
-    nsAccessible *accessible = mAccessibleCache.GetWeak(key);
+    nsAccessible* treeItem = mAccessibleCache.GetWeak(key);
 
-    if (accessible) {
+    if (treeItem) {
       nsRefPtr<AccEvent> event =
-        new AccEvent(nsIAccessibleEvent::EVENT_HIDE, accessible);
+        new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem);
       nsEventShell::FireEvent(event);
 
-      // Shutdown and remove accessible from document cache and tree cache.
-      nsDocAccessible *docAccessible = GetDocAccessible();
-      if (docAccessible)
-        docAccessible->ShutdownAccessible(accessible);
-
+      // Unbind from document, shutdown and remove from tree cache.
+      document->UnbindFromDocument(treeItem);
       mAccessibleCache.Remove(key);
     }
   }
 
   // We dealt with removed tree items already however we may keep tree items
   // having row indexes greater than row count. We should remove these dead tree
   // items silently from caches.
   PRInt32 newRowCount = 0;
@@ -529,24 +526,21 @@ nsXULTreeAccessible::InvalidateCache(PRI
   if (NS_FAILED(rv))
     return;
 
   PRInt32 oldRowCount = newRowCount - aCount;
 
   for (PRInt32 rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {
 
     void *key = reinterpret_cast<void*>(rowIdx);
-    nsAccessible *accessible = mAccessibleCache.GetWeak(key);
+    nsAccessible* treeItem = mAccessibleCache.GetWeak(key);
 
-    if (accessible) {
-      // Shutdown and remove accessible from document cache and tree cache.
-      nsDocAccessible *docAccessible = GetDocAccessible();
-      if (docAccessible)
-        docAccessible->ShutdownAccessible(accessible);
-
+    if (treeItem) {
+      // Unbind from document, shutdown and remove from tree cache.
+      document->UnbindFromDocument(treeItem);
       mAccessibleCache.Remove(key);
     }
   }
 }
 
 void
 nsXULTreeAccessible::TreeViewInvalidated(PRInt32 aStartRow, PRInt32 aEndRow,
                                          PRInt32 aStartCol, PRInt32 aEndCol)
--- a/accessible/src/xul/nsXULTreeGridAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeGridAccessible.cpp
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsXULTreeGridAccessibleWrap.h"
 
 #include "nsAccCache.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
+#include "nsDocAccessible.h"
 #include "nsEventShell.h"
 
 #include "nsITreeSelection.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeGridAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -724,35 +725,33 @@ nsXULTreeGridRowAccessible::GetChildCoun
 // nsXULTreeGridRowAccessible: nsXULTreeItemAccessibleBase implementation
 
 nsAccessible*
 nsXULTreeGridRowAccessible::GetCellAccessible(nsITreeColumn* aColumn)
 {
   NS_PRECONDITION(aColumn, "No tree column!");
 
   void* key = static_cast<void*>(aColumn);
-  nsRefPtr<nsAccessible> accessible = mAccessibleCache.GetWeak(key);
+  nsAccessible* cachedCell = mAccessibleCache.GetWeak(key);
+  if (cachedCell)
+    return cachedCell;
 
-  if (!accessible) {
-    accessible =
-      new nsXULTreeGridCellAccessibleWrap(mContent, mWeakShell, this, mTree,
-                                          mTreeView, mRow, aColumn);
-    if (!accessible)
-      return nsnull;
+  nsRefPtr<nsAccessible> cell =
+    new nsXULTreeGridCellAccessibleWrap(mContent, mWeakShell, this, mTree,
+                                        mTreeView, mRow, aColumn);
+  if (cell) {
+    if (mAccessibleCache.Put(key, cell)) {
+      if (GetDocAccessible()->BindToDocument(cell, nsnull))
+        return cell;
 
-    if (!accessible->Init()) {
-      accessible->Shutdown();
-      return nsnull;
+      mAccessibleCache.Remove(key);
     }
-
-    if (!mAccessibleCache.Put(key, accessible))
-      return nsnull;
   }
 
-  return accessible;
+  return nsnull;
 }
 
 void
 nsXULTreeGridRowAccessible::RowInvalidated(PRInt32 aStartColIdx,
                                            PRInt32 aEndColIdx)
 {
   nsCOMPtr<nsITreeColumns> treeColumns;
   mTree->GetColumns(getter_AddRefs(treeColumns));
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -42,20 +42,22 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible
 
 DIRS	= \
   actions \
   attributes \
   events \
   hyperlink \
+  name \
   relations \
   selectable \
   states \
   table \
+  text \
   tree \
   treeupdate \
   $(null)
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
@@ -66,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 \
@@ -90,39 +88,36 @@ include $(topsrcdir)/config/rules.mk
 		test_aria_roles.xul \
 		test_aria_token_attrs.html \
 		test_bug420863.html \
 	$(warning   test_childAtPoint.html temporarily disabled) \
 	$(warning	test_childAtPoint.xul temporarily disabled) \
 		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 \
 		test_role_nsHyperTextAcc.html \
 		test_text_caret.html \
 		test_textboxes.html \
 		test_textboxes.xul \
 		test_value.html \
 		test_value.xul \
 		testTextboxes.js \
+		text.js \
 		treeview.css \
 		treeview.js \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -315,37 +315,51 @@ function ensureAccessibleTree(aAccOrElmO
  *                                      fields
  */
 function testAccessibleTree(aAccOrElmOrID, aAccTree)
 {
   var acc = getAccessible(aAccOrElmOrID);
   if (!acc)
     return;
 
-  for (var prop in aAccTree) {
+  var accTree = aAccTree;
+
+  // Support of simplified accessible tree object.
+  var key = Object.keys(accTree)[0];
+  var roleName = "ROLE_" + key;
+  if (roleName in nsIAccessibleRole) {
+    accTree = {
+      role: nsIAccessibleRole[roleName],
+      children: accTree[key]
+    };
+  }
+
+  // Test accessible properties.
+  for (var prop in accTree) {
     var msg = "Wrong value of property '" + prop + "' for " + prettyName(acc) + ".";
     if (prop == "role") {
-      is(roleToString(acc[prop]), roleToString(aAccTree[prop]), msg);
+      is(roleToString(acc[prop]), roleToString(accTree[prop]), msg);
 
     } else if (prop == "states") {
-      var statesObj = aAccTree[prop];
+      var statesObj = accTree[prop];
       testStates(acc, statesObj.states, statesObj.extraStates,
                  statesObj.absentStates, statesObj.absentExtraStates);
 
     } else if (prop != "children") {
-      is(acc[prop], aAccTree[prop], msg);
+      is(acc[prop], accTree[prop], msg);
     }
   }
 
-  if ("children" in aAccTree && aAccTree["children"] instanceof Array) {
+  // Test children.
+  if ("children" in accTree && accTree["children"] instanceof Array) {
     var children = acc.children;
-    is(children.length, aAccTree.children.length,
+    is(children.length, accTree.children.length,
        "Different amount of expected children of " + prettyName(acc) + ".");
 
-    if (aAccTree.children.length == children.length) {
+    if (accTree.children.length == children.length) {
       var childCount = children.length;
 
       // nsIAccessible::firstChild
       var expectedFirstChild = childCount > 0 ?
         children.queryElementAt(0, nsIAccessible) : null;
       var firstChild = null;
       try { firstChild = acc.firstChild; } catch (e) {}
       is(firstChild, expectedFirstChild,
@@ -385,17 +399,17 @@ function testAccessibleTree(aAccOrElmOrI
         var expectedPrevSibling = (i > 0) ?
           children.queryElementAt(i - 1, nsIAccessible) : null;
         var prevSibling = null;
         try { prevSibling = child.previousSibling; } catch (e) {}
         is(prevSibling, expectedPrevSibling,
            "Wrong previous sibling of " + prettyName(child));
 
         // Go down through subtree
-        testAccessibleTree(child, aAccTree.children[i]);
+        testAccessibleTree(child, accTree.children[i]);
       }
     }
   }
 }
 
 /**
  * Return true if accessible for the given node is in cache.
  */
--- 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_mutation.html
+++ b/accessible/tests/mochitest/events/test_mutation.html
@@ -288,23 +288,36 @@
 
       // Show/hide events by changing of display style of accessible DOM node
       // from 'inline' to 'none', 'none' to 'inline'.
       var id = "link1";
       getAccessible(id); // ensure accessible is created
       gQueue.push(new changeStyle(id, "display", "none", kHideEvents));
       gQueue.push(new changeStyle(id, "display", "inline", kShowEvents));
 
+      // Show/hide events by changing of visibility style of accessible DOM node
+      // from 'visible' to 'hidden', 'hidden' to 'visible'.
+      var id = "link2";
+      getAccessible(id);
+      gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents));
+      gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
+
       // Show/hide events by changing of display style of accessible DOM node
       // from 'inline' to 'block', 'block' to 'inline'.
       var id = "link3";
       getAccessible(id); // ensure accessible is created
       gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents));
       gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents));
 
+      // Show/hide events by changing of visibility style of accessible DOM node
+      // from 'collapse' to 'visible', 'visible' to 'collapse'.
+      var id = "link4";
+      gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
+      gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents));
+
       // Show/hide events by adding new accessible DOM node and removing old one.
       var id = "link5";
       gQueue.push(new cloneAndAppendToDOM(id));
       gQueue.push(new removeFromDOM(id));
 
       // No show/hide events by adding new not accessible DOM node and removing
       // old one, no reorder event for their parent.
       var id = "child1";
@@ -338,16 +351,20 @@
       getAccessible("link6"); // ensure accessible is created
       gQueue.push(new cloneAndReplaceInDOM("link6"));
 
       // Show/hide events by changing class name on the parent node.
       gQueue.push(new changeClass("container2", "link7", "", kShowEvents));
       gQueue.push(new changeClass("container2", "link7", "displayNone",
                                   kHideEvents));
 
+      gQueue.push(new changeClass("container3", "link8", "", kShowEvents));
+      gQueue.push(new changeClass("container3", "link8", "visibilityHidden",
+                                  kHideEvents));
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
@@ -362,16 +379,21 @@
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662"
      title="no reorder event when html:link display property is changed from 'none' to 'inline'">
     Mozilla Bug 472662
   </a><br>
   <a target="_blank"
      title="Rework accessible tree update code"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
     Mozilla Bug 570275
+  </a><br>
+  <a target="_blank"
+     title="Develop a way to handle visibility style"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125">
+    Mozilla Bug 606125
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
   <div id="eventdump"></div>
 
--- a/accessible/tests/mochitest/events/test_statechange.html
+++ b/accessible/tests/mochitest/events/test_statechange.html
@@ -21,20 +21,16 @@
   <script type="application/javascript">
     ////////////////////////////////////////////////////////////////////////////
     // Invokers
 
     function makeEditableDoc(aDocNode, aIsEnabled)
     {
       this.DOMNode = aDocNode;
 
-      this.eventSeq = [
-        new invokerChecker(EVENT_STATE_CHANGE, getAccessible(this.DOMNode))
-      ];
-
       this.invoke = function editabledoc_invoke() {
         // Note: this should fire an EVENT_STATE_CHANGE
         this.DOMNode.designMode = 'on';
       };
 
       this.check = function editabledoc_check(aEvent) {
 
         testStates(aDocNode, 0, EXT_STATE_EDITABLE);
@@ -53,50 +49,79 @@
         ok(event.isEnabled(), "Expected editable state to be enabled");
       }
 
       this.getID = function editabledoc_getID() {
         return prettyName(aDocNode) + " editable state changed";
       };
     }
 
+    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.check = function invalidInput_check() {
+        testStates(aNodeOrID, STATE_INVALID);
+      };
+
+      this.getID = function invalidInput_getID() {
+        return prettyName(aNodeOrID) + " became invalid";
+      };
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests
 
     var gQueue = null;
 
     // var gA11yEventDumpID = "eventdump"; // debug stuff
 
     function doTests()
     {
-      gQueue = new eventQueue();
+      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.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=564471"
      title="Make state change events async">
     Mozilla Bug 564471
+  </a><br>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=555728"
+     title="Fire a11y event based on HTML5 constraint validation">
+    Mozilla Bug 555728
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="testContainer">
     <iframe id="iframe"></iframe>
   </div>
+
+  <input id="maxlength" maxlength="1">
+
   <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");
 
       //////////////////////////////////////////////////////////////////////////
@@ -171,16 +177,24 @@
       testName("checkbox", "Play the Haliluya sound when new mail arrives");
 
       testName("comboinend", "This day was sunny");
       testName("combo6", "This day was");
 
       testName("textboxinend", "This day was sunny");
       testName("textbox2", "This day was");
 
+      // placeholder
+      testName("ph_password", "a placeholder");
+      testName("ph_text", "a placeholder");
+      testName("ph_textarea", "a placeholder");
+      testName("ph_text2", "a label");
+      testName("ph_textarea2", "a label");
+      testName("ph_text3", "a label");
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
 </head>
@@ -191,16 +205,21 @@
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=444279"
      title="mochitest for accessible name calculating">
     Mozilla Bug 444279
   </a><br>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=530081"
      title="Clean up our tree walker ">
     Mozilla Bug 530081
+  </a><br>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=604391"
+     title="Use placeholder as name if name is otherwise empty">
+    Mozilla Bug 604391
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <!-- aria-label, simple label -->
   <span id="btn_simple_aria_label" role="button" aria-label="I am a button"/>
@@ -342,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.
@@ -416,10 +449,23 @@
       </select></label>
 
     <label id="textboxinend">
       This day was
       <input id="textbox2" value="sunny">
     </label>
   </form>
 
+  <!-- placeholder  -->
+  <input id="ph_password" type="password" value="" placeholder="a placeholder" />
+  <input id="ph_text" type="text" placeholder="a placeholder" />
+  <textarea id="ph_textarea" cols="5" placeholder="a placeholder"></textarea>  
+
+  <!-- placeholder does not win -->
+  <input id="ph_text2" type="text" aria-label="a label" placeholder="meh" />
+  <textarea id="ph_textarea2" cols="5" aria-labelledby="ph_text2" 
+            placeholder="meh"></textarea>
+
+  <label for="ph_text3">a label</label>
+  <input id="ph_text3" placeholder="meh" />
+
 </body>
 </html>
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/Makefile.in
+++ b/accessible/tests/mochitest/relations/Makefile.in
@@ -45,12 +45,13 @@ relativesrcdir  = accessible/relations
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		test_general.html \
 		test_general.xul \
 		test_tabbrowser.xul \
 		test_tree.xul \
+		test_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
--- 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"]);
@@ -60,16 +66,19 @@
       // direct accessible parent (fixed in bug 419770).
       var iframeElmObj = {};
       var iframeAcc = getAccessible("iframe", null, iframeElmObj);
       var iframeDoc = iframeElmObj.value.contentDocument;
       var iframeDocAcc = getAccessible(iframeDoc);
       testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
 
       // aria-controls
+      getAccessible("tab");
+      todo(false,
+           "Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that.");
       testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
       testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
 
       // aria-controls, multiple relations
       testRelation("lr1", RELATION_CONTROLLED_BY, "button");
       testRelation("lr2", RELATION_CONTROLLED_BY, "button");
       testRelation("button", RELATION_CONTROLLER_FOR, ["lr1", "lr2"]);
 
@@ -82,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
@@ -138,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>
@@ -199,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");
@@ -61,16 +73,19 @@
       // direct accessible parent (fixed in bug 419770).
       var iframeElmObj = {};
       var iframeAcc = getAccessible("iframe", null, iframeElmObj);
       var iframeDoc = iframeElmObj.value.contentDocument;
       var iframeDocAcc = getAccessible(iframeDoc);
       testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
 
       // aria-controls
+      getAccessible("tab");
+      todo(false,
+           "Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that.");
       testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
       testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
 
       // aria-controls, multiple relations
       testRelation("lr1", RELATION_CONTROLLED_BY, "button");
       testRelation("lr2", RELATION_CONTROLLED_BY, "button");
       testRelation("button", RELATION_CONTROLLER_FOR, ["lr1", "lr2"]);
 
@@ -137,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"/>
 
@@ -156,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>
 
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_update.html
@@ -0,0 +1,162 @@
+<html>
+
+<head>
+  <title>Test updating of accessible relations</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="../relations.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+    function testRelated(aRelAttr, aHostRelation, aDependentRelation)
+    {
+      // no attribute
+      testRelation("dependent1", aDependentRelation, null);
+      testRelation("dependent2", aDependentRelation, null);
+      if (aHostRelation)
+        testRelation("host", aHostRelation, null);
+
+      // set attribute
+      getNode("host").setAttribute(aRelAttr, "dependent1");
+      testRelation("dependent1", aDependentRelation, "host");
+      testRelation("dependent2", aDependentRelation, null);
+      if (aHostRelation)
+        testRelation("host", aHostRelation, "dependent1");
+
+      // change attribute
+      getNode("host").setAttribute(aRelAttr, "dependent2");
+      testRelation("dependent1", aDependentRelation, null);
+      testRelation("dependent2", aDependentRelation, "host");
+      if (aHostRelation)
+        testRelation("host", aHostRelation, "dependent2");
+
+      // remove attribute
+      getNode("host").removeAttribute(aRelAttr);
+      testRelation("dependent1", aDependentRelation, null);
+      testRelation("dependent2", aDependentRelation, null);
+      if (aHostRelation)
+        testRelation("host", aHostRelation, null);
+    }
+
+    function insertRelated(aHostRelAttr, aDependentID, aInsertHostFirst,
+                           aHostRelation, aDependentRelation)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, document)
+      ];
+
+      this.invoke = function insertRelated_invoke()
+      {
+        this.hostNode = document.createElement("div");
+        this.hostNode.setAttribute(aHostRelAttr, aDependentID);
+
+        this.dependentNode = document.createElement("div");
+        this.dependentNode.setAttribute("id", aDependentID);
+
+        if (aInsertHostFirst) {
+          document.body.appendChild(this.hostNode);
+          document.body.appendChild(this.dependentNode);
+        } else {
+          document.body.appendChild(this.dependentNode);
+          document.body.appendChild(this.hostNode);
+        }
+      }
+
+      this.finalCheck = function insertRelated_finalCheck()
+      {
+        testRelation(this.dependentNode, aDependentRelation, this.hostNode);
+        if (aHostRelation)
+          testRelation(this.hostNode, aHostRelation, this.dependentNode);
+      }
+
+      this.getID = function insertRelated_getID()
+      {
+        return "Insert " + aHostRelAttr + "='" + aDependentID + "' node" +
+          (aInsertHostFirst ? " before" : "after") + " dependent node";
+      }
+    }
+
+    var gQueue = null;
+
+    function doTest()
+    {
+      // Relation updates on ARIA attribute changes.
+      testRelated("aria-labelledby", RELATION_LABELLED_BY, RELATION_LABEL_FOR);
+      testRelated("aria-describedby",
+                  RELATION_DESCRIBED_BY, RELATION_DESCRIPTION_FOR);
+      testRelated("aria-owns", null, RELATION_NODE_CHILD_OF);
+      testRelated("aria-controls",
+                  RELATION_CONTROLLER_FOR, RELATION_CONTROLLED_BY);
+      testRelated("aria-flowto", RELATION_FLOWS_TO, RELATION_FLOWS_FROM);
+
+      // Insert related accessibles into tree.
+      gQueue = new eventQueue();
+      gQueue.push(new insertRelated("aria-labelledby", "dependent3", true,
+                                    RELATION_LABELLED_BY, RELATION_LABEL_FOR));
+      gQueue.push(new insertRelated("aria-labelledby", "dependent4", false,
+                                    RELATION_LABELLED_BY, RELATION_LABEL_FOR));
+
+      gQueue.push(new insertRelated("aria-describedby", "dependent5", true,
+                                    RELATION_DESCRIBED_BY,
+                                    RELATION_DESCRIPTION_FOR));
+      gQueue.push(new insertRelated("aria-describedby", "dependent6", false,
+                                    RELATION_DESCRIBED_BY,
+                                    RELATION_DESCRIPTION_FOR));
+
+      gQueue.push(new insertRelated("aria-owns", "dependent7", true,
+                                    null, RELATION_NODE_CHILD_OF));
+      gQueue.push(new insertRelated("aria-owns", "dependent8", false,
+                                    null, RELATION_NODE_CHILD_OF));
+
+      gQueue.push(new insertRelated("aria-controls", "dependent9", true,
+                                    RELATION_CONTROLLER_FOR,
+                                    RELATION_CONTROLLED_BY));
+      gQueue.push(new insertRelated("aria-controls", "dependent10", false,
+                                    RELATION_CONTROLLER_FOR,
+                                    RELATION_CONTROLLED_BY));
+
+      gQueue.push(new insertRelated("aria-flowto", "dependent11", true,
+                                    RELATION_FLOWS_TO, RELATION_FLOWS_FROM));
+      gQueue.push(new insertRelated("aria-flowto", "dependent12", false,
+                                    RELATION_FLOWS_TO, RELATION_FLOWS_FROM));
+
+      gQueue.invoke(); // will call SimpleTest.finish()
+
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=573469"
+     title="Cache relations defined by ARIA attributes">
+    Mozilla Bug 573469
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="dependent1">label</div>
+  <div id="dependent2">label2</div>
+  <div role="checkbox" id="host"></div>
+
+</body>
+</html>
--- a/accessible/tests/mochitest/role.js
+++ b/accessible/tests/mochitest/role.js
@@ -14,32 +14,35 @@ const ROLE_CHROME_WINDOW = nsIAccessible
 const ROLE_COMBOBOX = nsIAccessibleRole.ROLE_COMBOBOX;
 const ROLE_COMBOBOX_LIST = nsIAccessibleRole.ROLE_COMBOBOX_LIST;
 const ROLE_COMBOBOX_OPTION = nsIAccessibleRole.ROLE_COMBOBOX_OPTION;
 const ROLE_COLUMNHEADER = nsIAccessibleRole.ROLE_COLUMNHEADER;
 const ROLE_DIALOG = nsIAccessibleRole.ROLE_DIALOG;
 const ROLE_DOCUMENT = nsIAccessibleRole.ROLE_DOCUMENT;
 const ROLE_EMBEDDED_OBJECT = nsIAccessibleRole.ROLE_EMBEDDED_OBJECT;
 const ROLE_ENTRY = nsIAccessibleRole.ROLE_ENTRY;
+const ROLE_FOOTER = nsIAccessibleRole.ROLE_FOOTER;
 const ROLE_FLAT_EQUATION = nsIAccessibleRole.ROLE_FLAT_EQUATION;
 const ROLE_FORM = nsIAccessibleRole.ROLE_FORM;
 const ROLE_GRAPHIC = nsIAccessibleRole.ROLE_GRAPHIC;
 const ROLE_GRID_CELL = nsIAccessibleRole.ROLE_GRID_CELL;
 const ROLE_GROUPING = nsIAccessibleRole.ROLE_GROUPING;
+const ROLE_HEADER = nsIAccessibleRole.ROLE_HEADER;
 const ROLE_HEADING = nsIAccessibleRole.ROLE_HEADING;
 const ROLE_IMAGE_MAP = nsIAccessibleRole.ROLE_IMAGE_MAP;
 const ROLE_INTERNAL_FRAME = nsIAccessibleRole.ROLE_INTERNAL_FRAME;
 const ROLE_LABEL = nsIAccessibleRole.ROLE_LABEL;
 const ROLE_LINK = nsIAccessibleRole.ROLE_LINK;
 const ROLE_LIST = nsIAccessibleRole.ROLE_LIST;
 const ROLE_LISTBOX = nsIAccessibleRole.ROLE_LISTBOX;
 const ROLE_LISTITEM = nsIAccessibleRole.ROLE_LISTITEM;
 const ROLE_MENUITEM = nsIAccessibleRole.ROLE_MENUITEM;
 const ROLE_MENUPOPUP = nsIAccessibleRole.ROLE_MENUPOPUP;
 const ROLE_NOTHING = nsIAccessibleRole.ROLE_NOTHING;
+const ROLE_NOTE = nsIAccessibleRole.ROLE_NOTE;
 const ROLE_OPTION = nsIAccessibleRole.ROLE_OPTION;
 const ROLE_OUTLINE = nsIAccessibleRole.ROLE_OUTLINE;
 const ROLE_OUTLINEITEM = nsIAccessibleRole.ROLE_OUTLINEITEM;
 const ROLE_PAGETAB = nsIAccessibleRole.ROLE_PAGETAB;
 const ROLE_PAGETABLIST = nsIAccessibleRole.ROLE_PAGETABLIST;
 const ROLE_PANE = nsIAccessibleRole.ROLE_PANE;
 const ROLE_PARAGRAPH = nsIAccessibleRole.ROLE_PARAGRAPH;
 const ROLE_PARENT_MENUITEM = nsIAccessibleRole.ROLE_PARENT_MENUITEM;
--- a/accessible/tests/mochitest/states.js
+++ b/accessible/tests/mochitest/states.js
@@ -33,16 +33,17 @@ const STATE_UNAVAILABLE = nsIAccessibleS
 
 const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE;
 const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT;
 const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE;
 const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE;
 const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL;
 const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE;
 const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE;
+const EXT_STATE_STALE = nsIAccessibleStates.EXT_STATE_STALE;
 const EXT_STATE_SUPPORTS_AUTOCOMPLETION =
   nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION;
 const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL;
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Test functions
 
--- a/accessible/tests/mochitest/states/test_docarticle.html
+++ b/accessible/tests/mochitest/states/test_docarticle.html
@@ -12,27 +12,28 @@
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
 
   <script type="application/javascript">
     function doTest()
-    {      
+    {
       var docAcc = getAccessible(document, [nsIAccessibleDocument]);
       if (docAcc) {
         testStates(docAcc, STATE_READONLY);
         testStates("article", STATE_READONLY);
         testStates("editable_article", 0, EXT_STATE_EDITABLE);
 
         document.designMode = "on";
 
         testStates(docAcc, 0, EXT_STATE_EDITABLE);
         testStates("article", 0, EXT_STATE_EDITABLE);
+        testStates("article", 0, EXT_STATE_EDITABLE);
         testStates("editable_article", 0, EXT_STATE_EDITABLE);
   
         document.designMode = "off";
 
         testStates(docAcc, STATE_READONLY);
         testStates("article", STATE_READONLY);
         testStates("editable_article", 0, EXT_STATE_EDITABLE);
       }
--- a/accessible/tests/mochitest/test_aria_roles.html
+++ b/accessible/tests/mochitest/test_aria_roles.html
@@ -66,29 +66,36 @@ https://bugzilla.mozilla.org/show_bug.cg
                             "sectionhead"];
       for (a in abstract_roles)
         testRole(abstract_roles[a], ROLE_SECTION);
 
       // misc roles
       testRole("scrollbar", ROLE_SCROLLBAR);
       testRole("dir", ROLE_LIST);
 
+      // test document role map update
+      var testDoc = getAccessible(document, [nsIAccessibleDocument]);
+      testRole(testDoc, ROLE_DOCUMENT);
+      document.body.setAttribute("role", "application");
+      testRole(testDoc, ROLE_APPLICATION);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=481114">Mozilla Bug 481114</a>
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469688">Mozilla Bug 469688</a>
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469688">Mozilla Bug 520188</a>
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=529289">Mozilla Bug 529289</a>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=529289">Mozilla Bug 607219</a>
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
   
   <!-- "live" roles -->
   <table role="log" id="log_table">
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/test_elm_landmarks.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>HTML landmark 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"
+          src="role.js"></script>
+  <script type="application/javascript"
+          src="attributes.js"></script>
+
+  <script type="application/javascript">
+
+    function doTest()
+    {
+      testRole("nav", ROLE_SECTION);
+      testRole("header", ROLE_HEADER);
+      testRole("footer", ROLE_FOOTER);
+      testRole("article", ROLE_SECTION);
+      testRole("aside", ROLE_NOTE);
+
+      // Some AT may look for this
+      testAttrs("nav", {"xml-roles" : "navigation"}, true);
+      testAttrs("header", {"xml-roles" : "banner"}, true);
+      testAttrs("footer", {"xml-roles" : "contentinfo"}, true);
+      testAttrs("article", {"xml-roles" : "main"}, true);
+      testAttrs("aside", {"xml-roles" : "note"}, true);
+      testAttrs("document", {"xml-roles" : "document"}, true); // ARIA override
+
+      // And some AT may look for this
+      testAttrs("nav", {"tag" : "NAV"}, true);
+      testAttrs("header", {"tag" : "HEADER"}, true);
+      testAttrs("footer", {"tag" : "FOOTER"}, true);
+      testAttrs("article", {"tag" : "ARTICLE"}, true);
+      testAttrs("aside", {"tag" : "ASIDE"}, true);
+      testAttrs("document", {"tag" : "ARTICLE"}, true); // no override expected
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="Provide mappings for html5 <nav> <header> <footer> <article>"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=593368">Bug 593368</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <nav id="nav">a nav</nav>
+  <header id="header">a header</header>
+  <footer id="footer">a footer</footer>
+  <article id="article">an article</article>
+  <aside id="aside">by the way I am an aside</aside>
+
+  <article id="document" role="document">a document</article>
+
+</body>
+</html>
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>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/text.js
@@ -0,0 +1,180 @@
+////////////////////////////////////////////////////////////////////////////////
+// Public
+
+const BOUNDARY_CHAR = nsIAccessibleText.BOUNDARY_CHAR;
+const BOUNDARY_WORD_START = nsIAccessibleText.BOUNDARY_WORD_START;
+const BOUNDARY_WORD_END = nsIAccessibleText.BOUNDARY_WORD_END;
+const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START;
+const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END;
+const BOUNDARY_ATTRIBUTE_RANGE = nsIAccessibleText.BOUNDARY_ATTRIBUTE_RANGE;
+
+const kTodo = 1;
+const kOk = 2;
+
+function testText(aIDs, aStartOffset, aEndOffset, aText)
+{
+  for (var i = 0; i < aIDs.length; i++)
+  {
+    var acc = getAccessible(aIDs[i], nsIAccessibleText);
+    try {
+      is(acc.getText(aStartOffset, aEndOffset), aText,
+         "getText: wrong text between start and end offsets '" + aStartOffset +
+         "', '" + aEndOffset + " for '" + prettyName(aIDs[i]) + "'");
+    } catch (e) {
+      ok(false,
+         "getText fails between start and end offsets '" + aStartOffset +
+         "', '" + aEndOffset + " for '" + prettyName(aIDs[i]) + "'");
+    }
+  }
+}
+
+/**
+ * Test getTextAtOffset function over different elements
+ *
+ * @param aOffset         [in] the offset to get the text at
+ * @param aBoundaryType   [in] Boundary type for text to be retrieved
+ * @param aText           [in] expected return text for getTextAtOffset
+ * @param aStartOffset    [in] expected return start offset for getTextAtOffset
+ * @param aEndOffset      [in] expected return end offset for getTextAtOffset
+ * @param ...             [in] list of tuples made of:
+ *                              element identifier
+ *                              kTodo or kOk for returned text
+ *                              kTodo or kOk for returned start offset
+ *                              kTodo or kOk for returned offset result
+ *          
+ */
+function testTextAtOffset(aOffset, aBoundaryType, aText,
+                          aStartOffset, aEndOffset)
+{
+  for (var i = 5; i < arguments.length; i = i + 4) {
+    var ID = arguments[i];
+    var acc = getAccessible(ID, nsIAccessibleText);
+    var toDoFlag1 = arguments[i + 1];
+    var toDoFlag2 = arguments[i + 2];
+    var toDoFlag3 = arguments[i + 3];
+
+    testTextHelper(ID, aOffset, aBoundaryType,
+                   aText, aStartOffset, aEndOffset,
+                   toDoFlag1, toDoFlag2, toDoFlag3,
+                   acc.getTextAtOffset, "getTextAtOffset ");
+  }
+}
+
+/**
+ * Test getTextAfterOffset function over different elements
+ *
+ * @param aOffset         [in] the offset to get the text after
+ * @param aBoundaryType   [in] Boundary type for text to be retrieved
+ * @param aText           [in] expected return text for getTextAfterOffset
+ * @param aStartOffset    [in] expected return start offset for getTextAfterOffset
+ * @param aEndOffset      [in] expected return end offset for getTextAfterOffset
+ * @param ...             [in] list of tuples made of:
+ *                              element identifier
+ *                              kTodo or kOk for returned text
+ *                              kTodo or kOk for returned start offset
+ *                              kTodo or kOk for returned offset result
+ *          
+ */
+function testTextAfterOffset(aOffset, aBoundaryType,
+                             aText, aStartOffset, aEndOffset)
+{
+  for (var i = 5; i < arguments.length; i = i + 4) {
+    var ID = arguments[i];
+    var acc = getAccessible(ID, nsIAccessibleText);
+    var toDoFlag1 = arguments[i + 1];
+    var toDoFlag2 = arguments[i + 2];
+    var toDoFlag3 = arguments[i + 3];
+
+    testTextHelper(ID, aOffset, aBoundaryType,
+                   aText, aStartOffset, aEndOffset,
+                   toDoFlag1, toDoFlag2, toDoFlag3, 
+                   acc.getTextAfterOffset, "getTextAfterOffset ");
+  }
+}
+
+/**
+ * Test getTextBeforeOffset function over different elements
+ *
+ * @param aOffset         [in] the offset to get the text before
+ * @param aBoundaryType   [in] Boundary type for text to be retrieved
+ * @param aText           [in] expected return text for getTextBeforeOffset
+ * @param aStartOffset    [in] expected return start offset for getTextBeforeOffset
+ * @param aEndOffset      [in] expected return end offset for getTextBeforeOffset
+ * @param ...             [in] list of tuples made of:
+ *                              element identifier
+ *                              kTodo or kOk for returned text
+ *                              kTodo or kOk for returned start offset
+ *                              kTodo or kOk for returned offset result
+ *          
+ */
+function testTextBeforeOffset(aOffset, aBoundaryType,
+                              aText, aStartOffset, aEndOffset)
+{
+  for (var i = 5; i < arguments.length; i = i + 4) {
+    var ID = arguments[i];
+    var acc = getAccessible(ID, nsIAccessibleText);
+    var toDoFlag1 = arguments[i + 1];
+    var toDoFlag2 = arguments[i + 2];
+    var toDoFlag3 = arguments[i + 3];
+
+    testTextHelper(ID, aOffset, aBoundaryType,
+                   aText, aStartOffset, aEndOffset,
+                   toDoFlag1, toDoFlag2, toDoFlag3,
+                   acc.getTextBeforeOffset, "getTextBeforeOffset ");
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private
+
+function testTextHelper(aID, aOffset, aBoundaryType,
+                        aText, aStartOffset, aEndOffset,
+                        aToDoFlag1, aToDoFlag2, aToDoFlag3,
+                        aTextFunc, aTextFuncName)
+{
+  var exceptionFlag = aToDoFlag1 == undefined ||
+                      aToDoFlag2 == undefined ||
+                      aToDoFlag3 == undefined;
+  try {
+    var startOffsetObj = {}, endOffsetObj = {};
+    var text = aTextFunc(aOffset, aBoundaryType,
+                         startOffsetObj, endOffsetObj);
+    
+    var isFunc1 = (aToDoFlag1 == kTodo) ? todo_is : is;
+    var isFunc2 = (aToDoFlag2 == kTodo) ? todo_is : is;
+    var isFunc3 = (aToDoFlag3 == kTodo) ? todo_is : is;
+
+    var startMsg = aTextFuncName + "(" + boundaryToString(aBoundaryType) + "): ";
+
+    var endMsg = ", id: '" + prettyName(aID) + "';";
+    
+    isFunc1(text, aText,
+            startMsg + "wrong text, offset: " + aOffset + endMsg);
+    isFunc2(startOffsetObj.value, aStartOffset,
+            startMsg + "wrong start offset, offset: " + aOffset + endMsg);
+    isFunc3(endOffsetObj.value, aEndOffset,
+            startMsg + "wrong end offset, offset: " + aOffset + endMsg);
+    
+  } catch (e) {
+    var okFunc = exceptionFlag ? todo : ok;
+    okFunc(false, startMsg + "failed at offset " + aOffset + endMsg);
+  }
+}
+
+function boundaryToString(aBoundaryType)
+{
+  switch (aBoundaryType) {
+    case BOUNDARY_CHAR:
+      return "char";
+    case BOUNDARY_WORD_START:
+      return "word start";
+    case BOUNDARY_WORD_END:
+      return "word end";
+    case BOUNDARY_LINE_START:
+      return "line start";
+    case BOUNDARY_LINE_END:
+      return "line end";
+    case BOUNDARY_ATTRIBUTE_RANGE:
+      return "attr range";
+  }
+}
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/text/Makefile.in
@@ -0,0 +1,56 @@
+#
+# ***** 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):
+#   Fernando Herrera <fherrera@onirica.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/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_singleline.html
@@ -0,0 +1,574 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>nsIAccessibleText getText related function tests for html:input,html:div and html:textarea</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()
+    {
+      var IDs = ["input", "div", "textarea"];
+  
+      // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
+      //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+
+      ////////////////////////////////////////////////////////////////////////
+      // getText
+
+      testText(IDs, 0, 1, "h");
+      testText(IDs, 1, 3, "el");
+      testText(IDs, 14, 15, "d");
+      testText(IDs, 0, 15, "hello my friend");
+
+      ////////////////////////////////////////////////////////////////////////
+      // getTextAfterOffset
+
+      // BOUNDARY_CHAR
+      testTextAfterOffset(0, BOUNDARY_CHAR, "e", 1, 2,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(1, BOUNDARY_CHAR, "l", 2, 3,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(14, BOUNDARY_CHAR, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(15, BOUNDARY_CHAR, "", 15, 15,
+			  "input", kOk, kTodo, kTodo,
+			  "div", kOk, kTodo, kTodo,
+			  "editable", kOk, kTodo, kTodo,
+			  "textarea", kTodo, kOk, kTodo);
+
+      // BOUNDARY_WORD_START
+      testTextAfterOffset(0, BOUNDARY_WORD_START, "my ", 6, 9,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(1, BOUNDARY_WORD_START, "my ", 6, 9,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(5, BOUNDARY_WORD_START, "my ", 6, 9,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(6, BOUNDARY_WORD_START, "friend", 9, 15,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(7, BOUNDARY_WORD_START, "friend", 9, 15,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(8, BOUNDARY_WORD_START, "friend", 9, 15,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(9, BOUNDARY_WORD_START, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(11, BOUNDARY_WORD_START, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(14, BOUNDARY_WORD_START, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(15, BOUNDARY_WORD_START, "", 15, 15,
+                          "input", kOk, kTodo, kTodo,
+                          "div", kOk, kTodo, kTodo,
+                          "editable", kOk, kTodo, kTodo,
+                          "textarea", kTodo, kOk, kTodo);
+
+      // BOUNDARY_WORD_END
+      testTextAfterOffset(0, BOUNDARY_WORD_END, " my", 5, 8,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(1, BOUNDARY_WORD_END, " my", 5, 8,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(5, BOUNDARY_WORD_END, " my", 5, 8,
+                          "input", kOk, kOk, kOk,
+                          "div", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(6, BOUNDARY_WORD_END, " friend", 8, 15,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(7, BOUNDARY_WORD_END, " friend", 8, 15,
+                          "input", kTodo, kTodo, kTodo,
+                          "div", kTodo, kTodo, kTodo,
+                          "editable", kTodo, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(8, BOUNDARY_WORD_END, " friend", 8, 15,
+                          "input", kOk, kOk, kOk,
+                          "div", kOk, kOk, kOk,
+                          "editable", kOk, kOk, kOk,
+                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(9, BOUNDARY_WORD_END, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(11, BOUNDARY_WORD_END, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(14, BOUNDARY_WORD_END, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(15, BOUNDARY_WORD_END, "", 15, 15,
+                          "input", kOk, kTodo, kTodo,
+                          "div", kOk, kTodo, kTodo,
+                          "editable", kOk, kTodo, kTodo,
+                          "textarea", kTodo, kOk, kTodo);
+
+      // BOUNDARY_LINE_START
+      testTextAfterOffset(0, BOUNDARY_LINE_START, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(1, BOUNDARY_LINE_START, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(14, BOUNDARY_LINE_START, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kTodo);
+      testTextAfterOffset(15, BOUNDARY_LINE_START, "", 15, 15,
+                          "input", undefined, undefined, undefined,
+                          "div", undefined, undefined, undefined,
+                          "editable", undefined, undefined, undefined,
+                          "textarea", kTodo, undefined, kTodo);
+
+      // BOUNDARY_LINE_END
+      testTextAfterOffset(0, BOUNDARY_LINE_END, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(1, BOUNDARY_LINE_END, "", 15, 15,
+                          "input", kTodo, kTodo, kOk,
+                          "div", kTodo, kTodo, kOk,
+                          "editable", kTodo, kTodo, kOk,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(14, BOUNDARY_LINE_END, "", 15, 15,
+                          "input", kOk, kTodo, kTodo,
+                          "div", kOk, kTodo, kTodo,
+                          "editable", kOk, kTodo, kTodo,
+                          "textarea", kTodo, kTodo, kOk);
+      testTextAfterOffset(15, BOUNDARY_LINE_END, "", 15, 15,
+                          "input", kOk, kTodo, kTodo,
+                          "div", kOk, kTodo, kTodo,
+                          "editable", kOk, kTodo, kTodo,
+                          "textarea", kOk, kTodo, 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, "h", 0, 1,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(14, BOUNDARY_CHAR, "n", 13, 14,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(15, BOUNDARY_CHAR, "d", 14, 15,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, 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, "hello ", 0, 6,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(7, BOUNDARY_WORD_START, "hello ", 0, 6,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(8, BOUNDARY_WORD_START, "hello ", 0, 6,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(9, BOUNDARY_WORD_START, "my ", 6, 9,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(10, BOUNDARY_WORD_START, "my ", 6, 9,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(14, BOUNDARY_WORD_START, "my ", 6, 9,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(15, BOUNDARY_WORD_START, "my ", 6, 9,
+                           "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(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, "hello ", 0, 6,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(7, BOUNDARY_WORD_END, "hello ", 0, 6,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(8, BOUNDARY_WORD_END, "hello ", 0, 6,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(9, BOUNDARY_WORD_END, " my", 5, 8,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(10, BOUNDARY_WORD_END, " my", 5, 8,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(14, BOUNDARY_WORD_END, " my", 5, 8,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+      testTextBeforeOffset(15, BOUNDARY_WORD_END, " my", 5, 8,
+                           "input", kTodo, kTodo, kTodo,
+                           "div", kTodo, kTodo, kTodo,
+                           "editable", kTodo, kTodo, kTodo,
+                           "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_LINE_START
+      testTextBeforeOffset(0, BOUNDARY_LINE_START, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(1, BOUNDARY_LINE_START, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(14, BOUNDARY_LINE_START, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(15, BOUNDARY_LINE_START, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+
+      // BOUNDARY_LINE_END
+      testTextBeforeOffset(0, BOUNDARY_LINE_END, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(1, BOUNDARY_LINE_END, "", 0, 0,
+                           "input", kTodo, kOk, kTodo,
+                           "div", kTodo, kOk, kTodo,
+                           "editable", kTodo, kOk, kTodo,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(14, BOUNDARY_LINE_END, "", 0, 0,
+                           "input", kOk, kOk, kOk,
+                           "div", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "textarea", kTodo, kOk, kTodo);
+      testTextBeforeOffset(15, BOUNDARY_LINE_END, "", 0, 0,
+                           "input", kOk, kOk, kOk,
+                           "div", kOk, kOk, kOk,
+                           "editable", kOk, kOk, kOk,
+                           "textarea", kOk, kOk, kOk);
+
+      ////////////////////////////////////////////////////////////////////////
+      // getTextAtOffset
+
+      // BOUNDARY_CHAR
+      testTextAtOffset(0, BOUNDARY_CHAR, "h", 0, 1,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(1, BOUNDARY_CHAR, "e", 1, 2,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(14, BOUNDARY_CHAR, "d", 14, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(15, BOUNDARY_CHAR, "", 15, 15,
+                        "input", kOk, kTodo, kTodo,
+                        "div", kOk, kTodo, kTodo,
+                        "editable", kOk, kTodo, kTodo,
+                        "textarea", kTodo, kOk, kTodo);
+
+      // BOUNDARY_WORD_START
+      testTextAtOffset(0, BOUNDARY_WORD_START, "hello ", 0, 6,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(1, BOUNDARY_WORD_START, "hello ", 0, 6,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(5, BOUNDARY_WORD_START, "hello ", 0, 6,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(6, BOUNDARY_WORD_START, "my ", 6, 9,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(7, BOUNDARY_WORD_START, "my ", 6, 9,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(8, BOUNDARY_WORD_START, "my ", 6, 9,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(9, BOUNDARY_WORD_START, "friend", 9, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(10, BOUNDARY_WORD_START, "friend", 9, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(14, BOUNDARY_WORD_START, "friend", 9, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(15, BOUNDARY_WORD_START, "friend", 9, 15,
+                       "input", kTodo, kTodo, kTodo,
+                       "div", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_WORD_END
+      testTextAtOffset(0, BOUNDARY_WORD_END, "hello", 0, 5,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(1, BOUNDARY_WORD_END, "hello", 0, 5,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(5, BOUNDARY_WORD_END, "hello", 0, 5,
+                       "input", kTodo, kTodo, kTodo,
+                       "div", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(6, BOUNDARY_WORD_END, " my", 5, 8,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(7, BOUNDARY_WORD_END, " my", 5, 8,
+                        "input", kOk, kOk, kOk,
+                        "div", kOk, kOk, kOk,
+                        "editable", kOk, kOk, kOk,
+                        "textarea", kOk, kOk, kOk);
+      testTextAtOffset(8, BOUNDARY_WORD_END, " my", 5, 8,
+                       "input", kTodo, kTodo, kTodo,
+                       "div", kTodo, kTodo, kTodo,
+                       "editable", kTodo, kTodo, kTodo,
+                       "textarea", kTodo, kTodo, kTodo);
+      testTextAtOffset(9, BOUNDARY_WORD_END, " friend", 8, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(10, BOUNDARY_WORD_END, " friend", 8, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(14, BOUNDARY_WORD_END, " friend", 8, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(15, BOUNDARY_WORD_END, " friend", 8, 15,
+                        "input", kTodo, kTodo, kTodo,
+                        "div", kTodo, kTodo, kTodo,
+                        "editable", kTodo, kTodo, kTodo,
+                        "textarea", kTodo, kTodo, kTodo);
+
+      // BOUNDARY_LINE_START
+      testTextAtOffset(0, BOUNDARY_LINE_START, "hello my friend", 0, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(1, BOUNDARY_LINE_START, "hello my friend", 0, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+                       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(14, BOUNDARY_LINE_START, "hello my friend", 0, 15,
+                       "input", kOk, kOk, kOk,
+                       "div", kOk, kOk, kOk,
+                       "editable", kOk, kOk, kOk,
+			       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(15, BOUNDARY_LINE_START, "hello my friend", 0, 15,
+		       "input", kOk, kOk, kOk,
+		       "div", kOk, kOk, kOk,
+		       "editable", kOk, kOk, kOk,
+		       "textarea", kTodo, kOk, kTodo);
+
+      // BOUNDARY_LINE_END
+      testTextAtOffset(0, BOUNDARY_LINE_END, "hello my friend", 0, 15,
+		       "input", kOk, kOk, kOk,
+		       "div", kOk, kOk, kOk,
+		       "editable", kOk, kOk, kOk,
+		       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(1, BOUNDARY_LINE_END, "hello my friend", 0, 15,
+		       "input", kOk, kOk, kOk,
+		       "div", kOk, kOk, kOk,
+		       "editable", kOk, kOk, kOk,
+		       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(14, BOUNDARY_LINE_END, "hello my friend", 0, 15,
+		       "input", kTodo, kOk, kTodo,
+		       "div", kTodo, kOk, kTodo,
+		       "editable", kTodo, kOk, kTodo,
+		       "textarea", kOk, kOk, kOk);
+      testTextAtOffset(15, BOUNDARY_LINE_END, "hello my friend", 0, 15,
+		       "input", kTodo, kOk, kTodo,
+		       "div", kTodo, kOk, kTodo,
+		       "editable", kTodo, kOk, kTodo,
+		       "textarea", kTodo, kOk, kTodo);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="nsIAccessibleText getText related function tests for html:input,html:div and html:textarea"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=452769">Mozilla Bug 452769</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <input id="input" value="hello my friend"/>
+  <div id="div">hello my friend</div>
+  <div id="editable" contenteditable="true">hello my friend</div>
+  <textarea id="textarea">hello my friend</textarea>
+
+</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/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -66,19 +66,16 @@
     }
 
     function testAccTree()
     {
       var tabsAccTree = {
         role: ROLE_PAGETABLIST,
         children: [
           {
-            role: ROLE_PUSHBUTTON // tab scroll up button
-          },
-          {
             role: ROLE_PAGETAB,
             children: [
               {
                 role: ROLE_PUSHBUTTON
               }
             ]
           },
           {
@@ -86,19 +83,16 @@
             children: [
               {
                 role: ROLE_PUSHBUTTON
               }
             ]
           },
           {
             role: ROLE_PUSHBUTTON
-          },
-          {
-            role: ROLE_PUSHBUTTON // tab scroll down button
           }
         ]
       };
       testAccessibleTree(getNode("tabbrowser").tabContainer, tabsAccTree);
 
       var tabboxAccTree = {
         role: ROLE_PANE,
         children: [
--- a/accessible/tests/mochitest/treeupdate/Makefile.in
+++ b/accessible/tests/mochitest/treeupdate/Makefile.in
@@ -41,16 +41,19 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/treeupdate
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
+		test_ariadialog.html \
+		test_doc.html \
 		test_list_editabledoc.html \
 		test_list.html \
 		test_recreation.html \
-		test_tableinsubtree.html \
+		test_textleaf.html \
+		test_visibility.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
rename from accessible/tests/mochitest/treeupdate/test_tableinsubtree.html
rename to accessible/tests/mochitest/treeupdate/test_ariadialog.html
--- a/accessible/tests/mochitest/treeupdate/test_tableinsubtree.html
+++ b/accessible/tests/mochitest/treeupdate/test_ariadialog.html
@@ -29,44 +29,34 @@
       this.node = getNode(aID);
 
       this.eventSeq = [
         new invokerChecker(EVENT_SHOW, this.node)
       ];
 
       this.invoke = function showARIADialog_invoke()
       {
-        this.node.style.display = "block";
+        getNode("dialog").style.display = "block";
+        getNode("table").style.visibility = "visible";
+        getNode("a").textContent = "link";
         getNode("input").value = "hello";
-        getNode("cell").textContent = "cell1";
         getNode("input").focus();
       }
 
       this.finalCheck = function showARIADialog_finalCheck()
       {
         var tree = {
           role: ROLE_DIALOG,
           children: [
             {
-              role: ROLE_TABLE,
-              children: [
-                {
-                  role: ROLE_ROW,
-                  children: [
-                    {
-                      role: ROLE_CELL,
-                      children: [ { role: ROLE_TEXT_LEAF } ]
-                    },
-                    {
-                      role: ROLE_CELL,
-                      children: [ { role: ROLE_ENTRY } ]
-                    }
-                  ]
-                }
-              ]
+              role: ROLE_PUSHBUTTON,
+              children: [ { role: ROLE_TEXT_LEAF } ]
+            },
+            {
+              role: ROLE_ENTRY
             }
           ]
         };
         testAccessibleTree(aID, tree);
       }
 
       this.getID = function showARIADialog_getID()
       {
@@ -105,16 +95,26 @@
   </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="dialog" role="dialog" style="display: none;">
-    <table>
-      <tr><td id="cell"></td><td><input id="input"></td>
+    <table id="table" role="presentation"
+           style="display: block; position: fixed; top: 88px; left: 312.5px; z-index: 10010; visibility: hidden;">
+      <tbody>
+        <tr>
+          <td role="presentation">
+            <div role="presentation">
+              <a id="a" role="button">text</a>
+            </div>
+            <input id="input">
+          </td>
+        </tr>
+      </tbody>
     </table>
   </div>
 
   <div id="eventdump"></div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_doc.html
@@ -0,0 +1,409 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Test document root content mutations</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"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Helpers
+
+    function getDocNode(aID)
+    {
+      return getNode(aID).contentDocument;
+    }
+    function getDocChildNode(aID)
+    {
+      return getDocNode(aID).body.firstChild;
+    }
+
+    function rootContentReplaced(aID, aTextName)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_SHOW, getDocChildNode, aID),
+        new invokerChecker(EVENT_REORDER, getDocNode, aID)
+      ];
+
+      this.finalCheck = function rootContentReplaced_finalCheck()
+      {
+        var tree = {
+          role: ROLE_DOCUMENT,
+          children: [
+            {
+              role: ROLE_TEXT_LEAF,
+              name: aTextName
+            }
+          ]
+        };
+        testAccessibleTree(getDocNode(aID), tree);
+      }
+    }
+
+    function rootContentRemoved(aID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, null),
+        new invokerChecker(EVENT_REORDER, getDocNode, aID)
+      ];
+
+      this.preinvoke = function rootContentRemoved_preinvoke()
+      {
+        // Set up target for hide event before we invoke.
+        var text = getAccessible(getAccessible(getDocNode(aID)).firstChild,
+                                               [nsIAccessNode]).DOMNode;
+        this.eventSeq[0].target = text;
+      }
+
+      this.finalCheck = function rootContentRemoved_finalCheck()
+      {
+        var tree = {
+          role: ROLE_DOCUMENT,
+          states: {
+            // Out of date root content involves stale state presence.
+            states: 0,
+            extraStates: EXT_STATE_STALE
+          },
+          children: [ ]
+        };
+        testAccessibleTree(getDocNode(aID), tree);
+      }
+    }
+
+    function rootContentInserted(aID, aTextName)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_SHOW, getDocChildNode, aID),
+        new invokerChecker(EVENT_REORDER, getDocNode, aID)
+      ];
+
+      this.finalCheck = function rootContentInserted_finalCheck()
+      {
+        var tree = {
+          role: ROLE_DOCUMENT,
+          states: {
+            states: 0,
+            extraStates: 0,
+            absentStates: 0,
+            absentExtraStates: EXT_STATE_STALE
+          },
+          children: [
+            {
+              role: ROLE_TEXT_LEAF,
+              name: aTextName
+            }
+          ]
+        };
+        testAccessibleTree(getDocNode(aID), tree);
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function writeIFrameDoc(aID)
+    {
+      this.__proto__ = new rootContentReplaced(aID, "hello");
+
+      this.invoke = function writeIFrameDoc_invoke()
+      {
+        var docNode = getDocNode(aID);
+
+        // We can't use open/write/close outside of iframe document because of
+        // security error.
+        var script = docNode.createElement("script");
+        script.textContent = "document.open(); document.write('hello'); document.close();";
+        docNode.body.appendChild(script);
+      }
+
+      this.getID = function writeIFrameDoc_getID()
+      {
+        return "write document";
+      }
+    }
+
+    /**
+     * Replace HTML element.
+     */
+    function replaceIFrameHTMLElm(aID)
+    {
+      this.__proto__ = new rootContentReplaced(aID, "New Wave");
+
+      this.invoke = function replaceIFrameHTMLElm_invoke()
+      {
+        var docNode = getDocNode(aID);
+        var newHTMLNode = docNode.createElement("html");
+        var newBodyNode = docNode.createElement("body");
+        var newTextNode = docNode.createTextNode("New Wave");
+        newBodyNode.appendChild(newTextNode);
+        newHTMLNode.appendChild(newBodyNode);
+        docNode.replaceChild(newHTMLNode, docNode.documentElement);
+      }
+
+      this.getID = function replaceIFrameBody_getID()
+      {
+        return "replace HTML element";
+      }
+    }
+
+    /**
+     * Replace HTML body.
+     */
+    function replaceIFrameBody(aID)
+    {
+      this.__proto__ = new rootContentReplaced(aID, "New Hello");
+
+      this.invoke = function replaceIFrameBody_invoke()
+      {
+        var docNode = getDocNode(aID);
+        var newBodyNode = docNode.createElement("body");
+        var newTextNode = docNode.createTextNode("New Hello");
+        newBodyNode.appendChild(newTextNode);
+        docNode.documentElement.replaceChild(newBodyNode, docNode.body);
+      }
+
+      this.finalCheck = function replaceIFrameBody_finalCheck()
+      {
+        var tree = {
+          role: ROLE_DOCUMENT,
+          children: [
+            {
+              role: ROLE_TEXT_LEAF,
+              name: "New Hello"
+            }
+          ]
+        };
+        testAccessibleTree(getDocNode(aID), tree);
+      }
+
+      this.getID = function replaceIFrameBody_getID()
+      {
+        return "replace body";
+      }
+    }
+
+    /**
+     * Open/close document pair.
+     */
+    function openIFrameDoc(aID)
+    {
+      this.__proto__ = new rootContentRemoved(aID);
+
+      this.invoke = function openIFrameDoc_invoke()
+      {
+        this.preinvoke();
+
+        // Open document.
+        var docNode = getDocNode(aID);
+        var script = docNode.createElement("script");
+        script.textContent = "function closeMe() { document.write('Works?'); document.close(); } window.closeMe = closeMe; document.open();";
+        docNode.body.appendChild(script);
+      }
+
+      this.getID = function openIFrameDoc_getID()
+      {
+        return "open document";
+      }
+    }
+
+    function closeIFrameDoc(aID)
+    {
+      this.__proto__ = new rootContentInserted(aID, "Works?");
+
+      this.invoke = function closeIFrameDoc_invoke()
+      {
+        // Write and close document.
+        getDocNode(aID).write('Works?'); getDocNode(aID).close();
+      }
+
+      this.getID = function closeIFrameDoc_getID()
+      {
+        return "close document";
+      }
+    }
+
+    /**
+     * Remove/insert HTML element pair.
+     */
+    function removeHTMLFromIFrameDoc(aID)
+    {
+      this.__proto__ = new rootContentRemoved(aID);
+
+      this.invoke = function removeHTMLFromIFrameDoc_invoke()
+      {
+        this.preinvoke();
+
+        // Remove HTML element.
+        var docNode = getDocNode(aID);
+        docNode.removeChild(docNode.firstChild);
+      }
+
+      this.getID = function removeHTMLFromIFrameDoc_getID()
+      {
+        return "remove HTML element";
+      }
+    }
+
+    function insertHTMLToIFrameDoc(aID)
+    {
+      this.__proto__ = new rootContentInserted(aID, "Haha");
+
+      this.invoke = function insertHTMLToIFrameDoc_invoke()
+      {
+        // Insert HTML element.
+        var docNode = getDocNode(aID);
+        var html = docNode.createElement("html");
+        var body = docNode.createElement("body");
+        var text = docNode.createTextNode("Haha");
+        body.appendChild(text);
+        html.appendChild(body);
+        docNode.appendChild(html);
+      }
+
+      this.getID = function insertHTMLToIFrameDoc_getID()
+      {
+        return "insert HTML element document";
+      }
+    }
+
+    /**
+     * Remove/insert HTML body pair.
+     */
+    function removeBodyFromIFrameDoc(aID)
+    {
+      this.__proto__ = new rootContentRemoved(aID);
+
+      this.invoke = function removeBodyFromIFrameDoc_invoke()
+      {
+        this.preinvoke();
+
+        // Remove body element.
+        var docNode = getDocNode(aID);
+        docNode.documentElement.removeChild(docNode.body);
+      }
+
+      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);
+        var body = docNode.createElement("body");
+        var text = docNode.createTextNode("Yo ho ho i butylka roma!");
+        body.appendChild(text);
+        docNode.documentElement.appendChild(body);
+      }
+
+      this.getID = function insertBodyToIFrameDoc_getID()
+      {
+        return "insert body element";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Test
+
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+
+    var gQueue = null;
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      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>
+
+  <div id="eventdump"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_textleaf.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Test accessible recreation</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="../events.js"></script>
+
+  <script type="application/javascript">
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function textLeafUpdate(aID, aIsTextLeafLinkable)
+    {
+      this.node = getNode(aID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.node.parentNode)
+      ];
+
+      this.finalCheck = function textLeafUpdate_finalCheck()
+      {
+        var textLeaf = getAccessible(this.node).firstChild;
+        is(textLeaf.numActions, (aIsTextLeafLinkable ? 1 : 0),
+           "Wrong action numbers!");
+      }
+    }
+
+    function setOnClickAttr(aID)
+    {
+      this.__proto__ = new textLeafUpdate(aID, true);
+
+      this.invoke = function setOnClickAttr_invoke()
+      {
+        this.node.setAttribute("onclick", "alert(3);");
+      }
+
+      this.getID = function setOnClickAttr_getID()
+      {
+        return "make " + prettyName(aID) + " linkable";
+      }
+    }
+
+    function removeOnClickAttr(aID)
+    {
+      this.__proto__ = new textLeafUpdate(aID, false);
+
+      this.invoke = function removeOnClickAttr_invoke()
+      {
+        this.node.removeAttribute("onclick");
+      }
+
+      this.getID = function removeOnClickAttr_getID()
+      {
+        return "unmake " + prettyName(aID) + " linkable";
+      }
+    }
+
+    function setOnClickNRoleAttrs(aID)
+    {
+      this.__proto__ = new textLeafUpdate(aID, true);
+
+      this.invoke = function setOnClickAttr_invoke()
+      {
+        this.node.setAttribute("role", "link");
+        this.node.setAttribute("onclick", "alert(3);");
+      }
+
+      this.getID = function setOnClickAttr_getID()
+      {
+        return "make " + prettyName(aID) + " linkable";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Test
+
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      // adds onclick on element, text leaf should inherit its action
+      gQueue.push(new setOnClickAttr("div"));
+
+      // remove onclick attribute, text leaf shouldn't have any action
+      gQueue.push(new removeOnClickAttr("div"));
+
+      // set onclick attribute making span accessible, it's inserted into tree
+      // and adopts text leaf accessible, text leaf should have an action
+      gQueue.push(new setOnClickNRoleAttrs("span"));
+
+      gQueue.invoke(); // SimpleTest.finish() will be called in the end
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="Clean up the code of accessible initialization and binding to the tree"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=545465">
+    Mozilla Bug 545465
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <div id="container">
+    <div id="div">div</div>
+    <span id="span">span</span>
+  </div>
+
+  <div id="eventdump"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_visibility.html
@@ -0,0 +1,439 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Style visibility tree update test</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="../events.js"></script>
+
+  <script type="application/javascript">
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    /**
+     * Hide parent while child stays visible.
+     */
+    function test1(aContainerID, aParentID, aChildID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, getNode(aParentID)),
+        new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+        new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+      ];
+
+      this.invoke = function invoke()
+      {
+        var tree =
+          { SECTION: [
+            { SECTION: [
+              { SECTION: [
+                { TEXT_LEAF: [] }
+              ] }
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+
+        getNode(aParentID).style.visibility = "hidden";
+      }
+
+      this.finalCheck = function finalCheck()
+      {
+        var tree =
+          { SECTION: [
+            { SECTION: [
+              { TEXT_LEAF: [] }
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+      }
+
+      this.getID = function getID()
+      {
+        return "hide parent while child stays visible";
+      }
+    }
+
+    /**
+     * Hide grand parent while its children stay visible.
+     */
+    function test2(aContainerID, aGrandParentID, aChildID, aChild2ID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, getNode(aGrandParentID)),
+        new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+        new invokerChecker(EVENT_SHOW, getNode(aChild2ID)),
+        new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+      ];
+
+      this.invoke = function invoke()
+      {
+        var tree =
+          { SECTION: [ // container
+            { SECTION: [ // grand parent
+              { SECTION: [
+                { SECTION: [ // child
+                  { TEXT_LEAF: [] }
+                ] },
+                { SECTION: [ // child2
+                  { TEXT_LEAF: [] }
+                ] }
+              ] }
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+
+        getNode(aGrandParentID).style.visibility = "hidden";
+      }
+
+      this.finalCheck = function finalCheck()
+      {
+        var tree =
+          { SECTION: [ // container
+            { SECTION: [ // child
+              { TEXT_LEAF: [] }
+            ] },
+            { SECTION: [ // child2
+              { TEXT_LEAF: [] }
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+      }
+
+      this.getID = function getID()
+      {
+        return "hide grand parent while its children stay visible";
+      }
+    }
+
+    /**
+     * Change container style, hide parents while their children stay visible.
+     */
+    function test3(aContainerID, aParentID, aParent2ID, aChildID, aChild2ID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, getNode(aParentID)),
+        new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+        new invokerChecker(EVENT_HIDE, getNode(aParent2ID)),
+        new invokerChecker(EVENT_SHOW, getNode(aChild2ID)),
+        new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+      ];
+
+      this.invoke = function invoke()
+      {
+        var tree =
+          { SECTION: [ // container
+            { SECTION: [ // parent
+              { SECTION: [ // child
+                { TEXT_LEAF: [] }
+              ] }
+            ] },
+            { SECTION: [ // parent2
+              { SECTION: [ // child2
+                { TEXT_LEAF: [] }
+              ] },
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+
+        getNode(aContainerID).style.color = "red";
+        getNode(aParentID).style.visibility = "hidden";
+        getNode(aParent2ID).style.visibility = "hidden";
+      }
+
+      this.finalCheck = function finalCheck()
+      {
+        var tree =
+          { SECTION: [ // container
+            { SECTION: [ // child
+              { TEXT_LEAF: [] }
+            ] },
+            { SECTION: [ // child2
+              { TEXT_LEAF: [] }
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+      }
+
+      this.getID = function getID()
+      {
+        return "change container style, hide parents while their children stay visible";
+      }
+    }
+
+    /**
+     * Change container style and make visible child inside the table.
+     */
+    function test4(aContainerID, aChildID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+        new invokerChecker(EVENT_REORDER, getNode(aChildID).parentNode)
+      ];
+
+      this.invoke = function invoke()
+      {
+        var tree =
+          { SECTION: [
+            { TABLE: [
+              { ROW: [
+                { CELL: [ ] }
+              ] }
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+
+        getNode(aContainerID).style.color = "red";
+        getNode(aChildID).style.visibility = "visible";
+      }
+
+      this.finalCheck = function finalCheck()
+      {
+        var tree =
+          { SECTION: [
+            { TABLE: [
+              { ROW: [
+                { CELL: [
+                  { SECTION: [
+                    { TEXT_LEAF: [] }
+                  ] }
+              ] }
+            ] }
+          ] }
+        ] };
+        testAccessibleTree(aContainerID, tree);
+      }
+
+      this.getID = function getID()
+      {
+        return "change container style, make visible child insdie the table";
+      }
+    }
+
+    /**
+     * Hide subcontainer while child inside the table stays visible.
+     */
+    function test5(aContainerID, aSubContainerID, aChildID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, getNode(aSubContainerID)),
+        new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+        new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+      ];
+
+      this.invoke = function invoke()
+      {
+        var tree =
+          { SECTION: [ // container
+            { SECTION: [ // subcontainer
+              { TABLE: [
+                { ROW: [
+                  { CELL: [
+                    { SECTION: [ // child
+                      { TEXT_LEAF: [] }
+                    ] }
+                  ] }
+                ] }
+              ] }
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+
+        getNode(aSubContainerID).style.visibility = "hidden";
+      }
+
+      this.finalCheck = function finalCheck()
+      {
+        var tree =
+          { SECTION: [ // container
+            { SECTION: [ // child
+              { TEXT_LEAF: [] }
+            ] }
+          ] };
+        testAccessibleTree(aContainerID, tree);
+      }
+
+      this.getID = function getID()
+      {
+        return "hide subcontainer while child inside the table stays visible";
+      }
+    }
+
+    /**
+     * Hide subcontainer while its child and child inside the nested table stays visible.
+     */
+    function test6(aContainerID, aSubContainerID, aChildID, aChild2ID)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, getNode(aSubContainerID)),
+        new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+        new invokerChecker(EVENT_SHOW, getNode(aChild2ID)),
+        new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+      ];
+
+      this.invoke = function invoke()
+      {
+        var tree =
+          { SECTION: [ // container
+            { SECTION: [ // subcontainer
+              { TABLE: [
+                { ROW: [
+                  { CELL: [
+                    { TABLE: [ // nested table
+                      { ROW: [
+                        { CELL: [
+                          { SECTION: [ // child
+                            { TEXT_LEAF: [] } ]} ]} ]} ]} ]} ]} ]},
+              { SECTION: [ // child2
+                { TEXT_LEAF: [] } ]} ]} ]};
+
+        testAccessibleTree(aContainerID, tree);
+
+        // invoke
+        getNode(aSubContainerID).style.visibility = "hidden";
+      }
+
+      this.finalCheck = function finalCheck()
+      {
+        var tree =
+          { SECTION: [ // container
+            { SECTION: [ // child
+              { TEXT_LEAF: [] } ]},
+            { SECTION: [ // child2
+              { TEXT_LEAF: [] } ]} ]};
+
+        testAccessibleTree(aContainerID, tree);
+      }
+
+      this.getID = function getID()
+      {
+        return "hide subcontainer while its child and child inside the nested table stays visible";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Test
+
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
+
+    var gQueue = null;
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new test1("t1_container", "t1_parent", "t1_child"));
+      gQueue.push(new test2("t2_container", "t2_grandparent", "t2_child", "t2_child2"));
+      gQueue.push(new test3("t3_container", "t3_parent", "t3_parent2", "t3_child", "t3_child2"));
+      gQueue.push(new test4("t4_container", "t4_child"));
+      gQueue.push(new test5("t5_container", "t5_subcontainer", "t5_child"));
+      gQueue.push(new test6("t6_container", "t6_subcontainer", "t6_child", "t6_child2"));
+
+      gQueue.invoke(); // SimpleTest.finish() will be called in the end
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="Develop a way to handle visibility style"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125">
+    Mozilla Bug 606125
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <!-- hide parent while child stays visible -->
+  <div id="t1_container">
+    <div id="t1_parent">
+      <div id="t1_child" style="visibility: visible">text</div>
+    </div>
+  </div>
+
+  <!-- hide grandparent while its children stay visible -->
+  <div id="t2_container">
+    <div id="t2_grandparent">
+      <div>
+        <div id="t2_child" style="visibility: visible">text</div>
+        <div id="t2_child2" style="visibility: visible">text</div>
+      </div>
+    </div>
+  </div>
+
+  <!-- change container style, hide parents while their children stay visible -->
+  <div id="t3_container">
+    <div id="t3_parent">
+      <div id="t3_child" style="visibility: visible">text</div>
+    </div>
+    <div id="t3_parent2">
+      <div id="t3_child2" style="visibility: visible">text</div>
+    </div>
+  </div>
+
+  <!-- change container style, show child inside the table -->
+  <div id="t4_container">
+    <table>
+      <tr>
+        <td>
+          <div id="t4_child" style="visibility: hidden;">text</div>
+        </td>
+      </tr>
+    </table>
+  </div>
+
+  <!-- hide subcontainer while child inside the table stays visible -->
+  <div id="t5_container">
+    <div id="t5_subcontainer">
+      <table>
+        <tr>
+          <td>
+            <div id="t5_child" style="visibility: visible;">text</div>
+          </td>
+        </tr>
+      </table>
+    </div>
+  </div>
+
+  <!-- hide subcontainer while its child and child inside the nested table stays visible -->
+  <div id="t6_container">
+    <div id="t6_subcontainer">
+      <table>
+        <tr>
+          <td>
+            <table>
+              <tr>
+                <td>
+                  <div id="t6_child" style="visibility: visible;">text</div>
+                </td>
+              </tr>
+            </table>
+          </td>
+        </tr>
+      </table>
+      <div id="t6_child2" style="visibility: visible">text</div>
+    </div>
+  </div>
+
+  <div id="eventdump"></div>
+</body>
+</html>
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -88,16 +88,17 @@ ifneq (,$(filter OS2 WINCE WINNT,$(OS_AR
 PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
 else
 PROGRAM = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
 endif
 
 CPPSRCS = nsBrowserApp.cpp
 
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
 
 ifdef BUILD_STATIC_LIBS
 ifdef _MSC_VER
 STATIC_COMPONENTS_LINKER_PATH = -LIBPATH:$(DEPTH)/staticlib
 else
 STATIC_COMPONENTS_LINKER_PATH = -L$(DEPTH)/staticlib
 endif
 LIBS += $(DEPTH)/toolkit/xre/$(LIB_PREFIX)xulapp_s.$(LIB_SUFFIX)
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -234,16 +234,18 @@ pref("general.autoScroll", true);
 // is the default browser.
 pref("browser.shell.checkDefaultBrowser", true);
 
 // 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
 // The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
 pref("browser.startup.page",                1);
 pref("browser.startup.homepage",            "chrome://branding/locale/browserconfig.properties");
 
+pref("browser.aboutHomeSnippets.updateUrl", "http://snippets.mozilla.com/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
+
 pref("browser.enable_automatic_image_resizing", true);
 pref("browser.chrome.site_icons", true);
 pref("browser.chrome.favicons", true);
 pref("browser.warnOnQuit", true);
 pref("browser.warnOnRestart", true);
 pref("browser.fullscreen.autohide", true);
 pref("browser.fullscreen.animateUp", 1);
 
@@ -468,31 +470,16 @@ pref("privacy.cpd.siteSettings",        
 // 4 - Today
 pref("privacy.sanitize.timeSpan", 1);
 pref("privacy.sanitize.sanitizeOnShutdown", false);
 
 pref("privacy.sanitize.migrateFx3Prefs",    false);
 
 pref("network.proxy.share_proxy_settings",  false); // use the same proxy settings for all protocols
 
-// l12n and i18n
-pref("intl.accept_languages", "chrome://global/locale/intl.properties");
-pref("intl.charsetmenu.browser.static", "chrome://global/locale/intl.properties");
-pref("intl.charsetmenu.browser.more1",  "chrome://global/locale/intl.properties");
-pref("intl.charsetmenu.browser.more2",  "chrome://global/locale/intl.properties");
-pref("intl.charsetmenu.browser.more3",  "chrome://global/locale/intl.properties");
-pref("intl.charsetmenu.browser.more4",  "chrome://global/locale/intl.properties");
-pref("intl.charsetmenu.browser.more5",  "chrome://global/locale/intl.properties");
-pref("intl.charsetmenu.browser.unicode",  "UTF-8, UTF-16LE, UTF-16BE, UTF-32, UTF-32LE, UTF-32BE");
-pref("intl.charset.detector", "chrome://global/locale/intl.properties");
-pref("intl.charset.default",  "chrome://global-platform/locale/intl.properties");
-pref("font.language.group", "chrome://global/locale/intl.properties");
-pref("intl.menuitems.alwaysappendaccesskeys","chrome://global/locale/intl.properties");
-pref("intl.menuitems.insertseparatorbeforeaccesskeys","chrome://global/locale/intl.properties");
-
 // simple gestures support
 pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate");
 pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate");
 pref("browser.gesture.swipe.up", "Browser:HideTabView");
 pref("browser.gesture.swipe.down", "Browser:ShowTabView");
 #ifdef XP_MACOSX
 pref("browser.gesture.pinch.latched", true);
 pref("browser.gesture.pinch.threshold", 150);
@@ -1045,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/aboutHome.js
+++ b/browser/base/content/aboutHome.js
@@ -134,60 +134,70 @@ function setupSearchEngine()
 
 function loadSnippets()
 {
   // Check last snippets update.
   let lastUpdate = localStorage["snippets-last-update"];
   let updateURL = localStorage["snippets-update-url"];
   if (updateURL && (!lastUpdate ||
                     Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS)) {
+    // Even if fetching should fail we don't want to spam the server, thus
+    // set the last update time regardless its results.  Will retry tomorrow.
+    localStorage["snippets-last-update"] = Date.now();
+
     // Try to update from network.
     let xhr = new XMLHttpRequest();
     xhr.open('GET', updateURL, true);
     xhr.onerror = function (event) {
       showSnippets();
     };
     xhr.onload = function (event)
     {
       if (xhr.status == 200) {
         localStorage["snippets"] = xhr.responseText;
-        localStorage["snippets-last-update"] = Date.now();
       }
       showSnippets();
     };
     xhr.send(null);
   } else {
     showSnippets();
   }
 }
 
 function showSnippets()
 {
   let snippets = localStorage["snippets"];
+  // If there are remotely fetched snippets, try to to show them.
   if (snippets) {
     let snippetsElt = document.getElementById("snippets");
-    snippetsElt.innerHTML = snippets;
-    // Scripts injected by innerHTML are inactive, so we have to relocate them
-    // through DOM manipulation to activate their contents.
-    Array.forEach(snippetsElt.getElementsByTagName("script"), function(elt) {
-      let relocatedScript = document.createElement("script");
-      relocatedScript.type = "text/javascript;version=1.8";
-      relocatedScript.text = elt.text;
-      elt.parentNode.replaceChild(relocatedScript, elt);
-    });
-    snippetsElt.hidden = false;
-  } else {
-    // If there are no saved snippets, show one of the default ones.
-    let defaultSnippetsElt = document.getElementById("defaultSnippets");
-    let entries = defaultSnippetsElt.querySelectorAll("span");
-    // Choose a random snippet.  Assume there is always at least one.
-    let randIndex = Math.round(Math.random() * (entries.length - 1));
-    let entry = entries[randIndex];
-    // Inject url in the eventual link.
-    if (DEFAULT_SNIPPETS_URLS[randIndex]) {
-      let links = entry.getElementsByTagName("a");
-      if (links.length != 1)
-        return; // Something is messed up in this entry, we support just 1 link.
-      links[0].href = DEFAULT_SNIPPETS_URLS[randIndex];
+    // Injecting snippets can throw if they're invalid XML.
+    try {
+      snippetsElt.innerHTML = snippets;
+      // Scripts injected by innerHTML are inactive, so we have to relocate them
+      // through DOM manipulation to activate their contents.
+      Array.forEach(snippetsElt.getElementsByTagName("script"), function(elt) {
+        let relocatedScript = document.createElement("script");
+        relocatedScript.type = "text/javascript;version=1.8";
+        relocatedScript.text = elt.text;
+        elt.parentNode.replaceChild(relocatedScript, elt);
+      });
+      snippetsElt.hidden = false;
+      return;
+    } catch (ex) {
+      // Bad content, continue to show default snippets.
     }
-    entry.hidden = false;
   }
+
+  // Show default snippets otherwise.
+  let defaultSnippetsElt = document.getElementById("defaultSnippets");
+  let entries = defaultSnippetsElt.querySelectorAll("span");
+  // Choose a random snippet.  Assume there is always at least one.
+  let randIndex = Math.round(Math.random() * (entries.length - 1));
+  let entry = entries[randIndex];
+  // Inject url in the eventual link.
+  if (DEFAULT_SNIPPETS_URLS[randIndex]) {
+    let links = entry.getElementsByTagName("a");
+    if (links.length != 1)
+      return; // Something is messed up in this entry, we support just 1 link.
+    links[0].href = DEFAULT_SNIPPETS_URLS[randIndex];
+  }
+  entry.hidden = false;
 }
--- a/browser/base/content/browser-doctype.inc
+++ b/browser/base/content/browser-doctype.inc
@@ -1,17 +1,15 @@
 <!DOCTYPE window [
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
 %brandDTD;
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
 %browserDTD;
 <!ENTITY % baseMenuDTD SYSTEM "chrome://browser/locale/baseMenuOverlay.dtd" >
 %baseMenuDTD;
-<!ENTITY % globalRegionDTD SYSTEM "chrome://global-region/locale/region.dtd">
-%globalRegionDTD;
 <!ENTITY % charsetDTD SYSTEM "chrome://global/locale/charsetOverlay.dtd" >
 %charsetDTD;
 <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" >
 %textcontextDTD;
 <!ENTITY % customizeToolbarDTD SYSTEM "chrome://global/locale/customizeToolbar.dtd">
   %customizeToolbarDTD;
 <!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
 %placesDTD;
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -35,78 +35,95 @@
 #
 # ***** END LICENSE BLOCK *****
 
 let TabView = {
   _deck: null,
   _window: null,
   _sessionstore: null,
   _visibilityID: "tabview-visibility",
-  
+
   // ----------
   get windowTitle() {
     delete this.windowTitle;
     let brandBundle = document.getElementById("bundle_brand");
     let brandShortName = brandBundle.getString("brandShortName");
     let title = gNavigatorBundle.getFormattedString("tabView2.title", [brandShortName]);
     return this.windowTitle = title;
   },
 
   // ----------
-  init: function TabView_init() {    
+  init: function TabView_init() {
     // ___ keys    
     this._setBrowserKeyHandlers();
 
     // ___ visibility
     this._sessionstore =
       Cc["@mozilla.org/browser/sessionstore;1"].
         getService(Ci.nsISessionStore);
 
     let data = this._sessionstore.getWindowValue(window, this._visibilityID);
-    if (data && data == "true")
+    if (data && data == "true") {
       this.show();
+    } else {
+      let self = this;
+      // if a tab is changed from hidden to unhidden and the iframe is not 
+      // initialized, load the iframe and setup the tab.
+      this._tabShowEventListener = function (event) {
+        if (!self._window)
+          self._initFrame(function() {
+            self._window.UI.onTabSelect(gBrowser.selectedTab);
+          });
+      };
+      gBrowser.tabContainer.addEventListener(
+        "TabShow", this._tabShowEventListener, true);
+    }
   },
 
   // ----------
   // Creates the frame and calls the callback once it's loaded. 
   // If the frame already exists, calls the callback immediately. 
   _initFrame: function TabView__initFrame(callback) {
     if (this._window) {
       if (typeof callback == "function")
         callback();
     } else {
       // ___ find the deck
       this._deck = document.getElementById("tab-view-deck");
-      
+
       // ___ create the frame
       let iframe = document.createElement("iframe");
       iframe.id = "tab-view";
       iframe.setAttribute("transparent", "true");
       iframe.flex = 1;
-              
+
       if (typeof callback == "function")
         iframe.addEventListener("DOMContentLoaded", callback, false);
-      
+
       iframe.setAttribute("src", "chrome://browser/content/tabview.html");
       this._deck.appendChild(iframe);
       this._window = iframe.contentWindow;
 
       // ___ visibility storage handler
       let self = this;
       function observer(subject, topic, data) {
         if (topic == "quit-application-requested") {
           let data = (self.isVisible() ? "true" : "false");
           self._sessionstore.setWindowValue(window, self._visibilityID, data);
         }
       }
-      
       Services.obs.addObserver(observer, "quit-application-requested", false);
+
+