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