Bug 475522 - walk subtree once for all text attributes, r=davidb, rOMarcoZ, sr=neil
authorAlexander Surkov <surkov.alexander@gmail.com>
Sun, 15 Feb 2009 09:35:10 +0100
changeset 24999 bd35ca5e194ab9680e3d82dea7d2328694438da9
parent 24998 7d104921e0ee41ca449f7c7ca019d0ee98ad1ec5
child 25000 0a341c0bfc3ff04001835cbebfb42578bed86092
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdavidb, rOMarcoZ, neil
bugs475522
milestone1.9.2a1pre
Bug 475522 - walk subtree once for all text attributes, r=davidb, rOMarcoZ, sr=neil
accessible/src/base/Makefile.in
accessible/src/base/nsAccessibilityAtomList.h
accessible/src/base/nsTextAttrs.cpp
accessible/src/base/nsTextAttrs.h
accessible/src/base/nsTextUtils.cpp
accessible/src/base/nsTextUtils.h
accessible/src/html/nsHyperTextAccessible.cpp
accessible/src/html/nsHyperTextAccessible.h
accessible/tests/mochitest/attributes.js
accessible/tests/mochitest/test_textattrs.html
--- a/accessible/src/base/Makefile.in
+++ b/accessible/src/base/Makefile.in
@@ -88,17 +88,17 @@ CPPSRCS = \
   nsAccessibleRelation.cpp \
   nsAccessibleTreeWalker.cpp \
   nsBaseWidgetAccessible.cpp \
   nsFormControlAccessible.cpp \
   nsRootAccessible.cpp \
   nsApplicationAccessible.cpp \
   nsCaretAccessible.cpp \
   nsTextAccessible.cpp \
-  nsTextUtils.cpp \
+  nsTextAttrs.cpp \
   $(NULL)
 
 EXPORTS = \
   nsRootAccessible.h \
   nsAccessibleEventData.h \
   nsAccessNode.h \
   $(NULL)
 
--- a/accessible/src/base/nsAccessibilityAtomList.h
+++ b/accessible/src/base/nsAccessibilityAtomList.h
@@ -188,20 +188,27 @@ ACCESSIBILITY_ATOM(type, "type")
 ACCESSIBILITY_ATOM(value, "value")
 
   // Alphabetical list of object attributes
 ACCESSIBILITY_ATOM(display, "display")
 ACCESSIBILITY_ATOM(textAlign, "text-align")
 ACCESSIBILITY_ATOM(textIndent, "text-indent")
 
   // Alphabetical list of text attributes (AT API)
+ACCESSIBILITY_ATOM(color, "color")
 ACCESSIBILITY_ATOM(backgroundColor, "background-color")
+ACCESSIBILITY_ATOM(fontFamily, "font-family")
+ACCESSIBILITY_ATOM(fontStyle, "font-style")
+ACCESSIBILITY_ATOM(fontWeight, "font-weight")
 ACCESSIBILITY_ATOM(fontSize, "font-size")
 ACCESSIBILITY_ATOM(invalid, "invalid")
 ACCESSIBILITY_ATOM(language, "language")
+ACCESSIBILITY_ATOM(textLineThroughStyle, "text-line-through-style")
+ACCESSIBILITY_ATOM(textUnderlineStyle, "text-underline-style")
+ACCESSIBILITY_ATOM(textPosition, "text-position")
 
   // ARIA (DHTML accessibility) attributes
   // Also add to nsARIAMap.cpp and nsARIAMap.h
   // ARIA role attribute
 ACCESSIBILITY_ATOM(role, "role")
 ACCESSIBILITY_ATOM(aria_activedescendant, "aria-activedescendant")
 ACCESSIBILITY_ATOM(aria_atomic, "aria-atomic")
 ACCESSIBILITY_ATOM(aria_autocomplete, "aria-autocomplete")
rename from accessible/src/base/nsTextUtils.cpp
rename to accessible/src/base/nsTextAttrs.cpp
--- a/accessible/src/base/nsTextUtils.cpp
+++ b/accessible/src/base/nsTextAttrs.cpp
@@ -31,266 +31,573 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "nsTextUtils.h"
+#include "nsTextAttrs.h"
 
 #include "nsAccessNode.h"
+#include "nsHyperTextAccessibleWrap.h"
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsLangTextAttr
-
-PRBool
-nsLangTextAttr::Equal(nsIDOMElement *aElm)
-{
-  nsCOMPtr<nsIContent> content(do_QueryInterface(aElm));
-  if (!content)
-    return PR_FALSE;
-
-  nsAutoString lang;
-  nsCoreUtils::GetLanguageFor(content, mRootContent, lang);
-
-  return lang == mLang;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// nsCSSTextAttr
+// Constants and structures
 
 /**
  * Item of the gCSSTextAttrsMap map.
  */
 struct nsCSSTextAttrMapItem
 {
   const char* mCSSName;
   const char* mCSSValue;
-  const char* mAttrName;
+  nsIAtom** mAttrName;
   const char* mAttrValue;
 };
 
 /**
  * The map of CSS properties to text attributes.
  */
-
 const char* const kAnyValue = nsnull;
-const char* const kCopyName = nsnull;
 const char* const kCopyValue = nsnull;
 
-static nsCSSTextAttrMapItem gCSSTextAttrsMap[] = {
-  // CSS name            CSS value        Attribute name              Attribute value
-  { "color",             kAnyValue,       kCopyName,                  kCopyValue },
-  { "font-family",       kAnyValue,       kCopyName,                  kCopyValue },
-  { "font-style",        kAnyValue,       kCopyName,                  kCopyValue },
-  { "font-weight",       kAnyValue,       kCopyName,                  kCopyValue },
-  { "text-decoration",   "line-through",  "text-line-through-style",  "solid" },
-  { "text-decoration",   "underline",     "text-underline-style",     "solid" },
-  { "vertical-align",    kAnyValue,       "text-position",            kCopyValue }
+static nsCSSTextAttrMapItem gCSSTextAttrsMap[] =
+{
+  // CSS name            CSS value        Attribute name                                Attribute value
+  { "color",             kAnyValue,       &nsAccessibilityAtoms::color,                 kCopyValue },
+  { "font-family",       kAnyValue,       &nsAccessibilityAtoms::fontFamily,            kCopyValue },
+  { "font-style",        kAnyValue,       &nsAccessibilityAtoms::fontStyle,             kCopyValue },
+  { "font-weight",       kAnyValue,       &nsAccessibilityAtoms::fontWeight,            kCopyValue },
+  { "text-decoration",   "line-through",  &nsAccessibilityAtoms::textLineThroughStyle,  "solid" },
+  { "text-decoration",   "underline",     &nsAccessibilityAtoms::textUnderlineStyle,    "solid" },
+  { "vertical-align",    kAnyValue,       &nsAccessibilityAtoms::textPosition,          kCopyValue }
 };
 
-nsCSSTextAttr::nsCSSTextAttr(PRBool aIncludeDefAttrValue, nsIDOMElement *aElm,
-                             nsIDOMElement *aRootElm) :
-  mIndex(-1), mIncludeDefAttrValue(aIncludeDefAttrValue)
+////////////////////////////////////////////////////////////////////////////////
+// nsTextAttrs
+
+nsTextAttrsMgr::nsTextAttrsMgr(nsHyperTextAccessible *aHyperTextAcc,
+                               nsIDOMNode *aHyperTextNode,
+                               PRBool aIncludeDefAttrs,
+                               nsIDOMNode *aOffsetNode) :
+  mHyperTextAcc(aHyperTextAcc), mHyperTextNode(aHyperTextNode),
+  mIncludeDefAttrs(aIncludeDefAttrs), mOffsetNode(aOffsetNode)
+{
+}
+
+nsresult
+nsTextAttrsMgr::GetAttributes(nsIPersistentProperties *aAttributes,
+                              PRInt32 *aStartHTOffset,
+                              PRInt32 *aEndHTOffset)
+{
+  // 1. Hyper text accessible and its DOM node must be specified always.
+  // 2. Offset DOM node and result hyper text offsets must be specifed in
+  // the case of text attributes.
+  // 3. Offset DOM node and result hyper text offsets must not be specifed but
+  // include default text attributes flag and attributes list must be specified
+  // in the case of default text attributes.
+  NS_PRECONDITION(mHyperTextAcc && mHyperTextNode &&
+                  ((mOffsetNode && aStartHTOffset && aEndHTOffset) ||
+                  (!mOffsetNode && !aStartHTOffset && !aEndHTOffset &&
+                   mIncludeDefAttrs && aAttributes)),
+                  "Wrong usage of nsTextAttrsMgr!");
+
+  nsCOMPtr<nsIDOMElement> hyperTextElm =
+    nsCoreUtils::GetDOMElementFor(mHyperTextNode);
+  nsCOMPtr<nsIDOMElement> offsetElm;
+  if (mOffsetNode)
+    offsetElm = nsCoreUtils::GetDOMElementFor(mOffsetNode);
+
+  nsIFrame *rootFrame = nsCoreUtils::GetFrameFor(hyperTextElm);
+  nsIFrame *frame = nsnull;
+  if (offsetElm)
+    frame = nsCoreUtils::GetFrameFor(offsetElm);
+
+  nsTPtrArray<nsITextAttr> textAttrArray(10);
+
+  // "language" text attribute
+  nsLangTextAttr langTextAttr(mHyperTextAcc, mHyperTextNode, mOffsetNode);
+  textAttrArray.AppendElement(&langTextAttr);
+
+  // "color" text attribute
+  nsCSSTextAttr colorTextAttr(0, hyperTextElm, offsetElm);
+  textAttrArray.AppendElement(&colorTextAttr);
+
+  // "font-family" text attribute
+  nsCSSTextAttr fontFamilyTextAttr(1, hyperTextElm, offsetElm);
+  textAttrArray.AppendElement(&fontFamilyTextAttr);
+
+  // "font-style" text attribute
+  nsCSSTextAttr fontStyleTextAttr(2, hyperTextElm, offsetElm);
+  textAttrArray.AppendElement(&fontStyleTextAttr);
+
+  // "font-weight" text attribute
+  nsCSSTextAttr fontWeightTextAttr(3, hyperTextElm, offsetElm);
+  textAttrArray.AppendElement(&fontWeightTextAttr);
+
+  // "text-line-through-style" text attribute
+  nsCSSTextAttr lineThroughTextAttr(4, hyperTextElm, offsetElm);
+  textAttrArray.AppendElement(&lineThroughTextAttr);
+
+  // "text-underline-style" text attribute
+  nsCSSTextAttr underlineTextAttr(5, hyperTextElm, offsetElm);
+  textAttrArray.AppendElement(&underlineTextAttr);
+
+  // "text-position" text attribute
+  nsCSSTextAttr posTextAttr(6, hyperTextElm, offsetElm);
+  textAttrArray.AppendElement(&posTextAttr);
+
+  // "background-color" text attribute
+  nsBGColorTextAttr bgColorTextAttr(rootFrame, frame);
+  textAttrArray.AppendElement(&bgColorTextAttr);
+
+  // "font-size" text attribute
+  nsFontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
+  textAttrArray.AppendElement(&fontSizeTextAttr);
+
+  // Expose text attributes if applicable.
+  if (aAttributes) {
+    PRUint32 len = textAttrArray.Length();
+    for (PRUint32 idx = 0; idx < len; idx++) {
+      nsITextAttr *textAttr = textAttrArray[idx];
+
+      nsAutoString value;
+      if (textAttr->GetValue(value, mIncludeDefAttrs))
+        nsAccUtils::SetAccAttr(aAttributes, textAttr->GetName(), value);
+    }
+  }
+
+  nsresult rv = NS_OK;
+
+  // Expose text attributes range where they are applied if applicable.
+  if (mOffsetNode)
+    rv = GetRange(textAttrArray, aStartHTOffset, aEndHTOffset);
+
+  textAttrArray.Clear();
+  return rv;
+}
+
+nsresult
+nsTextAttrsMgr::GetRange(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
+                         PRInt32 *aStartHTOffset, PRInt32 *aEndHTOffset)
 {
-  nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aElm,
-                                           getter_AddRefs(mStyleDecl));
+  nsCOMPtr<nsIDOMElement> rootElm =
+    nsCoreUtils::GetDOMElementFor(mHyperTextNode);
+  NS_ENSURE_STATE(rootElm);
+
+  nsCOMPtr<nsIDOMNode> tmpNode(mOffsetNode);
+  nsCOMPtr<nsIDOMNode> currNode(mOffsetNode);
+
+  PRUint32 len = aTextAttrArray.Length();
+
+  // Navigate backwards and forwards from current node to the root node to
+  // calculate range bounds for the text attribute. Navigation sequence is the
+  // following:
+  // 1. Navigate through the siblings.
+  // 2. If the traversed sibling has children then navigate from its leaf child
+  //    to it through whole tree of the traversed sibling.
+  // 3. Get the parent and cycle algorithm until the root node.
+
+  // Navigate backwards (find the start offset).
+  while (currNode && currNode != rootElm) {
+    nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(currNode));
+    NS_ENSURE_STATE(currElm);
+
+    if (currNode != mOffsetNode) {
+      PRBool stop = PR_FALSE;
+      for (PRUint32 idx = 0; idx < len; idx++) {
+        nsITextAttr *textAttr = aTextAttrArray[idx];
+        if (!textAttr->Equal(currElm)) {
+
+          PRInt32 startHTOffset = 0;
+          nsCOMPtr<nsIAccessible> startAcc;
+          nsresult rv = mHyperTextAcc->
+            DOMPointToHypertextOffset(tmpNode, -1, &startHTOffset,
+                                      getter_AddRefs(startAcc));
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          if (!startAcc)
+            startHTOffset = 0;
+
+          if (startHTOffset > *aStartHTOffset)
+            *aStartHTOffset = startHTOffset;
+
+          stop = PR_TRUE;
+          break;
+        }
+      }
+      if (stop)
+        break;
+    }
 
-  if (!mIncludeDefAttrValue)
-    nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aRootElm,
-                                             getter_AddRefs(mDefStyleDecl));
+    currNode->GetPreviousSibling(getter_AddRefs(tmpNode));
+    if (tmpNode) {
+      // Navigate through the subtree of traversed children to calculate
+      // left bound of the range.
+      FindStartOffsetInSubtree(aTextAttrArray, tmpNode, currNode,
+                               aStartHTOffset);
+    }
+
+    currNode->GetParentNode(getter_AddRefs(tmpNode));
+    currNode.swap(tmpNode);
+  }
+
+  // Navigate forwards (find the end offset).
+  PRBool moveIntoSubtree = PR_TRUE;
+  currNode = mOffsetNode;
+
+  while (currNode && currNode != rootElm) {
+    nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(currNode));
+    NS_ENSURE_STATE(currElm);
+
+    // Stop new end offset searching if the given text attribute changes its
+    // value.
+    PRBool stop = PR_FALSE;
+    for (PRUint32 idx = 0; idx < len; idx++) {
+      nsITextAttr *textAttr = aTextAttrArray[idx];
+      if (!textAttr->Equal(currElm)) {
+
+        PRInt32 endHTOffset = 0;
+        nsresult rv = mHyperTextAcc->
+          DOMPointToHypertextOffset(currNode, -1, &endHTOffset);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (endHTOffset < *aEndHTOffset)
+          *aEndHTOffset = endHTOffset;
+
+        stop = PR_TRUE;
+        break;
+      }
+    }
+
+    if (stop)
+      break;
+
+    if (moveIntoSubtree) {
+      // Navigate through subtree of traversed node. We use 'moveIntoSubtree'
+      // flag to avoid traversing the same subtree twice.
+      currNode->GetFirstChild(getter_AddRefs(tmpNode));
+      if (tmpNode)
+        FindEndOffsetInSubtree(aTextAttrArray, tmpNode, aEndHTOffset);
+    }
+
+    currNode->GetNextSibling(getter_AddRefs(tmpNode));
+    moveIntoSubtree = PR_TRUE;
+    if (!tmpNode) {
+      currNode->GetParentNode(getter_AddRefs(tmpNode));
+      moveIntoSubtree = PR_FALSE;
+    }
+
+    currNode.swap(tmpNode);
+  }
+
+  return NS_OK;
 }
 
 PRBool
