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/media/protected_media_identifier_permission_context.h"
6
7#include <functional>
8#include <string>
9#include <vector>
10
11#include "base/bind.h"
12#include "base/prefs/pref_service.h"
13#include "base/strings/utf_string_conversions.h"
14#include "chrome/browser/content_settings/host_content_settings_map.h"
15#include "chrome/browser/content_settings/tab_specific_content_settings.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/tab_contents/tab_util.h"
18#include "chrome/common/pref_names.h"
19#include "components/content_settings/core/common/permission_request_id.h"
20#include "content/public/browser/browser_thread.h"
21#include "content/public/browser/render_process_host.h"
22#include "content/public/browser/render_view_host.h"
23#include "content/public/browser/web_contents.h"
24
25#if defined(ENABLE_EXTENSIONS)
26#include "chrome/browser/extensions/extension_service.h"
27#include "extensions/browser/extension_system.h"
28#include "extensions/browser/suggest_permission_util.h"
29#include "extensions/browser/view_type_utils.h"
30#include "extensions/common/extension.h"
31
32using extensions::APIPermission;
33#endif
34
35ProtectedMediaIdentifierPermissionContext::
36    ProtectedMediaIdentifierPermissionContext(Profile* profile)
37    : profile_(profile), shutting_down_(false) {}
38
39ProtectedMediaIdentifierPermissionContext::
40    ~ProtectedMediaIdentifierPermissionContext() {
41  // ProtectedMediaIdentifierPermissionContext may be destroyed on either
42  // the UI thread or the IO thread, but the PermissionQueueController must have
43  // been destroyed on the UI thread.
44  DCHECK(!permission_queue_controller_.get());
45}
46
47void ProtectedMediaIdentifierPermissionContext::
48    RequestProtectedMediaIdentifierPermission(
49        content::WebContents* web_contents,
50        const GURL& origin,
51        base::Callback<void(bool)> result_callback,
52        base::Closure* cancel_callback) {
53  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
54  if (shutting_down_)
55    return;
56
57  int render_process_id = web_contents->GetRenderProcessHost()->GetID();
58  int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
59  if (cancel_callback) {
60    *cancel_callback = base::Bind(
61        &ProtectedMediaIdentifierPermissionContext::
62            CancelProtectedMediaIdentifierPermissionRequests,
63        this, render_process_id, render_view_id, origin);
64  }
65
66  const PermissionRequestID id(
67      render_process_id, render_view_id, 0, origin);
68
69#if defined(ENABLE_EXTENSIONS)
70  if (extensions::GetViewType(web_contents) !=
71      extensions::VIEW_TYPE_TAB_CONTENTS) {
72    // The tab may have gone away, or the request may not be from a tab at all.
73    LOG(WARNING)
74        << "Attempt to use protected media identifier in tabless renderer: "
75        << id.ToString()
76        << " (can't prompt user without a visible tab)";
77    NotifyPermissionSet(id, origin, result_callback, false);
78    return;
79  }
80#endif
81
82  GURL embedder = web_contents->GetLastCommittedURL();
83  if (!origin.is_valid() || !embedder.is_valid()) {
84    LOG(WARNING)
85        << "Attempt to use protected media identifier from an invalid URL: "
86        << origin << "," << embedder
87        << " (proteced media identifier is not supported in popups)";
88    NotifyPermissionSet(id, origin, result_callback, false);
89    return;
90  }
91
92  content::RenderViewHost* rvh = web_contents->GetRenderViewHost();
93  DecidePermission(id, origin, embedder, rvh, result_callback);
94}
95
96void ProtectedMediaIdentifierPermissionContext::
97    CancelProtectedMediaIdentifierPermissionRequests(
98        int render_process_id,
99        int render_view_id,
100        const GURL& origin) {
101  CancelPendingInfobarRequests(
102      render_process_id, render_view_id, origin);
103}
104
105void ProtectedMediaIdentifierPermissionContext::DecidePermission(
106    const PermissionRequestID& id,
107    const GURL& origin,
108    const GURL& embedder,
109    content::RenderViewHost* rvh,
110    const base::Callback<void(bool)>& callback) {
111  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
112
113#if defined(OS_ANDROID)
114  // Check if the protected media identifier master switch is disabled.
115  if (!profile()->GetPrefs()->GetBoolean(
116        prefs::kProtectedMediaIdentifierEnabled)) {
117    PermissionDecided(id, origin, embedder, callback, false);
118    return;
119  }
120#endif
121
122  ContentSetting content_setting =
123     profile_->GetHostContentSettingsMap()->GetContentSetting(
124          origin,
125          embedder,
126          CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER,
127          std::string());
128  switch (content_setting) {
129    case CONTENT_SETTING_BLOCK:
130      PermissionDecided(id, origin, embedder, callback, false);
131      break;
132    case CONTENT_SETTING_ALLOW:
133      PermissionDecided(id, origin, embedder, callback, true);
134      break;
135    case CONTENT_SETTING_ASK:
136      QueueController()->CreateInfoBarRequest(
137          id,
138          origin,
139          embedder,
140          base::Bind(&ProtectedMediaIdentifierPermissionContext::
141                          NotifyPermissionSet,
142                     base::Unretained(this),
143                     id,
144                     origin,
145                     callback));
146      break;
147    default:
148      NOTREACHED();
149  }
150}
151
152void ProtectedMediaIdentifierPermissionContext::ShutdownOnUIThread() {
153  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
154  permission_queue_controller_.reset();
155  shutting_down_ = true;
156}
157
158void ProtectedMediaIdentifierPermissionContext::PermissionDecided(
159    const PermissionRequestID& id,
160    const GURL& origin,
161    const GURL& embedder,
162    const base::Callback<void(bool)>& callback,
163    bool allowed) {
164  NotifyPermissionSet(id, origin, callback, allowed);
165}
166
167void ProtectedMediaIdentifierPermissionContext::NotifyPermissionSet(
168    const PermissionRequestID& id,
169    const GURL& origin,
170    const base::Callback<void(bool)>& callback,
171    bool allowed) {
172  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
173
174  // WebContents may have gone away.
175  TabSpecificContentSettings* content_settings =
176      TabSpecificContentSettings::Get(id.render_process_id(),
177                                      id.render_view_id());
178  if (content_settings) {
179    content_settings->OnProtectedMediaIdentifierPermissionSet(
180        origin.GetOrigin(), allowed);
181  }
182
183  callback.Run(allowed);
184}
185
186PermissionQueueController*
187    ProtectedMediaIdentifierPermissionContext::QueueController() {
188  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
189  DCHECK(!shutting_down_);
190  if (!permission_queue_controller_)
191    permission_queue_controller_.reset(CreateQueueController());
192  return permission_queue_controller_.get();
193}
194
195PermissionQueueController*
196    ProtectedMediaIdentifierPermissionContext::CreateQueueController() {
197  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
198  return new PermissionQueueController(
199      profile(), CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER);
200}
201
202void
203ProtectedMediaIdentifierPermissionContext::CancelPendingInfobarRequests(
204    int render_process_id,
205    int render_view_id,
206    const GURL& origin) {
207  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
208    content::BrowserThread::PostTask(
209        content::BrowserThread::UI,
210        FROM_HERE,
211        base::Bind(&ProtectedMediaIdentifierPermissionContext::
212                        CancelPendingInfobarRequests,
213                   this,
214                   render_process_id,
215                   render_view_id,
216                   origin));
217    return;
218  }
219  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
220  if (shutting_down_)
221    return;
222  QueueController()->CancelInfoBarRequest(
223      PermissionRequestID(render_process_id, render_view_id, 0,
224                          origin));
225}
226