// 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/chrome_browser_main_mac.h"

#import <Cocoa/Cocoa.h>

#include <memory>

#include "base/apple/bundle_locations.h"
#import "base/apple/foundation_util.h"
#include "base/base_paths.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/i18n/rtl.h"
#include "base/mac/mac_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/path_service.h"
#include "base/strings/sys_string_conversions.h"
#include "build/branding_buildflags.h"
#import "chrome/browser/app_controller_mac.h"
#include "chrome/browser/apps/app_shim/app_shim_listener.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/buildflags.h"
#import "chrome/browser/chrome_browser_application_mac.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/mac/install_from_dmg.h"
#include "chrome/browser/mac/metrics.h"
#include "chrome/browser/ui/cocoa/main_menu_builder.h"
#include "chrome/browser/ui/cocoa/renderer_context_menu/chrome_swizzle_services_menu_updater.h"
#include "chrome/browser/updater/updater.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/branded_strings.h"
#include "components/metrics/metrics_service.h"
#include "components/version_info/channel.h"
#include "content/public/common/main_function_params.h"
#include "content/public/common/result_codes.h"
#include "net/cert/internal/system_trust_store.h"
#include "ui/base/cocoa/permissions_utils.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/resource/resource_handle.h"
#include "ui/native_theme/native_theme_mac.h"
#include "ui/views/cocoa/native_widget_mac_ns_window_host.h"

// ChromeBrowserMainPartsMac ---------------------------------------------------

namespace {

base::FilePath GetBundlePath() {
  if (!base::apple::AmIBundled()) {
    return base::FilePath();
  }
  return base::apple::OuterBundlePath();
}

base::FilePath GetMainExecutableName() {
  if (!base::apple::AmIBundled()) {
    return base::FilePath();
  }
  base::FilePath path;
  base::PathService::Get(base::FILE_EXE, &path);
  return path.BaseName();
}

}  // namespace

ChromeBrowserMainPartsMac::ChromeBrowserMainPartsMac(bool is_integration_test,
                                                     StartupData* startup_data)
    : ChromeBrowserMainPartsPosix(is_integration_test, startup_data),
      code_sign_clone_manager_(GetBundlePath(), GetMainExecutableName()) {}

ChromeBrowserMainPartsMac::~ChromeBrowserMainPartsMac() = default;

int ChromeBrowserMainPartsMac::PreEarlyInitialization() {
  if (base::mac::WasLaunchedAsLoginItemRestoreState()) {
    base::CommandLine* singleton_command_line =
        base::CommandLine::ForCurrentProcess();
    singleton_command_line->AppendSwitch(switches::kRestoreLastSession);
  } else if (base::mac::WasLaunchedAsHiddenLoginItem()) {
    base::CommandLine* singleton_command_line =
        base::CommandLine::ForCurrentProcess();
    singleton_command_line->AppendSwitch(switches::kNoStartupWindow);
  }

  [ChromeSwizzleServicesMenuUpdater install];

  return ChromeBrowserMainPartsPosix::PreEarlyInitialization();
}

void ChromeBrowserMainPartsMac::PreCreateMainMessageLoop() {
  ChromeBrowserMainPartsPosix::PreCreateMainMessageLoop();

  // ChromeBrowserMainParts should have loaded the resource bundle by this
  // point (needed to load the nib).
  CHECK(ui::ResourceBundle::HasSharedInstance());

#if !BUILDFLAG(CHROME_FOR_TESTING)
  // Disk image installation is sort of a first-run task, so it shares the
  // no first run switches.
  //
  // This needs to be done after the resource bundle is initialized (for
  // access to localizations in the UI) and after Keystone is initialized
  // (because the installation may need to promote Keystone) but before the
  // app controller is set up (and thus before MainMenu.nib is loaded, because
  // the app controller assumes that a browser has been set up and will crash
  // upon receipt of certain notifications if no browser exists), before
  // anyone tries doing anything silly like firing off an import job, and
  // before anything creating preferences like Local State in order for the
  // relaunched installed application to still consider itself as first-run.
  if (!first_run::IsFirstRunSuppressed(
          *base::CommandLine::ForCurrentProcess())) {
    if (MaybeInstallFromDiskImage()) {
      // The application was installed and the installed copy has been
      // launched.  This process is now obsolete.  Exit.
      exit(0);
    }
  }
#endif  // !BUILDFLAG(CHROME_FOR_TESTING)

  ui::WarmScreenCapture();

  metrics_ = std::make_unique<mac_metrics::Metrics>();
  metrics_->RecordAppFileSystemType();

  PrefService* local_state = g_browser_process->local_state();
  DCHECK(local_state);

  // AppKit only restores windows to their original spaces when relaunching
  // apps after a restart, and puts them all on the current space when an app
  // is manually quit and relaunched. If Chrome restarted itself, set a flag in
  // Views to have it restore spaces.
  views::NativeWidgetMacNSWindowHost::
      SetMoveWindowsToOriginalSpacesUponRestoration(
          local_state->GetBoolean(prefs::kWasRestarted));
}

void ChromeBrowserMainPartsMac::PostCreateMainMessageLoop() {
  ChromeBrowserMainPartsPosix::PostCreateMainMessageLoop();

  net::InitializeTrustStoreMacCache();
}

int ChromeBrowserMainPartsMac::PreCreateThreads() {
  if (int ret = ChromeBrowserMainPartsPosix::PreCreateThreads();
      ret != content::RESULT_CODE_NORMAL_EXIT) {
    return ret;
  }

  // Create the app delegate by requesting the shared AppController.
  CHECK_EQ(nil, NSApp.delegate);
  AppController* app_controller = AppController.sharedController;
  CHECK_NE(nil, NSApp.delegate);

  chrome::BuildMainMenu(NSApp, app_controller,
                        l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
                        /*is_pwa=*/false,
                        /*is_rtl=*/base::i18n::IsRTL());
  [app_controller mainMenuCreated];

  return content::RESULT_CODE_NORMAL_EXIT;
}

void ChromeBrowserMainPartsMac::PreProfileInit() {
  ChromeBrowserMainPartsPosix::PreProfileInit();

  // This is called here so that the app shim socket is only created after
  // taking the singleton lock.
  g_browser_process->platform_part()->app_shim_listener()->Init();
}

void ChromeBrowserMainPartsMac::PostProfileInit(Profile* profile,
                                                bool is_initial_profile) {
  ChromeBrowserMainPartsPosix::PostProfileInit(profile, is_initial_profile);
}

void ChromeBrowserMainPartsMac::DidEndMainMessageLoop() {
  [AppController.sharedController didEndMainMessageLoop];
}
