• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ensureClientMessageLoopCreated(WebDevToolsAgentClient * client)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 
inspectedViewClosed(WebViewImpl * view)85     static void inspectedViewClosed(WebViewImpl* view)
86     {
87         if (s_instance)
88             s_instance->m_frozenViews.remove(view);
89     }
90 
didNavigate()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:
ClientMessageLoopAdapter(PassOwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> messageLoop)99     ClientMessageLoopAdapter(PassOwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> messageLoop)
100         : m_running(false)
101         , m_messageLoop(messageLoop) { }
102 
103 
run(Page * page)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 
quitNow()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 
WebDevToolsAgentImpl(WebViewImpl * webViewImpl,WebDevToolsAgentClient * client)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 
~WebDevToolsAgentImpl()173 WebDevToolsAgentImpl::~WebDevToolsAgentImpl()
174 {
175     DebuggerAgentManager::onWebViewClosed(m_webViewImpl);
176     ClientMessageLoopAdapter::inspectedViewClosed(m_webViewImpl);
177 }
178 
attach()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 
detach()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 
frontendLoaded()203 void WebDevToolsAgentImpl::frontendLoaded()
204 {
205     inspectorController()->connectFrontend();
206 }
207 
didNavigate()208 void WebDevToolsAgentImpl::didNavigate()
209 {
210     ClientMessageLoopAdapter::didNavigate();
211     DebuggerAgentManager::onNavigate();
212 }
213 
didClearWindowObject(WebFrameImpl * webframe)214 void WebDevToolsAgentImpl::didClearWindowObject(WebFrameImpl* webframe)
215 {
216     DebuggerAgentManager::setHostId(webframe, m_hostId);
217 }
218 
dispatchOnInspectorBackend(const WebString & message)219 void WebDevToolsAgentImpl::dispatchOnInspectorBackend(const WebString& message)
220 {
221     inspectorController()->dispatchMessageFromFrontend(message);
222 }
223 
inspectElementAt(const WebPoint & point)224 void WebDevToolsAgentImpl::inspectElementAt(const WebPoint& point)
225 {
226     m_webViewImpl->inspectElementAt(point);
227 }
228 
setRuntimeProperty(const WebString & name,const WebString & value)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 
inspectorController()237 InspectorController* WebDevToolsAgentImpl::inspectorController()
238 {
239     if (Page* page = m_webViewImpl->page())
240         return page->inspectorController();
241     return 0;
242 }
243 
mainFrame()244 Frame* WebDevToolsAgentImpl::mainFrame()
245 {
246     if (Page* page = m_webViewImpl->page())
247         return page->mainFrame();
248     return 0;
249 }
250 
inspectorDestroyed()251 void WebDevToolsAgentImpl::inspectorDestroyed()
252 {
253     // Our lifetime is bound to the WebViewImpl.
254 }
255 
openInspectorFrontend(InspectorController *)256 void WebDevToolsAgentImpl::openInspectorFrontend(InspectorController*)
257 {
258 }
259 
highlight(Node * node)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 
hideHighlight()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 
sendMessageToFrontend(const String & message)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 
updateInspectorStateCookie(const String & state)290 void WebDevToolsAgentImpl::updateInspectorStateCookie(const String& state)
291 {
292     m_client->runtimePropertyChanged(kInspectorStateFeatureName, state);
293 }
294 
evaluateInWebInspector(long callId,const WebString & script)295 void WebDevToolsAgentImpl::evaluateInWebInspector(long callId, const WebString& script)
296 {
297     InspectorController* ic = inspectorController();
298     ic->evaluateForTestInFrontend(callId, script);
299 }
300 
setTimelineProfilingEnabled(bool enabled)301 void WebDevToolsAgentImpl::setTimelineProfilingEnabled(bool enabled)
302 {
303     InspectorController* ic = inspectorController();
304     if (enabled)
305         ic->startTimelineProfiler();
306     else
307         ic->stopTimelineProfiler();
308 }
309 
executeDebuggerCommand(const WebString & command,int callerId)310 void WebDevToolsAgent::executeDebuggerCommand(const WebString& command, int callerId)
311 {
312     DebuggerAgentManager::executeDebuggerCommand(command, callerId);
313 }
314 
debuggerPauseScript()315 void WebDevToolsAgent::debuggerPauseScript()
316 {
317     DebuggerAgentManager::pauseScript();
318 }
319 
interruptAndDispatch(MessageDescriptor * d)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 
shouldInterruptForMessage(const WebString & message)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 
processPendingMessages()352 void WebDevToolsAgent::processPendingMessages()
353 {
354     PageScriptDebugServer::shared().runPendingTasks();
355 }
356 
setMessageLoopDispatchHandler(MessageLoopDispatchHandler handler)357 void WebDevToolsAgent::setMessageLoopDispatchHandler(MessageLoopDispatchHandler handler)
358 {
359     DebuggerAgentManager::setMessageLoopDispatchHandler(handler);
360 }
361 
362 } // namespace WebKit
363