pepper_output_protection_message_filter.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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