blob: 38afcf6eea888e42aa6e680d7236ec394ccb1b93 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/blocked_content/popup_blocker_tab_helper.h"
#include <iterator>
#include <string>
#include "base/metrics/histogram_macros.h"
#include "build/build_config.h"
#include "components/back_forward_cache/back_forward_cache_disable.h"
#include "components/blocked_content/list_item_position.h"
#include "components/blocked_content/popup_navigation_delegate.h"
#include "components/blocked_content/popup_tracker.h"
#include "components/blocked_content/safe_browsing_triggered_popup_blocker.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
namespace blocked_content {
const size_t kMaximumNumberOfPopups = 25;
struct PopupBlockerTabHelper::BlockedRequest {
BlockedRequest(std::unique_ptr<PopupNavigationDelegate> delegate,
const blink::mojom::WindowFeatures& window_features,
PopupBlockType block_type)
: delegate(std::move(delegate)),
window_features(window_features),
block_type(block_type) {}
std::unique_ptr<PopupNavigationDelegate> delegate;
blink::mojom::WindowFeatures window_features;
PopupBlockType block_type;
};
PopupBlockerTabHelper::PopupBlockerTabHelper(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {
blocked_content::SafeBrowsingTriggeredPopupBlocker::MaybeCreate(web_contents);
}
PopupBlockerTabHelper::~PopupBlockerTabHelper() = default;
void PopupBlockerTabHelper::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
// Clear all page actions, blocked content notifications and browser actions
// for this tab, unless this is an same-document navigation. Also only
// consider main frame navigations that successfully committed.
if (!navigation_handle->IsInMainFrame() ||
!navigation_handle->HasCommitted() ||
navigation_handle->IsSameDocument()) {
return;
}
// Close blocked popups.
if (!blocked_popups_.empty()) {
blocked_popups_.clear();
HidePopupNotification();
// With back-forward cache we can restore the page, but |blocked_popups_|
// are lost here and can't be restored at the moment.
// Disable bfcache here to avoid potential loss of the page state.
content::BackForwardCache::DisableForRenderFrameHost(
navigation_handle->GetPreviousRenderFrameHostId(),
back_forward_cache::DisabledReason(
back_forward_cache::DisabledReasonId::kPopupBlockerTabHelper));
}
}
void PopupBlockerTabHelper::HidePopupNotification() {
auto* pscs = content_settings::PageSpecificContentSettings::GetForFrame(
web_contents()->GetMainFrame());
if (pscs)
pscs->ClearPopupsBlocked();
}
void PopupBlockerTabHelper::AddBlockedPopup(
std::unique_ptr<PopupNavigationDelegate> delegate,
const blink::mojom::WindowFeatures& window_features,
PopupBlockType block_type) {
LogAction(Action::kBlocked);
if (blocked_popups_.size() >= kMaximumNumberOfPopups)
return;
int id = next_id_;
next_id_++;
blocked_popups_[id] = std::make_unique<BlockedRequest>(
std::move(delegate), window_features, block_type);
auto* content_settings =
content_settings::PageSpecificContentSettings::GetForFrame(
web_contents()->GetMainFrame());
if (content_settings) {
content_settings->OnContentBlocked(ContentSettingsType::POPUPS);
}
auto* raw_delegate = blocked_popups_[id]->delegate.get();
manager_.NotifyObservers(id, raw_delegate->GetURL());
raw_delegate->OnPopupBlocked(web_contents(), GetBlockedPopupsCount());
}
void PopupBlockerTabHelper::ShowBlockedPopup(
int32_t id,
WindowOpenDisposition disposition) {
auto it = blocked_popups_.find(id);
if (it == blocked_popups_.end())
return;
blocked_content::ListItemPosition position =
blocked_content::GetListItemPositionFromDistance(
std::distance(blocked_popups_.begin(), it), blocked_popups_.size());
UMA_HISTOGRAM_ENUMERATION("ContentSettings.Popups.ClickThroughPosition",
position);
BlockedRequest* popup = it->second.get();
base::Optional<WindowOpenDisposition> updated_disposition;
if (disposition != WindowOpenDisposition::CURRENT_TAB)
updated_disposition = disposition;
PopupNavigationDelegate::NavigateResult result =
popup->delegate->NavigateWithGesture(popup->window_features,
updated_disposition);
if (result.navigated_or_inserted_contents) {
auto* tracker = blocked_content::PopupTracker::CreateForWebContents(
result.navigated_or_inserted_contents, web_contents(),
result.disposition);
tracker->set_is_trusted(true);
}
switch (popup->block_type) {
case PopupBlockType::kNotBlocked:
NOTREACHED();
break;
case PopupBlockType::kNoGesture:
LogAction(Action::kClickedThroughNoGesture);
break;
case PopupBlockType::kAbusive:
LogAction(Action::kClickedThroughAbusive);
break;
}
blocked_popups_.erase(id);
if (blocked_popups_.empty())
HidePopupNotification();
}
void PopupBlockerTabHelper::ShowAllBlockedPopups() {
PopupIdMap blocked_popups = GetBlockedPopupRequests();
for (const auto& elem : blocked_popups) {
ShowBlockedPopup(elem.first, WindowOpenDisposition::CURRENT_TAB);
}
}
size_t PopupBlockerTabHelper::GetBlockedPopupsCount() const {
return blocked_popups_.size();
}
PopupBlockerTabHelper::PopupIdMap
PopupBlockerTabHelper::GetBlockedPopupRequests() {
PopupIdMap result;
for (const auto& it : blocked_popups_) {
result[it.first] = it.second->delegate->GetURL();
}
return result;
}
// static
void PopupBlockerTabHelper::LogAction(Action action) {
UMA_HISTOGRAM_ENUMERATION("ContentSettings.Popups.BlockerActions", action);
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(PopupBlockerTabHelper)
} // namespace blocked_content