| // Copyright 2014 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/device/devtools_device_discovery.h" |
| |
| #include <map> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/json/json_reader.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/devtools/devtools_window.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/devtools_agent_host.h" |
| #include "content/public/browser/devtools_external_agent_proxy.h" |
| #include "content/public/browser/devtools_external_agent_proxy_delegate.h" |
| #include "net/base/escape.h" |
| |
| using content::BrowserThread; |
| using content::DevToolsAgentHost; |
| using RemoteBrowser = DevToolsDeviceDiscovery::RemoteBrowser; |
| using RemoteDevice = DevToolsDeviceDiscovery::RemoteDevice; |
| using RemotePage = DevToolsDeviceDiscovery::RemotePage; |
| |
| namespace { |
| |
| const char kPageListRequest[] = "/json"; |
| const char kVersionRequest[] = "/json/version"; |
| const char kClosePageRequest[] = "/json/close/%s"; |
| const char kActivatePageRequest[] = "/json/activate/%s"; |
| const char kBrowserTargetSocket[] = "/devtools/browser"; |
| const int kPollingIntervalMs = 1000; |
| |
| const char kPageReloadCommand[] = "{'method': 'Page.reload', id: 1}"; |
| |
| const char kWebViewSocketPrefix[] = "webview_devtools_remote"; |
| |
| static void ScheduleTaskDefault(base::OnceClosure task) { |
| content::GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, std::move(task), |
| base::TimeDelta::FromMilliseconds(kPollingIntervalMs)); |
| } |
| |
| // ProtocolCommand ------------------------------------------------------------ |
| |
| class ProtocolCommand |
| : public AndroidDeviceManager::AndroidWebSocket::Delegate { |
| public: |
| ProtocolCommand(scoped_refptr<AndroidDeviceManager::Device> device, |
| const std::string& socket, |
| const std::string& target_path, |
| const std::string& command); |
| |
| private: |
| void OnSocketOpened() override; |
| void OnFrameRead(const std::string& message) override; |
| void OnSocketClosed() override; |
| ~ProtocolCommand() override; |
| |
| const std::string command_; |
| std::unique_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ProtocolCommand); |
| }; |
| |
| ProtocolCommand::ProtocolCommand( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| const std::string& socket, |
| const std::string& target_path, |
| const std::string& command) |
| : command_(command), |
| web_socket_(device->CreateWebSocket(socket, target_path, this)) {} |
| |
| void ProtocolCommand::OnSocketOpened() { |
| web_socket_->SendFrame(command_); |
| } |
| |
| void ProtocolCommand::OnFrameRead(const std::string& message) { |
| delete this; |
| } |
| |
| void ProtocolCommand::OnSocketClosed() { |
| delete this; |
| } |
| |
| ProtocolCommand::~ProtocolCommand() { |
| } |
| |
| // AgentHostDelegate ---------------------------------------------------------- |
| |
| class WebSocketProxy : public AndroidDeviceManager::AndroidWebSocket::Delegate { |
| public: |
| explicit WebSocketProxy(content::DevToolsExternalAgentProxy* proxy) |
| : socket_opened_(false), proxy_(proxy) {} |
| |
| void WebSocketCreated(AndroidDeviceManager::AndroidWebSocket* web_socket) { |
| web_socket_.reset(web_socket); |
| } |
| |
| void SendMessageToBackend(std::string message) { |
| if (socket_opened_) |
| web_socket_->SendFrame(std::move(message)); |
| else |
| pending_messages_.push_back(std::move(message)); |
| } |
| |
| void OnSocketOpened() override { |
| socket_opened_ = true; |
| for (std::string& message : pending_messages_) |
| SendMessageToBackend(std::move(message)); |
| pending_messages_.clear(); |
| } |
| |
| void OnFrameRead(const std::string& message) override { |
| proxy_->DispatchOnClientHost(base::as_bytes(base::make_span(message))); |
| } |
| |
| void OnSocketClosed() override { |
| constexpr char kMsg[] = |
| "{\"method\":\"Inspector.detached\",\"params\":" |
| "{\"reason\":\"Connection lost.\"}}"; |
| proxy_->DispatchOnClientHost( |
| base::as_bytes(base::make_span(kMsg, strlen(kMsg)))); |
| web_socket_.reset(); |
| socket_opened_ = false; |
| proxy_->ConnectionClosed(); // Deletes |this|. |
| } |
| |
| private: |
| bool socket_opened_; |
| std::vector<std::string> pending_messages_; |
| std::unique_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_; |
| content::DevToolsExternalAgentProxy* proxy_; |
| DISALLOW_COPY_AND_ASSIGN(WebSocketProxy); |
| }; |
| |
| class AgentHostDelegate : public content::DevToolsExternalAgentProxyDelegate { |
| public: |
| static scoped_refptr<content::DevToolsAgentHost> GetOrCreateAgentHost( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| const std::string& browser_id, |
| const std::string& browser_version, |
| const std::string& local_id, |
| const std::string& target_path, |
| const std::string& type, |
| base::Value* value); |
| ~AgentHostDelegate() override; |
| |
| private: |
| AgentHostDelegate(scoped_refptr<AndroidDeviceManager::Device> device, |
| const std::string& browser_id, |
| const std::string& browser_version, |
| const std::string& local_id, |
| const std::string& target_path, |
| const std::string& type, |
| base::Value* value); |
| // DevToolsExternalAgentProxyDelegate overrides. |
| void Attach(content::DevToolsExternalAgentProxy* proxy) override; |
| void Detach(content::DevToolsExternalAgentProxy* proxy) override; |
| std::string GetType() override; |
| std::string GetTitle() override; |
| std::string GetDescription() override; |
| GURL GetURL() override; |
| GURL GetFaviconURL() override; |
| std::string GetFrontendURL() override; |
| bool Activate() override; |
| void Reload() override; |
| bool Close() override; |
| base::TimeTicks GetLastActivityTime() override; |
| void SendMessageToBackend(content::DevToolsExternalAgentProxy* proxy, |
| base::span<const uint8_t> message) override; |
| |
| scoped_refptr<AndroidDeviceManager::Device> device_; |
| std::string browser_id_; |
| std::string local_id_; |
| std::string target_path_; |
| std::string remote_type_; |
| std::string remote_id_; |
| std::string frontend_url_; |
| std::string title_; |
| std::string description_; |
| GURL url_; |
| GURL favicon_url_; |
| content::DevToolsAgentHost* agent_host_; |
| std::map<content::DevToolsExternalAgentProxy*, |
| std::unique_ptr<WebSocketProxy>> |
| proxies_; |
| DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate); |
| }; |
| |
| static std::string GetStringProperty(const base::Value& value, |
| const std::string& name) { |
| const std::string* result = value.FindStringKey(name); |
| return result ? *result : std::string(); |
| } |
| |
| static std::string BuildUniqueTargetId(const std::string& serial, |
| const std::string& browser_id, |
| const base::Value& value) { |
| return base::StringPrintf("%s:%s:%s", serial.c_str(), |
| browser_id.c_str(), GetStringProperty(value, "id").c_str()); |
| } |
| |
| static std::string GetFrontendURLFromValue(const base::Value& value, |
| const std::string& browser_version) { |
| std::string frontend_url = GetStringProperty(value, "devtoolsFrontendUrl"); |
| size_t ws_param = frontend_url.find("?ws"); |
| if (ws_param != std::string::npos) |
| frontend_url = frontend_url.substr(0, ws_param); |
| if (base::StartsWith(frontend_url, "http:", base::CompareCase::SENSITIVE)) |
| frontend_url = "https:" + frontend_url.substr(5); |
| if (!browser_version.empty()) |
| frontend_url += "?remoteVersion=" + browser_version; |
| return frontend_url; |
| } |
| |
| static std::string GetTargetPath(const base::Value& value) { |
| std::string target_path = GetStringProperty(value, "webSocketDebuggerUrl"); |
| |
| if (base::StartsWith(target_path, "ws://", base::CompareCase::SENSITIVE)) { |
| size_t pos = target_path.find("/", 5); |
| if (pos == std::string::npos) |
| pos = 5; |
| target_path = target_path.substr(pos); |
| } else { |
| target_path = std::string(); |
| } |
| return target_path; |
| } |
| |
| // static |
| scoped_refptr<content::DevToolsAgentHost> |
| AgentHostDelegate::GetOrCreateAgentHost( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| const std::string& browser_id, |
| const std::string& browser_version, |
| const std::string& local_id, |
| const std::string& target_path, |
| const std::string& type, |
| base::Value* value) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| scoped_refptr<DevToolsAgentHost> result = |
| DevToolsAgentHost::GetForId(local_id); |
| if (result) |
| return result; |
| |
| AgentHostDelegate* delegate = new AgentHostDelegate( |
| device, browser_id, browser_version, local_id, target_path, type, value); |
| result = content::DevToolsAgentHost::Forward( |
| local_id, base::WrapUnique(delegate)); |
| delegate->agent_host_ = result.get(); |
| return result; |
| } |
| |
| AgentHostDelegate::AgentHostDelegate( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| const std::string& browser_id, |
| const std::string& browser_version, |
| const std::string& local_id, |
| const std::string& target_path, |
| const std::string& type, |
| base::Value* value) |
| : device_(device), |
| browser_id_(browser_id), |
| local_id_(local_id), |
| target_path_(target_path), |
| remote_type_(type), |
| remote_id_(value ? GetStringProperty(*value, "id") : ""), |
| frontend_url_(value ? GetFrontendURLFromValue(*value, browser_version) |
| : ""), |
| title_(value ? base::UTF16ToUTF8(net::UnescapeForHTML( |
| base::UTF8ToUTF16(GetStringProperty(*value, "title")))) |
| : ""), |
| description_(value ? GetStringProperty(*value, "description") : ""), |
| url_(GURL(value ? GetStringProperty(*value, "url") : "")), |
| favicon_url_(GURL(value ? GetStringProperty(*value, "faviconUrl") : "")), |
| agent_host_(nullptr) {} |
| |
| AgentHostDelegate::~AgentHostDelegate() { |
| } |
| |
| void AgentHostDelegate::Attach(content::DevToolsExternalAgentProxy* proxy) { |
| std::unique_ptr<WebSocketProxy> ws_proxy(new WebSocketProxy(proxy)); |
| ws_proxy->WebSocketCreated( |
| device_->CreateWebSocket(browser_id_, target_path_, ws_proxy.get())); |
| proxies_[proxy] = std::move(ws_proxy); |
| base::RecordAction( |
| base::StartsWith(browser_id_, kWebViewSocketPrefix, |
| base::CompareCase::SENSITIVE) |
| ? base::UserMetricsAction("DevTools_InspectAndroidWebView") |
| : base::UserMetricsAction("DevTools_InspectAndroidPage")); |
| } |
| |
| void AgentHostDelegate::Detach(content::DevToolsExternalAgentProxy* proxy) { |
| proxies_.erase(proxy); |
| } |
| |
| std::string AgentHostDelegate::GetType() { |
| return remote_type_; |
| } |
| |
| std::string AgentHostDelegate::GetTitle() { |
| return title_; |
| } |
| |
| std::string AgentHostDelegate::GetDescription() { |
| return description_; |
| } |
| |
| GURL AgentHostDelegate::GetURL() { |
| return url_; |
| } |
| |
| GURL AgentHostDelegate::GetFaviconURL() { |
| return favicon_url_; |
| } |
| |
| std::string AgentHostDelegate::GetFrontendURL() { |
| return frontend_url_; |
| } |
| |
| bool AgentHostDelegate::Activate() { |
| std::string request = base::StringPrintf(kActivatePageRequest, |
| remote_id_.c_str()); |
| device_->SendJsonRequest(browser_id_, request, base::DoNothing()); |
| return true; |
| } |
| |
| void AgentHostDelegate::Reload() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (target_path_.empty()) |
| return; |
| new ProtocolCommand(device_, browser_id_, target_path_, kPageReloadCommand); |
| } |
| |
| bool AgentHostDelegate::Close() { |
| std::string request = base::StringPrintf(kClosePageRequest, |
| remote_id_.c_str()); |
| device_->SendJsonRequest(browser_id_, request, base::DoNothing()); |
| return true; |
| } |
| |
| base::TimeTicks AgentHostDelegate::GetLastActivityTime() { |
| return base::TimeTicks(); |
| } |
| |
| void AgentHostDelegate::SendMessageToBackend( |
| content::DevToolsExternalAgentProxy* proxy, |
| base::span<const uint8_t> message) { |
| auto it = proxies_.find(proxy); |
| // We could have detached due to physical connection being closed. |
| if (it == proxies_.end()) |
| return; |
| it->second->SendMessageToBackend(std::string(message.begin(), message.end())); |
| } |
| |
| } // namespace |
| |
| // DevToolsDeviceDiscovery::DiscoveryRequest ---------------------------------- |
| |
| class DevToolsDeviceDiscovery::DiscoveryRequest |
| : public base::RefCountedThreadSafe<DiscoveryRequest, |
| BrowserThread::DeleteOnUIThread> { |
| public: |
| static void Start(AndroidDeviceManager* device_manager, |
| base::OnceCallback<void(const CompleteDevices&)> callback); |
| |
| private: |
| friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; |
| friend class base::DeleteHelper<DiscoveryRequest>; |
| explicit DiscoveryRequest( |
| base::OnceCallback<void(const CompleteDevices&)> callback); |
| virtual ~DiscoveryRequest(); |
| |
| void ReceivedDevices(const AndroidDeviceManager::Devices& devices); |
| void ReceivedDeviceInfo(scoped_refptr<AndroidDeviceManager::Device> device, |
| const AndroidDeviceManager::DeviceInfo& device_info); |
| void ReceivedVersion(scoped_refptr<AndroidDeviceManager::Device> device, |
| scoped_refptr<RemoteBrowser>, |
| int result, |
| const std::string& response); |
| void ReceivedPages(scoped_refptr<AndroidDeviceManager::Device> device, |
| scoped_refptr<RemoteBrowser>, |
| int result, |
| const std::string& response); |
| |
| base::OnceCallback<void(const CompleteDevices&)> callback_; |
| DevToolsDeviceDiscovery::CompleteDevices complete_devices_; |
| }; |
| |
| // static |
| void DevToolsDeviceDiscovery::DiscoveryRequest::Start( |
| AndroidDeviceManager* device_manager, |
| base::OnceCallback<void(const CompleteDevices&)> callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto request = |
| base::WrapRefCounted(new DiscoveryRequest(std::move(callback))); |
| device_manager->QueryDevices( |
| base::BindOnce(&DiscoveryRequest::ReceivedDevices, request)); |
| } |
| |
| DevToolsDeviceDiscovery::DiscoveryRequest::DiscoveryRequest( |
| base::OnceCallback<void(const CompleteDevices&)> callback) |
| : callback_(std::move(callback)) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| } |
| |
| DevToolsDeviceDiscovery::DiscoveryRequest::~DiscoveryRequest() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| std::move(callback_).Run(complete_devices_); |
| } |
| |
| void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedDevices( |
| const AndroidDeviceManager::Devices& devices) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| for (const auto& device : devices) { |
| device->QueryDeviceInfo( |
| base::BindOnce(&DiscoveryRequest::ReceivedDeviceInfo, this, device)); |
| } |
| } |
| |
| void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedDeviceInfo( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| const AndroidDeviceManager::DeviceInfo& device_info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| scoped_refptr<RemoteDevice> remote_device = |
| new RemoteDevice(device->serial(), device_info); |
| complete_devices_.push_back(std::make_pair(device, remote_device)); |
| for (auto it = remote_device->browsers().begin(); |
| it != remote_device->browsers().end(); ++it) { |
| device->SendJsonRequest( |
| (*it)->socket(), kVersionRequest, |
| base::BindOnce(&DiscoveryRequest::ReceivedVersion, this, device, *it)); |
| } |
| } |
| |
| void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedVersion( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| scoped_refptr<RemoteBrowser> browser, |
| int result, |
| const std::string& response) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| device->SendJsonRequest( |
| browser->socket(), kPageListRequest, |
| base::BindOnce(&DiscoveryRequest::ReceivedPages, this, device, browser)); |
| |
| if (result < 0) |
| return; |
| // Parse version, append to package name if available, |
| absl::optional<base::Value> value = base::JSONReader::Read(response); |
| if (value && value->is_dict()) { |
| const std::string* browser_name = value->FindStringKey("Browser"); |
| if (browser_name) { |
| std::vector<std::string> parts = base::SplitString( |
| *browser_name, "/", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| if (parts.size() == 2) |
| browser->version_ = parts[1]; |
| else |
| browser->version_ = *browser_name; |
| } |
| browser->browser_target_id_ = GetTargetPath(*value); |
| if (browser->browser_target_id_.empty()) |
| browser->browser_target_id_ = kBrowserTargetSocket; |
| const std::string* package = value->FindStringKey("Android-Package"); |
| if (package) { |
| browser->display_name_ = |
| AndroidDeviceManager::GetBrowserName(browser->socket(), *package); |
| } |
| } |
| } |
| |
| void DevToolsDeviceDiscovery::DiscoveryRequest::ReceivedPages( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| scoped_refptr<RemoteBrowser> browser, |
| int result, |
| const std::string& response) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (result < 0) |
| return; |
| absl::optional<base::Value> value = base::JSONReader::Read(response); |
| if (value && value->is_list()) { |
| for (base::Value& page_value : value->GetList()) { |
| if (page_value.is_dict()) |
| browser->pages_.push_back(new RemotePage(device, browser->browser_id_, |
| browser->version_, |
| std::move(page_value))); |
| } |
| } |
| } |
| |
| // DevToolsDeviceDiscovery::RemotePage ---------------------------------------- |
| |
| DevToolsDeviceDiscovery::RemotePage::RemotePage( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| const std::string& browser_id, |
| const std::string& browser_version, |
| base::Value dict) |
| : device_(device), |
| browser_id_(browser_id), |
| browser_version_(browser_version), |
| dict_(std::move(dict)) {} |
| |
| DevToolsDeviceDiscovery::RemotePage::~RemotePage() { |
| } |
| |
| scoped_refptr<content::DevToolsAgentHost> |
| DevToolsDeviceDiscovery::RemotePage::CreateTarget() { |
| std::string local_id = |
| BuildUniqueTargetId(device_->serial(), browser_id_, dict_); |
| std::string target_path = GetTargetPath(dict_); |
| std::string type = GetStringProperty(dict_, "type"); |
| |
| std::string port_num = browser_id_; |
| if (type == "node") |
| port_num = GURL(GetStringProperty(dict_, "webSocketDebuggerUrl")).port(); |
| |
| agent_host_ = AgentHostDelegate::GetOrCreateAgentHost( |
| device_, port_num, browser_version_, local_id, target_path, type, |
| &dict_); |
| return agent_host_; |
| } |
| |
| // DevToolsDeviceDiscovery::RemoteBrowser ------------------------------------- |
| |
| DevToolsDeviceDiscovery::RemoteBrowser::RemoteBrowser( |
| const std::string& serial, |
| const AndroidDeviceManager::BrowserInfo& browser_info) |
| : serial_(serial), |
| browser_id_(browser_info.socket_name), |
| display_name_(browser_info.display_name), |
| user_(browser_info.user), |
| type_(browser_info.type) { |
| } |
| |
| bool DevToolsDeviceDiscovery::RemoteBrowser::IsChrome() { |
| return type_ == AndroidDeviceManager::BrowserInfo::kTypeChrome; |
| } |
| |
| std::string DevToolsDeviceDiscovery::RemoteBrowser::GetId() { |
| return serial() + ":" + socket(); |
| } |
| |
| DevToolsDeviceDiscovery::RemoteBrowser::ParsedVersion |
| DevToolsDeviceDiscovery::RemoteBrowser::GetParsedVersion() { |
| ParsedVersion result; |
| for (const base::StringPiece& part : |
| base::SplitStringPiece( |
| version_, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { |
| int value = 0; |
| base::StringToInt(part, &value); |
| result.push_back(value); |
| } |
| return result; |
| } |
| |
| DevToolsDeviceDiscovery::RemoteBrowser::~RemoteBrowser() { |
| } |
| |
| // DevToolsDeviceDiscovery::RemoteDevice -------------------------------------- |
| |
| DevToolsDeviceDiscovery::RemoteDevice::RemoteDevice( |
| const std::string& serial, |
| const AndroidDeviceManager::DeviceInfo& device_info) |
| : serial_(serial), |
| model_(device_info.model), |
| connected_(device_info.connected), |
| screen_size_(device_info.screen_size) { |
| for (auto it = device_info.browser_info.begin(); |
| it != device_info.browser_info.end(); ++it) { |
| browsers_.push_back(new RemoteBrowser(serial, *it)); |
| } |
| } |
| |
| DevToolsDeviceDiscovery::RemoteDevice::~RemoteDevice() { |
| } |
| |
| // DevToolsDeviceDiscovery ---------------------------------------------------- |
| |
| DevToolsDeviceDiscovery::DevToolsDeviceDiscovery( |
| AndroidDeviceManager* device_manager, |
| DeviceListCallback callback) |
| : device_manager_(device_manager), |
| callback_(std::move(callback)), |
| task_scheduler_(base::BindRepeating(&ScheduleTaskDefault)) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| RequestDeviceList(); |
| } |
| |
| DevToolsDeviceDiscovery::~DevToolsDeviceDiscovery() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| } |
| |
| void DevToolsDeviceDiscovery::SetScheduler( |
| base::RepeatingCallback<void(base::OnceClosure)> scheduler) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| task_scheduler_ = std::move(scheduler); |
| } |
| |
| // static |
| scoped_refptr<content::DevToolsAgentHost> |
| DevToolsDeviceDiscovery::CreateBrowserAgentHost( |
| scoped_refptr<AndroidDeviceManager::Device> device, |
| scoped_refptr<RemoteBrowser> browser) { |
| return AgentHostDelegate::GetOrCreateAgentHost( |
| device, browser->browser_id_, browser->version_, |
| "adb:" + browser->serial() + ":" + browser->socket(), |
| browser->browser_target_id(), DevToolsAgentHost::kTypeBrowser, nullptr); |
| } |
| |
| void DevToolsDeviceDiscovery::RequestDeviceList() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DiscoveryRequest::Start( |
| device_manager_, |
| base::BindOnce(&DevToolsDeviceDiscovery::ReceivedDeviceList, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void DevToolsDeviceDiscovery::ReceivedDeviceList( |
| const CompleteDevices& complete_devices) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| task_scheduler_.Run(base::BindOnce( |
| &DevToolsDeviceDiscovery::RequestDeviceList, weak_factory_.GetWeakPtr())); |
| // |callback_| should be run last as it may destroy |this|. |
| callback_.Run(complete_devices); |
| } |