browser_message_filter.cc revision 58e6fbe4ee35d65e14b626c557d37565bf8ad179
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/public/browser/browser_message_filter.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/logging.h" 10#include "base/process/kill.h" 11#include "base/process/process_handle.h" 12#include "base/task_runner.h" 13#include "content/public/browser/user_metrics.h" 14#include "content/public/common/result_codes.h" 15#include "ipc/ipc_sync_message.h" 16 17using content::BrowserMessageFilter; 18 19namespace content { 20 21BrowserMessageFilter::BrowserMessageFilter() 22 : channel_(NULL), 23#if defined(OS_WIN) 24 peer_handle_(base::kNullProcessHandle), 25#endif 26 peer_pid_(base::kNullProcessId) { 27} 28 29void BrowserMessageFilter::OnFilterAdded(IPC::Channel* channel) { 30 channel_ = channel; 31} 32 33void BrowserMessageFilter::OnChannelClosing() { 34 channel_ = NULL; 35} 36 37void BrowserMessageFilter::OnChannelConnected(int32 peer_pid) { 38 peer_pid_ = peer_pid; 39} 40 41bool BrowserMessageFilter::OnMessageReceived(const IPC::Message& message) { 42 BrowserThread::ID thread = BrowserThread::IO; 43 OverrideThreadForMessage(message, &thread); 44 45 if (thread == BrowserThread::IO) { 46 scoped_refptr<base::TaskRunner> runner = 47 OverrideTaskRunnerForMessage(message); 48 if (runner.get()) { 49 runner->PostTask( 50 FROM_HERE, 51 base::Bind(base::IgnoreResult(&BrowserMessageFilter::DispatchMessage), 52 this, 53 message)); 54 return true; 55 } 56 return DispatchMessage(message); 57 } 58 59 if (thread == BrowserThread::UI && !CheckCanDispatchOnUI(message, this)) 60 return true; 61 62 BrowserThread::PostTask( 63 thread, FROM_HERE, 64 base::Bind(base::IgnoreResult(&BrowserMessageFilter::DispatchMessage), 65 this, message)); 66 return true; 67} 68 69base::ProcessHandle BrowserMessageFilter::PeerHandle() { 70#if defined(OS_WIN) 71 base::AutoLock lock(peer_handle_lock_); 72 if (peer_handle_ == base::kNullProcessHandle) 73 base::OpenPrivilegedProcessHandle(peer_pid_, &peer_handle_); 74 75 return peer_handle_; 76#else 77 base::ProcessHandle result = base::kNullProcessHandle; 78 base::OpenPrivilegedProcessHandle(peer_pid_, &result); 79 return result; 80#endif 81} 82 83bool BrowserMessageFilter::Send(IPC::Message* message) { 84 if (message->is_sync()) { 85 // We don't support sending synchronous messages from the browser. If we 86 // really needed it, we can make this class derive from SyncMessageFilter 87 // but it seems better to not allow sending synchronous messages from the 88 // browser, since it might allow a corrupt/malicious renderer to hang us. 89 NOTREACHED() << "Can't send sync message through BrowserMessageFilter!"; 90 return false; 91 } 92 93 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 94 BrowserThread::PostTask( 95 BrowserThread::IO, 96 FROM_HERE, 97 base::Bind(base::IgnoreResult(&BrowserMessageFilter::Send), this, 98 message)); 99 return true; 100 } 101 102 if (channel_) 103 return channel_->Send(message); 104 105 delete message; 106 return false; 107} 108 109void BrowserMessageFilter::OverrideThreadForMessage(const IPC::Message& message, 110 BrowserThread::ID* thread) { 111} 112 113base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage( 114 const IPC::Message& message) { 115 return NULL; 116} 117 118bool BrowserMessageFilter::CheckCanDispatchOnUI(const IPC::Message& message, 119 IPC::Sender* sender) { 120#if defined(OS_WIN) 121 // On Windows there's a potential deadlock with sync messsages going in 122 // a circle from browser -> plugin -> renderer -> browser. 123 // On Linux we can avoid this by avoiding sync messages from browser->plugin. 124 // On Mac we avoid this by not supporting windowed plugins. 125 if (message.is_sync() && !message.is_caller_pumping_messages()) { 126 // NOTE: IF YOU HIT THIS ASSERT, THE SOLUTION IS ALMOST NEVER TO RUN A 127 // NESTED MESSAGE LOOP IN THE RENDERER!!! 128 // That introduces reentrancy which causes hard to track bugs. You should 129 // find a way to either turn this into an asynchronous message, or one 130 // that can be answered on the IO thread. 131 NOTREACHED() << "Can't send sync messages to UI thread without pumping " 132 "messages in the renderer or else deadlocks can occur if the page " 133 "has windowed plugins! (message type " << message.type() << ")"; 134 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); 135 reply->set_reply_error(); 136 sender->Send(reply); 137 return false; 138 } 139#endif 140 return true; 141} 142 143void BrowserMessageFilter::BadMessageReceived() { 144 base::KillProcess(PeerHandle(), content::RESULT_CODE_KILLED_BAD_MESSAGE, 145 false); 146} 147 148BrowserMessageFilter::~BrowserMessageFilter() { 149#if defined(OS_WIN) 150 if (peer_handle_ != base::kNullProcessHandle) 151 base::CloseProcessHandle(peer_handle_); 152#endif 153} 154 155bool BrowserMessageFilter::DispatchMessage(const IPC::Message& message) { 156 bool message_was_ok = true; 157 bool rv = OnMessageReceived(message, &message_was_ok); 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) || rv) << 159 "Must handle messages that were dispatched to another thread!"; 160 if (!message_was_ok) { 161 content::RecordAction(UserMetricsAction("BadMessageTerminate_BMF")); 162 BadMessageReceived(); 163 } 164 165 return rv; 166} 167 168} // namespace content 169