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. 4 5 #include "content/renderer/devtools/devtools_agent.h" 6 7 #include <map> 8 9 #include "base/debug/trace_event.h" 10 #include "base/lazy_instance.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/process/process.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "content/common/devtools_messages.h" 15 #include "content/common/view_messages.h" 16 #include "content/renderer/devtools/devtools_agent_filter.h" 17 #include "content/renderer/devtools/devtools_client.h" 18 #include "content/renderer/render_view_impl.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/WebConsoleMessage.h" 23 #include "third_party/WebKit/public/web/WebDevToolsAgent.h" 24 #include "third_party/WebKit/public/web/WebFrame.h" 25 #include "third_party/WebKit/public/web/WebView.h" 26 27 #if defined(USE_TCMALLOC) 28 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" 29 #endif 30 31 using WebKit::WebConsoleMessage; 32 using WebKit::WebDevToolsAgent; 33 using WebKit::WebDevToolsAgentClient; 34 using WebKit::WebFrame; 35 using WebKit::WebPoint; 36 using WebKit::WebString; 37 using WebKit::WebCString; 38 using WebKit::WebVector; 39 using WebKit::WebView; 40 41 using base::debug::TraceLog; 42 43 namespace content { 44 45 namespace { 46 47 class WebKitClientMessageLoopImpl 48 : public WebDevToolsAgentClient::WebKitClientMessageLoop { 49 public: 50 WebKitClientMessageLoopImpl() : message_loop_(base::MessageLoop::current()) {} 51 virtual ~WebKitClientMessageLoopImpl() { message_loop_ = NULL; } 52 virtual void run() { 53 base::MessageLoop::ScopedNestableTaskAllower allow(message_loop_); 54 message_loop_->Run(); 55 } 56 virtual void quitNow() { 57 message_loop_->QuitNow(); 58 } 59 private: 60 base::MessageLoop* message_loop_; 61 }; 62 63 typedef std::map<int, DevToolsAgent*> IdToAgentMap; 64 base::LazyInstance<IdToAgentMap>::Leaky 65 g_agent_for_routing_id = LAZY_INSTANCE_INITIALIZER; 66 67 } // namespace 68 69 DevToolsAgent::DevToolsAgent(RenderViewImpl* render_view) 70 : RenderViewObserver(render_view), 71 is_attached_(false), 72 is_devtools_client_(false) { 73 g_agent_for_routing_id.Get()[routing_id()] = this; 74 75 render_view->webview()->setDevToolsAgentClient(this); 76 render_view->webview()->devToolsAgent()->setProcessId( 77 base::Process::Current().pid()); 78 } 79 80 DevToolsAgent::~DevToolsAgent() { 81 g_agent_for_routing_id.Get().erase(routing_id()); 82 } 83 84 // Called on the Renderer thread. 85 bool DevToolsAgent::OnMessageReceived(const IPC::Message& message) { 86 bool handled = true; 87 IPC_BEGIN_MESSAGE_MAP(DevToolsAgent, message) 88 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Attach, OnAttach) 89 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Reattach, OnReattach) 90 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_Detach, OnDetach) 91 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_DispatchOnInspectorBackend, 92 OnDispatchOnInspectorBackend) 93 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_InspectElement, OnInspectElement) 94 IPC_MESSAGE_HANDLER(DevToolsAgentMsg_AddMessageToConsole, 95 OnAddMessageToConsole) 96 IPC_MESSAGE_HANDLER(DevToolsMsg_SetupDevToolsClient, OnSetupDevToolsClient) 97 IPC_MESSAGE_UNHANDLED(handled = false) 98 IPC_END_MESSAGE_MAP() 99 100 if (message.type() == ViewMsg_Navigate::ID || 101 message.type() == ViewMsg_Close::ID) 102 ContinueProgram(); // Don't want to swallow the message. 103 104 return handled; 105 } 106 107 void DevToolsAgent::sendMessageToInspectorFrontend( 108 const WebKit::WebString& message) { 109 Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(routing_id(), 110 message.utf8())); 111 } 112 113 int DevToolsAgent::hostIdentifier() { 114 return routing_id(); 115 } 116 117 void DevToolsAgent::saveAgentRuntimeState( 118 const WebKit::WebString& state) { 119 Send(new DevToolsHostMsg_SaveAgentRuntimeState(routing_id(), state.utf8())); 120 } 121 122 WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop* 123 DevToolsAgent::createClientMessageLoop() { 124 return new WebKitClientMessageLoopImpl(); 125 } 126 127 void DevToolsAgent::clearBrowserCache() { 128 Send(new DevToolsHostMsg_ClearBrowserCache(routing_id())); 129 } 130 131 void DevToolsAgent::clearBrowserCookies() { 132 Send(new DevToolsHostMsg_ClearBrowserCookies(routing_id())); 133 } 134 135 void DevToolsAgent::setTraceEventCallback(TraceEventCallback cb) { 136 TraceLog* trace_log = TraceLog::GetInstance(); 137 trace_log->SetEventCallback(cb); 138 if (!!cb) { 139 trace_log->SetEnabled(base::debug::CategoryFilter( 140 base::debug::CategoryFilter::kDefaultCategoryFilterString), 141 TraceLog::RECORD_UNTIL_FULL); 142 } else { 143 trace_log->SetDisabled(); 144 } 145 } 146 147 #if defined(USE_TCMALLOC) && !defined(OS_WIN) 148 static void AllocationVisitor(void* data, const void* ptr) { 149 typedef WebKit::WebDevToolsAgentClient::AllocatedObjectVisitor Visitor; 150 Visitor* visitor = reinterpret_cast<Visitor*>(data); 151 visitor->visitObject(ptr); 152 } 153 #endif 154 155 void DevToolsAgent::visitAllocatedObjects(AllocatedObjectVisitor* visitor) { 156 #if defined(USE_TCMALLOC) && !defined(OS_WIN) 157 IterateAllocatedObjects(&AllocationVisitor, visitor); 158 #endif 159 } 160 161 // static 162 DevToolsAgent* DevToolsAgent::FromHostId(int host_id) { 163 IdToAgentMap::iterator it = g_agent_for_routing_id.Get().find(host_id); 164 if (it != g_agent_for_routing_id.Get().end()) { 165 return it->second; 166 } 167 return NULL; 168 } 169 170 void DevToolsAgent::OnAttach() { 171 WebDevToolsAgent* web_agent = GetWebAgent(); 172 if (web_agent) { 173 web_agent->attach(); 174 is_attached_ = true; 175 } 176 } 177 178 void DevToolsAgent::OnReattach(const std::string& agent_state) { 179 WebDevToolsAgent* web_agent = GetWebAgent(); 180 if (web_agent) { 181 web_agent->reattach(WebString::fromUTF8(agent_state)); 182 is_attached_ = true; 183 } 184 } 185 186 void DevToolsAgent::OnDetach() { 187 WebDevToolsAgent* web_agent = GetWebAgent(); 188 if (web_agent) { 189 web_agent->detach(); 190 is_attached_ = false; 191 } 192 } 193 194 void DevToolsAgent::OnDispatchOnInspectorBackend(const std::string& message) { 195 WebDevToolsAgent* web_agent = GetWebAgent(); 196 if (web_agent) 197 web_agent->dispatchOnInspectorBackend(WebString::fromUTF8(message)); 198 } 199 200 void DevToolsAgent::OnInspectElement(int x, int y) { 201 WebDevToolsAgent* web_agent = GetWebAgent(); 202 if (web_agent) { 203 web_agent->attach(); 204 web_agent->inspectElementAt(WebPoint(x, y)); 205 } 206 } 207 208 void DevToolsAgent::OnAddMessageToConsole(ConsoleMessageLevel level, 209 const std::string& message) { 210 WebView* web_view = render_view()->GetWebView(); 211 if (!web_view) 212 return; 213 214 WebFrame* main_frame = web_view->mainFrame(); 215 if (!main_frame) 216 return; 217 218 WebConsoleMessage::Level target_level = WebConsoleMessage::LevelLog; 219 switch (level) { 220 case CONSOLE_MESSAGE_LEVEL_DEBUG: 221 target_level = WebConsoleMessage::LevelDebug; 222 break; 223 case CONSOLE_MESSAGE_LEVEL_LOG: 224 target_level = WebConsoleMessage::LevelLog; 225 break; 226 case CONSOLE_MESSAGE_LEVEL_WARNING: 227 target_level = WebConsoleMessage::LevelWarning; 228 break; 229 case CONSOLE_MESSAGE_LEVEL_ERROR: 230 target_level = WebConsoleMessage::LevelError; 231 break; 232 } 233 main_frame->addMessageToConsole( 234 WebConsoleMessage(target_level, WebString::fromUTF8(message))); 235 } 236 237 void DevToolsAgent::ContinueProgram() { 238 WebDevToolsAgent* web_agent = GetWebAgent(); 239 // TODO(pfeldman): rename didNavigate to continueProgram upstream. 240 // That is in fact the purpose of the signal. 241 if (web_agent) 242 web_agent->didNavigate(); 243 } 244 245 void DevToolsAgent::OnSetupDevToolsClient() { 246 // We only want to register once per render view. 247 if (is_devtools_client_) 248 return; 249 is_devtools_client_ = true; 250 new DevToolsClient(static_cast<RenderViewImpl*>(render_view())); 251 } 252 253 WebDevToolsAgent* DevToolsAgent::GetWebAgent() { 254 WebView* web_view = render_view()->GetWebView(); 255 if (!web_view) 256 return NULL; 257 return web_view->devToolsAgent(); 258 } 259 260 bool DevToolsAgent::IsAttached() { 261 return is_attached_; 262 } 263 264 } // namespace content 265