1 // Copyright (c) 2012 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 "content/renderer/devtools/devtools_agent.h"
9 #include "base/lazy_instance.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/trace_event/trace_event.h"
13 #include "content/common/devtools_messages.h"
14 #include "content/common/frame_messages.h"
15 #include "content/renderer/devtools/devtools_client.h"
16 #include "content/renderer/render_frame_impl.h"
17 #include "content/renderer/render_widget.h"
18 #include "ipc/ipc_channel.h"
19 #include "third_party/WebKit/public/platform/WebPoint.h"
20 #include "third_party/WebKit/public/platform/WebString.h"
21 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
22 #include "third_party/WebKit/public/web/WebDevToolsAgent.h"
23 #include "third_party/WebKit/public/web/WebLocalFrame.h"
25 #if defined(USE_TCMALLOC)
26 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
29 using blink::WebConsoleMessage
;
30 using blink::WebDevToolsAgent
;
31 using blink::WebDevToolsAgentClient
;
32 using blink::WebLocalFrame
;
33 using blink::WebPoint
;
34 using blink::WebString
;
36 using base::trace_event::TraceLog
;
42 const size_t kMaxMessageChunkSize
= IPC::Channel::kMaximumMessageSize
/ 4;
44 class WebKitClientMessageLoopImpl
45 : public WebDevToolsAgentClient::WebKitClientMessageLoop
{
47 WebKitClientMessageLoopImpl() : message_loop_(base::MessageLoop::current()) {}
48 virtual ~WebKitClientMessageLoopImpl() { message_loop_
= NULL
; }
50 base::MessageLoop::ScopedNestableTaskAllower
allow(message_loop_
);
53 virtual void quitNow() {
54 message_loop_
->QuitNow();
57 base::MessageLoop
* message_loop_
;
60 typedef std::map
<int, DevToolsAgent
*> IdToAgentMap
;
61 base::LazyInstance
<IdToAgentMap
>::Leaky
62 g_agent_for_routing_id
= LAZY_INSTANCE_INITIALIZER
;
66 DevToolsAgent::DevToolsAgent(RenderFrameImpl
* frame
)
67 : RenderFrameObserver(frame
),
69 is_devtools_client_(false),
70 paused_in_mouse_move_(false),
73 g_agent_for_routing_id
.Get()[routing_id()] = this;
74 frame_
->GetWebFrame()->setDevToolsAgentClient(this);
77 DevToolsAgent::~DevToolsAgent() {
78 g_agent_for_routing_id
.Get().erase(routing_id());
81 // Called on the Renderer thread.
82 bool DevToolsAgent::OnMessageReceived(const IPC::Message
& message
) {
84 IPC_BEGIN_MESSAGE_MAP(DevToolsAgent
, message
)
85 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Attach
, OnAttach
)
86 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Reattach
, OnReattach
)
87 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Detach
, OnDetach
)
88 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_DispatchOnInspectorBackend
,
89 OnDispatchOnInspectorBackend
)
90 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_InspectElement
, OnInspectElement
)
91 IPC_MESSAGE_HANDLER(DevToolsMsg_SetupDevToolsClient
, OnSetupDevToolsClient
)
92 IPC_MESSAGE_UNHANDLED(handled
= false)
95 if (message
.type() == FrameMsg_Navigate::ID
)
96 ContinueProgram(); // Don't want to swallow the message.
101 void DevToolsAgent::WidgetWillClose() {
105 void DevToolsAgent::sendProtocolMessage(
107 const blink::WebString
& message
,
108 const blink::WebString
& state_cookie
) {
109 SendChunkedProtocolMessage(
110 this, routing_id(), call_id
, message
.utf8(), state_cookie
.utf8());
113 blink::WebDevToolsAgentClient::WebKitClientMessageLoop
*
114 DevToolsAgent::createClientMessageLoop() {
115 return new WebKitClientMessageLoopImpl();
118 void DevToolsAgent::willEnterDebugLoop() {
120 if (RenderWidget
* widget
= frame_
->GetRenderWidget())
121 paused_in_mouse_move_
= widget
->SendAckForMouseMoveFromDebugger();
124 void DevToolsAgent::didExitDebugLoop() {
126 if (!paused_in_mouse_move_
)
128 if (RenderWidget
* widget
= frame_
->GetRenderWidget()) {
129 widget
->IgnoreAckForMouseMoveFromDebugger();
130 paused_in_mouse_move_
= false;
134 void DevToolsAgent::enableTracing(const WebString
& category_filter
) {
135 TraceLog
* trace_log
= TraceLog::GetInstance();
136 trace_log
->SetEnabled(
137 base::trace_event::TraceConfig(category_filter
.utf8(), ""),
138 TraceLog::RECORDING_MODE
);
141 void DevToolsAgent::disableTracing() {
142 TraceLog::GetInstance()->SetDisabled();
146 DevToolsAgent
* DevToolsAgent::FromRoutingId(int routing_id
) {
147 IdToAgentMap::iterator it
= g_agent_for_routing_id
.Get().find(routing_id
);
148 if (it
!= g_agent_for_routing_id
.Get().end()) {
155 void DevToolsAgent::SendChunkedProtocolMessage(
159 const std::string
& message
,
160 const std::string
& post_state
) {
161 DevToolsMessageChunk chunk
;
162 chunk
.message_size
= message
.size();
163 chunk
.is_first
= true;
165 if (message
.length() < kMaxMessageChunkSize
) {
166 chunk
.data
= message
;
167 chunk
.call_id
= call_id
;
168 chunk
.post_state
= post_state
;
169 chunk
.is_last
= true;
170 sender
->Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
175 for (size_t pos
= 0; pos
< message
.length(); pos
+= kMaxMessageChunkSize
) {
176 chunk
.is_last
= pos
+ kMaxMessageChunkSize
>= message
.length();
177 chunk
.call_id
= chunk
.is_last
? call_id
: 0;
178 chunk
.post_state
= chunk
.is_last
? post_state
: std::string();
179 chunk
.data
= message
.substr(pos
, kMaxMessageChunkSize
);
180 sender
->Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
182 chunk
.is_first
= false;
183 chunk
.message_size
= 0;
187 void DevToolsAgent::OnAttach(const std::string
& host_id
) {
188 WebDevToolsAgent
* web_agent
= GetWebAgent();
190 web_agent
->attach(WebString::fromUTF8(host_id
));
195 void DevToolsAgent::OnReattach(const std::string
& host_id
,
196 const std::string
& agent_state
) {
197 WebDevToolsAgent
* web_agent
= GetWebAgent();
199 web_agent
->reattach(WebString::fromUTF8(host_id
),
200 WebString::fromUTF8(agent_state
));
205 void DevToolsAgent::OnDetach() {
206 WebDevToolsAgent
* web_agent
= GetWebAgent();
209 is_attached_
= false;
213 void DevToolsAgent::OnDispatchOnInspectorBackend(const std::string
& message
) {
214 TRACE_EVENT0("devtools", "DevToolsAgent::OnDispatchOnInspectorBackend");
215 WebDevToolsAgent
* web_agent
= GetWebAgent();
217 web_agent
->dispatchOnInspectorBackend(WebString::fromUTF8(message
));
220 void DevToolsAgent::OnInspectElement(
221 const std::string
& host_id
, int x
, int y
) {
222 WebDevToolsAgent
* web_agent
= GetWebAgent();
224 web_agent
->attach(WebString::fromUTF8(host_id
));
225 web_agent
->inspectElementAt(WebPoint(x
, y
));
230 void DevToolsAgent::AddMessageToConsole(ConsoleMessageLevel level
,
231 const std::string
& message
) {
232 WebLocalFrame
* web_frame
= frame_
->GetWebFrame();
236 WebConsoleMessage::Level target_level
= WebConsoleMessage::LevelLog
;
238 case CONSOLE_MESSAGE_LEVEL_DEBUG
:
239 target_level
= WebConsoleMessage::LevelDebug
;
241 case CONSOLE_MESSAGE_LEVEL_LOG
:
242 target_level
= WebConsoleMessage::LevelLog
;
244 case CONSOLE_MESSAGE_LEVEL_WARNING
:
245 target_level
= WebConsoleMessage::LevelWarning
;
247 case CONSOLE_MESSAGE_LEVEL_ERROR
:
248 target_level
= WebConsoleMessage::LevelError
;
251 web_frame
->addMessageToConsole(
252 WebConsoleMessage(target_level
, WebString::fromUTF8(message
)));
255 void DevToolsAgent::ContinueProgram() {
256 WebDevToolsAgent
* web_agent
= GetWebAgent();
258 web_agent
->continueProgram();
261 void DevToolsAgent::OnSetupDevToolsClient(
262 const std::string
& compatibility_script
) {
263 // We only want to register once; and only in main frame.
264 DCHECK(!frame_
->GetWebFrame() || !frame_
->GetWebFrame()->parent());
265 if (is_devtools_client_
)
267 is_devtools_client_
= true;
268 new DevToolsClient(frame_
, compatibility_script
);
271 WebDevToolsAgent
* DevToolsAgent::GetWebAgent() {
272 WebLocalFrame
* web_frame
= frame_
->GetWebFrame();
273 return web_frame
? web_frame
->devToolsAgent() : nullptr;
276 bool DevToolsAgent::IsAttached() {
280 } // namespace content