| // Copyright 2016 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 "content/browser/devtools/devtools_session.h" |
| |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "content/browser/devtools/devtools_manager.h" |
| #include "content/browser/devtools/protocol/protocol.h" |
| #include "content/browser/devtools/render_frame_devtools_agent_host.h" |
| #include "content/browser/frame_host/render_frame_host_impl.h" |
| #include "content/public/browser/devtools_manager_delegate.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| bool ShouldSendOnIO(const std::string& method) { |
| // Keep in sync with WebDevToolsAgent::ShouldInterruptForMethod. |
| // TODO(dgozman): find a way to share this. |
| return method == "Debugger.pause" || method == "Debugger.setBreakpoint" || |
| method == "Debugger.setBreakpointByUrl" || |
| method == "Debugger.removeBreakpoint" || |
| method == "Debugger.setBreakpointsActive" || |
| method == "Performance.getMetrics" || method == "Page.crash"; |
| } |
| |
| } // namespace |
| |
| DevToolsSession::DevToolsSession(DevToolsAgentHostImpl* agent_host, |
| DevToolsAgentHostClient* client) |
| : binding_(this), |
| agent_host_(agent_host), |
| client_(client), |
| process_(nullptr), |
| host_(nullptr), |
| dispatcher_(new protocol::UberDispatcher(this)), |
| weak_factory_(this) {} |
| |
| DevToolsSession::~DevToolsSession() { |
| dispatcher_.reset(); |
| for (auto& pair : handlers_) |
| pair.second->Disable(); |
| handlers_.clear(); |
| } |
| |
| void DevToolsSession::AddHandler( |
| std::unique_ptr<protocol::DevToolsDomainHandler> handler) { |
| handler->Wire(dispatcher_.get()); |
| handler->SetRenderer(process_, host_); |
| handlers_[handler->name()] = std::move(handler); |
| } |
| |
| void DevToolsSession::SetRenderer(RenderProcessHost* process_host, |
| RenderFrameHostImpl* frame_host) { |
| process_ = process_host; |
| host_ = frame_host; |
| for (auto& pair : handlers_) |
| pair.second->SetRenderer(process_, host_); |
| } |
| |
| void DevToolsSession::SetFallThroughForNotFound(bool value) { |
| dispatcher_->setFallThroughForNotFound(value); |
| } |
| |
| void DevToolsSession::AttachToAgent( |
| const blink::mojom::DevToolsAgentAssociatedPtr& agent) { |
| blink::mojom::DevToolsSessionHostAssociatedPtrInfo host_ptr_info; |
| binding_.Bind(mojo::MakeRequest(&host_ptr_info)); |
| agent->AttachDevToolsSession( |
| std::move(host_ptr_info), mojo::MakeRequest(&session_ptr_), |
| mojo::MakeRequest(&io_session_ptr_), base::Optional<std::string>()); |
| session_ptr_.set_connection_error_handler(base::BindOnce( |
| &DevToolsSession::MojoConnectionDestroyed, base::Unretained(this))); |
| } |
| |
| void DevToolsSession::ReattachToAgent( |
| const blink::mojom::DevToolsAgentAssociatedPtr& agent) { |
| blink::mojom::DevToolsSessionHostAssociatedPtrInfo host_ptr_info; |
| binding_.Bind(mojo::MakeRequest(&host_ptr_info)); |
| agent->AttachDevToolsSession( |
| std::move(host_ptr_info), mojo::MakeRequest(&session_ptr_), |
| mojo::MakeRequest(&io_session_ptr_), state_cookie_); |
| session_ptr_.set_connection_error_handler(base::BindOnce( |
| &DevToolsSession::MojoConnectionDestroyed, base::Unretained(this))); |
| } |
| |
| void DevToolsSession::SendResponse( |
| std::unique_ptr<base::DictionaryValue> response) { |
| std::string json; |
| base::JSONWriter::Write(*response.get(), &json); |
| client_->DispatchProtocolMessage(agent_host_, json); |
| } |
| |
| void DevToolsSession::MojoConnectionDestroyed() { |
| binding_.Close(); |
| session_ptr_.reset(); |
| io_session_ptr_.reset(); |
| } |
| |
| protocol::Response::Status DevToolsSession::Dispatch( |
| const std::string& message, |
| int* call_id, |
| std::string* method) { |
| std::unique_ptr<base::Value> value = base::JSONReader::Read(message); |
| |
| DevToolsManagerDelegate* delegate = |
| DevToolsManager::GetInstance()->delegate(); |
| if (value && value->is_dict() && delegate) { |
| base::DictionaryValue* dict_value = |
| static_cast<base::DictionaryValue*>(value.get()); |
| |
| if (delegate->HandleCommand(agent_host_, client_, dict_value)) |
| return protocol::Response::kSuccess; |
| |
| if (delegate->HandleAsyncCommand(agent_host_, client_, dict_value, |
| base::Bind(&DevToolsSession::SendResponse, |
| weak_factory_.GetWeakPtr()))) { |
| return protocol::Response::kAsync; |
| } |
| } |
| |
| return dispatcher_->dispatch(protocol::toProtocolValue(value.get(), 1000), |
| call_id, method); |
| } |
| |
| void DevToolsSession::DispatchProtocolMessageToAgent( |
| int call_id, |
| const std::string& method, |
| const std::string& message) { |
| if (ShouldSendOnIO(method)) { |
| if (io_session_ptr_) |
| io_session_ptr_->DispatchProtocolMessage(call_id, method, message); |
| } else { |
| if (session_ptr_) |
| session_ptr_->DispatchProtocolMessage(call_id, method, message); |
| } |
| } |
| |
| void DevToolsSession::InspectElement(const gfx::Point& point) { |
| if (session_ptr_) |
| session_ptr_->InspectElement(point); |
| } |
| |
| void DevToolsSession::sendProtocolResponse( |
| int call_id, |
| std::unique_ptr<protocol::Serializable> message) { |
| client_->DispatchProtocolMessage(agent_host_, message->serialize()); |
| } |
| |
| void DevToolsSession::sendProtocolNotification( |
| std::unique_ptr<protocol::Serializable> message) { |
| client_->DispatchProtocolMessage(agent_host_, message->serialize()); |
| } |
| |
| void DevToolsSession::flushProtocolNotifications() { |
| } |
| |
| void DevToolsSession::DispatchProtocolMessage( |
| blink::mojom::DevToolsMessageChunkPtr chunk) { |
| if (chunk->is_first && !response_message_buffer_.empty()) { |
| ReceivedBadMessage(); |
| return; |
| } |
| |
| response_message_buffer_ += std::move(chunk->data); |
| |
| if (!chunk->is_last) |
| return; |
| |
| if (!chunk->post_state.empty()) |
| state_cookie_ = std::move(chunk->post_state); |
| waiting_for_response_messages_.erase(chunk->call_id); |
| std::string message; |
| message.swap(response_message_buffer_); |
| client_->DispatchProtocolMessage(agent_host_, message); |
| // |this| may be deleted at this point. |
| } |
| |
| void DevToolsSession::ReceivedBadMessage() { |
| MojoConnectionDestroyed(); |
| if (process_) { |
| bad_message::ReceivedBadMessage( |
| process_, bad_message::RFH_INCONSISTENT_DEVTOOLS_MESSAGE); |
| } |
| } |
| |
| } // namespace content |