• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "DebuggerAgentManager.h"
33 
34 #include "DebuggerAgentImpl.h"
35 #include "Frame.h"
36 #include "PageGroupLoadDeferrer.h"
37 #include "V8Proxy.h"
38 #include "WebDevToolsAgentImpl.h"
39 #include "WebFrameImpl.h"
40 #include "WebViewImpl.h"
41 #include <wtf/HashSet.h>
42 #include <wtf/Noncopyable.h>
43 
44 namespace WebKit {
45 
46 WebDevToolsAgent::MessageLoopDispatchHandler DebuggerAgentManager::s_messageLoopDispatchHandler = 0;
47 
48 bool DebuggerAgentManager::s_inHostDispatchHandler = false;
49 
50 DebuggerAgentManager::DeferrersMap DebuggerAgentManager::s_pageDeferrers;
51 
52 bool DebuggerAgentManager::s_inUtilityContext = false;
53 
54 bool DebuggerAgentManager::s_debugBreakDelayed = false;
55 
56 namespace {
57 
58 class CallerIdWrapper : public v8::Debug::ClientData, public Noncopyable {
59 public:
CallerIdWrapper()60     CallerIdWrapper() : m_callerIsMananager(true), m_callerId(0) { }
CallerIdWrapper(int callerId)61     explicit CallerIdWrapper(int callerId)
62         : m_callerIsMananager(false)
63         , m_callerId(callerId) { }
~CallerIdWrapper()64     ~CallerIdWrapper() { }
callerIsMananager() const65     bool callerIsMananager() const { return m_callerIsMananager; }
callerId() const66     int callerId() const { return m_callerId; }
67 private:
68     bool m_callerIsMananager;
69     int m_callerId;
70 };
71 
72 } // namespace
73 
74 
debugHostDispatchHandler()75 void DebuggerAgentManager::debugHostDispatchHandler()
76 {
77     if (!s_messageLoopDispatchHandler || !s_attachedAgentsMap)
78         return;
79 
80     if (s_inHostDispatchHandler)
81         return;
82 
83     s_inHostDispatchHandler = true;
84 
85     Vector<WebViewImpl*> views;
86     // 1. Disable active objects and input events.
87     for (AttachedAgentsMap::iterator it = s_attachedAgentsMap->begin(); it != s_attachedAgentsMap->end(); ++it) {
88         DebuggerAgentImpl* agent = it->second;
89         s_pageDeferrers.set(agent->webView(), new WebCore::PageGroupLoadDeferrer(agent->page(), true));
90         views.append(agent->webView());
91         agent->webView()->setIgnoreInputEvents(true);
92     }
93 
94     // 2. Process messages.
95     s_messageLoopDispatchHandler();
96 
97     // 3. Bring things back.
98     for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) {
99         if (s_pageDeferrers.contains(*it)) {
100             // The view was not closed during the dispatch.
101             (*it)->setIgnoreInputEvents(false);
102         }
103     }
104     deleteAllValues(s_pageDeferrers);
105     s_pageDeferrers.clear();
106 
107     s_inHostDispatchHandler = false;
108     if (!s_attachedAgentsMap) {
109         // Remove handlers if all agents were detached within host dispatch.
110         v8::Debug::SetMessageHandler(0);
111         v8::Debug::SetHostDispatchHandler(0);
112     }
113 }
114 
115 DebuggerAgentManager::AttachedAgentsMap* DebuggerAgentManager::s_attachedAgentsMap = 0;
116 
debugAttach(DebuggerAgentImpl * debuggerAgent)117 void DebuggerAgentManager::debugAttach(DebuggerAgentImpl* debuggerAgent)
118 {
119     if (!s_attachedAgentsMap) {
120         s_attachedAgentsMap = new AttachedAgentsMap();
121         v8::Debug::SetMessageHandler2(&DebuggerAgentManager::onV8DebugMessage);
122         v8::Debug::SetHostDispatchHandler(&DebuggerAgentManager::debugHostDispatchHandler, 100 /* ms */);
123     }
124     int hostId = debuggerAgent->webdevtoolsAgent()->hostId();
125     ASSERT(hostId);
126     s_attachedAgentsMap->set(hostId, debuggerAgent);
127 }
128 
debugDetach(DebuggerAgentImpl * debuggerAgent)129 void DebuggerAgentManager::debugDetach(DebuggerAgentImpl* debuggerAgent)
130 {
131     if (!s_attachedAgentsMap) {
132         ASSERT_NOT_REACHED();
133         return;
134     }
135     int hostId = debuggerAgent->webdevtoolsAgent()->hostId();
136     ASSERT(s_attachedAgentsMap->get(hostId) == debuggerAgent);
137     bool isOnBreakpoint = (findAgentForCurrentV8Context() == debuggerAgent);
138     s_attachedAgentsMap->remove(hostId);
139 
140     if (s_attachedAgentsMap->isEmpty()) {
141         delete s_attachedAgentsMap;
142         s_attachedAgentsMap = 0;
143         // Note that we do not empty handlers while in dispatch - we schedule
144         // continue and do removal once we are out of the dispatch. Also there is
145         // no need to send continue command in this case since removing message
146         // handler will cause debugger unload and all breakpoints will be cleared.
147         if (!s_inHostDispatchHandler) {
148             v8::Debug::SetMessageHandler2(0);
149             v8::Debug::SetHostDispatchHandler(0);
150         }
151     } else {
152       // Remove all breakpoints set by the agent.
153       String clearBreakpointGroupCmd = String::format(
154           "{\"seq\":1,\"type\":\"request\",\"command\":\"clearbreakpointgroup\","
155               "\"arguments\":{\"groupId\":%d}}",
156           hostId);
157       sendCommandToV8(clearBreakpointGroupCmd, new CallerIdWrapper());
158 
159       if (isOnBreakpoint) {
160           // Force continue if detach happened in nessted message loop while
161           // debugger was paused on a breakpoint(as long as there are other
162           // attached agents v8 will wait for explicit'continue' message).
163           sendContinueCommandToV8();
164       }
165     }
166 }
167 
onV8DebugMessage(const v8::Debug::Message & message)168 void DebuggerAgentManager::onV8DebugMessage(const v8::Debug::Message& message)
169 {
170     v8::HandleScope scope;
171     v8::String::Value value(message.GetJSON());
172     String out(reinterpret_cast<const UChar*>(*value), value.length());
173 
174     // If callerData is not 0 the message is a response to a debugger command.
175     if (v8::Debug::ClientData* callerData = message.GetClientData()) {
176         CallerIdWrapper* wrapper = static_cast<CallerIdWrapper*>(callerData);
177         if (wrapper->callerIsMananager()) {
178             // Just ignore messages sent by this manager.
179             return;
180         }
181         DebuggerAgentImpl* debuggerAgent = debuggerAgentForHostId(wrapper->callerId());
182         if (debuggerAgent)
183             debuggerAgent->debuggerOutput(out);
184         else if (!message.WillStartRunning()) {
185             // Autocontinue execution if there is no handler.
186             sendContinueCommandToV8();
187         }
188         return;
189     } // Otherwise it's an event message.
190     ASSERT(message.IsEvent());
191 
192     // Ignore unsupported event types.
193     if (message.GetEvent() != v8::AfterCompile && message.GetEvent() != v8::Break && message.GetEvent() != v8::Exception)
194         return;
195 
196     v8::Handle<v8::Context> context = message.GetEventContext();
197     // If the context is from one of the inpected tabs it should have its context
198     // data.
199     if (context.IsEmpty()) {
200         // Unknown context, skip the event.
201         return;
202     }
203 
204     if (s_inUtilityContext && message.GetEvent() == v8::Break) {
205         // This may happen when two tabs are being debugged in the same process.
206         // Suppose that first debugger is pauesed on an exception. It will run
207         // nested MessageLoop which may process Break request from the second
208         // debugger.
209         s_debugBreakDelayed = true;
210     } else {
211         // If the context is from one of the inpected tabs or injected extension
212         // scripts it must have hostId in the data field.
213         int hostId = WebCore::V8Proxy::contextDebugId(context);
214         if (hostId != -1) {
215             DebuggerAgentImpl* agent = debuggerAgentForHostId(hostId);
216             if (agent) {
217                 if (agent->autoContinueOnException()
218                     && message.GetEvent() == v8::Exception) {
219                     sendContinueCommandToV8();
220                     return;
221                 }
222 
223                 agent->debuggerOutput(out);
224                 return;
225             }
226         }
227     }
228 
229     if (!message.WillStartRunning()) {
230         // Autocontinue execution on break and exception  events if there is no
231         // handler.
232         sendContinueCommandToV8();
233     }
234 }
235 
pauseScript()236 void DebuggerAgentManager::pauseScript()
237 {
238     if (s_inUtilityContext)
239         s_debugBreakDelayed = true;
240     else
241         v8::Debug::DebugBreak();
242 }
243 
executeDebuggerCommand(const String & command,int callerId)244 void DebuggerAgentManager::executeDebuggerCommand(const String& command, int callerId)
245 {
246     sendCommandToV8(command, new CallerIdWrapper(callerId));
247 }
248 
setMessageLoopDispatchHandler(WebDevToolsAgent::MessageLoopDispatchHandler handler)249 void DebuggerAgentManager::setMessageLoopDispatchHandler(WebDevToolsAgent::MessageLoopDispatchHandler handler)
250 {
251     s_messageLoopDispatchHandler = handler;
252 }
253 
setHostId(WebFrameImpl * webframe,int hostId)254 void DebuggerAgentManager::setHostId(WebFrameImpl* webframe, int hostId)
255 {
256     ASSERT(hostId > 0);
257     WebCore::V8Proxy* proxy = WebCore::V8Proxy::retrieve(webframe->frame());
258     if (proxy)
259         proxy->setContextDebugId(hostId);
260 }
261 
onWebViewClosed(WebViewImpl * webview)262 void DebuggerAgentManager::onWebViewClosed(WebViewImpl* webview)
263 {
264     if (s_pageDeferrers.contains(webview)) {
265         delete s_pageDeferrers.get(webview);
266         s_pageDeferrers.remove(webview);
267     }
268 }
269 
onNavigate()270 void DebuggerAgentManager::onNavigate()
271 {
272     if (s_inHostDispatchHandler)
273         DebuggerAgentManager::sendContinueCommandToV8();
274 }
275 
sendCommandToV8(const String & cmd,v8::Debug::ClientData * data)276 void DebuggerAgentManager::sendCommandToV8(const String& cmd, v8::Debug::ClientData* data)
277 {
278     v8::Debug::SendCommand(reinterpret_cast<const uint16_t*>(cmd.characters()), cmd.length(), data);
279 }
280 
sendContinueCommandToV8()281 void DebuggerAgentManager::sendContinueCommandToV8()
282 {
283     String continueCmd("{\"seq\":1,\"type\":\"request\",\"command\":\"continue\"}");
284     sendCommandToV8(continueCmd, new CallerIdWrapper());
285 }
286 
findAgentForCurrentV8Context()287 DebuggerAgentImpl* DebuggerAgentManager::findAgentForCurrentV8Context()
288 {
289     if (!s_attachedAgentsMap)
290         return 0;
291     ASSERT(!s_attachedAgentsMap->isEmpty());
292 
293     WebCore::Frame* frame = WebCore::V8Proxy::retrieveFrameForEnteredContext();
294     if (!frame)
295         return 0;
296     WebCore::Page* page = frame->page();
297     for (AttachedAgentsMap::iterator it = s_attachedAgentsMap->begin(); it != s_attachedAgentsMap->end(); ++it) {
298         if (it->second->page() == page)
299             return it->second;
300     }
301     return 0;
302 }
303 
debuggerAgentForHostId(int hostId)304 DebuggerAgentImpl* DebuggerAgentManager::debuggerAgentForHostId(int hostId)
305 {
306     if (!s_attachedAgentsMap)
307         return 0;
308     return s_attachedAgentsMap->get(hostId);
309 }
310 
311 } // namespace WebKit
312