Example #1
0
String StructureShape::leastUpperBound(Vector<RefPtr<StructureShape>>* shapes)
{
    if (!shapes->size())
        return "";

    StringBuilder lub;
    RefPtr<StructureShape> origin = shapes->at(0);
    lub.append("{");
    for (auto iter = origin->m_fields.begin(), end = origin->m_fields.end(); iter != end; ++iter) {
        bool shouldAdd = true;
        for (size_t i = 1, size = shapes->size(); i < size; i++) {
            // If all other Shapes have the same field as origin, add it to the least upper bound.
            if (!shapes->at(i)->m_fields.contains(iter->key)) {
                shouldAdd = false;
                break;
            }
        }
        if (shouldAdd)
            lub.append(String(iter->key.get()) + String(", "));
    }

    if (lub.length() >= 3)
        lub.resize(lub.length() - 2); // Remove the trailing ', '

    lub.append("}");
    
    return lub.toString();
}
TEST(StringBuilderTest, ToAtomicString)
{
    StringBuilder builder;
    builder.append("123");
    AtomicString atomicString = builder.toAtomicString();
    EXPECT_EQ(String("123"), atomicString);

    builder.reserveCapacity(256);
    EXPECT_TRUE(builder.canShrink());
    for (int i = builder.length(); i < 128; i++)
        builder.append('x');
    AtomicString atomicString1 = builder.toAtomicString();
    EXPECT_EQ(128u, atomicString1.length());
    EXPECT_EQ('x', atomicString1[127]);

    // Later change of builder should not affect the atomic string.
    for (int i = builder.length(); i < 256; i++)
        builder.append('x');
    EXPECT_EQ(128u, atomicString1.length());

    EXPECT_FALSE(builder.canShrink());
    String string = builder.toString();
    AtomicString atomicString2 = builder.toAtomicString();
    // They should share the same StringImpl.
    EXPECT_EQ(atomicString2.impl(), string.impl());
}
Example #3
0
public boolean isValid(String s) {
    StringBuilder sb = new StringBuilder();
    boolean valid = true;
    for (int i = 0; valid && i < s.length(); i++) {
        int lastIdx = sb.length() - 1;
        char c = s.charAt(i);
        switch (c) {
        case '{':
        case '(':
        case '[':
        sb.append(c);
        break;
        case '}':
        case ')':
        case ']':
        char lchar = (c == ']' ? '[' : (c == ')' ? '(' : '{'));
        if (lastIdx >= 0 && sb.charAt(lastIdx) == lchar)
            sb.setLength(lastIdx);
        else
            valid = false;
        break;
        }
    }
    return valid && sb.length() == 0;
    }
