browser_message_filter.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/command_line.h" 10#include "base/logging.h" 11#include "base/process/kill.h" 12#include "base/process/process_handle.h" 13#include "base/task_runner.h" 14#include "content/public/browser/user_metrics.h" 15#include "content/public/common/content_switches.h" 16#include "content/public/common/result_codes.h" 17#include "ipc/ipc_sync_message.h" 18 19using content::BrowserMessageFilter; 20 21namespace content { 22 23class BrowserMessageFilter::Internal : public IPC::ChannelProxy::MessageFilter { 24 public: 25 explicit Internal(BrowserMessageFilter* filter) : filter_(filter) {} 26 27 private: 28 virtual ~Internal() {} 29 30 // IPC::ChannelProxy::MessageFilter implementation: 31 virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE { 32 filter_->channel_ = channel; 33 filter_->OnFilterAdded(channel); 34 } 35 36 virtual void OnFilterRemoved() OVERRIDE { 37 filter_->OnFilterRemoved(); 38 } 39 40 virtual void OnChannelClosing() OVERRIDE { 41 filter_->channel_ = NULL; 42 filter_->OnChannelClosing(); 43 } 44 45 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE { 46 filter_->peer_pid_ = peer_pid; 47 filter_->OnChannelConnected(peer_pid); 48 } 49 50 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 51 BrowserThread::ID thread = BrowserThread::IO; 52 filter_->OverrideThreadForMessage(message, &thread); 53 54 if (thread == BrowserThread::IO) { 55 scoped_refptr<base::TaskRunner> runner = 56 filter_->OverrideTaskRunnerForMessage(message); 57 if (runner.get()) { 58 runner->PostTask( 59 FROM_HERE, 60 base::Bind( 61 base::IgnoreResult(&Internal::DispatchMessage), this, message)); 62 return true; 63 } 64 return DispatchMessage(message); 65 } 66 67 if (thread == BrowserThread::UI && 68 !BrowserMessageFilter::CheckCanDispatchOnUI(message, filter_)) { 69 return true; 70 } 71 72 BrowserThread::PostTask( 73 thread, FROM_HERE, 74 base::Bind( 75 base::IgnoreResult(&Internal::DispatchMessage), this, message)); 76 return true; 77 } 78 79 // Dispatches a message to the derived class. 80 bool DispatchMessage(const IPC::Message& message) { 81 bool message_was_ok = true; 82 bool rv = filter_->OnMessageReceived(message, &message_was_ok); 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) || rv) << 84 "Must handle messages that were dispatched to another thread!"; 85 if (!message_was_ok) { 86 content::RecordAction(UserMetricsAction("BadMessageTerminate_BMF")); 87 filter_->BadMessageReceived(); 88 } 89 90 return rv; 91 } 92 93 scoped_refptr<BrowserMessageFilter> filter_; 94 95 DISALLOW_COPY_AND_ASSIGN(Internal); 96}; 97 98BrowserMessageFilter::BrowserMessageFilter() 99 : internal_(NULL), channel_(NULL), 100#if defined(OS_WIN) 101 peer_handle_(base::kNullProcessHandle), 102#endif 103 peer_pid_(base::kNullProcessId) { 104} 105 106base::ProcessHandle BrowserMessageFilter::PeerHandle() { 107#if defined(OS_WIN) 108 base::AutoLock lock(peer_handle_lock_); 109 if (peer_handle_ == base::kNullProcessHandle) 110 base::OpenPrivilegedProcessHandle(peer_pid_, &peer_handle_); 111 112 return peer_handle_; 113#else 114 base::ProcessHandle result = base::kNullProcessHandle; 115 base::OpenPrivilegedProcessHandle(peer_pid_, &result); 116 return result; 117#endif 118} 119 120 121void BrowserMessageFilter::OnDestruct() const { 122 delete this; 123} 124 125bool BrowserMessageFilter::Send(IPC::Message* message) { 126 if (message->is_sync()) { 127 // We don't support sending synchronous messages from the browser. If we 128 // really needed it, we can make this class derive from SyncMessageFilter 129 // but it seems better to not allow sending synchronous messages from the 130 // browser, since it might allow a corrupt/malicious renderer to hang us. 131 NOTREACHED() << "Can't send sync message through BrowserMessageFilter!"; 132 return false; 133 } 134 135 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 136 BrowserThread::PostTask( 137 BrowserThread::IO, 138 FROM_HERE, 139 base::Bind(base::IgnoreResult(&BrowserMessageFilter::Send), this, 140 message)); 141 return true; 142 } 143 144 if (channel_) 145 return channel_->Send(message); 146 147 delete message; 148 return false; 149} 150 151base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage( 152 const IPC::Message& message) { 153 return NULL; 154} 155 156bool BrowserMessageFilter::CheckCanDispatchOnUI(const IPC::Message& message, 157 IPC::Sender* sender) { 158#if defined(OS_WIN) 159 // On Windows there's a potential deadlock with sync messsages going in 160 // a circle from browser -> plugin -> renderer -> browser. 161 // On Linux we can avoid this by avoiding sync messages from browser->plugin. 162 // On Mac we avoid this by not supporting windowed plugins. 163 if (message.is_sync() && !message.is_caller_pumping_messages()) { 164 // NOTE: IF YOU HIT THIS ASSERT, THE SOLUTION IS ALMOST NEVER TO RUN A 165 // NESTED MESSAGE LOOP IN THE RENDERER!!! 166 // That introduces reentrancy which causes hard to track bugs. You should 167 // find a way to either turn this into an asynchronous message, or one 168 // that can be answered on the IO thread. 169 NOTREACHED() << "Can't send sync messages to UI thread without pumping " 170 "messages in the renderer or else deadlocks can occur if the page " 171 "has windowed plugins! (message type " << message.type() << ")"; 172 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); 173 reply->set_reply_error(); 174 sender->Send(reply); 175 return false; 176 } 177#endif 178 return true; 179} 180 181void BrowserMessageFilter::BadMessageReceived() { 182 CommandLine* command_line = CommandLine::ForCurrentProcess(); 183 184 if (!command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) { 185 base::KillProcess(PeerHandle(), content::RESULT_CODE_KILLED_BAD_MESSAGE, 186 false); 187 } 188} 189 190BrowserMessageFilter::~BrowserMessageFilter() { 191#if defined(OS_WIN) 192 if (peer_handle_ != base::kNullProcessHandle) 193 base::CloseProcessHandle(peer_handle_); 194#endif 195} 196 197IPC::ChannelProxy::MessageFilter* BrowserMessageFilter::GetFilter() { 198 // We create this on demand so that if a filter is used in a unit test but 199 // never attached to a channel, we don't leak Internal and this; 200 DCHECK(!internal_) << "Should only be called once."; 201 internal_ = new Internal(this); 202 return internal_; 203} 204 205} // namespace content 206