2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "WebDevToolsAgentImpl.h"
34 #include "DebuggerAgentImpl.h"
35 #include "DebuggerAgentManager.h"
36 #include "InjectedScriptHost.h"
37 #include "InspectorBackendDispatcher.h"
38 #include "InspectorController.h"
40 #include "PageGroup.h"
41 #include "PlatformString.h"
42 #include "ResourceError.h"
43 #include "ResourceRequest.h"
44 #include "ResourceResponse.h"
45 #include "ScriptDebugServer.h"
46 #include "V8Binding.h"
48 #include "V8Utilities.h"
49 #include "WebDataSource.h"
50 #include "WebDevToolsAgentClient.h"
51 #include "WebFrameImpl.h"
53 #include "WebString.h"
55 #include "WebURLError.h"
56 #include "WebURLRequest.h"
57 #include "WebURLResponse.h"
58 #include "WebViewClient.h"
59 #include "WebViewImpl.h"
60 #include <wtf/CurrentTime.h>
61 #include <wtf/Noncopyable.h>
62 #include <wtf/OwnPtr.h>
64 using WebCore::DocumentLoader;
65 using WebCore::FrameLoader;
66 using WebCore::InjectedScriptHost;
67 using WebCore::InspectorArray;
68 using WebCore::InspectorBackendDispatcher;
69 using WebCore::InspectorController;
72 using WebCore::ResourceError;
73 using WebCore::ResourceRequest;
74 using WebCore::ResourceResponse;
76 using WebCore::V8DOMWrapper;
77 using WebCore::V8Proxy;
83 static const char kApuAgentFeatureName[] = "apu-agent";
84 static const char kFrontendConnectedFeatureName[] = "frontend-connected";
85 static const char kInspectorStateFeatureName[] = "inspector-state";
87 class ClientMessageLoopAdapter : public WebCore::ScriptDebugServer::ClientMessageLoop {
89 static void ensureClientMessageLoopCreated(WebDevToolsAgentClient* client)
93 s_instance = new ClientMessageLoopAdapter(client->createClientMessageLoop());
94 WebCore::ScriptDebugServer::shared().setClientMessageLoop(s_instance);
97 static void inspectedViewClosed(WebViewImpl* view)
100 s_instance->m_frozenViews.remove(view);
103 static void didNavigate()
105 // Release render thread if necessary.
106 if (s_instance && s_instance->m_running)
107 WebCore::ScriptDebugServer::shared().continueProgram();
111 ClientMessageLoopAdapter(PassOwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> messageLoop)
113 , m_messageLoop(messageLoop) { }
116 virtual void run(Page* page)
122 Vector<WebViewImpl*> views;
124 // 1. Disable input events.
125 HashSet<Page*>::const_iterator end = page->group().pages().end();
126 for (HashSet<Page*>::const_iterator it = page->group().pages().begin(); it != end; ++it) {
127 WebViewImpl* view = WebViewImpl::fromPage(*it);
128 m_frozenViews.add(view);
130 view->setIgnoreInputEvents(true);
133 // 2. Disable active objects
134 WebView::willEnterModalLoop();
136 // 3. Process messages until quitNow is called.
137 m_messageLoop->run();
139 // 4. Resume active objects
140 WebView::didExitModalLoop();
142 // 5. Resume input events.
143 for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) {
144 if (m_frozenViews.contains(*it)) {
145 // The view was not closed during the dispatch.
146 (*it)->setIgnoreInputEvents(false);
150 // 6. All views have been resumed, clear the set.
151 m_frozenViews.clear();
156 virtual void quitNow()
158 m_messageLoop->quitNow();
162 OwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> m_messageLoop;
163 typedef HashSet<WebViewImpl*> FrozenViewsSet;
164 FrozenViewsSet m_frozenViews;
165 static ClientMessageLoopAdapter* s_instance;
169 ClientMessageLoopAdapter* ClientMessageLoopAdapter::s_instance = 0;
173 WebDevToolsAgentImpl::WebDevToolsAgentImpl(
174 WebViewImpl* webViewImpl,
175 WebDevToolsAgentClient* client)
176 : m_hostId(client->hostIdentifier())
178 , m_webViewImpl(webViewImpl)
179 , m_apuAgentEnabled(false)
182 DebuggerAgentManager::setExposeV8DebuggerProtocol(
183 client->exposeV8DebuggerProtocol());
186 WebDevToolsAgentImpl::~WebDevToolsAgentImpl()
188 DebuggerAgentManager::onWebViewClosed(m_webViewImpl);
189 ClientMessageLoopAdapter::inspectedViewClosed(m_webViewImpl);
192 void WebDevToolsAgentImpl::attach()
197 if (!m_client->exposeV8DebuggerProtocol())
198 ClientMessageLoopAdapter::ensureClientMessageLoopCreated(m_client);
200 m_debuggerAgentImpl.set(
201 new DebuggerAgentImpl(m_webViewImpl, this, m_client));
202 WebCString debuggerScriptJs = m_client->debuggerScriptSource();
203 WebCore::ScriptDebugServer::shared().setDebuggerScriptSource(
204 WTF::String(debuggerScriptJs.data(), debuggerScriptJs.length()));
208 void WebDevToolsAgentImpl::detach()
210 // Prevent controller from sending messages to the frontend.
211 InspectorController* ic = inspectorController();
212 ic->disconnectFrontend();
215 m_debuggerAgentImpl.set(0);
217 m_apuAgentEnabled = false;
220 void WebDevToolsAgentImpl::frontendLoaded()
222 connectFrontend(false);
225 void WebDevToolsAgentImpl::didNavigate()
227 ClientMessageLoopAdapter::didNavigate();
228 DebuggerAgentManager::onNavigate();
231 void WebDevToolsAgentImpl::didClearWindowObject(WebFrameImpl* webframe)
233 DebuggerAgentManager::setHostId(webframe, m_hostId);
236 void WebDevToolsAgentImpl::dispatchOnInspectorBackend(const WebString& message)
238 inspectorController()->inspectorBackendDispatcher()->dispatch(message);
241 void WebDevToolsAgentImpl::inspectElementAt(const WebPoint& point)
243 m_webViewImpl->inspectElementAt(point);
246 void WebDevToolsAgentImpl::setRuntimeProperty(const WebString& name, const WebString& value)
248 if (name == kApuAgentFeatureName)
249 setApuAgentEnabled(value == "true");
250 else if (name == kInspectorStateFeatureName) {
251 InspectorController* ic = inspectorController();
252 ic->restoreInspectorStateFromCookie(value);
253 } else if (name == kFrontendConnectedFeatureName && !inspectorController()->hasFrontend()) {
254 inspectorController()->injectedScriptHost()->setInjectedScriptSource(value);
255 connectFrontend(true);
259 void WebDevToolsAgentImpl::setApuAgentEnabled(bool enabled)
261 m_apuAgentEnabled = enabled;
262 InspectorController* ic = inspectorController();
264 if (!ic->hasFrontend())
265 connectFrontend(true);
267 ic->startTimelineProfiler();
268 m_debuggerAgentImpl->setAutoContinueOnException(true);
270 ic->stopTimelineProfiler();
272 m_client->runtimePropertyChanged(
273 kApuAgentFeatureName,
274 enabled ? String("true") : String("false"));
277 void WebDevToolsAgentImpl::connectFrontend(bool afterNavigation)
280 inspectorController()->reuseFrontend();
282 inspectorController()->connectFrontend();
283 // We know that by this time injected script has already been pushed to the backend.
284 m_client->runtimePropertyChanged(kFrontendConnectedFeatureName, inspectorController()->injectedScriptHost()->injectedScriptSource());
287 WebCore::InspectorController* WebDevToolsAgentImpl::inspectorController()
289 if (Page* page = m_webViewImpl->page())
290 return page->inspectorController();
295 //------- plugin resource load notifications ---------------
296 void WebDevToolsAgentImpl::identifierForInitialRequest(
297 unsigned long resourceId,
299 const WebURLRequest& request)
301 if (InspectorController* ic = inspectorController()) {
302 WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(frame);
303 FrameLoader* frameLoader = webFrameImpl->frame()->loader();
304 DocumentLoader* loader = frameLoader->activeDocumentLoader();
305 ic->identifierForInitialRequest(resourceId, loader, request.toResourceRequest());
309 void WebDevToolsAgentImpl::willSendRequest(unsigned long resourceId, WebURLRequest& request)
311 if (InspectorController* ic = inspectorController()) {
312 ic->willSendRequest(resourceId, request.toMutableResourceRequest(), ResourceResponse());
313 if (ic->hasFrontend() && request.reportLoadTiming())
314 request.setReportRawHeaders(true);
318 void WebDevToolsAgentImpl::didReceiveData(unsigned long resourceId, int length)
320 if (InspectorController* ic = inspectorController())
321 ic->didReceiveContentLength(resourceId, length);
324 void WebDevToolsAgentImpl::didReceiveResponse(unsigned long resourceId, const WebURLResponse& response)
326 if (InspectorController* ic = inspectorController())
327 ic->didReceiveResponse(resourceId, 0, response.toResourceResponse());
330 void WebDevToolsAgentImpl::didFinishLoading(unsigned long resourceId)
332 if (InspectorController* ic = inspectorController())
333 ic->didFinishLoading(resourceId, 0);
336 void WebDevToolsAgentImpl::didFailLoading(unsigned long resourceId, const WebURLError& error)
338 ResourceError resourceError;
339 if (InspectorController* ic = inspectorController())
340 ic->didFailLoading(resourceId, resourceError);
343 void WebDevToolsAgentImpl::inspectorDestroyed()
345 // Our lifetime is bound to the WebViewImpl.
348 void WebDevToolsAgentImpl::openInspectorFrontend(InspectorController*)
352 void WebDevToolsAgentImpl::highlight(Node* node)
354 // InspectorController does the actuall tracking of the highlighted node
355 // and the drawing of the highlight. Here we just make sure to invalidate
356 // the rects of the old and new nodes.
360 void WebDevToolsAgentImpl::hideHighlight()
362 // FIXME: able to invalidate a smaller rect.
363 // FIXME: Is it important to just invalidate the rect of the node region
364 // given that this is not on a critical codepath? In order to do so, we'd
365 // have to take scrolling into account.
366 const WebSize& size = m_webViewImpl->size();
367 WebRect damagedRect(0, 0, size.width, size.height);
368 if (m_webViewImpl->client())
369 m_webViewImpl->client()->didInvalidateRect(damagedRect);
372 void WebDevToolsAgentImpl::populateSetting(const String& key, String* value)
375 m_webViewImpl->inspectorSetting(key, &string);
379 void WebDevToolsAgentImpl::storeSetting(const String& key, const String& value)
381 m_webViewImpl->setInspectorSetting(key, value);
384 bool WebDevToolsAgentImpl::sendMessageToFrontend(const WTF::String& message)
386 WebDevToolsAgentImpl* devToolsAgent = static_cast<WebDevToolsAgentImpl*>(m_webViewImpl->devToolsAgent());
390 if (devToolsAgent->m_apuAgentEnabled) {
391 m_client->sendDispatchToAPU(message);
395 m_client->sendMessageToInspectorFrontend(message);
399 void WebDevToolsAgentImpl::updateInspectorStateCookie(const WTF::String& state)
401 m_client->runtimePropertyChanged(kInspectorStateFeatureName, state);
404 void WebDevToolsAgentImpl::evaluateInWebInspector(long callId, const WebString& script)
406 InspectorController* ic = inspectorController();
407 ic->evaluateForTestInFrontend(callId, script);
410 void WebDevToolsAgentImpl::setTimelineProfilingEnabled(bool enabled)
412 InspectorController* ic = inspectorController();
414 ic->startTimelineProfiler();
416 ic->stopTimelineProfiler();
419 void WebDevToolsAgent::executeDebuggerCommand(const WebString& command, int callerId)
421 DebuggerAgentManager::executeDebuggerCommand(command, callerId);
424 void WebDevToolsAgent::debuggerPauseScript()
426 DebuggerAgentManager::pauseScript();
429 void WebDevToolsAgent::interruptAndDispatch(MessageDescriptor* d)
431 class DebuggerTask : public WebCore::ScriptDebugServer::Task {
433 DebuggerTask(WebDevToolsAgent::MessageDescriptor* descriptor) : m_descriptor(descriptor) { }
434 virtual ~DebuggerTask() { }
437 if (WebDevToolsAgent* webagent = m_descriptor->agent())
438 webagent->dispatchOnInspectorBackend(m_descriptor->message());
441 OwnPtr<WebDevToolsAgent::MessageDescriptor> m_descriptor;
443 WebCore::ScriptDebugServer::interruptAndRun(new DebuggerTask(d));
446 bool WebDevToolsAgent::shouldInterruptForMessage(const WebString& message)
449 if (!InspectorBackendDispatcher::getCommandName(message, &commandName))
451 return commandName == InspectorBackendDispatcher::pauseCmd
452 || commandName == InspectorBackendDispatcher::setBreakpointCmd
453 || commandName == InspectorBackendDispatcher::removeBreakpointCmd
454 || commandName == InspectorBackendDispatcher::activateBreakpointsCmd
455 || commandName == InspectorBackendDispatcher::deactivateBreakpointsCmd
456 || commandName == InspectorBackendDispatcher::startProfilingCmd
457 || commandName == InspectorBackendDispatcher::stopProfilingCmd
458 || commandName == InspectorBackendDispatcher::getProfileCmd;
461 void WebDevToolsAgent::processPendingMessages()
463 WebCore::ScriptDebugServer::shared().runPendingTasks();
466 void WebDevToolsAgent::setMessageLoopDispatchHandler(MessageLoopDispatchHandler handler)
468 DebuggerAgentManager::setMessageLoopDispatchHandler(handler);
471 } // namespace WebKit