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