OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.git] / Source / WebKit / chromium / src / WebDevToolsAgentImpl.cpp
1 /*
2  * Copyright (C) 2010 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 "PlatformString.h"
44 #include "ResourceError.h"
45 #include "ResourceRequest.h"
46 #include "ResourceResponse.h"
47 #include "ScriptDebugServer.h"
48 #include "V8Binding.h"
49 #include "V8Node.h"
50 #include "V8Proxy.h"
51 #include "V8Utilities.h"
52 #include "WebDataSource.h"
53 #include "WebDevToolsAgentClient.h"
54 #include "WebFrameImpl.h"
55 #include "WebRect.h"
56 #include "WebString.h"
57 #include "WebURL.h"
58 #include "WebURLError.h"
59 #include "WebURLRequest.h"
60 #include "WebURLResponse.h"
61 #include "WebViewClient.h"
62 #include "WebViewImpl.h"
63 #include <wtf/CurrentTime.h>
64 #include <wtf/Noncopyable.h>
65 #include <wtf/OwnPtr.h>
66
67 using namespace WebCore;
68
69 namespace WebKit {
70
71 namespace {
72
73 static const char kFrontendConnectedFeatureName[] = "frontend-connected";
74 static const char kInspectorStateFeatureName[] = "inspector-state";
75
76 class ClientMessageLoopAdapter : public ScriptDebugServer::ClientMessageLoop {
77 public:
78     static void ensureClientMessageLoopCreated(WebDevToolsAgentClient* client)
79     {
80         if (s_instance)
81             return;
82         s_instance = new ClientMessageLoopAdapter(client->createClientMessageLoop());
83         ScriptDebugServer::shared().setClientMessageLoop(s_instance);
84     }
85
86     static void inspectedViewClosed(WebViewImpl* view)
87     {
88         if (s_instance)
89             s_instance->m_frozenViews.remove(view);
90     }
91
92     static void didNavigate()
93     {
94         // Release render thread if necessary.
95         if (s_instance && s_instance->m_running)
96             ScriptDebugServer::shared().continueProgram();
97     }
98
99 private:
100     ClientMessageLoopAdapter(PassOwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> messageLoop)
101         : m_running(false)
102         , m_messageLoop(messageLoop) { }
103
104
105     virtual void run(Page* page)
106     {
107         if (m_running)
108             return;
109         m_running = true;
110
111         Vector<WebViewImpl*> views;
112
113         // 1. Disable input events.
114         HashSet<Page*>::const_iterator end =  page->group().pages().end();
115         for (HashSet<Page*>::const_iterator it =  page->group().pages().begin(); it != end; ++it) {
116             WebViewImpl* view = WebViewImpl::fromPage(*it);
117             m_frozenViews.add(view);
118             views.append(view);
119             view->setIgnoreInputEvents(true);
120         }
121
122         // 2. Disable active objects
123         WebView::willEnterModalLoop();
124
125         // 3. Process messages until quitNow is called.
126         m_messageLoop->run();
127
128         // 4. Resume active objects
129         WebView::didExitModalLoop();
130
131         // 5. Resume input events.
132         for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) {
133             if (m_frozenViews.contains(*it)) {
134                 // The view was not closed during the dispatch.
135                 (*it)->setIgnoreInputEvents(false);
136             }
137         }
138
139         // 6. All views have been resumed, clear the set.
140         m_frozenViews.clear();
141
142         m_running = false;
143     }
144
145     virtual void quitNow()
146     {
147         m_messageLoop->quitNow();
148     }
149
150     bool m_running;
151     OwnPtr<WebKit::WebDevToolsAgentClient::WebKitClientMessageLoop> m_messageLoop;
152     typedef HashSet<WebViewImpl*> FrozenViewsSet;
153     FrozenViewsSet m_frozenViews;
154     static ClientMessageLoopAdapter* s_instance;
155
156 };
157
158 ClientMessageLoopAdapter* ClientMessageLoopAdapter::s_instance = 0;
159
160 } //  namespace
161
162 WebDevToolsAgentImpl::WebDevToolsAgentImpl(
163     WebViewImpl* webViewImpl,
164     WebDevToolsAgentClient* client)
165     : m_hostId(client->hostIdentifier())
166     , m_client(client)
167     , m_webViewImpl(webViewImpl)
168     , m_attached(false)
169 {
170     DebuggerAgentManager::setExposeV8DebuggerProtocol(
171         client->exposeV8DebuggerProtocol());
172 }
173
174 WebDevToolsAgentImpl::~WebDevToolsAgentImpl()
175 {
176     DebuggerAgentManager::onWebViewClosed(m_webViewImpl);
177     ClientMessageLoopAdapter::inspectedViewClosed(m_webViewImpl);
178 }
179
180 void WebDevToolsAgentImpl::attach()
181 {
182     if (m_attached)
183         return;
184
185     if (!m_client->exposeV8DebuggerProtocol())
186         ClientMessageLoopAdapter::ensureClientMessageLoopCreated(m_client);
187
188     m_debuggerAgentImpl.set(
189         new DebuggerAgentImpl(m_webViewImpl, this, m_client));
190     WebCString debuggerScriptJs = m_client->debuggerScriptSource();
191     ScriptDebugServer::shared().setDebuggerScriptSource(
192         String(debuggerScriptJs.data(), debuggerScriptJs.length()));
193     m_attached = true;
194 }
195
196 void WebDevToolsAgentImpl::detach()
197 {
198     // Prevent controller from sending messages to the frontend.
199     InspectorController* ic = inspectorController();
200     ic->disconnectFrontend();
201     ic->hideHighlight();
202     ic->close();
203     m_debuggerAgentImpl.set(0);
204     m_attached = false;
205 }
206
207 void WebDevToolsAgentImpl::frontendLoaded()
208 {
209     inspectorController()->connectFrontend();
210 }
211
212 void WebDevToolsAgentImpl::didNavigate()
213 {
214     ClientMessageLoopAdapter::didNavigate();
215     DebuggerAgentManager::onNavigate();
216 }
217
218 void WebDevToolsAgentImpl::didClearWindowObject(WebFrameImpl* webframe)
219 {
220     DebuggerAgentManager::setHostId(webframe, m_hostId);
221 }
222
223 void WebDevToolsAgentImpl::dispatchOnInspectorBackend(const WebString& message)
224 {
225     inspectorController()->dispatchMessageFromFrontend(message);
226 }
227
228 void WebDevToolsAgentImpl::inspectElementAt(const WebPoint& point)
229 {
230     m_webViewImpl->inspectElementAt(point);
231 }
232
233 void WebDevToolsAgentImpl::inspectNode(v8::Handle<v8::Value> node)
234 {
235     if (!V8Node::HasInstance(node))
236         V8Proxy::setDOMException(TYPE_MISMATCH_ERR);
237     else
238         inspectorController()->inspect(V8Node::toNative(v8::Handle<v8::Object>::Cast(node)));
239 }
240
241 void WebDevToolsAgentImpl::setRuntimeProperty(const WebString& name, const WebString& value)
242 {
243     if (name == kInspectorStateFeatureName) {
244         InspectorController* ic = inspectorController();
245         ic->restoreInspectorStateFromCookie(value);
246     }
247 }
248
249 InspectorController* WebDevToolsAgentImpl::inspectorController()
250 {
251     if (Page* page = m_webViewImpl->page())
252         return page->inspectorController();
253     return 0;
254 }
255
256 Frame* WebDevToolsAgentImpl::mainFrame()
257 {
258     if (Page* page = m_webViewImpl->page())
259         return page->mainFrame();
260     return 0;
261 }
262
263
264 //------- plugin resource load notifications ---------------
265 void WebDevToolsAgentImpl::identifierForInitialRequest(
266     unsigned long resourceId,
267     WebFrame* webFrame,
268     const WebURLRequest& request)
269 {
270     WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(webFrame);
271     Frame* frame = webFrameImpl->frame();
272     DocumentLoader* loader = frame->loader()->activeDocumentLoader();
273     InspectorInstrumentation::identifierForInitialRequest(frame, resourceId, loader, request.toResourceRequest());
274 }
275
276 void WebDevToolsAgentImpl::willSendRequest(unsigned long resourceId, WebURLRequest& request)
277 {
278     if (InspectorController* ic = inspectorController()) {
279         InspectorInstrumentation::willSendRequest(mainFrame(), resourceId, request.toMutableResourceRequest(), ResourceResponse());
280         if (ic->hasFrontend() && request.reportLoadTiming())
281             request.setReportRawHeaders(true);
282     }
283 }
284
285 void WebDevToolsAgentImpl::didReceiveData(unsigned long resourceId, int length)
286 {
287     InspectorInstrumentation::didReceiveContentLength(mainFrame(), resourceId, length);
288 }
289
290 void WebDevToolsAgentImpl::didReceiveResponse(unsigned long resourceId, const WebURLResponse& response)
291 {
292     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(mainFrame(), resourceId, response.toResourceResponse());
293     InspectorInstrumentation::didReceiveResourceResponse(cookie, resourceId, 0, response.toResourceResponse());
294 }
295
296 void WebDevToolsAgentImpl::didFinishLoading(unsigned long resourceId)
297 {
298     InspectorInstrumentation::didFinishLoading(mainFrame(), resourceId, 0);
299 }
300
301 void WebDevToolsAgentImpl::didFailLoading(unsigned long resourceId, const WebURLError& error)
302 {
303     InspectorInstrumentation::didFailLoading(mainFrame(), resourceId, error);
304 }
305
306 void WebDevToolsAgentImpl::inspectorDestroyed()
307 {
308     // Our lifetime is bound to the WebViewImpl.
309 }
310
311 void WebDevToolsAgentImpl::openInspectorFrontend(InspectorController*)
312 {
313 }
314
315 void WebDevToolsAgentImpl::highlight(Node* node)
316 {
317     // InspectorController does the actuall tracking of the highlighted node
318     // and the drawing of the highlight. Here we just make sure to invalidate
319     // the rects of the old and new nodes.
320     hideHighlight();
321 }
322
323 void WebDevToolsAgentImpl::hideHighlight()
324 {
325     // FIXME: able to invalidate a smaller rect.
326     // FIXME: Is it important to just invalidate the rect of the node region
327     // given that this is not on a critical codepath?  In order to do so, we'd
328     // have to take scrolling into account.
329     const WebSize& size = m_webViewImpl->size();
330     WebRect damagedRect(0, 0, size.width, size.height);
331     if (m_webViewImpl->client())
332         m_webViewImpl->client()->didInvalidateRect(damagedRect);
333 }
334
335 bool WebDevToolsAgentImpl::sendMessageToFrontend(const String& message)
336 {
337     WebDevToolsAgentImpl* devToolsAgent = static_cast<WebDevToolsAgentImpl*>(m_webViewImpl->devToolsAgent());
338     if (!devToolsAgent)
339         return false;
340
341     m_client->sendMessageToInspectorFrontend(message);
342     return true;
343 }
344
345 void WebDevToolsAgentImpl::updateInspectorStateCookie(const String& state)
346 {
347     m_client->runtimePropertyChanged(kInspectorStateFeatureName, state);
348 }
349
350 void WebDevToolsAgentImpl::evaluateInWebInspector(long callId, const WebString& script)
351 {
352     InspectorController* ic = inspectorController();
353     ic->evaluateForTestInFrontend(callId, script);
354 }
355
356 void WebDevToolsAgentImpl::setTimelineProfilingEnabled(bool enabled)
357 {
358     InspectorController* ic = inspectorController();
359     if (enabled)
360         ic->startTimelineProfiler();
361     else
362         ic->stopTimelineProfiler();
363 }
364
365 void WebDevToolsAgent::executeDebuggerCommand(const WebString& command, int callerId)
366 {
367     DebuggerAgentManager::executeDebuggerCommand(command, callerId);
368 }
369
370 void WebDevToolsAgent::debuggerPauseScript()
371 {
372     DebuggerAgentManager::pauseScript();
373 }
374
375 void WebDevToolsAgent::interruptAndDispatch(MessageDescriptor* d)
376 {
377     class DebuggerTask : public ScriptDebugServer::Task {
378     public:
379         DebuggerTask(WebDevToolsAgent::MessageDescriptor* descriptor) : m_descriptor(descriptor) { }
380         virtual ~DebuggerTask() { }
381         virtual void run()
382         {
383             if (WebDevToolsAgent* webagent = m_descriptor->agent())
384                 webagent->dispatchOnInspectorBackend(m_descriptor->message());
385         }
386     private:
387         OwnPtr<WebDevToolsAgent::MessageDescriptor> m_descriptor;
388     };
389     ScriptDebugServer::interruptAndRun(new DebuggerTask(d));
390 }
391
392 bool WebDevToolsAgent::shouldInterruptForMessage(const WebString& message)
393 {
394     String commandName;
395     if (!InspectorBackendDispatcher::getCommandName(message, &commandName))
396         return false;
397     return commandName == InspectorBackendDispatcher::Debugger_pauseCmd
398         || commandName == InspectorBackendDispatcher::Debugger_setJavaScriptBreakpointCmd
399         || commandName == InspectorBackendDispatcher::Debugger_removeJavaScriptBreakpointCmd
400         || commandName == InspectorBackendDispatcher::Debugger_activateBreakpointsCmd
401         || commandName == InspectorBackendDispatcher::Debugger_deactivateBreakpointsCmd
402         || commandName == InspectorBackendDispatcher::Inspector_startProfilingCmd
403         || commandName == InspectorBackendDispatcher::Inspector_stopProfilingCmd
404         || commandName == InspectorBackendDispatcher::Profiler_getProfileCmd;
405 }
406
407 void WebDevToolsAgent::processPendingMessages()
408 {
409     ScriptDebugServer::shared().runPendingTasks();
410 }
411
412 void WebDevToolsAgent::setMessageLoopDispatchHandler(MessageLoopDispatchHandler handler)
413 {
414     DebuggerAgentManager::setMessageLoopDispatchHandler(handler);
415 }
416
417 } // namespace WebKit