-nsCSSTextAttr::Equal(nsIDOMElement *aElm)
+nsTextAttrsMgr::FindEndOffsetInSubtree(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
+                                       nsIDOMNode *aCurrNode,
+                                       PRInt32 *aHTOffset)
 {
-  if (!aElm || !mStyleDecl)
+  if (!aCurrNode)
+    return PR_FALSE;
+
+  nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode));
+  NS_ENSURE_STATE(currElm);
+
+  // If the given text attribute (pointed by nsTextAttr object) changes its
+  // value on the traversed element then fit the end of range.
+  PRUint32 len = aTextAttrArray.Length();
+  for (PRUint32 idx = 0; idx < len; idx++) {
+    nsITextAttr *textAttr = aTextAttrArray[idx];
+    if (!textAttr->Equal(currElm)) {
+      PRInt32 endHTOffset = 0;
+      nsresult rv = mHyperTextAcc->
+        DOMPointToHypertextOffset(aCurrNode, -1, &endHTOffset);
+      NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+      if (endHTOffset < *aHTOffset)
+        *aHTOffset = endHTOffset;
+
+      return PR_TRUE;
+    }
+  }
+
+  // Deeply traverse into the tree to fit the end of range.
+  nsCOMPtr<nsIDOMNode> nextNode;
+  aCurrNode->GetFirstChild(getter_AddRefs(nextNode));
+  if (nextNode) {
+    PRBool res = FindEndOffsetInSubtree(aTextAttrArray, nextNode, aHTOffset);
+    if (res)
+      return res;
+  }
+
+  aCurrNode->GetNextSibling(getter_AddRefs(nextNode));
+  if (nextNode) {
+    if (FindEndOffsetInSubtree(aTextAttrArray, nextNode, aHTOffset))
+      return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+
+PRBool
+nsTextAttrsMgr::FindStartOffsetInSubtree(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
+                                         nsIDOMNode *aCurrNode,
+                                         nsIDOMNode *aPrevNode,
+                                         PRInt32 *aHTOffset)
+{
+  if (!aCurrNode)
     return PR_FALSE;
 
+  // Find the closest element back to the traversed element.
+  nsCOMPtr<nsIDOMNode> nextNode;
+  aCurrNode->GetLastChild(getter_AddRefs(nextNode));
+  if (nextNode) {
+    if (FindStartOffsetInSubtree(aTextAttrArray, nextNode, aPrevNode, aHTOffset))
+      return PR_TRUE;
+  }
+
+  nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode));
+  NS_ENSURE_STATE(currElm);
+
+  // If the given text attribute (pointed by nsTextAttr object) changes its
+  // value on the traversed element then fit the start of range.
+  PRUint32 len = aTextAttrArray.Length();
+  for (PRUint32 idx = 0; idx < len; idx++) {
+    nsITextAttr *textAttr = aTextAttrArray[idx];
+    if (!textAttr->Equal(currElm)) {
+
+      PRInt32 startHTOffset = 0;
+      nsCOMPtr<nsIAccessible> startAcc;
+      nsresult rv = mHyperTextAcc->
+        DOMPointToHypertextOffset(aPrevNode, -1, &startHTOffset,
+                                  getter_AddRefs(startAcc));
+      NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
+      if (!startAcc)
+        startHTOffset = 0;
+
+      if (startHTOffset > *aHTOffset)
+        *aHTOffset = startHTOffset;
+
+      return PR_TRUE;
+    }
+  }
+
+  // Moving backwards to find the start of range.
+  aCurrNode->GetPreviousSibling(getter_AddRefs(nextNode));
+  if (nextNode) {
+    if (FindStartOffsetInSubtree(aTextAttrArray, nextNode, aCurrNode, aHTOffset))
+      return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsLangTextAttr
+
+nsLangTextAttr::nsLangTextAttr(nsHyperTextAccessible *aRootAcc, 
+                               nsIDOMNode *aRootNode, nsIDOMNode *aNode) :
+  nsTextAttr<nsAutoString>::nsTextAttr(aNode == nsnull)
+{
+  mRootContent = do_QueryInterface(aRootNode);
+
+  nsresult rv = aRootAcc->GetLanguage(mRootNativeValue);
+  mIsRootDefined = NS_SUCCEEDED(rv) && !mRootNativeValue.IsEmpty();
+
+  if (aNode) {
+    nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
+    mIsDefined = GetLang(content, mNativeValue);
+  }
+}
+
+PRBool
+nsLangTextAttr::GetValueFor(nsIDOMElement *aElm, nsAutoString *aValue)
+{
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aElm);
+  return GetLang(content, *aValue);
+}
+
+void
+nsLangTextAttr::Format(const nsAutoString& aValue, nsAString& aFormattedValue)
+{
+  aFormattedValue = aValue;
+}
+
+PRBool
+nsLangTextAttr::GetLang(nsIContent *aContent, nsAString& aLang)
+{
+  nsCoreUtils::GetLanguageFor(aContent, mRootContent, aLang);
+  return !aLang.IsEmpty();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsCSSTextAttr
+
+nsCSSTextAttr::nsCSSTextAttr(PRUint32 aIndex, nsIDOMElement *aRootElm,
+                             nsIDOMElement *aElm) :
+  nsTextAttr<nsAutoString>::nsTextAttr(aElm == nsnull), mIndex(aIndex)
+{
+  mIsRootDefined = GetValueFor(aRootElm, &mRootNativeValue);
+
+  if (aElm)
+    mIsDefined = GetValueFor(aElm, &mNativeValue);
+}
+
+nsIAtom*
+nsCSSTextAttr::GetName()
+{
+  return *gCSSTextAttrsMap[mIndex].mAttrName;
+}
+
+PRBool
+nsCSSTextAttr::GetValueFor(nsIDOMElement *aElm, nsAutoString *aValue)
+{
   nsCOMPtr<nsIDOMCSSStyleDeclaration> currStyleDecl;
   nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), aElm,
                                            getter_AddRefs(currStyleDecl));
   if (!currStyleDecl)
     return PR_FALSE;
 
   NS_ConvertASCIItoUTF16 cssName(gCSSTextAttrsMap[mIndex].mCSSName);
 
