예제 #1
0
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 SelectorDataList::findTraverseRootsAndExecute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches
    // we would need to sort the results. For now, just traverse the document in that case.
    ASSERT(m_selectors.size() == 1);

    bool isRightmostSelector = true;
    bool startFromParent = false;
    Element* singleMatchingElement = 0;

    for (const CSSSelector* selector = &m_selectors[0].selector; selector; selector = selector->tagHistory()) {
        if (selector->m_match == CSSSelector::Id && (rootNode.document().getNumberOfElementsWithId(selector->value(), singleMatchingElement) <= 1)) {
            ContainerNode* adjustedNode = &rootNode;
            if (singleMatchingElement && (isTreeScopeRoot(rootNode) || singleMatchingElement->isDescendantOf(&rootNode)))
                adjustedNode = singleMatchingElement;
            else if (!singleMatchingElement || isRightmostSelector)
                adjustedNode = 0;
            if (isRightmostSelector) {
                executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], adjustedNode, MatchesTraverseRoots, rootNode, output);
                return;
            }

            if (startFromParent && adjustedNode)
                adjustedNode = adjustedNode->parentNode();

            executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], adjustedNode, DoesNotMatchTraverseRoots, rootNode, output);
            return;
        }

        // If we have both CSSSelector::Id and CSSSelector::Class at the same time, we should use Id
        // to find traverse root.
        if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent && selector->m_match == CSSSelector::Class) {
            if (isRightmostSelector) {
                ClassElementList<AllElements> traverseRoots(rootNode, selector->value());
                executeForTraverseRoots<SelectorQueryTrait>(m_selectors[0], traverseRoots, MatchesTraverseRoots, rootNode, output);
                return;
            }
            // Since there exists some ancestor element which has the class name, we need to see all children of rootNode.
            if (ancestorHasClassName(rootNode, selector->value())) {
                executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output);
                return;
            }

            ClassElementList<OnlyRoots> traverseRoots(rootNode, selector->value());
            executeForTraverseRoots<SelectorQueryTrait>(m_selectors[0], traverseRoots, DoesNotMatchTraverseRoots, rootNode, output);
            return;
        }

        if (selector->relation() == CSSSelector::SubSelector)
            continue;
        isRightmostSelector = false;
        if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
            startFromParent = true;
        else
            startFromParent = false;
    }

    executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output);
}
예제 #3
0
void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    if (!canUseFastQuery(rootNode)) {
        if (m_crossesTreeBoundary) {
            rootNode.document().updateDistributionForNodeIfNeeded(&rootNode);
            executeSlowTraversingShadowTree<SelectorQueryTrait>(rootNode, output);
        } else {
            executeSlow<SelectorQueryTrait>(rootNode, output);
        }
        return;
    }

    ASSERT(m_selectors.size() == 1);

    const CSSSelector& selector = *m_selectors[0];
    const CSSSelector& firstSelector = selector;

    // Fast path for querySelector*('#id'), querySelector*('tag#id').
    if (const CSSSelector* idSelector = selectorForIdLookup(firstSelector)) {
        const AtomicString& idToMatch = idSelector->value();
        if (rootNode.treeScope().containsMultipleElementsWithId(idToMatch)) {
            const Vector<Element*>& elements = rootNode.treeScope().getAllElementsById(idToMatch);
            size_t count = elements.size();
            for (size_t i = 0; i < count; ++i) {
                Element& element = *elements[i];
                if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootNode)))
                    continue;
                if (selectorMatches(selector, element, rootNode)) {
                    SelectorQueryTrait::appendElement(output, element);
                    if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                        return;
                }
            }
            return;
        }
        Element* element = rootNode.treeScope().getElementById(idToMatch);
        if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
            return;
        if (selectorMatches(selector, *element, rootNode))
            SelectorQueryTrait::appendElement(output, *element);
        return;
    }

    if (!firstSelector.tagHistory()) {
        // Fast path for querySelector*('.foo'), and querySelector*('div').
        switch (firstSelector.match()) {
        case CSSSelector::Class:
            collectElementsByClassName<SelectorQueryTrait>(rootNode, firstSelector.value(), output);
            return;
        case CSSSelector::Tag:
            collectElementsByTagName<SelectorQueryTrait>(rootNode, firstSelector.tagQName(), output);
            return;
        default:
            break; // If we need another fast path, add here.
        }
    }

    findTraverseRootsAndExecute<SelectorQueryTrait>(rootNode, output);
}
예제 #4
0
bool SelectorDataList::compileSelector(const SelectorData& selectorData, const ContainerNode& rootNode)
{
    if (selectorData.compilationStatus != SelectorCompilationStatus::NotCompiled)
        return isCompiledSelector(selectorData.compilationStatus);

    JSC::VM& vm = rootNode.document().scriptExecutionContext()->vm();
    selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, &vm, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
    return isCompiledSelector(selectorData.compilationStatus);
}
예제 #5
0
PassRefPtr<JSLazyEventListener> JSLazyEventListener::createForNode(ContainerNode& node, const QualifiedName& attributeName, const AtomicString& attributeValue)
{
    if (attributeValue.isNull())
        return nullptr;

    TextPosition position = TextPosition::minimumPosition();
    String sourceURL;

    // FIXME: We should be able to provide source information for frameless documents too (e.g. for importing nodes from XMLHttpRequest.responseXML).
    if (Frame* frame = node.document().frame()) {
        if (!frame->script().canExecuteScripts(AboutToExecuteScript))
            return nullptr;

        position = frame->script().eventHandlerPosition();
        sourceURL = node.document().url().string();
    }

    return adoptRef(new JSLazyEventListener(attributeName.localName().string(),
        eventParameterName(node.isSVGElement()), attributeValue,
        &node, sourceURL, position, nullptr, mainThreadNormalWorld()));
}
예제 #6
0
inline bool SelectorDataList::canUseFastQuery(const ContainerNode& rootNode) const
{
    if (m_crossesTreeBoundary)
        return false;
    if (m_needsUpdatedDistribution)
        return false;
    if (rootNode.document().inQuirksMode())
        return false;
    if (!rootNode.inDocument())
        return false;
    return m_selectors.size() == 1;
}
inline bool SelectorDataList::canUseFastQuery(const ContainerNode& rootNode) const
{
    if (m_usesDeepCombinatorOrShadowPseudo)
        return false;
    if (m_needsUpdatedDistribution)
        return false;
    if (rootNode.document().inQuirksMode())
        return false;
    if (!rootNode.inDocument())
        return false;
    return m_selectors.size() == 1;
}
예제 #8
0
static void willRemoveChildren(ContainerNode& container)
{
    NodeVector children;
    getChildNodes(container, children);

    ChildListMutationScope mutation(container);
    for (auto it = children.begin(); it != children.end(); ++it) {
        Node& child = it->get();
        mutation.willRemoveChild(child);
        child.notifyMutationObserversNodeWillDetach();

        // fire removed from document mutation events.
        dispatchChildRemovalEvents(child);
    }

    container.document().nodeChildrenWillBeRemoved(container);

    disconnectSubframesIfNeeded(container, DescendantsOnly);
}
예제 #9
0
파일: markup.cpp 프로젝트: rhythmkay/webkit
static void fillContainerFromString(ContainerNode& paragraph, const String& string)
{
    Document& document = paragraph.document();

    if (string.isEmpty()) {
        paragraph.appendChild(createBlockPlaceholderElement(document), ASSERT_NO_EXCEPTION);
        return;
    }

    ASSERT(string.find('\n') == notFound);

    Vector<String> tabList;
    string.split('\t', true, tabList);
    String tabText = emptyString();
    bool first = true;
    size_t numEntries = tabList.size();
    for (size_t i = 0; i < numEntries; ++i) {
        const String& s = tabList[i];

        // append the non-tab textual part
        if (!s.isEmpty()) {
            if (!tabText.isEmpty()) {
                paragraph.appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION);
                tabText = emptyString();
            }
            Ref<Node> textNode = document.createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
            paragraph.appendChild(WTF::move(textNode), ASSERT_NO_EXCEPTION);
        }

        // there is a tab after every entry, except the last entry
        // (if the last character is a tab, the list gets an extra empty entry)
        if (i + 1 != numEntries)
            tabText.append('\t');
        else if (!tabText.isEmpty())
            paragraph.appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION);

        first = false;
    }
}
예제 #10
0
inline bool SelectorDataList::canUseFastQuery(const ContainerNode& rootNode) const
{
    return m_selectors.size() == 1 && rootNode.inDocument() && !rootNode.document().inQuirksMode();
}
HTMLTagCollection::HTMLTagCollection(ContainerNode& rootNode, const AtomicString& localName)
    : TagCollection(rootNode, HTMLTagCollectionType, starAtom, localName)
    , m_loweredLocalName(localName.lower())
{
    ASSERT(rootNode.document().isHTMLDocument());
}
예제 #12
0
void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken* token)
{
    ASSERT(token->type() == HTMLTokenTypes::Comment);
    ContainerNode* parent = m_openElements.rootNode();
    attachLater(parent, Comment::create(parent->document(), token->comment()));
}
예제 #13
0
ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    ContainerNode* searchRootNode = &rootNode;
    switch (m_matchType) {
    case RightMostWithIdMatch:
        {
        const SelectorData& selectorData = m_selectors.first();
        if (const CSSSelector* idSelector = selectorForIdLookup(*searchRootNode, *selectorData.selector)) {
            executeFastPathForIdSelector<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), idSelector, output);
            break;
        }
