blob: f5c627265044994dd5d13756367482eca65555a3 [file] [log] [blame]
// Copyright 2018 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/devtools/protocol/target_handler.h"
#include "chrome/browser/devtools/chrome_devtools_manager_delegate.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_navigator.h"
#include "content/public/browser/devtools_agent_host.h"
namespace {
class DevToolsBrowserContextManager : public BrowserListObserver {
public:
DevToolsBrowserContextManager();
protocol::Response CreateBrowserContext(std::string* out_context_id);
protocol::Response GetBrowserContexts(
std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids);
Profile* GetBrowserContext(const std::string& context_id);
void DisposeBrowserContext(
const std::string& context_id,
std::unique_ptr<TargetHandler::DisposeBrowserContextCallback> callback);
private:
void OnOriginalProfileDestroyed(Profile* profile);
void OnBrowserRemoved(Browser* browser) override;
base::flat_map<
std::string,
std::unique_ptr<IndependentOTRProfileManager::OTRProfileRegistration>>
registrations_;
base::flat_map<std::string,
std::unique_ptr<TargetHandler::DisposeBrowserContextCallback>>
pending_context_disposals_;
base::WeakPtrFactory<DevToolsBrowserContextManager> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DevToolsBrowserContextManager);
};
DevToolsBrowserContextManager::DevToolsBrowserContextManager()
: weak_factory_(this) {}
Profile* DevToolsBrowserContextManager::GetBrowserContext(
const std::string& context_id) {
auto it = registrations_.find(context_id);
return it == registrations_.end() ? nullptr : it->second->profile();
}
protocol::Response DevToolsBrowserContextManager::CreateBrowserContext(
std::string* out_context_id) {
Profile* original_profile =
ProfileManager::GetActiveUserProfile()->GetOriginalProfile();
auto registration =
IndependentOTRProfileManager::GetInstance()->CreateFromOriginalProfile(
original_profile,
base::BindOnce(
&DevToolsBrowserContextManager::OnOriginalProfileDestroyed,
weak_factory_.GetWeakPtr()));
*out_context_id = registration->profile()->UniqueId();
registrations_[*out_context_id] = std::move(registration);
return protocol::Response::OK();
}
protocol::Response DevToolsBrowserContextManager::GetBrowserContexts(
std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids) {
*browser_context_ids = std::make_unique<protocol::Array<protocol::String>>();
for (const auto& registration_pair : registrations_) {
(*browser_context_ids)
->addItem(registration_pair.second->profile()->UniqueId());
}
return protocol::Response::OK();
}
void DevToolsBrowserContextManager::DisposeBrowserContext(
const std::string& context_id,
std::unique_ptr<TargetHandler::DisposeBrowserContextCallback> callback) {
if (pending_context_disposals_.find(context_id) !=
pending_context_disposals_.end()) {
callback->sendFailure(protocol::Response::Error(
"Disposal of browser context " + context_id + " is already pending"));
return;
}
auto it = registrations_.find(context_id);
if (it == registrations_.end()) {
callback->sendFailure(protocol::Response::InvalidParams(
"Failed to find browser context with id " + context_id));
return;
}
Profile* profile = it->second->profile();
bool has_opened_browser = false;
for (auto* opened_browser : *BrowserList::GetInstance()) {
if (opened_browser->profile() == profile) {
has_opened_browser = true;
break;
}
}
// If no browsers are opened - dispose right away.
if (!has_opened_browser) {
registrations_.erase(it);
callback->sendSuccess();
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::OnOriginalProfileDestroyed(
Profile* profile) {
base::EraseIf(registrations_, [&profile](const auto& it) {
return it.second->profile()->GetOriginalProfile() == 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;
for (auto* opened_browser : *BrowserList::GetInstance()) {
if (opened_browser->profile() == browser->profile())
return;
}
auto it = registrations_.find(context_id);
// We cannot delete immediately here: the profile might still be referenced
// during the browser tier-down process.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
it->second.release());
registrations_.erase(it);
pending_disposal->second->sendSuccess();
pending_context_disposals_.erase(pending_disposal);
if (pending_context_disposals_.empty())
BrowserList::RemoveObserver(this);
}
base::LazyInstance<DevToolsBrowserContextManager>::Leaky
g_devtools_browser_context_manager;
} // namespace
TargetHandler::TargetHandler(protocol::UberDispatcher* dispatcher) {
protocol::Target::Dispatcher::wire(dispatcher, this);
}
TargetHandler::~TargetHandler() {
ChromeDevToolsManagerDelegate* delegate =
ChromeDevToolsManagerDelegate::GetInstance();
if (delegate)
delegate->UpdateDeviceDiscovery();
}
protocol::Response TargetHandler::SetRemoteLocations(
std::unique_ptr<protocol::Array<protocol::Target::RemoteLocation>>
locations) {
remote_locations_.clear();
if (!locations)
return protocol::Response::OK();
for (size_t i = 0; i < locations->length(); ++i) {
auto* item = locations->get(i);
remote_locations_.insert(
net::HostPortPair(item->GetHost(), item->GetPort()));
}
ChromeDevToolsManagerDelegate* delegate =
ChromeDevToolsManagerDelegate::GetInstance();
if (delegate)
delegate->UpdateDeviceDiscovery();
return protocol::Response::OK();
}
protocol::Response TargetHandler::CreateTarget(
const std::string& url,
protocol::Maybe<int> width,
protocol::Maybe<int> height,
protocol::Maybe<std::string> browser_context_id,
protocol::Maybe<bool> enable_begin_frame_control,
std::string* out_target_id) {
Profile* profile = ProfileManager::GetActiveUserProfile();
if (browser_context_id.isJust()) {
std::string profile_id = browser_context_id.fromJust();
profile =
g_devtools_browser_context_manager.Get().GetBrowserContext(profile_id);
if (!profile) {
return protocol::Response::Error(
"Failed to find browser context with id " + profile_id);
}
}
NavigateParams params(profile, GURL(url), ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
Browser* target_browser = nullptr;
// Find a browser to open a new tab.
// We shouldn't use browser that is scheduled to close.
for (auto* browser : *BrowserList::GetInstance()) {
if (browser->profile() == profile &&
!browser->IsAttemptingToCloseBrowser()) {
target_browser = browser;
break;
}
}
if (target_browser) {
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.browser = target_browser;
} else {
params.disposition = WindowOpenDisposition::NEW_WINDOW;
}
Navigate(&params);
if (!params.navigated_or_inserted_contents)
return protocol::Response::Error("Failed to open a new tab");
*out_target_id = content::DevToolsAgentHost::GetOrCreateFor(
params.navigated_or_inserted_contents)
->GetId();
return protocol::Response::OK();
}
protocol::Response TargetHandler::CreateBrowserContext(
std::string* out_context_id) {
return g_devtools_browser_context_manager.Get().CreateBrowserContext(
out_context_id);
}
protocol::Response TargetHandler::GetBrowserContexts(
std::unique_ptr<protocol::Array<protocol::String>>* browser_context_ids) {
return g_devtools_browser_context_manager.Get().GetBrowserContexts(
browser_context_ids);
}
void TargetHandler::DisposeBrowserContext(
const std::string& context_id,
std::unique_ptr<DisposeBrowserContextCallback> callback) {
g_devtools_browser_context_manager.Get().DisposeBrowserContext(
context_id, std::move(callback));
}