// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/permissions/permission_manager.h" #include "base/callback.h" #include "chrome/browser/content_settings/permission_context_base.h" #include "chrome/browser/permissions/permission_context.h" #include "chrome/browser/profiles/profile.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/permission_request_id.h" #include "content/public/browser/permission_type.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" namespace { // Helper method to convert ContentSetting to content::PermissionStatus. content::PermissionStatus ContentSettingToPermissionStatus(ContentSetting setting) { switch (setting) { case CONTENT_SETTING_ALLOW: case CONTENT_SETTING_SESSION_ONLY: return content::PERMISSION_STATUS_GRANTED; case CONTENT_SETTING_BLOCK: return content::PERMISSION_STATUS_DENIED; case CONTENT_SETTING_ASK: return content::PERMISSION_STATUS_ASK; case CONTENT_SETTING_DETECT_IMPORTANT_CONTENT: case CONTENT_SETTING_DEFAULT: case CONTENT_SETTING_NUM_SETTINGS: break; } NOTREACHED(); return content::PERMISSION_STATUS_DENIED; } // Helper method to convert content::PermissionType to ContentSettingType. ContentSettingsType PermissionTypeToContentSetting( content::PermissionType permission) { switch (permission) { case content::PermissionType::MIDI_SYSEX: return CONTENT_SETTINGS_TYPE_MIDI_SYSEX; case content::PermissionType::PUSH_MESSAGING: return CONTENT_SETTINGS_TYPE_PUSH_MESSAGING; case content::PermissionType::NOTIFICATIONS: return CONTENT_SETTINGS_TYPE_NOTIFICATIONS; case content::PermissionType::GEOLOCATION: return CONTENT_SETTINGS_TYPE_GEOLOCATION; case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER: #if defined(OS_ANDROID) || defined(OS_CHROMEOS) return CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER; #else NOTIMPLEMENTED(); break; #endif case content::PermissionType::NUM: // This will hit the NOTREACHED below. break; } NOTREACHED() << "Unknown content setting for permission " << static_cast(permission); return CONTENT_SETTINGS_TYPE_DEFAULT; } // Helper method that wraps a callback a void(content::PermissionStatus) // callback into a void(ContentSetting) callback. void PermissionStatusCallbackWrapper( const base::Callback& callback, ContentSetting content_setting) { callback.Run(ContentSettingToPermissionStatus(content_setting)); } } // anonymous namespace struct PermissionManager::Subscription { content::PermissionType permission; GURL requesting_origin; GURL embedding_origin; base::Callback callback; ContentSetting current_value; }; PermissionManager::PermissionManager(Profile* profile) : profile_(profile) { } PermissionManager::~PermissionManager() { if (!subscriptions_.IsEmpty()) profile_->GetHostContentSettingsMap()->RemoveObserver(this); } void PermissionManager::RequestPermission( content::PermissionType permission, content::WebContents* web_contents, int request_id, const GURL& requesting_origin, bool user_gesture, const base::Callback& callback) { PermissionContextBase* context = PermissionContext::Get(profile_, permission); if (!context) { callback.Run(content::PERMISSION_STATUS_DENIED); return; } int render_process_id = web_contents->GetRenderProcessHost()->GetID(); int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID(); const PermissionRequestID request(render_process_id, render_view_id, request_id, requesting_origin); context->RequestPermission(web_contents, request, requesting_origin, user_gesture, base::Bind(&PermissionStatusCallbackWrapper, callback)); } void PermissionManager::CancelPermissionRequest( content::PermissionType permission, content::WebContents* web_contents, int request_id, const GURL& requesting_origin) { PermissionContextBase* context = PermissionContext::Get(profile_, permission); if (!context) return; int render_process_id = web_contents->GetRenderProcessHost()->GetID(); int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID(); const PermissionRequestID request(render_process_id, render_view_id, request_id, requesting_origin); context->CancelPermissionRequest(web_contents, request); } void PermissionManager::ResetPermission( content::PermissionType permission, const GURL& requesting_origin, const GURL& embedding_origin) { PermissionContextBase* context = PermissionContext::Get(profile_, permission); if (!context) return; context->ResetPermission(requesting_origin.GetOrigin(), embedding_origin.GetOrigin()); } content::PermissionStatus PermissionManager::GetPermissionStatus( content::PermissionType permission, const GURL& requesting_origin, const GURL& embedding_origin) { PermissionContextBase* context = PermissionContext::Get(profile_, permission); if (!context) return content::PERMISSION_STATUS_DENIED; return ContentSettingToPermissionStatus( context->GetPermissionStatus(requesting_origin.GetOrigin(), embedding_origin.GetOrigin())); } void PermissionManager::RegisterPermissionUsage( content::PermissionType permission, const GURL& requesting_origin, const GURL& embedding_origin) { profile_->GetHostContentSettingsMap()->UpdateLastUsage( requesting_origin, embedding_origin, PermissionTypeToContentSetting(permission)); } int PermissionManager::SubscribePermissionStatusChange( content::PermissionType permission, const GURL& requesting_origin, const GURL& embedding_origin, const base::Callback& callback) { if (subscriptions_.IsEmpty()) profile_->GetHostContentSettingsMap()->AddObserver(this); Subscription* subscription = new Subscription(); subscription->permission = permission; subscription->requesting_origin = requesting_origin; subscription->embedding_origin = embedding_origin; subscription->callback = callback; subscription->current_value = PermissionContext::Get(profile_, permission) ->GetPermissionStatus(subscription->requesting_origin, subscription->embedding_origin); return subscriptions_.Add(subscription); } void PermissionManager::UnsubscribePermissionStatusChange(int subscription_id) { // Whether |subscription_id| is known will be checked by the Remove() call. subscriptions_.Remove(subscription_id); if (subscriptions_.IsEmpty()) profile_->GetHostContentSettingsMap()->RemoveObserver(this); } void PermissionManager::OnContentSettingChanged( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, ContentSettingsType content_type, std::string resource_identifier) { std::list callbacks; for (SubscriptionsMap::iterator iter(&subscriptions_); !iter.IsAtEnd(); iter.Advance()) { Subscription* subscription = iter.GetCurrentValue(); if (PermissionTypeToContentSetting(subscription->permission) != content_type) { continue; } if (primary_pattern.IsValid() && !primary_pattern.Matches(subscription->requesting_origin)) continue; if (secondary_pattern.IsValid() && !secondary_pattern.Matches(subscription->embedding_origin)) continue; ContentSetting new_value = PermissionContext::Get(profile_, subscription->permission) ->GetPermissionStatus(subscription->requesting_origin, subscription->embedding_origin); if (subscription->current_value == new_value) continue; subscription->current_value = new_value; // Add the callback to |callbacks| which will be run after the loop to // prevent re-entrance issues. callbacks.push_back( base::Bind(subscription->callback, ContentSettingToPermissionStatus(new_value))); } for (const auto& callback : callbacks) callback.Run(); }