1// Copyright (c) 2012 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_flash_drm_host.h"
6
7#if defined(OS_WIN)
8#include <Windows.h>
9#endif
10
11#include "base/bind.h"
12#include "base/compiler_specific.h"
13#include "base/logging.h"
14#include "base/memory/ref_counted.h"
15#include "content/public/browser/browser_ppapi_host.h"
16#include "content/public/browser/browser_thread.h"
17#include "content/public/browser/child_process_security_policy.h"
18#include "content/public/browser/render_frame_host.h"
19#include "content/public/common/pepper_plugin_info.h"
20#include "ppapi/c/pp_errors.h"
21#include "ppapi/host/dispatch_host_message.h"
22#include "ppapi/host/host_message_context.h"
23#include "ppapi/host/ppapi_host.h"
24#include "ppapi/proxy/ppapi_messages.h"
25
26#if defined(USE_AURA)
27#include "ui/aura/window.h"
28#include "ui/aura/window_tree_host.h"
29#endif
30
31#if defined(OS_MACOSX)
32#include "chrome/browser/renderer_host/pepper/monitor_finder_mac.h"
33#endif
34
35using content::BrowserPpapiHost;
36
37namespace chrome {
38
39namespace {
40const char kVoucherFilename[] = "plugin.vch";
41}
42
43#if defined(OS_WIN)
44// Helper class to get the UI thread which monitor is showing the
45// window associated with the instance's render view. Since we get
46// called by the IO thread and we cannot block, the first answer is
47// of GetMonitor() may be NULL, but eventually it will contain the
48// right monitor.
49class MonitorFinder : public base::RefCountedThreadSafe<MonitorFinder> {
50 public:
51  MonitorFinder(int process_id, int render_frame_id)
52      : process_id_(process_id),
53        render_frame_id_(render_frame_id),
54        monitor_(NULL),
55        request_sent_(0) {}
56
57  int64_t GetMonitor() {
58    // We use |request_sent_| as an atomic boolean so that we
59    // never have more than one task posted at a given time. We
60    // do this because we don't know how often our client is going
61    // to call and we can't cache the |monitor_| value.
62    if (InterlockedCompareExchange(&request_sent_, 1, 0) == 0) {
63      content::BrowserThread::PostTask(
64          content::BrowserThread::UI,
65          FROM_HERE,
66          base::Bind(&MonitorFinder::FetchMonitorFromWidget, this));
67    }
68    return reinterpret_cast<int64_t>(monitor_);
69  }
70
71 private:
72  friend class base::RefCountedThreadSafe<MonitorFinder>;
73  ~MonitorFinder() {}
74
75  void FetchMonitorFromWidget() {
76    InterlockedExchange(&request_sent_, 0);
77    content::RenderFrameHost* rfh =
78        content::RenderFrameHost::FromID(process_id_, render_frame_id_);
79    if (!rfh)
80      return;
81    gfx::NativeView native_view = rfh->GetNativeView();
82#if defined(USE_AURA)
83    aura::WindowTreeHost* host = native_view->GetHost();
84    if (!host)
85      return;
86    HWND window = host->GetAcceleratedWidget();
87#else
88    HWND window = native_view;
89#endif
90    HMONITOR monitor = ::MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
91    InterlockedExchangePointer(reinterpret_cast<void* volatile*>(&monitor_),
92                               monitor);
93  }
94
95  const int process_id_;
96  const int render_frame_id_;
97  volatile HMONITOR monitor_;
98  volatile long request_sent_;
99};
100#elif !defined(OS_MACOSX)
101// TODO(cpu): Support Linux someday.
102class MonitorFinder : public base::RefCountedThreadSafe<MonitorFinder> {
103 public:
104  MonitorFinder(int, int) {}
105  int64_t GetMonitor() { return 0; }
106
107 private:
108  friend class base::RefCountedThreadSafe<MonitorFinder>;
109  ~MonitorFinder() {}
110};
111#endif
112
113PepperFlashDRMHost::PepperFlashDRMHost(BrowserPpapiHost* host,
114                                       PP_Instance instance,
115                                       PP_Resource resource)
116    : ppapi::host::ResourceHost(host->GetPpapiHost(), instance, resource),
117      weak_factory_(this) {
118  // Grant permissions to read the flash voucher file.
119  int render_process_id;
120  int render_frame_id;
121  bool success = host->GetRenderFrameIDsForInstance(
122      instance, &render_process_id, &render_frame_id);
123  base::FilePath plugin_dir = host->GetPluginPath().DirName();
124  DCHECK(!plugin_dir.empty() && success);
125  base::FilePath voucher_file = plugin_dir.AppendASCII(kVoucherFilename);
126  content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
127      render_process_id, voucher_file);
128
129  fetcher_ = new DeviceIDFetcher(render_process_id);
130  monitor_finder_ = new MonitorFinder(render_process_id, render_frame_id);
131  monitor_finder_->GetMonitor();
132}
133
134PepperFlashDRMHost::~PepperFlashDRMHost() {}
135
136int32_t PepperFlashDRMHost::OnResourceMessageReceived(
137    const IPC::Message& msg,
138    ppapi::host::HostMessageContext* context) {
139  PPAPI_BEGIN_MESSAGE_MAP(PepperFlashDRMHost, msg)
140    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FlashDRM_GetDeviceID,
141                                        OnHostMsgGetDeviceID)
142    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FlashDRM_GetHmonitor,
143                                        OnHostMsgGetHmonitor)
144    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_FlashDRM_MonitorIsExternal,
145                                        OnHostMsgMonitorIsExternal)
146  PPAPI_END_MESSAGE_MAP()
147  return PP_ERROR_FAILED;
148}
149
150int32_t PepperFlashDRMHost::OnHostMsgGetDeviceID(
151    ppapi::host::HostMessageContext* context) {
152  if (!fetcher_->Start(base::Bind(&PepperFlashDRMHost::GotDeviceID,
153                                  weak_factory_.GetWeakPtr(),
154                                  context->MakeReplyMessageContext()))) {
155    return PP_ERROR_INPROGRESS;
156  }
157  return PP_OK_COMPLETIONPENDING;
158}
159
160int32_t PepperFlashDRMHost::OnHostMsgGetHmonitor(
161    ppapi::host::HostMessageContext* context) {
162  int64_t monitor_id = monitor_finder_->GetMonitor();
163  if (monitor_id) {
164    context->reply_msg = PpapiPluginMsg_FlashDRM_GetHmonitorReply(monitor_id);
165    return PP_OK;
166  }
167  return PP_ERROR_FAILED;
168}
169
170int32_t PepperFlashDRMHost::OnHostMsgMonitorIsExternal(
171    ppapi::host::HostMessageContext* context) {
172  int64_t monitor_id = monitor_finder_->GetMonitor();
173  if (!monitor_id)
174    return PP_ERROR_FAILED;
175
176  PP_Bool is_external = PP_FALSE;
177#if defined(OS_MACOSX)
178  if (!MonitorFinder::IsMonitorBuiltIn(monitor_id))
179    is_external = PP_TRUE;
180#endif
181  context->reply_msg =
182      PpapiPluginMsg_FlashDRM_MonitorIsExternalReply(is_external);
183  return PP_OK;
184}
185
186void PepperFlashDRMHost::GotDeviceID(
187    ppapi::host::ReplyMessageContext reply_context,
188    const std::string& id,
189    int32_t result) {
190  if (id.empty() && result == PP_OK) {
191    NOTREACHED();
192    result = PP_ERROR_FAILED;
193  }
194  reply_context.params.set_result(result);
195  host()->SendReply(reply_context,
196                    PpapiPluginMsg_FlashDRM_GetDeviceIDReply(id));
197}
198
199}  // namespace chrome
200