ScriptPromise MediaKeys::setServerCertificate(ScriptState* scriptState, const DOMArrayPiece& serverCertificate)
{
    // From https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-setservercertificate:
    // The setServerCertificate(serverCertificate) method provides a server
    // certificate to be used to encrypt messages to the license server.
    // It must run the following steps:
    // 1. If serverCertificate is an empty array, return a promise rejected
    //    with a new DOMException whose name is "InvalidAccessError".
    if (!serverCertificate.byteLength()) {
        return ScriptPromise::rejectWithDOMException(
            scriptState, DOMException::create(InvalidAccessError, "The serverCertificate parameter is empty."));
    }

    // 2. If the keySystem does not support server certificates, return a
    //    promise rejected with a new DOMException whose name is
    //    "NotSupportedError".
    //    (Let the CDM decide whether to support this or not.)

    // 3. Let certificate be a copy of the contents of the serverCertificate
    //    parameter.
    RefPtr<DOMArrayBuffer> serverCertificateBuffer = DOMArrayBuffer::create(serverCertificate.data(), serverCertificate.byteLength());

    // 4. Let promise be a new promise.
    SimpleContentDecryptionModuleResultPromise* result = new SimpleContentDecryptionModuleResultPromise(scriptState);
    ScriptPromise promise = result->promise();

    // 5. Run the following steps asynchronously (documented in timerFired()).
    m_pendingActions.append(PendingAction::CreatePendingSetServerCertificate(result, serverCertificateBuffer.release()));
    if (!m_timer.isActive())
        m_timer.startOneShot(0, FROM_HERE);

    // 6. Return promise.
    return promise;
}
ScriptPromise MediaKeySession::close(ScriptState* scriptState)
{
    WTF_LOG(Media, "MediaKeySession(%p)::close", this);

    // From https://w3c.github.io/encrypted-media/#close:
    // Indicates that the application no longer needs the session and the CDM
    // should release any resources associated with this object and close it.
    // When this method is invoked, the user agent must run the following steps:

    // 1. If this object's callable value is false, return a promise rejected
    //    with a new DOMException whose name is "InvalidStateError".
    if (!m_isCallable)
        return CreateRejectedPromiseNotCallable(scriptState);

    // 2. If the Session Close algorithm has been run on this object,
    //    return a resolved promise.
    if (m_isClosed)
        return ScriptPromise::cast(scriptState, ScriptValue());

    // 3. Let promise be a new promise.
    SimpleContentDecryptionModuleResultPromise* result = new SimpleContentDecryptionModuleResultPromise(scriptState);
    ScriptPromise promise = result->promise();

    // 4. Run the following steps asynchronously (documented in
    //    actionTimerFired()).
    m_pendingActions.append(PendingAction::CreatePendingClose(result));
    if (!m_actionTimer.isActive())
        m_actionTimer.startOneShot(0, BLINK_FROM_HERE);

    // 5. Return promise.
    return promise;
}
ScriptPromise MediaKeySession::remove(ScriptState* scriptState)
{
    WTF_LOG(Media, "MediaKeySession(%p)::remove", this);

    // From https://w3c.github.io/encrypted-media/#remove:
    // Removes stored session data associated with this object. When this
    // method is invoked, the user agent must run the following steps:

    // 1. If this object's callable value is false, return a promise rejected
    //    with a new DOMException whose name is "InvalidStateError".
    if (!m_isCallable)
        return CreateRejectedPromiseNotCallable(scriptState);

    // 2. If this object's session type is not "persistent-license" or
    //    "persistent-release-message", return a promise rejected with a
    //    new DOMException whose name is InvalidAccessError.
    if (m_sessionType != WebEncryptedMediaSessionType::PersistentLicense && m_sessionType != WebEncryptedMediaSessionType::PersistentReleaseMessage) {
        return ScriptPromise::rejectWithDOMException(
            scriptState, DOMException::create(InvalidAccessError, "The session type is not persistent."));
    }

    // 3. If the Session Close algorithm has been run on this object, return a
    //    promise rejected with a new DOMException whose name is
    //    "InvalidStateError".
    if (m_isClosed) {
        return ScriptPromise::rejectWithDOMException(
            scriptState, DOMException::create(InvalidStateError, "The session is already closed."));
    }

    // 4. Let promise be a new promise.
    SimpleContentDecryptionModuleResultPromise* result = new SimpleContentDecryptionModuleResultPromise(scriptState);
    ScriptPromise promise = result->promise();

    // 5. Run the following steps asynchronously (documented in
    //    actionTimerFired()).
    m_pendingActions.append(PendingAction::CreatePendingRemove(result));
    if (!m_actionTimer.isActive())
        m_actionTimer.startOneShot(0, BLINK_FROM_HERE);

    // 6. Return promise.
    return promise;
}
ScriptPromise MediaKeySession::update(ScriptState* scriptState, const DOMArrayPiece& response)
{
    WTF_LOG(Media, "MediaKeySession(%p)::update", this);
    ASSERT(!m_isClosed);

    // From https://w3c.github.io/encrypted-media/#update:
    // Provides messages, including licenses, to the CDM. When this method is
    // invoked, the user agent must run the following steps:

    // 1. If this object's callable value is false, return a promise rejected
    //    with a new DOMException whose name is InvalidStateError.
    if (!m_isCallable)
        return CreateRejectedPromiseNotCallable(scriptState);

    // 2. If response is an empty array, return a promise rejected with a
    //    new DOMException whose name is InvalidAccessError.
    if (!response.byteLength()) {
        return ScriptPromise::rejectWithDOMException(
            scriptState, DOMException::create(InvalidAccessError, "The response parameter is empty."));
    }

    // 3. Let response copy be a copy of the contents of the response parameter.
    RefPtr<DOMArrayBuffer> responseCopy = DOMArrayBuffer::create(response.data(), response.byteLength());

    // 4. Let promise be a new promise.
    SimpleContentDecryptionModuleResultPromise* result = new SimpleContentDecryptionModuleResultPromise(scriptState);
    ScriptPromise promise = result->promise();

    // 5. Run the following steps asynchronously (documented in
    //    actionTimerFired())
    m_pendingActions.append(PendingAction::CreatePendingUpdate(result, responseCopy.release()));
    if (!m_actionTimer.isActive())
        m_actionTimer.startOneShot(0, BLINK_FROM_HERE);

    // 6. Return promise.
    return promise;
}