Bug 790475 - Part 1: Allow inline spellcheck to operate through adjacent text nodes. r=ehsan.
authorMike Conley <mconley@mozilla.com>
Thu, 25 Oct 2012 18:32:12 -0400
changeset 111587 72e6fd5a0358dd70c6267a10bf89d5538d34adb2
parent 111586 15c4d6334107a7874e84e248440fd057e148381f
child 111588 a75c3d861bf8536c06902d973db90ac80eed7f0b
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersehsan
bugs790475
milestone19.0a1
Bug 790475 - Part 1: Allow inline spellcheck to operate through adjacent text nodes. r=ehsan.
extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
--- a/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
@@ -362,35 +362,33 @@ IsDOMWordSeparator(PRUnichar ch)
 static inline bool
 IsBRElement(nsINode* aNode)
 {
   return aNode->IsElement() &&
          aNode->AsElement()->IsHTML(nsGkAtoms::br);
 }
 
 /**
- * Check if there's a DOM word separator before aBeforeOffset in this node.
- * Always returns true if it's a BR element.
- * aSeparatorOffset is set to the index of the first character in the last
- * separator if any is found (0 for BR elements).
+ * Given a TextNode, checks to see if there's a DOM word separator before
+ * aBeforeOffset within it. This function does not modify aSeparatorOffset when
+ * it returns false.
  *
- * This function does not modify aSeparatorOffset when it returns false.
+ * @param aNode the TextNode to check.
+ * @param aBeforeOffset the offset in the TextNode before which we will search
+ *        for the DOM separator. You can pass INT32_MAX to search the entire
+ *        length of the string.
+ * @param aSeparatorOffset will be set to the offset of the first separator it
+ *        encounters. Will not be written to if no separator is found.
+ * @returns True if it found a separator.
  */
 static bool
-ContainsDOMWordSeparator(nsINode* aNode, int32_t aBeforeOffset,
-                         int32_t* aSeparatorOffset)
+TextNodeContainsDOMWordSeparator(nsINode* aNode,
+                                 int32_t aBeforeOffset,
+                                 int32_t* aSeparatorOffset)
 {
-  if (IsBRElement(aNode)) {
-    *aSeparatorOffset = 0;
-    return true;
-  }
-  
-  if (!IsTextNode(aNode))
-    return false;
-
   // aNode is actually an nsIContent, since it's eTEXT
   nsIContent* content = static_cast<nsIContent*>(aNode);
   const nsTextFragment* textFragment = content->GetText();
   NS_ASSERTION(textFragment, "Where is our text?");
   for (int32_t i = NS_MIN(aBeforeOffset, int32_t(textFragment->GetLength())) - 1; i >= 0; --i) {
     if (IsDOMWordSeparator(textFragment->CharAt(i))) {
       // Be greedy, find as many separators as we can
       for (int32_t j = i - 1; j >= 0; --j) {
@@ -402,16 +400,40 @@ ContainsDOMWordSeparator(nsINode* aNode,
       }
       *aSeparatorOffset = i;
       return true;
     }
   }
   return false;
 }
 
+/**
+ * Check if there's a DOM word separator before aBeforeOffset in this node.
+ * Always returns true if it's a BR element.
+ * aSeparatorOffset is set to the index of the first character in the last
+ * separator if any is found (0 for BR elements).
+ *
+ * This function does not modify aSeparatorOffset when it returns false.
+ */
+static bool
+ContainsDOMWordSeparator(nsINode* aNode, int32_t aBeforeOffset,
+                         int32_t* aSeparatorOffset)
+{
+  if (IsBRElement(aNode)) {
+    *aSeparatorOffset = 0;
+    return true;
+  }
+
+  if (!IsTextNode(aNode))
+    return false;
+
+  return TextNodeContainsDOMWordSeparator(aNode, aBeforeOffset,
+                                          aSeparatorOffset);
+}
+
 static bool
 IsBreakElement(nsINode* aNode)
 {
   if (!aNode->IsElement()) {
     return false;
   }
 
   dom::Element *element = aNode->AsElement();
@@ -464,23 +486,37 @@ mozInlineSpellWordUtil::BuildSoftText()
   int32_t checkBeforeOffset = mSoftBegin.mOffset;
   while (node) {
     if (ContainsDOMWordSeparator(node, checkBeforeOffset, &firstOffsetInNode)) {
       if (node == mSoftBegin.mNode) {
         // If we find a word separator on the first node, look at the preceding
         // word on the text node as well.
         int32_t newOffset = 0;
         if (firstOffsetInNode > 0) {
-          // Try to find the previous word boundary.  We ignore the return value
-          // of ContainsDOMWordSeparator here because there might be no preceding
-          // word separator (such as when we're at the end of the first word in
-          // the text node), in which case we just set the found offsets to 0.
-          // Otherwise, ContainsDOMWordSeparator finds us the correct word
-          // boundary so that we can avoid looking at too many words.
-          ContainsDOMWordSeparator(node, firstOffsetInNode - 1, &newOffset);
+          // Try to find the previous word boundary in the current node. If
+          // we can't find one, start checking previous sibling nodes (if any
+          // adjacent ones exist) to see if we can find any text nodes with
+          // DOM word separators. We bail out as soon as we see a node that is
+          // not a text node, or we run out of previous sibling nodes. In the
+          // event that we simply cannot find any preceding word separator, the
+          // offset is set to 0, and the soft text beginning node is set to the
+          // "most previous" text node before the original starting node, or
+          // kept at the original starting node if no previous text nodes exist.
+          if (!ContainsDOMWordSeparator(node, firstOffsetInNode - 1,
+                                        &newOffset)) {
+            nsINode* prevNode = node->GetPreviousSibling();
+            while (prevNode && IsTextNode(prevNode)) {
+              mSoftBegin.mNode = prevNode;
+              if (TextNodeContainsDOMWordSeparator(prevNode, INT32_MAX,
+                                                   &newOffset)) {
+                break;
+              }
+              prevNode = prevNode->GetPreviousSibling();
+            }
+          }
         }
         firstOffsetInNode = newOffset;
         mSoftBegin.mOffset = newOffset;
       }
       break;
     }
     checkBeforeOffset = INT32_MAX;
     if (IsBreakElement(node)) {