[go: nahoru, domu]

blob: 5b73ccc051cc06e814909a9fd4d7c9f5d10993fc [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/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/extensions/tab_helper.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";
// The blocked actions that require a page refresh to run.
constexpr int kRefreshRequiredActionsMask =
BLOCKED_ACTION_WEB_REQUEST | BLOCKED_ACTION_SCRIPT_AT_START;
} // namespace
SitePermissionsHelper::SitePermissionsHelper(Profile* profile)
: profile_(profile) {}
SitePermissionsHelper::~SitePermissionsHelper() = default;
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 (PermissionsManager::Get(profile_)->HasActiveTabAndCanAccess(extension,
url)) {
return SiteInteraction::kActiveTab;
}
return SiteInteraction::kNone;
}
void SitePermissionsHelper::UpdateSiteAccess(
const Extension& extension,
content::WebContents* web_contents,
PermissionsManager::UserSiteAccess new_access) {
auto* permissions_manager = PermissionsManager::Get(profile_);
auto current_access = permissions_manager->GetUserSiteAccess(
extension, web_contents->GetLastCommittedURL());
if (new_access == current_access) {
return;
}
ScriptingPermissionsModifier modifier(profile_, &extension);
CHECK(permissions_manager->CanAffectExtension(extension));
auto current_url = web_contents->GetLastCommittedURL();
CHECK(permissions_manager->CanUserSelectSiteAccess(extension, current_url,
new_access));
switch (new_access) {
case PermissionsManager::UserSiteAccess::kOnClick:
if (permissions_manager->HasBroadGrantedHostPermissions(extension)) {
modifier.RemoveBroadGrantedHostPermissions();
}
// Note: SetWithholdHostPermissions() is a no-op if host permissions are
// already being withheld.
modifier.SetWithholdHostPermissions(true);
if (permissions_manager->HasGrantedHostPermission(extension,
current_url)) {
modifier.RemoveGrantedHostPermission(current_url);
}
break;
case PermissionsManager::UserSiteAccess::kOnSite:
if (permissions_manager->HasBroadGrantedHostPermissions(extension)) {
modifier.RemoveBroadGrantedHostPermissions();
}
// Note: SetWithholdHostPermissions() is a no-op if host permissions are
// already being withheld.
modifier.SetWithholdHostPermissions(true);
if (!permissions_manager->HasGrantedHostPermission(extension,
current_url)) {
modifier.GrantHostPermission(current_url);
}
break;
case PermissionsManager::UserSiteAccess::kOnAllSites:
modifier.SetWithholdHostPermissions(false);
break;
}
ExtensionActionRunner* runner =
ExtensionActionRunner::GetForWebContents(web_contents);
if (!runner) {
return;
}
// Clear extension's tab permission when revoking user site permissions.
bool revoking_current_site_permissions =
new_access == PermissionsManager::UserSiteAccess::kOnClick;
if (revoking_current_site_permissions) {
TabHelper::FromWebContents(web_contents)
->active_tab_permission_granter()
->ClearActiveExtensionAndNotify(extension.id());
// While revoking permissions doesn't necessarily mandate a page
// refresh, it is complicated to determine when an extension has affected
// the page. Showing a reload page bubble after the user blocks the
// extension re enforces the user confidence on blocking the extension.
// Also, this scenario should not be that common and therefore hopefully is
// not too noisy.
runner->ShowReloadPageBubble({extension.id()});
return;
}
// Run blocked actions when granting user site permissions.
int blocked_actions = runner->GetBlockedActions(extension.id());
if (PageNeedsRefreshToRun(blocked_actions)) {
// Show reload bubble when blocked actions mandate a page refresh.
// Refreshing the page will run them.
runner->ShowReloadPageBubble({extension.id()});
} else if (blocked_actions != BLOCKED_ACTION_NONE) {
runner->RunBlockedActions(&extension);
}
}
bool SitePermissionsHelper::PageNeedsRefreshToRun(int blocked_actions) {
return blocked_actions & kRefreshRequiredActionsMask;
}
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::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,
base::Value(show_access_requests_in_toolbar));
PermissionsManager::Get(profile_)->NotifyShowAccessRequestsInToolbarChanged(
extension_id, show_access_requests_in_toolbar);
}
} // namespace extensions