• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 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.
4 
5 // This file contains implementations of the DebuggerRemoteService methods,
6 // defines DebuggerRemoteService and DebuggerRemoteServiceCommand constants.
7 
8 #include "chrome/browser/debugger/debugger_remote_service.h"
9 
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/string_number_conversions.h"
13 #include "base/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/debugger/devtools_manager.h"
16 #include "chrome/browser/debugger/devtools_protocol_handler.h"
17 #include "chrome/browser/debugger/devtools_remote_message.h"
18 #include "chrome/browser/debugger/inspectable_tab_proxy.h"
19 #include "chrome/common/devtools_messages.h"
20 #include "chrome/common/render_messages.h"
21 #include "content/browser/renderer_host/render_view_host.h"
22 #include "content/browser/tab_contents/tab_contents.h"
23 
24 namespace {
25 
26 // Constants for the "data", "result", and "command" JSON message fields.
27 const char kDataKey[] = "data";
28 const char kResultKey[] = "result";
29 const char kCommandKey[] = "command";
30 
31 }  // namespace
32 
33 const std::string DebuggerRemoteServiceCommand::kAttach = "attach";
34 const std::string DebuggerRemoteServiceCommand::kDetach = "detach";
35 const std::string DebuggerRemoteServiceCommand::kDebuggerCommand =
36     "debugger_command";
37 const std::string DebuggerRemoteServiceCommand::kEvaluateJavascript =
38     "evaluate_javascript";
39 const std::string DebuggerRemoteServiceCommand::kFrameNavigate =
40     "navigated";
41 const std::string DebuggerRemoteServiceCommand::kTabClosed =
42     "closed";
43 
44 const std::string DebuggerRemoteService::kToolName = "V8Debugger";
45 
DebuggerRemoteService(DevToolsProtocolHandler * delegate)46 DebuggerRemoteService::DebuggerRemoteService(DevToolsProtocolHandler* delegate)
47     : delegate_(delegate) {}
48 
~DebuggerRemoteService()49 DebuggerRemoteService::~DebuggerRemoteService() {}
50 
51 // This method handles the V8Debugger tool commands which are
52 // retrieved from the request "command" field. If an operation result
53 // is ready off-hand (synchronously), it is sent back to the remote debugger.
54 // Otherwise the corresponding response is received through IPC from the
55 // V8 debugger via DevToolsClientHost.
HandleMessage(const DevToolsRemoteMessage & message)56 void DebuggerRemoteService::HandleMessage(
57     const DevToolsRemoteMessage& message) {
58   const std::string destination = message.destination();
59   scoped_ptr<Value> request(base::JSONReader::Read(message.content(), true));
60   if (request.get() == NULL) {
61     // Bad JSON
62     NOTREACHED();
63     return;
64   }
65   DictionaryValue* content;
66   if (!request->IsType(Value::TYPE_DICTIONARY)) {
67     NOTREACHED();  // Broken protocol :(
68     return;
69   }
70   content = static_cast<DictionaryValue*>(request.get());
71   if (!content->HasKey(kCommandKey)) {
72     NOTREACHED();  // Broken protocol :(
73     return;
74   }
75   std::string command;
76   DictionaryValue response;
77 
78   content->GetString(kCommandKey, &command);
79   response.SetString(kCommandKey, command);
80   bool send_response = true;
81   if (destination.empty()) {
82     // Unknown command (bad format?)
83     NOTREACHED();
84     response.SetInteger(kResultKey, RESULT_UNKNOWN_COMMAND);
85     SendResponse(response, message.tool(), message.destination());
86     return;
87   }
88   int32 tab_uid = -1;
89   base::StringToInt(destination, &tab_uid);
90 
91   if (command == DebuggerRemoteServiceCommand::kAttach) {
92     // TODO(apavlov): handle 0 for a new tab
93     response.SetString(kCommandKey, DebuggerRemoteServiceCommand::kAttach);
94     AttachToTab(destination, &response);
95   } else if (command == DebuggerRemoteServiceCommand::kDetach) {
96     response.SetString(kCommandKey, DebuggerRemoteServiceCommand::kDetach);
97     DetachFromTab(destination, &response);
98   } else if (command == DebuggerRemoteServiceCommand::kDebuggerCommand) {
99     send_response = DispatchDebuggerCommand(tab_uid, content, &response);
100   } else if (command == DebuggerRemoteServiceCommand::kEvaluateJavascript) {
101     send_response = DispatchEvaluateJavascript(tab_uid, content, &response);
102   } else {
103     // Unknown command
104     NOTREACHED();
105     response.SetInteger(kResultKey, RESULT_UNKNOWN_COMMAND);
106   }
107 
108   if (send_response) {
109     SendResponse(response, message.tool(), message.destination());
110   }
111 }
112 
OnConnectionLost()113 void DebuggerRemoteService::OnConnectionLost() {
114   delegate_->inspectable_tab_proxy()->OnRemoteDebuggerDetached();
115 }
116 
117 // Sends a JSON response to the remote debugger using |response| as content,
118 // |tool| and |destination| as the respective header values.
SendResponse(const Value & response,const std::string & tool,const std::string & destination)119 void DebuggerRemoteService::SendResponse(const Value& response,
120                                          const std::string& tool,
121                                          const std::string& destination) {
122   std::string response_content;
123   base::JSONWriter::Write(&response, false, &response_content);
124   scoped_ptr<DevToolsRemoteMessage> response_message(
125       DevToolsRemoteMessageBuilder::instance().Create(tool,
126                                                       destination,
127                                                       response_content));
128   delegate_->Send(*response_message.get());
129 }
130 
131 // Gets a TabContents instance corresponding to the |tab_uid| using the
132 // InspectableTabProxy controllers map, or NULL if none found.
ToTabContents(int32 tab_uid)133 TabContents* DebuggerRemoteService::ToTabContents(int32 tab_uid) {
134   const InspectableTabProxy::ControllersMap& navcon_map =
135       delegate_->inspectable_tab_proxy()->controllers_map();
136   InspectableTabProxy::ControllersMap::const_iterator it =
137       navcon_map.find(tab_uid);
138   if (it != navcon_map.end()) {
139     TabContents* tab_contents = it->second->tab_contents();
140     if (tab_contents == NULL) {
141       return NULL;
142     } else {
143       return tab_contents;
144     }
145   } else {
146     return NULL;
147   }
148 }
149 
150 // Gets invoked from a DevToolsClientHost callback whenever
151 // a message from the V8 VM debugger corresponding to |tab_id| is received.
152 // Composes a Chrome Developer Tools Protocol JSON response and sends it
153 // to the remote debugger.
DebuggerOutput(int32 tab_uid,const std::string & message)154 void DebuggerRemoteService::DebuggerOutput(int32 tab_uid,
155                                            const std::string& message) {
156   std::string content = StringPrintf(
157       "{\"command\":\"%s\",\"result\":%s,\"data\":%s}",
158       DebuggerRemoteServiceCommand::kDebuggerCommand.c_str(),
159       base::IntToString(RESULT_OK).c_str(),
160       message.c_str());
161   scoped_ptr<DevToolsRemoteMessage> response_message(
162       DevToolsRemoteMessageBuilder::instance().Create(
163           kToolName,
164           base::IntToString(tab_uid),
165           content));
166   delegate_->Send(*(response_message.get()));
167 }
168 
169 // Gets invoked from a DevToolsClientHost callback whenever
170 // a tab corresponding to |tab_id| changes its URL. |url| is the new
171 // URL of the tab (may be the same as the previous one if the tab is reloaded).
172 // Sends the corresponding message to the remote debugger.
FrameNavigate(int32 tab_uid,const std::string & url)173 void DebuggerRemoteService::FrameNavigate(int32 tab_uid,
174                                           const std::string& url) {
175   DictionaryValue value;
176   value.SetString(kCommandKey, DebuggerRemoteServiceCommand::kFrameNavigate);
177   value.SetInteger(kResultKey, RESULT_OK);
178   value.SetString(kDataKey, url);
179   SendResponse(value, kToolName, base::IntToString(tab_uid));
180 }
181 
182 // Gets invoked from a DevToolsClientHost callback whenever
183 // a tab corresponding to |tab_id| gets closed.
184 // Sends the corresponding message to the remote debugger.
TabClosed(int32 tab_id)185 void DebuggerRemoteService::TabClosed(int32 tab_id) {
186   DictionaryValue value;
187   value.SetString(kCommandKey, DebuggerRemoteServiceCommand::kTabClosed);
188   value.SetInteger(kResultKey, RESULT_OK);
189   SendResponse(value, kToolName, base::IntToString(tab_id));
190 }
191 
192 // Attaches a remote debugger to the target tab specified by |destination|
193 // by posting the DevToolsAgentMsg_Attach message and sends a response
194 // to the remote debugger immediately.
AttachToTab(const std::string & destination,DictionaryValue * response)195 void DebuggerRemoteService::AttachToTab(const std::string& destination,
196                                         DictionaryValue* response) {
197   int32 tab_uid = -1;
198   base::StringToInt(destination, &tab_uid);
199   if (tab_uid < 0) {
200     // Bad tab_uid received from remote debugger (perhaps NaN)
201     response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
202     return;
203   }
204   if (tab_uid == 0) {  // single tab_uid
205     // We've been asked to open a new tab with URL
206     // TODO(apavlov): implement
207     NOTIMPLEMENTED();
208     response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
209     return;
210   }
211   TabContents* tab_contents = ToTabContents(tab_uid);
212   if (tab_contents == NULL) {
213     // No active tab contents with tab_uid
214     response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
215     return;
216   }
217   RenderViewHost* target_host = tab_contents->render_view_host();
218   DevToolsClientHost* client_host =
219       delegate_->inspectable_tab_proxy()->ClientHostForTabId(tab_uid);
220   if (client_host == NULL) {
221     client_host =
222         delegate_->inspectable_tab_proxy()->NewClientHost(tab_uid, this);
223     DevToolsManager* manager = DevToolsManager::GetInstance();
224     if (manager != NULL) {
225       manager->RegisterDevToolsClientHostFor(target_host, client_host);
226       response->SetInteger(kResultKey, RESULT_OK);
227     } else {
228       response->SetInteger(kResultKey, RESULT_DEBUGGER_ERROR);
229     }
230   } else {
231     // DevToolsClientHost for this tab is already registered
232     response->SetInteger(kResultKey, RESULT_ILLEGAL_TAB_STATE);
233   }
234 }
235 
236 // Detaches a remote debugger from the target tab specified by |destination|
237 // by posting the DevToolsAgentMsg_Detach message and sends a response
238 // to the remote debugger immediately.
DetachFromTab(const std::string & destination,DictionaryValue * response)239 void DebuggerRemoteService::DetachFromTab(const std::string& destination,
240                                           DictionaryValue* response) {
241   int32 tab_uid = -1;
242   base::StringToInt(destination, &tab_uid);
243   if (tab_uid == -1) {
244     // Bad tab_uid received from remote debugger (NaN)
245     if (response != NULL) {
246       response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
247     }
248     return;
249   }
250   int result_code;
251   DevToolsClientHostImpl* client_host =
252       delegate_->inspectable_tab_proxy()->ClientHostForTabId(tab_uid);
253   if (client_host != NULL) {
254     client_host->Close();
255     result_code = RESULT_OK;
256   } else {
257     // No client host registered for |tab_uid|.
258     result_code = RESULT_UNKNOWN_TAB;
259   }
260   if (response != NULL) {
261     response->SetInteger(kResultKey, result_code);
262   }
263 }
264 
265 // Sends a V8 debugger command to the target tab V8 debugger.
266 // Does not send back a response (which is received asynchronously
267 // through IPC) unless an error occurs before the command has actually
268 // been sent.
DispatchDebuggerCommand(int tab_uid,DictionaryValue * content,DictionaryValue * response)269 bool DebuggerRemoteService::DispatchDebuggerCommand(int tab_uid,
270                                                     DictionaryValue* content,
271                                                     DictionaryValue* response) {
272   if (tab_uid == -1) {
273     // Invalid tab_uid from remote debugger (perhaps NaN)
274     response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
275     return true;
276   }
277   DevToolsManager* manager = DevToolsManager::GetInstance();
278   if (manager == NULL) {
279     response->SetInteger(kResultKey, RESULT_DEBUGGER_ERROR);
280     return true;
281   }
282   TabContents* tab_contents = ToTabContents(tab_uid);
283   if (tab_contents == NULL) {
284     // Unknown tab_uid from remote debugger
285     response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
286     return true;
287   }
288   DevToolsClientHost* client_host =
289       manager->GetDevToolsClientHostFor(tab_contents->render_view_host());
290   if (client_host == NULL) {
291     // tab_uid is not being debugged (Attach has not been invoked)
292     response->SetInteger(kResultKey, RESULT_ILLEGAL_TAB_STATE);
293     return true;
294   }
295   std::string v8_command;
296   DictionaryValue* v8_command_value;
297   content->GetDictionary(kDataKey, &v8_command_value);
298   base::JSONWriter::Write(v8_command_value, false, &v8_command);
299   manager->ForwardToDevToolsAgent(
300       client_host, DevToolsAgentMsg_DebuggerCommand(v8_command));
301   // Do not send the response right now, as the JSON will be received from
302   // the V8 debugger asynchronously.
303   return false;
304 }
305 
306 // Sends the immediate "evaluate Javascript" command to the V8 debugger.
307 // The evaluation result is not sent back to the client as this command
308 // is in fact needed to invoke processing of queued debugger commands.
DispatchEvaluateJavascript(int tab_uid,DictionaryValue * content,DictionaryValue * response)309 bool DebuggerRemoteService::DispatchEvaluateJavascript(
310     int tab_uid,
311     DictionaryValue* content,
312     DictionaryValue* response) {
313   if (tab_uid == -1) {
314     // Invalid tab_uid from remote debugger (perhaps NaN)
315     response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
316     return true;
317   }
318   TabContents* tab_contents = ToTabContents(tab_uid);
319   if (tab_contents == NULL) {
320     // Unknown tab_uid from remote debugger
321     response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
322     return true;
323   }
324   RenderViewHost* render_view_host = tab_contents->render_view_host();
325   if (render_view_host == NULL) {
326     // No RenderViewHost
327     response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB);
328     return true;
329   }
330   std::string javascript;
331   content->GetString(kDataKey, &javascript);
332   render_view_host->ExecuteJavascriptInWebFrame(string16(),
333                                                 UTF8ToUTF16(javascript));
334   return false;
335 }
336