blob: f1aede3d011fbb7cbf96a5c66c30d4bc8510c3d6 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/site_permissions_helper.h"
#include "chrome/browser/extensions/extension_action_runner.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "components/sessions/content/session_tab_helper.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/common/extension.h"
#include "url/gurl.h"
namespace extensions {
namespace {
// A preference indicating if the extension can show site access requests
// directly in the toolbar next to the omnibox.
constexpr const char kPrefShowAccessRequestsInToolbar[] =
"show_access_requests_in_toolbar";
} // namespace
SitePermissionsHelper::SitePermissionsHelper(Profile* profile)
: profile_(profile) {}
SitePermissionsHelper::~SitePermissionsHelper() = default;
SitePermissionsHelper::SiteAccess SitePermissionsHelper::GetSiteAccess(
const Extension& extension,
const GURL& gurl) const {
DCHECK(
!extension.permissions_data()->IsRestrictedUrl(gurl, /*error=*/nullptr));
PermissionsManager* permissions_manager = PermissionsManager::Get(profile_);
// Extension with no host permissions but with active tab permission has "on
// click" access.
if (!permissions_manager->CanAffectExtension(extension) &&
HasActiveTabAndCanAccess(extension, gurl))
return SiteAccess::kOnClick;
DCHECK(permissions_manager->CanAffectExtension(extension));
PermissionsManager::ExtensionSiteAccess site_access =
permissions_manager->GetSiteAccess(extension, gurl);
if (site_access.has_all_sites_access)
return SiteAccess::kOnAllSites;
if (site_access.has_site_access)
return SiteAccess::kOnSite;
return SiteAccess::kOnClick;
}
SitePermissionsHelper::SiteInteraction
SitePermissionsHelper::GetSiteInteraction(
const Extension& extension,
content::WebContents* web_contents) const {
if (!web_contents)
return SiteInteraction::kNone;
const int tab_id = sessions::SessionTabHelper::IdForTab(web_contents).id();
const GURL& url = web_contents->GetLastCommittedURL();
PermissionsData::PageAccess page_access =
extension.permissions_data()->GetPageAccess(url, tab_id,
/*error=*/nullptr);
PermissionsData::PageAccess script_access =
extension.permissions_data()->GetContentScriptAccess(url, tab_id,
/*error=*/nullptr);
if (page_access == PermissionsData::PageAccess::kAllowed ||
script_access == PermissionsData::PageAccess::kAllowed) {
return SiteInteraction::kGranted;
}
// An extension can request both host permissions and activeTab permission.
// Withholding a host permission takes priority over activeTab permission,
// because withheld hosts are hosts that the extension explicitly marked as
// 'required' permissions, so it is a stronger signal that the extension
// should run on the site. ActiveTab extensions, by contrast, are designed to
// run when the user explicitly invokes them.
// TODO(tjudkins): Investigate if we need to check HasBeenBlocked() for this
// case. We do know that extensions that have been blocked should always be
// marked pending, but those cases should be covered by the withheld page
// access checks.
if (page_access == PermissionsData::PageAccess::kWithheld ||
script_access == PermissionsData::PageAccess::kWithheld ||
HasBeenBlocked(extension, web_contents)) {
return SiteInteraction::kWithheld;
}
if (HasActiveTabAndCanAccess(extension, url)) {
return SiteInteraction::kActiveTab;
}
return SiteInteraction::kNone;
}
void SitePermissionsHelper::UpdateSiteAccess(
const Extension& extension,
content::WebContents* web_contents,
SitePermissionsHelper::SiteAccess new_access) {
ExtensionActionRunner* runner =
ExtensionActionRunner::GetForWebContents(web_contents);
if (!runner)
return;
auto current_access =
GetSiteAccess(extension, web_contents->GetLastCommittedURL());
if (new_access == current_access)
return;
runner->HandlePageAccessModified(&extension, current_access, new_access);
}
void SitePermissionsHelper::UpdateUserSiteSettings(
const base::flat_set<ToolbarActionsModel::ActionId>& action_ids,
content::WebContents* web_contents,
extensions::PermissionsManager::UserSiteSetting site_setting) {
DCHECK(web_contents);
ExtensionActionRunner* runner =
ExtensionActionRunner::GetForWebContents(web_contents);
if (!runner)
return;
runner->HandleUserSiteSettingModified(
action_ids, web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(),
site_setting);
}
bool SitePermissionsHelper::CanSelectSiteAccess(const Extension& extension,
const GURL& url,
SiteAccess site_access) const {
// Extensions cannot run on sites restricted to them (ever), so no type of
// site access is selectable.
if (extension.permissions_data()->IsRestrictedUrl(url, /*error=*/nullptr))
return false;
// The "on click" option is enabled if the extension has active tab,
// regardless of its granted host permissions.
if (site_access == SitePermissionsHelper::SiteAccess::kOnClick &&
HasActiveTabAndCanAccess(extension, url))
return true;
PermissionsManager* permissions_manager = PermissionsManager::Get(profile_);
if (!permissions_manager->CanAffectExtension(extension))
return false;
PermissionsManager::ExtensionSiteAccess extension_access =
permissions_manager->GetSiteAccess(extension, url);
switch (site_access) {
case SitePermissionsHelper::SiteAccess::kOnClick:
// The "on click" option is only enabled if the extension has active tab,
// previously handled, or wants to always run on the site without user
// interaction.
return extension_access.has_site_access ||
extension_access.withheld_site_access;
case SitePermissionsHelper::SiteAccess::kOnSite:
// The "on site" option is only enabled if the extension wants to
// always run on the site without user interaction.
return extension_access.has_site_access ||
extension_access.withheld_site_access;
case SitePermissionsHelper::SiteAccess::kOnAllSites:
// The "on all sites" option is only enabled if the extension wants to be
// able to run everywhere.
return extension_access.has_all_sites_access ||
extension_access.withheld_all_sites_access;
}
}
bool SitePermissionsHelper::HasBeenBlocked(
const Extension& extension,
content::WebContents* web_contents) const {
ExtensionActionRunner* action_runner =
ExtensionActionRunner::GetForWebContents(web_contents);
return action_runner && action_runner->WantsToRun(&extension);
}
bool SitePermissionsHelper::HasActiveTabAndCanAccess(const Extension& extension,
const GURL& url) const {
return extension.permissions_data()->HasAPIPermission(
mojom::APIPermissionID::kActiveTab) &&
!extension.permissions_data()->IsRestrictedUrl(url,
/*error=*/nullptr) &&
(!url.SchemeIsFile() ||
util::AllowFileAccess(extension.id(), profile_));
}
bool SitePermissionsHelper::ShowAccessRequestsInToolbar(
const std::string& extension_id) {
// By default, extensions requesting access should be visible in toolbar,
// otherwise the user would most likely never grant the extensions access.
bool show_access_requests = true;
ExtensionPrefs::Get(profile_)->ReadPrefAsBoolean(
extension_id, kPrefShowAccessRequestsInToolbar, &show_access_requests);
return show_access_requests;
}
void SitePermissionsHelper::SetShowAccessRequestsInToolbar(
const std::string& extension_id,
bool show_access_requests_in_toolbar) {
ExtensionPrefs::Get(profile_)->UpdateExtensionPref(
extension_id, kPrefShowAccessRequestsInToolbar,
std::make_unique<base::Value>(show_access_requests_in_toolbar));
}
} // namespace extensions