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