1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "mandoline/tab/frame_devtools_agent.h"
11 #include "base/bind.h"
12 #include "base/guid.h"
13 #include "base/json/json_reader.h"
14 #include "base/logging.h"
15 #include "base/values.h"
16 #include "mandoline/tab/frame_devtools_agent_delegate.h"
17 #include "mojo/application/public/cpp/application_impl.h"
18 #include "mojo/services/network/public/interfaces/url_loader.mojom.h"
23 using devtools_service::DevToolsAgentClientPtr
;
24 using devtools_service::DevToolsAgentPtr
;
25 using devtools_service::DevToolsRegistryPtr
;
31 void StringToVector(const std::string
& in
, std::vector
<uint8_t>* out
) {
32 out
->resize(in
.size());
34 memcpy(&out
->front(), in
.c_str(), in
.size());
39 // This class is used by FrameDevToolsAgent to relay client messages from the
40 // frame to the DevTools service.
41 class FrameDevToolsAgent::FrameDevToolsAgentClient
42 : public devtools_service::DevToolsAgentClient
{
44 FrameDevToolsAgentClient(FrameDevToolsAgent
* owner
,
45 DevToolsAgentClientPtr forward_client
)
46 : owner_(owner
), binding_(this), forward_client_(forward_client
.Pass()) {
47 forward_client_
.set_connection_error_handler(base::Bind(
48 &FrameDevToolsAgent::OnForwardClientClosed
, base::Unretained(owner_
)));
49 if (owner_
->forward_agent_
)
53 ~FrameDevToolsAgentClient() override
{}
55 void OnAttachedFrame() {
56 DCHECK(owner_
->forward_agent_
);
58 if (binding_
.is_bound())
61 DevToolsAgentClientPtr client
;
62 binding_
.Bind(&client
);
63 owner_
->forward_agent_
->SetClient(client
.Pass());
67 // DevToolsAgentClient implementation.
68 void DispatchProtocolMessage(int32_t call_id
,
69 const String
& message
,
70 const String
& state
) override
{
71 DCHECK(forward_client_
);
72 owner_
->OnReceivedClientMessage(call_id
, message
, state
);
73 // The state is used to persist state across frame navigations. There is no
74 // need to forward it.
75 forward_client_
->DispatchProtocolMessage(call_id
, message
, String());
78 FrameDevToolsAgent
* const owner_
;
79 mojo::Binding
<DevToolsAgentClient
> binding_
;
80 // The DevToolsAgentClient interface provided by the DevTools service. This
81 // class will forward DevToolsAgentClient method calls it receives to
83 DevToolsAgentClientPtr forward_client_
;
85 DISALLOW_COPY_AND_ASSIGN(FrameDevToolsAgentClient
);
88 FrameDevToolsAgent::FrameDevToolsAgent(mojo::ApplicationImpl
* app
,
89 FrameDevToolsAgentDelegate
* delegate
)
92 id_(base::GenerateGUID()),
98 FrameDevToolsAgent::~FrameDevToolsAgent() {}
100 void FrameDevToolsAgent::AttachFrame(
101 DevToolsAgentPtr forward_agent
,
102 mandoline::Frame::ClientPropertyMap
* devtools_properties
) {
103 RegisterAgentIfNecessary();
104 forward_agent_
= forward_agent
.Pass();
106 StringToVector(id_
, &(*devtools_properties
)["devtools-id"]);
108 StringToVector(state_
, &(*devtools_properties
)["devtools-state"]);
109 client_impl_
->OnAttachedFrame();
112 // This follows Chrome's logic and relies on the fact that call IDs increase
113 // monotonously so iterating over |pending_messages_| preserves the order in
114 // which they were received.
115 for (const auto& item
: pending_messages_
)
116 forward_agent_
->DispatchProtocolMessage(item
.second
);
119 void FrameDevToolsAgent::RegisterAgentIfNecessary() {
120 if (binding_
.is_bound())
123 mojo::URLRequestPtr
request(mojo::URLRequest::New());
124 request
->url
= "mojo:devtools_service";
125 DevToolsRegistryPtr devtools_registry
;
126 app_
->ConnectToService(request
.Pass(), &devtools_registry
);
128 DevToolsAgentPtr agent
;
129 binding_
.Bind(&agent
);
130 devtools_registry
->RegisterAgent(id_
, agent
.Pass());
133 void FrameDevToolsAgent::HandlePageNavigateRequest(
134 const base::DictionaryValue
* request
) {
136 if (!request
->GetString("method", &method
) || method
!= "Page.navigate")
139 std::string url_string
;
140 if (!request
->GetString("params.url", &url_string
))
143 GURL
url(url_string
);
147 // Stop sending messages to the old frame which will be navigated away soon.
148 // However, we don't reset |client_impl_| because we want to receive responses
149 // and events from the old frame in the meantime.
150 forward_agent_
.reset();
152 delegate_
->HandlePageNavigateRequest(url
);
155 void FrameDevToolsAgent::SetClient(DevToolsAgentClientPtr client
) {
156 client_impl_
.reset(new FrameDevToolsAgentClient(this, client
.Pass()));
159 void FrameDevToolsAgent::DispatchProtocolMessage(const String
& message
) {
160 // TODO(yzshen): Consider refactoring and reusing the existing DevTools
161 // protocol parsing code.
163 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(message
.get());
164 base::DictionaryValue
* command
= nullptr;
165 if (!value
|| !value
->GetAsDictionary(&command
)) {
166 VLOG(1) << "Unable to parse DevTools message: " << message
;
171 if (!command
->GetInteger("id", &call_id
)) {
172 VLOG(1) << "Call Id not found in a DevTools request message: " << message
;
176 pending_messages_
[call_id
] = message
;
178 HandlePageNavigateRequest(command
);
181 forward_agent_
->DispatchProtocolMessage(message
);
184 void FrameDevToolsAgent::OnReceivedClientMessage(int32_t call_id
,
185 const String
& message
,
186 const String
& state
) {
187 if (!state
.is_null() && state
.size() > 0)
190 pending_messages_
.erase(call_id
);
193 void FrameDevToolsAgent::OnForwardClientClosed() {
194 client_impl_
.reset();
196 pending_messages_
.clear();
199 } // namespace web_view