diff options
author | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-20 11:38:03 +0000 |
---|---|---|
committer | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-20 11:38:03 +0000 |
commit | 0639cfd4ed40aa9450c5781c86d6b47af8396220 (patch) | |
tree | 486f82e04cd53623ffb67ae7932523bde70b118f /chrome/browser/debugger/debugger_remote_service.cc | |
parent | d641b6e4c2ebf556cb42818e7b3470329baf170c (diff) | |
download | chromium_src-0639cfd4ed40aa9450c5781c86d6b47af8396220.zip chromium_src-0639cfd4ed40aa9450c5781c86d6b47af8396220.tar.gz chromium_src-0639cfd4ed40aa9450c5781c86d6b47af8396220.tar.bz2 |
DevTools: bulk move of legacy protocol back to chrome/.
Review URL: http://codereview.chromium.org/7458015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93182 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/debugger/debugger_remote_service.cc')
-rw-r--r-- | chrome/browser/debugger/debugger_remote_service.cc | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/chrome/browser/debugger/debugger_remote_service.cc b/chrome/browser/debugger/debugger_remote_service.cc new file mode 100644 index 0000000..a93084f --- /dev/null +++ b/chrome/browser/debugger/debugger_remote_service.cc @@ -0,0 +1,337 @@ +// Copyright (c) 2011 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. + +// This file contains implementations of the DebuggerRemoteService methods, +// defines DebuggerRemoteService and DebuggerRemoteServiceCommand constants. + +#include "chrome/browser/debugger/debugger_remote_service.h" + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/stringprintf.h" +#include "base/string_number_conversions.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/debugger/devtools_protocol_handler.h" +#include "chrome/browser/debugger/devtools_remote_message.h" +#include "chrome/browser/debugger/inspectable_tab_proxy.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/common/render_messages.h" +#include "content/browser/debugger/devtools_manager.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "content/common/devtools_messages.h" + +namespace { + +// Constants for the "data", "result", and "command" JSON message fields. +const char kDataKey[] = "data"; +const char kResultKey[] = "result"; +const char kCommandKey[] = "command"; + +} // namespace + +const std::string DebuggerRemoteServiceCommand::kAttach = "attach"; +const std::string DebuggerRemoteServiceCommand::kDetach = "detach"; +const std::string DebuggerRemoteServiceCommand::kDebuggerCommand = + "debugger_command"; +const std::string DebuggerRemoteServiceCommand::kEvaluateJavascript = + "evaluate_javascript"; +const std::string DebuggerRemoteServiceCommand::kFrameNavigate = + "navigated"; +const std::string DebuggerRemoteServiceCommand::kTabClosed = + "closed"; + +const std::string DebuggerRemoteService::kToolName = "V8Debugger"; + +DebuggerRemoteService::DebuggerRemoteService(DevToolsProtocolHandler* delegate) + : delegate_(delegate) {} + +DebuggerRemoteService::~DebuggerRemoteService() {} + +// 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) { + const std::string destination = message.destination(); + scoped_ptr<Value> request(base::JSONReader::Read(message.content(), true)); + if (request.get() == NULL) { + // Bad JSON + NOTREACHED(); + return; + } + DictionaryValue* content; + if (!request->IsType(Value::TYPE_DICTIONARY)) { + NOTREACHED(); // Broken protocol :( + return; + } + content = static_cast<DictionaryValue*>(request.get()); + if (!content->HasKey(kCommandKey)) { + NOTREACHED(); // Broken protocol :( + return; + } + std::string command; + DictionaryValue response; + + content->GetString(kCommandKey, &command); + response.SetString(kCommandKey, command); + bool send_response = true; + if (destination.empty()) { + // Unknown command (bad format?) + NOTREACHED(); + response.SetInteger(kResultKey, RESULT_UNKNOWN_COMMAND); + SendResponse(response, message.tool(), message.destination()); + return; + } + int32 tab_uid = -1; + base::StringToInt(destination, &tab_uid); + + if (command == DebuggerRemoteServiceCommand::kAttach) { + // TODO(apavlov): handle 0 for a new tab + response.SetString(kCommandKey, DebuggerRemoteServiceCommand::kAttach); + AttachToTab(destination, &response); + } else if (command == DebuggerRemoteServiceCommand::kDetach) { + response.SetString(kCommandKey, DebuggerRemoteServiceCommand::kDetach); + DetachFromTab(destination, &response); + } else if (command == DebuggerRemoteServiceCommand::kDebuggerCommand) { + send_response = DispatchDebuggerCommand(tab_uid, content, &response); + } else if (command == DebuggerRemoteServiceCommand::kEvaluateJavascript) { + send_response = DispatchEvaluateJavascript(tab_uid, content, &response); + } else { + // Unknown command + NOTREACHED(); + response.SetInteger(kResultKey, RESULT_UNKNOWN_COMMAND); + } + + if (send_response) { + SendResponse(response, message.tool(), message.destination()); + } +} + +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) { + std::string response_content; + base::JSONWriter::Write(&response, false, &response_content); + scoped_ptr<DevToolsRemoteMessage> response_message( + DevToolsRemoteMessageBuilder::instance().Create(tool, + destination, + response_content)); + 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::TabMap& tab_map = + delegate_->inspectable_tab_proxy()->tab_map(); + InspectableTabProxy::TabMap::const_iterator it = tab_map.find(tab_uid); + if (it != tab_map.end()) { + TabContents* tab_contents = it->second->tab_contents(); + if (tab_contents == NULL) { + return NULL; + } else { + return tab_contents; + } + } else { + return NULL; + } +} + +// 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_uid, + const std::string& message) { + std::string content = StringPrintf( + "{\"command\":\"%s\",\"result\":%s,\"data\":%s}", + DebuggerRemoteServiceCommand::kDebuggerCommand.c_str(), + base::IntToString(RESULT_OK).c_str(), + message.c_str()); + scoped_ptr<DevToolsRemoteMessage> response_message( + DevToolsRemoteMessageBuilder::instance().Create( + kToolName, + base::IntToString(tab_uid), + content)); + delegate_->Send(*(response_message.get())); +} + +// Gets invoked from a DevToolsClientHost callback whenever +// a tab corresponding to |tab_id| changes its URL. |url| is the new +// URL of the tab (may be the same as the previous one if the tab is reloaded). +// Sends the corresponding message to the remote debugger. +void DebuggerRemoteService::FrameNavigate(int32 tab_uid, + const std::string& url) { + DictionaryValue value; + value.SetString(kCommandKey, DebuggerRemoteServiceCommand::kFrameNavigate); + value.SetInteger(kResultKey, RESULT_OK); + value.SetString(kDataKey, url); + SendResponse(value, kToolName, base::IntToString(tab_uid)); +} + +// Gets invoked from a DevToolsClientHost callback whenever +// a tab corresponding to |tab_id| gets closed. +// Sends the corresponding message to the remote debugger. +void DebuggerRemoteService::TabClosed(int32 tab_id) { + DictionaryValue value; + value.SetString(kCommandKey, DebuggerRemoteServiceCommand::kTabClosed); + value.SetInteger(kResultKey, RESULT_OK); + SendResponse(value, kToolName, base::IntToString(tab_id)); +} + +// 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; + base::StringToInt(destination, &tab_uid); + if (tab_uid < 0) { + // Bad tab_uid received from remote debugger (perhaps NaN) + response->SetInteger(kResultKey, 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(kResultKey, RESULT_UNKNOWN_TAB); + return; + } + TabContents* tab_contents = ToTabContents(tab_uid); + if (tab_contents == NULL) { + // No active tab contents with tab_uid + response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB); + return; + } + RenderViewHost* target_host = tab_contents->render_view_host(); + DevToolsClientHost* client_host = + delegate_->inspectable_tab_proxy()->ClientHostForTabId(tab_uid); + if (client_host == NULL) { + client_host = + delegate_->inspectable_tab_proxy()->NewClientHost(tab_uid, this); + DevToolsManager* manager = DevToolsManager::GetInstance(); + if (manager != NULL) { + manager->RegisterDevToolsClientHostFor(target_host, client_host); + response->SetInteger(kResultKey, RESULT_OK); + } else { + response->SetInteger(kResultKey, RESULT_DEBUGGER_ERROR); + } + } else { + // DevToolsClientHost for this tab is already registered + response->SetInteger(kResultKey, RESULT_ILLEGAL_TAB_STATE); + } +} + +// 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; + base::StringToInt(destination, &tab_uid); + if (tab_uid == -1) { + // Bad tab_uid received from remote debugger (NaN) + if (response != NULL) { + response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB); + } + return; + } + int result_code; + DevToolsClientHostImpl* client_host = + delegate_->inspectable_tab_proxy()->ClientHostForTabId(tab_uid); + if (client_host != NULL) { + client_host->CloseImpl(); + result_code = RESULT_OK; + } else { + // No client host registered for |tab_uid|. + result_code = RESULT_UNKNOWN_TAB; + } + if (response != NULL) { + response->SetInteger(kResultKey, 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(kResultKey, RESULT_UNKNOWN_TAB); + return true; + } + DevToolsManager* manager = DevToolsManager::GetInstance(); + if (manager == NULL) { + response->SetInteger(kResultKey, RESULT_DEBUGGER_ERROR); + return true; + } + TabContents* tab_contents = ToTabContents(tab_uid); + if (tab_contents == NULL) { + // Unknown tab_uid from remote debugger + response->SetInteger(kResultKey, 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(kResultKey, RESULT_ILLEGAL_TAB_STATE); + return true; + } + std::string v8_command; + DictionaryValue* v8_command_value; + content->GetDictionary(kDataKey, &v8_command_value); + base::JSONWriter::Write(v8_command_value, false, &v8_command); + manager->ForwardToDevToolsAgent( + client_host, DevToolsAgentMsg_DebuggerCommand(MSG_ROUTING_NONE, + 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(kResultKey, RESULT_UNKNOWN_TAB); + return true; + } + TabContents* tab_contents = ToTabContents(tab_uid); + if (tab_contents == NULL) { + // Unknown tab_uid from remote debugger + response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB); + return true; + } + RenderViewHost* render_view_host = tab_contents->render_view_host(); + if (render_view_host == NULL) { + // No RenderViewHost + response->SetInteger(kResultKey, RESULT_UNKNOWN_TAB); + return true; + } + std::string javascript; + content->GetString(kDataKey, &javascript); + render_view_host->ExecuteJavascriptInWebFrame(string16(), + UTF8ToUTF16(javascript)); + return false; +} |