Home | History | Annotate | Download | only in media
      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