#if ENABLE(CSS_SELECTOR_JIT)
        if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker)
            goto CompiledSingleCase;
#endif
        }
        FALLTHROUGH;
    case CompilableSingleWithRootFilter:
    case CompilableSingle:
        {
#if ENABLE(CSS_SELECTOR_JIT)
        const SelectorData& selectorData = m_selectors.first();
        ASSERT(m_matchType == RightMostWithIdMatch || selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled);

        JSC::VM& vm = searchRootNode->document().scriptExecutionContext()->vm();
        selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, &vm, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
        RELEASE_ASSERT(selectorData.compilationStatus != SelectorCompilationStatus::SelectorCheckerWithCheckingContext);

        if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
            if (m_matchType == CompilableSingle) {
                m_matchType = CompiledSingle;
                goto CompiledSingleCase;
            }
            if (m_matchType == CompilableSingleWithRootFilter) {
                m_matchType = CompiledSingleWithRootFilter;
                goto CompiledSingleWithRootFilterCase;
            }
            goto CompiledSingleCase;
        }
        if (m_matchType != RightMostWithIdMatch)
            m_matchType = SingleSelector;
        goto SingleSelectorCase;
        ASSERT_NOT_REACHED();
        break;
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
        }
    case CompiledSingleWithRootFilter:
