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.

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

mercurial