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 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
// Copyright 2017 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/ui/blocked_content/tab_under_navigation_throttle.h"
#include <cmath>
#include <string>
#include <utility>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/blocked_content/popup_opener_tab_helper.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "extensions/buildflags/buildflags.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/android/framebust_intervention/framebust_blocked_delegate_android.h"
#else
#include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/constants.h"
#endif
namespace {
void LogTabUnderAttempt(content::NavigationHandle* handle) {
// The source id should generally be set, except for very rare circumstances
// where the popup opener tab helper is not observing at the time the
// previous navigation commit.
ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
ukm::SourceId opener_source_id =
handle->GetWebContents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
if (opener_source_id != ukm::kInvalidSourceId && ukm_recorder) {
ukm::builders::AbusiveExperienceHeuristic_TabUnder(opener_source_id)
.SetDidTabUnder(true)
.Record(ukm_recorder);
}
}
} // namespace
// static
void TabUnderNavigationThrottle::MaybeCreateAndAdd(
content::NavigationThrottleRegistry& registry) {
// TODO(crbug.com/40187173): TabUnderNavigationThrottle doesn't block
// prerendering activations. However, currently prerender is same-origin only
// so a prerendered activation could never be classified as a tab-under.
// Otherwise, it should be safe to avoid creating a throttle in non primary
// pages because prerendered pages should not be able to open popups. A
// tab-under could therefore never occur within the non-primary page.
if (registry.GetNavigationHandle().IsInPrimaryMainFrame()) {
registry.AddThrottle(
base::WrapUnique(new TabUnderNavigationThrottle(registry)));
}
}
TabUnderNavigationThrottle::~TabUnderNavigationThrottle() = default;
TabUnderNavigationThrottle::TabUnderNavigationThrottle(
content::NavigationThrottleRegistry& registry)
: content::NavigationThrottle(registry),
has_opened_popup_since_last_user_gesture_at_start_(
HasOpenedPopupSinceLastUserGesture()),
started_in_foreground_(
registry.GetNavigationHandle().GetWebContents()->GetVisibility() ==
content::Visibility::VISIBLE) {}
bool TabUnderNavigationThrottle::IsSuspiciousClientRedirect() const {
// This throttle is only created for primary main frame navigations. See
// MaybeCreate().
DCHECK(navigation_handle()->IsInPrimaryMainFrame());
DCHECK(!navigation_handle()->HasCommitted());
// Some browser initiated navigations have HasUserGesture set to false. This
// should eventually be fixed in crbug.com/617904. In the meantime, just dont
// block browser initiated ones.
if (started_in_foreground_ || navigation_handle()->HasUserGesture() ||
!navigation_handle()->IsRendererInitiated()) {
return false;
}
// An empty previous URL indicates this was the first load. We filter these
// out because we're primarily interested in sites which navigate themselves
// away while in the background.
content::WebContents* contents = navigation_handle()->GetWebContents();
const GURL& previous_main_frame_url = contents->GetLastCommittedURL();
if (previous_main_frame_url.is_empty()) {
return false;
}
// Same-site navigations are exempt from tab-under protection.
const GURL& target_url = navigation_handle()->GetURL();
if (net::registry_controlled_domains::SameDomainOrHost(
previous_main_frame_url, target_url,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
return false;
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
// Exempt navigating to or from extension URLs, as they will redirect pages in
// the background. By exempting in both directions, extensions can always
// round-trip a page through an extension URL in order to perform arbitrary
// redirections with content scripts.
if (target_url.SchemeIs(extensions::kExtensionScheme) ||
previous_main_frame_url.SchemeIs(extensions::kExtensionScheme)) {
return false;
}
#endif
return true;
}
content::NavigationThrottle::ThrottleCheckResult
TabUnderNavigationThrottle::MaybeBlockNavigation() {
if (seen_tab_under_ || !has_opened_popup_since_last_user_gesture_at_start_ ||
!IsSuspiciousClientRedirect()) {
return content::NavigationThrottle::PROCEED;
}
seen_tab_under_ = true;
LogTabUnderAttempt(navigation_handle());
// We unconditionally proceed. There used to be a tab-under blocking
// experiment, but it never launched.
return content::NavigationThrottle::PROCEED;
}
void TabUnderNavigationThrottle::ShowUI() {
content::WebContents* web_contents = navigation_handle()->GetWebContents();
const GURL& url = navigation_handle()->GetURL();
#if BUILDFLAG(IS_ANDROID)
blocked_content::FramebustBlockedMessageDelegate::CreateForWebContents(
web_contents);
blocked_content::FramebustBlockedMessageDelegate*
framebust_blocked_message_delegate =
blocked_content::FramebustBlockedMessageDelegate::FromWebContents(
web_contents);
framebust_blocked_message_delegate->ShowMessage(
url,
HostContentSettingsMapFactory::GetForProfile(
web_contents->GetBrowserContext()),
base::NullCallback());
#else
if (auto* tab_helper =
FramebustBlockTabHelper::FromWebContents(web_contents)) {
tab_helper->AddBlockedUrl(url, base::NullCallback());
}
#endif
}
bool TabUnderNavigationThrottle::HasOpenedPopupSinceLastUserGesture() const {
content::WebContents* contents = navigation_handle()->GetWebContents();
auto* popup_opener =
blocked_content::PopupOpenerTabHelper::FromWebContents(contents);
return popup_opener &&
popup_opener->has_opened_popup_since_last_user_gesture();
}
content::NavigationThrottle::ThrottleCheckResult
TabUnderNavigationThrottle::WillStartRequest() {
return MaybeBlockNavigation();
}
content::NavigationThrottle::ThrottleCheckResult
TabUnderNavigationThrottle::WillRedirectRequest() {
return MaybeBlockNavigation();
}
const char* TabUnderNavigationThrottle::GetNameForLogging() {
return "TabUnderNavigationThrottle";
}
|