pepper_output_protection_message_filter.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
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 122PepperOutputProtectionMessageFilter::Delegate::~Delegate() { 123 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 124 125 ui::DisplayConfigurator* configurator = 126 ash::Shell::GetInstance()->display_configurator(); 127 configurator->UnregisterContentProtectionClient(client_id_); 128 129 content::RenderFrameHost* rfh = 130 content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); 131 if (rfh) { 132 gfx::NativeView native_view = rfh->GetNativeView(); 133 if (native_view) 134 native_view->RemoveObserver(this); 135 } 136} 137 138ui::DisplayConfigurator::ContentProtectionClientId 139PepperOutputProtectionMessageFilter::Delegate::GetClientId() { 140 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 141 if (client_id_ == ui::DisplayConfigurator::kInvalidClientId) { 142 content::RenderFrameHost* rfh = 143 content::RenderFrameHost::FromID(render_process_id_, render_frame_id_); 144 if (!GetCurrentDisplayId(rfh, &display_id_)) 145 return ui::DisplayConfigurator::kInvalidClientId; 146 gfx::NativeView native_view = rfh->GetNativeView(); 147 if (!native_view) 148 return ui::DisplayConfigurator::kInvalidClientId; 149 native_view->AddObserver(this); 150 151 ui::DisplayConfigurator* configurator = 152 ash::Shell::GetInstance()->display_configurator(); 153 client_id_ = configurator->RegisterContentProtectionClient(); 154 } 155 return client_id_; 156} 157 158int32_t PepperOutputProtectionMessageFilter::Delegate::OnQueryStatus( 159 uint32_t* link_mask, 160 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)->GetCapturerCount() > 180 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( 249 content::BrowserThread::UI, FROM_HERE, 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(PpapiHostMsg_OutputProtection_QueryStatus, 266 OnQueryStatus); 267 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 268 PpapiHostMsg_OutputProtection_EnableProtection, OnEnableProtection); 269 IPC_END_MESSAGE_MAP() 270 return PP_ERROR_FAILED; 271} 272 273int32_t PepperOutputProtectionMessageFilter::OnQueryStatus( 274 ppapi::host::HostMessageContext* context) { 275#if defined(OS_CHROMEOS) 276 uint32_t link_mask = 0, protection_mask = 0; 277 int32_t result = delegate_->OnQueryStatus(&link_mask, &protection_mask); 278 279 ppapi::host::ReplyMessageContext reply_context = 280 context->MakeReplyMessageContext(); 281 reply_context.params.set_result(result); 282 SendReply(reply_context, 283 PpapiPluginMsg_OutputProtection_QueryStatusReply(link_mask, 284 protection_mask)); 285 return PP_OK_COMPLETIONPENDING; 286#else 287 NOTIMPLEMENTED(); 288 return PP_ERROR_NOTSUPPORTED; 289#endif 290} 291 292int32_t PepperOutputProtectionMessageFilter::OnEnableProtection( 293 ppapi::host::HostMessageContext* context, 294 uint32_t desired_method_mask) { 295#if defined(OS_CHROMEOS) 296 ppapi::host::ReplyMessageContext reply_context = 297 context->MakeReplyMessageContext(); 298 int32_t result = delegate_->OnEnableProtection(desired_method_mask); 299 reply_context.params.set_result(result); 300 SendReply(reply_context, 301 PpapiPluginMsg_OutputProtection_EnableProtectionReply()); 302 return PP_OK_COMPLETIONPENDING; 303#else 304 NOTIMPLEMENTED(); 305 return PP_ERROR_NOTSUPPORTED; 306#endif 307} 308 309} // namespace chrome 310