-  nsAutoString currValue;
-  nsresult rv = currStyleDecl->GetPropertyValue(cssName, currValue);
+  nsresult rv = currStyleDecl->GetPropertyValue(cssName, *aValue);
   if (NS_FAILED(rv))
-    return PR_FALSE;
-
-  nsAutoString value;
-  rv = mStyleDecl->GetPropertyValue(cssName, value);
-  return NS_SUCCEEDED(rv) && value == currValue;
-}
+    return PR_TRUE;
 
-PRBool
-nsCSSTextAttr::Iterate()
-{
-  return ++mIndex < static_cast<PRInt32>(NS_ARRAY_LENGTH(gCSSTextAttrsMap));
-}
-
-PRBool
-nsCSSTextAttr::Get(nsACString& aName, nsAString& aValue)
-{
-  if (!mStyleDecl)
-    return PR_FALSE;
-
-  NS_ConvertASCIItoUTF16 cssName(gCSSTextAttrsMap[mIndex].mCSSName);
-  nsresult rv = mStyleDecl->GetPropertyValue(cssName, aValue);
-  if (NS_FAILED(rv))
+  const char *cssValue = gCSSTextAttrsMap[mIndex].mCSSValue;
+  if (cssValue != kAnyValue && !aValue->EqualsASCII(cssValue))
     return PR_FALSE;
 
-  // Don't expose text attribute if corresponding CSS value on the element
-  // equals to CSS value on the root element and we don't want to include
-  // default values.
-  if (!mIncludeDefAttrValue) {
-    if (!mDefStyleDecl)
-      return PR_FALSE;
-
-    nsAutoString defValue;
-    mDefStyleDecl->GetPropertyValue(cssName, defValue);
-    if (defValue == aValue)
-      return PR_FALSE;
-  }
+  return PR_TRUE;
+}
 
-  // Don't expose text attribute if its required specific CSS value isn't
-  // matched with the CSS value we got.
-  const char *cssValue = gCSSTextAttrsMap[mIndex].mCSSValue;
-  if (cssValue != kAnyValue && !aValue.EqualsASCII(cssValue))
-    return PR_FALSE;
-
-  // Get the name of text attribute.
-  if (gCSSTextAttrsMap[mIndex].mAttrName != kCopyName)
-    aName = gCSSTextAttrsMap[mIndex].mAttrName;
-  else
-    aName = gCSSTextAttrsMap[mIndex].mCSSName;
-
-  // Get the value of text attribute.
+void
+nsCSSTextAttr::Format(const nsAutoString& aValue, nsAString& aFormattedValue)
+{
   const char *attrValue = gCSSTextAttrsMap[mIndex].mAttrValue;
   if (attrValue != kCopyValue)
-    AppendASCIItoUTF16(attrValue, aValue);
-
-  return PR_TRUE;
+    AppendASCIItoUTF16(attrValue, aFormattedValue);
+  else
+    aFormattedValue = aValue;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsBackgroundTextAttr
 
-nsBackgroundTextAttr::nsBackgroundTextAttr(nsIFrame *aFrame,
-                                           nsIFrame *aRootFrame) :
-  mFrame(aFrame), mRootFrame(aRootFrame)
+nsBGColorTextAttr::nsBGColorTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame) :
+  nsTextAttr<nscolor>::nsTextAttr(aFrame == nsnull), mRootFrame(aRootFrame)
 {
+  mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue);
+  if (aFrame)
+    mIsDefined = GetColor(aFrame, &mNativeValue);
 }
 
 PRBool
-nsBackgroundTextAttr::Equal(nsIDOMElement *aElm)
+nsBGColorTextAttr::GetValueFor(nsIDOMElement *aElm, nscolor *aValue)
 {
   nsIFrame *frame = nsCoreUtils::GetFrameFor(aElm);
   if (!frame)
     return PR_FALSE;
 
-  return GetColor(mFrame) == GetColor(frame);    
+  return GetColor(frame, aValue);
 }
 
-PRBool
-nsBackgroundTextAttr::Get(nsAString& aValue)
+void
+nsBGColorTextAttr::Format(const nscolor& aValue, nsAString& aFormattedValue)
 {
-  // Do not expose "background-color" text attribute if its value is matched
-  // with the default value.
-  nscolor color = GetColor(mFrame);
-  if (mRootFrame && color == GetColor(mRootFrame))
-    return PR_FALSE;
-
   // Combine the string like rgb(R, G, B) from nscolor.
   nsAutoString value;
   value.AppendLiteral("rgb(");
-  value.AppendInt(NS_GET_R(color));
+  value.AppendInt(NS_GET_R(aValue));
   value.AppendLiteral(", ");
-  value.AppendInt(NS_GET_G(color));
+  value.AppendInt(NS_GET_G(aValue));
   value.AppendLiteral(", ");
-  value.AppendInt(NS_GET_B(color));
+  value.AppendInt(NS_GET_B(aValue));
   value.Append(')');
 
-  aValue = value;
-  return PR_TRUE;
+  aFormattedValue = value;
 }
 
-nscolor
-nsBackgroundTextAttr::GetColor(nsIFrame *aFrame)
+PRBool
+nsBGColorTextAttr::GetColor(nsIFrame *aFrame, nscolor *aColor)
 {
   const nsStyleBackground *styleBackground = aFrame->GetStyleBackground();
 
-  if (!styleBackground->IsTransparent())
-    return styleBackground->mBackgroundColor;
+  if (!styleBackground->IsTransparent()) {
+    *aColor = styleBackground->mBackgroundColor;
+    return PR_TRUE;
+  }
 
   nsIFrame *parentFrame = aFrame->GetParent();
-  if (!parentFrame)
-    return aFrame->PresContext()->DefaultBackgroundColor();
+  if (!parentFrame) {
+    *aColor = aFrame->PresContext()->DefaultBackgroundColor();
+    return PR_TRUE;
+  }
 
   // Each frame of parents chain for the initially passed 'aFrame' has
   // transparent background color. So background color isn't changed from
   // 'mRootFrame' to initially passed 'aFrame'.
   if (parentFrame == mRootFrame)
-    return GetColor(mRootFrame);
+    return PR_FALSE;
 
-  return GetColor(parentFrame);
+  return GetColor(parentFrame, aColor);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsFontSizeTextAttr
 
-nsFontSizeTextAttr::nsFontSizeTextAttr(nsIFrame *aFrame,
-                                           nsIFrame *aRootFrame) :
-  mFrame(aFrame), mRootFrame(aRootFrame)
+nsFontSizeTextAttr::nsFontSizeTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame) :
+  nsTextAttr<nscoord>::nsTextAttr(aFrame == nsnull)
 {
+  mDC = aRootFrame->PresContext()->DeviceContext();
+
+  mRootNativeValue = GetFontSize(aRootFrame);
+  mIsRootDefined = PR_TRUE;
+
+  if (aFrame) {
+    mNativeValue = GetFontSize(aFrame);
+    mIsDefined = PR_TRUE;
+  }
 }
 
 PRBool
-nsFontSizeTextAttr::Equal(nsIDOMElement *aElm)
+nsFontSizeTextAttr::GetValueFor(nsIDOMElement *aElm, nscoord *aValue)
 {
   nsIFrame *frame = nsCoreUtils::GetFrameFor(aElm);
   if (!frame)
     return PR_FALSE;
-
-  return GetFontSize(mFrame) == GetFontSize(frame);    
+  
+  *aValue = GetFontSize(frame);
+  return PR_TRUE;
 }
 
