WKSafeBrowsingWarning.mm   [plain text]

/*
* Copyright (C) 2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#import "config.h"
#import "WKSafeBrowsingWarning.h"

#import "PageClient.h"
#import "SafeBrowsingWarning.h"
#import <WebCore/LocalizedStrings.h>
#import <wtf/URL.h>
#import <wtf/BlockPtr.h>
#import <wtf/Language.h>

#if PLATFORM(WATCHOS)
#import "UIKitSPI.h"
#import <PepperUICore/UIScrollView+PUICAdditionsPrivate.h>
#endif

constexpr CGFloat exclamationPointSize = 30;
constexpr CGFloat boxCornerRadius = 6;
#if HAVE(SAFE_BROWSING)
#if PLATFORM(WATCHOS)
constexpr CGFloat marginSize = 9;
#else
constexpr CGFloat marginSize = 20;
#endif
constexpr CGFloat maxWidth = 675;
#endif

#if PLATFORM(MAC)
using FontType = NSFont;
using TextViewType = NSTextView;
using ButtonType = NSButton;
using AlignmentType = NSLayoutAttribute;
using SizeType = NSSize;
#else
using FontType = UIFont;
using TextViewType = UITextView;
using ButtonType = UIButton;
using AlignmentType = UIStackViewAlignment;
using SizeType = CGSize;
#endif

enum class WarningItem : uint8_t {
Background,
BoxBackground,
ExclamationPoint,
TitleText,
MessageText,
ShowDetailsButton,
GoBackButton
};

enum class WarningTextSize : uint8_t {
Title,
Body
};

static FontType *fontOfSize(WarningTextSize size)
{
#if PLATFORM(MAC)
switch (size) {
case WarningTextSize::Title:
return [NSFont boldSystemFontOfSize:26];
case WarningTextSize::Body:
return [NSFont systemFontOfSize:14];
}
#elif HAVE(SAFE_BROWSING)
switch (size) {
case WarningTextSize::Title:
#if PLATFORM(WATCHOS)
return [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
#else
return [UIFont preferredFontForTextStyle:UIFontTextStyleLargeTitle];
#endif
case WarningTextSize::Body:
return [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
}
#else
return nil;
#endif
}

static ColorType *colorForItem(WarningItem item, ViewType *warning)
{
ASSERT([warning isKindOfClass:[WKSafeBrowsingWarning class]]);
#if PLATFORM(MAC)

auto colorNamed = [] (NSString *name) -> ColorType* {
#if HAVE(SAFE_BROWSING)
return [NSColor colorNamed:name bundle:[NSBundle bundleWithIdentifier:@"com.apple.WebKit"]];
#else
ASSERT_NOT_REACHED();
return nil;
#endif
};

switch (item) {
case WarningItem::Background:
return colorNamed(@"WKSafeBrowsingWarningBackground");
case WarningItem::BoxBackground:
return [NSColor windowBackgroundColor];
case WarningItem::TitleText:
case WarningItem::ExclamationPoint:
return colorNamed(@"WKSafeBrowsingWarningTitle");
case WarningItem::MessageText:
return colorNamed(@"WKSafeBrowsingWarningText");
case WarningItem::ShowDetailsButton:
case WarningItem::GoBackButton:
ASSERT_NOT_REACHED();
return nil;
}
#else
UIColor *red = [UIColor colorWithRed:0.998 green:0.239 blue:0.233 alpha:1.0];
UIColor *white = [UIColor whiteColor];
bool narrow = warning.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact;

switch (item) {
case WarningItem::Background:
return red;
case WarningItem::BoxBackground:
return narrow ? red : white;
case WarningItem::TitleText:
case WarningItem::ExclamationPoint:
return narrow ? white : red;
case WarningItem::MessageText:
case WarningItem::ShowDetailsButton:
return narrow ? white : [UIColor darkTextColor];
case WarningItem::GoBackButton:
return narrow ? white : warning.tintColor;
}
#endif
ASSERT_NOT_REACHED();
return nil;
}

@interface WKSafeBrowsingExclamationPoint : ViewType
@end

@implementation WKSafeBrowsingExclamationPoint

- (void)drawRect:(RectType)rect
{
constexpr CGFloat centerX = exclamationPointSize / 2;
constexpr CGFloat pointCenterY = exclamationPointSize * 7 / 30;
constexpr CGFloat pointRadius = 2.25 * exclamationPointSize / 30;
constexpr CGFloat lineBottomCenterY = exclamationPointSize * 13 / 30;
constexpr CGFloat lineTopCenterY = exclamationPointSize * 23 / 30;
constexpr CGFloat lineRadius = 1.75 * exclamationPointSize / 30;
ViewType *warning = self.superview.superview;
#if PLATFORM(MAC)
[colorForItem(WarningItem::ExclamationPoint, warning) set];
NSBezierPath *exclamationPoint = [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(0, 0, exclamationPointSize, exclamationPointSize)];
[exclamationPoint appendBezierPathWithArcWithCenter: { centerX, lineBottomCenterY } radius:lineRadius startAngle:0 endAngle:180 clockwise:YES];
[exclamationPoint appendBezierPathWithArcWithCenter: { centerX, lineTopCenterY } radius:lineRadius startAngle:180 endAngle:360 clockwise:YES];
[exclamationPoint lineToPoint: { centerX + lineRadius, lineBottomCenterY }];
[exclamationPoint appendBezierPathWithArcWithCenter: { centerX, pointCenterY } radius:pointRadius startAngle:0 endAngle:180 clockwise:YES];
[exclamationPoint appendBezierPathWithArcWithCenter: { centerX, pointCenterY } radius:pointRadius startAngle:180 endAngle:360 clockwise:YES];
#else
auto flip = [] (auto y) {
return exclamationPointSize - y;
};
[colorForItem(WarningItem::BoxBackground, warning) set];
auto square = CGRectMake(0, 0, exclamationPointSize, exclamationPointSize);
[[UIBezierPath bezierPathWithRect:square] fill];

[colorForItem(WarningItem::ExclamationPoint, warning) set];
UIBezierPath *exclamationPoint = [UIBezierPath bezierPathWithOvalInRect:square];
[exclamationPoint addArcWithCenter: { centerX, flip(lineTopCenterY) } radius:lineRadius startAngle:2 * piDouble endAngle:piDouble clockwise:NO];
[exclamationPoint addArcWithCenter: { centerX, flip(lineBottomCenterY) } radius:lineRadius startAngle:piDouble endAngle:0 clockwise:NO];
[exclamationPoint addArcWithCenter: { centerX, flip(pointCenterY) } radius:pointRadius startAngle:0 endAngle:piDouble clockwise:NO];
[exclamationPoint addArcWithCenter: { centerX, flip(pointCenterY) } radius:pointRadius startAngle:piDouble endAngle:piDouble * 2 clockwise:NO];
[exclamationPoint addLineToPoint: { centerX + lineRadius, flip(lineBottomCenterY) }];
[exclamationPoint addLineToPoint: { centerX + lineRadius, flip(lineTopCenterY) }];
#endif
[exclamationPoint fill];
}

#if PLATFORM(MAC)
- (void)viewDidChangeEffectiveAppearance
{
[self setNeedsDisplay:YES];
}
#endif

- (NSSize)intrinsicContentSize
{
return { exclamationPointSize, exclamationPointSize };
}

@end

static ButtonType *makeButton(WarningItem item, WKSafeBrowsingWarning *warning, SEL action)
{
NSString *title = nil;
if (item == WarningItem::ShowDetailsButton)
title = WEB_UI_NSSTRING(@"Show Details", "Action from safe browsing warning");
else
title = WEB_UI_NSSTRING(@"Go Back", "Action from safe browsing warning");
#if PLATFORM(MAC)
return [NSButton buttonWithTitle:title target:warning action:action];
#else
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
NSAttributedString *attributedTitle = [[[NSAttributedString alloc] initWithString:title attributes:@{
NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle),
NSUnderlineColorAttributeName:[UIColor whiteColor],
NSForegroundColorAttributeName:colorForItem(item, warning),
NSFontAttributeName:fontOfSize(WarningTextSize::Body)
}] autorelease];
[button setAttributedTitle:attributedTitle forState:UIControlStateNormal];
[button addTarget:warning action:action forControlEvents:UIControlEventTouchUpInside];
return button;
#endif
}

#if HAVE(SAFE_BROWSING)
static CGSize buttonSize(ButtonType *button)
{
#if PLATFORM(MAC)
return button.frame.size;
#else
return button.titleLabel.intrinsicContentSize;
#endif
}
#endif

static ViewType *makeLabel(NSAttributedString *attributedString)
{
#if PLATFORM(MAC)
return [NSTextField labelWithAttributedString:attributedString];
#else
auto label = [[UILabel new] autorelease];
label.attributedText = attributedString;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.numberOfLines = 0;
return label;
#endif
}

@implementation WKSafeBrowsingBox

- (void)setSafeBrowsingBackgroundColor:(ColorType *)color
{
#if PLATFORM(MAC)
_backgroundColor = color;
self.wantsLayer = YES;
#else
self.backgroundColor = color;
#endif
}

#if PLATFORM(MAC)
- (void)updateLayer
{
self.layer.backgroundColor = [_backgroundColor CGColor];
}
#endif

@end

@interface WKSafeBrowsingTextView : TextViewType {
@package
WeakObjCPtr<WKSafeBrowsingWarning> _warning;
}
- (instancetype)initWithAttributedString:(NSAttributedString *)attributedString forWarning:(WKSafeBrowsingWarning *)warning;
@end

@implementation WKSafeBrowsingWarning

- (instancetype)initWithFrame:(RectType)frame safeBrowsingWarning:(const WebKit::SafeBrowsingWarning&)warning completionHandler:(CompletionHandler<void(Variant<WebKit::ContinueUnsafeLoad, URL>&&)>&&)completionHandler
{
if (!(self = [super initWithFrame:frame])) {
completionHandler(WebKit::ContinueUnsafeLoad::Yes);
return nil;
}
_completionHandler = [weakSelf = WeakObjCPtr<WKSafeBrowsingWarning>(self), completionHandler = WTFMove(completionHandler)] (Variant<WebKit::ContinueUnsafeLoad, URL>&& result) mutable {
#if PLATFORM(WATCHOS)
if (auto strongSelf = weakSelf.get())
[strongSelf.get()->_previousFirstResponder becomeFirstResponder];
#endif
completionHandler(WTFMove(result));
};
_warning = makeRef(warning);
#if PLATFORM(MAC)
[self setSafeBrowsingBackgroundColor:colorForItem(WarningItem::Background, self)];
[self addContent];
#else
[self setBackgroundColor:colorForItem(WarningItem::Background, self)];
#endif

#if PLATFORM(WATCHOS)
self.crownInputScrollDirection = PUICCrownInputScrollDirectionVertical;
#endif
return self;
}

- (void)addContent
{
auto exclamationPoint = [[WKSafeBrowsingExclamationPoint new] autorelease];
auto title = makeLabel([[[NSAttributedString alloc] initWithString:_warning->title() attributes:@{
NSFontAttributeName:fontOfSize(WarningTextSize::Title),
NSForegroundColorAttributeName:colorForItem(WarningItem::TitleText, self)
#if PLATFORM(WATCHOS)
, NSHyphenationFactorDocumentAttribute:@1
#endif
}] autorelease]);
auto warning = makeLabel([[[NSAttributedString alloc] initWithString:_warning->warning() attributes:@{
NSFontAttributeName:fontOfSize(WarningTextSize::Body),
NSForegroundColorAttributeName:colorForItem(WarningItem::MessageText, self)
#if PLATFORM(WATCHOS)
, NSHyphenationFactorDocumentAttribute:@1
#endif
}] autorelease]);
auto showDetails = makeButton(WarningItem::ShowDetailsButton, self, @selector(showDetailsClicked));
auto goBack = makeButton(WarningItem::GoBackButton, self, @selector(goBackClicked));
auto box = [[WKSafeBrowsingBox new] autorelease];
_box = box;
[box setSafeBrowsingBackgroundColor:colorForItem(WarningItem::BoxBackground, self)];
box.layer.cornerRadius = boxCornerRadius;

for (ViewType *view in @[exclamationPoint, title, warning, goBack, showDetails]) {
view.translatesAutoresizingMaskIntoConstraints = NO;
[box addSubview:view];
}
box.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:box];

#if PLATFORM(WATCHOS)
[NSLayoutConstraint activateConstraints:@[
[[box.leadingAnchor anchorWithOffsetToAnchor:exclamationPoint.leadingAnchor] constraintEqualToAnchor:[exclamationPoint.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor]],
[[box.leadingAnchor anchorWithOffsetToAnchor:title.leadingAnchor] constraintEqualToConstant:marginSize],
[[title.bottomAnchor anchorWithOffsetToAnchor:warning.topAnchor] constraintEqualToConstant:marginSize],
[[exclamationPoint.bottomAnchor anchorWithOffsetToAnchor:title.topAnchor] constraintEqualToConstant:marginSize],
[[box.topAnchor anchorWithOffsetToAnchor:exclamationPoint.topAnchor] constraintEqualToConstant:marginSize + self.frame.size.height / 2],
[[self.topAnchor anchorWithOffsetToAnchor:box.topAnchor] constraintEqualToAnchor:[box.bottomAnchor anchorWithOffsetToAnchor:self.bottomAnchor] multiplier:0.2],
]];
#elif HAVE(SAFE_BROWSING)
[NSLayoutConstraint activateConstraints:@[
[[box.leadingAnchor anchorWithOffsetToAnchor:exclamationPoint.leadingAnchor] constraintEqualToConstant:marginSize],
[[box.leadingAnchor anchorWithOffsetToAnchor:title.leadingAnchor] constraintEqualToConstant:marginSize * 1.5 + exclamationPointSize],
[[title.topAnchor anchorWithOffsetToAnchor:exclamationPoint.topAnchor] constraintEqualToAnchor:[exclamationPoint.bottomAnchor anchorWithOffsetToAnchor:title.bottomAnchor]],
[[title.bottomAnchor anchorWithOffsetToAnchor:warning.topAnchor] constraintEqualToConstant:marginSize],
[[box.topAnchor anchorWithOffsetToAnchor:title.topAnchor] constraintEqualToConstant:marginSize],
[[self.topAnchor anchorWithOffsetToAnchor:box.topAnchor] constraintEqualToAnchor:[box.bottomAnchor anchorWithOffsetToAnchor:self.bottomAnchor] multiplier:0.5],
]];
#endif

#if HAVE(SAFE_BROWSING)
[NSLayoutConstraint activateConstraints:@[
[[self.leftAnchor anchorWithOffsetToAnchor:box.leftAnchor] constraintEqualToAnchor:[box.rightAnchor anchorWithOffsetToAnchor:self.rightAnchor]],

[box.widthAnchor constraintLessThanOrEqualToConstant:maxWidth],
[box.widthAnchor constraintLessThanOrEqualToAnchor:self.widthAnchor],

[[box.leadingAnchor anchorWithOffsetToAnchor:warning.leadingAnchor] constraintEqualToConstant:marginSize],

[[title.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor] constraintGreaterThanOrEqualToConstant:marginSize],
[[warning.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor] constraintGreaterThanOrEqualToConstant:marginSize],
[[goBack.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor] constraintEqualToConstant:marginSize],

[[warning.bottomAnchor anchorWithOffsetToAnchor:goBack.topAnchor] constraintEqualToConstant:marginSize],
]];

bool needsVerticalButtonLayout = buttonSize(showDetails).width + buttonSize(goBack).width + 3 * marginSize > self.frame.size.width;
if (needsVerticalButtonLayout) {
[NSLayoutConstraint activateConstraints:@[
[[showDetails.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor] constraintEqualToConstant:marginSize],
[[goBack.bottomAnchor anchorWithOffsetToAnchor:showDetails.topAnchor] constraintEqualToConstant:marginSize],
[[goBack.bottomAnchor anchorWithOffsetToAnchor:box.bottomAnchor] constraintEqualToConstant:marginSize * 2 + buttonSize(showDetails).height],
]];
} else {
[NSLayoutConstraint activateConstraints:@[
[[showDetails.trailingAnchor anchorWithOffsetToAnchor:goBack.leadingAnchor] constraintEqualToConstant:marginSize],
[goBack.topAnchor constraintEqualToAnchor:showDetails.topAnchor],
[[goBack.bottomAnchor anchorWithOffsetToAnchor:box.bottomAnchor] constraintEqualToConstant:marginSize],
]];
}
#if !PLATFORM(MAC)
[self updateContentSize];
#endif
#endif

#if PLATFORM(WATCHOS)
self->_previousFirstResponder = [self firstResponder];
[self becomeFirstResponder];
#endif
}

- (void)showDetailsClicked
{
WKSafeBrowsingBox *box = _box.get().get();
ButtonType *showDetails = box.subviews.lastObject;
[showDetails removeFromSuperview];

NSMutableAttributedString *text = [[_warning->details() mutableCopy] autorelease];
[text addAttributes:@{ NSFontAttributeName:fontOfSize(WarningTextSize::Body) } range:NSMakeRange(0, text.length)];
WKSafeBrowsingTextView *details = [[[WKSafeBrowsingTextView alloc] initWithAttributedString:text forWarning:self] autorelease];
_details = details;
WKSafeBrowsingBox *bottom = [[WKSafeBrowsingBox new] autorelease];
[bottom setSafeBrowsingBackgroundColor:colorForItem(WarningItem::BoxBackground, self)];
bottom.layer.cornerRadius = boxCornerRadius;

#if HAVE(SAFE_BROWSING)
constexpr auto maxY = kCALayerMinXMaxYCorner | kCALayerMaxXMaxYCorner;
constexpr auto minY = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
#if PLATFORM(MAC)
box.layer.maskedCorners = maxY;
bottom.layer.maskedCorners = minY;
#else
box.layer.maskedCorners = minY;
bottom.layer.maskedCorners = maxY;
#endif
#endif

WKSafeBrowsingBox *line = [[WKSafeBrowsingBox new] autorelease];
[line setSafeBrowsingBackgroundColor:[ColorType lightGrayColor]];
for (ViewType *view in @[details, bottom, line])
view.translatesAutoresizingMaskIntoConstraints = NO;

[self addSubview:bottom];
[bottom addSubview:line];
[bottom addSubview:details];
#if HAVE(SAFE_BROWSING)
[NSLayoutConstraint activateConstraints:@[
[box.widthAnchor constraintEqualToAnchor:bottom.widthAnchor],
[box.bottomAnchor constraintEqualToAnchor:bottom.topAnchor],
[box.leadingAnchor constraintEqualToAnchor:bottom.leadingAnchor],
[line.widthAnchor constraintEqualToAnchor:bottom.widthAnchor],
[line.leadingAnchor constraintEqualToAnchor:bottom.leadingAnchor],
[line.topAnchor constraintEqualToAnchor:bottom.topAnchor],
[line.heightAnchor constraintEqualToConstant:1],
[[bottom.topAnchor anchorWithOffsetToAnchor:details.topAnchor] constraintEqualToConstant:marginSize],
[[details.bottomAnchor anchorWithOffsetToAnchor:bottom.bottomAnchor] constraintEqualToConstant:marginSize],
[[bottom.leadingAnchor anchorWithOffsetToAnchor:details.leadingAnchor] constraintEqualToConstant:marginSize],
[[details.trailingAnchor anchorWithOffsetToAnchor:bottom.trailingAnchor] constraintEqualToConstant:marginSize],
]];
#endif
[self layoutText];
#if !PLATFORM(MAC)
[self updateContentSize];
#endif
}

#if !PLATFORM(MAC)
- (void)updateContentSize
{
[self layoutIfNeeded];
CGFloat height = 0;
for (ViewType *subview in self.subviews)
height += subview.frame.size.height;
[self setContentSize: { self.frame.size.width, self.frame.size.height / 2 + height }];
}
#endif

- (void)layoutText
{
[_details invalidateIntrinsicContentSize];
}

#if PLATFORM(MAC)
- (BOOL)textView:(NSTextView *)textView clickedOnLink:(id)link atIndex:(NSUInteger)charIndex
{
[self clickedOnLink:link];
return YES;
}

- (void)layout
{
[super layout];
[self layoutText];
}
#else
- (void)layoutSubviews
{
[super layoutSubviews];
[self layoutText];
}

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction
{
[self clickedOnLink:URL];
return NO;
}

- (void)didMoveToWindow
{
[self addContent];
}
#endif

- (void)dealloc
{
if (_completionHandler)
_completionHandler(WebKit::ContinueUnsafeLoad::No);
[super dealloc];
}

- (void)goBackClicked
{
if (_completionHandler)
_completionHandler(WebKit::ContinueUnsafeLoad::No);
}

- (void)clickedOnLink:(NSURL *)link
{
if (!_completionHandler)
return;

if ([link isEqual:WebKit::SafeBrowsingWarning::visitUnsafeWebsiteSentinel()])
return _completionHandler(WebKit::ContinueUnsafeLoad::Yes);

if ([link isEqual:WebKit::SafeBrowsingWarning::confirmMalwareSentinel()]) {
#if PLATFORM(MAC)
auto alert = adoptNS([NSAlert new]);
[alert setMessageText:WEB_UI_NSSTRING(@"Are you sure you wish to go to this site?", "Malware confirmation dialog title")];
[alert setInformativeText:WEB_UI_NSSTRING(@"Merely visiting a site is sufficient for malware to install itself and harm your computer.", "Malware confirmation dialog")];
[alert addButtonWithTitle:WEB_UI_NSSTRING(@"Cancel", "Cancel")];
[alert addButtonWithTitle:WEB_UI_NSSTRING(@"Continue", "Continue")];
[alert beginSheetModalForWindow:self.window completionHandler:makeBlockPtr([weakSelf = WeakObjCPtr<WKSafeBrowsingWarning>(self), alert](NSModalResponse returnCode) {
if (auto strongSelf = weakSelf.get()) {
if (returnCode == NSAlertSecondButtonReturn && strongSelf->_completionHandler)
strongSelf->_completionHandler(WebKit::ContinueUnsafeLoad::Yes);
}
}).get()];
#else
_completionHandler(WebKit::ContinueUnsafeLoad::Yes);
#endif
return;
}

ASSERT([link isKindOfClass:[NSURL class]]);
_completionHandler((NSURL *)link);
}

- (BOOL)forMainFrameNavigation
{
return _warning->forMainFrameNavigation();
}

@end

@implementation WKSafeBrowsingTextView

- (instancetype)initWithAttributedString:(NSAttributedString *)attributedString forWarning:(WKSafeBrowsingWarning *)warning
{
if (!(self = [super init]))
return nil;
self->_warning = warning;
self.delegate = warning;

ColorType *foregroundColor = colorForItem(WarningItem::MessageText, warning);
NSMutableAttributedString *string = [[attributedString mutableCopy] autorelease];
[string addAttributes:@{ NSForegroundColorAttributeName : foregroundColor } range:NSMakeRange(0, string.length)];
[self setBackgroundColor:colorForItem(WarningItem::BoxBackground, warning)];
[self setLinkTextAttributes:@{ NSForegroundColorAttributeName : foregroundColor }];
[self.textStorage appendAttributedString:string];
self.editable = NO;
#if !PLATFORM(MAC)
self.scrollEnabled = NO;
#endif

return self;
}

- (SizeType)intrinsicContentSize
{
#if PLATFORM(MAC)
[self.layoutManager ensureLayoutForTextContainer:self.textContainer];
return { NSViewNoIntrinsicMetric, [self.layoutManager usedRectForTextContainer:self.textContainer].size.height };
#elif HAVE(SAFE_BROWSING)
auto width = std::min<CGFloat>(maxWidth, [_warning frame].size.width) - 2 * marginSize;
constexpr auto noHeightConstraint = CGFLOAT_MAX;
return { width, [self sizeThatFits: { width, noHeightConstraint }].height };
#else
return { 0, 0 };
#endif
}

@end