Resolve unused parameter warning in FindController.cpp
[WebKit-https.git] / Source / WebKit2 / WebProcess / WebPage / FindController.cpp
1 /*
2  * Copyright (C) 2010 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 #include "config.h"
27 #include "FindController.h"
28
29 #include "PluginView.h"
30 #include "ShareableBitmap.h"
31 #include "WKPage.h"
32 #include "WebCoreArgumentCoders.h"
33 #include "WebPage.h"
34 #include "WebPageProxyMessages.h"
35 #include <WebCore/DocumentMarkerController.h>
36 #include <WebCore/FloatQuad.h>
37 #include <WebCore/FocusController.h>
38 #include <WebCore/Frame.h>
39 #include <WebCore/FrameView.h>
40 #include <WebCore/GraphicsContext.h>
41 #include <WebCore/Page.h>
42 #include <WebCore/PluginDocument.h>
43
44 using namespace WebCore;
45
46 namespace WebKit {
47
48 static WebCore::FindOptions core(FindOptions options)
49 {
50     return (options & FindOptionsCaseInsensitive ? CaseInsensitive : 0)
51         | (options & FindOptionsAtWordStarts ? AtWordStarts : 0)
52         | (options & FindOptionsTreatMedialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0)
53         | (options & FindOptionsBackwards ? Backwards : 0)
54         | (options & FindOptionsWrapAround ? WrapAround : 0);
55 }
56
57 FindController::FindController(WebPage* webPage)
58     : m_webPage(webPage)
59     , m_findPageOverlay(0)
60     , m_isShowingFindIndicator(false)
61 {
62 }
63
64 FindController::~FindController()
65 {
66 }
67
68 static PluginView* pluginViewForFrame(Frame* frame)
69 {
70     if (!frame->document()->isPluginDocument())
71         return 0;
72
73     PluginDocument* pluginDocument = static_cast<PluginDocument*>(frame->document());
74     return static_cast<PluginView*>(pluginDocument->pluginWidget());
75 }
76
77 void FindController::countStringMatches(const String& string, FindOptions options, unsigned maxMatchCount)
78 {
79     if (maxMatchCount == std::numeric_limits<unsigned>::max())
80         --maxMatchCount;
81     
82     PluginView* pluginView = pluginViewForFrame(m_webPage->mainFrame());
83     
84     unsigned matchCount;
85
86     if (pluginView)
87         matchCount = pluginView->countFindMatches(string, core(options), maxMatchCount + 1);
88     else {
89         matchCount = m_webPage->corePage()->countFindMatches(string, core(options), maxMatchCount + 1);
90         m_webPage->corePage()->unmarkAllTextMatches();
91     }
92
93     if (matchCount > maxMatchCount)
94         matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
95     
96     m_webPage->send(Messages::WebPageProxy::DidCountStringMatches(string, matchCount));
97 }
98
99 static Frame* frameWithSelection(Page* page)
100 {
101     for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
102         if (frame->selection().isRange())
103             return frame;
104     }
105
106     return 0;
107 }
108
109 void FindController::updateFindUIAfterPageScroll(bool found, const String& string, FindOptions options, unsigned maxMatchCount)
110 {
111     Frame* selectedFrame = frameWithSelection(m_webPage->corePage());
112     
113     PluginView* pluginView = pluginViewForFrame(m_webPage->mainFrame());
114
115     bool shouldShowOverlay = false;
116
117     if (!found) {
118         if (!pluginView)
119             m_webPage->corePage()->unmarkAllTextMatches();
120
121         if (selectedFrame)
122             selectedFrame->selection().clear();
123
124         hideFindIndicator();
125
126         m_webPage->send(Messages::WebPageProxy::DidFailToFindString(string));
127     } else {
128         shouldShowOverlay = options & FindOptionsShowOverlay;
129         bool shouldShowHighlight = options & FindOptionsShowHighlight;
130         unsigned matchCount = 1;
131
132         if (shouldShowOverlay || shouldShowHighlight) {
133             if (maxMatchCount == std::numeric_limits<unsigned>::max())
134                 --maxMatchCount;
135
136             if (pluginView) {
137                 matchCount = pluginView->countFindMatches(string, core(options), maxMatchCount + 1);
138                 shouldShowOverlay = false;
139             } else {
140                 m_webPage->corePage()->unmarkAllTextMatches();
141                 matchCount = m_webPage->corePage()->markAllMatchesForText(string, core(options), shouldShowHighlight, maxMatchCount + 1);
142             }
143
144             // If we have a large number of matches, we don't want to take the time to paint the overlay.
145             if (matchCount > maxMatchCount) {
146                 shouldShowOverlay = false;
147                 matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
148             }
149         }
150
151         m_webPage->send(Messages::WebPageProxy::DidFindString(string, matchCount));
152
153         if (!(options & FindOptionsShowFindIndicator) || !updateFindIndicator(selectedFrame, shouldShowOverlay))
154             hideFindIndicator();
155     }
156
157     if (!shouldShowOverlay) {
158         if (m_findPageOverlay)
159             m_webPage->uninstallPageOverlay(m_findPageOverlay, true);
160     } else {
161         if (!m_findPageOverlay) {
162             RefPtr<PageOverlay> findPageOverlay = PageOverlay::create(this);
163             m_findPageOverlay = findPageOverlay.get();
164             m_webPage->installPageOverlay(findPageOverlay.release(), true);
165             m_findPageOverlay->setNeedsDisplay();
166         } else
167             m_findPageOverlay->setNeedsDisplay();
168     }
169 }
170
171 void FindController::findString(const String& string, FindOptions options, unsigned maxMatchCount)
172 {
173     PluginView* pluginView = pluginViewForFrame(m_webPage->mainFrame());
174     
175     bool found;
176     
177     if (pluginView)
178         found = pluginView->findString(string, core(options), maxMatchCount);
179     else
180         found = m_webPage->corePage()->findString(string, core(options));
181
182     m_webPage->drawingArea()->dispatchAfterEnsuringUpdatedScrollPosition(WTF::bind(&FindController::updateFindUIAfterPageScroll, this, found, string, options, maxMatchCount));
183 }
184
185 void FindController::findStringMatches(const String& string, FindOptions options, unsigned maxMatchCount)
186 {
187     m_findMatches.clear();
188     int indexForSelection;
189
190     m_webPage->corePage()->findStringMatchingRanges(string, core(options), maxMatchCount, &m_findMatches, indexForSelection);
191
192     Vector<Vector<IntRect>> matchRects;
193     for (size_t i = 0; i < m_findMatches.size(); ++i) {
194         Vector<IntRect> rects;
195         m_findMatches[i]->textRects(rects);
196         matchRects.append(rects);
197     }
198
199     m_webPage->send(Messages::WebPageProxy::DidFindStringMatches(string, matchRects, indexForSelection));
200 }
201
202 bool FindController::getFindIndicatorBitmapAndRect(Frame* frame, ShareableBitmap::Handle& handle, IntRect& selectionRect)
203 {
204     selectionRect = enclosingIntRect(frame->selection().bounds());
205
206     // Selection rect can be empty for matches that are currently obscured from view.
207     if (selectionRect.isEmpty())
208         return false;
209
210     IntSize backingStoreSize = selectionRect.size();
211     backingStoreSize.scale(m_webPage->corePage()->deviceScaleFactor());
212
213     // Create a backing store and paint the find indicator text into it.
214     RefPtr<ShareableBitmap> findIndicatorTextBackingStore = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha);
215     if (!findIndicatorTextBackingStore)
216         return false;
217
218     OwnPtr<GraphicsContext> graphicsContext = findIndicatorTextBackingStore->createGraphicsContext();
219     graphicsContext->scale(FloatSize(m_webPage->corePage()->deviceScaleFactor(), m_webPage->corePage()->deviceScaleFactor()));
220
221     IntRect paintRect = selectionRect;
222     paintRect.move(frame->view()->frameRect().x(), frame->view()->frameRect().y());
223     paintRect.move(-frame->view()->scrollOffset());
224
225     graphicsContext->translate(-paintRect.x(), -paintRect.y());
226     frame->view()->setPaintBehavior(PaintBehaviorSelectionOnly | PaintBehaviorForceBlackText | PaintBehaviorFlattenCompositingLayers);
227     frame->document()->updateLayout();
228
229     frame->view()->paint(graphicsContext.get(), paintRect);
230     frame->view()->setPaintBehavior(PaintBehaviorNormal);
231
232     if (!findIndicatorTextBackingStore->createHandle(handle))
233         return false;
234     return true;
235 }
236
237 void FindController::getImageForFindMatch(uint32_t matchIndex)
238 {
239     if (matchIndex >= m_findMatches.size())
240         return;
241     Frame* frame = m_findMatches[matchIndex]->startContainer()->document().frame();
242     if (!frame)
243         return;
244
245     VisibleSelection oldSelection = frame->selection().selection();
246     frame->selection().setSelection(VisibleSelection(m_findMatches[matchIndex].get()));
247
248     IntRect selectionRect;
249     ShareableBitmap::Handle handle;
250     getFindIndicatorBitmapAndRect(frame, handle, selectionRect);
251
252     frame->selection().setSelection(oldSelection);
253
254     if (handle.isNull())
255         return;
256
257     m_webPage->send(Messages::WebPageProxy::DidGetImageForFindMatch(handle, matchIndex));
258 }
259
260 void FindController::selectFindMatch(uint32_t matchIndex)
261 {
262     if (matchIndex >= m_findMatches.size())
263         return;
264     Frame* frame = m_findMatches[matchIndex]->startContainer()->document().frame();
265     if (!frame)
266         return;
267     frame->selection().setSelection(VisibleSelection(m_findMatches[matchIndex].get()));
268 }
269
270 void FindController::hideFindUI()
271 {
272     m_findMatches.clear();
273     if (m_findPageOverlay)
274         m_webPage->uninstallPageOverlay(m_findPageOverlay, true);
275
276     PluginView* pluginView = pluginViewForFrame(m_webPage->mainFrame());
277     
278     if (pluginView)
279         pluginView->findString(emptyString(), 0, 0);
280     else
281         m_webPage->corePage()->unmarkAllTextMatches();
282     
283     hideFindIndicator();
284 }
285
286 bool FindController::updateFindIndicator(Frame* selectedFrame, bool isShowingOverlay, bool shouldAnimate)
287 {
288     if (!selectedFrame)
289         return false;
290
291     IntRect selectionRect;
292     ShareableBitmap::Handle handle;
293     if (!getFindIndicatorBitmapAndRect(selectedFrame, handle, selectionRect))
294         return false;
295
296     // We want the selection rect in window coordinates.
297     IntRect selectionRectInWindowCoordinates = selectedFrame->view()->contentsToWindow(selectionRect);
298
299     Vector<FloatRect> textRects;
300     selectedFrame->selection().getClippedVisibleTextRectangles(textRects);
301
302     // We want the text rects in selection rect coordinates.
303     Vector<FloatRect> textRectsInSelectionRectCoordinates;
304     
305     for (size_t i = 0; i < textRects.size(); ++i) {
306         IntRect textRectInSelectionRectCoordinates = selectedFrame->view()->contentsToWindow(enclosingIntRect(textRects[i]));
307         textRectInSelectionRectCoordinates.move(-selectionRectInWindowCoordinates.x(), -selectionRectInWindowCoordinates.y());
308
309         textRectsInSelectionRectCoordinates.append(textRectInSelectionRectCoordinates);
310     }            
311
312     m_webPage->send(Messages::WebPageProxy::SetFindIndicator(selectionRectInWindowCoordinates, textRectsInSelectionRectCoordinates, m_webPage->corePage()->deviceScaleFactor(), handle, !isShowingOverlay, shouldAnimate));
313     m_findIndicatorRect = selectionRectInWindowCoordinates;
314     m_isShowingFindIndicator = true;
315
316     return true;
317 }
318
319 void FindController::hideFindIndicator()
320 {
321     if (!m_isShowingFindIndicator)
322         return;
323
324     ShareableBitmap::Handle handle;
325     m_webPage->send(Messages::WebPageProxy::SetFindIndicator(FloatRect(), Vector<FloatRect>(), m_webPage->corePage()->deviceScaleFactor(), handle, false, true));
326     m_isShowingFindIndicator = false;
327 }
328
329 void FindController::showFindIndicatorInSelection()
330 {
331     Frame& selectedFrame = m_webPage->corePage()->focusController().focusedOrMainFrame();
332     updateFindIndicator(&selectedFrame, false);
333 }
334
335 void FindController::deviceScaleFactorDidChange()
336 {
337     ASSERT(isShowingOverlay());
338
339     Frame* selectedFrame = frameWithSelection(m_webPage->corePage());
340     if (!selectedFrame)
341         return;
342
343     updateFindIndicator(selectedFrame, true, false);
344 }
345
346 Vector<IntRect> FindController::rectsForTextMatches()
347 {
348     Vector<IntRect> rects;
349
350     for (Frame* frame = &m_webPage->corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
351         Document* document = frame->document();
352         if (!document)
353             continue;
354
355         IntRect visibleRect = frame->view()->visibleContentRect();
356         Vector<IntRect> frameRects = document->markers().renderedRectsForMarkers(DocumentMarker::TextMatch);
357         IntPoint frameOffset(-frame->view()->scrollOffsetRelativeToDocument().width(), -frame->view()->scrollOffsetRelativeToDocument().height());
358         frameOffset = frame->view()->convertToContainingWindow(frameOffset);
359
360         for (Vector<IntRect>::iterator it = frameRects.begin(), end = frameRects.end(); it != end; ++it) {
361             it->intersect(visibleRect);
362             it->move(frameOffset.x(), frameOffset.y());
363             rects.append(*it);
364         }
365     }
366
367     return rects;
368 }
369
370 void FindController::pageOverlayDestroyed(PageOverlay*)
371 {
372 }
373
374 void FindController::willMoveToWebPage(PageOverlay*, WebPage* webPage)
375 {
376     if (webPage)
377         return;
378
379     ASSERT(m_findPageOverlay);
380     m_findPageOverlay = 0;
381 }
382     
383 void FindController::didMoveToWebPage(PageOverlay*, WebPage*)
384 {
385 }
386
387 static const float shadowOffsetX = 0.0;
388 static const float shadowOffsetY = 1.0;
389 static const float shadowBlurRadius = 2.0;
390 static const float whiteFrameThickness = 1.0;
391
392 static const float overlayBackgroundRed = 0.1;
393 static const float overlayBackgroundGreen = 0.1;
394 static const float overlayBackgroundBlue = 0.1;
395 static const float overlayBackgroundAlpha = 0.25;
396
397 void FindController::drawRect(PageOverlay* pageOverlay, GraphicsContext& graphicsContext, const IntRect& dirtyRect)
398 {
399     Color overlayBackgroundColor(overlayBackgroundRed, overlayBackgroundGreen, overlayBackgroundBlue, overlayBackgroundAlpha);
400
401     Vector<IntRect> rects = rectsForTextMatches();
402
403     // Draw the background.
404     graphicsContext.fillRect(dirtyRect, overlayBackgroundColor, ColorSpaceSRGB);
405
406     {
407         GraphicsContextStateSaver stateSaver(graphicsContext);
408
409         graphicsContext.setShadow(FloatSize(shadowOffsetX, shadowOffsetY), shadowBlurRadius, Color::black, ColorSpaceSRGB);
410         graphicsContext.setFillColor(Color::white, ColorSpaceSRGB);
411
412         // Draw white frames around the holes.
413         for (size_t i = 0; i < rects.size(); ++i) {
414             IntRect whiteFrameRect = rects[i];
415             whiteFrameRect.inflate(1);
416
417             graphicsContext.fillRect(whiteFrameRect);
418         }
419     }
420
421     graphicsContext.setFillColor(Color::transparent, ColorSpaceSRGB);
422
423     // Clear out the holes.
424     for (size_t i = 0; i < rects.size(); ++i)
425         graphicsContext.fillRect(rects[i]);
426
427     if (!m_isShowingFindIndicator)
428         return;
429
430     if (Frame* selectedFrame = frameWithSelection(m_webPage->corePage())) {
431         IntRect findIndicatorRect = selectedFrame->view()->contentsToWindow(enclosingIntRect(selectedFrame->selection().bounds()));
432
433         if (findIndicatorRect != m_findIndicatorRect)
434             hideFindIndicator();
435     }
436 }
437
438 bool FindController::mouseEvent(PageOverlay*, const WebMouseEvent& mouseEvent)
439 {
440     if (mouseEvent.type() == WebEvent::MouseDown)
441         hideFindUI();
442
443     return false;
444 }
445
446 } // namespace WebKit