chromium/chrome/browser/browser_process_platform_part_chromeos.cc

// Copyright 2022 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/browser_process_platform_part_chromeos.h"

#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/sessions/session_service_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/custom_handlers/protocol_handler_registry.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/window_open_disposition.h"

BrowserProcessPlatformPartChromeOS::BrowserProcessPlatformPartChromeOS()
    : browser_restore_observer_(this) {}

BrowserProcessPlatformPartChromeOS::~BrowserProcessPlatformPartChromeOS() =
    default;

bool BrowserProcessPlatformPartChromeOS::CanRestoreUrlsForProfile(
    const Profile* profile) const {
  return profile->IsRegularProfile();
}

BrowserProcessPlatformPartChromeOS::BrowserRestoreObserver::
    BrowserRestoreObserver(
        const BrowserProcessPlatformPartChromeOS* browser_process_platform_part)
    : browser_process_platform_part_(browser_process_platform_part) {
  BrowserList::AddObserver(this);
}

BrowserProcessPlatformPartChromeOS::BrowserRestoreObserver::
    ~BrowserRestoreObserver() {
  BrowserList::RemoveObserver(this);
}

void BrowserProcessPlatformPartChromeOS::BrowserRestoreObserver::OnBrowserAdded(
    Browser* browser) {
  // If |browser| is the only browser, restores urls based on the on startup
  // setting.
  if (chrome::GetBrowserCount(browser->profile()) == 1 &&
      ShouldRestoreUrls(browser)) {
    if (ShouldOpenUrlsInNewBrowser(browser)) {
      // Delay creating a new browser until |browser| is activated.
      on_session_restored_callback_subscription_ =
          SessionRestore::RegisterOnSessionRestoredCallback(base::BindRepeating(
              &BrowserProcessPlatformPartChromeOS::BrowserRestoreObserver::
                  OnSessionRestoreDone,
              base::Unretained(this)));
    } else {
      RestoreUrls(browser);
    }
  }

  // If the startup urls from LAST_AND_URLS pref are already opened in a new
  // browser, skip opening the same browser.
  if (browser->creation_source() ==
      Browser::CreationSource::kLastAndUrlsStartupPref) {
    DCHECK(on_session_restored_callback_subscription_);
    on_session_restored_callback_subscription_ = {};
  }
}

void BrowserProcessPlatformPartChromeOS::BrowserRestoreObserver::
    OnSessionRestoreDone(Profile* profile, int num_tabs_restored) {
  // Ensure this callback to be called exactly once.
  on_session_restored_callback_subscription_ = {};

  // All browser windows are created. Open startup urls in a new browser.
  auto create_params = Browser::CreateParams(profile, /*user_gesture*/ false);
  Browser* browser = Browser::Create(create_params);
  RestoreUrls(browser);
  browser->window()->Show();
  browser->window()->Activate();
}

bool BrowserProcessPlatformPartChromeOS::BrowserRestoreObserver::
    ShouldRestoreUrls(Browser* browser) const {
  Profile* profile = browser->profile();

  // Only open urls for regular sign in users.
  DCHECK(profile);
  if (!browser_process_platform_part_->CanRestoreUrlsForProfile(profile))
    return false;

  // If during the restore process, or restore from a crash, don't launch urls.
  // However, in case of LAST_AND_URLS startup setting, urls should be opened
  // even when the restore session is in progress.
  SessionStartupPref pref =
      SessionStartupPref::GetStartupPref(browser->profile()->GetPrefs());
  if ((SessionRestore::IsRestoring(profile) &&
       pref.type != SessionStartupPref::LAST_AND_URLS) ||
      HasPendingUncleanExit(profile)) {
    return false;
  }

  // App windows should not be restored.
  auto window_type = WindowTypeForBrowserType(browser->type());
  if (window_type == sessions::SessionWindow::TYPE_APP ||
      window_type == sessions::SessionWindow::TYPE_APP_POPUP) {
    return false;
  }

  // If the browser is created by StartupBrowserCreator,
  // StartupBrowserCreatorImpl::OpenTabsInBrowser can open tabs, so don't
  // restore urls here.
  if (browser->creation_source() == Browser::CreationSource::kStartupCreator)
    return false;

  // If the startup setting is not open urls, don't launch urls.
  if (!pref.ShouldOpenUrls() || pref.urls.empty())
    return false;

  return true;
}

// If the startup setting is both the restore last session and the open urls,
// those should be opened in a new browser.
bool BrowserProcessPlatformPartChromeOS::BrowserRestoreObserver::
    ShouldOpenUrlsInNewBrowser(Browser* browser) const {
  SessionStartupPref pref =
      SessionStartupPref::GetStartupPref(browser->profile()->GetPrefs());
  return pref.type == SessionStartupPref::LAST_AND_URLS;
}

void BrowserProcessPlatformPartChromeOS::BrowserRestoreObserver::RestoreUrls(
    Browser* browser) {
  DCHECK(browser);

  SessionStartupPref pref =
      SessionStartupPref::GetStartupPref(browser->profile()->GetPrefs());
  std::vector<GURL> urls;
  for (const auto& url : pref.urls)
    urls.push_back(url);

  custom_handlers::ProtocolHandlerRegistry* registry =
      ProtocolHandlerRegistryFactory::GetForBrowserContext(browser->profile());
  for (const GURL& url : urls) {
    // We skip URLs that we'd have to launch an external protocol handler for.
    // This avoids us getting into an infinite loop asking ourselves to open
    // a URL, should the handler be (incorrectly) configured to be us. Anyone
    // asking us to open such a URL should really ask the handler directly.
    bool handled_by_chrome =
        ProfileIOData::IsHandledURL(url) ||
        (registry && registry->IsHandledProtocol(url.scheme()));
    if (!handled_by_chrome)
      continue;

    int add_types = AddTabTypes::ADD_NONE | AddTabTypes::ADD_FORCE_INDEX;
    NavigateParams params(browser, url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
    params.disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
    params.tabstrip_add_types = add_types;
    Navigate(&params);
  }
}