blob: f269f50cc4bc54f145471e3b215fe6c626081cfe [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/host_access_request_helper.h"
#include <sys/types.h>
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/common/extension.h"
#include "extensions/common/url_pattern.h"
namespace extensions {
HostAccessRequestsHelper::HostAccessRequestsHelper(
PassKey pass_key,
PermissionsManager* permissions_manager,
content::WebContents* web_contents,
int tab_id)
: content::WebContentsObserver(web_contents),
permissions_manager_(permissions_manager),
web_contents_(web_contents),
tab_id_(tab_id) {
extension_registry_observation_.Observe(
ExtensionRegistry::Get(web_contents->GetBrowserContext()));
}
HostAccessRequestsHelper::~HostAccessRequestsHelper() = default;
void HostAccessRequestsHelper::AddRequest(
const Extension& extension,
const std::optional<URLPattern>& filter) {
// Extension must not have granted access to the current site.
auto site_access = permissions_manager_->GetSiteAccess(
extension, web_contents_->GetLastCommittedURL());
CHECK(!site_access.has_site_access && !site_access.has_all_sites_access);
extensions_with_requests_.insert({extension.id(), filter});
}
void HostAccessRequestsHelper::UpdateRequest(
const Extension& extension,
const std::optional<URLPattern>& filter) {
// We can only update a request if there is an existent one.
CHECK(HasRequest(extension.id()));
// Extension must not have granted access to the current site.
auto site_access = permissions_manager_->GetSiteAccess(
extension, web_contents_->GetLastCommittedURL());
CHECK(!site_access.has_site_access && !site_access.has_all_sites_access);
extensions_with_requests_.at(extension.id()) = filter;
}
bool HostAccessRequestsHelper::RemoveRequest(
const ExtensionId& extension_id,
const std::optional<URLPattern>& filter) {
auto requests_iter = extensions_with_requests_.find(extension_id);
if (requests_iter == extensions_with_requests_.end()) {
return false;
}
// Remove request iff it matches the parameter when given. Otherwise, always
// remove the request.
if (!filter || requests_iter->second == filter) {
extensions_with_requests_.erase(extension_id);
return true;
}
return false;
}
bool HostAccessRequestsHelper::RemoveRequestIfGrantedAccess(
const Extension& extension) {
// Request is removed iff extension has access to the current site.
const GURL& url = web_contents_->GetLastCommittedURL();
PermissionsManager::ExtensionSiteAccess site_access =
permissions_manager_->GetSiteAccess(extension, url);
if (!site_access.has_site_access && !site_access.has_all_sites_access &&
!permissions_manager_->HasActiveTabAndCanAccess(extension, url)) {
return false;
}
return RemoveRequest(extension.id(), /*filter=*/std::nullopt);
}
void HostAccessRequestsHelper::UserDismissedRequest(
const ExtensionId& extension_id) {
CHECK(extensions_with_requests_.contains(extension_id));
extensions_with_requests_dismissed_.insert(extension_id);
}
bool HostAccessRequestsHelper::HasRequest(
const ExtensionId& extension_id) const {
return extensions_with_requests_.contains(extension_id);
}
bool HostAccessRequestsHelper::HasActiveRequest(
const ExtensionId& extension_id) const {
if (!extensions_with_requests_.contains(extension_id)) {
return false;
}
if (extensions_with_requests_dismissed_.contains(extension_id)) {
return false;
}
// Web contents must match the filter if provided, otherwise request is always
// active.
std::optional<URLPattern> filter = extensions_with_requests_.at(extension_id);
return !filter ||
filter.value().MatchesURL(web_contents_->GetLastCommittedURL());
}
bool HostAccessRequestsHelper::HasRequests() {
return !extensions_with_requests_.empty();
}
void HostAccessRequestsHelper::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionReason reason) {
RemoveRequest(extension->id(), /*filter=*/std::nullopt);
if (!HasRequests()) {
permissions_manager_->DeleteHostAccessRequestHelperFor(tab_id_);
// IMPORTANT: This object is now deleted and is unsafe to use.
}
}
void HostAccessRequestsHelper::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
// Sub-frames don't get specific requests.
if (!navigation_handle->IsInPrimaryMainFrame() ||
!navigation_handle->HasCommitted() ||
navigation_handle->IsSameDocument()) {
return;
}
// Only clear requests for cross-origin navigations.
if (navigation_handle->IsSameOrigin()) {
return;
}
extensions_with_requests_.clear();
extensions_with_requests_dismissed_.clear();
permissions_manager_->NotifyHostAccessRequestsCleared(tab_id_);
permissions_manager_->DeleteHostAccessRequestHelperFor(tab_id_);
// IMPORTANT: This object is now deleted and is unsafe to use.
}
void HostAccessRequestsHelper::WebContentsDestroyed() {
// Delete web contents pointer so it's not dangling at helper's destruction.
web_contents_ = nullptr;
extensions_with_requests_.clear();
extensions_with_requests_dismissed_.clear();
permissions_manager_->DeleteHostAccessRequestHelperFor(tab_id_);
// IMPORTANT: This object is now deleted and is unsafe to use.
}
} // namespace extensions