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