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