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

#include "chrome/browser/ui/read_anything/read_anything_side_panel_web_view.h"

#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/read_anything/read_anything_controller.h"
#include "chrome/browser/ui/side_panel/side_panel_entry_scope.h"
#include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/input/native_web_keyboard_event.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents_delegate.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/events/keycodes/keyboard_codes.h"

using SidePanelWebUIViewT_ReadAnythingUntrustedUI =
    SidePanelWebUIViewT<ReadAnythingUntrustedUI>;
BEGIN_TEMPLATE_METADATA(SidePanelWebUIViewT_ReadAnythingUntrustedUI,
                        SidePanelWebUIViewT);

END_METADATA

ReadAnythingSidePanelWebView::ReadAnythingSidePanelWebView(
    Profile* profile,
    SidePanelEntryScope& scope)
    : SidePanelWebUIViewT(
          scope,
          base::RepeatingClosure(),
          base::RepeatingClosure(),
          std::make_unique<WebUIContentsWrapperT<ReadAnythingUntrustedUI>>(
              GURL(chrome::kChromeUIUntrustedReadAnythingSidePanelURL),
              profile,
              IDS_READING_MODE_TITLE,
              /*esc_closes_ui=*/false)) {}

ReadAnythingSidePanelWebView::ReadAnythingSidePanelWebView(
    Profile* profile,
    SidePanelEntryScope& scope,
    std::unique_ptr<WebUIContentsWrapperT<ReadAnythingUntrustedUI>>
        contents_wrapper)
    : SidePanelWebUIViewT(scope,
                          base::RepeatingClosure(),
                          base::RepeatingClosure(),
                          std::move(contents_wrapper)) {
  // If the UI has been shown once, the reused WebUI will be available but
  // won't send a new "showUI" message. Manually call ShowUI() to unblock the
  // SidePanelEntryWaiter and make the view visible.
  if (features::IsImmersiveReadAnythingEnabled()) {
    auto* controller = ReadAnythingController::From(&scope.GetTabInterface());
    if (controller && controller->has_shown_ui()) {
      ShowUI();
    }
  }
}

content::WebContents* ReadAnythingSidePanelWebView::OpenURLFromTab(
    content::WebContents* source,
    const content::OpenURLParams& params,
    base::OnceCallback<void(content::NavigationHandle&)>
        navigation_handle_callback) {
  // Reading Mode only renders links from distilled web content, so restrict
  // forwarded navigations to web schemes.
  if (!params.url.SchemeIsHTTPOrHTTPS()) {
    return nullptr;
  }
  ReadAnythingSidePanelController* controller =
      ReadAnythingSidePanelControllerGlue::FromWebContents(web_contents())
          ->controller();
  if (controller && controller->tab() &&
      controller->tab()->GetBrowserWindowInterface()) {
    content::OpenURLParams modified_params = params;
    content::RenderFrameHost* source_rfh = nullptr;
    if (params.initiator_frame_token.has_value()) {
      source_rfh = content::RenderFrameHost::FromFrameToken(
          content::GlobalRenderFrameHostToken(
              params.initiator_process_id,
              params.initiator_frame_token.value()));
    }

    // Check that user_gesture is really true, by confirming that there was a
    // recent activation in the RM rfh
    if (modified_params.user_gesture &&
        (!source_rfh || !source_rfh->HasTransientUserActivation())) {
      modified_params.user_gesture = false;
    }

    // If a compromised renderer requests a CURRENT_TAB navigation, it
    // bypasses the popup blocker, and also has other security risks like
    // spoofing the original webpage. Set to NEW_FOREGROUND_TAB to make sure
    // the popup blocker runs on links opened from the untrusted webui. We
    // strictly allow-list dispositions that open new windows/tabs, and demote
    // all others.
    switch (modified_params.disposition) {
      case WindowOpenDisposition::NEW_FOREGROUND_TAB:
      case WindowOpenDisposition::NEW_BACKGROUND_TAB:
      case WindowOpenDisposition::NEW_POPUP:
      case WindowOpenDisposition::NEW_WINDOW:
      case WindowOpenDisposition::SAVE_TO_DISK:
      case WindowOpenDisposition::OFF_THE_RECORD:
        break;
      default:
        modified_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
        break;
    }

    // Pass the main tab's WebContents as the source so the navigation pipeline
    // has the correct context to evaluate disposition and blocking rules.
    content::WebContents* tab_contents = controller->tab()->GetContents();
    if (tab_contents && tab_contents->GetDelegate()) {
      return tab_contents->GetDelegate()->OpenURLFromTab(
          tab_contents, modified_params, std::move(navigation_handle_callback));
    }
  }
  return nullptr;
}

bool ReadAnythingSidePanelWebView::HandleContextMenu(
    content::RenderFrameHost& render_frame_host,
    const content::ContextMenuParams& params) {
  return false;
}

bool ReadAnythingSidePanelWebView::HandleKeyboardEvent(
    content::WebContents* source,
    const input::NativeWebKeyboardEvent& event) {
  if (event.windows_key_code == ui::VKEY_ESCAPE) {
    ReadAnythingSidePanelController* controller =
        ReadAnythingSidePanelControllerGlue::FromWebContents(web_contents())
            ->controller();
    if (controller && controller->tab() &&
        controller->tab()->GetBrowserWindowInterface()) {
      ExclusiveAccessManager::From(
          controller->tab()->GetBrowserWindowInterface())
          ->HandleUserKeyEvent(event);
      return true;
    }
  }
  return SidePanelWebUIViewT::HandleKeyboardEvent(source, event);
}

base::WeakPtr<ReadAnythingSidePanelWebView>
ReadAnythingSidePanelWebView::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

std::unique_ptr<WebUIContentsWrapperT<ReadAnythingUntrustedUI>>
ReadAnythingSidePanelWebView::TakeContentsWrapper() {
  SetWebContents(nullptr);
  return std::move(contents_wrapper_);
}

ReadAnythingSidePanelWebView::~ReadAnythingSidePanelWebView() = default;

BEGIN_METADATA(ReadAnythingSidePanelWebView)
END_METADATA
