910e62b5创建于 1月15日历史提交
// Copyright 2018 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/devtools/devtools_browser_context_manager.h"

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/no_destructor.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/profiles/profile_destroyer.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"

namespace {

const int64_t kDestroyProfileTimeoutSeconds = 60;

void DestroyOTRProfileWhenAppropriate(base::WeakPtr<Profile> weak_profile) {
  if (Profile* profile = weak_profile.get()) {
    ProfileDestroyer::DestroyOTRProfileWhenAppropriateWithTimeout(
        profile, base::Seconds(kDestroyProfileTimeoutSeconds));
  }
}

}  // namespace

DevToolsBrowserContextManager::DevToolsBrowserContextManager() = default;

DevToolsBrowserContextManager::~DevToolsBrowserContextManager() = default;

// static
DevToolsBrowserContextManager& DevToolsBrowserContextManager::GetInstance() {
  static base::NoDestructor<DevToolsBrowserContextManager> instance;
  return *instance;
}

Profile* DevToolsBrowserContextManager::GetProfileById(
    const std::string& context_id) {
  Profile* default_profile =
      ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
  if (context_id == default_profile->UniqueId()) {
    return default_profile;
  }
  auto it = otr_profiles_.find(context_id);
  if (it == otr_profiles_.end())
    return nullptr;
  return it->second;
}

content::BrowserContext* DevToolsBrowserContextManager::CreateBrowserContext() {
  Profile* original_profile =
      ProfileManager::GetLastUsedProfile()->GetOriginalProfile();

  Profile* otr_profile = original_profile->GetOffTheRecordProfile(
      Profile::OTRProfileID::CreateUniqueForDevTools(),
      /*create_if_needed=*/true);
  const std::string& context_id = otr_profile->UniqueId();

  // The two lines are matched in `StopObservingProfileIfAny()`.
  profile_observation_.AddObservation(otr_profile);
  otr_profiles_[context_id] = otr_profile;
  return otr_profile;
}

std::vector<content::BrowserContext*>
DevToolsBrowserContextManager::GetBrowserContexts() {
  std::vector<content::BrowserContext*> result;
  for (const auto& profile_pair : otr_profiles_)
    result.push_back(profile_pair.second);
  return result;
}

content::BrowserContext*
DevToolsBrowserContextManager::GetDefaultBrowserContext() {
  return ProfileManager::GetLastUsedProfile()->GetOriginalProfile();
}

void DevToolsBrowserContextManager::DisposeBrowserContext(
    content::BrowserContext* context,
    content::DevToolsManagerDelegate::DisposeCallback callback) {
  std::string context_id = context->UniqueId();
  if (pending_context_disposals_.find(context_id) !=
      pending_context_disposals_.end()) {
    std::move(callback).Run(false, "Disposal of browser context " + context_id +
                                       " is already pending");
    return;
  }
  auto it = otr_profiles_.find(context_id);
  if (it == otr_profiles_.end()) {
    std::move(callback).Run(
        false, "Failed to find browser context with id " + context_id);
    return;
  }

  Profile* profile = it->second;
  bool has_opened_browser = false;
  ForEachCurrentBrowserWindowInterfaceOrderedByActivation(
      [profile,
       &has_opened_browser](BrowserWindowInterface* browser_window_interface) {
        if (browser_window_interface->GetProfile() == profile) {
          has_opened_browser = true;
          return false;
        }
        return true;
      });

  // If no browsers are opened - dispose right away.
  if (!has_opened_browser) {
    StopObservingProfileIfAny(profile);
    DestroyOTRProfileWhenAppropriate(profile->GetWeakPtr());
    std::move(callback).Run(true, "");
    return;
  }

  if (pending_context_disposals_.empty())
    BrowserList::AddObserver(this);

  pending_context_disposals_[context_id] = std::move(callback);
  BrowserList::CloseAllBrowsersWithIncognitoProfile(
      profile, base::DoNothing(), base::DoNothing(),
      true /* skip_beforeunload */);
}

void DevToolsBrowserContextManager::OnProfileWillBeDestroyed(Profile* profile) {
  // This is likely happening during shutdown. We'll immediately
  // close all browser windows for our profile without unload handling.
  ForEachCurrentBrowserWindowInterfaceOrderedByActivation(
      [profile](BrowserWindowInterface* browser_window_interface) {
        if (browser_window_interface->GetProfile() == profile) {
          browser_window_interface->GetWindow()->Close();
        }
        return true;
      });

  StopObservingProfileIfAny(profile);
}

void DevToolsBrowserContextManager::OnBrowserRemoved(Browser* browser) {
  std::string context_id = browser->profile()->UniqueId();
  auto pending_disposal = pending_context_disposals_.find(context_id);
  if (pending_disposal == pending_context_disposals_.end())
    return;
  bool found = false;
  ForEachCurrentBrowserWindowInterfaceOrderedByActivation(
      [browser, &found](BrowserWindowInterface* browser_window_interface) {
        if (browser_window_interface->GetProfile() == browser->profile()) {
          found = true;
          return false;
        }
        return true;
      });
  if (found) {
    return;
  }

  StopObservingProfileIfAny(browser->profile());

  // We cannot delete immediately here: the profile might still be referenced
  // during the browser tear-down process.
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, base::BindOnce(&DestroyOTRProfileWhenAppropriate,
                                browser->profile()->GetWeakPtr()));

  std::move(pending_disposal->second).Run(true, "");
  pending_context_disposals_.erase(pending_disposal);
  if (pending_context_disposals_.empty())
    BrowserList::RemoveObserver(this);
}

void DevToolsBrowserContextManager::StopObservingProfileIfAny(
    Profile* profile) {
  if (!profile_observation_.IsObservingSource(profile))
    return;

  profile_observation_.RemoveObservation(profile);
  otr_profiles_.erase(profile->UniqueId());
}