| // Copyright (c) 2012 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 "content/public/browser/browser_message_filter.h" |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/process/kill.h" |
| #include "base/process/process_handle.h" |
| #include "base/task_runner.h" |
| #include "content/public/browser/user_metrics.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/result_codes.h" |
| #include "ipc/ipc_sync_message.h" |
| |
| using content::BrowserMessageFilter; |
| |
| namespace content { |
| |
| class BrowserMessageFilter::Internal : public IPC::ChannelProxy::MessageFilter { |
| public: |
| explicit Internal(BrowserMessageFilter* filter) : filter_(filter) {} |
| |
| private: |
| virtual ~Internal() {} |
| |
| // IPC::ChannelProxy::MessageFilter implementation: |
| virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE { |
| filter_->channel_ = channel; |
| filter_->OnFilterAdded(channel); |
| } |
| |
| virtual void OnFilterRemoved() OVERRIDE { |
| filter_->OnFilterRemoved(); |
| } |
| |
| virtual void OnChannelClosing() OVERRIDE { |
| filter_->channel_ = NULL; |
| filter_->OnChannelClosing(); |
| } |
| |
| virtual void OnChannelConnected(int32 peer_pid) OVERRIDE { |
| filter_->peer_pid_ = peer_pid; |
| filter_->OnChannelConnected(peer_pid); |
| } |
| |
| virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { |
| BrowserThread::ID thread = BrowserThread::IO; |
| filter_->OverrideThreadForMessage(message, &thread); |
| |
| if (thread == BrowserThread::IO) { |
| scoped_refptr<base::TaskRunner> runner = |
| filter_->OverrideTaskRunnerForMessage(message); |
| if (runner.get()) { |
| runner->PostTask( |
| FROM_HERE, |
| base::Bind( |
| base::IgnoreResult(&Internal::DispatchMessage), this, message)); |
| return true; |
| } |
| return DispatchMessage(message); |
| } |
| |
| if (thread == BrowserThread::UI && |
| !BrowserMessageFilter::CheckCanDispatchOnUI(message, filter_)) { |
| return true; |
| } |
| |
| BrowserThread::PostTask( |
| thread, FROM_HERE, |
| base::Bind( |
| base::IgnoreResult(&Internal::DispatchMessage), this, message)); |
| return true; |
| } |
| |
| // Dispatches a message to the derived class. |
| bool DispatchMessage(const IPC::Message& message) { |
| bool message_was_ok = true; |
| bool rv = filter_->OnMessageReceived(message, &message_was_ok); |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) || rv) << |
| "Must handle messages that were dispatched to another thread!"; |
| if (!message_was_ok) { |
| content::RecordAction(UserMetricsAction("BadMessageTerminate_BMF")); |
| filter_->BadMessageReceived(); |
| } |
| |
| return rv; |
| } |
| |
| scoped_refptr<BrowserMessageFilter> filter_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Internal); |
| }; |
| |
| BrowserMessageFilter::BrowserMessageFilter() |
| : internal_(NULL), channel_(NULL), |
| #if defined(OS_WIN) |
| peer_handle_(base::kNullProcessHandle), |
| #endif |
| peer_pid_(base::kNullProcessId) { |
| } |
| |
| base::ProcessHandle BrowserMessageFilter::PeerHandle() { |
| #if defined(OS_WIN) |
| base::AutoLock lock(peer_handle_lock_); |
| if (peer_handle_ == base::kNullProcessHandle) |
| base::OpenPrivilegedProcessHandle(peer_pid_, &peer_handle_); |
| |
| return peer_handle_; |
| #else |
| base::ProcessHandle result = base::kNullProcessHandle; |
| base::OpenPrivilegedProcessHandle(peer_pid_, &result); |
| return result; |
| #endif |
| } |
| |
| |
| void BrowserMessageFilter::OnDestruct() const { |
| delete this; |
| } |
| |
| bool BrowserMessageFilter::Send(IPC::Message* message) { |
| if (message->is_sync()) { |
| // We don't support sending synchronous messages from the browser. If we |
| // really needed it, we can make this class derive from SyncMessageFilter |
| // but it seems better to not allow sending synchronous messages from the |
| // browser, since it might allow a corrupt/malicious renderer to hang us. |
| NOTREACHED() << "Can't send sync message through BrowserMessageFilter!"; |
| return false; |
| } |
| |
| if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(base::IgnoreResult(&BrowserMessageFilter::Send), this, |
| message)); |
| return true; |
| } |
| |
| if (channel_) |
| return channel_->Send(message); |
| |
| delete message; |
| return false; |
| } |
| |
| base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage( |
| const IPC::Message& message) { |
| return NULL; |
| } |
| |
| bool BrowserMessageFilter::CheckCanDispatchOnUI(const IPC::Message& message, |
| IPC::Sender* sender) { |
| #if defined(OS_WIN) |
| // On Windows there's a potential deadlock with sync messsages going in |
| // a circle from browser -> plugin -> renderer -> browser. |
| // On Linux we can avoid this by avoiding sync messages from browser->plugin. |
| // On Mac we avoid this by not supporting windowed plugins. |
| if (message.is_sync() && !message.is_caller_pumping_messages()) { |
| // NOTE: IF YOU HIT THIS ASSERT, THE SOLUTION IS ALMOST NEVER TO RUN A |
| // NESTED MESSAGE LOOP IN THE RENDERER!!! |
| // That introduces reentrancy which causes hard to track bugs. You should |
| // find a way to either turn this into an asynchronous message, or one |
| // that can be answered on the IO thread. |
| NOTREACHED() << "Can't send sync messages to UI thread without pumping " |
| "messages in the renderer or else deadlocks can occur if the page " |
| "has windowed plugins! (message type " << message.type() << ")"; |
| IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); |
| reply->set_reply_error(); |
| sender->Send(reply); |
| return false; |
| } |
| #endif |
| return true; |
| } |
| |
| void BrowserMessageFilter::BadMessageReceived() { |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| |
| if (!command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) { |
| base::KillProcess(PeerHandle(), content::RESULT_CODE_KILLED_BAD_MESSAGE, |
| false); |
| } |
| } |
| |
| BrowserMessageFilter::~BrowserMessageFilter() { |
| #if defined(OS_WIN) |
| if (peer_handle_ != base::kNullProcessHandle) |
| base::CloseProcessHandle(peer_handle_); |
| #endif |
| } |
| |
| IPC::ChannelProxy::MessageFilter* BrowserMessageFilter::GetFilter() { |
| // We create this on demand so that if a filter is used in a unit test but |
| // never attached to a channel, we don't leak Internal and this; |
| DCHECK(!internal_) << "Should only be called once."; |
| internal_ = new Internal(this); |
| return internal_; |
| } |
| |
| } // namespace content |