Home | History | Annotate | Download | only in browser
      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 
     17 using content::BrowserMessageFilter;
     18 
     19 namespace content {
     20 
     21 BrowserMessageFilter::BrowserMessageFilter()
     22     : channel_(NULL),
     23 #if defined(OS_WIN)
     24       peer_handle_(base::kNullProcessHandle),
     25 #endif
     26       peer_pid_(base::kNullProcessId) {
     27 }
     28 
     29 void BrowserMessageFilter::OnFilterAdded(IPC::Channel* channel) {
     30   channel_ = channel;
     31 }
     32 
     33 void BrowserMessageFilter::OnChannelClosing() {
     34   channel_ = NULL;
     35 }
     36 
     37 void BrowserMessageFilter::OnChannelConnected(int32 peer_pid) {
     38   peer_pid_ = peer_pid;
     39 }
     40 
     41 bool 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 
     69 base::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 
     83 bool 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 
    109 void BrowserMessageFilter::OverrideThreadForMessage(const IPC::Message& message,
    110                                                     BrowserThread::ID* thread) {
    111 }
    112 
    113 base::TaskRunner* BrowserMessageFilter::OverrideTaskRunnerForMessage(
    114     const IPC::Message& message) {
    115   return NULL;
    116 }
    117 
    118 bool 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 
    143 void BrowserMessageFilter::BadMessageReceived() {
    144   base::KillProcess(PeerHandle(), content::RESULT_CODE_KILLED_BAD_MESSAGE,
    145                     false);
    146 }
    147 
    148 BrowserMessageFilter::~BrowserMessageFilter() {
    149 #if defined(OS_WIN)
    150   if (peer_handle_ != base::kNullProcessHandle)
    151     base::CloseProcessHandle(peer_handle_);
    152 #endif
    153 }
    154 
    155 bool 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