// Copyright 2012 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/views/chrome_browser_main_extra_parts_views.h"

#include <utility>

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/path_service.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/bookmarks/bookmark_merged_surface_service.h"
#include "chrome/browser/bookmarks/bookmark_merged_surface_service_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/ui/infobars/browser_infobar_registry.h"
#include "chrome/browser/ui/views/bookmarks/bookmark_account_storage_move_dialog.h"
#include "chrome/browser/ui/views/chrome_constrained_window_views_client.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_views_delegate.h"
#include "chrome/browser/ui/views/media_router/media_router_dialog_controller_views.h"
#include "chrome/browser/ui/views/relaunch_notification/relaunch_notification_controller.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/infobars/core/infobar_delegate.h"
#include "components/media_router/browser/media_router_dialog_controller.h"
#include "components/strings/grit/components_strings.h"
#include "components/ui_devtools/devtools_server.h"
#include "components/ui_devtools/switches.h"
#include "components/ui_devtools/views/server_holder.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
#include "sandbox/policy/switches.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"

#if defined(USE_AURA)
#include "base/run_loop.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "services/viz/public/cpp/gpu/gpu.h"  // nogncheck
#if !BUILDFLAG(IS_CHROMEOS)
#include "ui/display/screen.h"
#include "ui/views/widget/desktop_aura/desktop_screen.h"
#endif
#include "ui/wm/core/wm_state.h"
#endif  // defined(USE_AURA)

#if BUILDFLAG(IS_LINUX)
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "chrome/browser/ui/simple_message_box.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/common/content_switches.h"
#include "ui/base/l10n/l10n_util.h"
#endif  // BUILDFLAG(IS_LINUX)

namespace {

// Owned by ChromeBrowserMainParts.
ChromeBrowserMainExtraPartsViews* g_main_parts_views = nullptr;

}  // namespace

ChromeBrowserMainExtraPartsViews::ChromeBrowserMainExtraPartsViews() {
  DCHECK(!g_main_parts_views);
  g_main_parts_views = this;
}

ChromeBrowserMainExtraPartsViews::~ChromeBrowserMainExtraPartsViews() {
  DCHECK_EQ(g_main_parts_views, this);
  g_main_parts_views = nullptr;
  constrained_window::SetConstrainedWindowViewsClient(nullptr);
}

// static
ChromeBrowserMainExtraPartsViews* ChromeBrowserMainExtraPartsViews::Get() {
  return g_main_parts_views;
}

void ChromeBrowserMainExtraPartsViews::ToolkitInitialized() {
  // The delegate needs to be set before any UI is created so that windows
  // display the correct icon.
  if (!views::ViewsDelegate::GetInstance()) {
    views_delegate_ = std::make_unique<ChromeViewsDelegate>();
  }

  SetConstrainedWindowViewsClient(CreateChromeConstrainedWindowViewsClient());

#if defined(USE_AURA)
  wm_state_ = std::make_unique<wm::WMState>();
#endif

  // TODO(pkasting): Try to move ViewsDelegate creation here as well;
  // see https://crbug.com/41301678#comment2
  if (!views::LayoutProvider::Get()) {
    layout_provider_ = ChromeLayoutProvider::CreateLayoutProvider();
  }
}

void ChromeBrowserMainExtraPartsViews::PreCreateThreads() {
#if defined(USE_AURA) && !BUILDFLAG(IS_CHROMEOS)
  // The Screen instance may already be set in tests.
  if (!display::Screen::Get()) {
    screen_ = views::CreateDesktopScreen();
  }
#endif
}

void ChromeBrowserMainExtraPartsViews::PreProfileInit() {
  infobars::RegisterPreProfileInitInfoBars();
  if (ui_devtools::UiDevToolsServer::IsUiDevToolsEnabled(
          ui_devtools::switches::kEnableUiDevTools)) {
    base::FilePath output_dir;
    bool result = base::PathService::Get(chrome::DIR_USER_DATA, &output_dir);
    DCHECK(result);

    ui_devtools::ServerHolder::GetInstance()->CreateUiDevTools(output_dir);
  }

  media_router::MediaRouterDialogController::SetGetOrCreate(
      base::BindRepeating([](content::WebContents* web_contents) {
        DCHECK(web_contents);
        media_router::MediaRouterDialogController* controller = nullptr;
        // This call does nothing if the controller already exists.
        media_router::MediaRouterDialogControllerViews::CreateForWebContents(
            web_contents);
        controller =
            media_router::MediaRouterDialogControllerViews::FromWebContents(
                web_contents);
        return controller;
      }));

#if BUILDFLAG(IS_LINUX)
  // On the Linux desktop, we want to prevent the user from logging in as root,
  // so that we don't destroy the profile. Now that we have some minimal ui
  // initialized, check to see if we're running as root and bail if we are.
  if (geteuid() != 0) {
    return;
  }

  // Allow running inside an unprivileged user namespace. In that case, the
  // root directory will be owned by an unmapped UID and GID (although this
  // may not be the case if a chroot is also being used).
  struct stat st;
  if (stat("/", &st) == 0 && st.st_uid != 0) {
    return;
  }

  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(sandbox::policy::switches::kNoSandbox)) {
    return;
  }

  std::u16string title = l10n_util::GetStringFUTF16(
      IDS_REFUSE_TO_RUN_AS_ROOT, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
  std::u16string message = l10n_util::GetStringFUTF16(
      IDS_REFUSE_TO_RUN_AS_ROOT_2, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));

  chrome::ShowWarningMessageBoxAsync(nullptr, title, message);

  // Avoids gpu_process_transport_factory.cc(153)] Check failed:
  // per_compositor_data_.empty() when quit is chosen.
  base::RunLoop().RunUntilIdle();

  exit(EXIT_FAILURE);
#endif  // BUILDFLAG(IS_LINUX)
}

void ChromeBrowserMainExtraPartsViews::PostProfileInit(
    Profile* profile,
    bool is_initial_profile) {
  auto* service = BookmarkMergedSurfaceServiceFactory::GetForProfile(profile);
  if (service) {
    service->SetShowMoveStorageDialogCallback(base::BindRepeating(
        [](BrowserWindowInterface* browser, const bookmarks::BookmarkNode* node,
           const bookmarks::BookmarkNode* target_folder, size_t index) {
          ShowBookmarkAccountStorageMoveDialog(browser, node, target_folder,
                                               index);
        }));
  }
}

void ChromeBrowserMainExtraPartsViews::PostBrowserStart() {
  relaunch_notification_controller_ =
      std::make_unique<RelaunchNotificationController>(
          UpgradeDetector::GetInstance());

  infobars::RegisterInfoBars();
}

void ChromeBrowserMainExtraPartsViews::PostMainMessageLoopRun() {
  // The relaunch notification controller acts on timer-based events. Tear it
  // down explicitly here to avoid a case where such an event arrives during
  // shutdown.
  relaunch_notification_controller_.reset();
}
