Remove STREAMS_API compilation guard
[WebKit-https.git] / Source / WebKitLegacy / mac / WebView / WebImmediateActionController.mm
1 /*
2  * Copyright (C) 2014 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 "WebImmediateActionController.h"
27
28 #if PLATFORM(MAC)
29
30 #import "DOMElementInternal.h"
31 #import "DOMNodeInternal.h"
32 #import "DOMRangeInternal.h"
33 #import "WebElementDictionary.h"
34 #import "WebFrameInternal.h"
35 #import "WebHTMLView.h"
36 #import "WebHTMLViewInternal.h"
37 #import "WebUIDelegatePrivate.h"
38 #import "WebViewInternal.h"
39 #import <WebCore/DataDetection.h>
40 #import <WebCore/DataDetectorsSPI.h>
41 #import <WebCore/DictionaryLookup.h>
42 #import <WebCore/Editor.h>
43 #import <WebCore/EventHandler.h>
44 #import <WebCore/FocusController.h>
45 #import <WebCore/FontMetrics.h>
46 #import <WebCore/Frame.h>
47 #import <WebCore/FrameView.h>
48 #import <WebCore/GeometryUtilities.h>
49 #import <WebCore/HTMLConverter.h>
50 #import <WebCore/LookupSPI.h>
51 #import <WebCore/NSMenuSPI.h>
52 #import <WebCore/Page.h>
53 #import <WebCore/QuickLookMacSPI.h>
54 #import <WebCore/RenderElement.h>
55 #import <WebCore/RenderObject.h>
56 #import <WebCore/RuntimeApplicationChecks.h>
57 #import <WebCore/TextIndicator.h>
58 #import <objc/objc-class.h>
59 #import <objc/objc.h>
60 #import <wtf/SoftLinking.h>
61
62 SOFT_LINK_FRAMEWORK_IN_UMBRELLA(Quartz, QuickLookUI)
63 SOFT_LINK_CLASS(QuickLookUI, QLPreviewMenuItem)
64
65 @interface WebImmediateActionController () <QLPreviewMenuItemDelegate>
66 @end
67
68 @interface WebAnimationController : NSObject <NSImmediateActionAnimationController>
69 @end
70
71 @implementation WebAnimationController
72 @end
73
74 using namespace WebCore;
75
76 @implementation WebImmediateActionController
77
78 - (instancetype)initWithWebView:(WebView *)webView recognizer:(NSImmediateActionGestureRecognizer *)immediateActionRecognizer
79 {
80     if (!(self = [super init]))
81         return nil;
82
83     _webView = webView;
84     _type = WebImmediateActionNone;
85     _immediateActionRecognizer = immediateActionRecognizer;
86
87     return self;
88 }
89
90 - (void)webViewClosed
91 {
92     _webView = nil;
93
94     id animationController = [_immediateActionRecognizer animationController];
95     if ([animationController isKindOfClass:NSClassFromString(@"QLPreviewMenuItem")]) {
96         QLPreviewMenuItem *menuItem = (QLPreviewMenuItem *)animationController;
97         menuItem.delegate = nil;
98     }
99
100     _immediateActionRecognizer = nil;
101     _currentActionContext = nil;
102 }
103
104 - (void)webView:(WebView *)webView didHandleScrollWheel:(NSEvent *)event
105 {
106     [_currentQLPreviewMenuItem close];
107     [self _clearImmediateActionState];
108     [_webView _clearTextIndicatorWithAnimation:TextIndicatorWindowDismissalAnimation::None];
109 }
110
111 - (NSImmediateActionGestureRecognizer *)immediateActionRecognizer
112 {
113     return _immediateActionRecognizer.get();
114 }
115
116 - (void)_cancelImmediateAction
117 {
118     // Reset the recognizer by turning it off and on again.
119     [_immediateActionRecognizer setEnabled:NO];
120     [_immediateActionRecognizer setEnabled:YES];
121
122     [self _clearImmediateActionState];
123     [_webView _clearTextIndicatorWithAnimation:TextIndicatorWindowDismissalAnimation::FadeOut];
124 }
125
126 - (void)_clearImmediateActionState
127 {
128     if (!DataDetectorsLibrary())
129         return;
130
131     DDActionsManager *actionsManager = [getDDActionsManagerClass() sharedManager];
132     [actionsManager requestBubbleClosureUnanchorOnFailure:YES];
133
134     if (_currentActionContext && _hasActivatedActionContext) {
135         _hasActivatedActionContext = NO;
136         [getDDActionsManagerClass() didUseActions];
137     }
138
139     _type = WebImmediateActionNone;
140     _currentActionContext = nil;
141     _currentQLPreviewMenuItem = nil;
142     _contentPreventsDefault = NO;
143 }
144
145 - (void)performHitTestAtPoint:(NSPoint)viewPoint
146 {
147     Frame* coreFrame = core([[[[_webView _selectedOrMainFrame] frameView] documentView] _frame]);
148     if (!coreFrame)
149         return;
150     _hitTestResult = coreFrame->eventHandler().hitTestResultAtPoint(IntPoint(viewPoint));
151     coreFrame->eventHandler().setImmediateActionStage(ImmediateActionStage::PerformedHitTest);
152
153     if (Element* element = _hitTestResult.targetElement())
154         _contentPreventsDefault = element->dispatchMouseForceWillBegin();
155 }
156
157 #pragma mark NSImmediateActionGestureRecognizerDelegate
158
159 - (void)immediateActionRecognizerWillPrepare:(NSImmediateActionGestureRecognizer *)immediateActionRecognizer
160 {
161     if (!_webView)
162         return;
163
164     NSView *documentView = [[[_webView _selectedOrMainFrame] frameView] documentView];
165     if (![documentView isKindOfClass:[WebHTMLView class]]) {
166         [self _cancelImmediateAction];
167         return;
168     }
169
170     if (immediateActionRecognizer != _immediateActionRecognizer)
171         return;
172
173     [_webView _setMaintainsInactiveSelection:YES];
174
175     NSPoint locationInDocumentView = [immediateActionRecognizer locationInView:documentView];
176     [self performHitTestAtPoint:locationInDocumentView];
177     [self _updateImmediateActionItem];
178
179     if (![_immediateActionRecognizer animationController]) {
180         // FIXME: We should be able to remove the dispatch_async when rdar://problem/19502927 is resolved.
181         dispatch_async(dispatch_get_main_queue(), ^{
182             [self _cancelImmediateAction];
183         });
184     }
185 }
186
187 - (void)immediateActionRecognizerWillBeginAnimation:(NSImmediateActionGestureRecognizer *)immediateActionRecognizer
188 {
189     if (!DataDetectorsLibrary())
190         return;
191
192     if (immediateActionRecognizer != _immediateActionRecognizer)
193         return;
194
195     if (_currentActionContext) {
196         _hasActivatedActionContext = YES;
197         if (![getDDActionsManagerClass() shouldUseActionsWithContext:_currentActionContext.get()])
198             [self _cancelImmediateAction];
199     }
200 }
201
202 - (void)immediateActionRecognizerDidUpdateAnimation:(NSImmediateActionGestureRecognizer *)immediateActionRecognizer
203 {
204     if (immediateActionRecognizer != _immediateActionRecognizer)
205         return;
206
207     Frame* coreFrame = core([[[[_webView _selectedOrMainFrame] frameView] documentView] _frame]);
208     if (!coreFrame)
209         return;
210     coreFrame->eventHandler().setImmediateActionStage(ImmediateActionStage::ActionUpdated);
211     if (_contentPreventsDefault)
212         return;
213
214     [_webView _setTextIndicatorAnimationProgress:[immediateActionRecognizer animationProgress]];
215 }
216
217 - (void)immediateActionRecognizerDidCancelAnimation:(NSImmediateActionGestureRecognizer *)immediateActionRecognizer
218 {
219     if (immediateActionRecognizer != _immediateActionRecognizer)
220         return;
221
222     NSView *documentView = [[[_webView _selectedOrMainFrame] frameView] documentView];
223     if (![documentView isKindOfClass:[WebHTMLView class]])
224         return;
225
226     Frame* coreFrame = core([(WebHTMLView *)documentView _frame]);
227     if (coreFrame) {
228         ImmediateActionStage lastStage = coreFrame->eventHandler().immediateActionStage();
229         if (lastStage == ImmediateActionStage::ActionUpdated)
230             coreFrame->eventHandler().setImmediateActionStage(ImmediateActionStage::ActionCancelledAfterUpdate);
231         else
232             coreFrame->eventHandler().setImmediateActionStage(ImmediateActionStage::ActionCancelledWithoutUpdate);
233     }
234
235     [_webView _setTextIndicatorAnimationProgress:0];
236     [self _clearImmediateActionState];
237     [_webView _clearTextIndicatorWithAnimation:TextIndicatorWindowDismissalAnimation::None];
238     [_webView _setMaintainsInactiveSelection:NO];
239 }
240
241 - (void)immediateActionRecognizerDidCompleteAnimation:(NSImmediateActionGestureRecognizer *)immediateActionRecognizer
242 {
243     if (immediateActionRecognizer != _immediateActionRecognizer)
244         return;
245
246     Frame* coreFrame = core([[[[_webView _selectedOrMainFrame] frameView] documentView] _frame]);
247     if (!coreFrame)
248         return;
249     coreFrame->eventHandler().setImmediateActionStage(ImmediateActionStage::ActionCompleted);
250
251     [_webView _setTextIndicatorAnimationProgress:1];
252     [_webView _setMaintainsInactiveSelection:NO];
253 }
254
255 #pragma mark Immediate actions
256
257 - (id <NSImmediateActionAnimationController>)_defaultAnimationController
258 {
259     if (_contentPreventsDefault)
260         return [[[WebAnimationController alloc] init] autorelease];
261
262     NSURL *url = _hitTestResult.absoluteLinkURL();
263     NSString *absoluteURLString = [url absoluteString];
264     if (url && _hitTestResult.URLElement()) {
265         if (protocolIs(absoluteURLString, "mailto")) {
266             _type = WebImmediateActionMailtoLink;
267             return [self _animationControllerForDataDetectedLink];
268         }
269
270         if (protocolIs(absoluteURLString, "tel")) {
271             _type = WebImmediateActionTelLink;
272             return [self _animationControllerForDataDetectedLink];
273         }
274
275         if (WebCore::protocolIsInHTTPFamily(absoluteURLString)) {
276             _type = WebImmediateActionLinkPreview;
277
278             RefPtr<Range> linkRange = rangeOfContents(*_hitTestResult.URLElement());
279             auto indicator = TextIndicator::createWithRange(*linkRange, TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges, TextIndicatorPresentationTransition::FadeIn);
280             if (indicator)
281                 [_webView _setTextIndicator:*indicator withLifetime:TextIndicatorWindowLifetime::Permanent];
282
283             QLPreviewMenuItem *item = [NSMenuItem standardQuickLookMenuItem];
284             item.previewStyle = QLPreviewStylePopover;
285             item.delegate = self;
286             _currentQLPreviewMenuItem = item;
287             return (id <NSImmediateActionAnimationController>)item;
288         }
289     }
290
291     Node* node = _hitTestResult.innerNode();
292     if ((node && node->isTextNode()) || _hitTestResult.isOverTextInsideFormControlElement()) {
293         if (auto animationController = [self _animationControllerForDataDetectedText]) {
294             _type = WebImmediateActionDataDetectedItem;
295             return animationController;
296         }
297
298         if (auto animationController = [self _animationControllerForText]) {
299             _type = WebImmediateActionText;
300             return animationController;
301         }
302     }
303
304     return nil;
305 }
306
307 - (void)_updateImmediateActionItem
308 {
309     _type = WebImmediateActionNone;
310
311     id <NSImmediateActionAnimationController> defaultAnimationController = [self _defaultAnimationController];
312
313     if (_contentPreventsDefault) {
314         [_immediateActionRecognizer setAnimationController:defaultAnimationController];
315         return;
316     }
317
318     // Allow clients the opportunity to override the default immediate action.
319     id customClientAnimationController = nil;
320     if ([[_webView UIDelegate] respondsToSelector:@selector(_webView:immediateActionAnimationControllerForHitTestResult:withType:)]) {
321         RetainPtr<WebElementDictionary> webHitTestResult = adoptNS([[WebElementDictionary alloc] initWithHitTestResult:_hitTestResult]);
322         customClientAnimationController = [(id)[_webView UIDelegate] _webView:_webView immediateActionAnimationControllerForHitTestResult:webHitTestResult.get() withType:_type];
323     }
324
325     if (customClientAnimationController == [NSNull null]) {
326         [self _cancelImmediateAction];
327         return;
328     }
329
330 #if PLATFORM(MAC)
331     // FIXME: We should not permanently disable this for iTunes. rdar://problem/19461358
332     if (MacApplication::isITunes()) {
333         [self _cancelImmediateAction];
334         return;
335     }
336 #endif
337
338     if (customClientAnimationController && [customClientAnimationController conformsToProtocol:@protocol(NSImmediateActionAnimationController)])
339         [_immediateActionRecognizer setAnimationController:(id <NSImmediateActionAnimationController>)customClientAnimationController];
340     else
341         [_immediateActionRecognizer setAnimationController:defaultAnimationController];
342 }
343
344 #pragma mark QLPreviewMenuItemDelegate implementation
345
346 - (NSView *)menuItem:(NSMenuItem *)menuItem viewAtScreenPoint:(NSPoint)screenPoint
347 {
348     return _webView;
349 }
350
351 - (id<QLPreviewItem>)menuItem:(NSMenuItem *)menuItem previewItemAtPoint:(NSPoint)point
352 {
353     if (!_webView)
354         return nil;
355
356     return _hitTestResult.absoluteLinkURL();
357 }
358
359 - (NSRectEdge)menuItem:(NSMenuItem *)menuItem preferredEdgeForPoint:(NSPoint)point
360 {
361     return NSMaxYEdge;
362 }
363
364 - (void)menuItemDidClose:(NSMenuItem *)menuItem
365 {
366     [self _clearImmediateActionState];
367     [_webView _clearTextIndicatorWithAnimation:TextIndicatorWindowDismissalAnimation::FadeOut];
368 }
369
370 static IntRect elementBoundingBoxInWindowCoordinatesFromNode(Node* node)
371 {
372     if (!node)
373         return IntRect();
374
375     Frame* frame = node->document().frame();
376     if (!frame)
377         return IntRect();
378
379     FrameView* view = frame->view();
380     if (!view)
381         return IntRect();
382
383     RenderObject* renderer = node->renderer();
384     if (!renderer)
385         return IntRect();
386
387     return view->contentsToWindow(renderer->absoluteBoundingBoxRect());
388 }
389
390 - (NSRect)menuItem:(NSMenuItem *)menuItem itemFrameForPoint:(NSPoint)point
391 {
392     if (!_webView)
393         return NSZeroRect;
394
395     Node* node = _hitTestResult.innerNode();
396     if (!node)
397         return NSZeroRect;
398
399     return elementBoundingBoxInWindowCoordinatesFromNode(node);
400 }
401
402 - (NSSize)menuItem:(NSMenuItem *)menuItem maxSizeForPoint:(NSPoint)point
403 {
404     if (!_webView)
405         return NSZeroSize;
406
407     NSSize screenSize = _webView.window.screen.frame.size;
408     FloatRect largestRect = largestRectWithAspectRatioInsideRect(screenSize.width / screenSize.height, _webView.bounds);
409     return NSMakeSize(largestRect.width() * 0.75, largestRect.height() * 0.75);
410 }
411
412 #pragma mark Data Detectors actions
413
414 - (id <NSImmediateActionAnimationController>)_animationControllerForDataDetectedText
415 {
416     if (!DataDetectorsLibrary())
417         return nil;
418
419     RefPtr<Range> detectedDataRange;
420     FloatRect detectedDataBoundingBox;
421     RetainPtr<DDActionContext> actionContext;
422
423     if ([[_webView UIDelegate] respondsToSelector:@selector(_webView:actionContextForHitTestResult:range:)]) {
424         RetainPtr<WebElementDictionary> hitTestDictionary = adoptNS([[WebElementDictionary alloc] initWithHitTestResult:_hitTestResult]);
425
426         DOMRange *customDataDetectorsRange;
427         actionContext = [(id)[_webView UIDelegate] _webView:_webView actionContextForHitTestResult:hitTestDictionary.get() range:&customDataDetectorsRange];
428
429         if (actionContext && customDataDetectorsRange)
430             detectedDataRange = core(customDataDetectorsRange);
431     }
432
433     // If the client didn't give us an action context, try to scan around the hit point.
434     if (!actionContext || !detectedDataRange)
435         actionContext = DataDetection::detectItemAroundHitTestResult(_hitTestResult, detectedDataBoundingBox, detectedDataRange);
436
437     if (!actionContext || !detectedDataRange)
438         return nil;
439
440     [actionContext setAltMode:YES];
441     [actionContext setImmediate:YES];
442     if (![[getDDActionsManagerClass() sharedManager] hasActionsForResult:[actionContext mainResult] actionContext:actionContext.get()])
443         return nil;
444
445     auto indicator = TextIndicator::createWithRange(*detectedDataRange, TextIndicatorOptionDefault, TextIndicatorPresentationTransition::FadeIn);
446
447     _currentActionContext = [actionContext contextForView:_webView altMode:YES interactionStartedHandler:^() {
448     } interactionChangedHandler:^() {
449         if (indicator)
450             [_webView _setTextIndicator:*indicator withLifetime:TextIndicatorWindowLifetime::Permanent];
451     } interactionStoppedHandler:^() {
452         [_webView _clearTextIndicatorWithAnimation:TextIndicatorWindowDismissalAnimation::FadeOut];
453     }];
454
455     [_currentActionContext setHighlightFrame:[_webView.window convertRectToScreen:detectedDataBoundingBox]];
456
457     NSArray *menuItems = [[getDDActionsManagerClass() sharedManager] menuItemsForResult:[_currentActionContext mainResult] actionContext:_currentActionContext.get()];
458     if (menuItems.count != 1)
459         return nil;
460
461     return menuItems.lastObject;
462 }
463
464 - (id <NSImmediateActionAnimationController>)_animationControllerForDataDetectedLink
465 {
466     if (!DataDetectorsLibrary())
467         return nil;
468
469     RetainPtr<DDActionContext> actionContext = adoptNS([allocDDActionContextInstance() init]);
470
471     if (!actionContext)
472         return nil;
473
474     [actionContext setAltMode:YES];
475     [actionContext setImmediate:YES];
476
477     RefPtr<Range> linkRange = rangeOfContents(*_hitTestResult.URLElement());
478     if (!linkRange)
479         return nullptr;
480     auto indicator = TextIndicator::createWithRange(*linkRange, TextIndicatorOptionDefault, TextIndicatorPresentationTransition::FadeIn);
481
482     _currentActionContext = [actionContext contextForView:_webView altMode:YES interactionStartedHandler:^() {
483     } interactionChangedHandler:^() {
484         if (indicator)
485             [_webView _setTextIndicator:*indicator withLifetime:TextIndicatorWindowLifetime::Permanent];
486     } interactionStoppedHandler:^() {
487         [_webView _clearTextIndicatorWithAnimation:TextIndicatorWindowDismissalAnimation::FadeOut];
488     }];
489
490     [_currentActionContext setHighlightFrame:[_webView.window convertRectToScreen:elementBoundingBoxInWindowCoordinatesFromNode(_hitTestResult.URLElement())]];
491
492     NSArray *menuItems = [[getDDActionsManagerClass() sharedManager] menuItemsForTargetURL:_hitTestResult.absoluteLinkURL() actionContext:_currentActionContext.get()];
493     if (menuItems.count != 1)
494         return nil;
495     
496     return menuItems.lastObject;
497 }
498
499 #pragma mark Text action
500
501 + (DictionaryPopupInfo)_dictionaryPopupInfoForRange:(Range&)range inFrame:(Frame*)frame withLookupOptions:(NSDictionary *)lookupOptions indicatorOptions:(TextIndicatorOptions)indicatorOptions transition:(TextIndicatorPresentationTransition)presentationTransition
502 {
503     Editor& editor = frame->editor();
504     editor.setIsGettingDictionaryPopupInfo(true);
505
506     // Dictionary API will accept a whitespace-only string and display UI as if it were real text,
507     // so bail out early to avoid that.
508     DictionaryPopupInfo popupInfo;
509     if (range.text().stripWhiteSpace().isEmpty()) {
510         editor.setIsGettingDictionaryPopupInfo(false);
511         return popupInfo;
512     }
513
514     RenderObject* renderer = range.startContainer().renderer();
515     const RenderStyle& style = renderer->style();
516
517     Vector<FloatQuad> quads;
518     range.absoluteTextQuads(quads);
519     if (quads.isEmpty()) {
520         editor.setIsGettingDictionaryPopupInfo(false);
521         return popupInfo;
522     }
523
524     IntRect rangeRect = frame->view()->contentsToWindow(quads[0].enclosingBoundingBox());
525
526     popupInfo.origin = NSMakePoint(rangeRect.x(), rangeRect.y() + (style.fontMetrics().descent() * frame->page()->pageScaleFactor()));
527     popupInfo.options = lookupOptions;
528
529     NSAttributedString *nsAttributedString = editingAttributedStringFromRange(range, IncludeImagesInAttributedString::No);
530     RetainPtr<NSMutableAttributedString> scaledNSAttributedString = adoptNS([[NSMutableAttributedString alloc] initWithString:[nsAttributedString string]]);
531     NSFontManager *fontManager = [NSFontManager sharedFontManager];
532
533     [nsAttributedString enumerateAttributesInRange:NSMakeRange(0, [nsAttributedString length]) options:0 usingBlock:^(NSDictionary *attributes, NSRange attributeRange, BOOL *stop) {
534         RetainPtr<NSMutableDictionary> scaledAttributes = adoptNS([attributes mutableCopy]);
535
536         NSFont *font = [scaledAttributes objectForKey:NSFontAttributeName];
537         if (font) {
538             font = [fontManager convertFont:font toSize:[font pointSize] * frame->page()->pageScaleFactor()];
539             [scaledAttributes setObject:font forKey:NSFontAttributeName];
540         }
541
542         [scaledNSAttributedString addAttributes:scaledAttributes.get() range:attributeRange];
543     }];
544
545     popupInfo.attributedString = scaledNSAttributedString.get();
546
547     if (auto textIndicator = TextIndicator::createWithRange(range, indicatorOptions, presentationTransition))
548         popupInfo.textIndicator = textIndicator->data();
549
550     editor.setIsGettingDictionaryPopupInfo(false);
551     return popupInfo;
552 }
553
554 - (id<NSImmediateActionAnimationController>)_animationControllerForText
555 {
556     if (!getLULookupDefinitionModuleClass())
557         return nil;
558
559     Node* node = _hitTestResult.innerNode();
560     if (!node)
561         return nil;
562
563     Frame* frame = node->document().frame();
564     if (!frame)
565         return nil;
566
567     NSDictionary *options = nil;
568     RefPtr<Range> dictionaryRange = DictionaryLookup::rangeAtHitTestResult(_hitTestResult, &options);
569     if (!dictionaryRange)
570         return nil;
571
572     DictionaryPopupInfo dictionaryPopupInfo = [WebImmediateActionController _dictionaryPopupInfoForRange:*dictionaryRange inFrame:frame withLookupOptions:options indicatorOptions:TextIndicatorOptionDefault transition: TextIndicatorPresentationTransition::FadeIn];
573     if (!dictionaryPopupInfo.attributedString)
574         return nil;
575
576     return [_webView _animationControllerForDictionaryLookupPopupInfo:dictionaryPopupInfo];
577 }
578
579 @end
580
581 #endif // PLATFORM(MAC)