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