// Copyright 2014 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/modules/modules_initializer.h"

#include <memory>

#include "base/task/thread_pool.h"
#include "base/trace_event/trace_event.h"
#include "build/android_buildflags.h"
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "third_party/blink/public/mojom/dom_storage/session_storage_namespace.mojom-blink.h"
#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
#include "third_party/blink/public/platform/interface_registry.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_view_client.h"
#include "third_party/blink/renderer/bindings/modules/v8/module_bindings_initializer.h"
#include "third_party/blink/renderer/core/css/background_color_paint_image_generator.h"
#include "third_party/blink/renderer/core/css/clip_path_paint_image_generator.h"
#include "third_party/blink/renderer/core/css/css_paint_image_generator.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/editing/suggestion/text_suggestion_backend_impl.h"
#include "third_party/blink/renderer/core/event_type_names.h"
#include "third_party/blink/renderer/core/exported/web_shared_worker_impl.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
#include "third_party/blink/renderer/core/html/media/html_media_element.h"
#include "third_party/blink/renderer/core/inspector/devtools_session.h"
#include "third_party/blink/renderer/core/inspector/inspector_media_context_impl.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/scheduler/task_attribution_tracker_impl.h"
#include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
#include "third_party/blink/renderer/modules/accessibility/inspector_accessibility_agent.h"
#include "third_party/blink/renderer/modules/app_banner/app_banner_controller.h"
#include "third_party/blink/renderer/modules/audio_output_devices/html_media_element_audio_output_device.h"
#include "third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.h"
#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h"
#include "third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context.h"
#include "third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h"
#include "third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.h"
#include "third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.h"
#include "third_party/blink/renderer/modules/content_extraction/inner_html_agent.h"
#include "third_party/blink/renderer/modules/content_extraction/inner_text_agent.h"
#include "third_party/blink/renderer/modules/csspaint/css_paint_image_generator_impl.h"
#include "third_party/blink/renderer/modules/csspaint/nativepaint/background_color_paint_image_generator_impl.h"
#include "third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_image_generator_impl.h"
#include "third_party/blink/renderer/modules/device_orientation/device_motion_controller.h"
#include "third_party/blink/renderer/modules/device_orientation/device_orientation_absolute_controller.h"
#include "third_party/blink/renderer/modules/device_orientation/device_orientation_controller.h"
#include "third_party/blink/renderer/modules/device_orientation/device_orientation_inspector_agent.h"
#include "third_party/blink/renderer/modules/document_metadata/document_metadata_server.h"
#include "third_party/blink/renderer/modules/document_picture_in_picture/picture_in_picture_controller_impl.h"
#include "third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.h"
#include "third_party/blink/renderer/modules/event_interface_modules_names.h"
#include "third_party/blink/renderer/modules/event_modules_factory.h"
#include "third_party/blink/renderer/modules/event_target_modules_names.h"
#include "third_party/blink/renderer/modules/exported/web_embedded_worker_impl.h"
#include "third_party/blink/renderer/modules/file_system_access/bucket_file_system_agent.h"
#include "third_party/blink/renderer/modules/filesystem/dragged_isolated_file_system_impl.h"
#include "third_party/blink/renderer/modules/filesystem/file_system_dispatcher.h"
#include "third_party/blink/renderer/modules/gamepad/navigator_gamepad.h"
#include "third_party/blink/renderer/modules/image_downloader/image_downloader_impl.h"
#include "third_party/blink/renderer/modules/indexed_db_names.h"
#include "third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.h"
#include "third_party/blink/renderer/modules/installation/installation_service_impl.h"
#include "third_party/blink/renderer/modules/launch/launch_queue.h"
#include "third_party/blink/renderer/modules/manifest/manifest_manager.h"
#include "third_party/blink/renderer/modules/media/audio/audio_renderer_sink_cache.h"
#include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
#include "third_party/blink/renderer/modules/mediasource/media_source_registry_impl.h"
#include "third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl.h"
#include "third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.h"
#include "third_party/blink/renderer/modules/presentation/presentation.h"
#include "third_party/blink/renderer/modules/push_messaging/push_messaging_client.h"
#include "third_party/blink/renderer/modules/remoteplayback/html_media_element_remote_playback.h"
#include "third_party/blink/renderer/modules/remoteplayback/remote_playback.h"
#include "third_party/blink/renderer/modules/screen_details/screen_details.h"
#include "third_party/blink/renderer/modules/screen_details/window_screen_details.h"
#include "third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller.h"
#include "third_party/blink/renderer/modules/service_worker/navigator_service_worker.h"
#include "third_party/blink/renderer/modules/speech/speech_synthesis.h"
#include "third_party/blink/renderer/modules/storage/dom_window_storage.h"
#include "third_party/blink/renderer/modules/storage/dom_window_storage_controller.h"
#include "third_party/blink/renderer/modules/storage/inspector_dom_storage_agent.h"
#include "third_party/blink/renderer/modules/storage/storage_namespace.h"
#include "third_party/blink/renderer/modules/webaudio/audio_graph_tracer.h"
#include "third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h"
#include "third_party/blink/renderer/modules/webgl/webgl_context_factory.h"
#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context.h"
#include "third_party/blink/renderer/modules/webgpu/gpu_canvas_context.h"
#include "third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread.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/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "third_party/blink/renderer/platform/widget/compositing/blink_categorized_worker_pool_delegate.h"
#include "third_party/blink/renderer/platform/widget/frame_widget.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/webrtc_overrides/init_webrtc.h"
#include "ui/accessibility/accessibility_features.h"