#if ENABLE(CSS_SELECTOR_JIT)
        CompiledSingleWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
#endif // ENABLE(CSS_SELECTOR_JIT)
        FALLTHROUGH;
    case CompiledSingle:
#if ENABLE(CSS_SELECTOR_JIT)
        {
        CompiledSingleCase:
        const SelectorData& selectorData = m_selectors.first();
        void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
        SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
        executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output);
        break;
        }
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case SingleSelector:
#if ENABLE(CSS_SELECTOR_JIT)
        SingleSelectorCase:
#endif
        executeSingleSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case TagNameMatch:
        executeSingleTagNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case ClassNameMatch:
        executeSingleClassNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case MultipleSelectorMatch:
        executeSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
    }
}
EphemeralRange PlainTextRange::createRangeFor(const ContainerNode& scope, GetRangeFor getRangeFor) const
{
    ASSERT(isNotNull());

    size_t docTextPosition = 0;
    bool startRangeFound = false;

    Position textRunStartPosition;
    Position textRunEndPosition;

    TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacementCharacter;
    if (getRangeFor == ForSelection)
        behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions;
    auto range = EphemeralRange::rangeOfContents(scope);
    TextIterator it(range.startPosition(), range.endPosition(), behaviorFlags);

    // FIXME: the atEnd() check shouldn't be necessary, workaround for
    // <http://bugs.webkit.org/show_bug.cgi?id=6289>.
    if (!start() && !length() && it.atEnd())
        return EphemeralRange(Position(it.currentContainer(), 0));

    Position resultStart = Position(&scope.document(), 0);
    Position resultEnd = resultStart;

    for (; !it.atEnd(); it.advance()) {
        int len = it.length();

        textRunStartPosition = it.startPositionInCurrentContainer();
        textRunEndPosition = it.endPositionInCurrentContainer();

        bool foundStart = start() >= docTextPosition && start() <= docTextPosition + len;
        bool foundEnd = end() >= docTextPosition && end() <= docTextPosition + len;

        // Fix textRunRange->endPosition(), but only if foundStart || foundEnd, because it is only
        // in those cases that textRunRange is used.
        if (foundEnd) {
            // FIXME: This is a workaround for the fact that the end of a run
            // is often at the wrong position for emitted '\n's or if the
            // layoutObject of the current node is a replaced element.
            if (len == 1 && (it.characterAt(0) == '\n' || it.isInsideAtomicInlineElement())) {
                it.advance();
                if (!it.atEnd()) {
                    textRunEndPosition = it.startPositionInCurrentContainer();
                } else {
                    Position runEnd = nextPositionOf(createVisiblePosition(textRunStartPosition)).deepEquivalent();
                    if (runEnd.isNotNull())
                        textRunEndPosition = runEnd;
                }
            }
        }

        if (foundStart) {
            startRangeFound = true;
            if (textRunStartPosition.computeContainerNode()->isTextNode()) {
                int offset = start() - docTextPosition;
                resultStart = Position(textRunStartPosition.computeContainerNode(), offset + textRunStartPosition.offsetInContainerNode());
            } else {
                if (start() == docTextPosition)
                    resultStart = textRunStartPosition;
                else
                    resultStart = textRunEndPosition;
            }
        }

        if (foundEnd) {
            if (textRunStartPosition.computeContainerNode()->isTextNode()) {
                int offset = end() - docTextPosition;
                resultEnd = Position(textRunStartPosition.computeContainerNode(), offset + textRunStartPosition.offsetInContainerNode());
            } else {
                if (end() == docTextPosition)
                    resultEnd = textRunStartPosition;
                else
                    resultEnd = textRunEndPosition;
            }
            docTextPosition += len;
            break;
        }
        docTextPosition += len;
    }

    if (!startRangeFound)
        return EphemeralRange();

    if (length() && end() > docTextPosition) { // end() is out of bounds
        resultEnd = textRunEndPosition;
    }

    return EphemeralRange(resultStart.toOffsetInAnchor(), resultEnd.toOffsetInAnchor());
}
예제 #15
0
PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeFor(const ContainerNode& scope, GetRangeFor getRangeFor) const
{
    ASSERT(isNotNull());

    RefPtrWillBeRawPtr<Range> resultRange = scope.document().createRange();

    size_t docTextPosition = 0;
    bool startRangeFound = false;

    Position textRunStartPosition;
    Position textRunEndPosition;

    TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacementCharacter;
    if (getRangeFor == ForSelection)
        behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions;
    auto range = rangeOfContents(const_cast<ContainerNode*>(&scope));
    TextIterator it(range->startPosition(), range->endPosition(), behaviorFlags);

    // FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>.
    if (!start() && !length() && it.atEnd()) {
        resultRange->setStart(it.currentContainer(), 0, ASSERT_NO_EXCEPTION);
        resultRange->setEnd(it.currentContainer(), 0, ASSERT_NO_EXCEPTION);
        return resultRange.release();
    }

    for (; !it.atEnd(); it.advance()) {
        int len = it.length();

        textRunStartPosition = it.startPositionInCurrentContainer();
        textRunEndPosition = it.endPositionInCurrentContainer();

        bool foundStart = start() >= docTextPosition && start() <= docTextPosition + len;
        bool foundEnd = end() >= docTextPosition && end() <= docTextPosition + len;

        // Fix textRunRange->endPosition(), but only if foundStart || foundEnd, because it is only
        // in those cases that textRunRange is used.
        if (foundEnd) {
            // FIXME: This is a workaround for the fact that the end of a run
            // is often at the wrong position for emitted '\n's or if the
            // renderer of the current node is a replaced element.
            if (len == 1 && (it.characterAt(0) == '\n' || it.isInsideReplacedElement())) {
                it.advance();
                if (!it.atEnd()) {
                    textRunEndPosition = it.startPositionInCurrentContainer();
                } else {
                    Position runEnd = VisiblePosition(textRunStartPosition).next().deepEquivalent();
                    if (runEnd.isNotNull())
                        textRunEndPosition = createLegacyEditingPosition(runEnd.containerNode(), runEnd.computeOffsetInContainerNode());
                }
            }
        }

        if (foundStart) {
            startRangeFound = true;
            if (textRunStartPosition.containerNode()->isTextNode()) {
                int offset = start() - docTextPosition;
                resultRange->setStart(textRunStartPosition.containerNode(), offset + textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
            } else {
                if (start() == docTextPosition)
                    resultRange->setStart(textRunStartPosition.containerNode(), textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
                else
                    resultRange->setStart(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
            }
        }

        if (foundEnd) {
            if (textRunStartPosition.containerNode()->isTextNode()) {
                int offset = end() - docTextPosition;
                resultRange->setEnd(textRunStartPosition.containerNode(), offset + textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
            } else {
                if (end() == docTextPosition)
                    resultRange->setEnd(textRunStartPosition.containerNode(), textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
                else
                    resultRange->setEnd(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
            }
            docTextPosition += len;
            break;
        }
        docTextPosition += len;
    }

    if (!startRangeFound)
        return nullptr;

    if (length() && end() > docTextPosition) { // end() is out of bounds
        resultRange->setEnd(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
    }

    return resultRange.release();
}