// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/renderer/debug_message_handler.h" #include "chrome/common/render_messages.h" #include "chrome/renderer/render_view.h" //////////////////////////////////////// // methods called from the RenderThread DebugMessageHandler::DebugMessageHandler(RenderView* view) : debugger_(NULL), view_(view), channel_(NULL) { view_loop_ = MessageLoop::current(); view_routing_id_ = view_->routing_id(); } DebugMessageHandler::~DebugMessageHandler() { } void DebugMessageHandler::EvaluateScript(const std::wstring& script) { DCHECK(MessageLoop::current() == view_loop_); // It's possible that this will get cleared out from under us. RenderView* view = view_; if (view) { view->EvaluateScript(L"", script); } } void DebugMessageHandler::Attach() { DCHECK(MessageLoop::current() == view_loop_); debugger_->Attach(); } /////////////////////////////////////////////// // all methods below called from the IO thread void DebugMessageHandler::DebuggerOutput(const std::wstring& out) { channel_->Send(new ViewHostMsg_DebuggerOutput(view_routing_id_, out)); } void DebugMessageHandler::OnBreak(bool force) { // Set the debug break flag in the V8 enging. debugger_->Break(force); // If a forced break has been requested make sure that it will occour by // running some JavaScript in the renderer. if (force && view_loop_) { view_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &DebugMessageHandler::EvaluateScript, std::wstring(L"javascript:void(0)"))); } } void DebugMessageHandler::OnAttach() { if (!debugger_) { debugger_ = new DebuggerBridge(this); } // Run the actual debugger attach in the renderer as it uses V8 methods which // most run in the V8 thread. if (view_loop_) { view_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &DebugMessageHandler::Attach)); } } void DebugMessageHandler::OnCommand(const std::wstring& cmd) { if (!debugger_) { NOTREACHED(); std::wstring msg = StringPrintf(L"before attach, ignored command (%S)", cmd.c_str()); DebuggerOutput(msg); } else { debugger_->Command(cmd); } } void DebugMessageHandler::OnDetach() { if (debugger_) debugger_->Detach(); if (view_loop_ && view_) view_loop_->PostTask(FROM_HERE, NewRunnableMethod( view_, &RenderView::OnDebugDetach)); } void DebugMessageHandler::OnFilterAdded(IPC::Channel* channel) { channel_ = channel; } void DebugMessageHandler::OnFilterRemoved() { channel_ = NULL; view_loop_ = NULL; view_ = NULL; // By the time this is called, the view is not in a state where it can // receive messages from the MessageLoop, so we need to clear those first. OnDetach(); } bool DebugMessageHandler::OnMessageReceived(const IPC::Message& message) { DCHECK(channel_ != NULL); // In theory, there could be multiple debuggers running (in practice this // hasn't been implemented yet), so make sure we only handle messages meant // for the view we were initialized for. if (message.routing_id() != view_routing_id_) return false; bool handled = true; IPC_BEGIN_MESSAGE_MAP(DebugMessageHandler, message) IPC_MESSAGE_HANDLER_GENERIC(ViewMsg_DebugAttach, OnAttach(); handled = false;) IPC_MESSAGE_HANDLER(ViewMsg_DebugBreak, OnBreak) IPC_MESSAGE_HANDLER(ViewMsg_DebugCommand, OnCommand) IPC_MESSAGE_HANDLER_GENERIC(ViewMsg_DebugDetach, OnDetach(); handled = false;) // If the debugger is active, then it's possible that the renderer thread // is suspended handling a breakpoint. In that case, the renderer will // hang forever and never exit. To avoid this, we look for close messages // and tell the debugger to shutdown. IPC_MESSAGE_HANDLER_GENERIC(ViewMsg_Close, if (debugger_) OnDetach(); handled = false;) IPC_MESSAGE_UNHANDLED(handled = false); IPC_END_MESSAGE_MAP() return handled; }