int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int startOffset, int endOffset, bool markAll) const { // Found some bad grammar. Find the earliest detail range that starts in our search range (if any). // Optionally add a DocumentMarker for each detail in the range. int earliestDetailLocationSoFar = -1; int earliestDetailIndex = -1; for (unsigned i = 0; i < grammarDetails.size(); i++) { const GrammarDetail* detail = &grammarDetails[i]; ASSERT(detail->length > 0 && detail->location >= 0); int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location; // Skip this detail if it starts before the original search range if (detailStartOffsetInParagraph < startOffset) continue; // Skip this detail if it starts after the original search range if (detailStartOffsetInParagraph >= endOffset) continue; if (markAll) { const EphemeralRange badGrammarRange = calculateCharacterSubrange(EphemeralRange(m_start, m_end), badGrammarPhraseLocation - startOffset + detail->location, detail->length); badGrammarRange.document().markers().addMarker(badGrammarRange.startPosition(), badGrammarRange.endPosition(), DocumentMarker::Grammar, detail->userDescription); } // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order) if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) { earliestDetailIndex = i; earliestDetailLocationSoFar = detail->location; } } return earliestDetailIndex; }
String TextCheckingHelper::findFirstMisspelling(int& firstMisspellingOffset, bool markAll) { WordAwareIterator it(m_start, m_end); firstMisspellingOffset = 0; String firstMisspelling; int currentChunkOffset = 0; while (!it.atEnd()) { int length = it.length(); // Skip some work for one-space-char hunks if (!(length == 1 && it.characterAt(0) == ' ')) { int misspellingLocation = -1; int misspellingLength = 0; m_client->textChecker().checkSpellingOfString(it.substring(0, length), &misspellingLocation, &misspellingLength); // 5490627 shows that there was some code path here where the String constructor below crashes. // We don't know exactly what combination of bad input caused this, so we're making this much // more robust against bad input on release builds. ASSERT(misspellingLength >= 0); ASSERT(misspellingLocation >= -1); ASSERT(!misspellingLength || misspellingLocation >= 0); ASSERT(misspellingLocation < length); ASSERT(misspellingLength <= length); ASSERT(misspellingLocation + misspellingLength <= length); if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < length && misspellingLength <= length && misspellingLocation + misspellingLength <= length) { // Compute range of misspelled word const EphemeralRange misspellingRange = calculateCharacterSubrange(EphemeralRange(m_start, m_end), currentChunkOffset + misspellingLocation, misspellingLength); // Remember first-encountered misspelling and its offset. if (!firstMisspelling) { firstMisspellingOffset = currentChunkOffset + misspellingLocation; firstMisspelling = it.substring(misspellingLocation, misspellingLength); } // Store marker for misspelled word. misspellingRange.document().markers().addMarker(misspellingRange.startPosition(), misspellingRange.endPosition(), DocumentMarker::Spelling); // Bail out if we're marking only the first misspelling, and not all instances. if (!markAll) break; } } currentChunkOffset += length; it.advance(); } return firstMisspelling; }
TEST_F(CharacterIteratorTest, SubrangeWithReplacedElements) { static const char* bodyContent = "<div id='div' contenteditable='true'>1<img src='foo.png'>345</div>"; setBodyContent(bodyContent); document().view()->updateAllLifecyclePhases(); Node* divNode = document().getElementById("div"); Range* entireRange = Range::create(document(), divNode, 0, divNode, 3); EphemeralRange result = calculateCharacterSubrange(EphemeralRange(entireRange), 2, 3); Node* textNode = divNode->lastChild(); EXPECT_EQ(Position(textNode, 0), result.startPosition()); EXPECT_EQ(Position(textNode, 3), result.endPosition()); }
EphemeralRange TextCheckingParagraph::subrange(int characterOffset, int characterCount) const { ASSERT(m_checkingRange.isNotNull()); return calculateCharacterSubrange(paragraphRange(), characterOffset, characterCount); }