chromium/third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_token_provider.h"

#include "third_party/blink/public/mojom/webview/webview_media_integrity.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/extensions_webview/v8/v8_media_integrity_error_name.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/extensions/webview/media_integrity/media_integrity_error.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
#include "third_party/blink/renderer/platform/supplementable.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"

namespace {
const char kInvalidContext[] = "Invalid context";
}  // namespace

namespace blink {

MediaIntegrityTokenProvider::MediaIntegrityTokenProvider(
    ExecutionContext* context,
    mojo::PendingRemote<mojom::blink::WebViewMediaIntegrityProvider>
        provider_pending_remote,
    uint64_t cloud_project_number)
    : provider_remote_(context), cloud_project_number_(cloud_project_number) {
  provider_remote_.Bind(std::move(provider_pending_remote),
                        context->GetTaskRunner(TaskType::kInternalDefault));
  provider_remote_.set_disconnect_handler(
      WTF::BindOnce(&MediaIntegrityTokenProvider::OnProviderConnectionError,
                    WrapWeakPersistent(this)));
}

void MediaIntegrityTokenProvider::OnProviderConnectionError() {
  provider_remote_.reset();
  for (auto& resolver : token_resolvers_) {
    resolver->Reject(MediaIntegrityError::CreateForName(
        V8MediaIntegrityErrorName::Enum::kTokenProviderInvalid));
  }
  token_resolvers_.clear();
}

ScriptPromise<IDLString> MediaIntegrityTokenProvider::requestToken(
    ScriptState* script_state,
    const String& opt_content_binding,
    ExceptionState& exception_state) {
  if (!script_state->ContextIsValid()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      kInvalidContext);
    return EmptyPromise();
  }
  ScriptPromiseResolver<IDLString>* resolver =
      MakeGarbageCollected<ScriptPromiseResolver<IDLString>>(
          script_state, exception_state.GetContext());
  ScriptPromise<IDLString> promise = resolver->Promise();

  if (!provider_remote_.is_bound()) {
    // We cannot reconnect ourselves. The caller must request a new provider.
    resolver->Reject(MediaIntegrityError::CreateForName(
        V8MediaIntegrityErrorName::Enum::kTokenProviderInvalid));
    return promise;
  }

  token_resolvers_.insert(resolver);
  provider_remote_->RequestToken(
      opt_content_binding,
      WTF::BindOnce(&MediaIntegrityTokenProvider::OnRequestTokenResponse,
                    WrapPersistent(this), WrapPersistent(script_state),
                    WrapPersistent(resolver)));
  return promise;
}

void MediaIntegrityTokenProvider::OnRequestTokenResponse(
    ScriptState* script_state,
    ScriptPromiseResolver<IDLString>* resolver,
    const mojom::blink::WebViewMediaIntegrityTokenResponsePtr response) {
  token_resolvers_.erase(resolver);

  if (!script_state->ContextIsValid()) {
    resolver->Reject(MakeGarbageCollected<DOMException>(
        DOMExceptionCode::kInvalidStateError, kInvalidContext));
    return;
  }

  if (response->is_token()) {
    resolver->Resolve(response->get_token());
  } else {
    const mojom::blink::WebViewMediaIntegrityErrorCode error_code =
        response->get_error_code();
    resolver->Reject(MediaIntegrityError::CreateFromMojomEnum(error_code));
  }
}

void MediaIntegrityTokenProvider::Trace(Visitor* visitor) const {
  visitor->Trace(token_resolvers_);
  visitor->Trace(provider_remote_);
  ScriptWrappable::Trace(visitor);
}

}  // namespace blink