pepper_output_protection_message_filter.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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 28namespace chrome { 29 30namespace { 31 32#if defined(OS_CHROMEOS) 33COMPILE_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); 36COMPILE_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); 40COMPILE_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); 44COMPILE_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); 47COMPILE_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); 50COMPILE_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); 53COMPILE_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); 57COMPILE_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); 61COMPILE_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); 64COMPILE_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 68bool 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. 85class 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 95 int32_t OnQueryStatus(uint32_t* link_mask, uint32_t* protection_mask); 96 int32_t OnEnableProtection(uint32_t desired_method_mask); 97 98 private: 99 ui::DisplayConfigurator::ContentProtectionClientId GetClientId(); 100 101 // Used to lookup the WebContents associated with this PP_Instance. 102 int render_process_id_; 103 int render_frame_id_; 104 105 ui::DisplayConfigurator::ContentProtectionClientId client_id_; 106 // The display id which the renderer currently uses. 107 int64 display_id_; 108 // The last desired method mask. Will enable this mask on new display if 109 // renderer changes display. 110 uint32_t desired_method_mask_; 111}; 112 113PepperOutputProtectionMessageFilter::Delegate::Delegate(int render_process_id, 114 int render_frame_id) 115 : render_process_id_(render_process_id), 116 render_frame_id_(render_frame_id), 117 client_id_(ui::DisplayConfigurator::kInvalidClientId), 118 display_id_(0) { 119 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 120 121} 122 123PepperOutputProtectionMessageFilter::Delegate::~Delegate() { 124 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 125 126 ui::DisplayConfigurator* configurator = 127 ash::Shell::GetInstance()->display_configurator(); 128 configurator->UnregisterContentProtectionClient(client_id_); 129 130 content::RenderFrameHost* rfh = 131 content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); 132 if (rfh) { 133 gfx::NativeView native_view = rfh->GetNativeView(); 134 if (native_view) 135 native_view->RemoveObserver(this); 136 } 137} 138 139ui::DisplayConfigurator::ContentProtectionClientId 140PepperOutputProtectionMessageFilter::Delegate::GetClientId() { 141 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 142 if (client_id_ == ui::DisplayConfigurator::kInvalidClientId) { 143 content::RenderFrameHost* rfh = 144 content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); 145 if (!GetCurrentDisplayId(rfh, &display_id_)) 146 return ui::DisplayConfigurator::kInvalidClientId; 147 gfx::NativeView native_view = rfh->GetNativeView(); 148 if (!native_view) 149 return ui::DisplayConfigurator::kInvalidClientId; 150 native_view->AddObserver(this); 151 152 ui::DisplayConfigurator* configurator = 153 ash::Shell::GetInstance()->display_configurator(); 154 client_id_ = configurator->RegisterContentProtectionClient(); 155 } 156 return client_id_; 157} 158 159int32_t PepperOutputProtectionMessageFilter::Delegate::OnQueryStatus( 160 uint32_t* link_mask, uint32_t* protection_mask) { 161 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 162 163 content::RenderFrameHost* rfh = 164 content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); 165 if (!rfh) { 166 LOG(WARNING) << "RenderFrameHost is not alive."; 167 return PP_ERROR_FAILED; 168 } 169 170 ui::DisplayConfigurator* configurator = 171 ash::Shell::GetInstance()->display_configurator(); 172 bool result = configurator->QueryContentProtectionStatus( 173 GetClientId(), display_id_, link_mask, protection_mask); 174 175 // If we successfully retrieved the device level status, check for capturers. 176 if (result) { 177 const bool capture_detected = 178 // Check for tab capture on the current tab. 179 content::WebContents::FromRenderFrameHost(rfh)-> 180 GetCapturerCount() > 0 || 181 // Check for desktop capture. 182 MediaCaptureDevicesDispatcher::GetInstance() 183 ->IsDesktopCaptureInProgress(); 184 if (capture_detected) 185 *link_mask |= ui::DISPLAY_CONNECTION_TYPE_NETWORK; 186 } 187 188 return result ? PP_OK : PP_ERROR_FAILED; 189} 190 191int32_t PepperOutputProtectionMessageFilter::Delegate::OnEnableProtection( 192 uint32_t desired_method_mask) { 193 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 194 195 ui::DisplayConfigurator* configurator = 196 ash::Shell::GetInstance()->display_configurator(); 197 bool result = configurator->EnableContentProtection( 198 GetClientId(), display_id_, desired_method_mask); 199 desired_method_mask_ = desired_method_mask; 200 return result ? PP_OK : PP_ERROR_FAILED; 201} 202 203void PepperOutputProtectionMessageFilter::Delegate::OnWindowHierarchyChanged( 204 const aura::WindowObserver::HierarchyChangeParams& params) { 205 content::RenderFrameHost* rfh = 206 content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); 207 if (!rfh) { 208 LOG(WARNING) << "RenderFrameHost is not alive."; 209 return; 210 } 211 212 int64 new_display_id = 0; 213 if (!GetCurrentDisplayId(rfh, &new_display_id)) 214 return; 215 if (display_id_ == new_display_id) 216 return; 217 218 if (desired_method_mask_ != ui::CONTENT_PROTECTION_METHOD_NONE) { 219 // Display changed and should enable output protections on new display. 220 ui::DisplayConfigurator* configurator = 221 ash::Shell::GetInstance()->display_configurator(); 222 configurator->EnableContentProtection( 223 GetClientId(), new_display_id, desired_method_mask_); 224 configurator->EnableContentProtection( 225 GetClientId(), display_id_, ui::CONTENT_PROTECTION_METHOD_NONE); 226 } 227 display_id_ = new_display_id; 228} 229#endif 230 231PepperOutputProtectionMessageFilter::PepperOutputProtectionMessageFilter( 232 content::BrowserPpapiHost* host, 233 PP_Instance instance) { 234#if defined(OS_CHROMEOS) 235 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 236 int render_process_id = 0; 237 int render_frame_id = 0; 238 host->GetRenderFrameIDsForInstance( 239 instance, &render_process_id, &render_frame_id); 240 delegate_ = new Delegate(render_process_id, render_frame_id); 241#else 242 NOTIMPLEMENTED(); 243#endif 244} 245 246PepperOutputProtectionMessageFilter::~PepperOutputProtectionMessageFilter() { 247#if defined(OS_CHROMEOS) 248 content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE, 249 delegate_); 250 delegate_ = NULL; 251#endif 252} 253 254scoped_refptr<base::TaskRunner> 255PepperOutputProtectionMessageFilter::OverrideTaskRunnerForMessage( 256 const IPC::Message& message) { 257 return content::BrowserThread::GetMessageLoopProxyForThread( 258 content::BrowserThread::UI); 259} 260 261int32_t PepperOutputProtectionMessageFilter::OnResourceMessageReceived( 262 const IPC::Message& msg, 263 ppapi::host::HostMessageContext* context) { 264 IPC_BEGIN_MESSAGE_MAP(PepperOutputProtectionMessageFilter, msg) 265 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( 266 PpapiHostMsg_OutputProtection_QueryStatus, 267 OnQueryStatus); 268 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 269 PpapiHostMsg_OutputProtection_EnableProtection, 270 OnEnableProtection); 271 IPC_END_MESSAGE_MAP() 272 return PP_ERROR_FAILED; 273} 274 275int32_t PepperOutputProtectionMessageFilter::OnQueryStatus( 276 ppapi::host::HostMessageContext* context) { 277#if defined(OS_CHROMEOS) 278 uint32_t link_mask = 0, protection_mask = 0; 279 int32_t result = delegate_->OnQueryStatus(&link_mask, &protection_mask); 280 281 ppapi::host::ReplyMessageContext reply_context = 282 context->MakeReplyMessageContext(); 283 reply_context.params.set_result(result); 284 SendReply( 285 reply_context, 286 PpapiPluginMsg_OutputProtection_QueryStatusReply( 287 link_mask, protection_mask)); 288 return PP_OK_COMPLETIONPENDING; 289#else 290 NOTIMPLEMENTED(); 291 return PP_ERROR_NOTSUPPORTED; 292#endif 293} 294 295int32_t PepperOutputProtectionMessageFilter::OnEnableProtection( 296 ppapi::host::HostMessageContext* context, 297 uint32_t desired_method_mask) { 298#if defined(OS_CHROMEOS) 299 ppapi::host::ReplyMessageContext reply_context = 300 context->MakeReplyMessageContext(); 301 int32_t result = delegate_->OnEnableProtection(desired_method_mask); 302 reply_context.params.set_result(result); 303 SendReply( 304 reply_context, 305 PpapiPluginMsg_OutputProtection_EnableProtectionReply()); 306 return PP_OK_COMPLETIONPENDING; 307#else 308 NOTIMPLEMENTED(); 309 return PP_ERROR_NOTSUPPORTED; 310#endif 311} 312 313} // namespace chrome 314 315