1 /* 2 * Copyright (C) 2010-2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "WebDevToolsAgentImpl.h" 33 34 #include "DebuggerAgentImpl.h" 35 #include "DebuggerAgentManager.h" 36 #include "ExceptionCode.h" 37 #include "InjectedScriptHost.h" 38 #include "InspectorBackendDispatcher.h" 39 #include "InspectorController.h" 40 #include "InspectorInstrumentation.h" 41 #include "Page.h" 42 #include "PageGroup.h" 43 #include "PageScriptDebugServer.h" 44 #include "PlatformString.h" 45 #include "ResourceError.h" 46 #include "ResourceRequest.h" 47 #include "ResourceResponse.h" 48 #include "V8Binding.h" 49 #include "V8Proxy.h" 50 #include "V8Utilities.h" 51 #include "WebDataSource.h" 52 #include "WebDevToolsAgentClient.h" 53 #include "WebFrameImpl.h" 54 #include "WebRect.h" 55 #include "WebString.h" 56 #include "WebURL.h" 57 #include "WebURLError.h" 58 #include "WebURLRequest.h" 59 #include "WebURLResponse.h" 60 #include "WebViewClient.h" 61 #include "WebViewImpl.h" 62 #include <wtf/CurrentTime.h> 63 #include <wtf/Noncopyable.h> 64 #include <wtf/OwnPtr.h> 65 66 using namespace WebCore; 67 68 namespace WebKit { 69 70 namespace { 71 72 static const char kFrontendConnectedFeatureName[] = "frontend-connected"; 73 static const char kInspectorStateFeatureName[] = "inspector-state"; 74 75 class ClientMessageLoopAdapter : public PageScriptDebugServer::ClientMessageLoop { 76 public: 77 static void ensureClientMessageLoopCreated(WebDevToolsAgentClient* client) 78 { 79 if (s_instance) 80 return; 81 s_instance = new ClientMessageLoopAdapter(client->createClientMessageLoop()); 82 PageScriptDebugServer::shared().setClientMessageLoop(s_instance); 83 } 84 85 static void inspectedViewClosed(WebViewImpl* view) 86 { 87 if (s_instance) 88 s_instance->m_frozenViews.remove(view); 89 } 90 91 static void didNavigate() 92 { 93 // Release render thread if necessary. 94 if (s_instance && s_instance->m_running) 95 PageScriptDebugServer::shared().continueProgram(); 96 } 97 98 private: 99 ClientMessageLoopAdapter(PassOwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> messageLoop) 100 : m_running(false) 101 , m_messageLoop(messageLoop) { } 102 103 104 virtual void run(Page* page) 105 { 106 if (m_running) 107 return; 108 m_running = true; 109 110 Vector<WebViewImpl*> views; 111 112 // 1. Disable input events. 113 HashSet<Page*>::const_iterator end = page->group().pages().end(); 114 for (HashSet<Page*>::const_iterator it = page->group().pages().begin(); it != end; ++it) { 115 WebViewImpl* view = WebViewImpl::fromPage(*it); 116 m_frozenViews.add(view); 117 views.append(view); 118 view->setIgnoreInputEvents(true); 119 } 120 121 // 2. Disable active objects 122 WebView::willEnterModalLoop(); 123 124 // 3. Process messages until quitNow is called. 125 m_messageLoop->run(); 126 127 // 4. Resume active objects 128 WebView::didExitModalLoop(); 129 130 // 5. Resume input events. 131 for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) { 132 if (m_frozenViews.contains(*it)) { 133 // The view was not closed during the dispatch. 134 (*it)->setIgnoreInputEvents(false); 135 } 136 } 137 138 // 6. All views have been resumed, clear the set. 139 m_frozenViews.clear(); 140 141 m_running = false; 142 } 143 144 virtual void quitNow() 145 { 146 m_messageLoop->quitNow(); 147 } 148 149 bool m_running; 150 OwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> m_messageLoop; 151 typedef HashSet<WebViewImpl*> FrozenViewsSet; 152 FrozenViewsSet m_frozenViews; 153 static ClientMessageLoopAdapter* s_instance; 154 155 }; 156 157 ClientMessageLoopAdapter* ClientMessageLoopAdapter::s_instance = 0; 158 159 } // namespace 160 161 WebDevToolsAgentImpl::WebDevToolsAgentImpl( 162 WebViewImpl* webViewImpl, 163 WebDevToolsAgentClient* client) 164 : m_hostId(client->hostIdentifier()) 165 , m_client(client) 166 , m_webViewImpl(webViewImpl) 167 , m_attached(false) 168 { 169 DebuggerAgentManager::setExposeV8DebuggerProtocol( 170 client->exposeV8DebuggerProtocol()); 171 } 172 173 WebDevToolsAgentImpl::~WebDevToolsAgentImpl() 174 { 175 DebuggerAgentManager::onWebViewClosed(m_webViewImpl); 176 ClientMessageLoopAdapter::inspectedViewClosed(m_webViewImpl); 177 } 178 179 void WebDevToolsAgentImpl::attach() 180 { 181 if (m_attached) 182 return; 183 184 if (!m_client->exposeV8DebuggerProtocol()) 185 ClientMessageLoopAdapter::ensureClientMessageLoopCreated(m_client); 186 187 m_debuggerAgentImpl.set( 188 new DebuggerAgentImpl(m_webViewImpl, this, m_client)); 189 m_attached = true; 190 } 191 192 void WebDevToolsAgentImpl::detach() 193 { 194 // Prevent controller from sending messages to the frontend. 195 InspectorController* ic = inspectorController(); 196 ic->disconnectFrontend(); 197 ic->hideHighlight(); 198 ic->close(); 199 m_debuggerAgentImpl.set(0); 200 m_attached = false; 201 } 202 203 void WebDevToolsAgentImpl::frontendLoaded() 204 { 205 inspectorController()->connectFrontend(); 206 } 207 208 void WebDevToolsAgentImpl::didNavigate() 209 { 210 ClientMessageLoopAdapter::didNavigate(); 211 DebuggerAgentManager::onNavigate(); 212 } 213 214 void WebDevToolsAgentImpl::didClearWindowObject(WebFrameImpl* webframe) 215 { 216 DebuggerAgentManager::setHostId(webframe, m_hostId); 217 } 218 219 void WebDevToolsAgentImpl::dispatchOnInspectorBackend(const WebString& message) 220 { 221 inspectorController()->dispatchMessageFromFrontend(message); 222 } 223 224 void WebDevToolsAgentImpl::inspectElementAt(const WebPoint& point) 225 { 226 m_webViewImpl->inspectElementAt(point); 227 } 228 229 void WebDevToolsAgentImpl::setRuntimeProperty(const WebString& name, const WebString& value) 230 { 231 if (name == kInspectorStateFeatureName) { 232 InspectorController* ic = inspectorController(); 233 ic->restoreInspectorStateFromCookie(value); 234 } 235 } 236 237 InspectorController* WebDevToolsAgentImpl::inspectorController() 238 { 239 if (Page* page = m_webViewImpl->page()) 240 return page->inspectorController(); 241 return 0; 242 } 243 244 Frame* WebDevToolsAgentImpl::mainFrame() 245 { 246 if (Page* page = m_webViewImpl->page()) 247 return page->mainFrame(); 248 return 0; 249 } 250 251 void WebDevToolsAgentImpl::inspectorDestroyed() 252 { 253 // Our lifetime is bound to the WebViewImpl. 254 } 255 256 void WebDevToolsAgentImpl::openInspectorFrontend(InspectorController*) 257 { 258 } 259 260 void WebDevToolsAgentImpl::highlight(Node* node) 261 { 262 // InspectorController does the actuall tracking of the highlighted node 263 // and the drawing of the highlight. Here we just make sure to invalidate 264 // the rects of the old and new nodes. 265 hideHighlight(); 266 } 267 268 void WebDevToolsAgentImpl::hideHighlight() 269 { 270 // FIXME: able to invalidate a smaller rect. 271 // FIXME: Is it important to just invalidate the rect of the node region 272 // given that this is not on a critical codepath? In order to do so, we'd 273 // have to take scrolling into account. 274 const WebSize& size = m_webViewImpl->size(); 275 WebRect damagedRect(0, 0, size.width, size.height); 276 if (m_webViewImpl->client()) 277 m_webViewImpl->client()->didInvalidateRect(damagedRect); 278 } 279 280 bool WebDevToolsAgentImpl::sendMessageToFrontend(const String& message) 281 { 282 WebDevToolsAgentImpl* devToolsAgent = static_cast<WebDevToolsAgentImpl*>(m_webViewImpl->devToolsAgent()); 283 if (!devToolsAgent) 284 return false; 285 286 m_client->sendMessageToInspectorFrontend(message); 287 return true; 288 } 289 290 void WebDevToolsAgentImpl::updateInspectorStateCookie(const String& state) 291 { 292 m_client->runtimePropertyChanged(kInspectorStateFeatureName, state); 293 } 294 295 void WebDevToolsAgentImpl::evaluateInWebInspector(long callId, const WebString& script) 296 { 297 InspectorController* ic = inspectorController(); 298 ic->evaluateForTestInFrontend(callId, script); 299 } 300 301 void WebDevToolsAgentImpl::setTimelineProfilingEnabled(bool enabled) 302 { 303 InspectorController* ic = inspectorController(); 304 if (enabled) 305 ic->startTimelineProfiler(); 306 else 307 ic->stopTimelineProfiler(); 308 } 309 310 void WebDevToolsAgent::executeDebuggerCommand(const WebString& command, int callerId) 311 { 312 DebuggerAgentManager::executeDebuggerCommand(command, callerId); 313 } 314 315 void WebDevToolsAgent::debuggerPauseScript() 316 { 317 DebuggerAgentManager::pauseScript(); 318 } 319 320 void WebDevToolsAgent::interruptAndDispatch(MessageDescriptor* d) 321 { 322 class DebuggerTask : public PageScriptDebugServer::Task { 323 public: 324 DebuggerTask(WebDevToolsAgent::MessageDescriptor* descriptor) : m_descriptor(descriptor) { } 325 virtual ~DebuggerTask() { } 326 virtual void run() 327 { 328 if (WebDevToolsAgent* webagent = m_descriptor->agent()) 329 webagent->dispatchOnInspectorBackend(m_descriptor->message()); 330 } 331 private: 332 OwnPtr<WebDevToolsAgent::MessageDescriptor> m_descriptor; 333 }; 334 PageScriptDebugServer::interruptAndRun(new DebuggerTask(d)); 335 } 336 337 bool WebDevToolsAgent::shouldInterruptForMessage(const WebString& message) 338 { 339 String commandName; 340 if (!InspectorBackendDispatcher::getCommandName(message, &commandName)) 341 return false; 342 return commandName == InspectorBackendDispatcher::Debugger_pauseCmd 343 || commandName == InspectorBackendDispatcher::Debugger_setBreakpointCmd 344 || commandName == InspectorBackendDispatcher::Debugger_setBreakpointByUrlCmd 345 || commandName == InspectorBackendDispatcher::Debugger_removeBreakpointCmd 346 || commandName == InspectorBackendDispatcher::Debugger_setBreakpointsActiveCmd 347 || commandName == InspectorBackendDispatcher::Profiler_startCmd 348 || commandName == InspectorBackendDispatcher::Profiler_stopCmd 349 || commandName == InspectorBackendDispatcher::Profiler_getProfileCmd; 350 } 351 352 void WebDevToolsAgent::processPendingMessages() 353 { 354 PageScriptDebugServer::shared().runPendingTasks(); 355 } 356 357 void WebDevToolsAgent::setMessageLoopDispatchHandler(MessageLoopDispatchHandler handler) 358 { 359 DebuggerAgentManager::setMessageLoopDispatchHandler(handler); 360 } 361 362 } // namespace WebKit 363