-
-PRBool
-nsFontSizeTextAttr::Get(nsAString& aValue)
+void
+nsFontSizeTextAttr::Format(const nscoord& aValue, nsAString& aFormattedValue)
 {
-  // Do not expose "font-size" text attribute if its value is the same
-  // as the default value.
-  nscoord fontsize = GetFontSize(mFrame);
-  if (mRootFrame && fontsize == GetFontSize(mRootFrame))
-    return PR_FALSE;
-
   // Convert from nscoord to pt.
   //
   // Note: according to IA2, "The conversion doesn't have to be exact.
   // The intent is to give the user a feel for the size of the text."
   // 
   // ATK does not specify a unit and will likely follow IA2 here.
   //
   // XXX todo: consider sharing this code with layout module? (bug 474621)
-  nsIDeviceContext *dc = mFrame->PresContext()->DeviceContext();
-  float inches = static_cast<float>(GetFontSize(mFrame)) /
-                        static_cast<float>(dc->AppUnitsPerInch());
-  int pts = inches * 72 + .5; // 72 pts per inch
-  
+  float inches = static_cast<float>(aValue) /
+    static_cast<float>(mDC->AppUnitsPerInch());
+  int pts = static_cast<int>(inches * 72 + .5); // 72 pts per inch
+
   nsAutoString value;
   value.AppendInt(pts);
   value.Append(NS_LITERAL_STRING("pt"));
-  aValue = value;
-
-  return PR_TRUE;
+  aFormattedValue = value;
 }
 
 nscoord
 nsFontSizeTextAttr::GetFontSize(nsIFrame *aFrame)
 {
   nsStyleFont* styleFont =
     (nsStyleFont*)(aFrame->GetStyleDataExternal(eStyleStruct_Font));
 
rename from accessible/src/base/nsTextUtils.h
rename to accessible/src/base/nsTextAttrs.h
--- a/accessible/src/base/nsTextUtils.h
+++ b/accessible/src/base/nsTextAttrs.h
@@ -31,160 +31,341 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#ifndef nsTextUtils_h_
-#define nsTextUtils_h_
+#ifndef nsTextAttrs_h_
+#define nsTextAttrs_h_
 
+class nsHyperTextAccessible;
+
+#include "nsAccessibilityAtoms.h"
+
+#include "nsIDOMNode.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 
 #include "nsIContent.h"
 #include "nsIFrame.h"
+#include "nsIPersistentProperties2.h"
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
+#include "nsTPtrArray.h"
+
+class nsITextAttr;
 
 /**
- * Base class to work with text attributes. See derived classes below.
+ * Used to expose text attributes for the hyper text accessible (see
+ * nsHyperTextAccessible class). It is indended for the work with 'language' and
+ * CSS based text attributes.
+ *
+ * @note "invalid: spelling" text attrbiute is implemented entirerly in
+ *       nsHyperTextAccessible class.
  */
-class nsTextAttr
+class nsTextAttrsMgr
 {
 public:
   /**
-   * Return true if the text attribute for the given element equals with
-   * predefined attribute.
+   * Constructor. If instance of the class is intended to expose default text
+   * attributes then 'aIncludeDefAttrs' and 'oOffsetNode' argument must be
+   * skiped.
+   *
+   * @param aHyperTextAcc    hyper text accessible text attributes are
+   *                         calculated for
+   * @param aHyperTextNode   DOM node of the given hyper text accessbile
+   * @param aIncludeDefAttrs [optional] indicates whether default text
+   *                         attributes should be included into list of exposed
+   *                         text attributes.
+   * @param oOffsetNode      [optional] DOM node represents hyper text offset
+   *                         inside hyper text accessible
+   */
+  nsTextAttrsMgr(nsHyperTextAccessible *aHyperTextAcc,
+                 nsIDOMNode *aHyperTextNode,
+                 PRBool aIncludeDefAttrs = PR_TRUE,
+                 nsIDOMNode *oOffsetNode = nsnull);
+
+  /*
+   * Return text attributes and hyper text offsets where these attributes are
+   * applied. Offsets are calculated in the case of non default attributes.
+   *
+   * @note In the case of default attributes pointers on hyper text offsets
+   *       must be skiped.
+   *
+   * @param aAttributes    [in, out] text attributes list
+   * @param aStartHTOffset [out, optional] start hyper text offset
+   * @param aEndHTOffset   [out, optional] end hyper text offset
+   */
+  nsresult GetAttributes(nsIPersistentProperties *aAttributes,
+                         PRInt32 *aStartHTOffset = nsnull,
+                         PRInt32 *aEndHTOffset = nsnull);
+
+protected:
+
+  /**
+   * Calculates range (start and end offsets) of text where the text attributes
+   * are stretched. New offsets may be smaller if one of text attributes changes
+   * its value before or after the given offsets.
+   *
+   * @param aTextAttrArray  [in] text attributes array
+   * @param aStartHTOffset  [in, out] the start offset
+   * @param aEndHTOffset    [in, out] the end offset
+   */
+   nsresult GetRange(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
+                     PRInt32 *aStartHTOffset, PRInt32 *aEndHTOffset);
+
+  /*
+   * Find new end offset for text attributes navigating through the tree. New
+   * end offset may be smaller if one of text attributes changes its value
+   * before the given end offset.
+   *
+   * @param  aTextAttrArray  [in] text attributes array
+   * @param  aCurrNode       [in] the first node of the tree
+   * @param  aHTOffset       [in, out] the end offset
+   * @return                 true if the end offset has been changed
+   */
+   PRBool FindEndOffsetInSubtree(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
+                                 nsIDOMNode *aCurrNode, PRInt32 *aHTOffset);
+
+  /*
+   * Find the start offset for text attributes navigating through the tree. New
+   * start offset may be bigger if one of text attributes changes its value
+   * after the given start offset.
+   *
+   * @param  aTextAttrArray  [in] text attributes array
+   * @param  aCurrNode       [in] the node navigating through thee thee is
+   *                         started from
+   * @param  aPrevNode       [in] the previous node placed before the start node
+   * @param  aHTOffset       [in, out] the start offset
+   * @return                 true if the start offset has been changed
+   */
+   PRBool FindStartOffsetInSubtree(const nsTPtrArray<nsITextAttr>& aTextAttrArray,
+                                   nsIDOMNode *aCurrNode, nsIDOMNode *aPrevNode,
+                                   PRInt32 *aHTOffset);
+
+private:
+  nsRefPtr<nsHyperTextAccessible> mHyperTextAcc;
+  nsCOMPtr<nsIDOMNode> mHyperTextNode;
+
+  PRBool mIncludeDefAttrs;
+  nsCOMPtr<nsIDOMNode> mOffsetNode;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Private implementation details
+
+/**
+ * Interface class of text attribute class implementations.
+ */
+class nsITextAttr
+{
+public:
+  /**
+   * Return the name of text attribute.
+   */
+  virtual nsIAtom* GetName() = 0;
+
+  /**
+   * Retrieve the value of text attribute in out param, return true if differs
+   * from the default value of text attribute or if include default attribute
+   * value flag is setted.
+   * 
+   * @param aValue                [in, out] the value of text attribute
+   * @param aIncludeDefAttrValue  [in] include default attribute value flag
+   * @return                      true if text attribute value differs from
+   *                              default or include default attribute value
+   *                              flag is applied
+   */
+  virtual PRBool GetValue(nsAString& aValue, PRBool aIncludeDefAttrValue) = 0;
+
+  /**
+   * Return true if the text attribute value on the given element equals with
+   * predefined attribute value.
    */
   virtual PRBool Equal(nsIDOMElement *aElm) = 0;
 };
 
+
 /**
- * Class is used for the work with 'lang' text attributes. Used in
- * nsHyperTextAccessible.
+ * Base class to work with text attributes. See derived classes below.
  */
-class nsLangTextAttr : public nsTextAttr
+template<class T>
+class nsTextAttr : public nsITextAttr
 {
 public:
-  nsLangTextAttr(nsAString& aLang, nsIContent *aRootContent) :
-    mLang(aLang), mRootContent(aRootContent) { }
+  nsTextAttr(PRBool aGetRootValue) : mGetRootValue(aGetRootValue) {}
+
+  // nsITextAttr
+  virtual PRBool GetValue(nsAString& aValue, PRBool aIncludeDefAttrValue)
+  {
+    if (mGetRootValue) {
+      Format(mRootNativeValue, aValue);
+      return mIsRootDefined;
+    }
+
+    PRBool isDefined = mIsDefined;
+    T* nativeValue = &mNativeValue;
+
+    if (!isDefined) {
+      if (aIncludeDefAttrValue) {
+        isDefined = mIsRootDefined;
+        nativeValue = &mRootNativeValue;
+      }
+    } else if (!aIncludeDefAttrValue) {
+      isDefined = mRootNativeValue != mNativeValue;
+    }
+
+    if (!isDefined)
+      return PR_FALSE;
+
+    Format(*nativeValue, aValue);
+    return PR_TRUE;
+  }
+
+  virtual PRBool Equal(nsIDOMElement *aElm)
+  {
+    T nativeValue;
+    PRBool isDefined = GetValueFor(aElm, &nativeValue);
+
+    if (!mIsDefined && !isDefined)
+      return PR_TRUE;
+
+    if (mIsDefined && isDefined)
+      return nativeValue == mNativeValue;
+
+    if (mIsDefined)
+      return mNativeValue == mRootNativeValue;
 
-  virtual PRBool Equal(nsIDOMElement *aElm);
+    return nativeValue == mRootNativeValue;
+  }
+
+protected:
+
+  // Return native value for the given DOM element.
+  virtual PRBool GetValueFor(nsIDOMElement *aElm, T *aValue) = 0;
+
+  // Format native value to text attribute value.
+  virtual void Format(const T& aValue, nsAString& aFormattedValue) = 0;
+
+  // Indicates if root value should be exposed.
+  PRBool mGetRootValue;
+
+  // Native value and flag indicating if the value is defined (initialized in
+  // derived classes). Note, undefined native value means it is inherited
+  // from root.
+  T mNativeValue;
+  PRBool mIsDefined;
+
+  // Native root value and flag indicating if the value is defined  (initialized
+  // in derived classes).
+  T mRootNativeValue;
+  PRBool mIsRootDefined;
+};
+
+
+/**
+ * Class is used for the work with 'language' text attribute in nsTextAttrsMgr
+ * class.
+ */
+class nsLangTextAttr : public nsTextAttr<nsAutoString>
+{
+public:
+  nsLangTextAttr(nsHyperTextAccessible *aRootAcc, nsIDOMNode *aRootNode,
+                 nsIDOMNode *aNode);
+
+  // nsITextAttr
+  virtual nsIAtom *GetName() { return nsAccessibilityAtoms::language; }
+
+protected:
+
+  // nsTextAttr
+  virtual PRBool GetValueFor(nsIDOMElement *aElm, nsAutoString *aValue);
+  virtual void Format(const nsAutoString& aValue, nsAString& aFormattedValue);
 
 private:
-  nsString mLang;
+  PRBool GetLang(nsIContent *aContent, nsAString& aLang);
   nsCOMPtr<nsIContent> mRootContent;
 };
 
+
 /**
- * Class is used for the work with CSS based text attributes. Used in
- * nsHyperTextAccessible.
+ * Class is used for the work with CSS based text attributes in nsTextAttrsMgr
+ * class.
  */
-class nsCSSTextAttr : public nsTextAttr
+class nsCSSTextAttr : public nsTextAttr<nsAutoString>
 {
 public:
-  nsCSSTextAttr(PRBool aIncludeDefAttrValue, nsIDOMElement *aElm,
-                nsIDOMElement *aRootElm);
+  nsCSSTextAttr(PRUint32 aIndex, nsIDOMElement *aRootElm, nsIDOMElement *aElm);
+
+  // nsITextAttr
+  virtual nsIAtom *GetName();
+
+protected:
 
   // nsTextAttr
-  virtual PRBool Equal(nsIDOMElement *aElm);
-
-  // nsCSSTextAttr
-  /**
-   * Interates through attributes.
-   */
-  PRBool Iterate();
-
-  /**
-   * Get name and value of attribute.
-   */
-  PRBool Get(nsACString& aName, nsAString& aValue);
+  virtual PRBool GetValueFor(nsIDOMElement *aElm, nsAutoString *aValue);
+  virtual void Format(const nsAutoString& aValue, nsAString& aFormattedValue);
 
 private:
   PRInt32 mIndex;
-  PRBool mIncludeDefAttrValue;
-
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> mStyleDecl;
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> mDefStyleDecl;
 };
 
+
 /**
- * Class is used for the work with "background-color" text attribute. It is
- * used in nsHyperTextAccessible.
+ * Class is used for the work with 'background-color' text attribute in
+ * nsTextAttrsMgr class.
  */
-class nsBackgroundTextAttr : public nsTextAttr
+class nsBGColorTextAttr : public nsTextAttr<nscolor>
 {
 public:
-  nsBackgroundTextAttr(nsIFrame *aFrame, nsIFrame *aRootFrame);
-  
-  // nsTextAttr
-  virtual PRBool Equal(nsIDOMElement *aElm);
+  nsBGColorTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame);
 
-  // nsBackgroundTextAttr
+  // nsITextAttr
+  virtual nsIAtom *GetName() { return nsAccessibilityAtoms::backgroundColor; }
 
-  /**
-   * Retrieve the "background-color" in out param, return true if differs from
-   * the default background-color.
-   * 
-   * @param aValue      [out] the background color in pts
-   * @return            true if background color differs from default
-   */
-  virtual PRBool Get(nsAString& aValue);
+protected:
+  // nsTextAttr
+  virtual PRBool GetValueFor(nsIDOMElement *aElm, nscolor *aValue);
+  virtual void Format(const nscolor& aValue, nsAString& aFormattedValue);
 
 private:
-  /**
-   * Return background color for the given frame.
-   *
-   * @note  If background color for the given frame is transparent then walk
-   *        trhough the frame parents chain until we'll got either a frame with
-   *        not transparent background color or the given root frame. In the
-   *        last case return background color for the root frame.
-   *
-   * @param aFrame      [in] the given frame to calculate background-color
-   * @return            background color
-   */
-  nscolor GetColor(nsIFrame *aFrame);
-
-  nsIFrame *mFrame;
+  PRBool GetColor(nsIFrame *aFrame, nscolor *aColor);
   nsIFrame *mRootFrame;
 };
 
+
 /**
- * Class is used for the work with "font-size" text attribute. It is
- * used in nsHyperTextAccessible.
+ * Class is used for the work with "font-size" text attribute in nsTextAttrsMgr
+ * class.
  */
-class nsFontSizeTextAttr : public nsTextAttr
+class nsFontSizeTextAttr : public nsTextAttr<nscoord>
 {
 public:
-  nsFontSizeTextAttr(nsIFrame *aFrame, nsIFrame *aRootFrame);
-  
-  // nsTextAttr
-  virtual PRBool Equal(nsIDOMElement *aElm);
+  nsFontSizeTextAttr(nsIFrame *aRootFrame, nsIFrame *aFrame);
 
-  // nsFontSizeTextAttr
+  // nsITextAttr
+  virtual nsIAtom *GetName() { return nsAccessibilityAtoms::fontSize; }
 
-  /**
-   * Retrieve the "font-size" in out param, return true if differs from
-   * the default font-size.
-   * 
-   * @param aValue      [out] the font size in pts
-   * @return            true if font size differs from default
-   */
-  virtual PRBool Get(nsAString& aValue);
+protected:
+
+  // nsTextAttr
+  virtual PRBool GetValueFor(nsIDOMElement *aElm, nscoord *aValue);
+  virtual void Format(const nscoord& aValue, nsAString& aFormattedValue);
 
 private:
+
   /**
    * Return font size for the given frame.
    *
    * @param aFrame      [in] the given frame to query font-size
    * @return            font size
    */
    nscoord GetFontSize(nsIFrame *aFrame);
 
-  nsIFrame *mFrame;
-  nsIFrame *mRootFrame;
+  nsIDeviceContext *mDC;
 };
 
 #endif
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -36,17 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHyperTextAccessible.h"
 #include "nsAccessibilityAtoms.h"
 #include "nsAccessibilityService.h"
 #include "nsAccessibleTreeWalker.h"
-#include "nsTextUtils.h"
+#include "nsTextAttrs.h"
 
 #include "nsIClipboard.h"
 #include "nsContentCID.h"
 #include "nsIDOMAbstractView.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsPIDOMWindow.h"        
 #include "nsIDOMDocumentView.h"
@@ -1169,27 +1169,18 @@ nsHyperTextAccessible::GetTextAttributes
 
   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
   if (content && content->IsNodeOfType(nsINode::eELEMENT))
     node = do_QueryInterface(content->GetChildAt(nodeOffset));
 
   if (!node)
     return NS_OK;
 
-  // Set 'lang' text attribute.
-  rv =  GetLangTextAttributes(aIncludeDefAttrs, node,
-                              aStartOffset, aEndOffset,
-                              aAttributes ? *aAttributes : nsnull);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Set CSS based text attributes.
-  rv = GetCSSTextAttributes(aIncludeDefAttrs, node,
-                            aStartOffset, aEndOffset,
-                            aAttributes ? *aAttributes : nsnull);
-  return rv;
+  nsTextAttrsMgr textAttrsMgr(this, mDOMNode, aIncludeDefAttrs, node);
+  return textAttrsMgr.GetAttributes(*aAttributes, aStartOffset, aEndOffset);
 }
 
 // nsIPersistentProperties
 // nsIAccessibleText::defaultTextAttributes
 NS_IMETHODIMP
 nsHyperTextAccessible::GetDefaultTextAttributes(nsIPersistentProperties **aAttributes)
 {
   NS_ENSURE_ARG_POINTER(aAttributes);
@@ -1199,45 +1190,18 @@ nsHyperTextAccessible::GetDefaultTextAtt
     do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
   NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(*aAttributes = attributes);
 
   if (!mDOMNode)
     return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIDOMElement> element = nsCoreUtils::GetDOMElementFor(mDOMNode);
-
-  nsCSSTextAttr textAttr(PR_TRUE, element, nsnull);
-  while (textAttr.Iterate()) {
-    nsCAutoString name;
-    nsAutoString value, oldValue;
-    if (textAttr.Get(name, value))
-      attributes->SetStringProperty(name, value, oldValue);
-  }
-  
-  nsIFrame *sourceFrame = nsCoreUtils::GetFrameFor(element);
-  NS_ENSURE_STATE(sourceFrame);
-
-  // set font size
-  nsAutoString value;
-  nsFontSizeTextAttr fontSizeTextAttr(sourceFrame, nsnull);
-  fontSizeTextAttr.Get(value);
-  nsAccUtils::SetAccAttr(attributes,
-                         nsAccessibilityAtoms::fontSize, value);
-  
-  value.Truncate();
-  
-  // set font background color
-  nsBackgroundTextAttr backgroundTextAttr(sourceFrame, nsnull);
-  backgroundTextAttr.Get(value);
-  nsAccUtils::SetAccAttr(attributes,
-                         nsAccessibilityAtoms::backgroundColor, value);
-
-  return NS_OK;
+  nsTextAttrsMgr textAttrsMgr(this, mDOMNode, PR_TRUE, nsnull);
+  return textAttrsMgr.GetAttributes(*aAttributes);
 }
 
 nsresult
 nsHyperTextAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
 {
   if (!mDOMNode) {
     return NS_ERROR_FAILURE;  // Node already shut down
   }
@@ -2315,280 +2279,8 @@ nsHyperTextAccessible::GetSpellTextAttri
       }
 
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
-
-// nsHyperTextAccessible
-nsresult
-nsHyperTextAccessible::GetLangTextAttributes(PRBool aIncludeDefAttrs,
-                                             nsIDOMNode *aSourceNode,
-                                             PRInt32 *aStartHTOffset,
-                                             PRInt32 *aEndHTOffset,
-                                             nsIPersistentProperties *aAttributes)
-{
-  nsCOMPtr<nsIDOMElement> sourceElm(nsCoreUtils::GetDOMElementFor(aSourceNode));
-
-  nsCOMPtr<nsIContent> content(do_QueryInterface(sourceElm));
-  nsCOMPtr<nsIContent> rootContent(do_QueryInterface(mDOMNode));
-
-  nsAutoString lang;
-  nsCoreUtils::GetLanguageFor(content, rootContent, lang);
-
-  nsAutoString rootLang;
-  nsresult rv = GetLanguage(rootLang);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (aAttributes) {
-    // Expose 'language' text attribute if the DOM 'lang' attribute is
-    // presented and it's different from the 'lang' attribute on the root
-    // element or we should include default values of text attribute.
-    const nsAString& resultLang = lang.IsEmpty() ? rootLang : lang;
-    if (!resultLang.IsEmpty() && (aIncludeDefAttrs || lang != rootLang))
-      nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::language,
-                             resultLang);
-  }
-
-  nsLangTextAttr textAttr(lang, rootContent);
-  return GetRangeForTextAttr(aSourceNode, &textAttr,
-                             aStartHTOffset, aEndHTOffset);
-}
-
-// nsHyperTextAccessible
-nsresult
-nsHyperTextAccessible::GetCSSTextAttributes(PRBool aIncludeDefAttrs,
-                                            nsIDOMNode *aSourceNode,
-                                            PRInt32 *aStartHTOffset,
-                                            PRInt32 *aEndHTOffset,
-                                            nsIPersistentProperties *aAttributes)
-{
-  nsCOMPtr<nsIDOMElement> sourceElm(nsCoreUtils::GetDOMElementFor(aSourceNode));
-  nsCOMPtr<nsIDOMElement> rootElm(nsCoreUtils::GetDOMElementFor(mDOMNode));
-
-  nsCSSTextAttr textAttr(aIncludeDefAttrs, sourceElm, rootElm);
-  while (textAttr.Iterate()) {
-    nsCAutoString name;
-    nsAutoString value, oldValue;
-    if (aAttributes && textAttr.Get(name, value))
-      aAttributes->SetStringProperty(name, value, oldValue);
-
-    nsresult rv = GetRangeForTextAttr(aSourceNode, &textAttr,
-                                      aStartHTOffset, aEndHTOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  nsIFrame *sourceFrame = nsCoreUtils::GetFrameFor(sourceElm);
-  if (sourceFrame) {
-    nsIFrame *rootFrame = nsnull;
-
-    if (!aIncludeDefAttrs)
-      rootFrame = nsCoreUtils::GetFrameFor(rootElm);
-
-    nsFontSizeTextAttr fontSizeTextAttr(sourceFrame, rootFrame);
-    nsAutoString value;
-    if (fontSizeTextAttr.Get(value)) {
-      nsAccUtils::SetAccAttr(aAttributes,
-                             nsAccessibilityAtoms::fontSize, value);
-    }
-
-    nsBackgroundTextAttr backgroundTextAttr(sourceFrame, rootFrame);
-    value.Truncate();
-    if (backgroundTextAttr.Get(value)) {
-      nsAccUtils::SetAccAttr(aAttributes,
-                             nsAccessibilityAtoms::backgroundColor, value);
-    }
-
-    nsresult rv = GetRangeForTextAttr(aSourceNode, &backgroundTextAttr,
-                                      aStartHTOffset, aEndHTOffset);
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-// nsHyperTextAccessible
-nsresult
-nsHyperTextAccessible::GetRangeForTextAttr(nsIDOMNode *aNode,
-                                           nsTextAttr *aComparer,
-                                           PRInt32 *aStartHTOffset,
-                                           PRInt32 *aEndHTOffset)
-{
-  nsCOMPtr<nsIDOMElement> rootElm(nsCoreUtils::GetDOMElementFor(mDOMNode));
-  NS_ENSURE_STATE(rootElm);
-
-  nsCOMPtr<nsIDOMNode> tmpNode(aNode);
-  nsCOMPtr<nsIDOMNode> currNode(aNode);
-
-  // Navigate backwards and forwards from current node to the root node to
-  // calculate range bounds for the text attribute. Navigation sequence is the
-  // following:
-  // 1. Navigate through the siblings.
-  // 2. If the traversed sibling has children then navigate from its leaf child
-  //    to it through whole tree of the traversed sibling.
-  // 3. Get the parent and cycle algorithm until the root node.
-
-  // Navigate backwards (find the start offset).
-  while (currNode && currNode != rootElm) {
-    nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(currNode));
-    NS_ENSURE_STATE(currElm);
-
-    if (currNode != aNode && !aComparer->Equal(currElm)) {
-      PRInt32 startHTOffset = 0;
-      nsCOMPtr<nsIAccessible> startAcc;
-      nsresult rv = DOMPointToHypertextOffset(tmpNode, -1, &startHTOffset,
-                                              getter_AddRefs(startAcc));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      if (!startAcc)
-        startHTOffset = 0;
-
-      if (startHTOffset > *aStartHTOffset)
-        *aStartHTOffset = startHTOffset;
-
-      break;
-    }
-
-    currNode->GetPreviousSibling(getter_AddRefs(tmpNode));
-    if (tmpNode) {
-      // Navigate through the subtree of traversed children to calculate
-      // left bound of the range.
-      FindStartOffsetInSubtree(tmpNode, currNode, aComparer, aStartHTOffset);
-    }
-
-    currNode->GetParentNode(getter_AddRefs(tmpNode));
-    currNode.swap(tmpNode);
-  }
-
-  // Navigate forwards (find the end offset).
-  PRBool moveIntoSubtree = PR_TRUE;
-  currNode = aNode;
-  while (currNode && currNode != rootElm) {
-    nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(currNode));
-    NS_ENSURE_STATE(currElm);
-
-    // Stop new end offset searching if the given text attribute changes its
-    // value.
-    if (!aComparer->Equal(currElm)) {
-      PRInt32 endHTOffset = 0;
-      nsresult rv = DOMPointToHypertextOffset(currNode, -1, &endHTOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      if (endHTOffset < *aEndHTOffset)
-        *aEndHTOffset = endHTOffset;
-
-      break;
-    }
-
-    if (moveIntoSubtree) {
-      // Navigate through subtree of traversed node. We use 'moveIntoSubtree'
-      // flag to avoid traversing the same subtree twice.
-      currNode->GetFirstChild(getter_AddRefs(tmpNode));
-      if (tmpNode)
-        FindEndOffsetInSubtree(tmpNode, aComparer, aEndHTOffset);
-    }
-
-    currNode->GetNextSibling(getter_AddRefs(tmpNode));
-    moveIntoSubtree = PR_TRUE;
-    if (!tmpNode) {
-      currNode->GetParentNode(getter_AddRefs(tmpNode));
-      moveIntoSubtree = PR_FALSE;
-    }
-
-    currNode.swap(tmpNode);
-  }
-
-  return NS_OK;
-}
-
-
-PRBool
-nsHyperTextAccessible::FindEndOffsetInSubtree(nsIDOMNode *aCurrNode,
-                                              nsTextAttr *aComparer,
-                                              PRInt32 *aHTOffset)
-{
-  if (!aCurrNode)
-    return PR_FALSE;
-
-  nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode));
-  NS_ENSURE_STATE(currElm);
-
-  // If the given text attribute (pointed by nsTextAttr object) changes its
-  // value on the traversed element then fit the end of range.
-  if (!aComparer->Equal(currElm)) {
-    PRInt32 endHTOffset = 0;
-    nsresult rv = DOMPointToHypertextOffset(aCurrNode, -1, &endHTOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (endHTOffset < *aHTOffset)
-      *aHTOffset = endHTOffset;
-
-    return PR_TRUE;
-  }
-
-  // Deeply traverse into the tree to fit the end of range.
-  nsCOMPtr<nsIDOMNode> nextNode;
-  aCurrNode->GetFirstChild(getter_AddRefs(nextNode));
-  if (nextNode) {
-    PRBool res = FindEndOffsetInSubtree(nextNode, aComparer, aHTOffset);
-    if (res)
-      return res;
-  }
-
-  aCurrNode->GetNextSibling(getter_AddRefs(nextNode));
-  if (nextNode) {
-    if (FindEndOffsetInSubtree(nextNode, aComparer, aHTOffset))
-      return PR_TRUE;
-  }
-
-  return PR_FALSE;
-}
-
-PRBool
-nsHyperTextAccessible::FindStartOffsetInSubtree(nsIDOMNode *aCurrNode,
-                                                nsIDOMNode *aPrevNode,
-                                                nsTextAttr *aComparer,
-                                                PRInt32 *aHTOffset)
-{
-  if (!aCurrNode)
-    return PR_FALSE;
-
-  // Find the closest element back to the traversed element.
-  nsCOMPtr<nsIDOMNode> nextNode;
-  aCurrNode->GetLastChild(getter_AddRefs(nextNode));
-  if (nextNode) {
-    if (FindStartOffsetInSubtree(nextNode, aPrevNode, aComparer, aHTOffset))
-      return PR_TRUE;
-  }
-
-  nsCOMPtr<nsIDOMElement> currElm(nsCoreUtils::GetDOMElementFor(aCurrNode));
-  NS_ENSURE_STATE(currElm);
-
-  // If the given text attribute (pointed by nsTextAttr object) changes its
-  // value on the traversed element then fit the start of range.
-  if (!aComparer->Equal(currElm)) {
-    PRInt32 startHTOffset = 0;
-    nsCOMPtr<nsIAccessible> startAcc;
-    nsresult rv = DOMPointToHypertextOffset(aPrevNode, -1, &startHTOffset,
-                                            getter_AddRefs(startAcc));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!startAcc)
-      startHTOffset = 0;
-
-    if (startHTOffset > *aHTOffset)
-      *aHTOffset = startHTOffset;
-
-    return PR_TRUE;
-  }
-
-  // Moving backwards to find the start of range.
-  aCurrNode->GetPreviousSibling(getter_AddRefs(nextNode));
-  if (nextNode) {
-    if (FindStartOffsetInSubtree(nextNode, aCurrNode, aComparer, aHTOffset))
-      return PR_TRUE;
-  }
-
-  return PR_FALSE;
-}
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -40,17 +40,17 @@
 #ifndef _nsHyperTextAccessible_H_
 #define _nsHyperTextAccessible_H_
 
 #include "nsAccessibleWrap.h"
 #include "nsIAccessibleText.h"
 #include "nsIAccessibleHyperText.h"
 #include "nsIAccessibleEditableText.h"
 #include "nsAccessibleEventData.h"
-#include "nsTextUtils.h"
+#include "nsTextAttrs.h"
 
 #include "nsFrameSelection.h"
 #include "nsISelectionController.h"
 
 enum EGetTextType { eGetBefore=-1, eGetAt=0, eGetAfter=1 };
 
 // This character marks where in the text returned via nsIAccessibleText(),
 // that embedded object characters exist
@@ -288,97 +288,15 @@ protected:
    * @param aStartOffset      [in, out] the start offset
    * @param aEndOffset        [in, out] the end offset
    * @param aAttributes       [out, optional] result attributes
    */
   nsresult GetSpellTextAttribute(nsIDOMNode *aNode, PRInt32 aNodeOffset,
                                  PRInt32 *aStartOffset,
                                  PRInt32 *aEndOffset,
                                  nsIPersistentProperties *aAttributes);
-
-  /**
-   * Set 'lang' text attribute and return range offsets where attibute is
-   * stretched. The method is used by GetTextAttributes() method.
-   *
-   * @param aIncludeDefAttrs  [in] points whether text attributes having default
-   *                          values of attributes should be included
-   * @param aSourceNode       [in] the node we start to traverse from
-   * @param aStartOffset      [in, out] the start offset
-   * @param aEndOffset        [in, out] the end offset
-   * @param aAttributes       [out, optional] result attributes
-   */
-  nsresult GetLangTextAttributes(PRBool aIncludeDefAttrs,
-                                 nsIDOMNode *aSourceNode,
-                                 PRInt32 *aStartOffset,
-                                 PRInt32 *aEndOffset,
-                                 nsIPersistentProperties *aAttributes);
-
-  /**
-   * Set CSS based text attribute and return range offsets where attibutes are
-   * stretched. The method is used by GetTextAttributes() method.
-   *
-   * @param aIncludeDefAttrs  [in] points whether text attributes having default
-   *                          values of attributes should be included
-   * @param aSourceNode       [in] the node we start to traverse from
-   * @param aStartOffset      [in, out] the start offset
-   * @param aEndOffset        [in, out] the end offset
-   * @param aAttributes       [out, optional] result attributes
-   */
-  nsresult GetCSSTextAttributes(PRBool aIncludeDefAttrs,
-                                nsIDOMNode *aSourceNode,
-                                PRInt32 *aStartOffset,
-                                PRInt32 *aEndOffset,
-                                nsIPersistentProperties *aAttributes);
-
-  /**
-   * Calculates range (start and end offsets) of text where the text attribute
-   * (pointed by nsTextAttr object) is stretched. New offsets may be smaller if
-   * the given text attribute changes its value before or after the given
-   * offsets.
-   *
-   * @param aNode          [in] the node we start to traverse from
-   * @param aComparer      [in] object used to describe the text attribute
-   * @param aStartHTOffset [in, out] the start offset
-   * @param aEndHTOffset   [in, out] the end offset
-   */
-  nsresult GetRangeForTextAttr(nsIDOMNode *aNode,
-                               nsTextAttr *aComparer,
-                               PRInt32 *aStartHTOffset,
-                               PRInt32 *aEndHTOffset);
-
-  /**
-   * Find new end offset for text attributes navigating through the tree. New
-   * end offset may be smaller if the given text attribute (pointed by
-   * nsTextAttr object) changes its value before the given end offset.
-   *
-   * @param  aCurrNode  [in] the first node of the tree
-   * @param  aComparer  [in] object used to describe the text attribute
-   * @param  aHTOffset  [in, out] the end offset
-   * @return            true if the end offset has been changed
-   */
-  PRBool FindEndOffsetInSubtree(nsIDOMNode *aCurrNode,
-                                nsTextAttr *aComparer,
-                                PRInt32 *aHTOffset);
-
-  /**
-   * Find the start offset for text attributes navigating through the tree. New
-   * start offset may be bigger if the given text attribute (pointed by
-   * nsTextAttr object) changes its value after the given start offset.
-   *
-   * @param  aCurrNode  [in] the node navigating through thee thee is started
-   *                    from
-   * @param  aPrevNode  [in] the previous node placed before the start node
-   * @param  aComparer  [in] object used to describe the text attribute
-   * @param  aHTOffset  [in, out] the start offset
-   * @return            true if the start offset has been changed
-   */
-  PRBool FindStartOffsetInSubtree(nsIDOMNode *aCurrNode,
-                                  nsIDOMNode *aPrevNode,
-                                  nsTextAttr *aComparer,
-                                  PRInt32 *aHTOffset);
-
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsHyperTextAccessible,
                               NS_HYPERTEXTACCESSIBLE_IMPL_CID)
 
 #endif  // _nsHyperTextAccessible_H_
 
