void AnimationTimeline::serviceAnimations(TimingUpdateReason reason)
{
    TRACE_EVENT0("blink", "AnimationTimeline::serviceAnimations");

    m_lastCurrentTimeInternal = currentTimeInternal();

    m_timing->cancelWake();

    WillBeHeapVector<RawPtrWillBeMember<Animation>> animations;
    animations.reserveInitialCapacity(m_animationsNeedingUpdate.size());
    for (RefPtrWillBeMember<Animation> animation : m_animationsNeedingUpdate)
        animations.append(animation.get());

    std::sort(animations.begin(), animations.end(), Animation::hasLowerPriority);

    for (Animation* animation : animations) {
        if (!animation->update(reason))
            m_animationsNeedingUpdate.remove(animation);
    }

    ASSERT(m_outdatedAnimationCount == 0);

#if ENABLE(ASSERT)
    for (const auto& animation : m_animationsNeedingUpdate)
        ASSERT(!animation->outdated());
#endif
}
예제 #2
0
void AnimationTimeline::serviceAnimations(TimingUpdateReason reason)
{
    TRACE_EVENT0("webkit", "AnimationTimeline::serviceAnimations");

    m_timing->cancelWake();

    double timeToNextEffect = std::numeric_limits<double>::infinity();
    WillBeHeapVector<RawPtrWillBeMember<AnimationPlayer> > players;
    for (WillBeHeapHashSet<RefPtrWillBeMember<AnimationPlayer> >::iterator it = m_playersNeedingUpdate.begin(); it != m_playersNeedingUpdate.end(); ++it)
        players.append(it->get());

    std::sort(players.begin(), players.end(), AnimationPlayer::hasLowerPriority);

    for (size_t i = 0; i < players.size(); ++i) {
        AnimationPlayer* player = players[i];
        if (player->update(reason))
            timeToNextEffect = std::min(timeToNextEffect, player->timeToEffectChange());
        else
            m_playersNeedingUpdate.remove(player);
    }

    if (timeToNextEffect < s_minimumDelay)
        m_timing->serviceOnNextFrame();
    else if (timeToNextEffect != std::numeric_limits<double>::infinity())
        m_timing->wakeAfter(timeToNextEffect - s_minimumDelay);

    ASSERT(!hasOutdatedAnimationPlayer());
}
예제 #3
0
void SVGDocumentExtensions::serviceAnimations(double monotonicAnimationStartTime)
{
    WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers;
    timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());
    WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end();
    for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr)
        (*itr)->timeContainer()->serviceAnimations(monotonicAnimationStartTime);
}
예제 #4
0
WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer>> AnimationTimeline::getAnimationPlayers()
{
    WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer>> animationPlayers;
    for (const auto& player : m_players) {
        if (player->source() && (player->source()->isCurrent() || player->source()->isInEffect()))
            animationPlayers.append(player);
    }
    std::sort(animationPlayers.begin(), animationPlayers.end(), compareAnimationPlayers);
    return animationPlayers;
}
WillBeHeapVector<RefPtrWillBeMember<Animation>> AnimationTimeline::getAnimations()
{
    WillBeHeapVector<RefPtrWillBeMember<Animation>> animations;
    for (const auto& animation : m_animations) {
        if (animation->effect() && (animation->effect()->isCurrent() || animation->effect()->isInEffect()))
            animations.append(animation);
    }
    std::sort(animations.begin(), animations.end(), compareAnimations);
    return animations;
}
WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer> > AnimationTimeline::getAnimationPlayers()
{
    WillBeHeapVector<RefPtrWillBeMember<AnimationPlayer> > animationPlayers;
    for (WillBeHeapHashSet<RawPtrWillBeWeakMember<AnimationPlayer> >::iterator it = m_players.begin(); it != m_players.end(); ++it) {
        if ((*it)->source() && (*it)->source()->isCurrent()) {
            animationPlayers.append(*it);
        }
    }
    std::sort(animationPlayers.begin(), animationPlayers.end(), compareAnimationPlayers);
    return animationPlayers;
}
void RTCDataChannel::scheduledEventTimerFired(Timer<RTCDataChannel>*)
{
    WillBeHeapVector<RefPtrWillBeMember<Event>> events;
    events.swap(m_scheduledEvents);

    WillBeHeapVector<RefPtrWillBeMember<Event>>::iterator it = events.begin();
    for (; it != events.end(); ++it)
        dispatchEvent((*it).release());

    events.clear();
}
void PrintTo(const AnimatableRepeatable& animValue, ::std::ostream* os)
{
    *os << "AnimatableRepeatable(";

    const WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> > v = animValue.values();
    for (WillBeHeapVector<RefPtrWillBeMember<AnimatableValue> >::const_iterator it = v.begin(); it != v.end(); ++it) {
        PrintTo(*(it->get()), os);
        if (it+1 != v.end())
            *os << ", ";
    }
    *os << ")";
}
예제 #9
0
void RTCDTMFSender::scheduledEventTimerFired(Timer<RTCDTMFSender>*)
{
    if (m_stopped)
        return;

    WillBeHeapVector<RefPtrWillBeMember<Event> > events;
    events.swap(m_scheduledEvents);

    WillBeHeapVector<RefPtrWillBeMember<Event> >::iterator it = events.begin();
    for (; it != events.end(); ++it)
        dispatchEvent((*it).release());
}
void SVGAnimatedTypeAnimator::stopAnimValAnimation(const WillBeHeapVector<RawPtrWillBeMember<SVGElement> >& list)
{
    ASSERT(isAnimatingSVGDom());
    SVGElement::InstanceUpdateBlocker blocker(m_contextElement);

    WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator it = list.begin();
    WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator itEnd = list.end();
    for (; it != itEnd; ++it) {
        RefPtr<SVGAnimatedPropertyBase> animatedProperty = (*it)->propertyFromAttribute(m_animatedProperty->attributeName());
        if (animatedProperty)
            animatedProperty->animationEnded();
    }
}
예제 #11
0
void RTCPeerConnection::dispatchScheduledEvent()
{
    if (m_stopped)
        return;

    WillBeHeapVector<RefPtrWillBeMember<Event> > events;
    events.swap(m_scheduledEvents);

    WillBeHeapVector<RefPtrWillBeMember<Event> >::iterator it = events.begin();
    for (; it != events.end(); ++it)
        dispatchEvent((*it).release());

    events.clear();
}
예제 #12
0
void SVGDocumentExtensions::startAnimations()
{
    // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer
    // starting animations for a document will do this "latching"
    // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us.
    // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704
    WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers;
    timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());
    WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end();
    for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr) {
        SMILTimeContainer* timeContainer = (*itr)->timeContainer();
        if (!timeContainer->isStarted())
            timeContainer->begin();
    }
}
예제 #13
0
void SVGDocumentExtensions::dispatchSVGLoadEventToOutermostSVGElements()
{
    WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers;
    timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end());

    WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end();
    for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator it = timeContainers.begin(); it != end; ++it) {
        SVGSVGElement* outerSVG = it->get();
        if (!outerSVG->isOutermostSVGSVGElement())
            continue;

        // don't dispatch the load event document is not wellformed (for XML/standalone svg)
        if (outerSVG->document().wellFormed() || !outerSVG->document().isSVGDocument())
            outerSVG->sendSVGLoadEventIfPossible();
    }
}
예제 #14
0
void PageRuleCollector::matchPageRules(RuleSet* rules)
{
    if (!rules)
        return;

    rules->compactRulesIfNeeded();
    WillBeHeapVector<RawPtrWillBeMember<StyleRulePage> > matchedPageRules;
    matchPageRulesForList(matchedPageRules, rules->pageRules(), m_isLeftPage, m_isFirstPage, m_pageName);
    if (matchedPageRules.isEmpty())
        return;

    std::stable_sort(matchedPageRules.begin(), matchedPageRules.end(), comparePageRules);

    for (unsigned i = 0; i < matchedPageRules.size(); i++)
        m_result.addMatchedProperties(&matchedPageRules[i]->properties());
}
예제 #15
0
EventTarget* EventPath::findRelatedNode(TreeScope& scope, RelatedTargetMap& relatedTargetMap)
{
    WillBeHeapVector<RawPtrWillBeMember<TreeScope>, 32> parentTreeScopes;
    EventTarget* relatedNode = 0;
    for (TreeScope* current = &scope; current; current = current->olderShadowRootOrParentTreeScope()) {
        parentTreeScopes.append(current);
        RelatedTargetMap::const_iterator iter = relatedTargetMap.find(current);
        if (iter != relatedTargetMap.end() && iter->value) {
            relatedNode = iter->value;
            break;
        }
    }
    ASSERT(relatedNode);
    for (WillBeHeapVector<RawPtrWillBeMember<TreeScope>, 32>::iterator iter = parentTreeScopes.begin(); iter < parentTreeScopes.end(); ++iter)
        relatedTargetMap.add(*iter, relatedNode);
    return relatedNode;
}
예제 #16
0
void SVGDocumentExtensions::rebuildAllElementReferencesForTarget(SVGElement* referencedElement)
{
    ASSERT(referencedElement);
    if (!referencedElement->hasSVGRareData())
        return;

    const SVGElementSet& referencingElements = referencedElement->svgRareData()->referencingElements();

    // Iterate on a snapshot as |referencingElements| may be altered inside loop.
    WillBeHeapVector<RawPtrWillBeMember<SVGElement> > referencingElementsSnapShot;
    copyToVector(referencingElements, referencingElementsSnapShot);

    // Force rebuilding the referencingElement so it knows about this change.
    for (WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::iterator it = referencingElementsSnapShot.begin(), itEnd = referencingElementsSnapShot.end(); it != itEnd; ++it) {
        SVGElement* referencingElement = *it;

        // Before rebuilding referencingElement ensure it was not removed from under us.
        if (referencingElements.contains(referencingElement))
            referencingElement->svgAttributeChanged(XLinkNames::hrefAttr);
    }
}
void SVGDocumentExtensions::rebuildAllElementReferencesForTarget(SVGElement* referencedElement)
{
    ASSERT(referencedElement);
    ElementDependenciesMap::iterator it = m_elementDependencies.find(referencedElement);
    if (it == m_elementDependencies.end())
        return;
    ASSERT(it->key == referencedElement);

    WillBeHeapVector<RawPtrWillBeMember<SVGElement> > toBeNotified;
    SVGElementSet* referencingElements = it->value.get();
    copyToVector(*referencingElements, toBeNotified);

    // Force rebuilding the referencingElement so it knows about this change.
    WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::iterator vectorEnd = toBeNotified.end();
    for (WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::iterator vectorIt = toBeNotified.begin(); vectorIt != vectorEnd; ++vectorIt) {
        // Before rebuilding referencingElement ensure it was not removed from under us.
        if (SVGElementSet* referencingElements = setOfElementsReferencingTarget(referencedElement)) {
            if (referencingElements->contains(*vectorIt))
                (*vectorIt)->svgAttributeChanged(XLinkNames::hrefAttr);
        }
    }
}
예제 #18
0
void AnimationTimeline::serviceAnimations(TimingUpdateReason reason)
{
    TRACE_EVENT0("blink", "AnimationTimeline::serviceAnimations");

    m_lastCurrentTimeInternal = currentTimeInternal();

    m_timing->cancelWake();

    WillBeHeapVector<RawPtrWillBeMember<AnimationPlayer>> players;
    players.reserveInitialCapacity(m_playersNeedingUpdate.size());
    for (RefPtrWillBeMember<AnimationPlayer> player : m_playersNeedingUpdate)
        players.append(player.get());

    std::sort(players.begin(), players.end(), AnimationPlayer::hasLowerPriority);

    for (AnimationPlayer* player : players) {
        if (!player->update(reason))
            m_playersNeedingUpdate.remove(player);
    }

    ASSERT(!hasOutdatedAnimationPlayer());
}
예제 #19
0
void SVGAnimateElement::resetAnimatedType()
{
    SVGAnimatedTypeAnimator* animator = ensureAnimator();

    SVGElement* targetElement = this->targetElement();
    const QualifiedName& attributeName = this->attributeName();
    ShouldApplyAnimation shouldApply = shouldApplyAnimation(targetElement, attributeName);

    if (shouldApply == DontApplyAnimation)
        return;

    if (shouldApply == ApplyXMLAnimation) {
        // SVG DOM animVal animation code-path.
        WillBeHeapVector<RawPtrWillBeMember<SVGElement> > animatedElements = findElementInstances(targetElement);
        ASSERT(!animatedElements.isEmpty());

        WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator end = animatedElements.end();
        for (WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::const_iterator it = animatedElements.begin(); it != end; ++it)
            document().accessSVGExtensions().addElementReferencingTarget(this, *it);

        if (!m_animatedProperty)
            m_animatedProperty = animator->startAnimValAnimation(animatedElements);
        else
            m_animatedProperty = animator->resetAnimValToBaseVal(animatedElements);

        return;
    }

    // CSS properties animation code-path.
    String baseValue;

    if (shouldApply == ApplyCSSAnimation) {
        ASSERT(SVGAnimationElement::isTargetAttributeCSSProperty(targetElement, attributeName));
        computeCSSPropertyValue(targetElement, cssPropertyID(attributeName.localName()), baseValue);
    }

    m_animatedProperty = animator->constructFromString(baseValue);
}
예제 #20
0
void IdTargetObserverRegistry::notifyObserversInternal(const AtomicString& id)
{
    ASSERT(!id.isEmpty());
    ASSERT(!m_registry.isEmpty());

    m_notifyingObserversInSet = m_registry.get(id.impl());
    if (!m_notifyingObserversInSet)
        return;

    WillBeHeapVector<RawPtrWillBeMember<IdTargetObserver> > copy;
    copyToVector(*m_notifyingObserversInSet, copy);
    for (WillBeHeapVector<RawPtrWillBeMember<IdTargetObserver> >::const_iterator it = copy.begin(); it != copy.end(); ++it) {
        if (m_notifyingObserversInSet->contains(*it))
            (*it)->idTargetChanged();
    }

    if (m_notifyingObserversInSet->isEmpty())
        m_registry.remove(id.impl());

    m_notifyingObserversInSet = nullptr;
}
예제 #21
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();
}
    void run(LocalFrame* frame) override
    {
        if (m_running)
            return;
        m_running = true;

        // 0. Flush pending frontend messages.
        WebLocalFrameImpl* frameImpl = WebLocalFrameImpl::fromFrame(frame);
        WebDevToolsAgentImpl* agent = frameImpl->devToolsAgentImpl();
        agent->flushPendingProtocolNotifications();

        Vector<WebViewImpl*> views;
        WillBeHeapVector<RawPtrWillBeMember<WebFrameWidgetImpl>> widgets;

        // 1. Disable input events.
        const HashSet<WebViewImpl*>& viewImpls = WebViewImpl::allInstances();
        HashSet<WebViewImpl*>::const_iterator viewImplsEnd = viewImpls.end();
        for (HashSet<WebViewImpl*>::const_iterator it =  viewImpls.begin(); it != viewImplsEnd; ++it) {
            WebViewImpl* view = *it;
            m_frozenViews.add(view);
            views.append(view);
            view->setIgnoreInputEvents(true);
        }

        const WebFrameWidgetsSet& widgetImpls = WebFrameWidgetImpl::allInstances();
        WebFrameWidgetsSet::const_iterator widgetImplsEnd = widgetImpls.end();
        for (WebFrameWidgetsSet::const_iterator it =  widgetImpls.begin(); it != widgetImplsEnd; ++it) {
            WebFrameWidgetImpl* widget = *it;
            m_frozenWidgets.add(widget);
            widgets.append(widget);
            widget->setIgnoreInputEvents(true);
        }

        // 2. Notify embedder about pausing.
        agent->client()->willEnterDebugLoop();

        // 3. Disable active objects
        WebView::willEnterModalLoop();

        // 4. Process messages until quitNow is called.
        m_messageLoop->run();

        // 5. Resume active objects
        WebView::didExitModalLoop();

        // 6. Resume input events.
        for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) {
            if (m_frozenViews.contains(*it)) {
                // The view was not closed during the dispatch.
                (*it)->setIgnoreInputEvents(false);
            }
        }
        for (WillBeHeapVector<RawPtrWillBeMember<WebFrameWidgetImpl>>::iterator it = widgets.begin(); it != widgets.end(); ++it) {
            if (m_frozenWidgets.contains(*it)) {
                // The widget was not closed during the dispatch.
                (*it)->setIgnoreInputEvents(false);
            }
        }

        // 7. Notify embedder about resuming.
        agent->client()->didExitDebugLoop();

        // 8. All views have been resumed, clear the set.
        m_frozenViews.clear();
        m_frozenWidgets.clear();

        m_running = false;
    }