910e62b5创建于 1月15日历史提交
// 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