--- a/accessible/tests/mochitest/attributes.js
+++ b/accessible/tests/mochitest/attributes.js
@@ -57,51 +57,68 @@ function testGroupAttrs(aAccOrElmOrID, a
 /**
  * Test text attributes.
  *
  * @param aID                   [in] the ID of DOM element having text
  *                              accessible
  * @param aOffset               [in] the offset inside text accessible to fetch
  *                              text attributes
  * @param aAttrs                [in] the map of expected text attributes
- *                              (name/value pairs)
+ *                              (name/value pairs) exposed at the offset
+ * @param aDefAttrs             [in] the map of expected text attributes
+ *                              (name/value pairs) exposed on hyper text
+ *                              accessible
  * @param aStartOffset          [in] expected start offset where text attributes
  *                              are applied
  * @param aEndOffset            [in] expected end offset where text attribute
  *                              are applied
  * @param aSkipUnexpectedAttrs  [in] points the function doesn't fail if
  *                              unexpected attribute is encountered
  */
-function testTextAttrs(aID, aOffset, aAttrs, aStartOffset, aEndOffset,
-                       aSkipUnexpectedAttrs)
+function testTextAttrs(aID, aOffset, aAttrs, aDefAttrs,
+                       aStartOffset, aEndOffset, aSkipUnexpectedAttrs)
 {
   var accessible = getAccessible(aID, [nsIAccessibleText]);
   if (!accessible)
     return;
 
   var startOffset = { value: -1 };
   var endOffset = { value: -1 };
-  var attrs = null;
-  try {
-    attrs = accessible.getTextAttributes(false, aOffset,
-                                         startOffset, endOffset);
-  } catch (e) {
-  }
 
-  if (!attrs) {
-    ok(false, "Can't get text attributes for " + aID);
+  // do not include attributes exposed on hyper text accessbile
+  var attrs = getTextAttributes(aID, accessible, false, aOffset,
+                                startOffset, endOffset);
+
+  if (!attrs)
     return;
-  }
 
   var errorMsg = " for " + aID + " at offset " + aOffset;
 
   is(startOffset.value, aStartOffset, "Wrong start offset" + errorMsg);
   is(endOffset.value, aEndOffset, "Wrong end offset" + errorMsg);
 
   compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs);
+
+  // include attributes exposed on hyper text accessbile
+  var expectedAttrs = {};
+  for (var name in aAttrs)
+    expectedAttrs[name] = aAttrs[name];
+
+  for (var name in aDefAttrs) {
+    if (!(name in expectedAttrs))
+      expectedAttrs[name] = aDefAttrs[name];
+  }
+
+  attrs = getTextAttributes(aID, accessible, true, aOffset,
+                            startOffset, endOffset);
+  
+  if (!attrs)
+    return;
+
+  compareAttrs(errorMsg, attrs, expectedAttrs, aSkipUnexpectedAttrs);
 }
 
 /**
  * Test default text attributes.
  *
  * @param aID                   [in] the ID of DOM element having text
  *                              accessible
  * @param aDefAttrs             [in] the map of expected text attributes
@@ -128,16 +145,35 @@ function testDefaultTextAttrs(aID, aDefA
   
   var errorMsg = ". Getting default text attributes for " + aID;
   compareAttrs(errorMsg, defAttrs, aDefAttrs, aSkipUnexpectedAttrs);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Private.
 
+function getTextAttributes(aID, aAccessible, aIncludeDefAttrs, aOffset,
+                           aStartOffset, aEndOffset)
+{
+  // This function expects the passed in accessible to already be queried for
+  // nsIAccessibleText.
+  var attrs = null;
+  try {
+    attrs = aAccessible.getTextAttributes(aIncludeDefAttrs, aOffset,
+                                          aStartOffset, aEndOffset);
+  } catch (e) {
+  }
+
+  if (attrs)
+    return attrs;
+
+  ok(false, "Can't get text attributes for " + aID);
+  return null;
+}
+
 function compareAttrs(aErrorMsg, aAttrs, aExpectedAttrs, aSkipUnexpectedAttrs)
 {
   var enumerate = aAttrs.enumerate();
   while (enumerate.hasMoreElements()) {
     var prop = enumerate.getNext().QueryInterface(nsIPropertyElement);
 
     if (!(prop.key in aExpectedAttrs)) {
       if (!aSkipUnexpectedAttrs)
--- a/accessible/tests/mochitest/test_textattrs.html
+++ b/accessible/tests/mochitest/test_textattrs.html
@@ -61,20 +61,20 @@
           };
           testDefaultTextAttrs(ID, defAttrs);
 
           var attrs = { };
           var misspelledAttrs = {
             "invalid": "spelling"
           };
 
-          testTextAttrs(ID, 0, attrs, 0, 11);
-          testTextAttrs(ID, 11, misspelledAttrs, 11, 17);
-          testTextAttrs(ID, 17, attrs, 17, 18);
-          testTextAttrs(ID, 18, misspelledAttrs, 18, 22);
+          testTextAttrs(ID, 0, attrs, defAttrs, 0, 11);
+          testTextAttrs(ID, 11, misspelledAttrs, defAttrs, 11, 17);
+          testTextAttrs(ID, 17, attrs, defAttrs, 17, 18);
+          testTextAttrs(ID, 18, misspelledAttrs, defAttrs, 18, 22);
 
           is(gTextAttrChangedEventHandler.eventNumber, 2,
              "Wrong count of 'text attribute changed' events for " + ID);
 
           unregisterA11yEventListener(nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHANGED,
                                       gTextAttrChangedEventHandler);
 
           SimpleTest.finish();
@@ -96,25 +96,25 @@
         "color": gComputedStyle.color,
         "font-family": gComputedStyle.fontFamily,
         "text-position": gComputedStyle.verticalAlign
       };
 
       testDefaultTextAttrs(ID, defAttrs);
 
       var attrs = {};
-      testTextAttrs(ID, 0, attrs, 0, 7);
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
 
       tempElem = tempElem.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"font-weight": gComputedStyle.fontWeight};
-      testTextAttrs(ID, 7, attrs, 7, 11);
+      testTextAttrs(ID, 7, attrs, defAttrs, 7, 11);
 
       attrs = {};
-      testTextAttrs(ID, 12, attrs, 11, 18);
+      testTextAttrs(ID, 12, attrs, defAttrs, 11, 18);
 
       //////////////////////////////////////////////////////////////////////////
       // area2
       ID = "area2";
       tempElem = document.getElementById(ID);
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       defAttrs = {
         "font-style": gComputedStyle.fontStyle,
@@ -124,36 +124,36 @@
         "color": gComputedStyle.color,
         "font-family": gComputedStyle.fontFamily,
         "text-position": gComputedStyle.verticalAlign
       };
 
       testDefaultTextAttrs(ID, defAttrs);
 
       attrs = {};
-      testTextAttrs(ID, 0, attrs, 0, 7);
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
 
       tempElem = tempElem.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"font-weight": gComputedStyle.fontWeight};
-      testTextAttrs(ID, 7, attrs, 7, 12);
+      testTextAttrs(ID, 7, attrs, defAttrs, 7, 12);
 
       tempElem = tempElem.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"font-style": gComputedStyle.fontStyle,
                "font-weight": gComputedStyle.fontWeight};
-      testTextAttrs(ID, 13, attrs, 12, 19);
+      testTextAttrs(ID, 13, attrs, defAttrs, 12, 19);
 
       tempElem = tempElem.parentNode;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"font-weight": "401"};
-      testTextAttrs(ID, 20, attrs, 19, 23);
+      testTextAttrs(ID, 20, attrs, defAttrs, 19, 23);
 
       attrs = {};
-      testTextAttrs(ID, 24, attrs, 23, 30);
+      testTextAttrs(ID, 24, attrs, defAttrs, 23, 30);
 
       //////////////////////////////////////////////////////////////////////////
       // area3
       ID = "area3";
       tempElem = document.getElementById(ID);
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       defAttrs = {
         "font-style": gComputedStyle.fontStyle,
@@ -165,33 +165,33 @@
         "text-position": gComputedStyle.verticalAlign
       };
 
       testDefaultTextAttrs(ID, defAttrs);
 
       tempElem = tempElem.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 0, attrs, 0, 6);
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 6);
 
       tempElem = tempElem.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 6, attrs, 6, 26);
+      testTextAttrs(ID, 6, attrs, defAttrs, 6, 26);
 
       tempElem = tempElem.parentNode;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 26, attrs, 26, 27);
+      testTextAttrs(ID, 26, attrs, defAttrs, 26, 27);
 
       tempElem = tempElem.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color,
                "background-color": gComputedStyle.backgroundColor};
-      testTextAttrs(ID, 27, attrs, 27, 50);
+      testTextAttrs(ID, 27, attrs, defAttrs, 27, 50);
 
       //////////////////////////////////////////////////////////////////////////
       // area4
       ID = "area4";
       tempElem = document.getElementById(ID);
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       defAttrs = {
         "font-style": gComputedStyle.fontStyle,
@@ -203,27 +203,27 @@
         "text-position": gComputedStyle.verticalAlign
       };
 
       testDefaultTextAttrs(ID, defAttrs);
 
       tempElem = tempElem.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 0, attrs, 0, 16);
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 16);
 
       tempElem = tempElem.nextSibling.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 16, attrs, 16, 33);
+      testTextAttrs(ID, 16, attrs, defAttrs, 16, 33);
 
       tempElem = tempElem.parentNode;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 34, attrs, 33, 46);
+      testTextAttrs(ID, 34, attrs, defAttrs, 33, 46);
 
       //////////////////////////////////////////////////////////////////////////
       // area5
       ID = "area5";
       tempElem = document.getElementById(ID);
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       defAttrs = {
         "font-style": gComputedStyle.fontStyle,
@@ -235,28 +235,28 @@
         "text-position": gComputedStyle.verticalAlign
       };
 
       testDefaultTextAttrs(ID, defAttrs);
 
       tempElem = tempElem.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 0, attrs, 0, 5);
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 5);
 
       attrs = {};
-      testTextAttrs(ID, 7, attrs, 5, 8);
+      testTextAttrs(ID, 7, attrs, defAttrs, 5, 8);
 
       tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 9, attrs, 8, 11);
+      testTextAttrs(ID, 9, attrs, defAttrs, 8, 11);
 
       attrs = {};
-      testTextAttrs(ID, 11, attrs, 11, 18);
+      testTextAttrs(ID, 11, attrs, defAttrs, 11, 18);
 
       //////////////////////////////////////////////////////////////////////////
       // area6 (CSS vertical-align property, bug 445938)
       ID = "area6";
       tempElem = document.getElementById(ID);
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       defAttrs = {
         "font-style": gComputedStyle.fontStyle,
@@ -266,105 +266,234 @@
         "color": gComputedStyle.color,
         "font-family": gComputedStyle.fontFamily,
         "text-position": gComputedStyle.verticalAlign
       };
 
       testDefaultTextAttrs(ID, defAttrs);
 
       attrs = {};
-      testTextAttrs(ID, 0, attrs, 0, 5);
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 5);
 
       tempElem = tempElem.firstChild.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"text-position": gComputedStyle.verticalAlign,
                "font-size": "10pt"};
-      testTextAttrs(ID, 5, attrs, 5, 13);
+      testTextAttrs(ID, 5, attrs, defAttrs, 5, 13);
 
       attrs = {};
-      testTextAttrs(ID, 13, attrs, 13, 27);
+      testTextAttrs(ID, 13, attrs, defAttrs, 13, 27);
 
       tempElem = tempElem.nextSibling.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"text-position": gComputedStyle.verticalAlign};
-      testTextAttrs(ID, 27, attrs, 27, 35);
+      testTextAttrs(ID, 27, attrs, defAttrs, 27, 35);
 
       attrs = {};
-      testTextAttrs(ID, 35, attrs, 35, 39);
+      testTextAttrs(ID, 35, attrs, defAttrs, 35, 39);
 
       tempElem = tempElem.nextSibling.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"text-position": gComputedStyle.verticalAlign,
                "font-size": "10pt"};
-      testTextAttrs(ID, 39, attrs, 39, 50);
+      testTextAttrs(ID, 39, attrs, defAttrs, 39, 50);
 
       attrs = {};
-      testTextAttrs(ID, 50, attrs, 50, 55);
+      testTextAttrs(ID, 50, attrs, defAttrs, 50, 55);
 
       tempElem = tempElem.nextSibling.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       attrs = {"text-position": gComputedStyle.verticalAlign};
-      testTextAttrs(ID, 55, attrs, 55, 64);
+      testTextAttrs(ID, 55, attrs, defAttrs, 55, 64);
 
       //////////////////////////////////////////////////////////////////////////
       // area7
       ID = "area7";
       tempElem = document.getElementById(ID);
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
       defAttrs = {
+        "language": "en",
+        "font-style": gComputedStyle.fontStyle,
+        "font-size": "12pt",
+        "background-color": "rgb(255, 255, 255)",
+        "font-weight": gComputedStyle.fontWeight,
+        "color": gComputedStyle.color,
+        "font-family": gComputedStyle.fontFamily,
+        "text-position": gComputedStyle.verticalAlign
+      };
+
+      testDefaultTextAttrs(ID, defAttrs);
+
+      attrs = {"language": "ru"};
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 12);
+
+      attrs = {};
+      testTextAttrs(ID, 12, attrs, defAttrs, 12, 13);
+
+      tempElem = tempElem.firstChild.nextSibling.nextSibling.nextSibling;
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      attrs = { "background-color": gComputedStyle.backgroundColor};
+      testTextAttrs(ID, 13, attrs, defAttrs, 13, 26);
+
+      attrs = {};
+      testTextAttrs(ID, 26, attrs, defAttrs, 26, 27);
+
+      attrs = {"language": "de"};
+      testTextAttrs(ID, 27, attrs, defAttrs, 27, 42);
+
+      attrs = {};
+      testTextAttrs(ID, 42, attrs, defAttrs, 42, 50);
+
+      attrs = {};
+      testTextAttrs(ID, 43, attrs, defAttrs, 42, 50);
+
+      tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling.firstChild.nextSibling;
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      attrs = {"color": gComputedStyle.color};
+      testTextAttrs(ID, 50, attrs, defAttrs, 50, 57);
+
+      tempElem = tempElem.firstChild.nextSibling;
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      attrs = {"font-weight": gComputedStyle.fontWeight,
+               "color": gComputedStyle.color};
+      testTextAttrs(ID, 57, attrs, defAttrs, 57, 61);
+
+      tempElem = tempElem.parentNode;
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      attrs = {"color": gComputedStyle.color};
+      testTextAttrs(ID, 61, attrs, defAttrs, 61, 68);
+
+      //////////////////////////////////////////////////////////////////////////
+      // area9, different single style spans in styled paragraph
+      ID = "area9";
+      tempElem = document.getElementById(ID);
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      defAttrs = {
+        "font-style": gComputedStyle.fontStyle,
+        "font-size": "10pt",
+        "background-color": "rgb(255, 255, 255)",
+        "font-weight": gComputedStyle.fontWeight,
+        "color": gComputedStyle.color,
+        "font-family": gComputedStyle.fontFamily,
+        "text-position": gComputedStyle.verticalAlign
+      };
+
+      testDefaultTextAttrs(ID, defAttrs);
+
+      attrs = {};
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 6);
+
+      attrs = { "font-size": "12pt" };
+      testTextAttrs(ID, 7, attrs, defAttrs, 6, 12);
+
+      attrs = {};
+      testTextAttrs(ID, 13, attrs, defAttrs, 12, 21);
+
+      // Walk to the span with the different background color
+      tempElem = tempElem.firstChild.nextSibling.nextSibling.nextSibling;
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      attrs = { "background-color": gComputedStyle.backgroundColor };
+      testTextAttrs(ID, 22, attrs, defAttrs, 21, 36);
+
+      attrs = {};
+      testTextAttrs(ID, 37, attrs, defAttrs, 36, 44);
+
+      // Walk from the background color span to the one with font-style
+      tempElem = tempElem.nextSibling.nextSibling;
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      attrs = { "font-style": gComputedStyle.fontStyle };
+      testTextAttrs(ID, 45, attrs, defAttrs, 44, 61);
+
+      attrs = {};
+      testTextAttrs(ID, 62, attrs, defAttrs, 61, 69);
+
+      // Walk from span with font-style to the one with font-family.
+      tempElem = tempElem.nextSibling.nextSibling;
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      attrs = { "font-family": gComputedStyle.fontFamily };
+      testTextAttrs(ID, 70, attrs, defAttrs, 69, 83);
+
+      attrs = {};
+      testTextAttrs(ID, 84, attrs, defAttrs, 83, 91);
+
+      attrs = { "text-underline-style": "solid" };
+      testTextAttrs(ID, 92, attrs, defAttrs, 91, 101);
+
+      attrs = {};
+      testTextAttrs(ID, 102, attrs, defAttrs, 101, 109);
+
+      attrs = { "text-line-through-style": "solid" };
+      testTextAttrs(ID, 110, attrs, defAttrs, 109, 122);
+
+      attrs = {};
+      testTextAttrs(ID, 123, attrs, defAttrs, 122, 130);
+
+      // area10, different single style spans in non-styled paragraph
+      ID = "area10";
+      tempElem = document.getElementById(ID);
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      defAttrs = {
         "font-style": gComputedStyle.fontStyle,
         "font-size": "12pt",
         "background-color": "rgb(255, 255, 255)",
         "font-weight": gComputedStyle.fontWeight,
         "color": gComputedStyle.color,
         "font-family": gComputedStyle.fontFamily,
         "text-position": gComputedStyle.verticalAlign
       };
 
       testDefaultTextAttrs(ID, defAttrs);
 
-      attrs = {"language": "ru"};
-      testTextAttrs(ID, 0, attrs, 0, 12);
+      attrs = {};
+      testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
 
-      attrs = {"language": "en"};
-      testTextAttrs(ID, 12, attrs, 12, 13);
+      attrs = { "font-size": "14pt" };
+      testTextAttrs(ID, 7, attrs, defAttrs, 7, 13);
 
+      attrs = {};
+      testTextAttrs(ID, 13, attrs, defAttrs, 13, 22);
+
+      // Walk to the span with the different background color
       tempElem = tempElem.firstChild.nextSibling.nextSibling.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
-      attrs = {"language" :"en",
-               "background-color": gComputedStyle.backgroundColor};
-      testTextAttrs(ID, 13, attrs, 13, 26);
+      attrs = { "background-color": gComputedStyle.backgroundColor };
+      testTextAttrs(ID, 23, attrs, defAttrs, 22, 37);
+
+      attrs = {};
+      testTextAttrs(ID, 38, attrs, defAttrs, 37, 45);
 
-      attrs = {"language": "en" };
-      testTextAttrs(ID, 26, attrs, 26, 27);
-
-      attrs = {"language": "de"};
-      testTextAttrs(ID, 27, attrs, 27, 42);
-
-      attrs = {"language": "en"};
-      testTextAttrs(ID, 42, attrs, 42, 43);
+      // Walk from the background color span to the one with font-style
+      tempElem = tempElem.nextSibling.nextSibling;
+      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+      attrs = {"font-style": gComputedStyle.fontStyle};
+      testTextAttrs(ID, 46, attrs, defAttrs, 45, 62);
 
       attrs = {};
-      testTextAttrs(ID, 43, attrs, 43, 50);
+      testTextAttrs(ID, 63, attrs, defAttrs, 62, 70);
 
-      tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling.firstChild.nextSibling;
-      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
-      attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 50, attrs, 50, 57);
-
-      tempElem = tempElem.firstChild.nextSibling;
+      // Walk from span with font-style to the one with font-family.
+      tempElem = tempElem.nextSibling.nextSibling;
       gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
-      attrs = {"font-weight": gComputedStyle.fontWeight,
-               "color": gComputedStyle.color};
-      testTextAttrs(ID, 57, attrs, 57, 61);
+      attrs = {"font-family": gComputedStyle.fontFamily};
+      testTextAttrs(ID, 71, attrs, defAttrs, 70, 84);
+
+      attrs = {};
+      testTextAttrs(ID, 85, attrs, defAttrs, 84, 92);
+
+      attrs = { "text-underline-style": "solid" };
+      testTextAttrs(ID, 93, attrs, defAttrs, 92, 102);
 
-      tempElem = tempElem.parentNode;
-      gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
-      attrs = {"color": gComputedStyle.color};
-      testTextAttrs(ID, 61, attrs, 61, 68);
+      attrs = {};
+      testTextAttrs(ID, 103, attrs, defAttrs, 102, 110);
+
+      attrs = { "text-line-through-style": "solid" };
+      testTextAttrs(ID, 111, attrs, defAttrs, 110, 123);
+
+      attrs = {};
+      testTextAttrs(ID, 124, attrs, defAttrs, 123, 131);
 
       //////////////////////////////////////////////////////////////////////////
       // test spelling text attributes
       testSpellTextAttrs(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
@@ -420,11 +549,27 @@
     <span lang="en">
       Normal
       <span style="color: magenta">Magenta<b>Bold</b>Magenta</span>
     </span>
   </p>
 
   <input id="area8"/>
 
-  <p id="output"/>
+  <p id="area9" style="font-size: smaller">Small
+    <span style="font-size: 120%">bigger</span> smaller
+    <span style="background-color: blue;">background blue</span> normal
+    <span style="font-style: italic;">Different styling</span> normal
+    <span style="font-family: tahoma;">Different font</span> normal
+    <span style="text-decoration: underline;">underlined</span> normal
+    <span style="text-decoration: line-through;">strikethrough</span> normal
+  </p>
+
+  <p id="area10">Normal
+    <span style="font-size: 120%">bigger</span> smaller
+    <span style="background-color: blue;">background blue</span> normal
+    <span style="font-style: italic;">Different styling</span> normal
+    <span style="font-family: tahoma;">Different font</span> normal
+    <span style="text-decoration: underline;">underlined</span> normal
+    <span style="text-decoration: line-through;">strikethrough</span> normal
+  </p>
 </body>
 </html>