#if BUILDFLAG(IS_ANDROID)
#include "third_party/blink/public/platform/modules/video_capture/web_video_capture_impl_manager.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/web/modules/mediastream/web_media_stream_device_observer.h"
#include "third_party/blink/renderer/core/page/page_visibility_observer.h"
#include "third_party/blink/renderer/modules/remote_objects/remote_object_gateway_impl.h"
#endif

namespace blink {
namespace {

#if BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_DESKTOP_ANDROID)

class SuspendCaptureObserver : public GarbageCollected<SuspendCaptureObserver>,
                               public Supplement<Page>,
                               public PageVisibilityObserver {
 public:
  static const char kSupplementName[];

  explicit SuspendCaptureObserver(Page& page)
      : Supplement<Page>(page), PageVisibilityObserver(&page) {}

  // PageVisibilityObserver overrides:
  void PageVisibilityChanged() override {
    // TODO(crbug.com/487935): We don't yet suspend video capture devices for
    // OOPIFs.
    WebLocalFrameImpl* frame = WebLocalFrameImpl::FromFrame(
        DynamicTo<LocalFrame>(GetPage()->MainFrame()));
    if (!frame)
      return;
    WebMediaStreamDeviceObserver* media_stream_device_observer =
        frame->Client()->MediaStreamDeviceObserver();
    if (!media_stream_device_observer)
      return;
    // Don't suspend media capture devices if page visibility is
    // PageVisibilityState::kHiddenButPainting (e.g. Picture-in-Picture).
    // TODO(crbug.com/1339252): Add tests.
    bool suspend = (GetPage()->GetVisibilityState() ==
                    mojom::blink::PageVisibilityState::kHidden);
    MediaStreamDevices video_devices =
        media_stream_device_observer->GetNonScreenCaptureDevices();
    Platform::Current()->GetVideoCaptureImplManager()->SuspendDevices(
        video_devices, suspend);
  }

  void Trace(Visitor* visitor) const override {
    Supplement<Page>::Trace(visitor);
    PageVisibilityObserver::Trace(visitor);
  }
};

const char SuspendCaptureObserver::kSupplementName[] = "SuspendCaptureObserver";
#endif  // BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_DESKTOP_ANDROID)

}  // namespace

void ModulesInitializer::Initialize() {
  // Strings must be initialized before calling CoreInitializer::init().
  const unsigned kModulesStaticStringsCount =
      event_interface_names::kModulesNamesCount +
      event_target_names::kModulesNamesCount + indexed_db_names::kNamesCount;
  StringImpl::ReserveStaticStringsCapacityForSize(
      kModulesStaticStringsCount + StringImpl::AllStaticStrings().size());

  event_interface_names::InitModules();
  event_target_names::InitModules();
  Document::RegisterEventFactory(EventModulesFactory::Create());
  ModuleBindingsInitializer::Init();
  indexed_db_names::Init();
  AXObjectCache::Init(AXObjectCacheImpl::Create);
  DraggedIsolatedFileSystem::Init(
      DraggedIsolatedFileSystemImpl::PrepareForDataObject);
  CSSPaintImageGenerator::Init(CSSPaintImageGeneratorImpl::Create);
  BackgroundColorPaintImageGenerator::Init(
      BackgroundColorPaintImageGeneratorImpl::Create);
  ClipPathPaintImageGenerator::Init(ClipPathPaintImageGeneratorImpl::Create);
  MediaSourceRegistryImpl::Init();

  CoreInitializer::Initialize();

  // Canvas context types must be registered with the HTMLCanvasElement.
  HTMLCanvasElement::RegisterRenderingContextFactory(
      std::make_unique<CanvasRenderingContext2D::Factory>());
  HTMLCanvasElement::RegisterRenderingContextFactory(
      WebGLContextFactory::MakeWebGL1());
  HTMLCanvasElement::RegisterRenderingContextFactory(
      WebGLContextFactory::MakeWebGL2());
  HTMLCanvasElement::RegisterRenderingContextFactory(
      std::make_unique<ImageBitmapRenderingContext::Factory>());
  HTMLCanvasElement::RegisterRenderingContextFactory(
      std::make_unique<GPUCanvasContext::Factory>());

  // OffscreenCanvas context types must be registered with the OffscreenCanvas.
  OffscreenCanvas::RegisterRenderingContextFactory(
      std::make_unique<OffscreenCanvasRenderingContext2D::Factory>());
  OffscreenCanvas::RegisterRenderingContextFactory(
      WebGLContextFactory::MakeWebGL1());
  OffscreenCanvas::RegisterRenderingContextFactory(
      WebGLContextFactory::MakeWebGL2());
  OffscreenCanvas::RegisterRenderingContextFactory(
      std::make_unique<ImageBitmapRenderingContext::Factory>());
  OffscreenCanvas::RegisterRenderingContextFactory(
      std::make_unique<GPUCanvasContext::Factory>());

  V8PerIsolateData::SetTaskAttributionTrackerFactory(
      &scheduler::TaskAttributionTrackerImpl::Create);

  ::InitializeWebRtcModule();
}

void ModulesInitializer::InitLocalFrame(LocalFrame& frame) const {
  if (frame.IsMainFrame()) {
    frame.GetInterfaceRegistry()->AddInterface(BindRepeating(
        &DocumentMetadataServer::BindReceiver, WrapWeakPersistent(&frame)));
  }
  if (frame.IsLocalRoot()) {
    frame.GetInterfaceRegistry()->AddInterface(BindRepeating(
        &AIPageContentAgent::BindReceiver, WrapWeakPersistent(&frame)));
  }
  frame.GetInterfaceRegistry()->AddAssociatedInterface(
      BindRepeating(&LaunchQueue::BindReceiver, WrapWeakPersistent(&frame)));

  frame.GetInterfaceRegistry()->AddInterface(BindRepeating(
      &InstallationServiceImpl::BindReceiver, WrapWeakPersistent(&frame)));
  // TODO(dominickn): This interface should be document-scoped rather than
  // frame-scoped, as the resulting banner event is dispatched to
  // frame()->document().
  frame.GetInterfaceRegistry()->AddInterface(BindRepeating(
      &AppBannerController::BindReceiver, WrapWeakPersistent(&frame)));
  frame.GetInterfaceRegistry()->AddInterface(BindRepeating(
      &TextSuggestionBackendImpl::Bind, WrapWeakPersistent(&frame)));
#if BUILDFLAG(IS_ANDROID)
  frame.GetInterfaceRegistry()->AddInterface(BindRepeating(
      &RemoteObjectGatewayFactoryImpl::Bind, WrapWeakPersistent(&frame)));
#endif  // BUILDFLAG(IS_ANDROID)

  frame.GetInterfaceRegistry()->AddInterface(
      BindRepeating(&PeerConnectionTracker::BindToFrame,
                    WrapCrossThreadWeakPersistent(&frame)));

  frame.GetInterfaceRegistry()->AddInterface(
      BindRepeating(&InnerTextAgent::BindReceiver, WrapWeakPersistent(&frame)));

  frame.GetInterfaceRegistry()->AddInterface(
      BindRepeating(&InnerHtmlAgent::BindReceiver, WrapWeakPersistent(&frame)));

  if (base::FeatureList::IsEnabled(features::kFrameMetadataObserver)) {
    frame.GetInterfaceRegistry()->AddInterface(
        BindRepeating(&FrameMetadataObserverRegistry::BindReceiver,
                      WrapWeakPersistent(&frame)));
  }
}

void ModulesInitializer::InstallSupplements(LocalFrame& frame) const {
  DCHECK(WebLocalFrameImpl::FromFrame(&frame)->Client());
  InspectorAccessibilityAgent::ProvideTo(&frame);
  ImageDownloaderImpl::ProvideTo(frame);
  AudioRendererSinkCache::InstallWindowObserver(*frame.DomWindow());
  UserMediaRequestProviderImpl::ProvideTo(*frame.DomWindow());
#if DCHECK_IS_ON()
  if (frame.IsLocalRoot() &&
      RuntimeEnabledFeatures::AIPageContentBuildOnLoadForTestingEnabled()) {
    AIPageContentAgent::EnableAutomaticActionableExtractionOnPageLoadForTesting(
        frame);
  }
#endif
}

MediaControls* ModulesInitializer::CreateMediaControls(
    HTMLMediaElement& media_element,
    ShadowRoot& shadow_root) const {
  return MediaControlsImpl::Create(media_element, shadow_root);
}

PictureInPictureController*
ModulesInitializer::CreatePictureInPictureController(Document& document) const {
  return MakeGarbageCollected<PictureInPictureControllerImpl>(document);
}

void ModulesInitializer::InitInspectorAgentSession(
    DevToolsSession* session,
    InspectorDOMAgent* dom_agent,
    InspectedFrames* inspected_frames,
    Page* page) const {
  session->CreateAndAppend<InspectorIndexedDBAgent>(
      inspected_frames, /*worker_global_scope=*/nullptr);
  session->CreateAndAppend<DeviceOrientationInspectorAgent>(inspected_frames);
  session->CreateAndAppend<InspectorDOMStorageAgent>(inspected_frames);
  session->CreateAndAppend<InspectorAccessibilityAgent>(inspected_frames,
                                                        dom_agent);
  session->CreateAndAppend<InspectorWebAudioAgent>(page);
  session->CreateAndAppend<InspectorCacheStorageAgent>(inspected_frames);
  session->CreateAndAppend<BucketFileSystemAgent>(inspected_frames);
}

void ModulesInitializer::InitWorkerInspectorAgentSession(
    DevToolsSession* session,
    WorkerGlobalScope* worker_global_scope) const {
  session->CreateAndAppend<InspectorIndexedDBAgent>(
      /*inspected_frames=*/nullptr, worker_global_scope);
}

void ModulesInitializer::OnClearWindowObjectInMainWorld(
    Document& document,
    const Settings& settings) const {
  LocalDOMWindow& window = *document.domWindow();
  DeviceMotionController::From(window);
  DeviceOrientationController::From(window);
  DeviceOrientationAbsoluteController::From(window);
  NavigatorGamepad::From(*window.navigator());

  // TODO(nhiroki): Figure out why ServiceWorkerContainer needs to be eagerly
  // initialized.
  if (!document.IsInitialEmptyDocument())
    NavigatorServiceWorker::From(window);

  DOMWindowStorageController::From(window);
  if (RuntimeEnabledFeatures::PresentationEnabled() &&
      settings.GetPresentationReceiver()) {
    // We eagerly create Presentation and associated PresentationReceiver so
    // that the frame creating the presentation can offer a connection to the
    // presentation receiver.
    Presentation::presentation(*window.navigator());
  }
  ManifestManager::From(window);

#if BUILDFLAG(IS_ANDROID)
  LocalFrame* frame = window.GetFrame();
  DCHECK(frame);
  if (auto* gateway = RemoteObjectGatewayImpl::From(*frame))
    gateway->OnClearWindowObjectInMainWorld();
#endif  // BUILDFLAG(IS_ANDROID)
}

std::unique_ptr<WebMediaPlayer> ModulesInitializer::CreateWebMediaPlayer(
    WebLocalFrameClient* web_frame_client,
    HTMLMediaElement& html_media_element,
    const WebMediaPlayerSource& source,
    WebMediaPlayerClient* media_player_client) const {
  HTMLMediaElementEncryptedMedia& encrypted_media =
      HTMLMediaElementEncryptedMedia::From(html_media_element);
  WebString sink_id(
      HTMLMediaElementAudioOutputDevice::sinkId(html_media_element));
  MediaInspectorContextImpl* context_impl = MediaInspectorContextImpl::From(
      *To<LocalDOMWindow>(html_media_element.GetExecutionContextForPlayer()));
  FrameWidget* frame_widget =
      html_media_element.GetDocument().GetFrame()->GetWidgetForLocalRoot();
  return web_frame_client->CreateMediaPlayer(
      source, media_player_client, context_impl, &encrypted_media,
      encrypted_media.ContentDecryptionModule(), sink_id,
      frame_widget->GetLayerTreeSettings(),
      base::ThreadPool::CreateTaskRunner(base::TaskTraits{
          base::InheritThreadType(),
          base::MaxThreadType(base::ThreadType::kPresentation)}));
}

RemotePlaybackClient* ModulesInitializer::CreateRemotePlaybackClient(
    HTMLMediaElement& html_media_element) const {
  return &RemotePlayback::From(html_media_element);
}

void ModulesInitializer::ProvideModulesToPage(
    Page& page,
    const SessionStorageNamespaceId& namespace_id) const {
  StorageNamespace::ProvideSessionStorageNamespaceTo(page, namespace_id);
  AudioGraphTracer::ProvideAudioGraphTracerTo(page);
#if BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_DESKTOP_ANDROID)
  page.ProvideSupplement(MakeGarbageCollected<SuspendCaptureObserver>(page));
#endif  // BUILDFLAG(IS_ANDROID)  && !BUILDFLAG(IS_DESKTOP_ANDROID)
}

void ModulesInitializer::ForceNextWebGLContextCreationToFail() const {
  WebGLRenderingContext::ForceNextWebGLContextCreationToFail();
}

void ModulesInitializer::
    CollectAllGarbageForAnimationAndPaintWorkletForTesting() const {
  AnimationAndPaintWorkletThread::CollectAllGarbageForTesting();
}

void ModulesInitializer::CloneSessionStorage(
    Page* clone_from_page,
    const SessionStorageNamespaceId& clone_to_namespace) {
  StorageNamespace* storage_namespace = StorageNamespace::From(clone_from_page);
  if (storage_namespace)
    storage_namespace->CloneTo(WebString::FromLatin1(clone_to_namespace));
}

void ModulesInitializer::EvictSessionStorageCachedData(Page* page) {
  StorageNamespace* storage_namespace = StorageNamespace::From(page);
  if (storage_namespace)
    storage_namespace->EvictSessionStorageCachedData();
}

void ModulesInitializer::DidChangeManifest(LocalFrame& frame) {
  ManifestManager::From(*frame.DomWindow())->DidChangeManifest();
}

void ModulesInitializer::NotifyOrientationChanged(LocalFrame& frame) {
  ScreenOrientationController::From(*frame.DomWindow())
      ->NotifyOrientationChanged();
}

void ModulesInitializer::DidUpdateScreens(
    LocalFrame& frame,
    const display::ScreenInfos& screen_infos) {
  auto* window = frame.DomWindow();
  if (auto* supplement =
          Supplement<LocalDOMWindow>::From<WindowScreenDetails>(window)) {
    // screen_details() may be null if permission has not been granted.
    if (auto* screen_details = supplement->screen_details()) {
      screen_details->UpdateScreenInfos(window, screen_infos);
    }
  }
}

void ModulesInitializer::SetLocalStorageArea(
    LocalFrame& frame,
    mojo::PendingRemote<mojom::blink::StorageArea> local_storage_area) {
  if (!frame.DomWindow())
    return;
  DOMWindowStorage::From(*frame.DomWindow())
      .InitLocalStorage(std::move(local_storage_area));
}

void ModulesInitializer::SetSessionStorageArea(
    LocalFrame& frame,
    mojo::PendingRemote<mojom::blink::StorageArea> session_storage_area) {
  if (!frame.DomWindow())
    return;
  DOMWindowStorage::From(*frame.DomWindow())
      .InitSessionStorage(std::move(session_storage_area));
}

mojom::blink::FileSystemManager& ModulesInitializer::GetFileSystemManager(
    ExecutionContext* context) {
  return FileSystemDispatcher::From(context).GetFileSystemManager();
}

}  // namespace blink
