PlainTextRange PlainTextRange::create(const ContainerNode& scope, const Range& range)
{
    if (!range.startContainer())
        return PlainTextRange();

    // The critical assumption is that this only gets called with ranges that
    // concentrate on a given area containing the selection root. This is done
    // because of text fields and textareas. The DOM for those is not
    // directly in the document DOM, so ensure that the range does not cross a
    // boundary of one of those.
    if (range.startContainer() != &scope && !range.startContainer()->isDescendantOf(&scope))
        return PlainTextRange();
    if (range.endContainer() != scope && !range.endContainer()->isDescendantOf(&scope))
        return PlainTextRange();

    RefPtrWillBeRawPtr<Range> testRange = Range::create(scope.document(), const_cast<ContainerNode*>(&scope), 0, range.startContainer(), range.startOffset());
    ASSERT(testRange->startContainer() == &scope);
    size_t start = TextIterator::rangeLength(testRange->startPosition(), testRange->endPosition());

    testRange->setEnd(range.endContainer(), range.endOffset(), IGNORE_EXCEPTION);
    ASSERT(testRange->startContainer() == &scope);
    size_t end = TextIterator::rangeLength(testRange->startPosition(), testRange->endPosition());

    return PlainTextRange(start, end);
}
示例#2
0
void visibleTextQuads(const Range& range, Vector<FloatQuad>& quads, bool useSelectionHeight)
{
    // Range::textQuads includes hidden text, which we don't want.
    // To work around this, this is a copy of it which skips hidden elements.
    Node* startContainer = range.startContainer();
    Node* endContainer = range.endContainer();

    if (!startContainer || !endContainer)
        return;

    Node* stopNode = range.pastLastNode();
    for (Node* node = range.firstNode(); node != stopNode; node = node->traverseNextNode()) {
        RenderObject* r = node->renderer();
        if (!r || !r->isText())
            continue;

        if (r->style()->visibility() != VISIBLE)
            continue;

        RenderText* renderText = toRenderText(r);
        int startOffset = node == startContainer ? range.startOffset() : 0;
        int endOffset = node == endContainer ? range.endOffset() : std::numeric_limits<int>::max();
        renderText->absoluteQuadsForRange(quads, startOffset, endOffset, useSelectionHeight);
    }
}
示例#3
0
JSValue* JSRange::getValueProperty(ExecState* exec, int token) const
{
    switch (token) {
    case StartContainerAttrNum: {
        ExceptionCode ec = 0;
        Range* imp = static_cast<Range*>(impl());

        KJS::JSValue* result = toJS(exec, WTF::getPtr(imp->startContainer(ec)));
        setDOMException(exec, ec);
        return result;
    }
    case StartOffsetAttrNum: {
        ExceptionCode ec = 0;
        Range* imp = static_cast<Range*>(impl());

        KJS::JSValue* result = jsNumber(imp->startOffset(ec));
        setDOMException(exec, ec);
        return result;
    }
    case EndContainerAttrNum: {
        ExceptionCode ec = 0;
        Range* imp = static_cast<Range*>(impl());

        KJS::JSValue* result = toJS(exec, WTF::getPtr(imp->endContainer(ec)));
        setDOMException(exec, ec);
        return result;
    }
    case EndOffsetAttrNum: {
        ExceptionCode ec = 0;
        Range* imp = static_cast<Range*>(impl());

        KJS::JSValue* result = jsNumber(imp->endOffset(ec));
        setDOMException(exec, ec);
        return result;
    }
    case CollapsedAttrNum: {
        ExceptionCode ec = 0;
        Range* imp = static_cast<Range*>(impl());

        KJS::JSValue* result = jsBoolean(imp->collapsed(ec));
        setDOMException(exec, ec);
        return result;
    }
    case CommonAncestorContainerAttrNum: {
        ExceptionCode ec = 0;
        Range* imp = static_cast<Range*>(impl());

        KJS::JSValue* result = toJS(exec, WTF::getPtr(imp->commonAncestorContainer(ec)));
        setDOMException(exec, ec);
        return result;
    }
    case ConstructorAttrNum:
        return getConstructor(exec);
    }
    return 0;
}
bool operator==(const Range &a, const Range &b)
{
    RangeImpl *ai = a.handle();
    RangeImpl *bi = b.handle();
    if (ai == bi)
        return true;
    if (!ai || !bi)
        return false;
    bool ad = ai->isDetached();
    bool bd = bi->isDetached();
    if (ad && bd)
        return true;
    if (ad || bd)
        return false;
    return a.startContainer() == b.startContainer()
        && a.endContainer() == b.endContainer()
        && a.startOffset() == b.startOffset()
        && a.endOffset() == b.endOffset();
}
VisiblePosition endVisiblePosition(const Range &r, EAffinity affinity)
{
    return VisiblePosition(r.endContainer().handle(), r.endOffset(), affinity);
}
示例#6
0
void DOMSelection::addRange(Range* newRange)
{
    if (!m_frame)
        return;

    // FIXME: Should we throw DOMException for error cases below?
    if (!newRange) {
        addConsoleError("The given range is null.");
        return;
    }

    if (!newRange->startContainer()) {
        addConsoleError("The given range has no container. Perhaps 'detach()' has been invoked on it?");
        return;
    }

    FrameSelection& selection = m_frame->selection();

    if (selection.isNone()) {
        selection.setSelectedRange(newRange, VP_DEFAULT_AFFINITY);
        return;
    }

    RefPtrWillBeRawPtr<Range> originalRange = selection.firstRange();

    if (originalRange->startContainer()->document() != newRange->startContainer()->document()) {
        addConsoleError("The given range does not belong to the current selection's document.");
        return;
    }
    if (originalRange->startContainer()->treeScope() != newRange->startContainer()->treeScope()) {
        addConsoleError("The given range and the current selection belong to two different document fragments.");
        return;
    }

    if (originalRange->compareBoundaryPoints(Range::START_TO_END, newRange, ASSERT_NO_EXCEPTION) < 0
        || newRange->compareBoundaryPoints(Range::START_TO_END, originalRange.get(), ASSERT_NO_EXCEPTION) < 0) {
        addConsoleError("Discontiguous selection is not supported.");
        return;
    }

    // FIXME: "Merge the ranges if they intersect" is Blink-specific behavior; other browsers supporting discontiguous
    // selection (obviously) keep each Range added and return it in getRangeAt(). But it's unclear if we can really
    // do the same, since we don't support discontiguous selection. Further discussions at
    // <https://code.google.com/p/chromium/issues/detail?id=353069>.

    Range* start = originalRange->compareBoundaryPoints(Range::START_TO_START, newRange, ASSERT_NO_EXCEPTION) < 0 ? originalRange.get() : newRange;
    Range* end = originalRange->compareBoundaryPoints(Range::END_TO_END, newRange, ASSERT_NO_EXCEPTION) < 0 ? newRange : originalRange.get();
    RefPtrWillBeRawPtr<Range> merged = Range::create(originalRange->startContainer()->document(), start->startContainer(), start->startOffset(), end->endContainer(), end->endOffset());
    EAffinity affinity = selection.selection().affinity();
    selection.setSelectedRange(merged.get(), affinity);
}
Position endPosition(const Range &r)
{
    if (r.isNull() || r.isDetached())
        return Position();
    return Position(r.endContainer().handle(), r.endOffset());
}
TEST_F(StaticRangeTest, SplitTextNodeRangeOutsideText)
{
    document().body()->setInnerHTML("<span id=\"outer\">0<span id=\"inner-left\">1</span>SPLITME<span id=\"inner-right\">2</span>3</span>", ASSERT_NO_EXCEPTION);

    Element* outer = document().getElementById(AtomicString::fromUTF8("outer"));
    Element* innerLeft = document().getElementById(AtomicString::fromUTF8("inner-left"));
    Element* innerRight = document().getElementById(AtomicString::fromUTF8("inner-right"));
    Text* oldText = toText(outer->childNodes()->item(2));

    StaticRange* staticRangeOuterOutside = StaticRange::create(document(), outer, 0, outer, 5);
    StaticRange* staticRangeOuterInside = StaticRange::create(document(), outer, 1, outer, 4);
    StaticRange* staticRangeOuterSurroundingText = StaticRange::create(document(), outer, 2, outer, 3);
    StaticRange* staticRangeInnerLeft = StaticRange::create(document(), innerLeft, 0, innerLeft, 1);
    StaticRange* staticRangeInnerRight = StaticRange::create(document(), innerRight, 0, innerRight, 1);
    StaticRange* staticRangeFromTextToMiddleOfElement = StaticRange::create(document(), oldText, 6, outer, 3);

    Range* rangeOuterOutside = staticRangeOuterOutside->toRange(ASSERT_NO_EXCEPTION);
    Range* rangeOuterInside = staticRangeOuterInside->toRange(ASSERT_NO_EXCEPTION);
    Range* rangeOuterSurroundingText = staticRangeOuterSurroundingText->toRange(ASSERT_NO_EXCEPTION);
    Range* rangeInnerLeft = staticRangeInnerLeft->toRange(ASSERT_NO_EXCEPTION);
    Range* rangeInnerRight = staticRangeInnerRight->toRange(ASSERT_NO_EXCEPTION);
    Range* rangeFromTextToMiddleOfElement = staticRangeFromTextToMiddleOfElement->toRange(ASSERT_NO_EXCEPTION);

    oldText->splitText(3, ASSERT_NO_EXCEPTION);
    Text* newText = toText(oldText->nextSibling());

    // Range should mutate.
    EXPECT_TRUE(rangeOuterOutside->boundaryPointsValid());
    EXPECT_EQ(outer, rangeOuterOutside->startContainer());
    EXPECT_EQ(0, rangeOuterOutside->startOffset());
    EXPECT_EQ(outer, rangeOuterOutside->endContainer());
    EXPECT_EQ(6, rangeOuterOutside->endOffset()); // Increased by 1 since a new node is inserted.

    EXPECT_TRUE(rangeOuterInside->boundaryPointsValid());
    EXPECT_EQ(outer, rangeOuterInside->startContainer());
    EXPECT_EQ(1, rangeOuterInside->startOffset());
    EXPECT_EQ(outer, rangeOuterInside->endContainer());
    EXPECT_EQ(5, rangeOuterInside->endOffset());

    EXPECT_TRUE(rangeOuterSurroundingText->boundaryPointsValid());
    EXPECT_EQ(outer, rangeOuterSurroundingText->startContainer());
    EXPECT_EQ(2, rangeOuterSurroundingText->startOffset());
    EXPECT_EQ(outer, rangeOuterSurroundingText->endContainer());
    EXPECT_EQ(4, rangeOuterSurroundingText->endOffset());

    EXPECT_TRUE(rangeInnerLeft->boundaryPointsValid());
    EXPECT_EQ(innerLeft, rangeInnerLeft->startContainer());
    EXPECT_EQ(0, rangeInnerLeft->startOffset());
    EXPECT_EQ(innerLeft, rangeInnerLeft->endContainer());
    EXPECT_EQ(1, rangeInnerLeft->endOffset());

    EXPECT_TRUE(rangeInnerRight->boundaryPointsValid());
    EXPECT_EQ(innerRight, rangeInnerRight->startContainer());
    EXPECT_EQ(0, rangeInnerRight->startOffset());
    EXPECT_EQ(innerRight, rangeInnerRight->endContainer());
    EXPECT_EQ(1, rangeInnerRight->endOffset());

    EXPECT_TRUE(rangeFromTextToMiddleOfElement->boundaryPointsValid());
    EXPECT_EQ(newText, rangeFromTextToMiddleOfElement->startContainer());
    EXPECT_EQ(3, rangeFromTextToMiddleOfElement->startOffset());
    EXPECT_EQ(outer, rangeFromTextToMiddleOfElement->endContainer());
    EXPECT_EQ(4, rangeFromTextToMiddleOfElement->endOffset());

    // StaticRange shouldn't mutate.
    EXPECT_EQ(outer, staticRangeOuterOutside->startContainer());
    EXPECT_EQ(0, staticRangeOuterOutside->startOffset());
    EXPECT_EQ(outer, staticRangeOuterOutside->endContainer());
    EXPECT_EQ(5, staticRangeOuterOutside->endOffset());

    EXPECT_EQ(outer, staticRangeOuterInside->startContainer());
    EXPECT_EQ(1, staticRangeOuterInside->startOffset());
    EXPECT_EQ(outer, staticRangeOuterInside->endContainer());
    EXPECT_EQ(4, staticRangeOuterInside->endOffset());

    EXPECT_EQ(outer, staticRangeOuterSurroundingText->startContainer());
    EXPECT_EQ(2, staticRangeOuterSurroundingText->startOffset());
    EXPECT_EQ(outer, staticRangeOuterSurroundingText->endContainer());
    EXPECT_EQ(3, staticRangeOuterSurroundingText->endOffset());

    EXPECT_EQ(innerLeft, staticRangeInnerLeft->startContainer());
    EXPECT_EQ(0, staticRangeInnerLeft->startOffset());
    EXPECT_EQ(innerLeft, staticRangeInnerLeft->endContainer());
    EXPECT_EQ(1, staticRangeInnerLeft->endOffset());

    EXPECT_EQ(innerRight, staticRangeInnerRight->startContainer());
    EXPECT_EQ(0, staticRangeInnerRight->startOffset());
    EXPECT_EQ(innerRight, staticRangeInnerRight->endContainer());
    EXPECT_EQ(1, staticRangeInnerRight->endOffset());

    EXPECT_EQ(oldText, staticRangeFromTextToMiddleOfElement->startContainer());
    EXPECT_EQ(6, staticRangeFromTextToMiddleOfElement->startOffset());
    EXPECT_EQ(outer, staticRangeFromTextToMiddleOfElement->endContainer());
    EXPECT_EQ(3, staticRangeFromTextToMiddleOfElement->endOffset());
}
TEST_F(TextFinderTest, FindTextSimple)
{
    document().body()->setInnerHTML("XXXXFindMeYYYYfindmeZZZZ", ASSERT_NO_EXCEPTION);
    Node* textNode = document().body()->firstChild();

    int identifier = 0;
    WebString searchText(String("FindMe"));
    WebFindOptions findOptions; // Default.
    bool wrapWithinFrame = true;
    WebRect* selectionRect = nullptr;

    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    Range* activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textNode, activeMatch->startContainer());
    EXPECT_EQ(4, activeMatch->startOffset());
    EXPECT_EQ(textNode, activeMatch->endContainer());
    EXPECT_EQ(10, activeMatch->endOffset());

    findOptions.findNext = true;
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textNode, activeMatch->startContainer());
    EXPECT_EQ(14, activeMatch->startOffset());
    EXPECT_EQ(textNode, activeMatch->endContainer());
    EXPECT_EQ(20, activeMatch->endOffset());

    // Should wrap to the first match.
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textNode, activeMatch->startContainer());
    EXPECT_EQ(4, activeMatch->startOffset());
    EXPECT_EQ(textNode, activeMatch->endContainer());
    EXPECT_EQ(10, activeMatch->endOffset());

    // Search in the reverse order.
    identifier = 1;
    findOptions = WebFindOptions();
    findOptions.forward = false;

    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textNode, activeMatch->startContainer());
    EXPECT_EQ(14, activeMatch->startOffset());
    EXPECT_EQ(textNode, activeMatch->endContainer());
    EXPECT_EQ(20, activeMatch->endOffset());

    findOptions.findNext = true;
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textNode, activeMatch->startContainer());
    EXPECT_EQ(4, activeMatch->startOffset());
    EXPECT_EQ(textNode, activeMatch->endContainer());
    EXPECT_EQ(10, activeMatch->endOffset());

    // Wrap to the first match (last occurence in the document).
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textNode, activeMatch->startContainer());
    EXPECT_EQ(14, activeMatch->startOffset());
    EXPECT_EQ(textNode, activeMatch->endContainer());
    EXPECT_EQ(20, activeMatch->endOffset());
}
示例#10
0
TEST_F(TextFinderTest, FindTextInShadowDOM)
{
    document().body()->setInnerHTML("<b>FOO</b><i>foo</i>", ASSERT_NO_EXCEPTION);
    RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = document().body()->createShadowRootInternal(ShadowRootType::V0, ASSERT_NO_EXCEPTION);
    shadowRoot->setInnerHTML("<content select=\"i\"></content><u>Foo</u><content></content>", ASSERT_NO_EXCEPTION);
    Node* textInBElement = document().body()->firstChild()->firstChild();
    Node* textInIElement = document().body()->lastChild()->firstChild();
    Node* textInUElement = shadowRoot->childNodes()->item(1)->firstChild();

    int identifier = 0;
    WebString searchText(String("foo"));
    WebFindOptions findOptions; // Default.
    bool wrapWithinFrame = true;
    WebRect* selectionRect = nullptr;

    // TextIterator currently returns the matches in the composed treeorder, so
    // in this case the matches will be returned in the order of
    // <i> -> <u> -> <b>.
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    Range* activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textInIElement, activeMatch->startContainer());
    EXPECT_EQ(0, activeMatch->startOffset());
    EXPECT_EQ(textInIElement, activeMatch->endContainer());
    EXPECT_EQ(3, activeMatch->endOffset());

    findOptions.findNext = true;
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textInUElement, activeMatch->startContainer());
    EXPECT_EQ(0, activeMatch->startOffset());
    EXPECT_EQ(textInUElement, activeMatch->endContainer());
    EXPECT_EQ(3, activeMatch->endOffset());

    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textInBElement, activeMatch->startContainer());
    EXPECT_EQ(0, activeMatch->startOffset());
    EXPECT_EQ(textInBElement, activeMatch->endContainer());
    EXPECT_EQ(3, activeMatch->endOffset());

    // Should wrap to the first match.
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textInIElement, activeMatch->startContainer());
    EXPECT_EQ(0, activeMatch->startOffset());
    EXPECT_EQ(textInIElement, activeMatch->endContainer());
    EXPECT_EQ(3, activeMatch->endOffset());

    // Fresh search in the reverse order.
    identifier = 1;
    findOptions = WebFindOptions();
    findOptions.forward = false;

    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textInBElement, activeMatch->startContainer());
    EXPECT_EQ(0, activeMatch->startOffset());
    EXPECT_EQ(textInBElement, activeMatch->endContainer());
    EXPECT_EQ(3, activeMatch->endOffset());

    findOptions.findNext = true;
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textInUElement, activeMatch->startContainer());
    EXPECT_EQ(0, activeMatch->startOffset());
    EXPECT_EQ(textInUElement, activeMatch->endContainer());
    EXPECT_EQ(3, activeMatch->endOffset());

    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textInIElement, activeMatch->startContainer());
    EXPECT_EQ(0, activeMatch->startOffset());
    EXPECT_EQ(textInIElement, activeMatch->endContainer());
    EXPECT_EQ(3, activeMatch->endOffset());

    // And wrap.
    ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
    activeMatch = textFinder().activeMatch();
    ASSERT_TRUE(activeMatch);
    EXPECT_EQ(textInBElement, activeMatch->startContainer());
    EXPECT_EQ(0, activeMatch->startOffset());
    EXPECT_EQ(textInBElement, activeMatch->endContainer());
    EXPECT_EQ(3, activeMatch->endOffset());
}