Home | History | Annotate | Download | only in pepper
      1 // Copyright 2013 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 "chrome/browser/renderer_host/pepper/pepper_output_protection_message_filter.h"
      6 
      7 #include "build/build_config.h"
      8 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
      9 #include "content/public/browser/browser_ppapi_host.h"
     10 #include "content/public/browser/browser_thread.h"
     11 #include "content/public/browser/render_frame_host.h"
     12 #include "content/public/browser/web_contents.h"
     13 #include "ppapi/c/pp_errors.h"
     14 #include "ppapi/c/private/ppb_output_protection_private.h"
     15 #include "ppapi/host/dispatch_host_message.h"
     16 #include "ppapi/host/host_message_context.h"
     17 #include "ppapi/host/ppapi_host.h"
     18 #include "ppapi/proxy/ppapi_messages.h"
     19 
     20 #if defined(OS_CHROMEOS)
     21 #include "ash/shell.h"
     22 #include "ash/shell_delegate.h"
     23 #include "ui/aura/window.h"
     24 #include "ui/display/chromeos/display_configurator.h"
     25 #include "ui/gfx/screen.h"
     26 #endif
     27 
     28 namespace chrome {
     29 
     30 namespace {
     31 
     32 #if defined(OS_CHROMEOS)
     33 COMPILE_ASSERT(static_cast<int>(PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_NONE) ==
     34                    static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_NONE),
     35                PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_NONE);
     36 COMPILE_ASSERT(
     37     static_cast<int>(PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_UNKNOWN) ==
     38         static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_UNKNOWN),
     39     PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_UNKNOWN);
     40 COMPILE_ASSERT(
     41     static_cast<int>(PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_INTERNAL) ==
     42         static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_INTERNAL),
     43     PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_INTERNAL);
     44 COMPILE_ASSERT(static_cast<int>(PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_VGA) ==
     45                    static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_VGA),
     46                PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_VGA);
     47 COMPILE_ASSERT(static_cast<int>(PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_HDMI) ==
     48                    static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_HDMI),
     49                PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_HDMI);
     50 COMPILE_ASSERT(static_cast<int>(PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_DVI) ==
     51                    static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_DVI),
     52                PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_DVI);
     53 COMPILE_ASSERT(
     54     static_cast<int>(PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_DISPLAYPORT) ==
     55         static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_DISPLAYPORT),
     56     PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_DISPLAYPORT);
     57 COMPILE_ASSERT(
     58     static_cast<int>(PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_NETWORK) ==
     59         static_cast<int>(ui::DISPLAY_CONNECTION_TYPE_NETWORK),
     60     PP_OUTPUT_PROTECTION_LINK_TYPE_PRIVATE_NETWORK);
     61 COMPILE_ASSERT(static_cast<int>(PP_OUTPUT_PROTECTION_METHOD_PRIVATE_NONE) ==
     62                    static_cast<int>(ui::CONTENT_PROTECTION_METHOD_NONE),
     63                PP_OUTPUT_PROTECTION_METHOD_PRIVATE_NONE);
     64 COMPILE_ASSERT(static_cast<int>(PP_OUTPUT_PROTECTION_METHOD_PRIVATE_HDCP) ==
     65                    static_cast<int>(ui::CONTENT_PROTECTION_METHOD_HDCP),
     66                PP_OUTPUT_PROTECTION_METHOD_PRIVATE_HDCP);
     67 
     68 bool GetCurrentDisplayId(content::RenderFrameHost* rfh, int64* display_id) {
     69   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     70   gfx::NativeView native_view = rfh->GetNativeView();
     71   gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view);
     72   if (!screen)
     73     return false;
     74   gfx::Display display = screen->GetDisplayNearestWindow(native_view);
     75   *display_id = display.id();
     76   return true;
     77 }
     78 #endif
     79 
     80 }  // namespace
     81 
     82 #if defined(OS_CHROMEOS)
     83 // Output protection delegate. All methods except constructor should be
     84 // invoked in UI thread.
     85 class PepperOutputProtectionMessageFilter::Delegate
     86     : public aura::WindowObserver {
     87  public:
     88   Delegate(int render_process_id, int render_frame_id);
     89   virtual ~Delegate();
     90 
     91   // aura::WindowObserver overrides.
     92   virtual void OnWindowHierarchyChanged(
     93       const aura::WindowObserver::HierarchyChangeParams& params) OVERRIDE;
     94   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
     95 
     96   int32_t OnQueryStatus(uint32_t* link_mask, uint32_t* protection_mask);
     97   int32_t OnEnableProtection(uint32_t desired_method_mask);
     98 
     99  private:
    100   ui::DisplayConfigurator::ContentProtectionClientId GetClientId();
    101 
    102   // Used to lookup the WebContents associated with this PP_Instance.
    103   int render_process_id_;
    104   int render_frame_id_;
    105 
    106   // Native window being observed.
    107   aura::Window* window_;
    108 
    109   ui::DisplayConfigurator::ContentProtectionClientId client_id_;
    110 
    111   // The display id which the renderer currently uses.
    112   int64 display_id_;
    113 
    114   // The last desired method mask. Will enable this mask on new display if
    115   // renderer changes display.
    116   uint32_t desired_method_mask_;
    117 };
    118 
    119 PepperOutputProtectionMessageFilter::Delegate::Delegate(int render_process_id,
    120                                                         int render_frame_id)
    121     : render_process_id_(render_process_id),
    122       render_frame_id_(render_frame_id),
    123       window_(NULL),
    124       client_id_(ui::DisplayConfigurator::kInvalidClientId),
    125       display_id_(0) {
    126   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    127 #if defined(USE_ATHENA)
    128   // Fail for now so that we can catch the event in the crash report.
    129   // crbug.com/381398
    130   CHECK(false);
    131 #endif
    132 }
    133 
    134 PepperOutputProtectionMessageFilter::Delegate::~Delegate() {
    135   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    136 
    137   ui::DisplayConfigurator* configurator =
    138       ash::Shell::GetInstance()->display_configurator();
    139   configurator->UnregisterContentProtectionClient(client_id_);
    140 
    141   if (window_)
    142     window_->RemoveObserver(this);
    143 }
    144 
    145 ui::DisplayConfigurator::ContentProtectionClientId
    146 PepperOutputProtectionMessageFilter::Delegate::GetClientId() {
    147   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    148   if (client_id_ == ui::DisplayConfigurator::kInvalidClientId) {
    149     content::RenderFrameHost* rfh =
    150         content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
    151     if (!GetCurrentDisplayId(rfh, &display_id_))
    152       return ui::DisplayConfigurator::kInvalidClientId;
    153 
    154     window_ = rfh->GetNativeView();
    155     if (!window_)
    156       return ui::DisplayConfigurator::kInvalidClientId;
    157     window_->AddObserver(this);
    158 
    159     ui::DisplayConfigurator* configurator =
    160         ash::Shell::GetInstance()->display_configurator();
    161     client_id_ = configurator->RegisterContentProtectionClient();
    162   }
    163   return client_id_;
    164 }
    165 
    166 int32_t PepperOutputProtectionMessageFilter::Delegate::OnQueryStatus(
    167     uint32_t* link_mask,
    168     uint32_t* protection_mask) {
    169   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    170 
    171   content::RenderFrameHost* rfh =
    172       content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
    173   if (!rfh) {
    174     LOG(WARNING) << "RenderFrameHost is not alive.";
    175     return PP_ERROR_FAILED;
    176   }
    177 
    178   ui::DisplayConfigurator* configurator =
    179       ash::Shell::GetInstance()->display_configurator();
    180   bool result = configurator->QueryContentProtectionStatus(
    181       GetClientId(), display_id_, link_mask, protection_mask);
    182 
    183   // If we successfully retrieved the device level status, check for capturers.
    184   if (result) {
    185     const bool capture_detected =
    186         // Check for tab capture on the current tab.
    187         content::WebContents::FromRenderFrameHost(rfh)->GetCapturerCount() >
    188             0 ||
    189         // Check for desktop capture.
    190         MediaCaptureDevicesDispatcher::GetInstance()
    191             ->IsDesktopCaptureInProgress();
    192     if (capture_detected)
    193       *link_mask |= ui::DISPLAY_CONNECTION_TYPE_NETWORK;
    194   }
    195 
    196   return result ? PP_OK : PP_ERROR_FAILED;
    197 }
    198 
    199 int32_t PepperOutputProtectionMessageFilter::Delegate::OnEnableProtection(
    200     uint32_t desired_method_mask) {
    201   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    202 
    203   ui::DisplayConfigurator* configurator =
    204       ash::Shell::GetInstance()->display_configurator();
    205   bool result = configurator->EnableContentProtection(
    206       GetClientId(), display_id_, desired_method_mask);
    207   desired_method_mask_ = desired_method_mask;
    208   return result ? PP_OK : PP_ERROR_FAILED;
    209 }
    210 
    211 void PepperOutputProtectionMessageFilter::Delegate::OnWindowHierarchyChanged(
    212     const aura::WindowObserver::HierarchyChangeParams& params) {
    213   content::RenderFrameHost* rfh =
    214       content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
    215   if (!rfh) {
    216     LOG(WARNING) << "RenderFrameHost is not alive.";
    217     return;
    218   }
    219 
    220   int64 new_display_id = 0;
    221   if (!GetCurrentDisplayId(rfh, &new_display_id))
    222     return;
    223   if (display_id_ == new_display_id)
    224     return;
    225 
    226   if (desired_method_mask_ != ui::CONTENT_PROTECTION_METHOD_NONE) {
    227     // Display changed and should enable output protections on new display.
    228     ui::DisplayConfigurator* configurator =
    229         ash::Shell::GetInstance()->display_configurator();
    230     configurator->EnableContentProtection(
    231         GetClientId(), new_display_id, desired_method_mask_);
    232     configurator->EnableContentProtection(
    233         GetClientId(), display_id_, ui::CONTENT_PROTECTION_METHOD_NONE);
    234   }
    235   display_id_ = new_display_id;
    236 }
    237 
    238 void PepperOutputProtectionMessageFilter::Delegate::OnWindowDestroying(
    239     aura::Window* window) {
    240   DCHECK_EQ(window, window_);
    241   window_->RemoveObserver(this);
    242   window_ = NULL;
    243 }
    244 #endif  // defined(OS_CHROMEOS)
    245 
    246 PepperOutputProtectionMessageFilter::PepperOutputProtectionMessageFilter(
    247     content::BrowserPpapiHost* host,
    248     PP_Instance instance) {
    249 #if defined(OS_CHROMEOS)
    250   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    251   int render_process_id = 0;
    252   int render_frame_id = 0;
    253   host->GetRenderFrameIDsForInstance(
    254       instance, &render_process_id, &render_frame_id);
    255   delegate_ = new Delegate(render_process_id, render_frame_id);
    256 #else
    257   NOTIMPLEMENTED();
    258 #endif
    259 }
    260 
    261 PepperOutputProtectionMessageFilter::~PepperOutputProtectionMessageFilter() {
    262 #if defined(OS_CHROMEOS)
    263   content::BrowserThread::DeleteSoon(
    264       content::BrowserThread::UI, FROM_HERE, delegate_);
    265   delegate_ = NULL;
    266 #endif
    267 }
    268 
    269 scoped_refptr<base::TaskRunner>
    270 PepperOutputProtectionMessageFilter::OverrideTaskRunnerForMessage(
    271     const IPC::Message& message) {
    272   return content::BrowserThread::GetMessageLoopProxyForThread(
    273       content::BrowserThread::UI);
    274 }
    275 
    276 int32_t PepperOutputProtectionMessageFilter::OnResourceMessageReceived(
    277     const IPC::Message& msg,
    278     ppapi::host::HostMessageContext* context) {
    279   PPAPI_BEGIN_MESSAGE_MAP(PepperOutputProtectionMessageFilter, msg)
    280     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
    281         PpapiHostMsg_OutputProtection_QueryStatus, OnQueryStatus);
    282     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
    283         PpapiHostMsg_OutputProtection_EnableProtection, OnEnableProtection);
    284   PPAPI_END_MESSAGE_MAP()
    285   return PP_ERROR_FAILED;
    286 }
    287 
    288 int32_t PepperOutputProtectionMessageFilter::OnQueryStatus(
    289     ppapi::host::HostMessageContext* context) {
    290 #if defined(OS_CHROMEOS)
    291   uint32_t link_mask = 0, protection_mask = 0;
    292   int32_t result = delegate_->OnQueryStatus(&link_mask, &protection_mask);
    293 
    294   ppapi::host::ReplyMessageContext reply_context =
    295       context->MakeReplyMessageContext();
    296   reply_context.params.set_result(result);
    297   SendReply(reply_context,
    298             PpapiPluginMsg_OutputProtection_QueryStatusReply(link_mask,
    299                                                              protection_mask));
    300   return PP_OK_COMPLETIONPENDING;
    301 #else
    302   NOTIMPLEMENTED();
    303   return PP_ERROR_NOTSUPPORTED;
    304 #endif
    305 }
    306 
    307 int32_t PepperOutputProtectionMessageFilter::OnEnableProtection(
    308     ppapi::host::HostMessageContext* context,
    309     uint32_t desired_method_mask) {
    310 #if defined(OS_CHROMEOS)
    311   ppapi::host::ReplyMessageContext reply_context =
    312       context->MakeReplyMessageContext();
    313   int32_t result = delegate_->OnEnableProtection(desired_method_mask);
    314   reply_context.params.set_result(result);
    315   SendReply(reply_context,
    316             PpapiPluginMsg_OutputProtection_EnableProtectionReply());
    317   return PP_OK_COMPLETIONPENDING;
    318 #else
    319   NOTIMPLEMENTED();
    320   return PP_ERROR_NOTSUPPORTED;
    321 #endif
    322 }
    323 
    324 }  // namespace chrome
    325