TEST_F(MemoryCacheTest, ResourceMapIsolation)
{
    ResourcePtr<FakeResource> resource1 = new FakeResource(ResourceRequest("http://test/resource"), Resource::Raw);
    memoryCache()->add(resource1.get());

    ResourcePtr<FakeResource> resource2 = new FakeResource(ResourceRequest("http://test/resource"), Resource::Raw);
    resource2->setCacheIdentifier("foo");
    memoryCache()->add(resource2.get());
    EXPECT_TRUE(memoryCache()->contains(resource1.get()));
    EXPECT_TRUE(memoryCache()->contains(resource2.get()));

    const KURL url = KURL(ParsedURLString, "http://test/resource");
    EXPECT_EQ(resource1.get(), memoryCache()->resourceForURL(url));
    EXPECT_EQ(resource1.get(), memoryCache()->resourceForURL(url, memoryCache()->defaultCacheIdentifier()));
    EXPECT_EQ(resource2.get(), memoryCache()->resourceForURL(url, "foo"));
    EXPECT_EQ(0, memoryCache()->resourceForURL(KURL()));

    ResourcePtr<FakeResource> resource3 = new FakeResource(ResourceRequest("http://test/resource"), Resource::Raw);
    resource3->setCacheIdentifier("foo");
    memoryCache()->remove(resource2.get());
    memoryCache()->add(resource3.get());
    EXPECT_TRUE(memoryCache()->contains(resource1.get()));
    EXPECT_FALSE(memoryCache()->contains(resource2.get()));
    EXPECT_TRUE(memoryCache()->contains(resource3.get()));

    ResourcePtr<FakeResource> resource4 = new FakeResource(ResourceRequest("http://test/resource"), Resource::Raw);
    resource4->setCacheIdentifier("foo");
    memoryCache()->replace(resource4.get(), resource3.get());
    EXPECT_TRUE(memoryCache()->contains(resource1.get()));
    EXPECT_FALSE(memoryCache()->contains(resource3.get()));
    EXPECT_TRUE(memoryCache()->contains(resource4.get()));

    WillBeHeapVector<RawPtrWillBeMember<Resource>> resources = memoryCache()->resourcesForURL(url);
    EXPECT_EQ(2u, resources.size());

    memoryCache()->evictResources();
    EXPECT_FALSE(memoryCache()->contains(resource1.get()));
    EXPECT_FALSE(memoryCache()->contains(resource3.get()));
}
bool HTMLFormControlElement::reportValidity()
{
    WillBeHeapVector<RefPtrWillBeMember<HTMLFormControlElement>> unhandledInvalidControls;
    bool isValid = checkValidity(&unhandledInvalidControls, CheckValidityDispatchInvalidEvent);
    if (isValid || unhandledInvalidControls.isEmpty())
        return isValid;
    ASSERT(unhandledInvalidControls.size() == 1);
    ASSERT(unhandledInvalidControls[0].get() == this);
    // Update layout now before calling isFocusable(), which has
    // !layoutObject()->needsLayout() assertion.
    document().updateLayoutIgnorePendingStylesheets();
    if (isFocusable()) {
        showValidationMessage();
        return false;
    }
    if (document().frame()) {
        String message("An invalid form control with name='%name' is not focusable.");
        message.replace("%name", name());
        document().addConsoleMessage(ConsoleMessage::create(RenderingMessageSource, ErrorMessageLevel, message));
    }
    return false;
}
PassRefPtrWillBeRawPtr<MHTMLArchive> MHTMLArchive::create(const KURL& url, SharedBuffer* data)
{
    // For security reasons we only load MHTML pages from local URLs.
    if (!SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol()))
        return nullptr;

    MHTMLParser parser(data);
    WillBeHeapVector<RefPtrWillBeMember<ArchiveResource>> resources = parser.parseArchive();
    if (resources.isEmpty())
        return nullptr; // Invalid MHTML file.

    RefPtrWillBeRawPtr<MHTMLArchive> archive = adoptRefWillBeNoop(new MHTMLArchive);
    // The first document suitable resource is the main resource of the top frame.
    for (size_t i = 0; i < resources.size(); ++i) {
        const AtomicString& mimeType = resources[i]->mimeType();
        if (archive->mainResource() || !MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType) || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) || mimeType == "text/css")
            archive->addSubresource(resources[i].get());
        else
            archive->setMainResource(resources[i].get());
    }
    return archive.release();
}
Example #4
0
void NodeSet::traversalSort() const
{
    WillBeHeapHashSet<RawPtrWillBeMember<Node> > nodes;
    bool containsAttributeNodes = false;

    unsigned nodeCount = m_nodes.size();
    ASSERT(nodeCount > 1);
    for (unsigned i = 0; i < nodeCount; ++i) {
        Node* node = m_nodes[i].get();
        nodes.add(node);
        if (node->isAttributeNode())
            containsAttributeNodes = true;
    }

    WillBeHeapVector<RefPtrWillBeMember<Node> > sortedNodes;
    sortedNodes.reserveInitialCapacity(nodeCount);

    for (Node* n = findRootNode(m_nodes.first().get()); n; n = NodeTraversal::next(*n)) {
        if (nodes.contains(n))
            sortedNodes.append(n);

        if (!containsAttributeNodes || !n->isElementNode())
            continue;

        Element* element = toElement(n);
        if (!element->hasAttributes())
            continue;

        unsigned attributeCount = element->attributeCount();
        for (unsigned i = 0; i < attributeCount; ++i) {
            RefPtrWillBeRawPtr<Attr> attr = element->attrIfExists(element->attributeItem(i).name());
            if (attr && nodes.contains(attr.get()))
                sortedNodes.append(attr);
        }
    }

    ASSERT(sortedNodes.size() == nodeCount);
    const_cast<WillBeHeapVector<RefPtrWillBeMember<Node> >&>(m_nodes).swap(sortedNodes);
}
void FontFace::setLoadStatus(LoadStatus status)
{
    m_status = status;
    ASSERT(m_status != Error || m_error);

    if (m_status == Loaded || m_status == Error) {
        if (m_loadedProperty) {
            if (m_status == Loaded)
                m_loadedProperty->resolve(this);
            else
                m_loadedProperty->reject(m_error.get());
        }

        WillBeHeapVector<RefPtrWillBeMember<LoadFontCallback>> callbacks;
        m_callbacks.swap(callbacks);
        for (size_t i = 0; i < callbacks.size(); ++i) {
            if (m_status == Loaded)
                callbacks[i]->notifyLoaded(this);
            else
                callbacks[i]->notifyError(this);
        }
    }
}
static String selectMisspellingAsync(LocalFrame* selectedFrame, DocumentMarker& marker)
{
    VisibleSelection selection = selectedFrame->selection().selection();
    if (!selection.isCaretOrRange())
        return String();

    // Caret and range selections always return valid normalized ranges.
    RefPtrWillBeRawPtr<Range> selectionRange = selection.toNormalizedRange();
    WillBeHeapVector<DocumentMarker*> markers = selectedFrame->document()->markers().markersInRange(selectionRange.get(), DocumentMarker::MisspellingMarkers());
    if (markers.size() != 1)
        return String();
    marker = *markers[0];

    // Cloning a range fails only for invalid ranges.
    RefPtrWillBeRawPtr<Range> markerRange = selectionRange->cloneRange();
    markerRange->setStart(markerRange->startContainer(), marker.startOffset());
    markerRange->setEnd(markerRange->endContainer(), marker.endOffset());

    if (markerRange->text().stripWhiteSpace(&IsWhiteSpaceOrPunctuation) != selectionRange->text().stripWhiteSpace(&IsWhiteSpaceOrPunctuation))
        return String();

    return markerRange->text();
}
int SimplifyMarkupCommand::pruneSubsequentAncestorsToRemove(WillBeHeapVector<RefPtrWillBeMember<ContainerNode>>& nodesToRemove, size_t startNodeIndex)
{
    size_t pastLastNodeToRemove = startNodeIndex + 1;
    for (; pastLastNodeToRemove < nodesToRemove.size(); ++pastLastNodeToRemove) {
        if (nodesToRemove[pastLastNodeToRemove - 1]->parentNode() != nodesToRemove[pastLastNodeToRemove])
            break;
        ASSERT(nodesToRemove[pastLastNodeToRemove]->firstChild() == nodesToRemove[pastLastNodeToRemove]->lastChild());
    }

    ContainerNode* highestAncestorToRemove = nodesToRemove[pastLastNodeToRemove - 1].get();
    RefPtrWillBeRawPtr<ContainerNode> parent = highestAncestorToRemove->parentNode();
    if (!parent) // Parent has already been removed.
        return -1;

    if (pastLastNodeToRemove == startNodeIndex + 1)
        return 0;

    removeNode(nodesToRemove[startNodeIndex], AssumeContentIsAlwaysEditable);
    insertNodeBefore(nodesToRemove[startNodeIndex], highestAncestorToRemove, AssumeContentIsAlwaysEditable);
    removeNode(highestAncestorToRemove, AssumeContentIsAlwaysEditable);

    return pastLastNodeToRemove - startNodeIndex - 1;
}
bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(WillBeHeapVector<RefPtrWillBeMember<HTMLFormControlElement>>* unhandledInvalidControls, CheckValidityEventBehavior eventBehavior)
{
    RefPtrWillBeRawPtr<HTMLFormElement> protector(this);
    // Copy associatedElements because event handlers called from
    // HTMLFormControlElement::checkValidity() might change associatedElements.
    const FormAssociatedElement::List& associatedElements = this->associatedElements();
    WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement>> elements;
    elements.reserveCapacity(associatedElements.size());
    for (unsigned i = 0; i < associatedElements.size(); ++i)
        elements.append(associatedElements[i]);
    int invalidControlsCount = 0;
    for (unsigned i = 0; i < elements.size(); ++i) {
        if (elements[i]->form() == this && elements[i]->isFormControlElement()) {
            HTMLFormControlElement* control = toHTMLFormControlElement(elements[i].get());
            if (control->isSubmittableElement() && !control->checkValidity(unhandledInvalidControls, eventBehavior) && control->formOwner() == this) {
                ++invalidControlsCount;
                if (!unhandledInvalidControls && eventBehavior == CheckValidityDispatchNoEvent)
                    return true;
            }
        }
    }
    return invalidControlsCount;
}
void HTMLFormElement::anonymousNamedGetter(const AtomicString& name, RadioNodeListOrElement& returnValue)
{
    // Call getNamedElements twice, first time check if it has a value
    // and let HTMLFormElement update its cache.
    // See issue: 867404
    {
        WillBeHeapVector<RefPtrWillBeMember<Element>> elements;
        getNamedElements(name, elements);
        if (elements.isEmpty())
            return;
    }

    // Second call may return different results from the first call,
    // but if the first the size cannot be zero.
    WillBeHeapVector<RefPtrWillBeMember<Element>> elements;
    getNamedElements(name, elements);
    ASSERT(!elements.isEmpty());

    bool onlyMatchImg = !elements.isEmpty() && isHTMLImageElement(*elements.first());
    if (onlyMatchImg) {
        UseCounter::count(document(), UseCounter::FormNameAccessForImageElement);
        // The following code has performance impact, but it should be small
        // because <img> access via <form> name getter is rarely used.
        for (auto& element : elements) {
            if (isHTMLImageElement(*element) && !element->isDescendantOf(this)) {
                UseCounter::count(document(), UseCounter::FormNameAccessForNonDescendantImageElement);
                break;
            }
        }
    }
    if (elements.size() == 1) {
        returnValue.setElement(elements.at(0));
        return;
    }

    returnValue.setRadioNodeList(radioNodeList(name, onlyMatchImg));
}
void MutationObserver::deliver()
{
    ASSERT(!shouldBeSuspended());

    // Calling clearTransientRegistrations() can modify m_registrations, so it's necessary
    // to make a copy of the transient registrations before operating on them.
    WillBeHeapVector<RawPtrWillBeMember<MutationObserverRegistration>, 1> transientRegistrations;
    for (auto& registration : m_registrations) {
        if (registration->hasTransientRegistrations())
            transientRegistrations.append(registration);
    }
    for (size_t i = 0; i < transientRegistrations.size(); ++i)
        transientRegistrations[i]->clearTransientRegistrations();

    if (m_records.isEmpty())
        return;

    MutationRecordVector records;
    records.swap(m_records);

    InspectorInstrumentation::willDeliverMutationRecords(m_callback->executionContext(), this);
    m_callback->call(records, this);
    InspectorInstrumentation::didDeliverMutationRecords(m_callback->executionContext());
}
Example #11
0
void PageAnimator::serviceScriptedAnimations(double monotonicAnimationStartTime)
{
    m_animationFramePending = false;
    TemporaryChange<bool> servicing(m_servicingAnimations, true);

    for (RefPtr<Frame> frame = m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
        if (frame->isLocalFrame()) {
            RefPtr<LocalFrame> localFrame = toLocalFrame(frame.get());
            localFrame->view()->serviceScrollAnimations();

            DocumentAnimations::updateAnimationTimingForAnimationFrame(*localFrame->document(), monotonicAnimationStartTime);
            SVGDocumentExtensions::serviceOnAnimationFrame(*localFrame->document(), monotonicAnimationStartTime);
        }
    }

    WillBeHeapVector<RefPtrWillBeMember<Document> > documents;
    for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
        if (frame->isLocalFrame())
            documents.append(toLocalFrame(frame)->document());
    }

    for (size_t i = 0; i < documents.size(); ++i)
        documents[i]->serviceScriptedAnimations(monotonicAnimationStartTime);
}
Example #12
0
void HTMLTextAreaElement::setDefaultValue(const String& defaultValue)
{
    RefPtrWillBeRawPtr<Node> protectFromMutationEvents(this);

    // To preserve comments, remove only the text nodes, then add a single text node.
    WillBeHeapVector<RefPtrWillBeMember<Node>> textNodes;
    for (Node* n = firstChild(); n; n = n->nextSibling()) {
        if (n->isTextNode())
            textNodes.append(n);
    }
    size_t size = textNodes.size();
    for (size_t i = 0; i < size; ++i)
        removeChild(textNodes[i].get(), IGNORE_EXCEPTION);

    // Normalize line endings.
    String value = defaultValue;
    value.replace("\r\n", "\n");
    value.replace('\r', '\n');

    insertBefore(document().createTextNode(value), firstChild(), IGNORE_EXCEPTION);

    if (!m_isDirty)
        setNonDirtyValue(value);
}
void BreakBlockquoteCommand::doApply()
{
    if (endingSelection().isNone())
        return;

    // Delete the current selection.
    if (endingSelection().isRange())
        deleteSelection(false, false);

    // This is a scenario that should never happen, but we want to
    // make sure we don't dereference a null pointer below.

    ASSERT(!endingSelection().isNone());

    if (endingSelection().isNone())
        return;

    VisiblePosition visiblePos = endingSelection().visibleStart();

    // pos is a position equivalent to the caret.  We use downstream() so that pos will
    // be in the first node that we need to move (there are a few exceptions to this, see below).
    Position pos = mostForwardCaretPosition(endingSelection().start());

    // Find the top-most blockquote from the start.
    HTMLQuoteElement* topBlockquote = toHTMLQuoteElement(highestEnclosingNodeOfType(pos, isMailHTMLBlockquoteElement));
    if (!topBlockquote || !topBlockquote->parentNode())
        return;

    RefPtrWillBeRawPtr<HTMLBRElement> breakElement = createBreakElement(document());

    bool isLastVisPosInNode = isLastVisiblePositionInNode(visiblePos, topBlockquote);

    // If the position is at the beginning of the top quoted content, we don't need to break the quote.
    // Instead, insert the break before the blockquote, unless the position is as the end of the the quoted content.
    if (isFirstVisiblePositionInNode(visiblePos, topBlockquote) && !isLastVisPosInNode) {
        insertNodeBefore(breakElement.get(), topBlockquote);
        setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get()), TextAffinity::Downstream, endingSelection().isDirectional()));
        rebalanceWhitespace();
        return;
    }

    // Insert a break after the top blockquote.
    insertNodeAfter(breakElement.get(), topBlockquote);

    // If we're inserting the break at the end of the quoted content, we don't need to break the quote.
    if (isLastVisPosInNode) {
        setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get()), TextAffinity::Downstream, endingSelection().isDirectional()));
        rebalanceWhitespace();
        return;
    }

    // Don't move a line break just after the caret.  Doing so would create an extra, empty paragraph
    // in the new blockquote.
    if (lineBreakExistsAtVisiblePosition(visiblePos)) {
        // TODO(yosin) We should use |PositionMoveType::Character| for
        // |nextPositionOf()| to avoid editing middle of character.
        pos = nextPositionOf(pos, PositionMoveType::CodePoint);
    }

    // Adjust the position so we don't split at the beginning of a quote.
    while (isFirstVisiblePositionInNode(createVisiblePosition(pos), toHTMLQuoteElement(enclosingNodeOfType(pos, isMailHTMLBlockquoteElement)))) {
        // TODO(yosin) We should use |PositionMoveType::Character| for
        // |previousPositionOf()| to avoid editing middle character.
        pos = previousPositionOf(pos, PositionMoveType::CodePoint);
    }

    // startNode is the first node that we need to move to the new blockquote.
    Node* startNode = pos.anchorNode();
    ASSERT(startNode);

    // Split at pos if in the middle of a text node.
    if (startNode->isTextNode()) {
        Text* textNode = toText(startNode);
        int textOffset = pos.computeOffsetInContainerNode();
        if ((unsigned)textOffset >= textNode->length()) {
            startNode = NodeTraversal::next(*startNode);
            ASSERT(startNode);
        } else if (textOffset > 0) {
            splitTextNode(textNode, textOffset);
        }
    } else if (pos.computeEditingOffset() > 0) {
        Node* childAtOffset = NodeTraversal::childAt(*startNode, pos.computeEditingOffset());
        startNode = childAtOffset ? childAtOffset : NodeTraversal::next(*startNode);
        ASSERT(startNode);
    }

    // If there's nothing inside topBlockquote to move, we're finished.
    if (!startNode->isDescendantOf(topBlockquote)) {
        setEndingSelection(VisibleSelection(createVisiblePosition(firstPositionInOrBeforeNode(startNode)), endingSelection().isDirectional()));
        return;
    }

    // Build up list of ancestors in between the start node and the top blockquote.
    WillBeHeapVector<RefPtrWillBeMember<Element>> ancestors;
    for (Element* node = startNode->parentElement(); node && node != topBlockquote; node = node->parentElement())
        ancestors.append(node);

    // Insert a clone of the top blockquote after the break.
    RefPtrWillBeRawPtr<Element> clonedBlockquote = topBlockquote->cloneElementWithoutChildren();
    insertNodeAfter(clonedBlockquote.get(), breakElement.get());

    // Clone startNode's ancestors into the cloned blockquote.
    // On exiting this loop, clonedAncestor is the lowest ancestor
    // that was cloned (i.e. the clone of either ancestors.last()
    // or clonedBlockquote if ancestors is empty).
    RefPtrWillBeRawPtr<Element> clonedAncestor = clonedBlockquote;
    for (size_t i = ancestors.size(); i != 0; --i) {
        RefPtrWillBeRawPtr<Element> clonedChild = ancestors[i - 1]->cloneElementWithoutChildren();
        // Preserve list item numbering in cloned lists.
        if (isHTMLOListElement(*clonedChild)) {
            Node* listChildNode = i > 1 ? ancestors[i - 2].get() : startNode;
            // The first child of the cloned list might not be a list item element,
            // find the first one so that we know where to start numbering.
            while (listChildNode && !isHTMLLIElement(*listChildNode))
                listChildNode = listChildNode->nextSibling();
            if (isListItem(listChildNode))
                setNodeAttribute(clonedChild, startAttr, AtomicString::number(toLayoutListItem(listChildNode->layoutObject())->value()));
        }

        appendNode(clonedChild.get(), clonedAncestor.get());
        clonedAncestor = clonedChild;
    }

    moveRemainingSiblingsToNewParent(startNode, 0, clonedAncestor);

    if (!ancestors.isEmpty()) {
        // Split the tree up the ancestor chain until the topBlockquote
        // Throughout this loop, clonedParent is the clone of ancestor's parent.
        // This is so we can clone ancestor's siblings and place the clones
        // into the clone corresponding to the ancestor's parent.
        RefPtrWillBeRawPtr<Element> ancestor = nullptr;
        RefPtrWillBeRawPtr<Element> clonedParent = nullptr;
        for (ancestor = ancestors.first(), clonedParent = clonedAncestor->parentElement();
            ancestor && ancestor != topBlockquote;
            ancestor = ancestor->parentElement(), clonedParent = clonedParent->parentElement())
            moveRemainingSiblingsToNewParent(ancestor->nextSibling(), 0, clonedParent);

        // If the startNode's original parent is now empty, remove it
        Element* originalParent = ancestors.first().get();
        if (!originalParent->hasChildren())
            removeNode(originalParent);
    }

    // Make sure the cloned block quote renders.
    addBlockPlaceholderIfNeeded(clonedBlockquote.get());

    // Put the selection right before the break.
    setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get()), TextAffinity::Downstream, endingSelection().isDirectional()));
    rebalanceWhitespace();
}
void AutomaticTrackSelection::performAutomaticTextTrackSelection(const TrackGroup& group)
{
    ASSERT(group.tracks.size());

    // First, find the track in the group that should be enabled (if any).
    WillBeHeapVector<RefPtrWillBeMember<TextTrack>> currentlyEnabledTracks;
    RefPtrWillBeRawPtr<TextTrack> trackToEnable = nullptr;
    RefPtrWillBeRawPtr<TextTrack> defaultTrack = nullptr;
    RefPtrWillBeRawPtr<TextTrack> preferredTrack = nullptr;
    RefPtrWillBeRawPtr<TextTrack> fallbackTrack = nullptr;

    int highestTrackScore = 0;

    for (size_t i = 0; i < group.tracks.size(); ++i) {
        RefPtrWillBeRawPtr<TextTrack> textTrack = group.tracks[i];

        if (m_configuration.disableCurrentlyEnabledTracks && textTrack->mode() == TextTrack::showingKeyword())
            currentlyEnabledTracks.append(textTrack);

        int trackScore = textTrackSelectionScore(*textTrack);

        if (textTrack->kind() == preferredTrackKind())
            trackScore += 1;
        if (trackScore) {
            // * If the text track kind is subtitles or captions and the user has indicated an interest in having a
            // track with this text track kind, text track language, and text track label enabled, and there is no
            // other text track in the media element's list of text tracks with a text track kind of either subtitles
            // or captions whose text track mode is showing
            //    Let the text track mode be showing.
            if (trackScore > highestTrackScore) {
                preferredTrack = textTrack;
                highestTrackScore = trackScore;
            }
            if (!defaultTrack && textTrack->isDefault())
                defaultTrack = textTrack;

            if (!fallbackTrack)
                fallbackTrack = textTrack;
        } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) {
            // * If the track element has a default attribute specified, and there is no other text track in the media
            // element's list of text tracks whose text track mode is showing or showing by default
            //    Let the text track mode be showing by default.
            defaultTrack = textTrack;
        }
    }

    if (m_configuration.textTrackKindUserPreference != TextTrackKindUserPreference::Default)
        trackToEnable = preferredTrack;

    if (!trackToEnable && defaultTrack)
        trackToEnable = defaultTrack;

    if (!trackToEnable && m_configuration.forceEnableSubtitleOrCaptionTrack && group.kind == TrackGroup::CaptionsAndSubtitles)
        trackToEnable = fallbackTrack ? fallbackTrack : group.tracks[0];

    if (currentlyEnabledTracks.size()) {
        for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) {
            RefPtrWillBeRawPtr<TextTrack> textTrack = currentlyEnabledTracks[i];
            if (textTrack != trackToEnable)
                textTrack->setMode(TextTrack::disabledKeyword());
        }
    }

    if (trackToEnable)
        trackToEnable->setMode(TextTrack::showingKeyword());
}
void ScopedStyleResolver::addKeyframeRules(const RuleSet& ruleSet)
{
    const WillBeHeapVector<RawPtrWillBeMember<StyleRuleKeyframes>> keyframesRules = ruleSet.keyframesRules();
    for (unsigned i = 0; i < keyframesRules.size(); ++i)
        addKeyframeStyle(keyframesRules[i]);
}
StyleSheetInvalidationAnalysis::StyleSheetInvalidationAnalysis(const TreeScope& treeScope, const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents>>& sheets)
    : m_treeScope(&treeScope)
{
    for (unsigned i = 0; i < sheets.size() && !m_dirtiesAllStyle; ++i)
        analyzeStyleSheet(sheets[i]);
}
Example #17
0
void MutableStylePropertySet::addParsedProperties(const WillBeHeapVector<CSSProperty, 256>& properties)
{
    m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
    for (unsigned i = 0; i < properties.size(); ++i)
        addParsedProperty(properties[i]);
}
Example #18
0
void CueTimeline::updateActiveCues(double movieTime)
{
    // 4.8.10.8 Playing the media resource

    //  If the current playback position changes while the steps are running,
    //  then the user agent must wait for the steps to complete, and then must
    //  immediately rerun the steps.
    if (ignoreUpdateRequests())
        return;

    HTMLMediaElement& mediaElement = this->mediaElement();

    // https://html.spec.whatwg.org/#time-marches-on

    // 1 - Let current cues be a list of cues, initialized to contain all the
    // cues of all the hidden, showing, or showing by default text tracks of the
    // media element (not the disabled ones) whose start times are less than or
    // equal to the current playback position and whose end times are greater
    // than the current playback position.
    CueList currentCues;

    // The user agent must synchronously unset [the text track cue active] flag
    // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
    if (mediaElement.readyState() != HTMLMediaElement::HAVE_NOTHING && mediaElement.webMediaPlayer())
        currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));

    CueList previousCues;
    CueList missedCues;

    // 2 - Let other cues be a list of cues, initialized to contain all the cues
    // of hidden, showing, and showing by default text tracks of the media
    // element that are not present in current cues.
    previousCues = m_currentlyActiveCues;

    // 3 - Let last time be the current playback position at the time this
    // algorithm was last run for this media element, if this is not the first
    // time it has run.
    double lastTime = m_lastUpdateTime;
    double lastSeekTime = mediaElement.lastSeekTime();

    // 4 - If the current playback position has, since the last time this
    // algorithm was run, only changed through its usual monotonic increase
    // during normal playback, then let missed cues be the list of cues in other
    // cues whose start times are greater than or equal to last time and whose
    // end times are less than or equal to the current playback position.
    // Otherwise, let missed cues be an empty list.
    if (lastTime >= 0 && lastSeekTime < movieTime) {
        CueList potentiallySkippedCues =
            m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime));

        for (CueInterval cue : potentiallySkippedCues) {
            // Consider cues that may have been missed since the last seek time.
            if (cue.low() > std::max(lastSeekTime, lastTime) && cue.high() < movieTime)
                missedCues.append(cue);
        }
    }

    m_lastUpdateTime = movieTime;

    // 5 - If the time was reached through the usual monotonic increase of the
    // current playback position during normal playback, and if the user agent
    // has not fired a timeupdate event at the element in the past 15 to 250ms
    // and is not still running event handlers for such an event, then the user
    // agent must queue a task to fire a simple event named timeupdate at the
    // element. (In the other cases, such as explicit seeks, relevant events get
    // fired as part of the overall process of changing the current playback
    // position.)
    if (!mediaElement.seeking() && lastSeekTime < lastTime)
        mediaElement.scheduleTimeupdateEvent(true);

    // Explicitly cache vector sizes, as their content is constant from here.
    size_t currentCuesSize = currentCues.size();
    size_t missedCuesSize = missedCues.size();
    size_t previousCuesSize = previousCues.size();

    // 6 - If all of the cues in current cues have their text track cue active
    // flag set, none of the cues in other cues have their text track cue active
    // flag set, and missed cues is empty, then abort these steps.
    bool activeSetChanged = missedCuesSize;

    for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i) {
        if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
            activeSetChanged = true;
    }

    for (CueInterval currentCue : currentCues) {
        // Notify any cues that are already active of the current time to mark
        // past and future nodes. Any inactive cues have an empty display state;
        // they will be notified of the current time when the display state is
        // updated.
        if (currentCue.data()->isActive())
            currentCue.data()->updatePastAndFutureNodes(movieTime);
        else
            activeSetChanged = true;
    }

    if (!activeSetChanged)
        return;

    // 7 - If the time was reached through the usual monotonic increase of the
    // current playback position during normal playback, and there are cues in
    // other cues that have their text track cue pause-on-exi flag set and that
    // either have their text track cue active flag set or are also in missed
    // cues, then immediately pause the media element.
    for (size_t i = 0; !mediaElement.paused() && i < previousCuesSize; ++i) {
        if (previousCues[i].data()->pauseOnExit()
            && previousCues[i].data()->isActive()
            && !currentCues.contains(previousCues[i]))
            mediaElement.pause();
    }

    for (size_t i = 0; !mediaElement.paused() && i < missedCuesSize; ++i) {
        if (missedCues[i].data()->pauseOnExit())
            mediaElement.pause();
    }

    // 8 - Let events be a list of tasks, initially empty. Each task in this
    // list will be associated with a text track, a text track cue, and a time,
    // which are used to sort the list before the tasks are queued.
    WillBeHeapVector<std::pair<double, RawPtrWillBeMember<TextTrackCue>>> eventTasks;

    // 8 - Let affected tracks be a list of text tracks, initially empty.
    WillBeHeapVector<RawPtrWillBeMember<TextTrack>> affectedTracks;

    for (size_t i = 0; i < missedCuesSize; ++i) {
        // 9 - For each text track cue in missed cues, prepare an event named enter
        // for the TextTrackCue object with the text track cue start time.
        eventTasks.append(std::make_pair(missedCues[i].data()->startTime(), missedCues[i].data()));

        // 10 - For each text track [...] in missed cues, prepare an event
        // named exit for the TextTrackCue object with the  with the later of
        // the text track cue end time and the text track cue start time.

        // Note: An explicit task is added only if the cue is NOT a zero or
        // negative length cue. Otherwise, the need for an exit event is
        // checked when these tasks are actually queued below. This doesn't
        // affect sorting events before dispatch either, because the exit
        // event has the same time as the enter event.
        if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime())
            eventTasks.append(std::make_pair(missedCues[i].data()->endTime(), missedCues[i].data()));
    }

    for (size_t i = 0; i < previousCuesSize; ++i) {
        // 10 - For each text track cue in other cues that has its text
        // track cue active flag set prepare an event named exit for the
        // TextTrackCue object with the text track cue end time.
        if (!currentCues.contains(previousCues[i]))
            eventTasks.append(std::make_pair(previousCues[i].data()->endTime(), previousCues[i].data()));
    }

    for (size_t i = 0; i < currentCuesSize; ++i) {
        // 11 - For each text track cue in current cues that does not have its
        // text track cue active flag set, prepare an event named enter for the
        // TextTrackCue object with the text track cue start time.
        if (!previousCues.contains(currentCues[i]))
            eventTasks.append(std::make_pair(currentCues[i].data()->startTime(), currentCues[i].data()));
    }

    // 12 - Sort the tasks in events in ascending time order (tasks with earlier
    // times first).
    nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);

    for (size_t i = 0; i < eventTasks.size(); ++i) {
        if (!affectedTracks.contains(eventTasks[i].second->track()))
            affectedTracks.append(eventTasks[i].second->track());

        // 13 - Queue each task in events, in list order.

        // Each event in eventTasks may be either an enterEvent or an exitEvent,
        // depending on the time that is associated with the event. This
        // correctly identifies the type of the event, if the startTime is
        // less than the endTime in the cue.
        if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) {
            mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::enter, eventTasks[i].second));
            mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::exit, eventTasks[i].second));
        } else {
            bool isEnterEvent = eventTasks[i].first == eventTasks[i].second->startTime();
            AtomicString eventName = isEnterEvent ? EventTypeNames::enter : EventTypeNames::exit;
            mediaElement.scheduleEvent(createEventWithTarget(eventName, eventTasks[i].second));
        }
    }

    // 14 - Sort affected tracks in the same order as the text tracks appear in
    // the media element's list of text tracks, and remove duplicates.
    nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);

    // 15 - For each text track in affected tracks, in the list order, queue a
    // task to fire a simple event named cuechange at the TextTrack object, and, ...
    for (size_t i = 0; i < affectedTracks.size(); ++i) {
        mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::cuechange, affectedTracks[i]));

        // ... if the text track has a corresponding track element, to then fire a
        // simple event named cuechange at the track element as well.
        if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
            HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i].get())->trackElement();
            ASSERT(trackElement);
            mediaElement.scheduleEvent(createEventWithTarget(EventTypeNames::cuechange, trackElement));
        }
    }

    // 16 - Set the text track cue active flag of all the cues in the current
    // cues, and unset the text track cue active flag of all the cues in the
    // other cues.
    for (size_t i = 0; i < currentCuesSize; ++i)
        currentCues[i].data()->setIsActive(true);

    for (size_t i = 0; i < previousCuesSize; ++i) {
        if (!currentCues.contains(previousCues[i])) {
            TextTrackCue* cue = previousCues[i].data();
            cue->setIsActive(false);
            cue->removeDisplayTree();
        }
    }

    // Update the current active cues.
    m_currentlyActiveCues = currentCues;
    mediaElement.updateTextTrackDisplay();
}
Example #19
0
void EventPath::calculatePath()
{
    ASSERT(m_node);
    ASSERT(m_nodeEventContexts.isEmpty());
    m_node->updateDistribution();

    // For performance and memory usage reasons we want to store the
    // path using as few bytes as possible and with as few allocations
    // as possible which is why we gather the data on the stack before
    // storing it in a perfectly sized m_nodeEventContexts Vector.
    WillBeHeapVector<RawPtrWillBeMember<Node>, 64> nodesInPath;
    Node* current = m_node;
    nodesInPath.append(current);
    while (current) {
        if (m_event && current->keepEventInNode(m_event))
            break;
        WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
        collectDestinationInsertionPoints(*current, insertionPoints);
        if (!insertionPoints.isEmpty()) {
            for (const auto& insertionPoint : insertionPoints) {
                if (insertionPoint->isShadowInsertionPoint()) {
                    ShadowRoot* containingShadowRoot = insertionPoint->containingShadowRoot();
                    ASSERT(containingShadowRoot);
                    if (!containingShadowRoot->isOldest())
                        nodesInPath.append(containingShadowRoot->olderShadowRoot());
                }
                nodesInPath.append(insertionPoint);
            }
            current = insertionPoints.last();
            continue;
        }
        if (current->isChildOfV1ShadowHost()) {
            if (HTMLSlotElement* slot = current->assignedSlot()) {
                current = slot;
                nodesInPath.append(current);
                continue;
            }
        }
        if (current->isShadowRoot()) {
            if (m_event && shouldStopAtShadowRoot(*m_event, *toShadowRoot(current), *m_node))
                break;
            current = current->shadowHost();
#if !ENABLE(OILPAN)
            // TODO(kochi): crbug.com/507413 This check is necessary when some asynchronous event
            // is queued while its shadow host is removed and the shadow root gets the event
            // immediately after it.  When Oilpan is enabled, this situation does not happen.
            // Except this case, shadow root's host is assumed to be non-null.
            if (current)
                nodesInPath.append(current);
#else
            nodesInPath.append(current);
#endif
        } else {
            current = current->parentNode();
            if (current)
                nodesInPath.append(current);
        }
    }

    m_nodeEventContexts.reserveCapacity(nodesInPath.size());
    for (Node* nodeInPath : nodesInPath) {
        m_nodeEventContexts.append(NodeEventContext(nodeInPath, eventTargetRespectingTargetRules(*nodeInPath)));
    }
}