Remove "virtual" from all lines that have both "virtual" and "override".
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / WKTextFinderClient.mm
1 /*
2  * Copyright (C) 2015 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 "WKTextFinderClient.h"
28
29 #if PLATFORM(MAC) && WK_API_ENABLED
30
31 #import "APIFindClient.h"
32 #import "APIFindMatchesClient.h"
33 #import "WebImage.h"
34 #import "WebPageProxy.h"
35 #import <WebCore/NSTextFinderSPI.h>
36 #import <wtf/Deque.h>
37
38 // FIXME: Implement support for replace.
39 // FIXME: Implement scrollFindMatchToVisible.
40 // FIXME: The NSTextFinder overlay doesn't move with scrolling; we should have a mode where we manage the overlay.
41
42 using namespace WebCore;
43 using namespace WebKit;
44
45 @interface WKTextFinderClient ()
46
47 - (void)didFindStringMatchesWithRects:(const Vector<Vector<IntRect>>&)rects;
48
49 - (void)getImageForMatchResult:(id <NSTextFinderAsynchronousDocumentFindMatch>)findMatch completionHandler:(void (^)(NSImage *generatedImage))completionHandler;
50 - (void)didGetImageForMatchResult:(WebImage *)string;
51
52 @end
53
54 namespace WebKit {
55
56 class TextFinderFindClient : public API::FindMatchesClient, public API::FindClient {
57 public:
58     explicit TextFinderFindClient(WKTextFinderClient *textFinderClient)
59         : m_textFinderClient(textFinderClient)
60     {
61     }
62
63 private:
64     void didFindStringMatches(WebPageProxy* page, const String&, const Vector<Vector<IntRect>>& matchRects, int32_t) override
65     {
66         [m_textFinderClient didFindStringMatchesWithRects:matchRects];
67     }
68
69     void didGetImageForMatchResult(WebPageProxy* page, WebImage* image, int32_t index) override
70     {
71         [m_textFinderClient didGetImageForMatchResult:image];
72     }
73
74     void didFindString(WebPageProxy*, const String&, const Vector<IntRect>& matchRects, uint32_t, int32_t) override
75     {
76         [m_textFinderClient didFindStringMatchesWithRects:{ matchRects }];
77     }
78
79     void didFailToFindString(WebPageProxy*, const String& string) override
80     {
81         [m_textFinderClient didFindStringMatchesWithRects:{ }];
82     }
83
84     RetainPtr<WKTextFinderClient> m_textFinderClient;
85 };
86     
87 }
88
89 @interface WKTextFinderMatch : NSObject <NSTextFinderAsynchronousDocumentFindMatch>
90
91 @property (nonatomic, readonly) unsigned index;
92
93 @end
94
95 @implementation WKTextFinderMatch {
96     WKTextFinderClient *_client;
97     NSView *_view;
98     RetainPtr<NSArray> _rects;
99     unsigned _index;
100 }
101
102 - (instancetype)initWithClient:(WKTextFinderClient *)client view:(NSView *)view index:(unsigned)index rects:(NSArray *)rects
103 {
104     self = [super init];
105
106     if (!self)
107         return nil;
108
109     _client = client;
110     _view = view;
111     _rects = rects;
112     _index = index;
113
114     return self;
115 }
116
117 - (NSView *)containingView
118 {
119     return _view;
120 }
121
122 - (NSArray *)textRects
123 {
124     return _rects.get();
125 }
126
127 - (void)generateTextImage:(void (^)(NSImage *generatedImage))completionHandler
128 {
129     [_client getImageForMatchResult:self completionHandler:completionHandler];
130 }
131
132 - (unsigned)index
133 {
134     return _index;
135 }
136
137 @end
138
139 @implementation WKTextFinderClient {
140     WebPageProxy *_page;
141     NSView *_view;
142     Deque<std::function<void(NSArray *, bool didWrap)>> _findReplyCallbacks;
143     Deque<std::function<void(NSImage *)>> _imageReplyCallbacks;
144 }
145
146 - (instancetype)initWithPage:(WebPageProxy&)page view:(NSView *)view
147 {
148     self = [super init];
149
150     if (!self)
151         return nil;
152
153     _page = &page;
154     _view = view;
155     
156     _page->setFindMatchesClient(std::make_unique<TextFinderFindClient>(self));
157     _page->setFindClient(std::make_unique<TextFinderFindClient>(self));
158
159     return self;
160 }
161
162 - (void)willDestroyView:(NSView *)view
163 {
164     _page->setFindMatchesClient(nullptr);
165     _page->setFindClient(nullptr);
166
167     _page = nullptr;
168     _view = nil;
169 }
170
171 #pragma mark - NSTextFinderClient SPI
172
173 - (void)findMatchesForString:(NSString *)targetString relativeToMatch:(id <NSTextFinderAsynchronousDocumentFindMatch>)relativeMatch findOptions:(NSTextFinderAsynchronousDocumentFindOptions)findOptions maxResults:(NSUInteger)maxResults resultCollector:(void (^)(NSArray *matches, BOOL didWrap))resultCollector
174 {
175     unsigned kitFindOptions = 0;
176
177     if (findOptions & NSTextFinderAsynchronousDocumentFindOptionsBackwards)
178         kitFindOptions |= FindOptionsBackwards;
179     if (findOptions & NSTextFinderAsynchronousDocumentFindOptionsWrap)
180         kitFindOptions |= FindOptionsWrapAround;
181     if (findOptions & NSTextFinderAsynchronousDocumentFindOptionsCaseInsensitive)
182         kitFindOptions |= FindOptionsCaseInsensitive;
183     if (findOptions & NSTextFinderAsynchronousDocumentFindOptionsStartsWith)
184         kitFindOptions |= FindOptionsAtWordStarts;
185
186     RetainPtr<NSProgress> progress = [NSProgress progressWithTotalUnitCount:1];
187     auto copiedResultCollector = Block_copy(resultCollector);
188     _findReplyCallbacks.append([progress, copiedResultCollector] (NSArray *matches, bool didWrap) {
189         [progress setCompletedUnitCount:1];
190         copiedResultCollector(matches, didWrap);
191         Block_release(copiedResultCollector);
192     });
193
194     if (maxResults == 1)
195         _page->findString(targetString, static_cast<WebKit::FindOptions>(kitFindOptions), maxResults);
196     else
197         _page->findStringMatches(targetString, static_cast<WebKit::FindOptions>(kitFindOptions), maxResults);
198 }
199
200 - (void)getSelectedText:(void (^)(NSString *selectedTextString))completionHandler
201 {
202     void (^copiedCompletionHandler)(NSString *) = Block_copy(completionHandler);
203
204     _page->getSelectionOrContentsAsString([copiedCompletionHandler] (const String& string, CallbackBase::Error) {
205         copiedCompletionHandler(string);
206         Block_release(copiedCompletionHandler);
207     });
208 }
209
210 - (void)selectFindMatch:(id <NSTextFinderAsynchronousDocumentFindMatch>)findMatch completionHandler:(void (^)(void))completionHandler
211 {
212     ASSERT([findMatch isKindOfClass:[WKTextFinderMatch class]]);
213     ASSERT(!completionHandler);
214
215     WKTextFinderMatch *textFinderMatch = static_cast<WKTextFinderMatch *>(findMatch);
216     _page->selectFindMatch(textFinderMatch.index);
217 }
218
219 #pragma mark - FindMatchesClient
220
221 static RetainPtr<NSArray> arrayFromRects(const Vector<IntRect>& matchRects)
222 {
223     RetainPtr<NSMutableArray> nsMatchRects = adoptNS([[NSMutableArray alloc] initWithCapacity:matchRects.size()]);
224     for (auto& rect : matchRects)
225         [nsMatchRects addObject:[NSValue valueWithRect:rect]];
226     return nsMatchRects;
227 }
228
229 - (void)didFindStringMatchesWithRects:(const Vector<Vector<IntRect>>&)rectsForMatches
230 {
231     if (_findReplyCallbacks.isEmpty())
232         return;
233
234     auto replyCallback = _findReplyCallbacks.takeFirst();
235     unsigned matchCount = rectsForMatches.size();
236     RetainPtr<NSMutableArray> matchObjects = adoptNS([[NSMutableArray alloc] initWithCapacity:matchCount]);
237     for (unsigned i = 0; i < matchCount; i++) {
238         RetainPtr<NSArray> nsMatchRects = arrayFromRects(rectsForMatches[i]);
239         RetainPtr<WKTextFinderMatch> match = adoptNS([[WKTextFinderMatch alloc] initWithClient:self view:_view index:i rects:nsMatchRects.get()]);
240         [matchObjects addObject:match.get()];
241     }
242
243     replyCallback(matchObjects.get(), NO);
244 }
245
246 - (void)didGetImageForMatchResult:(WebImage *)image
247 {
248     if (_imageReplyCallbacks.isEmpty())
249         return;
250
251     auto imageCallback = _imageReplyCallbacks.takeFirst();
252     imageCallback([[[NSImage alloc] initWithCGImage:image->bitmap()->makeCGImage().get() size:NSZeroSize] autorelease]);
253 }
254
255 #pragma mark - WKTextFinderMatch callbacks
256
257 - (void)getImageForMatchResult:(id <NSTextFinderAsynchronousDocumentFindMatch>)findMatch completionHandler:(void (^)(NSImage *generatedImage))completionHandler
258 {
259     ASSERT([findMatch isKindOfClass:[WKTextFinderMatch class]]);
260
261     WKTextFinderMatch *textFinderMatch = static_cast<WKTextFinderMatch *>(findMatch);
262
263     auto copiedImageCallback = Block_copy(completionHandler);
264     _imageReplyCallbacks.append([copiedImageCallback] (NSImage *image) {
265         copiedImageCallback(image);
266         Block_release(copiedImageCallback);
267     });
268
269     _page->getImageForFindMatch(textFinderMatch.index);
270 }
271
272 @end
273
274 #endif // PLATFORM(MAC)