accessible/src/base/TextUpdater.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "TextUpdater.h"
     8 #include "Accessible-inl.h"
     9 #include "DocAccessible-inl.h"
    10 #include "TextLeafAccessible.h"
    11 #include <algorithm>
    13 using namespace mozilla::a11y;
    15 void
    16 TextUpdater::Run(DocAccessible* aDocument, TextLeafAccessible* aTextLeaf,
    17                  const nsAString& aNewText)
    18 {
    19   NS_ASSERTION(aTextLeaf, "No text leaf accessible?");
    21   const nsString& oldText = aTextLeaf->Text();
    22   uint32_t oldLen = oldText.Length(), newLen = aNewText.Length();
    23   uint32_t minLen = std::min(oldLen, newLen);
    25   // Skip coinciding begin substrings.
    26   uint32_t skipStart = 0;
    27   for (; skipStart < minLen; skipStart++) {
    28     if (aNewText[skipStart] != oldText[skipStart])
    29       break;
    30   }
    32   // The text was changed. Do update.
    33   if (skipStart != minLen || oldLen != newLen) {
    34     TextUpdater updater(aDocument, aTextLeaf);
    35     updater.DoUpdate(aNewText, oldText, skipStart);
    36   }
    37 }
    39 void
    40 TextUpdater::DoUpdate(const nsAString& aNewText, const nsAString& aOldText,
    41                       uint32_t aSkipStart)
    42 {
    43   Accessible* parent = mTextLeaf->Parent();
    44   if (!parent)
    45     return;
    47   mHyperText = parent->AsHyperText();
    48   if (!mHyperText) {
    49     NS_ERROR("Text leaf parent is not hypertext!");
    50     return;
    51   }
    53   // Get the text leaf accessible offset and invalidate cached offsets after it.
    54   mTextOffset = mHyperText->GetChildOffset(mTextLeaf, true);
    55   NS_ASSERTION(mTextOffset != -1,
    56                "Text leaf hasn't offset within hyper text!");
    58   uint32_t oldLen = aOldText.Length(), newLen = aNewText.Length();
    59   uint32_t minLen = std::min(oldLen, newLen);
    61   // Trim coinciding substrings from the end.
    62   uint32_t skipEnd = 0;
    63   while (minLen - skipEnd > aSkipStart &&
    64          aNewText[newLen - skipEnd - 1] == aOldText[oldLen - skipEnd - 1]) {
    65     skipEnd++;
    66   }
    68   uint32_t strLen1 = oldLen - aSkipStart - skipEnd;
    69   uint32_t strLen2 = newLen - aSkipStart - skipEnd;
    71   const nsAString& str1 = Substring(aOldText, aSkipStart, strLen1);
    72   const nsAString& str2 = Substring(aNewText, aSkipStart, strLen2);
    74   // Increase offset of the text leaf on skipped characters amount.
    75   mTextOffset += aSkipStart;
    77   // It could be single insertion or removal or the case of long strings. Do not
    78   // calculate the difference between long strings and prefer to fire pair of
    79   // insert/remove events as the old string was replaced on the new one.
    80   if (strLen1 == 0 || strLen2 == 0 ||
    81       strLen1 > kMaxStrLen || strLen2 > kMaxStrLen) {
    82     if (strLen1 > 0) {
    83       // Fire text change event for removal.
    84       nsRefPtr<AccEvent> textRemoveEvent =
    85         new AccTextChangeEvent(mHyperText, mTextOffset, str1, false);
    86       mDocument->FireDelayedEvent(textRemoveEvent);
    87     }
    89     if (strLen2 > 0) {
    90       // Fire text change event for insertion.
    91       nsRefPtr<AccEvent> textInsertEvent =
    92         new AccTextChangeEvent(mHyperText, mTextOffset, str2, true);
    93       mDocument->FireDelayedEvent(textInsertEvent);
    94     }
    96     mDocument->MaybeNotifyOfValueChange(mHyperText);
    98     // Update the text.
    99     mTextLeaf->SetText(aNewText);
   100     return;
   101   }
   103   // Otherwise find the difference between strings and fire events.
   104   // Note: we can skip initial and final coinciding characters since they don't
   105   // affect the Levenshtein distance.
   107   // Compute the flat structured matrix need to compute the difference.
   108   uint32_t len1 = strLen1 + 1, len2 = strLen2 + 1;
   109   uint32_t* entries = new uint32_t[len1 * len2];
   111   for (uint32_t colIdx = 0; colIdx < len1; colIdx++)
   112     entries[colIdx] = colIdx;
   114   uint32_t* row = entries;
   115   for (uint32_t rowIdx = 1; rowIdx < len2; rowIdx++) {
   116     uint32_t* prevRow = row;
   117     row += len1;
   118     row[0] = rowIdx;
   119     for (uint32_t colIdx = 1; colIdx < len1; colIdx++) {
   120       if (str1[colIdx - 1] != str2[rowIdx - 1]) {
   121         uint32_t left = row[colIdx - 1];
   122         uint32_t up = prevRow[colIdx];
   123         uint32_t upleft = prevRow[colIdx - 1];
   124         row[colIdx] = std::min(upleft, std::min(left, up)) + 1;
   125       } else {
   126         row[colIdx] = prevRow[colIdx - 1];
   127       }
   128     }
   129   }
   131   // Compute events based on the difference.
   132   nsTArray<nsRefPtr<AccEvent> > events;
   133   ComputeTextChangeEvents(str1, str2, entries, events);
   135   delete [] entries;
   137   // Fire events.
   138   for (int32_t idx = events.Length() - 1; idx >= 0; idx--)
   139     mDocument->FireDelayedEvent(events[idx]);
   141   mDocument->MaybeNotifyOfValueChange(mHyperText);
   143   // Update the text.
   144   mTextLeaf->SetText(aNewText);
   145 }
   147 void
   148 TextUpdater::ComputeTextChangeEvents(const nsAString& aStr1,
   149                                      const nsAString& aStr2,
   150                                      uint32_t* aEntries,
   151                                      nsTArray<nsRefPtr<AccEvent> >& aEvents)
   152 {
   153   int32_t colIdx = aStr1.Length(), rowIdx = aStr2.Length();
   155   // Point at which strings last matched.
   156   int32_t colEnd = colIdx;
   157   int32_t rowEnd = rowIdx;
   159   int32_t colLen = colEnd + 1;
   160   uint32_t* row = aEntries + rowIdx * colLen;
   161   uint32_t dist = row[colIdx]; // current Levenshtein distance
   162   while (rowIdx && colIdx) { // stop when we can't move diagonally
   163     if (aStr1[colIdx - 1] == aStr2[rowIdx - 1]) { // match
   164       if (rowIdx < rowEnd) { // deal with any pending insertion
   165         FireInsertEvent(Substring(aStr2, rowIdx, rowEnd - rowIdx),
   166                         rowIdx, aEvents);
   167       }
   168       if (colIdx < colEnd) { // deal with any pending deletion
   169         FireDeleteEvent(Substring(aStr1, colIdx, colEnd - colIdx),
   170                         rowIdx, aEvents);
   171       }
   173       colEnd = --colIdx; // reset the match point
   174       rowEnd = --rowIdx;
   175       row -= colLen;
   176       continue;
   177     }
   178     --dist;
   179     if (dist == row[colIdx - 1 - colLen]) { // substitution
   180       --colIdx;
   181       --rowIdx;
   182       row -= colLen;
   183       continue;
   184     }
   185     if (dist == row[colIdx - colLen]) { // insertion
   186       --rowIdx;
   187       row -= colLen;
   188       continue;
   189     }
   190     if (dist == row[colIdx - 1]) { // deletion
   191       --colIdx;
   192       continue;
   193     }
   194     NS_NOTREACHED("huh?");
   195     return;
   196   }
   198   if (rowEnd)
   199     FireInsertEvent(Substring(aStr2, 0, rowEnd), 0, aEvents);
   200   if (colEnd)
   201     FireDeleteEvent(Substring(aStr1, 0, colEnd), 0, aEvents);
   202 }

mercurial