pepper_output_protection_message_filter.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 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 119PepperOutputProtectionMessageFilter::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 134PepperOutputProtectionMessageFilter::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 145ui::DisplayConfigurator::ContentProtectionClientId 146PepperOutputProtectionMessageFilter::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 166int32_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 199int32_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 211void 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 238void 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 246PepperOutputProtectionMessageFilter::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 261PepperOutputProtectionMessageFilter::~PepperOutputProtectionMessageFilter() { 262#if defined(OS_CHROMEOS) 263 content::BrowserThread::DeleteSoon( 264 content::BrowserThread::UI, FROM_HERE, delegate_); 265 delegate_ = NULL; 266#endif 267} 268 269scoped_refptr<base::TaskRunner> 270PepperOutputProtectionMessageFilter::OverrideTaskRunnerForMessage( 271 const IPC::Message& message) { 272 return content::BrowserThread::GetMessageLoopProxyForThread( 273 content::BrowserThread::UI); 274} 275 276int32_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 288int32_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 307int32_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