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 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 75 MidiPermissionRequest::~MidiPermissionRequest() { 76 DCHECK(is_finished_); 77 } 78 79 int MidiPermissionRequest::GetIconID() const { 80 return IDR_ALLOWED_MIDI_SYSEX; 81 } 82 83 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 89 base::string16 MidiPermissionRequest::GetMessageTextFragment() const { 90 return l10n_util::GetStringUTF16(IDS_MIDI_SYSEX_PERMISSION_FRAGMENT); 91 } 92 93 bool MidiPermissionRequest::HasUserGesture() const { 94 return user_gesture_; 95 } 96 97 GURL MidiPermissionRequest::GetRequestingHostname() const { 98 return requesting_frame_; 99 } 100 101 void MidiPermissionRequest::PermissionGranted() { 102 context_->NotifyPermissionSet(id_, requesting_frame_, callback_, true); 103 } 104 105 void MidiPermissionRequest::PermissionDenied() { 106 context_->NotifyPermissionSet(id_, requesting_frame_, callback_, false); 107 } 108 109 void MidiPermissionRequest::Cancelled() { 110 } 111 112 void MidiPermissionRequest::RequestFinished() { 113 is_finished_ = true; 114 // Deletes 'this'. 115 context_->RequestFinished(this); 116 } 117 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 125 MidiPermissionContext::~MidiPermissionContext() { 126 DCHECK(!permission_queue_controller_); 127 DCHECK(pending_requests_.empty()); 128 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 129 } 130 131 void MidiPermissionContext::Shutdown() { 132 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 133 permission_queue_controller_.reset(); 134 shutting_down_ = true; 135 } 136 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 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 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 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 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 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 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 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