Stop using hyphenationFactor
[WebKit-https.git] / Source / WebKit / UIProcess / Cocoa / WKSafeBrowsingWarning.mm
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKSafeBrowsingWarning.h"
28
29 #import "PageClient.h"
30 #import "SafeBrowsingWarning.h"
31 #import <WebCore/LocalizedStrings.h>
32 #import <wtf/URL.h>
33 #import <wtf/BlockPtr.h>
34 #import <wtf/Language.h>
35
36 #if PLATFORM(WATCHOS)
37 #import "UIKitSPI.h"
38 #import <PepperUICore/UIScrollView+PUICAdditionsPrivate.h>
39 #endif
40
41 constexpr CGFloat exclamationPointSize = 30;
42 constexpr CGFloat boxCornerRadius = 6;
43 #if HAVE(SAFE_BROWSING)
44 #if PLATFORM(WATCHOS)
45 constexpr CGFloat marginSize = 9;
46 #else
47 constexpr CGFloat marginSize = 20;
48 #endif
49 constexpr CGFloat maxWidth = 675;
50 #endif
51
52 #if PLATFORM(MAC)
53 using ColorType = NSColor;
54 using FontType = NSFont;
55 using TextViewType = NSTextView;
56 using ButtonType = NSButton;
57 using AlignmentType = NSLayoutAttribute;
58 using SizeType = NSSize;
59 #else
60 using ColorType = UIColor;
61 using FontType = UIFont;
62 using TextViewType = UITextView;
63 using ButtonType = UIButton;
64 using AlignmentType = UIStackViewAlignment;
65 using SizeType = CGSize;
66 #endif
67
68 enum class WarningItem : uint8_t {
69     Background,
70     BoxBackground,
71     ExclamationPoint,
72     TitleText,
73     MessageText,
74     ShowDetailsButton,
75     GoBackButton
76 };
77
78 enum class WarningTextSize : uint8_t {
79     Title,
80     Body
81 };
82
83 static FontType *fontOfSize(WarningTextSize size)
84 {
85 #if PLATFORM(MAC)
86     switch (size) {
87     case WarningTextSize::Title:
88         return [NSFont boldSystemFontOfSize:26];
89     case WarningTextSize::Body:
90         return [NSFont systemFontOfSize:14];
91     }
92 #elif HAVE(SAFE_BROWSING)
93     switch (size) {
94     case WarningTextSize::Title:
95 #if PLATFORM(WATCHOS)
96         return [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
97 #else
98         return [UIFont preferredFontForTextStyle:UIFontTextStyleLargeTitle];
99 #endif
100     case WarningTextSize::Body:
101         return [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
102     }
103 #else
104     return nil;
105 #endif
106 }
107
108 static ColorType *colorForItem(WarningItem item, ViewType *warning)
109 {
110     ASSERT([warning isKindOfClass:[WKSafeBrowsingWarning class]]);
111 #if PLATFORM(MAC)
112
113     auto colorNamed = [] (NSString *name) -> ColorType* {
114 #if HAVE(SAFE_BROWSING)
115         return [NSColor colorNamed:name bundle:[NSBundle bundleWithIdentifier:@"com.apple.WebKit"]];
116 #else
117         ASSERT_NOT_REACHED();
118         return nil;
119 #endif
120     };
121
122     switch (item) {
123     case WarningItem::Background:
124         return colorNamed(@"WKSafeBrowsingWarningBackground");
125     case WarningItem::BoxBackground:
126         return [NSColor windowBackgroundColor];
127     case WarningItem::TitleText:
128     case WarningItem::ExclamationPoint:
129         return colorNamed(@"WKSafeBrowsingWarningTitle");
130     case WarningItem::MessageText:
131         return colorNamed(@"WKSafeBrowsingWarningText");
132     case WarningItem::ShowDetailsButton:
133     case WarningItem::GoBackButton:
134         ASSERT_NOT_REACHED();
135         return nil;
136     }
137 #else
138     UIColor *red = [UIColor colorWithRed:0.998 green:0.239 blue:0.233 alpha:1.0];
139     UIColor *white = [UIColor whiteColor];
140     bool narrow = warning.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact;
141
142     switch (item) {
143     case WarningItem::Background:
144         return red;
145     case WarningItem::BoxBackground:
146         return narrow ? red : white;
147     case WarningItem::TitleText:
148     case WarningItem::ExclamationPoint:
149         return narrow ? white : red;
150     case WarningItem::MessageText:
151     case WarningItem::ShowDetailsButton:
152         return narrow ? white : [UIColor darkTextColor];
153     case WarningItem::GoBackButton:
154         return narrow ? white : warning.tintColor;
155     }
156 #endif
157     ASSERT_NOT_REACHED();
158     return nil;
159 }
160
161 @interface WKSafeBrowsingExclamationPoint : ViewType
162 @end
163
164 @implementation WKSafeBrowsingExclamationPoint
165
166 - (void)drawRect:(RectType)rect
167 {
168     constexpr CGFloat centerX = exclamationPointSize / 2;
169     constexpr CGFloat pointCenterY = exclamationPointSize * 7 / 30;
170     constexpr CGFloat pointRadius = 2.25 * exclamationPointSize / 30;
171     constexpr CGFloat lineBottomCenterY = exclamationPointSize * 13 / 30;
172     constexpr CGFloat lineTopCenterY = exclamationPointSize * 23 / 30;
173     constexpr CGFloat lineRadius = 1.75 * exclamationPointSize / 30;
174     ViewType *warning = self.superview.superview;
175 #if PLATFORM(MAC)
176     [colorForItem(WarningItem::ExclamationPoint, warning) set];
177     NSBezierPath *exclamationPoint = [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(0, 0, exclamationPointSize, exclamationPointSize)];
178     [exclamationPoint appendBezierPathWithArcWithCenter: { centerX, lineBottomCenterY } radius:lineRadius startAngle:0 endAngle:180 clockwise:YES];
179     [exclamationPoint appendBezierPathWithArcWithCenter: { centerX, lineTopCenterY } radius:lineRadius startAngle:180 endAngle:360 clockwise:YES];
180     [exclamationPoint lineToPoint: { centerX + lineRadius, lineBottomCenterY }];
181     [exclamationPoint appendBezierPathWithArcWithCenter: { centerX, pointCenterY } radius:pointRadius startAngle:0 endAngle:180 clockwise:YES];
182     [exclamationPoint appendBezierPathWithArcWithCenter: { centerX, pointCenterY } radius:pointRadius startAngle:180 endAngle:360 clockwise:YES];
183 #else
184     auto flip = [] (auto y) {
185         return exclamationPointSize - y;
186     };
187     [colorForItem(WarningItem::BoxBackground, warning) set];
188     auto square = CGRectMake(0, 0, exclamationPointSize, exclamationPointSize);
189     [[UIBezierPath bezierPathWithRect:square] fill];
190     
191     [colorForItem(WarningItem::ExclamationPoint, warning) set];
192     UIBezierPath *exclamationPoint = [UIBezierPath bezierPathWithOvalInRect:square];
193     [exclamationPoint addArcWithCenter: { centerX, flip(lineTopCenterY) } radius:lineRadius startAngle:2 * piDouble endAngle:piDouble clockwise:NO];
194     [exclamationPoint addArcWithCenter: { centerX, flip(lineBottomCenterY) } radius:lineRadius startAngle:piDouble endAngle:0 clockwise:NO];
195     [exclamationPoint addArcWithCenter: { centerX, flip(pointCenterY) } radius:pointRadius startAngle:0 endAngle:piDouble clockwise:NO];
196     [exclamationPoint addArcWithCenter: { centerX, flip(pointCenterY) } radius:pointRadius startAngle:piDouble endAngle:piDouble * 2 clockwise:NO];
197     [exclamationPoint addLineToPoint: { centerX + lineRadius, flip(lineBottomCenterY) }];
198     [exclamationPoint addLineToPoint: { centerX + lineRadius, flip(lineTopCenterY) }];
199 #endif
200     [exclamationPoint fill];
201 }
202
203 - (NSSize)intrinsicContentSize
204 {
205     return { exclamationPointSize, exclamationPointSize };
206 }
207
208 @end
209
210 static ButtonType *makeButton(WarningItem item, WKSafeBrowsingWarning *warning, SEL action)
211 {
212     NSString *title = nil;
213     if (item == WarningItem::ShowDetailsButton)
214         title = WEB_UI_NSSTRING(@"Show Details", "Action from safe browsing warning");
215     else
216         title = WEB_UI_NSSTRING(@"Go Back", "Action from safe browsing warning");
217 #if PLATFORM(MAC)
218     return [NSButton buttonWithTitle:title target:warning action:action];
219 #else
220     UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
221     NSAttributedString *attributedTitle = [[[NSAttributedString alloc] initWithString:title attributes:@{
222         NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle),
223         NSUnderlineColorAttributeName:[UIColor whiteColor],
224         NSForegroundColorAttributeName:colorForItem(item, warning),
225         NSFontAttributeName:fontOfSize(WarningTextSize::Body)
226     }] autorelease];
227     [button setAttributedTitle:attributedTitle forState:UIControlStateNormal];
228     [button addTarget:warning action:action forControlEvents:UIControlEventTouchUpInside];
229     return button;
230 #endif
231 }
232
233 #if HAVE(SAFE_BROWSING)
234 static CGSize buttonSize(ButtonType *button)
235 {
236 #if PLATFORM(MAC)
237     return button.frame.size;
238 #else
239     return button.titleLabel.intrinsicContentSize;
240 #endif
241 }
242 #endif
243
244 static ViewType *makeLabel(NSAttributedString *attributedString)
245 {
246 #if PLATFORM(MAC)
247     return [NSTextField labelWithAttributedString:attributedString];
248 #else
249     auto label = [[UILabel new] autorelease];
250     label.attributedText = attributedString;
251     label.lineBreakMode = NSLineBreakByWordWrapping;
252     label.numberOfLines = 0;
253     return label;
254 #endif
255 }
256
257 static void setBackground(ViewType *view, ColorType *color)
258 {
259 #if PLATFORM(MAC)
260     view.wantsLayer = YES;
261     view.layer.backgroundColor = color.CGColor;
262 #else
263     view.backgroundColor = color;
264 #endif
265 }
266
267 @interface WKSafeBrowsingTextView : TextViewType {
268 @package
269     WeakObjCPtr<WKSafeBrowsingWarning> _warning;
270 }
271 - (instancetype)initWithAttributedString:(NSAttributedString *)attributedString forWarning:(WKSafeBrowsingWarning *)warning;
272 @end
273
274 @implementation WKSafeBrowsingWarning
275
276 - (instancetype)initWithFrame:(RectType)frame safeBrowsingWarning:(const WebKit::SafeBrowsingWarning&)warning completionHandler:(CompletionHandler<void(Variant<WebKit::ContinueUnsafeLoad, URL>&&)>&&)completionHandler
277 {
278     if (!(self = [super initWithFrame:frame])) {
279         completionHandler(WebKit::ContinueUnsafeLoad::Yes);
280         return nil;
281     }
282     _completionHandler = [weakSelf = WeakObjCPtr<WKSafeBrowsingWarning>(self), completionHandler = WTFMove(completionHandler)] (Variant<WebKit::ContinueUnsafeLoad, URL>&& result) mutable {
283 #if PLATFORM(WATCHOS)
284         if (auto strongSelf = weakSelf.get())
285             [strongSelf.get()->_previousFirstResponder becomeFirstResponder];
286 #endif
287         completionHandler(WTFMove(result));
288     };
289     _warning = makeRef(warning);
290     setBackground(self, colorForItem(WarningItem::Background, self));
291 #if PLATFORM(MAC)
292     [self addContent];
293 #endif
294
295 #if PLATFORM(WATCHOS)
296     self.crownInputScrollDirection = PUICCrownInputScrollDirectionVertical;
297 #endif
298     return self;
299 }
300
301 - (void)addContent
302 {
303     auto exclamationPoint = [[WKSafeBrowsingExclamationPoint new] autorelease];
304     auto title = makeLabel([[[NSAttributedString alloc] initWithString:_warning->title() attributes:@{
305         NSFontAttributeName:fontOfSize(WarningTextSize::Title),
306         NSForegroundColorAttributeName:colorForItem(WarningItem::TitleText, self)
307 #if PLATFORM(WATCHOS)
308         , NSHyphenationFactorDocumentAttribute:@1
309 #endif
310     }] autorelease]);
311     auto warning = makeLabel([[[NSAttributedString alloc] initWithString:_warning->warning() attributes:@{
312         NSFontAttributeName:fontOfSize(WarningTextSize::Body),
313         NSForegroundColorAttributeName:colorForItem(WarningItem::MessageText, self)
314 #if PLATFORM(WATCHOS)
315         , NSHyphenationFactorDocumentAttribute:@1
316 #endif
317     }] autorelease]);
318     auto showDetails = makeButton(WarningItem::ShowDetailsButton, self, @selector(showDetailsClicked));
319     auto goBack = makeButton(WarningItem::GoBackButton, self, @selector(goBackClicked));
320     auto box = [[ViewType new] autorelease];
321     _box = box;
322     setBackground(box, colorForItem(WarningItem::BoxBackground, self));
323     box.layer.cornerRadius = boxCornerRadius;
324
325     for (ViewType *view in @[exclamationPoint, title, warning, goBack, showDetails]) {
326         view.translatesAutoresizingMaskIntoConstraints = NO;
327         [box addSubview:view];
328     }
329     box.translatesAutoresizingMaskIntoConstraints = NO;
330     [self addSubview:box];
331
332 #if PLATFORM(WATCHOS)
333     [NSLayoutConstraint activateConstraints:@[
334         [[box.leadingAnchor anchorWithOffsetToAnchor:exclamationPoint.leadingAnchor] constraintEqualToAnchor:[exclamationPoint.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor]],
335         [[box.leadingAnchor anchorWithOffsetToAnchor:title.leadingAnchor] constraintEqualToConstant:marginSize],
336         [[title.bottomAnchor anchorWithOffsetToAnchor:warning.topAnchor] constraintEqualToConstant:marginSize],
337         [[exclamationPoint.bottomAnchor anchorWithOffsetToAnchor:title.topAnchor] constraintEqualToConstant:marginSize],
338         [[box.topAnchor anchorWithOffsetToAnchor:exclamationPoint.topAnchor] constraintEqualToConstant:marginSize + self.frame.size.height / 2],
339         [[self.topAnchor anchorWithOffsetToAnchor:box.topAnchor] constraintEqualToAnchor:[box.bottomAnchor anchorWithOffsetToAnchor:self.bottomAnchor] multiplier:0.2],
340     ]];
341 #elif HAVE(SAFE_BROWSING)
342     [NSLayoutConstraint activateConstraints:@[
343         [[box.leadingAnchor anchorWithOffsetToAnchor:exclamationPoint.leadingAnchor] constraintEqualToConstant:marginSize],
344         [[box.leadingAnchor anchorWithOffsetToAnchor:title.leadingAnchor] constraintEqualToConstant:marginSize * 1.5 + exclamationPointSize],
345         [[title.topAnchor anchorWithOffsetToAnchor:exclamationPoint.topAnchor] constraintEqualToAnchor:[exclamationPoint.bottomAnchor anchorWithOffsetToAnchor:title.bottomAnchor]],
346         [[title.bottomAnchor anchorWithOffsetToAnchor:warning.topAnchor] constraintEqualToConstant:marginSize],
347         [[box.topAnchor anchorWithOffsetToAnchor:title.topAnchor] constraintEqualToConstant:marginSize],
348         [[self.topAnchor anchorWithOffsetToAnchor:box.topAnchor] constraintEqualToAnchor:[box.bottomAnchor anchorWithOffsetToAnchor:self.bottomAnchor] multiplier:0.5],
349     ]];
350 #endif
351
352 #if HAVE(SAFE_BROWSING)
353     [NSLayoutConstraint activateConstraints:@[
354         [[self.leftAnchor anchorWithOffsetToAnchor:box.leftAnchor] constraintEqualToAnchor:[box.rightAnchor anchorWithOffsetToAnchor:self.rightAnchor]],
355
356         [box.widthAnchor constraintLessThanOrEqualToConstant:maxWidth],
357         [box.widthAnchor constraintLessThanOrEqualToAnchor:self.widthAnchor],
358
359         [[box.leadingAnchor anchorWithOffsetToAnchor:warning.leadingAnchor] constraintEqualToConstant:marginSize],
360
361         [[title.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor] constraintGreaterThanOrEqualToConstant:marginSize],
362         [[warning.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor] constraintGreaterThanOrEqualToConstant:marginSize],
363         [[goBack.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor] constraintEqualToConstant:marginSize],
364
365         [[warning.bottomAnchor anchorWithOffsetToAnchor:goBack.topAnchor] constraintEqualToConstant:marginSize],
366     ]];
367     
368     bool needsVerticalButtonLayout = buttonSize(showDetails).width + buttonSize(goBack).width + 3 * marginSize > self.frame.size.width;
369     if (needsVerticalButtonLayout) {
370         [NSLayoutConstraint activateConstraints:@[
371             [[showDetails.trailingAnchor anchorWithOffsetToAnchor:box.trailingAnchor] constraintEqualToConstant:marginSize],
372             [[goBack.bottomAnchor anchorWithOffsetToAnchor:showDetails.topAnchor] constraintEqualToConstant:marginSize],
373             [[goBack.bottomAnchor anchorWithOffsetToAnchor:box.bottomAnchor] constraintEqualToConstant:marginSize * 2 + buttonSize(showDetails).height],
374         ]];
375     } else {
376         [NSLayoutConstraint activateConstraints:@[
377             [[showDetails.trailingAnchor anchorWithOffsetToAnchor:goBack.leadingAnchor] constraintEqualToConstant:marginSize],
378             [goBack.topAnchor constraintEqualToAnchor:showDetails.topAnchor],
379             [[goBack.bottomAnchor anchorWithOffsetToAnchor:box.bottomAnchor] constraintEqualToConstant:marginSize],
380         ]];
381     }
382 #if !PLATFORM(MAC)
383     [self updateContentSize];
384 #endif
385 #endif
386     
387 #if PLATFORM(WATCHOS)
388     self->_previousFirstResponder = [self firstResponder];
389     [self becomeFirstResponder];
390 #endif
391 }
392
393 - (void)showDetailsClicked
394 {
395     ViewType *box = _box.get().get();
396     ButtonType *showDetails = box.subviews.lastObject;
397     [showDetails removeFromSuperview];
398
399     NSMutableAttributedString *text = [[_warning->details() mutableCopy] autorelease];
400     [text addAttributes:@{ NSFontAttributeName:fontOfSize(WarningTextSize::Body) } range:NSMakeRange(0, text.length)];
401     WKSafeBrowsingTextView *details = [[[WKSafeBrowsingTextView alloc] initWithAttributedString:text forWarning:self] autorelease];
402     _details = details;
403     ViewType *bottom = [[ViewType new] autorelease];
404     setBackground(bottom, colorForItem(WarningItem::BoxBackground, self));
405     bottom.layer.cornerRadius = boxCornerRadius;
406
407 #if HAVE(SAFE_BROWSING)
408     constexpr auto maxY = kCALayerMinXMaxYCorner | kCALayerMaxXMaxYCorner;
409     constexpr auto minY = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
410 #if PLATFORM(MAC)
411     box.layer.maskedCorners = maxY;
412     bottom.layer.maskedCorners = minY;
413 #else
414     box.layer.maskedCorners = minY;
415     bottom.layer.maskedCorners = maxY;
416 #endif
417 #endif
418
419     ViewType *line = [[ViewType new] autorelease];
420     setBackground(line, [ColorType lightGrayColor]);
421     for (ViewType *view in @[details, bottom, line])
422         view.translatesAutoresizingMaskIntoConstraints = NO;
423
424     [self addSubview:bottom];
425     [bottom addSubview:line];
426     [bottom addSubview:details];
427 #if HAVE(SAFE_BROWSING)
428     [NSLayoutConstraint activateConstraints:@[
429         [box.widthAnchor constraintEqualToAnchor:bottom.widthAnchor],
430         [box.bottomAnchor constraintEqualToAnchor:bottom.topAnchor],
431         [box.leadingAnchor constraintEqualToAnchor:bottom.leadingAnchor],
432         [line.widthAnchor constraintEqualToAnchor:bottom.widthAnchor],
433         [line.leadingAnchor constraintEqualToAnchor:bottom.leadingAnchor],
434         [line.topAnchor constraintEqualToAnchor:bottom.topAnchor],
435         [line.heightAnchor constraintEqualToConstant:1],
436         [[bottom.topAnchor anchorWithOffsetToAnchor:details.topAnchor] constraintEqualToConstant:marginSize],
437         [[details.bottomAnchor anchorWithOffsetToAnchor:bottom.bottomAnchor] constraintEqualToConstant:marginSize],
438         [[bottom.leadingAnchor anchorWithOffsetToAnchor:details.leadingAnchor] constraintEqualToConstant:marginSize],
439         [[details.trailingAnchor anchorWithOffsetToAnchor:bottom.trailingAnchor] constraintEqualToConstant:marginSize],
440     ]];
441 #endif
442     [self layoutText];
443 #if !PLATFORM(MAC)
444     [self updateContentSize];
445 #endif
446 }
447
448 #if !PLATFORM(MAC)
449 - (void)updateContentSize
450 {
451     [self layoutIfNeeded];
452     CGFloat height = 0;
453     for (ViewType *subview in self.subviews)
454         height += subview.frame.size.height;
455     [self setContentSize: { self.frame.size.width, self.frame.size.height / 2 + height }];
456 }
457 #endif
458
459 - (void)layoutText
460 {
461     [_details invalidateIntrinsicContentSize];
462 }
463
464 #if PLATFORM(MAC)
465 - (BOOL)textView:(NSTextView *)textView clickedOnLink:(id)link atIndex:(NSUInteger)charIndex
466 {
467     [self clickedOnLink:link];
468     return YES;
469 }
470
471 - (void)layout
472 {
473     [super layout];
474     [self layoutText];
475 }
476 #else
477 - (void)layoutSubviews
478 {
479     [super layoutSubviews];
480     [self layoutText];
481 }
482
483 - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction
484 {
485     [self clickedOnLink:URL];
486     return NO;
487 }
488
489 - (void)didMoveToWindow
490 {
491     [self addContent];
492 }
493 #endif
494
495 - (void)dealloc
496 {
497     if (_completionHandler)
498         _completionHandler(WebKit::ContinueUnsafeLoad::No);
499     [super dealloc];
500 }
501
502 - (void)goBackClicked
503 {
504     if (_completionHandler)
505         _completionHandler(WebKit::ContinueUnsafeLoad::No);
506 }
507
508 - (void)clickedOnLink:(NSURL *)link
509 {
510     if (!_completionHandler)
511         return;
512
513     if ([link isEqual:WebKit::SafeBrowsingWarning::visitUnsafeWebsiteSentinel()])
514         return _completionHandler(WebKit::ContinueUnsafeLoad::Yes);
515
516     if ([link isEqual:WebKit::SafeBrowsingWarning::confirmMalwareSentinel()]) {
517 #if PLATFORM(MAC)
518         auto alert = adoptNS([NSAlert new]);
519         [alert setMessageText:WEB_UI_NSSTRING(@"Are you sure you wish to go to this site?", "Malware confirmation dialog title")];
520         [alert setInformativeText:WEB_UI_NSSTRING(@"Merely visiting a site is sufficient for malware to install itself and harm your computer.", "Malware confirmation dialog")];
521         [alert addButtonWithTitle:WEB_UI_NSSTRING(@"Cancel", "Cancel")];
522         [alert addButtonWithTitle:WEB_UI_NSSTRING(@"Continue", "Continue")];
523         [alert beginSheetModalForWindow:self.window completionHandler:makeBlockPtr([weakSelf = WeakObjCPtr<WKSafeBrowsingWarning>(self), alert](NSModalResponse returnCode) {
524             if (auto strongSelf = weakSelf.get()) {
525                 if (returnCode == NSAlertSecondButtonReturn && strongSelf->_completionHandler)
526                     strongSelf->_completionHandler(WebKit::ContinueUnsafeLoad::Yes);
527             }
528         }).get()];
529 #else
530         _completionHandler(WebKit::ContinueUnsafeLoad::Yes);
531 #endif
532         return;
533     }
534
535     ASSERT([link isKindOfClass:[NSURL class]]);
536     _completionHandler((NSURL *)link);
537 }
538
539 - (BOOL)forMainFrameNavigation
540 {
541     return _warning->forMainFrameNavigation();
542 }
543
544 @end
545
546 @implementation WKSafeBrowsingTextView
547
548 - (instancetype)initWithAttributedString:(NSAttributedString *)attributedString forWarning:(WKSafeBrowsingWarning *)warning
549 {
550     if (!(self = [super init]))
551         return nil;
552     self->_warning = warning;
553     self.delegate = warning;
554
555     ColorType *foregroundColor = colorForItem(WarningItem::MessageText, warning);
556     NSMutableAttributedString *string = [[attributedString mutableCopy] autorelease];
557     [string addAttributes:@{ NSForegroundColorAttributeName : foregroundColor } range:NSMakeRange(0, string.length)];
558     [self setBackgroundColor:colorForItem(WarningItem::BoxBackground, warning)];
559     [self setLinkTextAttributes:@{ NSForegroundColorAttributeName : foregroundColor }];
560     [self.textStorage appendAttributedString:string];
561     self.editable = NO;
562 #if !PLATFORM(MAC)
563     self.scrollEnabled = NO;
564 #endif
565
566     return self;
567 }
568
569 - (SizeType)intrinsicContentSize
570 {
571 #if PLATFORM(MAC)
572     [self.layoutManager ensureLayoutForTextContainer:self.textContainer];
573     return { NSViewNoIntrinsicMetric, [self.layoutManager usedRectForTextContainer:self.textContainer].size.height };
574 #elif HAVE(SAFE_BROWSING)
575     auto width = std::min<CGFloat>(maxWidth, [_warning frame].size.width) - 2 * marginSize;
576     constexpr auto noHeightConstraint = CGFLOAT_MAX;
577     return { width, [self sizeThatFits: { width, noHeightConstraint }].height };
578 #else
579     return { 0, 0 };
580 #endif
581 }
582
583 @end