| // 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()); | 
 | } |