Use TextIndicator instead of the built in Lookup highlight
[WebKit-https.git] / Source / WebCore / editing / mac / DictionaryLookup.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 "config.h"
27 #import "DictionaryLookup.h"
28
29 #if PLATFORM(MAC)
30
31 #import "Document.h"
32 #import "FocusController.h"
33 #import "Frame.h"
34 #import "FrameSelection.h"
35 #import "HTMLConverter.h"
36 #import "HitTestResult.h"
37 #import "LookupSPI.h"
38 #import "Page.h"
39 #import "Range.h"
40 #import "RenderObject.h"
41 #import "TextIterator.h"
42 #import "VisiblePosition.h"
43 #import "VisibleSelection.h"
44 #import "VisibleUnits.h"
45 #import "WebCoreSystemInterface.h"
46 #import "htmlediting.h"
47 #import <wtf/RefPtr.h>
48
49 namespace WebCore {
50
51 bool isPositionInRange(const VisiblePosition& position, Range* range)
52 {
53     RefPtr<Range> positionRange = makeRange(position, position);
54
55     ExceptionCode ec = 0;
56     range->compareBoundaryPoints(Range::START_TO_START, positionRange.get(), ec);
57     if (ec)
58         return false;
59
60     if (!range->isPointInRange(positionRange->startContainer(), positionRange->startOffset(), ec))
61         return false;
62     if (ec)
63         return false;
64
65     return true;
66 }
67
68 bool shouldUseSelection(const VisiblePosition& position, const VisibleSelection& selection)
69 {
70     if (!selection.isRange())
71         return false;
72
73     RefPtr<Range> selectedRange = selection.toNormalizedRange();
74     if (!selectedRange)
75         return false;
76
77     return isPositionInRange(position, selectedRange.get());
78 }
79
80 PassRefPtr<Range> rangeExpandedAroundPositionByCharacters(const VisiblePosition& position, int numberOfCharactersToExpand)
81 {
82     Position start = position.deepEquivalent();
83     Position end = position.deepEquivalent();
84     for (int i = 0; i < numberOfCharactersToExpand; ++i) {
85         if (directionOfEnclosingBlock(start) == LTR)
86             start = start.previous(Character);
87         else
88             start = start.next(Character);
89
90         if (directionOfEnclosingBlock(end) == LTR)
91             end = end.next(Character);
92         else
93             end = end.previous(Character);
94     }
95
96     return makeRange(start, end);
97 }
98
99 PassRefPtr<Range> rangeForDictionaryLookupForSelection(const VisibleSelection& selection, NSDictionary **options)
100 {
101     RefPtr<Range> selectedRange = selection.toNormalizedRange();
102     if (!selectedRange)
103         return nullptr;
104
105     VisiblePosition selectionStart = selection.visibleStart();
106     VisiblePosition selectionEnd = selection.visibleEnd();
107
108     // As context, we are going to use the surrounding paragraphs of text.
109     VisiblePosition paragraphStart = startOfParagraph(selectionStart);
110     VisiblePosition paragraphEnd = endOfParagraph(selectionEnd);
111
112     int lengthToSelectionStart = TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
113     int lengthToSelectionEnd = TextIterator::rangeLength(makeRange(paragraphStart, selectionEnd).get());
114     NSRange rangeToPass = NSMakeRange(lengthToSelectionStart, lengthToSelectionEnd - lengthToSelectionStart);
115
116     String fullPlainTextString = plainText(makeRange(paragraphStart, paragraphEnd).get());
117
118     // Since we already have the range we want, we just need to grab the returned options.
119     if (Class luLookupDefinitionModule = getLULookupDefinitionModuleClass())
120         [luLookupDefinitionModule tokenRangeForString:fullPlainTextString range:rangeToPass options:options];
121
122     return selectedRange.release();
123 }
124
125 PassRefPtr<Range> rangeForDictionaryLookupAtHitTestResult(const HitTestResult& hitTestResult, NSDictionary **options)
126 {
127     Node* node = hitTestResult.innerNonSharedNode();
128     if (!node)
129         return nullptr;
130
131     auto renderer = node->renderer();
132     if (!renderer)
133         return nullptr;
134
135     Frame* frame = node->document().frame();
136     if (!frame)
137         return nullptr;
138
139     // Don't do anything if there is no character at the point.
140     if (!frame->rangeForPoint(hitTestResult.roundedPointInInnerNodeFrame()))
141         return nullptr;
142
143     VisiblePosition position = renderer->positionForPoint(hitTestResult.localPoint(), nullptr);
144     if (position.isNull())
145         position = firstPositionInOrBeforeNode(node);
146
147     VisibleSelection selection = frame->page()->focusController().focusedOrMainFrame().selection().selection();
148     if (shouldUseSelection(position, selection))
149         return rangeForDictionaryLookupForSelection(selection, options);
150
151     // As context, we are going to use 250 characters of text before and after the point.
152     RefPtr<Range> fullCharacterRange = rangeExpandedAroundPositionByCharacters(position, 250);
153     if (!fullCharacterRange)
154         return nullptr;
155
156     NSRange rangeToPass = NSMakeRange(TextIterator::rangeLength(makeRange(fullCharacterRange->startPosition(), position).get()), 0);
157
158     String fullPlainTextString = plainText(fullCharacterRange.get());
159
160     NSRange extractedRange = NSMakeRange(rangeToPass.location, 0);
161     if (Class luLookupDefinitionModule = getLULookupDefinitionModuleClass())
162         extractedRange = [luLookupDefinitionModule tokenRangeForString:fullPlainTextString range:rangeToPass options:options];
163
164     // This function sometimes returns {NSNotFound, 0} if it was unable to determine a good string.
165     if (extractedRange.location == NSNotFound)
166         return nullptr;
167
168     return TextIterator::subrange(fullCharacterRange.get(), extractedRange.location, extractedRange.length);
169 }
170
171 } // namespace WebCore
172
173 #endif // PLATFORM(MAC)
174