void InspectorAnimationAgent::resolveAnimation(
    ErrorString* errorString,
    const String& animationId,
    std::unique_ptr<v8_inspector::protocol::Runtime::API::RemoteObject>*
        result) {
  blink::Animation* animation = assertAnimation(errorString, animationId);
  if (!animation)
    return;
  if (m_idToAnimationClone.get(animationId))
    animation = m_idToAnimationClone.get(animationId);
  const Element* element = toKeyframeEffect(animation->effect())->target();
  Document* document = element->ownerDocument();
  LocalFrame* frame = document ? document->frame() : nullptr;
  ScriptState* scriptState = frame ? ScriptState::forMainWorld(frame) : nullptr;
  if (!scriptState) {
    *errorString = "Element not associated with a document.";
    return;
  }

  ScriptState::Scope scope(scriptState);
  static const char kAnimationObjectGroup[] = "animation";
  m_v8Session->releaseObjectGroup(
      toV8InspectorStringView(kAnimationObjectGroup));
  *result = m_v8Session->wrapObject(
      scriptState->context(),
      toV8(animation, scriptState->context()->Global(), scriptState->isolate()),
      toV8InspectorStringView(kAnimationObjectGroup));
  if (!*result)
    *errorString = "Element not associated with a document.";
}
void InspectorAnimationAgent::setPaused(
    ErrorString* errorString,
    std::unique_ptr<protocol::Array<String>> animationIds,
    bool paused) {
  for (size_t i = 0; i < animationIds->length(); ++i) {
    String animationId = animationIds->get(i);
    blink::Animation* animation = assertAnimation(errorString, animationId);
    if (!animation)
      return;
    blink::Animation* clone = animationClone(animation);
    if (!clone) {
      *errorString = "Failed to clone detached animation";
      return;
    }
    if (paused && !clone->paused()) {
      // Ensure we restore a current time if the animation is limited.
      double currentTime =
          clone->timeline()->currentTime() - clone->startTime();
      clone->pause();
      clone->setCurrentTime(currentTime);
    } else if (!paused && clone->paused()) {
      clone->unpause();
    }
  }
}
void InspectorAnimationAgent::resolveAnimation(ErrorString* errorString, const String& animationId, RefPtr<TypeBuilder::Runtime::RemoteObject>& result)
{
    Animation* animation = assertAnimation(errorString, animationId);
    if (!animation)
        return;
    if (m_idToAnimationClone.get(animationId))
        animation = m_idToAnimationClone.get(animationId);
    const Element* element = toKeyframeEffect(animation->effect())->target();
    Document* document = element->ownerDocument();
    LocalFrame* frame = document ? document->frame() : nullptr;
    if (!frame) {
        *errorString = "Element not associated with a document.";
        return;
    }

    ScriptState* scriptState = ScriptState::forMainWorld(frame);
    InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
    if (injectedScript.isEmpty())
        return;

    ScriptState::Scope scope(scriptState);
    v8::Isolate* isolate = scriptState->isolate();
    ScriptValue scriptValue = ScriptValue(scriptState, toV8(animation, scriptState->context()->Global(), isolate));
    injectedScript.releaseObjectGroup("animation");
    result = injectedScript.wrapObject(scriptValue, "animation");
}
void InspectorAnimationAgent::setTiming(ErrorString* errorString, const String& animationId, double duration, double delay)
{
    Animation* animation = assertAnimation(errorString, animationId);
    if (!animation)
        return;

    AnimationType type = m_idToAnimationType.get(animationId);
    if (type == AnimationType::CSSTransition) {
        KeyframeEffect* effect = toKeyframeEffect(animation->effect());
        KeyframeEffectModelBase* model = toKeyframeEffectModelBase(effect->model());
        const AnimatableValueKeyframeEffectModel* oldModel = toAnimatableValueKeyframeEffectModel(model);
        // Refer to CSSAnimations::calculateTransitionUpdateForProperty() for the structure of transitions.
        const KeyframeVector& frames = oldModel->getFrames();
        ASSERT(frames.size() == 3);
        KeyframeVector newFrames;
        for (int i = 0; i < 3; i++)
            newFrames.append(toAnimatableValueKeyframe(frames[i]->clone().get()));
        // Update delay, represented by the distance between the first two keyframes.
        newFrames[1]->setOffset(delay / (delay + duration));
        model->setFrames(newFrames);

        AnimationEffectTiming* timing = animation->effect()->timing();
        UnrestrictedDoubleOrString unrestrictedDuration;
        unrestrictedDuration.setUnrestrictedDouble(duration + delay);
        timing->setDuration(unrestrictedDuration);
    } else if (type == AnimationType::WebAnimation) {
        AnimationEffectTiming* timing = animation->effect()->timing();
        UnrestrictedDoubleOrString unrestrictedDuration;
        unrestrictedDuration.setUnrestrictedDouble(duration);
        timing->setDuration(unrestrictedDuration);
        timing->setDelay(delay);
    }
}
void InspectorAnimationAgent::getCurrentTime(ErrorString* errorString, const String& id, double* currentTime)
{
    Animation* animation = assertAnimation(errorString, id);
    if (m_idToAnimationClone.get(id))
        animation = m_idToAnimationClone.get(id);
    *currentTime = animation->timeline()->currentTime() - animation->startTime();
}
void InspectorAnimationAgent::getCurrentTime(ErrorString* errorString,
                                             const String& id,
                                             double* currentTime) {
  blink::Animation* animation = assertAnimation(errorString, id);
  if (!animation)
    return;
  if (m_idToAnimationClone.get(id))
    animation = m_idToAnimationClone.get(id);

  if (animation->paused()) {
    *currentTime = animation->currentTime();
  } else {
    // Use startTime where possible since currentTime is limited.
    *currentTime =
        animation->timeline()->currentTime() - animation->startTime();
  }
}
void InspectorAnimationAgent::seekAnimations(ErrorString* errorString, const RefPtr<JSONArray>& animationIds, double currentTime)
{
    for (const auto& id : *animationIds) {
        String animationId;
        if (!(id->asString(&animationId))) {
            *errorString = "Invalid argument type";
            return;
        }
        Animation* animation = assertAnimation(errorString, animationId);
        if (!animation)
            return;
        m_isCloning = true;
        Animation* clone = animationClone(animation);
        m_isCloning = false;
        clone->play();
        clone->setCurrentTime(currentTime);
    }
}
void InspectorAnimationAgent::seekAnimations(
    ErrorString* errorString,
    std::unique_ptr<protocol::Array<String>> animationIds,
    double currentTime) {
  for (size_t i = 0; i < animationIds->length(); ++i) {
    String animationId = animationIds->get(i);
    blink::Animation* animation = assertAnimation(errorString, animationId);
    if (!animation)
      return;
    blink::Animation* clone = animationClone(animation);
    if (!clone) {
      *errorString = "Failed to clone a detached animation.";
      return;
    }
    if (!clone->paused())
      clone->play();
    clone->setCurrentTime(currentTime);
  }
}