blob: cc77246a5ffb1e00625f56e9c93a30c729841e68 [file] [log] [blame]
// Copyright 2012 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.
#import "ios/chrome/browser/web/blocked_popup_tab_helper.h"
#include <UIKit/UIKit.h>
#include <memory>
#include <utility>
#include "base/format_macros.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/infobars/core/confirm_infobar_delegate.h"
#include "components/infobars/core/infobar.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/web/public/referrer.h"
#include "net/base/mac/url_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The infobar to display when a popup is blocked.
class BlockPopupInfoBarDelegate : public ConfirmInfoBarDelegate {
public:
BlockPopupInfoBarDelegate(
ios::ChromeBrowserState* browser_state,
web::WebState* web_state,
const std::vector<BlockedPopupTabHelper::Popup>& popups)
: browser_state_(browser_state), web_state_(web_state), popups_(popups) {}
~BlockPopupInfoBarDelegate() override {}
InfoBarIdentifier GetIdentifier() const override {
return POPUP_BLOCKED_INFOBAR_DELEGATE_MOBILE;
}
gfx::Image GetIcon() const override {
if (icon_.IsEmpty()) {
icon_ = gfx::Image([UIImage imageNamed:@"infobar_popup_blocker"]);
}
return icon_;
}
base::string16 GetMessageText() const override {
return l10n_util::GetStringFUTF16(
IDS_IOS_POPUPS_BLOCKED_MOBILE,
base::UTF8ToUTF16(base::StringPrintf("%" PRIuS, popups_.size())));
}
base::string16 GetButtonLabel(InfoBarButton button) const override {
DCHECK(button == BUTTON_OK);
return l10n_util::GetStringUTF16(IDS_IOS_POPUPS_ALWAYS_SHOW_MOBILE);
}
bool Accept() override {
scoped_refptr<HostContentSettingsMap> host_content_map_settings(
ios::HostContentSettingsMapFactory::GetForBrowserState(browser_state_));
for (auto& popup : popups_) {
web::WebState::OpenURLParams params(
popup.popup_url, popup.referrer, WindowOpenDisposition::NEW_POPUP,
ui::PAGE_TRANSITION_LINK, true /* is_renderer_initiated */);
web_state_->OpenURL(params);
host_content_map_settings->SetContentSettingCustomScope(
ContentSettingsPattern::FromURL(popup.referrer.url),
ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_POPUPS,
std::string(), CONTENT_SETTING_ALLOW);
}
return true;
}
int GetButtons() const override { return BUTTON_OK; }
private:
ios::ChromeBrowserState* browser_state_;
web::WebState* web_state_;
// The popups to open.
std::vector<BlockedPopupTabHelper::Popup> popups_;
// The icon to display.
mutable gfx::Image icon_;
};
} // namespace
BlockedPopupTabHelper::BlockedPopupTabHelper(web::WebState* web_state)
: web_state_(web_state), infobar_(nullptr), scoped_observer_(this) {}
BlockedPopupTabHelper::~BlockedPopupTabHelper() = default;
bool BlockedPopupTabHelper::ShouldBlockPopup(const GURL& source_url) {
HostContentSettingsMap* settings_map =
ios::HostContentSettingsMapFactory::GetForBrowserState(GetBrowserState());
ContentSetting setting = settings_map->GetContentSetting(
source_url, source_url, CONTENT_SETTINGS_TYPE_POPUPS, std::string());
return setting != CONTENT_SETTING_ALLOW;
}
void BlockedPopupTabHelper::HandlePopup(const GURL& popup_url,
const web::Referrer& referrer) {
DCHECK(ShouldBlockPopup(referrer.url));
popups_.push_back(Popup(popup_url, referrer));
ShowInfoBar();
}
void BlockedPopupTabHelper::OnInfoBarRemoved(infobars::InfoBar* infobar,
bool animate) {
if (infobar == infobar_) {
infobar_ = nullptr;
popups_.clear();
scoped_observer_.RemoveAll();
}
}
void BlockedPopupTabHelper::OnManagerShuttingDown(
infobars::InfoBarManager* infobar_manager) {
scoped_observer_.Remove(infobar_manager);
}
void BlockedPopupTabHelper::ShowInfoBar() {
infobars::InfoBarManager* infobar_manager =
InfoBarManagerImpl::FromWebState(web_state_);
if (!popups_.size() || !infobar_manager)
return;
RegisterAsInfoBarManagerObserverIfNeeded(infobar_manager);
std::unique_ptr<BlockPopupInfoBarDelegate> delegate(
std::make_unique<BlockPopupInfoBarDelegate>(GetBrowserState(), web_state_,
popups_));
std::unique_ptr<infobars::InfoBar> infobar =
infobar_manager->CreateConfirmInfoBar(std::move(delegate));
if (infobar_) {
infobar_ = infobar_manager->ReplaceInfoBar(infobar_, std::move(infobar));
} else {
infobar_ = infobar_manager->AddInfoBar(std::move(infobar));
}
}
ios::ChromeBrowserState* BlockedPopupTabHelper::GetBrowserState() const {
return ios::ChromeBrowserState::FromBrowserState(
web_state_->GetBrowserState());
}
void BlockedPopupTabHelper::RegisterAsInfoBarManagerObserverIfNeeded(
infobars::InfoBarManager* infobar_manager) {
DCHECK(infobar_manager);
if (scoped_observer_.IsObserving(infobar_manager)) {
return;
}
// Verify that this object is never observing more than one InfoBarManager.
DCHECK(!scoped_observer_.IsObservingSources());
scoped_observer_.Add(infobar_manager);
}