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/midi_permission_context.h"
6
7 #include "base/prefs/pref_service.h"
8 #include "chrome/browser/content_settings/host_content_settings_map.h"
9 #include "chrome/browser/content_settings/permission_queue_controller.h"
10 #include "chrome/browser/content_settings/permission_request_id.h"
11 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/tab_contents/tab_util.h"
14 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
15 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
16 #include "chrome/common/pref_names.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "grit/generated_resources.h"
22 #include "grit/theme_resources.h"
23 #include "net/base/net_util.h"
24 #include "ui/base/l10n/l10n_util.h"
25
26 class MidiPermissionRequest : public PermissionBubbleRequest {
27 public:
28 MidiPermissionRequest(
29 MidiPermissionContext* context,
30 const PermissionRequestID& id,
31 const GURL& requesting_frame,
32 bool user_gesture,
33 const std::string& display_languages,
34 const base::Callback<void(bool)>& callback);
35 virtual ~MidiPermissionRequest();
36
37 // PermissionBubbleDelegate:
38 virtual int GetIconID() const OVERRIDE;
39 virtual base::string16 GetMessageText() const OVERRIDE;
40 virtual base::string16 GetMessageTextFragment() const OVERRIDE;
41 virtual bool HasUserGesture() const OVERRIDE;
42 virtual GURL GetRequestingHostname() const OVERRIDE;
43 virtual void PermissionGranted() OVERRIDE;
44 virtual void PermissionDenied() OVERRIDE;
45 virtual void Cancelled() OVERRIDE;
46 virtual void RequestFinished() OVERRIDE;
47
48 private:
49 MidiPermissionContext* context_;
50 const PermissionRequestID id_;
51 GURL requesting_frame_;
52 bool user_gesture_;
53 std::string display_languages_;
54 const base::Callback<void(bool)>& callback_;
55 bool is_finished_;
56
57 DISALLOW_COPY_AND_ASSIGN(MidiPermissionRequest);
58 };
59
MidiPermissionRequest(MidiPermissionContext * context,const PermissionRequestID & id,const GURL & requesting_frame,bool user_gesture,const std::string & display_languages,const base::Callback<void (bool)> & callback)60 MidiPermissionRequest::MidiPermissionRequest(
61 MidiPermissionContext* context,
62 const PermissionRequestID& id,
63 const GURL& requesting_frame,
64 bool user_gesture,
65 const std::string& display_languages,
66 const base::Callback<void(bool)>& callback)
67 : context_(context),
68 id_(id),
69 requesting_frame_(requesting_frame),
70 user_gesture_(user_gesture),
71 display_languages_(display_languages),
72 callback_(callback),
73 is_finished_(false) {}
74
~MidiPermissionRequest()75 MidiPermissionRequest::~MidiPermissionRequest() {
76 DCHECK(is_finished_);
77 }
78
GetIconID() const79 int MidiPermissionRequest::GetIconID() const {
80 return IDR_ALLOWED_MIDI_SYSEX;
81 }
82
GetMessageText() const83 base::string16 MidiPermissionRequest::GetMessageText() const {
84 return l10n_util::GetStringFUTF16(
85 IDS_MIDI_SYSEX_INFOBAR_QUESTION,
86 net::FormatUrl(requesting_frame_.GetOrigin(), display_languages_));
87 }
88
GetMessageTextFragment() const89 base::string16 MidiPermissionRequest::GetMessageTextFragment() const {
90 return l10n_util::GetStringUTF16(IDS_MIDI_SYSEX_PERMISSION_FRAGMENT);
91 }
92
HasUserGesture() const93 bool MidiPermissionRequest::HasUserGesture() const {
94 return user_gesture_;
95 }
96
GetRequestingHostname() const97 GURL MidiPermissionRequest::GetRequestingHostname() const {
98 return requesting_frame_;
99 }
100
PermissionGranted()101 void MidiPermissionRequest::PermissionGranted() {
102 context_->NotifyPermissionSet(id_, requesting_frame_, callback_, true);
103 }
104
PermissionDenied()105 void MidiPermissionRequest::PermissionDenied() {
106 context_->NotifyPermissionSet(id_, requesting_frame_, callback_, false);
107 }
108
Cancelled()109 void MidiPermissionRequest::Cancelled() {
110 }
111
RequestFinished()112 void MidiPermissionRequest::RequestFinished() {
113 is_finished_ = true;
114 // Deletes 'this'.
115 context_->RequestFinished(this);
116 }
117
MidiPermissionContext(Profile * profile)118 MidiPermissionContext::MidiPermissionContext(Profile* profile)
119 : profile_(profile),
120 shutting_down_(false),
121 weak_factory_(this) {
122 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
123 }
124
~MidiPermissionContext()125 MidiPermissionContext::~MidiPermissionContext() {
126 DCHECK(!permission_queue_controller_);
127 DCHECK(pending_requests_.empty());
128 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
129 }
130
Shutdown()131 void MidiPermissionContext::Shutdown() {
132 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
133 permission_queue_controller_.reset();
134 shutting_down_ = true;
135 }
136
RequestMidiSysExPermission(content::WebContents * web_contents,int bridge_id,const GURL & requesting_frame,bool user_gesture,const base::Callback<void (bool)> & result_callback,base::Closure * cancel_callback)137 void MidiPermissionContext::RequestMidiSysExPermission(
138 content::WebContents* web_contents,
139 int bridge_id,
140 const GURL& requesting_frame,
141 bool user_gesture,
142 const base::Callback<void(bool)>& result_callback,
143 base::Closure* cancel_callback) {
144 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
145 DCHECK(!shutting_down_);
146
147 // TODO(toyoshim): Support Extension's manifest declared permission.
148 // See http://crbug.com/266338.
149
150 int render_process_id = web_contents->GetRenderProcessHost()->GetID();
151 int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
152 if (cancel_callback) {
153 *cancel_callback = base::Bind(
154 &MidiPermissionContext::CancelMidiSysExPermissionRequest,
155 weak_factory_.GetWeakPtr(), render_process_id, render_view_id,
156 bridge_id);
157 }
158
159 const PermissionRequestID id(
160 render_process_id, render_view_id, bridge_id, GURL());
161
162 GURL embedder = web_contents->GetURL();
163 // |requesting_frame| can be empty and invalid when the frame is a local
164 // file. Here local files should be granted to show an infobar.
165 // Any user's action will not be stored to content settings data base.
166 if ((!requesting_frame.is_valid() && !requesting_frame.is_empty()) ||
167 !embedder.is_valid()) {
168 LOG(WARNING) << "Attempt to use MIDI sysex from an invalid URL: "
169 << requesting_frame << "," << embedder
170 << " (Web MIDI is not supported in popups)";
171 PermissionDecided(id, requesting_frame, embedder, result_callback, false);
172 return;
173 }
174
175 DecidePermission(web_contents, id, requesting_frame, embedder, user_gesture,
176 result_callback);
177 }
178
CancelMidiSysExPermissionRequest(int render_process_id,int render_view_id,int bridge_id)179 void MidiPermissionContext::CancelMidiSysExPermissionRequest(
180 int render_process_id,
181 int render_view_id,
182 int bridge_id) {
183 CancelPendingInfobarRequest(
184 PermissionRequestID(
185 render_process_id, render_view_id, bridge_id, GURL()));
186 }
187
DecidePermission(content::WebContents * web_contents,const PermissionRequestID & id,const GURL & requesting_frame,const GURL & embedder,bool user_gesture,const base::Callback<void (bool)> & callback)188 void MidiPermissionContext::DecidePermission(
189 content::WebContents* web_contents,
190 const PermissionRequestID& id,
191 const GURL& requesting_frame,
192 const GURL& embedder,
193 bool user_gesture,
194 const base::Callback<void(bool)>& callback) {
195 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
196
197 ContentSetting content_setting =
198 profile_->GetHostContentSettingsMap()->GetContentSetting(
199 requesting_frame,
200 embedder,
201 CONTENT_SETTINGS_TYPE_MIDI_SYSEX,
202 std::string());
203 switch (content_setting) {
204 case CONTENT_SETTING_BLOCK:
205 PermissionDecided(id, requesting_frame, embedder, callback, false);
206 break;
207 case CONTENT_SETTING_ALLOW:
208 PermissionDecided(id, requesting_frame, embedder, callback, true);
209 break;
210 default:
211 if (PermissionBubbleManager::Enabled()) {
212 PermissionBubbleManager* bubble_manager =
213 PermissionBubbleManager::FromWebContents(web_contents);
214 if (bubble_manager) {
215 scoped_ptr<MidiPermissionRequest> request_ptr(
216 new MidiPermissionRequest(
217 this, id, requesting_frame, user_gesture,
218 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages),
219 callback));
220 MidiPermissionRequest* request = request_ptr.get();
221 bool inserted = pending_requests_.add(
222 id.ToString(), request_ptr.Pass()).second;
223 DCHECK(inserted) << "Duplicate id " << id.ToString();
224 bubble_manager->AddRequest(request);
225 }
226 return;
227 }
228
229 // TODO(gbillock): Delete this and the infobar delegate when
230 // we're using only bubbles. crbug.com/337458
231 GetQueueController()->CreateInfoBarRequest(
232 id, requesting_frame, embedder, std::string(), base::Bind(
233 &MidiPermissionContext::NotifyPermissionSet,
234 base::Unretained(this), id, requesting_frame, callback));
235 }
236 }
237
PermissionDecided(const PermissionRequestID & id,const GURL & requesting_frame,const GURL & embedder,const base::Callback<void (bool)> & callback,bool allowed)238 void MidiPermissionContext::PermissionDecided(
239 const PermissionRequestID& id,
240 const GURL& requesting_frame,
241 const GURL& embedder,
242 const base::Callback<void(bool)>& callback,
243 bool allowed) {
244 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
245 NotifyPermissionSet(id, requesting_frame, callback, allowed);
246 }
247
NotifyPermissionSet(const PermissionRequestID & id,const GURL & requesting_frame,const base::Callback<void (bool)> & callback,bool allowed)248 void MidiPermissionContext::NotifyPermissionSet(
249 const PermissionRequestID& id,
250 const GURL& requesting_frame,
251 const base::Callback<void(bool)>& callback,
252 bool allowed) {
253 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
254
255 TabSpecificContentSettings* content_settings =
256 TabSpecificContentSettings::Get(id.render_process_id(),
257 id.render_view_id());
258 if (content_settings) {
259 if (allowed)
260 content_settings->OnMidiSysExAccessed(requesting_frame);
261 else
262 content_settings->OnMidiSysExAccessBlocked(requesting_frame);
263 }
264
265 callback.Run(allowed);
266 }
267
GetQueueController()268 PermissionQueueController* MidiPermissionContext::GetQueueController() {
269 if (!permission_queue_controller_) {
270 permission_queue_controller_.reset(
271 new PermissionQueueController(profile_,
272 CONTENT_SETTINGS_TYPE_MIDI_SYSEX));
273 }
274 return permission_queue_controller_.get();
275 }
276
RequestFinished(MidiPermissionRequest * request)277 void MidiPermissionContext::RequestFinished(
278 MidiPermissionRequest* request) {
279 base::ScopedPtrHashMap<std::string, MidiPermissionRequest>::iterator it;
280 for (it = pending_requests_.begin(); it != pending_requests_.end(); it++) {
281 if (it->second == request) {
282 pending_requests_.take_and_erase(it);
283 return;
284 }
285 }
286
287 NOTREACHED() << "Missing request";
288 }
289
CancelPendingInfobarRequest(const PermissionRequestID & id)290 void MidiPermissionContext::CancelPendingInfobarRequest(
291 const PermissionRequestID& id) {
292 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
293 if (shutting_down_)
294 return;
295
296 if (PermissionBubbleManager::Enabled()) {
297 MidiPermissionRequest* cancelling = pending_requests_.get(id.ToString());
298 content::WebContents* web_contents = tab_util::GetWebContentsByID(
299 id.render_process_id(), id.render_view_id());
300 if (cancelling != NULL && web_contents != NULL &&
301 PermissionBubbleManager::FromWebContents(web_contents) != NULL) {
302 PermissionBubbleManager::FromWebContents(web_contents)->
303 CancelRequest(cancelling);
304 }
305 return;
306 }
307
308 GetQueueController()->CancelInfoBarRequest(id);
309 }
310