| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/lacros/browser_service_lacros.h" |
| |
| #include "base/callback.h" |
| #include "base/callback_helpers.h" |
| #include "base/check.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/metrics/statistics_recorder.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/task/task_traits.h" |
| #include "base/task/thread_pool.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chromeos/arc/arc_web_contents_data.h" |
| #include "chrome/browser/feedback/feedback_dialog_utils.h" |
| #include "chrome/browser/lacros/app_mode/kiosk_session_service_lacros.h" |
| #include "chrome/browser/lacros/feedback_util.h" |
| #include "chrome/browser/lacros/system_logs/lacros_system_log_fetcher.h" |
| #include "chrome/browser/prefs/incognito_mode_prefs.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/profiles/profile_window.h" |
| #include "chrome/browser/profiles/profiles_state.h" |
| #include "chrome/browser/sessions/exit_type_service.h" |
| #include "chrome/browser/sessions/session_restore.h" |
| #include "chrome/browser/sessions/session_service_factory.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.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/profile_picker.h" |
| #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" |
| #include "chrome/browser/ui/startup/lacros_first_run_service.h" |
| #include "chrome/browser/ui/startup/startup_browser_creator.h" |
| #include "chrome/browser/ui/startup/startup_tab.h" |
| #include "chrome/browser/ui/views/tabs/tab_scrubber_chromeos.h" |
| #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_util.h" |
| #include "chrome/common/channel_info.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "chromeos/crosapi/mojom/crosapi.mojom.h" |
| #include "chromeos/lacros/lacros_service.h" |
| #include "chromeos/startup/browser_params_proxy.h" |
| #include "components/feedback/feedback_common.h" |
| #include "components/feedback/feedback_report.h" |
| #include "components/feedback/feedback_util.h" |
| #include "components/feedback/system_logs/system_logs_fetcher.h" |
| #include "components/keep_alive_registry/keep_alive_types.h" |
| #include "components/keep_alive_registry/scoped_keep_alive.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| #include "ui/platform_window/platform_window.h" |
| #include "ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| |
| constexpr char kHistogramsFilename[] = "lacros_histograms.txt"; |
| |
| std::string GetCompressedHistograms() { |
| std::string histograms = |
| base::StatisticsRecorder::ToJSON(base::JSON_VERBOSITY_LEVEL_FULL); |
| std::string compressed_histograms; |
| if (feedback_util::ZipString(base::FilePath(kHistogramsFilename), |
| std::move(histograms), &compressed_histograms)) { |
| return compressed_histograms; |
| } else { |
| LOG(ERROR) << "Failed to compress lacros histograms."; |
| return std::string(); |
| } |
| } |
| |
| void MaybeProceedWithProfile(base::OnceCallback<void(Profile*)> callback, |
| Profile* profile, |
| bool proceed) { |
| std::move(callback).Run(proceed ? profile : nullptr); |
| } |
| |
| // Helper function to handle profile loading errors. |
| void OnMainProfileLoaded(base::OnceCallback<void(Profile*)>& callback, |
| bool can_trigger_fre, |
| Profile* profile, |
| Profile::CreateStatus status) { |
| DCHECK(callback); |
| switch (status) { |
| case Profile::CREATE_STATUS_LOCAL_FAIL: |
| // Profile creation failed, show the profile picker instead. |
| ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint( |
| ProfilePicker::EntryPoint::kNewSessionOnExistingProcess)); |
| std::move(callback).Run(nullptr); |
| return; |
| case Profile::CREATE_STATUS_CREATED: |
| // Do nothing, wait for the profile to be fully initialized. |
| return; |
| case Profile::CREATE_STATUS_INITIALIZED: |
| DCHECK(profile); |
| |
| auto* fre_service = |
| LacrosFirstRunServiceFactory::GetForBrowserContext(profile); |
| DCHECK(fre_service); |
| if (can_trigger_fre && fre_service->ShouldOpenFirstRun()) { |
| // TODO(https://crbug.com/1313848): Consider taking a |
| // `ScopedProfileKeepAlive`. |
| fre_service->OpenFirstRunIfNeeded( |
| LacrosFirstRunService::EntryPoint::kOther, |
| base::BindOnce(&MaybeProceedWithProfile, std::move(callback), |
| base::Unretained(profile))); |
| } else { |
| std::move(callback).Run(profile); |
| } |
| return; |
| } |
| } |
| |
| void LoadMainProfile(base::OnceCallback<void(Profile*)> callback, |
| bool can_trigger_fre) { |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| profile_manager->CreateProfileAsync( |
| ProfileManager::GetPrimaryUserProfilePath(), |
| // Use base::OwnedRef as `OnMainProfileLoaded()` is called multiple |
| // times, but `callback` is only called once. |
| base::BindRepeating(&OnMainProfileLoaded, |
| base::OwnedRef(std::move(callback)), |
| can_trigger_fre)); |
| } |
| |
| NavigateParams::PathBehavior ConvertPathBehavior( |
| crosapi::mojom::OpenUrlParams_SwitchToTabPathBehavior path_behavior) { |
| switch (path_behavior) { |
| case crosapi::mojom::OpenUrlParams_SwitchToTabPathBehavior::kRespect: |
| return NavigateParams::RESPECT; |
| case crosapi::mojom::OpenUrlParams_SwitchToTabPathBehavior::kIgnore: |
| return NavigateParams::IGNORE_AND_NAVIGATE; |
| } |
| } |
| |
| } // namespace |
| |
| // A struct to keep the pending OpenUrl task. |
| struct BrowserServiceLacros::PendingOpenUrl { |
| raw_ptr<Profile> profile; |
| GURL url; |
| crosapi::mojom::OpenUrlParamsPtr params; |
| OpenUrlCallback callback; |
| }; |
| |
| BrowserServiceLacros::BrowserServiceLacros() { |
| session_restored_subscription_ = |
| SessionRestore::RegisterOnSessionRestoredCallback( |
| base::BindRepeating(&BrowserServiceLacros::OnSessionRestored, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| auto* lacros_service = chromeos::LacrosService::Get(); |
| const auto* init_params = chromeos::BrowserParamsProxy::Get(); |
| |
| if (init_params->InitialKeepAlive() == |
| crosapi::mojom::BrowserInitParams::InitialKeepAlive::kUnknown) { |
| // ash-chrome is too old, so for backward compatibility fallback to the old |
| // way, which is "if launched with kDoNotOpenWindow, run the lacro process |
| // on background, and reset the state when a Browser instance is created." |
| // Thus, if a user creates a browser window then close it, Lacros is |
| // terminated, but ash-chrome has responsibility to re-launch it soon. |
| if (init_params->InitialBrowserAction() == |
| crosapi::mojom::InitialBrowserAction::kDoNotOpenWindow) { |
| keep_alive_ = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::BROWSER_PROCESS_LACROS, |
| KeepAliveRestartOption::ENABLED); |
| BrowserList::AddObserver(this); |
| } |
| } else { |
| if (init_params->InitialKeepAlive() == |
| crosapi::mojom::BrowserInitParams::InitialKeepAlive::kEnabled) { |
| keep_alive_ = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::BROWSER_PROCESS_LACROS, |
| KeepAliveRestartOption::ENABLED); |
| } |
| } |
| |
| if (lacros_service->IsAvailable<crosapi::mojom::BrowserServiceHost>()) { |
| lacros_service->GetRemote<crosapi::mojom::BrowserServiceHost>() |
| ->AddBrowserService(receiver_.BindNewPipeAndPassRemoteWithVersion()); |
| } |
| } |
| |
| BrowserServiceLacros::~BrowserServiceLacros() { |
| BrowserList::RemoveObserver(this); |
| } |
| |
| void BrowserServiceLacros::REMOVED_0(REMOVED_0Callback callback) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BrowserServiceLacros::REMOVED_2(crosapi::mojom::BrowserInitParamsPtr) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BrowserServiceLacros::REMOVED_16( |
| base::flat_map<policy::PolicyNamespace, std::vector<uint8_t>> policy) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void BrowserServiceLacros::NewWindow(bool incognito, |
| bool should_trigger_session_restore, |
| NewWindowCallback callback) { |
| if (ProfilePicker::ShouldShowAtLaunch() && |
| chrome::GetTotalBrowserCount() == 0 && !incognito) { |
| // Profile picker does not support passing through the incognito param. It |
| // also does not support passing through the |
| // `should_trigger_session_restore` param but that's very common (left |
| // clicking the launcher icon) so we can't skip the picker in this case. The |
| // default behavior for the first browser window supports session restore, |
| // additional windows are opened blank and thus it works reasonably well for |
| // BrowserServiceLacros. |
| ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint( |
| ProfilePicker::EntryPoint::kNewSessionOnExistingProcess)); |
| std::move(callback).Run(); |
| return; |
| } |
| |
| LoadMainProfile( |
| base::BindOnce(&BrowserServiceLacros::NewWindowWithProfile, |
| weak_ptr_factory_.GetWeakPtr(), incognito, |
| should_trigger_session_restore, std::move(callback)), |
| /*can_trigger_fre=*/true); |
| } |
| |
| void BrowserServiceLacros::NewFullscreenWindow( |
| const GURL& url, |
| NewFullscreenWindowCallback callback) { |
| LoadMainProfile( |
| base::BindOnce(&BrowserServiceLacros::NewFullscreenWindowWithProfile, |
| weak_ptr_factory_.GetWeakPtr(), url, std::move(callback)), |
| /*can_trigger_fre=*/false); |
| } |
| |
| void BrowserServiceLacros::NewGuestWindow(NewGuestWindowCallback callback) { |
| if (profiles::IsGuestModeEnabled()) |
| profiles::SwitchToGuestProfile(); |
| |
| std::move(callback).Run(); |
| } |
| |
| void BrowserServiceLacros::NewWindowForDetachingTab( |
| const std::u16string& tab_id, |
| const std::u16string& group_id, |
| NewWindowForDetachingTabCallback callback) { |
| LoadMainProfile( |
| base::BindOnce(&BrowserServiceLacros::NewWindowForDetachingTabWithProfile, |
| weak_ptr_factory_.GetWeakPtr(), tab_id, group_id, |
| std::move(callback)), |
| /*can_trigger_fre=*/false); |
| } |
| |
| void BrowserServiceLacros::NewTab(bool should_trigger_session_restore, |
| NewTabCallback callback) { |
| if (ProfilePicker::ShouldShowAtLaunch() && |
| chrome::GetTotalBrowserCount() == 0) { |
| // The first browser window will trigger session restore if needed. |
| ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint( |
| ProfilePicker::EntryPoint::kNewSessionOnExistingProcess)); |
| std::move(callback).Run(); |
| return; |
| } |
| |
| LoadMainProfile( |
| base::BindOnce(&BrowserServiceLacros::NewTabWithProfile, |
| weak_ptr_factory_.GetWeakPtr(), |
| should_trigger_session_restore, std::move(callback)), |
| /*can_trigger_fre=*/true); |
| } |
| |
| void BrowserServiceLacros::OpenUrl(const GURL& url, |
| crosapi::mojom::OpenUrlParamsPtr params, |
| OpenUrlCallback callback) { |
| LoadMainProfile(base::BindOnce(&BrowserServiceLacros::OpenUrlWithProfile, |
| weak_ptr_factory_.GetWeakPtr(), url, |
| std::move(params), std::move(callback)), |
| /*can_trigger_fre=*/true); |
| } |
| |
| void BrowserServiceLacros::RestoreTab(RestoreTabCallback callback) { |
| LoadMainProfile( |
| base::BindOnce(&BrowserServiceLacros::RestoreTabWithProfile, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback)), |
| /*can_trigger_fre=*/true); |
| } |
| |
| void BrowserServiceLacros::HandleTabScrubbing(float x_offset) { |
| TabScrubberChromeOS::GetInstance()->SynthesizedScrollEvent(x_offset); |
| } |
| |
| void BrowserServiceLacros::GetFeedbackData(GetFeedbackDataCallback callback) { |
| DCHECK(!callback.is_null()); |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // Self-deleting object. |
| system_logs::SystemLogsFetcher* fetcher = |
| system_logs::BuildLacrosSystemLogsFetcher(/*scrub_data=*/true); |
| fetcher->Fetch(base::BindOnce(&BrowserServiceLacros::OnSystemInformationReady, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void BrowserServiceLacros::GetHistograms(GetHistogramsCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| // GetCompressedHistograms calls functions marking as blocking, so it |
| // can not be running on UI thread. |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, |
| base::BindOnce(GetCompressedHistograms), |
| base::BindOnce(&BrowserServiceLacros::OnGetCompressedHistograms, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void BrowserServiceLacros::GetActiveTabUrl(GetActiveTabUrlCallback callback) { |
| Browser* browser = chrome::FindBrowserWithActiveWindow(); |
| GURL page_url; |
| if (browser) { |
| page_url = chrome::GetTargetTabUrl( |
| browser->session_id(), browser->tab_strip_model()->active_index()); |
| } |
| std::move(callback).Run(page_url); |
| } |
| |
| void BrowserServiceLacros::UpdateDeviceAccountPolicy( |
| const std::vector<uint8_t>& policy) { |
| chromeos::LacrosService::Get()->NotifyPolicyUpdated(policy); |
| } |
| |
| void BrowserServiceLacros::NotifyPolicyFetchAttempt() { |
| chromeos::LacrosService::Get()->NotifyPolicyFetchAttempt(); |
| } |
| |
| void BrowserServiceLacros::UpdateKeepAlive(bool enabled) { |
| if (enabled == static_cast<bool>(keep_alive_)) |
| return; |
| |
| if (enabled) { |
| keep_alive_ = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::BROWSER_PROCESS_LACROS, |
| KeepAliveRestartOption::ENABLED); |
| } else { |
| keep_alive_.reset(); |
| } |
| } |
| |
| void BrowserServiceLacros::OpenForFullRestore(bool skip_crash_restore) { |
| LoadMainProfile( |
| base::BindOnce(&BrowserServiceLacros::OpenForFullRestoreWithProfile, |
| weak_ptr_factory_.GetWeakPtr(), skip_crash_restore), |
| /*can_trigger_fre=*/true); |
| } |
| |
| void BrowserServiceLacros::OnSystemInformationReady( |
| GetFeedbackDataCallback callback, |
| std::unique_ptr<system_logs::SystemLogsResponse> sys_info) { |
| base::Value system_log_entries(base::Value::Type::DICTIONARY); |
| if (sys_info) { |
| std::string user_email = feedback_util::GetSignedInUserEmail(); |
| const bool google_email = gaia::IsGoogleInternalAccountEmail(user_email); |
| |
| for (auto& it : *sys_info) { |
| // We only send the list of all the crash report IDs if the user has a |
| // @google.com email. We strip this here so that the system information |
| // view properly reflects what we will be uploading to the server. It is |
| // also stripped later on in the feedback processing for other code paths |
| // that don't go through this. |
| if (FeedbackCommon::IncludeInSystemLogs(it.first, google_email)) { |
| system_log_entries.SetStringKey(std::move(it.first), |
| std::move(it.second)); |
| } |
| } |
| } |
| |
| DCHECK(!callback.is_null()); |
| std::move(callback).Run(std::move(system_log_entries)); |
| } |
| |
| void BrowserServiceLacros::OnGetCompressedHistograms( |
| GetHistogramsCallback callback, |
| const std::string& compressed_histograms) { |
| DCHECK(!callback.is_null()); |
| std::move(callback).Run(compressed_histograms); |
| } |
| |
| void BrowserServiceLacros::OnSessionRestored(Profile* profile, |
| int num_tabs_restored) { |
| if (pending_open_urls_.empty()) |
| return; |
| // Extract pending OpenUrl tasks for the restored |profile|. |
| std::vector<PendingOpenUrl> pendings; |
| for (auto& pending : pending_open_urls_) { |
| if (pending.profile == profile) { |
| pendings.push_back(std::move(pending)); |
| pending.profile = nullptr; // Mark as moved. |
| } |
| } |
| // Remove marked entries. |
| pending_open_urls_.erase(base::ranges::remove(pending_open_urls_, nullptr, |
| [](PendingOpenUrl& pending) { |
| return pending.profile; |
| }), |
| pending_open_urls_.end()); |
| |
| // Then, run for each. |
| for (auto& pending : pendings) |
| OpenUrlImpl(pending.profile, pending.url, std::move(pending.params), |
| std::move(pending.callback)); |
| } |
| |
| void BrowserServiceLacros::OpenUrlImpl(Profile* profile, |
| const GURL& url, |
| crosapi::mojom::OpenUrlParamsPtr params, |
| OpenUrlCallback callback) { |
| NavigateParams navigate_params( |
| profile, url, |
| ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK | |
| ui::PAGE_TRANSITION_FROM_API)); |
| |
| using OpenUrlParams = crosapi::mojom::OpenUrlParams; |
| |
| // Set up the window disposition. |
| auto mojo_disposition = |
| params ? params->disposition |
| : OpenUrlParams::WindowOpenDisposition::kLegacyAutoDetection; |
| switch (mojo_disposition) { |
| // This is to support M99 or earlier ash-chrome behavior. |
| // We can drop this when we deprecate to support it. |
| case OpenUrlParams::WindowOpenDisposition::kLegacyAutoDetection: |
| if (url.SchemeIs(content::kChromeUIScheme) && |
| (url.host() == chrome::kChromeUIFlagsHost || |
| url.host() == chrome::kChromeUIVersionHost || |
| url.host() == chrome::kChromeUIAboutHost || |
| url.host() == chrome::kChromeUIComponentsHost)) { |
| // Try to re-activate an existing tab for a few specified URLs. |
| navigate_params.disposition = WindowOpenDisposition::SWITCH_TO_TAB; |
| } else { |
| navigate_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; |
| } |
| break; |
| case OpenUrlParams::WindowOpenDisposition::kNewForegroundTab: |
| navigate_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; |
| break; |
| case OpenUrlParams::WindowOpenDisposition::kSwitchToTab: |
| navigate_params.disposition = WindowOpenDisposition::SWITCH_TO_TAB; |
| navigate_params.path_behavior = |
| ConvertPathBehavior(params->path_behavior); |
| break; |
| } |
| |
| // Ensure the browser window is showing when the URL is opened. This avoids |
| // the user being unaware a new tab with `url` has been opened (if the window |
| // was minimized for example). |
| navigate_params.window_action = NavigateParams::SHOW_WINDOW; |
| Navigate(&navigate_params); |
| |
| auto* tab = navigate_params.navigated_or_inserted_contents; |
| if (tab && params->from == crosapi::mojom::OpenUrlFrom::kArc) { |
| // Add a flag to remember this tab originated in the ARC context. |
| tab->SetUserData(&arc::ArcWebContentsData::kArcTransitionFlag, |
| std::make_unique<arc::ArcWebContentsData>(tab)); |
| } |
| |
| std::move(callback).Run(); |
| } |
| |
| void BrowserServiceLacros::NewWindowWithProfile( |
| bool incognito, |
| bool should_trigger_session_restore, |
| NewWindowCallback callback, |
| Profile* profile) { |
| if (!profile) { |
| LOG(WARNING) << "No profile, it might be an early exit from the FRE. " |
| "Aborting the requested action."; |
| std::move(callback).Run(); |
| return; |
| } |
| |
| switch (IncognitoModePrefs::GetAvailability(profile->GetPrefs())) { |
| case IncognitoModePrefs::Availability::kEnabled: |
| // Default behavior: both incognito and regular mode are allowed. |
| break; |
| case IncognitoModePrefs::Availability::kDisabled: |
| incognito = false; |
| break; |
| case IncognitoModePrefs::Availability::kForced: |
| incognito = true; |
| break; |
| case IncognitoModePrefs::Availability::kNumTypes: |
| NOTREACHED(); |
| break; |
| } |
| |
| chrome::NewEmptyWindow( |
| incognito ? profile->GetPrimaryOTRProfile(/*create_if_needed=*/true) |
| : profile, |
| should_trigger_session_restore); |
| std::move(callback).Run(); |
| } |
| |
| void BrowserServiceLacros::NewFullscreenWindowWithProfile( |
| const GURL& url, |
| NewFullscreenWindowCallback callback, |
| Profile* profile) { |
| if (!profile) { |
| std::move(callback).Run(crosapi::mojom::CreationResult::kProfileNotExist); |
| return; |
| } |
| |
| // Launch a fullscreen window with the user profile, and navigate to the |
| // target URL. |
| Browser::CreateParams params = Browser::CreateParams::CreateForApp( |
| "app_name", true, gfx::Rect(), profile, false); |
| params.initial_show_state = ui::SHOW_STATE_FULLSCREEN; |
| Browser* browser = Browser::Create(params); |
| NavigateParams nav_params(browser, url, |
| ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL); |
| Navigate(&nav_params); |
| |
| // Verify the creation result of browser window. |
| if (!browser || !browser->window()) { |
| std::move(callback).Run( |
| crosapi::mojom::CreationResult::kBrowserWindowUnavailable); |
| return; |
| } |
| |
| browser->window()->Show(); |
| |
| if (chromeos::BrowserParamsProxy::Get()->SessionType() == |
| crosapi::mojom::SessionType::kWebKioskSession) { |
| KioskSessionServiceLacros::Get()->InitWebKioskSession(browser, url); |
| } |
| |
| // Report a success result to ash. Please note that showing Lacros window is |
| // asynchronous. Ash-chrome should use the `exo::WMHelper` class rather than |
| // this callback method call to track window creation status. |
| std::move(callback).Run(crosapi::mojom::CreationResult::kSuccess); |
| } |
| |
| void BrowserServiceLacros::NewWindowForDetachingTabWithProfile( |
| const std::u16string& tab_id, |
| const std::u16string& group_id, |
| NewWindowForDetachingTabCallback callback, |
| Profile* profile) { |
| if (!profile) { |
| LOG(ERROR) << "No profile is found."; |
| std::move(callback).Run(crosapi::mojom::CreationResult::kUnknown, |
| std::string()); |
| return; |
| } |
| |
| Browser* browser = chrome::FindBrowserWithProfile(profile); |
| if (!browser) { |
| LOG(ERROR) << "No browser is found."; |
| std::move(callback).Run(crosapi::mojom::CreationResult::kUnknown, |
| std::string()); |
| return; |
| } |
| |
| Browser::CreateParams params = browser->create_params(); |
| params.user_gesture = true; |
| params.initial_show_state = ui::SHOW_STATE_DEFAULT; |
| Browser* new_browser = Browser::Create(params); |
| CHECK(new_browser); |
| |
| if (!tab_strip_ui::DropTabsInNewBrowser(new_browser, tab_id, group_id)) { |
| new_browser->window()->Close(); |
| // TODO(tonikitoo): Return a more specific error status, in case anything |
| // goes wrong. |
| std::move(callback).Run(crosapi::mojom::CreationResult::kUnknown, |
| std::string()); |
| return; |
| } |
| |
| new_browser->window()->Show(); |
| |
| auto* native_window = new_browser->window()->GetNativeWindow(); |
| auto* dwth_platform = |
| views::DesktopWindowTreeHostLacros::From(native_window->GetHost()); |
| auto* platform_window = dwth_platform->platform_window(); |
| std::move(callback).Run(crosapi::mojom::CreationResult::kSuccess, |
| platform_window->GetWindowUniqueId()); |
| } |
| |
| void BrowserServiceLacros::NewTabWithProfile( |
| bool should_trigger_session_restore, |
| NewTabCallback callback, |
| Profile* profile) { |
| if (!profile) { |
| LOG(WARNING) << "No profile, it might be an early exit from the FRE. " |
| "Aborting the requested action."; |
| std::move(callback).Run(); |
| return; |
| } |
| |
| Browser* browser; |
| { |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| profile, should_trigger_session_restore); |
| browser = displayer.browser(); |
| if (browser) |
| chrome::NewTab(browser); |
| } |
| if (browser) |
| browser->SetFocusToLocationBar(); |
| std::move(callback).Run(); |
| } |
| |
| void BrowserServiceLacros::OpenUrlWithProfile( |
| const GURL& url, |
| crosapi::mojom::OpenUrlParamsPtr params, |
| OpenUrlCallback callback, |
| Profile* profile) { |
| if (!profile) { |
| LOG(WARNING) << "No profile, it might be an early exit from the FRE. " |
| "Aborting the requested action."; |
| std::move(callback).Run(); |
| return; |
| } |
| |
| // If there is on-going session restoring task, wait for its completion. |
| if (SessionRestore::IsRestoring(profile)) { |
| pending_open_urls_.push_back( |
| PendingOpenUrl{profile, url, std::move(params), std::move(callback)}); |
| return; |
| } |
| |
| // If there's no available browsers, but there's a session to be restored, |
| // trigger it, and wait for its completion. |
| SessionService* session_service = |
| SessionServiceFactory::GetForProfileForSessionRestore(profile); |
| if (!chrome::FindBrowserWithProfile(profile) && session_service && |
| session_service->ShouldRestore(nullptr)) { |
| pending_open_urls_.push_back( |
| PendingOpenUrl{profile, url, std::move(params), std::move(callback)}); |
| session_service->RestoreIfNecessary(StartupTabs(), |
| /* restore apps */ false); |
| return; |
| } |
| |
| // Otherwise, directly try to open the URL. |
| OpenUrlImpl(profile, url, std::move(params), std::move(callback)); |
| } |
| |
| void BrowserServiceLacros::RestoreTabWithProfile(RestoreTabCallback callback, |
| Profile* profile) { |
| if (!profile) { |
| LOG(WARNING) << "No profile, it might be an early exit from the FRE. " |
| "Aborting the requested action."; |
| std::move(callback).Run(); |
| return; |
| } |
| |
| Browser* browser = chrome::FindBrowserWithProfile(profile); |
| if (browser) { |
| chrome::RestoreTab(browser); |
| } else { |
| chrome::OpenWindowWithRestoredTabs(profile); |
| } |
| std::move(callback).Run(); |
| } |
| |
| void BrowserServiceLacros::OpenForFullRestoreWithProfile( |
| bool skip_crash_restore, |
| Profile* profile) { |
| if (!profile) { |
| LOG(WARNING) << "No profile, it might be an early exit from the FRE. " |
| "Aborting the requested action."; |
| return; |
| } |
| |
| // There must not be any previously opened browsers as this could change the |
| // list of profiles returned from `GetLastOpenedProfiles()` below. |
| if (BrowserList::GetInstance()->size() != 0) { |
| LOG(ERROR) << "Cannot full restore with pre-existing browser instances."; |
| return; |
| } |
| |
| // Ensure that we do not start with the profile picker. |
| StartupProfileInfo profile_info{profile, StartupProfileMode::kBrowserWindow}; |
| |
| // Get the last opened profiles from the last session. This is only valid |
| // before any browsers have been opened for the current session as opening / |
| // closing browsers will cause the last opened profiles to change. |
| auto last_opened_profiles = |
| g_browser_process->profile_manager()->GetLastOpenedProfiles(); |
| |
| // Currently the kNoStartupWindow flag is set when lacros-chrome is launched |
| // with crosapi::mojom::InitialBrowserAction::kDoNotOpenWindow. The intention |
| // is to prevent lacros-chrome from launching a window during startup. However |
| // this flag remains set throughout the life of the browser process. |
| // This leads to issues where browsers can no longer be opened by the startup |
| // browser creator (such as below and in SessionService::RestoreIfNecessary). |
| // As a temporary workaround remove the kNoStartupWindow switch from the |
| // command line when launching for full restore. This is safe as by this point |
| // the browser process has already been started in its windowless state and |
| // the flag is no longer required. |
| base::CommandLine* lacros_command_line = |
| base::CommandLine::ForCurrentProcess(); |
| lacros_command_line->RemoveSwitch(switches::kNoStartupWindow); |
| |
| // Modify the command line to restore browser sessions. |
| lacros_command_line->AppendSwitch(switches::kRestoreLastSession); |
| |
| if (skip_crash_restore) |
| lacros_command_line->AppendSwitch(switches::kHideCrashRestoreBubble); |
| |
| StartupBrowserCreator browser_creator; |
| browser_creator.LaunchBrowserForLastProfiles( |
| *lacros_command_line, base::FilePath(), |
| chrome::startup::IsProcessStartup::kYes, chrome::startup::IsFirstRun::kNo, |
| profile_info, last_opened_profiles); |
| } |
| |
| void BrowserServiceLacros::UpdateComponentPolicy( |
| policy::ComponentPolicyMap policy) { |
| chromeos::LacrosService::Get()->NotifyComponentPolicyUpdated( |
| std::move(policy)); |
| } |
| |
| void BrowserServiceLacros::OnBrowserAdded(Browser* browser) { |
| // Note: this happens only when ash-chrome is too old. |
| // Please see the comment in the ctor for the detail. |
| BrowserList::RemoveObserver(this); |
| keep_alive_.reset(); |
| } |