Move Lookup Code for better cross platform usage
[WebKit-https.git] / Source / WebKit / WebProcess / WebPage / Cocoa / WebPageCocoa.mm
1 /*
2  * Copyright (C) 2016-2017 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 "WebPage.h"
28
29
30 #import "LoadParameters.h"
31 #import "PluginView.h"
32 #import "WebPageProxyMessages.h"
33 #import <WebCore/DictionaryLookup.h>
34 #import <WebCore/Editor.h>
35 #import <WebCore/EventHandler.h>
36 #import <WebCore/FocusController.h>
37 #import <WebCore/HTMLConverter.h>
38 #import <WebCore/HitTestResult.h>
39 #import <WebCore/NodeRenderStyle.h>
40 #import <WebCore/PlatformMediaSessionManager.h>
41 #import <WebCore/RenderElement.h>
42 #import <WebCore/RenderObject.h>
43
44 #if PLATFORM(COCOA)
45
46 namespace WebKit {
47 using namespace WebCore;
48
49 void WebPage::platformDidReceiveLoadParameters(const LoadParameters& loadParameters)
50 {
51     m_dataDetectionContext = loadParameters.dataDetectionContext;
52 }
53
54 void WebPage::requestActiveNowPlayingSessionInfo(CallbackID callbackID)
55 {
56     bool hasActiveSession = false;
57     String title = emptyString();
58     double duration = NAN;
59     double elapsedTime = NAN;
60     uint64_t uniqueIdentifier = 0;
61     bool registeredAsNowPlayingApplication = false;
62     if (auto* sharedManager = WebCore::PlatformMediaSessionManager::sharedManagerIfExists()) {
63         hasActiveSession = sharedManager->hasActiveNowPlayingSession();
64         title = sharedManager->lastUpdatedNowPlayingTitle();
65         duration = sharedManager->lastUpdatedNowPlayingDuration();
66         elapsedTime = sharedManager->lastUpdatedNowPlayingElapsedTime();
67         uniqueIdentifier = sharedManager->lastUpdatedNowPlayingInfoUniqueIdentifier();
68         registeredAsNowPlayingApplication = sharedManager->registeredAsNowPlayingApplication();
69     }
70
71     send(Messages::WebPageProxy::NowPlayingInfoCallback(hasActiveSession, registeredAsNowPlayingApplication, title, duration, elapsedTime, uniqueIdentifier, callbackID));
72 }
73     
74 void WebPage::performDictionaryLookupAtLocation(const FloatPoint& floatPoint)
75 {
76     if (auto* pluginView = pluginViewForFrame(&m_page->mainFrame())) {
77         if (pluginView->performDictionaryLookupAtLocation(floatPoint))
78             return;
79     }
80     
81     // Find the frame the point is over.
82     HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(m_page->mainFrame().view()->windowToContents(roundedIntPoint(floatPoint)));
83     RefPtr<Range> range;
84     NSDictionary *options;
85     std::tie(range, options) = DictionaryLookup::rangeAtHitTestResult(result);
86     if (!range)
87         return;
88     
89     auto* frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document().frame() : &m_page->focusController().focusedOrMainFrame();
90     if (!frame)
91         return;
92     
93     performDictionaryLookupForRange(*frame, *range, options, TextIndicatorPresentationTransition::Bounce);
94 }
95
96 void WebPage::performDictionaryLookupForSelection(Frame& frame, const VisibleSelection& selection, TextIndicatorPresentationTransition presentationTransition)
97 {
98     RefPtr<Range> selectedRange;
99     NSDictionary *options;
100     std::tie(selectedRange, options) = DictionaryLookup::rangeForSelection(selection);
101     if (selectedRange)
102         performDictionaryLookupForRange(frame, *selectedRange, options, presentationTransition);
103 }
104
105 void WebPage::performDictionaryLookupOfCurrentSelection()
106 {
107     auto& frame = m_page->focusController().focusedOrMainFrame();
108     performDictionaryLookupForSelection(frame, frame.selection().selection(), TextIndicatorPresentationTransition::BounceAndCrossfade);
109 }
110     
111 void WebPage::performDictionaryLookupForRange(Frame& frame, Range& range, NSDictionary *options, TextIndicatorPresentationTransition presentationTransition)
112 {
113     send(Messages::WebPageProxy::DidPerformDictionaryLookup(dictionaryPopupInfoForRange(frame, range, options, presentationTransition)));
114 }
115
116 DictionaryPopupInfo WebPage::dictionaryPopupInfoForRange(Frame& frame, Range& range, NSDictionary *options, TextIndicatorPresentationTransition presentationTransition)
117 {
118     Editor& editor = frame.editor();
119     editor.setIsGettingDictionaryPopupInfo(true);
120     
121     DictionaryPopupInfo dictionaryPopupInfo;
122     if (range.text().stripWhiteSpace().isEmpty()) {
123         editor.setIsGettingDictionaryPopupInfo(false);
124         return dictionaryPopupInfo;
125     }
126     
127     Vector<FloatQuad> quads;
128     range.absoluteTextQuads(quads);
129     if (quads.isEmpty()) {
130         editor.setIsGettingDictionaryPopupInfo(false);
131         return dictionaryPopupInfo;
132     }
133     
134     IntRect rangeRect = frame.view()->contentsToWindow(quads[0].enclosingBoundingBox());
135     
136     const RenderStyle* style = range.startContainer().renderStyle();
137     float scaledAscent = style ? style->fontMetrics().ascent() * pageScaleFactor() : 0;
138     dictionaryPopupInfo.origin = FloatPoint(rangeRect.x(), rangeRect.y() + scaledAscent);
139     dictionaryPopupInfo.options = options;
140
141 #if PLATFORM(MAC)
142
143     NSAttributedString *nsAttributedString = editingAttributedStringFromRange(range, IncludeImagesInAttributedString::No);
144     
145     RetainPtr<NSMutableAttributedString> scaledNSAttributedString = adoptNS([[NSMutableAttributedString alloc] initWithString:[nsAttributedString string]]);
146     
147     NSFontManager *fontManager = [NSFontManager sharedFontManager];
148     
149     [nsAttributedString enumerateAttributesInRange:NSMakeRange(0, [nsAttributedString length]) options:0 usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
150         RetainPtr<NSMutableDictionary> scaledAttributes = adoptNS([attributes mutableCopy]);
151         
152         NSFont *font = [scaledAttributes objectForKey:NSFontAttributeName];
153         if (font)
154             font = [fontManager convertFont:font toSize:font.pointSize * pageScaleFactor()];
155         if (font)
156             [scaledAttributes setObject:font forKey:NSFontAttributeName];
157         
158         [scaledNSAttributedString addAttributes:scaledAttributes.get() range:range];
159     }];
160
161 #endif // PLATFORM(MAC)
162     
163     TextIndicatorOptions indicatorOptions = TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges;
164     if (presentationTransition == TextIndicatorPresentationTransition::BounceAndCrossfade)
165         indicatorOptions |= TextIndicatorOptionIncludeSnapshotWithSelectionHighlight;
166     
167     RefPtr<TextIndicator> textIndicator = TextIndicator::createWithRange(range, indicatorOptions, presentationTransition);
168     if (!textIndicator) {
169         editor.setIsGettingDictionaryPopupInfo(false);
170         return dictionaryPopupInfo;
171     }
172     
173     dictionaryPopupInfo.textIndicator = textIndicator->data();
174 #if PLATFORM(MAC)
175     dictionaryPopupInfo.attributedString = scaledNSAttributedString;
176 #endif // PLATFORM(MAC)
177     
178     editor.setIsGettingDictionaryPopupInfo(false);
179     return dictionaryPopupInfo;
180 }
181
182 } // namespace WebKit
183
184 #endif // PLATFORM(COCOA)