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