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