| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/devtools/protocol/target_auto_attacher.h" |
| |
| #include "base/observer_list.h" |
| #include "content/browser/devtools/devtools_renderer_channel.h" |
| #include "content/browser/devtools/render_frame_devtools_agent_host.h" |
| #include "content/browser/renderer_host/frame_tree_node.h" |
| #include "content/browser/renderer_host/navigation_request.h" |
| #include "content/browser/renderer_host/render_frame_host_impl.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| |
| namespace content { |
| namespace protocol { |
| |
| TargetAutoAttacher::TargetAutoAttacher() = default; |
| |
| TargetAutoAttacher::~TargetAutoAttacher() { |
| for (auto& client : clients_) { |
| client.AutoAttacherDestroyed(this); |
| } |
| } |
| |
| bool TargetAutoAttacher::auto_attach() const { |
| return !clients_.empty(); |
| } |
| |
| bool TargetAutoAttacher::wait_for_debugger_on_start() const { |
| return !clients_requesting_wait_for_debugger_.empty(); |
| } |
| |
| scoped_refptr<RenderFrameDevToolsAgentHost> |
| TargetAutoAttacher::HandleNavigation(NavigationRequest* navigation_request, |
| bool wait_for_debugger_on_start) { |
| DCHECK(auto_attach()); |
| FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node(); |
| RenderFrameHostImpl* new_host = navigation_request->GetRenderFrameHost(); |
| |
| // |new_host| can be nullptr for navigation that doesn't commmit |
| // (e.g. download). Skip possibly detaching the old agent host so the DevTools |
| // message logged via the old RFH can be seen. |
| if (!new_host) { |
| return nullptr; |
| } |
| |
| scoped_refptr<RenderFrameDevToolsAgentHost> agent_host = |
| RenderFrameDevToolsAgentHost::FindForDangling(frame_tree_node); |
| |
| bool needs_host_attached = new_host->is_local_root_subframe(); |
| |
| if (needs_host_attached) { |
| if (!agent_host) { |
| agent_host = RenderFrameDevToolsAgentHost:: |
| CreateForLocalRootOrEmbeddedPageNavigation(navigation_request); |
| } else if (navigation_request->state() >= |
| NavigationRequest::NavigationState::DID_COMMIT) { |
| // If we've just committed and there's already an agent, update |
| // targetInfo. |
| DispatchTargetInfoChanged(agent_host.get()); |
| } |
| |
| return agent_host; |
| } |
| |
| // At this point we don't need a host, so we must auto detach if we auto |
| // attached earlier. |
| if (agent_host) { |
| DispatchAutoDetach(agent_host.get()); |
| } |
| |
| return nullptr; |
| } |
| |
| void TargetAutoAttacher::UpdateAutoAttach(base::OnceClosure callback) { |
| std::move(callback).Run(); |
| } |
| |
| void TargetAutoAttacher::AddClient(Client* client, |
| bool wait_for_debugger_on_start, |
| base::OnceClosure callback) { |
| clients_.AddObserver(client); |
| if (wait_for_debugger_on_start) { |
| clients_requesting_wait_for_debugger_.insert(client); |
| } |
| UpdateAutoAttach(std::move(callback)); |
| } |
| |
| void TargetAutoAttacher::UpdateWaitForDebuggerOnStart( |
| Client* client, |
| bool wait_for_debugger_on_start, |
| base::OnceClosure callback) { |
| DCHECK(clients_.HasObserver(client)); |
| if (wait_for_debugger_on_start) { |
| clients_requesting_wait_for_debugger_.insert(client); |
| } else { |
| clients_requesting_wait_for_debugger_.erase(client); |
| } |
| UpdateAutoAttach(std::move(callback)); |
| } |
| |
| void TargetAutoAttacher::RemoveClient(Client* client) { |
| clients_.RemoveObserver(client); |
| clients_requesting_wait_for_debugger_.erase(client); |
| DCHECK(clients_requesting_wait_for_debugger_.empty() || !clients_.empty()); |
| if (clients_.empty() || clients_requesting_wait_for_debugger_.empty()) { |
| UpdateAutoAttach(base::DoNothing()); |
| } |
| } |
| |
| void TargetAutoAttacher::CreateAndAddNavigationThrottles( |
| NavigationThrottleRegistry& registry) { |
| for (auto& client : clients_) { |
| client.MaybeCreateAndAddNavigationThrottle(this, registry); |
| } |
| } |
| |
| void TargetAutoAttacher::DispatchAutoAttach(DevToolsAgentHost* host, |
| bool waiting_for_debugger) { |
| for (auto& client : clients_) { |
| client.AutoAttach( |
| this, host, |
| waiting_for_debugger && |
| clients_requesting_wait_for_debugger_.contains(&client)); |
| } |
| } |
| |
| void TargetAutoAttacher::DispatchAutoDetach(DevToolsAgentHost* host) { |
| for (auto& client : clients_) { |
| client.AutoDetach(this, host); |
| } |
| } |
| |
| void TargetAutoAttacher::DispatchSetAttachedTargetsOfType( |
| const base::flat_set<scoped_refptr<DevToolsAgentHost>>& hosts, |
| const std::string& type) { |
| for (auto& client : clients_) { |
| client.SetAttachedTargetsOfType(this, hosts, type); |
| } |
| } |
| |
| void TargetAutoAttacher::DispatchTargetInfoChanged(DevToolsAgentHost* host) { |
| for (auto& client : clients_) { |
| client.TargetInfoChanged(host); |
| } |
| } |
| |
| RendererAutoAttacherBase::RendererAutoAttacherBase( |
| DevToolsRendererChannel* renderer_channel) |
| : renderer_channel_(renderer_channel) {} |
| |
| RendererAutoAttacherBase::~RendererAutoAttacherBase() = default; |
| |
| void RendererAutoAttacherBase::UpdateAutoAttach(base::OnceClosure callback) { |
| DevToolsRendererChannel::ChildTargetCreatedCallback report_worker_callback; |
| if (auto_attach()) { |
| report_worker_callback = base::BindRepeating( |
| &RendererAutoAttacherBase::ChildWorkerCreated, base::Unretained(this)); |
| } |
| renderer_channel_->SetReportChildTargets(std::move(report_worker_callback), |
| wait_for_debugger_on_start(), |
| std::move(callback)); |
| } |
| |
| void RendererAutoAttacherBase::ChildWorkerCreated( |
| DevToolsAgentHostImpl* agent_host, |
| bool waiting_for_debugger) { |
| DispatchAutoAttach(agent_host, waiting_for_debugger); |
| } |
| |
| } // namespace protocol |
| } // namespace content |