diff options
6 files changed, 207 insertions, 110 deletions
diff --git a/chrome/browser/debugger/debugger_remote_service.cc b/chrome/browser/debugger/debugger_remote_service.cc index 95e8ec6..f2a0b68 100644 --- a/chrome/browser/debugger/debugger_remote_service.cc +++ b/chrome/browser/debugger/debugger_remote_service.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file contains implementations of the DebuggerRemoteService methods, +// defines DebuggerRemoteService and DebuggerRemoteServiceCommand constants. + #include "chrome/browser/debugger/debugger_remote_service.h" #include "base/json_reader.h" @@ -17,6 +20,20 @@ #include "chrome/common/devtools_messages.h" #include "chrome/common/render_messages.h" +namespace { + +// A constant for the "data" JSON message field. +// The type is wstring because the constant is used to get a +// DictionaryValue field (which requires a wide string). +static const std::wstring kDataWide = L"data"; + +// A constant for the "result" JSON message field. +// The type is wstring because the constant is used to get a +// DictionaryValue field (which requires a wide string). +static const std::wstring kResultWide = L"result"; + +} // namespace + const std::string DebuggerRemoteServiceCommand::kAttach = "attach"; const std::string DebuggerRemoteServiceCommand::kDetach = "detach"; const std::string DebuggerRemoteServiceCommand::kDebuggerCommand = @@ -25,15 +42,17 @@ const std::string DebuggerRemoteServiceCommand::kEvaluateJavascript = "evaluate_javascript"; const std::string DebuggerRemoteService::kToolName = "V8Debugger"; -const std::wstring DebuggerRemoteService::kDataWide = L"data"; -const std::wstring DebuggerRemoteService::kResultWide = L"result"; DebuggerRemoteService::DebuggerRemoteService(DevToolsProtocolHandler* delegate) : delegate_(delegate) {} DebuggerRemoteService::~DebuggerRemoteService() {} -// message from remote debugger +// This method handles the V8Debugger tool commands which are +// retrieved from the request "command" field. If an operation result +// is ready off-hand (synchronously), it is sent back to the remote debugger. +// Otherwise the corresponding response is received through IPC from the +// V8 debugger via DevToolsClientHost. void DebuggerRemoteService::HandleMessage( const DevToolsRemoteMessage& message) { static const std::wstring kCommandWide = L"command"; @@ -63,7 +82,7 @@ void DebuggerRemoteService::HandleMessage( if (destination.size() == 0) { // Unknown command (bad format?) NOTREACHED(); - response.SetInteger(kResultWide, Result::kUnknownCommand); + response.SetInteger(kResultWide, RESULT_UNKNOWN_COMMAND); SendResponse(response, message.tool(), message.destination()); return; } @@ -73,66 +92,18 @@ void DebuggerRemoteService::HandleMessage( if (command == DebuggerRemoteServiceCommand::kAttach) { // TODO(apavlov): handle 0 for a new tab response.SetString(kCommandWide, DebuggerRemoteServiceCommand::kAttach); - AttachTab(destination, &response); + AttachToTab(destination, &response); } else if (command == DebuggerRemoteServiceCommand::kDetach) { response.SetString(kCommandWide, DebuggerRemoteServiceCommand::kDetach); - DetachTab(destination, &response); + DetachFromTab(destination, &response); } else if (command == DebuggerRemoteServiceCommand::kDebuggerCommand) { - if (tab_uid != -1) { - DevToolsManager* manager = g_browser_process->devtools_manager(); - if (manager == NULL) { - response.SetInteger(kResultWide, Result::kDebuggerError); - } - TabContents* tab_contents = ToTabContents(tab_uid); - if (tab_contents != NULL) { - DevToolsClientHost* client_host = - manager->GetDevToolsClientHostFor(tab_contents->render_view_host()); - if (client_host != NULL) { - std::string v8_command; - DictionaryValue* v8_command_value; - content->GetDictionary(kDataWide, &v8_command_value); - JSONWriter::Write(v8_command_value, false, &v8_command); - g_browser_process->devtools_manager()->ForwardToDevToolsAgent( - client_host, DevToolsAgentMsg_DebuggerCommand(v8_command)); - send_response = false; - // Do not send response right now as the JSON will be received from - // the V8 debugger asynchronously - } else { - // tab_uid is not being debugged (Attach has not been invoked) - response.SetInteger(kResultWide, Result::kIllegalTabState); - } - } else { - // Unknown tab_uid from remote debugger - response.SetInteger(kResultWide, Result::kUnknownTab); - } - } else { - // Invalid tab_uid from remote debugger (perhaps NaN) - response.SetInteger(kResultWide, Result::kUnknownTab); - } + send_response = DispatchDebuggerCommand(tab_uid, content, &response); } else if (command == DebuggerRemoteServiceCommand::kEvaluateJavascript) { - if (tab_uid != -1) { - TabContents* tab_contents = ToTabContents(tab_uid); - if (tab_contents != NULL) { - RenderViewHost* rvh = tab_contents->render_view_host(); - if (rvh != NULL) { - std::wstring javascript; - content->GetString(kDataWide, &javascript); - rvh->Send(new ViewMsg_ScriptEvalRequest( - rvh->routing_id(), L"", javascript)); - send_response = false; - } else { - // No RenderViewHost - response.SetInteger(kResultWide, Result::kDebuggerError); - } - } else { - // Unknown tab_uid from remote debugger - response.SetInteger(kResultWide, Result::kUnknownTab); - } - } + send_response = DispatchEvaluateJavascript(tab_uid, content, &response); } else { // Unknown command NOTREACHED(); - response.SetInteger(kResultWide, Result::kUnknownCommand); + response.SetInteger(kResultWide, RESULT_UNKNOWN_COMMAND); } if (send_response) { @@ -144,6 +115,8 @@ void DebuggerRemoteService::OnConnectionLost() { delegate_->inspectable_tab_proxy()->OnRemoteDebuggerDetached(); } +// Sends a JSON response to the remote debugger using |response| as content, +// |tool| and |destination| as the respective header values. void DebuggerRemoteService::SendResponse(const Value& response, const std::string& tool, const std::string& destination) { @@ -156,6 +129,8 @@ void DebuggerRemoteService::SendResponse(const Value& response, delegate_->Send(*response_message.get()); } +// Gets a TabContents instance corresponding to the |tab_uid| using the +// InspectableTabProxy controllers map, or NULL if none found. TabContents* DebuggerRemoteService::ToTabContents(int32 tab_uid) { const InspectableTabProxy::ControllersMap& navcon_map = delegate_->inspectable_tab_proxy()->controllers_map(); @@ -173,16 +148,17 @@ TabContents* DebuggerRemoteService::ToTabContents(int32 tab_uid) { } } +// Gets invoked from a DevToolsClientHost callback whenever +// a message from the V8 VM debugger corresponding to |tab_id| is received. +// Composes a Chrome Developer Tools Protocol JSON response and sends it +// to the remote debugger. void DebuggerRemoteService::DebuggerOutput(int32 tab_id, const std::string& message) { - std::string content; - content.append("{\"command\":\"") - .append(DebuggerRemoteServiceCommand::kDebuggerCommand) - .append("\",\"result\":") - .append(IntToString(Result::kOk)) - .append(",\"data\":") - .append(message) - .append("}"); + std::string content = StringPrintf( + "{\"command\":\"%s\",\"result\":%s,\"data\":%s}", + DebuggerRemoteServiceCommand::kDebuggerCommand.c_str(), + IntToString(RESULT_OK).c_str(), + message.c_str()); scoped_ptr<DevToolsRemoteMessage> response_message( DevToolsRemoteMessageBuilder::instance().Create( kToolName, @@ -191,63 +167,69 @@ void DebuggerRemoteService::DebuggerOutput(int32 tab_id, delegate_->Send(*(response_message.get())); } -void DebuggerRemoteService::AttachTab(const std::string& destination, - DictionaryValue* response) { +// Attaches a remote debugger to the target tab specified by |destination| +// by posting the DevToolsAgentMsg_Attach message and sends a response +// to the remote debugger immediately. +void DebuggerRemoteService::AttachToTab(const std::string& destination, + DictionaryValue* response) { int32 tab_uid = -1; StringToInt(destination, &tab_uid); if (tab_uid < 0) { // Bad tab_uid received from remote debugger (perhaps NaN) - response->SetInteger(kResultWide, Result::kUnknownTab); + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); return; } if (tab_uid == 0) { // single tab_uid // We've been asked to open a new tab with URL. // TODO(apavlov): implement NOTIMPLEMENTED(); - response->SetInteger(kResultWide, Result::kUnknownTab); + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); return; } TabContents* tab_contents = ToTabContents(tab_uid); if (tab_contents == NULL) { - // No active web contents with tab_uid - response->SetInteger(kResultWide, Result::kUnknownTab); + // No active tab contents with tab_uid + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); return; } RenderViewHost* target_host = tab_contents->render_view_host(); if (g_browser_process->devtools_manager()->GetDevToolsClientHostFor( - target_host) == NULL) { + target_host) == NULL) { DevToolsClientHost* client_host = delegate_->inspectable_tab_proxy()->NewClientHost(tab_uid, this); DevToolsManager* manager = g_browser_process->devtools_manager(); if (manager != NULL) { manager->RegisterDevToolsClientHostFor(target_host, client_host); manager->ForwardToDevToolsAgent(client_host, DevToolsAgentMsg_Attach()); - response->SetInteger(kResultWide, Result::kOk); + response->SetInteger(kResultWide, RESULT_OK); } else { - response->SetInteger(kResultWide, Result::kDebuggerError); + response->SetInteger(kResultWide, RESULT_DEBUGGER_ERROR); } } else { // DevToolsClientHost for this tab already registered - response->SetInteger(kResultWide, Result::kIllegalTabState); + response->SetInteger(kResultWide, RESULT_ILLEGAL_TAB_STATE); } } -void DebuggerRemoteService::DetachTab(const std::string& destination, - DictionaryValue* response) { +// Detaches a remote debugger from the target tab specified by |destination| +// by posting the DevToolsAgentMsg_Detach message and sends a response +// to the remote debugger immediately. +void DebuggerRemoteService::DetachFromTab(const std::string& destination, + DictionaryValue* response) { int32 tab_uid = -1; - int resultCode = -1; StringToInt(destination, &tab_uid); if (tab_uid == -1) { // Bad tab_uid received from remote debugger (NaN) if (response != NULL) { - response->SetInteger(kResultWide, Result::kUnknownTab); + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); } return; } + int result_code; TabContents* tab_contents = ToTabContents(tab_uid); if (tab_contents == NULL) { // Unknown tab - resultCode = Result::kUnknownTab; + result_code = RESULT_UNKNOWN_TAB; } else { DevToolsManager* manager = g_browser_process->devtools_manager(); if (manager != NULL) { @@ -257,17 +239,91 @@ void DebuggerRemoteService::DetachTab(const std::string& destination, manager->ForwardToDevToolsAgent( client_host, DevToolsAgentMsg_Detach()); client_host->InspectedTabClosing(); - resultCode = Result::kOk; + result_code = RESULT_OK; } else { // No client host registered - resultCode = Result::kUnknownTab; + result_code = RESULT_UNKNOWN_TAB; } } else { // No DevToolsManager - resultCode = Result::kDebuggerError; + result_code = RESULT_DEBUGGER_ERROR; } } if (response != NULL) { - response->SetInteger(kResultWide, resultCode); + response->SetInteger(kResultWide, result_code); + } +} + +// Sends a V8 debugger command to the target tab V8 debugger. +// Does not send back a response (which is received asynchronously +// through IPC) unless an error occurs before the command has actually +// been sent. +bool DebuggerRemoteService::DispatchDebuggerCommand(int tab_uid, + DictionaryValue* content, + DictionaryValue* response) { + if (tab_uid == -1) { + // Invalid tab_uid from remote debugger (perhaps NaN) + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); + return true; + } + DevToolsManager* manager = g_browser_process->devtools_manager(); + if (manager == NULL) { + response->SetInteger(kResultWide, RESULT_DEBUGGER_ERROR); + return true; + } + TabContents* tab_contents = ToTabContents(tab_uid); + if (tab_contents == NULL) { + // Unknown tab_uid from remote debugger + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); + return true; + } + DevToolsClientHost* client_host = + manager->GetDevToolsClientHostFor(tab_contents->render_view_host()); + if (client_host == NULL) { + // tab_uid is not being debugged (Attach has not been invoked) + response->SetInteger(kResultWide, RESULT_ILLEGAL_TAB_STATE); + return true; + } + std::string v8_command; + DictionaryValue* v8_command_value; + content->GetDictionary(kDataWide, &v8_command_value); + JSONWriter::Write(v8_command_value, false, &v8_command); + g_browser_process->devtools_manager()->ForwardToDevToolsAgent( + client_host, DevToolsAgentMsg_DebuggerCommand(v8_command)); + // Do not send the response right now, as the JSON will be received from + // the V8 debugger asynchronously. + return false; +} + +// Sends the immediate "evaluate Javascript" command to the V8 debugger. +// The evaluation result is not sent back to the client as this command +// is in fact needed to invoke processing of queued debugger commands. +bool DebuggerRemoteService::DispatchEvaluateJavascript( + int tab_uid, + DictionaryValue* content, + DictionaryValue* response) { + if (tab_uid == -1) { + // Invalid tab_uid from remote debugger (perhaps NaN) + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); + return true; + } + TabContents* tab_contents = ToTabContents(tab_uid); + if (tab_contents == NULL) { + // Unknown tab_uid from remote debugger + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); + return true; + } + RenderViewHost* render_view_host = tab_contents->render_view_host(); + if (render_view_host == NULL) { + // No RenderViewHost + response->SetInteger(kResultWide, RESULT_UNKNOWN_TAB); + return true; } + std::wstring javascript; + content->GetString(kDataWide, &javascript); + render_view_host->Send( + new ViewMsg_ScriptEvalRequest(render_view_host->routing_id(), + L"", + javascript)); + return false; } diff --git a/chrome/browser/debugger/debugger_remote_service.h b/chrome/browser/debugger/debugger_remote_service.h index 7b2b683..67342f4 100644 --- a/chrome/browser/debugger/debugger_remote_service.h +++ b/chrome/browser/debugger/debugger_remote_service.h @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file declares the DebuggerRemoteServiceCommand struct and the +// DebuggerRemoteService class which handles commands directed to the +// "V8Debugger" tool. #ifndef CHROME_BROWSER_DEBUGGER_DEBUGGER_REMOTE_SERVICE_H_ #define CHROME_BROWSER_DEBUGGER_DEBUGGER_REMOTE_SERVICE_H_ @@ -9,7 +12,6 @@ #include "base/basictypes.h" #include "base/scoped_ptr.h" -#include "chrome/browser/debugger/devtools_protocol_handler.h" #include "chrome/browser/debugger/devtools_remote.h" class DevToolsProtocolHandler; @@ -19,7 +21,7 @@ class Value; class TabContents; // Contains constants for DebuggerRemoteService tool protocol commands -// (only V8-related). +// (V8-related only). struct DebuggerRemoteServiceCommand { static const std::string kAttach; static const std::string kDetach; @@ -32,43 +34,80 @@ struct DebuggerRemoteServiceCommand { // and proxies JSON messages from V8 debugger to the remote debugger. class DebuggerRemoteService : public DevToolsRemoteListener { public: + // |delegate| (never NULL) is the protocol handler instance + // which dispatches messages to this service. The responses from the + // V8 VM debugger are routed back to |delegate|. + // The ownership of |delegate| is NOT transferred to this class. explicit DebuggerRemoteService(DevToolsProtocolHandler* delegate); virtual ~DebuggerRemoteService(); // Handles a JSON message from the tab_id-associated V8 debugger. void DebuggerOutput(int32 tab_id, const std::string& message); - // Expose to public so that we can detach from tab - // on remote debugger connection loss. If |response| is not NULL, - // the operation result will be written as the "result" field in |response|, - // otherwise it will not be propagated back to the caller. - void DetachTab(const std::string& destination, - DictionaryValue* response); + // Detaches the remote debugger from the tab specified by |destination|. + // It is public so that we can detach from the tab on the remote debugger + // connection loss. + // If |response| is not NULL, the operation result will be written + // as the "result" field in |response|, otherwise the result + // will not be propagated back to the caller. + void DetachFromTab(const std::string& destination, + DictionaryValue* response); - // DevToolsRemoteListener interface + // DevToolsRemoteListener interface. + + // Processes |message| from the remote debugger, where the tool is + // "V8Debugger". Either sends the reply immediately or waits for an + // asynchronous response from the V8 debugger. virtual void HandleMessage(const DevToolsRemoteMessage& message); + + // Gets invoked on the remote debugger [socket] connection loss. + // Notifies the InspectableTabProxy of the remote debugger detachment. virtual void OnConnectionLost(); + // Specifies a tool name ("V8Debugger") handled by this class. static const std::string kToolName; private: // Operation result returned in the "result" field. - struct Result { - static const int kOk = 0; - static const int kIllegalTabState = 1; - static const int kUnknownTab = 2; - static const int kDebuggerError = 3; - static const int kUnknownCommand = 4; - }; - - void AttachTab(const std::string& destination, - DictionaryValue* response); + typedef enum { + RESULT_OK = 0, + RESULT_ILLEGAL_TAB_STATE, + RESULT_UNKNOWN_TAB, + RESULT_DEBUGGER_ERROR, + RESULT_UNKNOWN_COMMAND + } Result; + + // Attaches a remote debugger to the tab specified by |destination|. + // Writes the attachment result (one of Result enum values) into |response|. + void AttachToTab(const std::string& destination, + DictionaryValue* response); + + // Retrieves a WebContents instance for the specified |tab_uid| + // or NULL if no such tab is found or no WebContents instance + // corresponds to that tab. TabContents* ToTabContents(int32 tab_uid); + + // Sends a JSON message with the |response| to the remote debugger. + // |tool| and |destination| are used as the respective header values. void SendResponse(const Value& response, const std::string& tool, const std::string& destination); - static const std::wstring kDataWide; - static const std::wstring kResultWide; + + // Redirects a V8 debugger command from |content| to a V8 debugger associated + // with the |tab_uid| and writes the result into |response| if it becomes + // known immediately. + bool DispatchDebuggerCommand(int tab_uid, + DictionaryValue* content, + DictionaryValue* response); + + // Redirects a Javascript evaluation command from |content| to + // a V8 debugger associated with the |tab_uid| and writes the result + // into |response| if it becomes known immediately. + bool DispatchEvaluateJavascript(int tab_uid, + DictionaryValue* content, + DictionaryValue* response); + + // The delegate is used to get an InspectableTabProxy instance. DevToolsProtocolHandler* delegate_; DISALLOW_COPY_AND_ASSIGN(DebuggerRemoteService); }; diff --git a/chrome/browser/debugger/devtools_protocol_handler.cc b/chrome/browser/debugger/devtools_protocol_handler.cc index 5b7644c..39f3dea 100644 --- a/chrome/browser/debugger/devtools_protocol_handler.cc +++ b/chrome/browser/debugger/devtools_protocol_handler.cc @@ -96,7 +96,7 @@ void DevToolsProtocolHandler::DidAccept(ListenSocket *server, void DevToolsProtocolHandler::DidRead(ListenSocket *connection, const std::string& data) { - // This method is not used. + // Not used. } void DevToolsProtocolHandler::DidClose(ListenSocket *sock) { diff --git a/chrome/browser/debugger/devtools_protocol_handler.h b/chrome/browser/debugger/devtools_protocol_handler.h index 7b4c7fd..7c1d5e0 100644 --- a/chrome/browser/debugger/devtools_protocol_handler.h +++ b/chrome/browser/debugger/devtools_protocol_handler.h @@ -18,7 +18,7 @@ class DevToolsRemoteListenSocket; class DevToolsRemoteMessage; // Dispatches DevToolsRemoteMessages to their appropriate handlers (Tools) -// based on the value of the Tool message header. +// based on the "Tool" message header value. class DevToolsProtocolHandler : public DevToolsRemoteListener, public OutboundSocketDelegate, diff --git a/chrome/browser/debugger/inspectable_tab_proxy.cc b/chrome/browser/debugger/inspectable_tab_proxy.cc index 943a480..8c0caa0 100644 --- a/chrome/browser/debugger/inspectable_tab_proxy.cc +++ b/chrome/browser/debugger/inspectable_tab_proxy.cc @@ -38,7 +38,7 @@ void DevToolsClientHostImpl::OnRpcMessage(const std::string& msg) { static const std::string kDebuggerOutput = "DebuggerOutput"; scoped_ptr<Value> message(JSONReader::Read(msg, false)); if (!message->IsType(Value::TYPE_LIST)) { - NOTREACHED(); // The protocol has changed :( + NOTREACHED(); // The RPC protocol has changed :( return; } ListValue* list_msg = static_cast<ListValue*>(message.get()); @@ -84,7 +84,7 @@ DevToolsClientHost* InspectableTabProxy::NewClientHost( void InspectableTabProxy::OnRemoteDebuggerDetached() { while (id_to_client_host_map_.size() > 0) { IdToClientHostMap::iterator it = id_to_client_host_map_.begin(); - it->second->debugger_remote_service()->DetachTab(IntToString(it->first), - NULL); + it->second->debugger_remote_service()->DetachFromTab(IntToString(it->first), + NULL); } } diff --git a/chrome/browser/debugger/inspectable_tab_proxy.h b/chrome/browser/debugger/inspectable_tab_proxy.h index 23e1b11..6729cda 100644 --- a/chrome/browser/debugger/inspectable_tab_proxy.h +++ b/chrome/browser/debugger/inspectable_tab_proxy.h @@ -17,6 +17,8 @@ class DevToolsClientHostImpl; class NavigationController; // Proxies debugged tabs' NavigationControllers using their UIDs. +// Keeps track of tabs being debugged so that we can detach from +// them on remote debugger connection loss. class InspectableTabProxy { public: typedef base::hash_map<int32, NavigationController*> ControllersMap; |