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