Example #4
0
void Element::formatForDebugger(char* buffer, unsigned length) const
{
    StringBuilder result;
    String s;

    result.append(nodeName());

    s = getIdAttribute();
    if (s.length() > 0) {
        if (result.length() > 0)
            result.appendLiteral("; ");
        result.appendLiteral("id=");
        result.append(s);
    }

    s = getAttribute(HTMLNames::classAttr);
    if (s.length() > 0) {
        if (result.length() > 0)
            result.appendLiteral("; ");
        result.appendLiteral("class=");
        result.append(s);
    }

    strncpy(buffer, result.toString().utf8().data(), length - 1);
}
Example #5
0
String StructureShape::stringRepresentation()
{
    StringBuilder representation;
    RefPtr<StructureShape> curShape = this;

    representation.append('{');
    while (curShape) {
        for (auto it = curShape->m_fields.begin(), end = curShape->m_fields.end(); it != end; ++it) {
            String prop((*it).get());
            representation.append(prop);
            representation.append(", ");
        }

        if (curShape->m_proto) {
            String prot = makeString("__proto__ [", curShape->m_proto->m_constructorName, ']');
            representation.append(prot);
            representation.append(", ");
        }

        curShape = curShape->m_proto;
    }

    if (representation.length() >= 3)
        representation.resize(representation.length() - 2);

    representation.append('}');

    return representation.toString();
}
// This is only needed for TextDocuments where we might have text nodes
// approaching the default length limit (~64k) and we don't want to
// break a text node in the middle of a combining character.
static unsigned findBreakIndexBetween(const StringBuilder& string, unsigned currentPosition, unsigned proposedBreakIndex)
{
    ASSERT(currentPosition < proposedBreakIndex);
    ASSERT(proposedBreakIndex <= string.length());
    // The end of the string is always a valid break.
    if (proposedBreakIndex == string.length())
        return proposedBreakIndex;

    // Latin-1 does not have breakable boundaries. If we ever moved to a differnet 8-bit encoding this could be wrong.
    if (string.is8Bit())
        return proposedBreakIndex;

    const UChar* breakSearchCharacters = string.characters16() + currentPosition;
    // We need at least two characters look-ahead to account for UTF-16 surrogates, but can't search off the end of the buffer!
    unsigned breakSearchLength = std::min(proposedBreakIndex - currentPosition + 2, string.length() - currentPosition);
    NonSharedCharacterBreakIterator it(breakSearchCharacters, breakSearchLength);

    if (it.isBreak(proposedBreakIndex - currentPosition))
        return proposedBreakIndex;

    int adjustedBreakIndexInSubstring = it.preceding(proposedBreakIndex - currentPosition);
    if (adjustedBreakIndexInSubstring > 0)
        return currentPosition + adjustedBreakIndexInSubstring;
    // We failed to find a breakable point, let the caller figure out what to do.
    return 0;
}
Example #7
0
static void frameContentAsPlainText(size_t maxChars, LocalFrame* frame, StringBuilder& output)
{
    Document* document = frame->document();
    if (!document)
        return;

    if (!frame->view())
        return;

    // Select the document body.
    RefPtr<Range> range(document->createRange());
    TrackExceptionState exceptionState;
    range->selectNodeContents(document, exceptionState);

    if (!exceptionState.had_exception()) {
        // The text iterator will walk nodes giving us text. This is similar to
        // the plainText() function in core/editing/TextIterator.h, but we implement the maximum
        // size and also copy the results directly into a wstring, avoiding the
        // string conversion.
        for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
            it.appendTextToStringBuilder(output, 0, maxChars - output.length());
            if (output.length() >= maxChars)
                return; // Filled up the buffer.
        }
    }
}
static void frameContentAsPlainText(size_t maxChars, LocalFrame* frame, StringBuilder& output)
{
    Document* document = frame->document();
    if (!document)
        return;

    if (!frame->view())
        return;

    // Select the document body.
    if (document->body()) {
        const EphemeralRange range = EphemeralRange::rangeOfContents(*document->body());

        // The text iterator will walk nodes giving us text. This is similar to
        // the plainText() function in core/editing/TextIterator.h, but we implement the maximum
        // size and also copy the results directly into a wstring, avoiding the
        // string conversion.
        for (TextIterator it(range.startPosition(), range.endPosition()); !it.atEnd(); it.advance()) {
            it.text().appendTextToStringBuilder(output, 0, maxChars - output.length());
            if (output.length() >= maxChars)
                return; // Filled up the buffer.
        }
    }

    // The separator between frames when the frames are converted to plain text.
    const LChar frameSeparator[] = { '\n', '\n' };
    const size_t frameSeparatorLength = WTF_ARRAY_LENGTH(frameSeparator);

    // Recursively walk the children.
    const FrameTree& frameTree = frame->tree();
    for (Frame* curChild = frameTree.firstChild(); curChild; curChild = curChild->tree().nextSibling()) {
        if (!curChild->isLocalFrame())
            continue;
        LocalFrame* curLocalChild = toLocalFrame(curChild);
        // Ignore the text of non-visible frames.
        LayoutView* contentLayoutObject = curLocalChild->contentLayoutObject();
        LayoutPart* ownerLayoutObject = curLocalChild->ownerLayoutObject();
        if (!contentLayoutObject || !contentLayoutObject->size().width() || !contentLayoutObject->size().height()
            || (contentLayoutObject->location().x() + contentLayoutObject->size().width() <= 0) || (contentLayoutObject->location().y() + contentLayoutObject->size().height() <= 0)
            || (ownerLayoutObject && ownerLayoutObject->style() && ownerLayoutObject->style()->visibility() != VISIBLE)) {
            continue;
        }

        // Make sure the frame separator won't fill up the buffer, and give up if
        // it will. The danger is if the separator will make the buffer longer than
        // maxChars. This will cause the computation above:
        //   maxChars - output->size()
        // to be a negative number which will crash when the subframe is added.
        if (output.length() >= maxChars - frameSeparatorLength)
            return;

        output.append(frameSeparator, frameSeparatorLength);
        frameContentAsPlainText(maxChars, curLocalChild, output);
        if (output.length() >= maxChars)
            return; // Filled up the buffer.
    }
}
Example #9
0
// This returns an AtomicString because it is always passed as argument to
// setValue() and setValue() takes an AtomicString in argument.
AtomicString DOMTokenList::removeTokens(const AtomicString& input,
                                        const Vector<String>& tokens) {
  // Algorithm defined at
  // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#remove-a-token-from-a-string
  // New spec is at https://dom.spec.whatwg.org/#remove-a-token-from-a-string

  unsigned inputLength = input.length();
  StringBuilder output;  // 3
  output.reserveCapacity(inputLength);
  unsigned position = 0;  // 4

  // Step 5
  while (position < inputLength) {
    if (isHTMLSpace<UChar>(input[position])) {  // 6
      position++;
      continue;  // 6.3
    }

    // Step 7
    StringBuilder tokenBuilder;
    while (position < inputLength && isNotHTMLSpace<UChar>(input[position]))
      tokenBuilder.append(input[position++]);

    // Step 8
    String token = tokenBuilder.toString();
    if (tokens.contains(token)) {
      // Step 8.1
      while (position < inputLength && isHTMLSpace<UChar>(input[position]))
        ++position;

      // Step 8.2
      size_t j = output.length();
      while (j > 0 && isHTMLSpace<UChar>(output[j - 1]))
        --j;
      output.resize(j);
    } else {
      output.append(token);  // Step 9
    }

    if (position < inputLength && !output.isEmpty())
      output.append(' ');
  }

  size_t j = output.length();
  if (j > 0 && isHTMLSpace<UChar>(output[j - 1]))
    output.resize(j - 1);

  return output.toAtomicString();
}
Example #10
0
public String toString() {
  StringBuilder result = new StringBuilder(8 * getDegree());
  for (int degree = getDegree(); degree >= 0; degree--) {
    int coefficient = getCoefficient(degree);
    if (coefficient != 0) {
      if (coefficient < 0) {
        result.append(" - ");
        coefficient = -coefficient;
      } else {
        if (result.length() > 0) {
          result.append(" + ");
        }
      }
      if (degree == 0 || coefficient != 1) {
        result.append(coefficient);
      }
      if (degree != 0) {
        if (degree == 1) {
          result.append('x');
        } else {
          result.append("x^");
          result.append(degree);
        }
      }
    }
  }
  return result.toString();
}
Example #11
0
static void commitLiteralToken(StringBuilder& literalBuffer, Vector<DateFormatToken>& tokens)
{
    if (literalBuffer.length() <= 0)
        return;
    tokens.append(DateFormatToken(literalBuffer.toString()));
    literalBuffer.clear();
}
Example #12
0
String Text::wholeText() const
{
    const Text* startText = earliestLogicallyAdjacentTextNode(this);
    const Text* endText = latestLogicallyAdjacentTextNode(this);

    Node* onePastEndText = endText->nextSibling();
    unsigned resultLength = 0;
    for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
        if (!n->isTextNode())
            continue;
        const String& data = toText(n)->data();
        if (std::numeric_limits<unsigned>::max() - data.length() < resultLength)
            CRASH();
        resultLength += data.length();
    }
    StringBuilder result;
    result.reserveCapacity(resultLength);
    for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
        if (!n->isTextNode())
            continue;
        result.append(toText(n)->data());
    }
    ASSERT(result.length() == resultLength);

    return result.toString();
}
Example #13
0
String generateHTMLContent(unsigned contentLength)
{
    String baseString("abcdefghijklmnopqrstuvwxyz0123457890");
    unsigned baseLength = baseString.length();

    StringBuilder builder;
    builder.append("<html><body>");

    if (contentLength <= baseLength)
        builder.append(baseString, 0, contentLength);
    else {
        unsigned currentLength = 0;
        while (currentLength < contentLength) {
            if ((currentLength + baseLength) <= contentLength)
                builder.append(baseString);
            else
                builder.append(baseString, 0, contentLength - currentLength);

            // Account for the 12 characters of the '<html><body>' prefix.
            currentLength = builder.length() - 12;
        }
    }
    builder.append("</body></html>");

    return builder.toString();
}
static void stripTrailingNewline(StringBuilder& result)
{
    // Remove one trailing newline; there's always one that's collapsed out by rendering.
    size_t size = result.length();
    if (size && result[size - 1] == newlineCharacter)
        result.resize(size - 1);
}
const String WebMediaConstraintsPrivate::toString() const
{
    StringBuilder builder;
    if (!isEmpty()) {
        builder.append('{');
        builder.append(basic().toString());
        if (!advanced().isEmpty()) {
            if (builder.length() > 1)
                builder.appendLiteral(", ");
            builder.appendLiteral("advanced: [");
            bool first = true;
            for (const auto& constraintSet : advanced()) {
                if (!first)
                    builder.appendLiteral(", ");
                builder.append('{');
                builder.append(constraintSet.toString());
                builder.append('}');
                first = false;
            }
            builder.append(']');
        }
        builder.append('}');
    }
    return builder.toString();
}
Example #16
0
String Text::wholeText() const
{
    const Text* startText = earliestLogicallyAdjacentTextNode(this);
    const Text* endText = latestLogicallyAdjacentTextNode(this);

    Node* onePastEndText = endText->nextSibling();
    Checked<unsigned> resultLength = 0;
    for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
        if (!n->isTextNode())
            continue;
        const Text* t = static_cast<const Text*>(n);
        const String& data = t->data();
        resultLength += data.length();
    }
    StringBuilder result;
    result.reserveCapacity(resultLength.unsafeGet());
    for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
        if (!n->isTextNode())
            continue;
        const Text* t = static_cast<const Text*>(n);
        result.append(t->data());
    }
    ASSERT(result.length() == resultLength.unsafeGet());

    return result.toString();
}
Example #17
0
void StyleElement::process(Element* e)
{
    if (!e || !e->inDocument())
        return;

    unsigned resultLength = 0;
    for (Node* c = e->firstChild(); c; c = c->nextSibling()) {
        if (isValidStyleChild(c)) {
            unsigned length = c->nodeValue().length();
            if (length > std::numeric_limits<unsigned>::max() - resultLength) {
                createSheet(e, m_startLineNumber, "");
                return;
            }
            resultLength += length;
        }
    }
    StringBuilder sheetText;
    sheetText.reserveCapacity(resultLength);

    for (Node* c = e->firstChild(); c; c = c->nextSibling()) {
        if (isValidStyleChild(c)) {
            sheetText.append(c->nodeValue());
        }
    }
    ASSERT(sheetText.length() == resultLength);

    createSheet(e, m_startLineNumber, sheetText.toString());
}
Example #18
0
void StyledMarkupAccumulator::appendText(StringBuilder& out, Text* text)
{    
    const bool parentIsTextarea = text->parentElement() && text->parentElement()->tagQName() == textareaTag;
    const bool wrappingSpan = shouldApplyWrappingStyle(text) && !parentIsTextarea;
    if (wrappingSpan) {
        RefPtr<EditingStyle> wrappingStyle = m_wrappingStyle->copy();
        // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
        // Make sure spans are inline style in paste side e.g. span { display: block }.
        wrappingStyle->forceInline();
        // FIXME: Should this be included in forceInline?
        wrappingStyle->style()->setProperty(CSSPropertyFloat, CSSValueNone);

        StringBuilder openTag;
        appendStyleNodeOpenTag(openTag, wrappingStyle->style(), text->document());
        out.append(openTag.characters(), openTag.length());
    }

    if (!shouldAnnotate() || parentIsTextarea)
        MarkupAccumulator::appendText(out, text);
    else {
        const bool useRenderedText = !enclosingNodeWithTag(firstPositionInNode(text), selectTag);
        String content = useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range);
        StringBuilder buffer;
        appendCharactersReplacingEntities(buffer, content.characters(), content.length(), EntityMaskInPCDATA);
        out.append(convertHTMLTextToInterchangeFormat(buffer.toString(), text));
    }

    if (wrappingSpan)
        out.append(styleNodeCloseTag());
}
Example #19
0
            std::string ParagraphStream::read() throw(IOException)
            {

              StringBuilder *paragraph = new StringBuilder();

              while (true)
              {
                std::string line = samples->read();

                // The last paragraph in the input might not
                // be terminated well with a new line at the end.

                if (line == "" || line == "")
                {
                  if (paragraph->length() > 0)
                  {
//JAVA TO C++ CONVERTER TODO TASK: There is no native C++ equivalent to 'toString':
                    return paragraph->toString();
                  }
                }
                else
                {
                  paragraph->append(line)->append('\n');
                }

                if (line == "")
                  return 0;
              }
            }
