1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
// 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
|