Example #20
0
static void commitLiteralToken(StringBuilder& literalBuffer,
                               StringBuilder& converted) {
  if (literalBuffer.length() <= 0)
    return;
  DateTimeFormat::quoteAndappend(literalBuffer.toString(), converted);
  literalBuffer.clear();
}
Example #21
0
void RenderMathMLFenced::updateFromElement()
{
    const auto& fenced = element();
 
    // The open operator defaults to a left parenthesis.
    AtomicString open = fenced.fastGetAttribute(MathMLNames::openAttr);
    m_open = open.isNull() ? gOpeningBraceChar : open;

    // The close operator defaults to a right parenthesis.
    AtomicString close = fenced.fastGetAttribute(MathMLNames::closeAttr);
    m_close = close.isNull() ? gClosingBraceChar : close;

    AtomicString separators = fenced.fastGetAttribute(MathMLNames::separatorsAttr);
    if (!separators.isNull()) {
        StringBuilder characters;
        for (unsigned int i = 0; i < separators.length(); i++) {
            if (!isSpaceOrNewline(separators[i]))
                characters.append(separators[i]);
        }
        m_separators = !characters.length() ? 0 : characters.toString().impl();
    } else {
        // The separator defaults to a single comma.
        m_separators = StringImpl::create(",");
    }
    
    if (isEmpty())
        makeFences();
    else {
        // FIXME: The mfenced element fails to update dynamically when its open, close and separators attributes are changed (https://bugs.webkit.org/show_bug.cgi?id=57696).
        toRenderMathMLOperator(firstChild())->updateTokenContent(m_open);
        m_closeFenceRenderer->updateTokenContent(m_close);
    }
}
Example #22
0
String extractMIMETypeFromMediaType(const String& mediaType)
{
    StringBuilder mimeType;
    unsigned length = mediaType.length();
    mimeType.reserveCapacity(length);
    for (unsigned i = 0; i < length; i++) {
        UChar c = mediaType[i];

        if (c == ';')
            break;

        // While RFC 2616 does not allow it, other browsers allow multiple values in the HTTP media
        // type header field, Content-Type. In such cases, the media type string passed here may contain
        // the multiple values separated by commas. For now, this code ignores text after the first comma,
        // which prevents it from simply failing to parse such types altogether. Later for better
        // compatibility we could consider using the first or last valid MIME type instead.
        // See https://bugs.webkit.org/show_bug.cgi?id=25352 for more discussion.
        if (c == ',')
            break;

        // FIXME: The following is not correct. RFC 2616 allows linear white space before and
        // after the MIME type, but not within the MIME type itself. And linear white space
        // includes only a few specific ASCII characters; a small subset of isSpaceOrNewline.
        // See https://bugs.webkit.org/show_bug.cgi?id=8644 for a bug tracking part of this.
        if (isSpaceOrNewline(c))
            continue;

        mimeType.append(c);
    }

    if (mimeType.length() == length)
        return mediaType;
    return mimeType.toString();
}
Example #23
0
WebString StringConstraint::toString() const {
  StringBuilder builder;
  builder.append('{');
  if (!m_ideal.isEmpty()) {
    builder.append("ideal: [");
    bool first = true;
    for (const auto& iter : m_ideal) {
      if (!first)
        builder.append(", ");
      builder.append('"');
      builder.append(iter);
      builder.append('"');
      first = false;
    }
    builder.append(']');
  }
  if (!m_exact.isEmpty()) {
    if (builder.length() > 1)
      builder.append(", ");
    builder.append("exact: [");
    bool first = true;
    for (const auto& iter : m_exact) {
      if (!first)
        builder.append(", ");
      builder.append('"');
      builder.append(iter);
      builder.append('"');
    }
    builder.append(']');
  }
  builder.append('}');
  return builder.toString();
}
Example #24
0
void RenderMathMLFenced::updateFromElement()
{
    Element* fenced = static_cast<Element*>(node());
 
    // FIXME: Handle open/close values with more than one character (they should be treated like text).
    AtomicString openValue = fenced->getAttribute(MathMLNames::openAttr);
    if (openValue.length() > 0)
        m_open = openValue[0];
    AtomicString closeValue = fenced->getAttribute(MathMLNames::closeAttr);
    if (closeValue.length() > 0)
        m_close = closeValue[0];
    
    AtomicString separators = fenced->getAttribute(MathMLNames::separatorsAttr);
    if (!separators.isNull()) {
        StringBuilder characters;
        for (unsigned int i = 0; i < separators.length(); i++) {
            if (!isSpaceOrNewline(separators[i]))
                characters.append(separators[i]);
        }
        m_separators = !characters.length() ? 0 : characters.toString().impl();
    } else {
        // The separator defaults to a single comma.
        m_separators = StringImpl::create(",");
    }
    
    if (isEmpty())
        makeFences();
}
Example #25
0
static bool parseHTMLIntegerInternal(const CharacterType* position, const CharacterType* end, int& value)
{
    // Step 3
    int sign = 1;

    // Step 4
    while (position < end) {
        if (!isHTMLSpace<CharacterType>(*position))
            break;
        ++position;
    }

    // Step 5
    if (position == end)
        return false;
    ASSERT(position < end);

    // Step 6
    if (*position == '-') {
        sign = -1;
        ++position;
    } else if (*position == '+')
        ++position;
    if (position == end)
        return false;
    ASSERT(position < end);

    // Step 7
    if (!isASCIIDigit(*position))
        return false;

    // Step 8
    StringBuilder digits;
    while (position < end) {
        if (!isASCIIDigit(*position))
            break;
        digits.append(*position++);
    }

    // Step 9
    bool ok;
    if (digits.is8Bit())
        value = sign * charactersToIntStrict(digits.characters8(), digits.length(), &ok);
    else
        value = sign * charactersToIntStrict(digits.characters16(), digits.length(), &ok);
    return ok;
}
Example #26
0
String StructureShape::stringRepresentation()
{
    StringBuilder representation;
    representation.append("{");
    for (auto iter = m_fields.begin(), end = m_fields.end(); iter != end; ++iter) {
        String prop(iter->key.get());
        representation.append(prop);
        representation.append(", ");
    }

    if (representation.length() >= 3)
        representation.resize(representation.length() - 2);

    representation.append("}");

    return representation.toString();
}
static String finishText(StringBuilder& result)
{
    // Remove one trailing newline; there's always one that's collapsed out by rendering.
    size_t size = result.length();
    if (size && result[size - 1] == '\n')
        result.resize(--size);
    return result.toString();
}
Example #28
0
TEST(StringBuilderTest, Resize)
{
    StringBuilder builder;
    builder.append("0123456789");
    builder.resize(10);
    EXPECT_EQ(10U, builder.length());
    expectBuilderContent("0123456789", builder);
    builder.resize(8);
    EXPECT_EQ(8U, builder.length());
    expectBuilderContent("01234567", builder);

    builder.toString();
    builder.resize(7);
    EXPECT_EQ(7U, builder.length());
    expectBuilderContent("0123456", builder);
    builder.resize(0);
    expectEmpty(builder);
}
Example #29
0
static Position findWordBoundary(const HTMLElement* innerEditor, const Position& startPosition, const Position& endPosition, FindOption findOption)
{
    StringBuilder concatTexts;
    Vector<unsigned> lengthList;
    HeapVector<Member<Text>> textList;

    if (startPosition.anchorNode()->isTextNode())
        ASSERT(startPosition.isOffsetInAnchor());
    if (endPosition.anchorNode()->isTextNode())
        ASSERT(endPosition.isOffsetInAnchor());

    // Traverse text nodes.
    for (Node* node = startPosition.anchorNode(); node; node = NodeTraversal::next(*node, innerEditor)) {
        bool isStartNode = node == startPosition.anchorNode();
        bool isEndNode = node == endPosition.anchorNode();
        if (node->isTextNode()) {
            Text* text = toText(node);
            const unsigned start = isStartNode ? startPosition.offsetInContainerNode() : 0;
            const unsigned end = isEndNode ? endPosition.offsetInContainerNode() : text->data().length();
            const unsigned length = end - start;

            concatTexts.append(text->data(), start, length);
            lengthList.append(length);
            textList.append(text);
        }

        if (isEndNode)
            break;
    }

    if (concatTexts.length() == 0)
        return startPosition;

    int start, end;
    if (findOption == FindEnd && concatTexts[0] == '\n') {
        // findWordBoundary("\ntext", 0, &start, &end) assigns 1 to |end| but
        // we expect 0 at the case.
        start = 0;
        end = 0;
    } else {
        Vector<UChar> characters;
        concatTexts.toString().appendTo(characters);
        findWordBoundary(characters.data(), characters.size(), findOption == FindStart ? characters.size() : 0, &start, &end);
    }
    ASSERT(start >= 0);
    ASSERT(end >= 0);
    unsigned remainingOffset = findOption == FindStart ? start : end;
    // Find position.
    for (unsigned i = 0; i < lengthList.size(); ++i) {
        if (remainingOffset <= lengthList[i])
            return Position(textList[i], (textList[i] == startPosition.anchorNode()) ? remainingOffset + startPosition.offsetInContainerNode() : remainingOffset);
        remainingOffset -= lengthList[i];
    }

    ASSERT_NOT_REACHED();
    return Position();
}
String ScrollingCoordinator::synchronousScrollingReasonsAsText(SynchronousScrollingReasons reasons)
{
    StringBuilder stringBuilder;

    if (reasons & ScrollingCoordinator::ForcedOnMainThread)
        stringBuilder.append("Forced on main thread, ");
    if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
        stringBuilder.append("Has slow repaint objects, ");
    if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
        stringBuilder.append("Has viewport constrained objects without supporting fixed layers, ");
    if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
        stringBuilder.append("Has non-layer viewport-constrained objects, ");
    if (reasons & ScrollingCoordinator::IsImageDocument)
        stringBuilder.append("Is image document, ");

    if (stringBuilder.length())
        stringBuilder.resize(stringBuilder.length() - 2);
    return stringBuilder.toString();
}