afe19922d754c8c84761a56bf9fa06ab5d18a685
[WebKit.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 "ShareableBitmap.h"
30 #include "WKPage.h"
31 #include "WebCoreArgumentCoders.h"
32 #include "WebPage.h"
33 #include "WebPageProxyMessages.h"
34 #include "WebProcess.h"
35 #include <WebCore/DocumentMarkerController.h>
36 #include <WebCore/Frame.h>
37 #include <WebCore/FrameView.h>
38 #include <WebCore/GraphicsContext.h>
39 #include <WebCore/Page.h>
40
41 using namespace WebCore;
42
43 namespace WebKit {
44
45 static WebCore::FindOptions core(FindOptions options)
46 {
47     return (options & FindOptionsCaseInsensitive ? CaseInsensitive : 0)
48         | (options & FindOptionsAtWordStarts ? AtWordStarts : 0)
49         | (options & FindOptionsTreatMedialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0)
50         | (options & FindOptionsBackwards ? Backwards : 0)
51         | (options & FindOptionsWrapAround ? WrapAround : 0);
52 }
53
54 FindController::FindController(WebPage* webPage)
55     : m_webPage(webPage)
56     , m_findPageOverlay(0)
57     , m_isShowingFindIndicator(false)
58 {
59 }
60
61 FindController::~FindController()
62 {
63 }
64
65 void FindController::countStringMatches(const String& string, FindOptions options, unsigned maxMatchCount)
66 {
67     unsigned matchCount = m_webPage->corePage()->markAllMatchesForText(string, core(options), false, maxMatchCount);
68     m_webPage->corePage()->unmarkAllTextMatches();
69
70     m_webPage->send(Messages::WebPageProxy::DidCountStringMatches(string, matchCount));
71 }
72
73 static Frame* frameWithSelection(Page* page)
74 {
75     for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
76         if (frame->selection()->isRange())
77             return frame;
78     }
79
80     return 0;
81 }
82
83 void FindController::findString(const String& string, FindOptions options, unsigned maxMatchCount)
84 {
85     m_webPage->corePage()->unmarkAllTextMatches();
86
87     bool found = m_webPage->corePage()->findString(string, core(options));
88
89     Frame* selectedFrame = frameWithSelection(m_webPage->corePage());
90
91     bool shouldShowOverlay = false;
92
93     if (!found) {
94         // Clear the selection.
95         if (selectedFrame)
96             selectedFrame->selection()->clear();
97
98         hideFindIndicator();
99
100         m_webPage->send(Messages::WebPageProxy::DidFailToFindString(string));
101     } else {
102         shouldShowOverlay = options & FindOptionsShowOverlay;
103
104         if (shouldShowOverlay) {
105             unsigned matchCount = m_webPage->corePage()->markAllMatchesForText(string, core(options), false, maxMatchCount + 1);
106
107             // Check if we have more matches than allowed.
108             if (matchCount > maxMatchCount) {
109                 shouldShowOverlay = false;
110                 matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
111             }
112
113             m_webPage->send(Messages::WebPageProxy::DidFindString(string, matchCount));
114         }
115
116         if (!(options & FindOptionsShowFindIndicator) || !updateFindIndicator(selectedFrame, shouldShowOverlay)) {
117             // Either we shouldn't show the find indicator, or we couldn't update it.
118             hideFindIndicator();
119         }
120     }
121
122     if (!shouldShowOverlay) {
123         if (m_findPageOverlay) {
124             // Get rid of the overlay.
125             m_webPage->uninstallPageOverlay(m_findPageOverlay);
126         }
127         
128         ASSERT(!m_findPageOverlay);
129         return;
130     }
131
132     if (!m_findPageOverlay) {
133         RefPtr<PageOverlay> findPageOverlay = PageOverlay::create(this);
134         m_findPageOverlay = findPageOverlay.get();
135         m_webPage->installPageOverlay(findPageOverlay.release());
136     } else {
137         // The page overlay needs to be repainted.
138         m_findPageOverlay->setNeedsDisplay();
139     }
140 }
141
142 void FindController::hideFindUI()
143 {
144     if (m_findPageOverlay)
145         m_webPage->uninstallPageOverlay(m_findPageOverlay);
146
147     hideFindIndicator();
148 }
149
150 bool FindController::updateFindIndicator(Frame* selectedFrame, bool isShowingOverlay)
151 {
152     if (!selectedFrame)
153         return false;
154
155     // We want the selection rect in window coordinates.
156     IntRect selectionRectInWindowCoordinates = selectedFrame->view()->contentsToWindow(enclosingIntRect(selectedFrame->selection()->bounds()));
157     
158     Vector<FloatRect> textRects;
159     selectedFrame->selection()->getClippedVisibleTextRectangles(textRects);
160
161     // Create a backing store and paint the find indicator text into it.
162     RefPtr<ShareableBitmap> findIndicatorTextBackingStore = ShareableBitmap::createShareable(selectionRectInWindowCoordinates.size());
163     if (!findIndicatorTextBackingStore)
164         return false;
165     
166     OwnPtr<GraphicsContext> graphicsContext = findIndicatorTextBackingStore->createGraphicsContext();
167
168     graphicsContext->translate(-selectionRectInWindowCoordinates.x(), -selectionRectInWindowCoordinates.y());
169     selectedFrame->view()->setPaintBehavior(PaintBehaviorSelectionOnly | PaintBehaviorForceBlackText | PaintBehaviorFlattenCompositingLayers);
170     selectedFrame->document()->updateLayout();
171
172     graphicsContext->clip(selectionRectInWindowCoordinates);
173     selectedFrame->view()->paint(graphicsContext.get(), selectionRectInWindowCoordinates);
174     selectedFrame->view()->setPaintBehavior(PaintBehaviorNormal);
175     
176     SharedMemory::Handle handle;
177     if (!findIndicatorTextBackingStore->createHandle(handle))
178         return false;
179
180     // We want the text rects in selection rect coordinates.
181     Vector<FloatRect> textRectsInSelectionRectCoordinates;
182     
183     for (size_t i = 0; i < textRects.size(); ++i) {
184         IntRect textRectInSelectionRectCoordinates = selectedFrame->view()->contentsToWindow(enclosingIntRect(textRects[i]));
185         textRectInSelectionRectCoordinates.move(-selectionRectInWindowCoordinates.x(), -selectionRectInWindowCoordinates.y());
186
187         textRectsInSelectionRectCoordinates.append(textRectInSelectionRectCoordinates);
188     }            
189     
190     m_webPage->send(Messages::WebPageProxy::SetFindIndicator(selectionRectInWindowCoordinates, textRectsInSelectionRectCoordinates, handle, !isShowingOverlay));
191     m_isShowingFindIndicator = true;
192
193     return true;
194 }
195
196 void FindController::hideFindIndicator()
197 {
198     if (!m_isShowingFindIndicator)
199         return;
200
201     SharedMemory::Handle handle;
202     m_webPage->send(Messages::WebPageProxy::SetFindIndicator(FloatRect(), Vector<FloatRect>(), handle, false));
203     m_isShowingFindIndicator = false;
204 }
205
206 Vector<IntRect> FindController::rectsForTextMatches()
207 {
208     Vector<IntRect> rects;
209
210     for (Frame* frame = m_webPage->corePage()->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
211         Document* document = frame->document();
212         if (!document)
213             continue;
214
215         IntRect visibleRect = frame->view()->visibleContentRect();
216         Vector<IntRect> frameRects = document->markers()->renderedRectsForMarkers(DocumentMarker::TextMatch);
217         IntPoint frameOffset(-frame->view()->scrollOffset().width(), -frame->view()->scrollOffset().height());
218         frameOffset = frame->view()->convertToContainingWindow(frameOffset);
219
220         for (Vector<IntRect>::iterator it = frameRects.begin(), end = frameRects.end(); it != end; ++it) {
221             it->intersect(visibleRect);
222             it->move(frameOffset.x(), frameOffset.y());
223             rects.append(*it);
224         }
225     }
226
227     return rects;
228 }
229
230 void FindController::pageOverlayDestroyed(PageOverlay*)
231 {
232 }
233
234 void FindController::willMoveToWebPage(PageOverlay*, WebPage* webPage)
235 {
236     if (webPage)
237         return;
238
239     // The page overlay is moving away from the web page, reset it.
240     ASSERT(m_findPageOverlay);
241     m_findPageOverlay = 0;
242 }
243     
244 void FindController::didMoveToWebPage(PageOverlay*, WebPage*)
245 {
246 }
247
248 static const float shadowOffsetX = 0.0;
249 static const float shadowOffsetY = 1.0;
250 static const float shadowBlurRadius = 2.0;
251 static const float whiteFrameThickness = 1.0;
252
253 static const int overlayBackgroundRed = 25;
254 static const int overlayBackgroundGreen = 25;
255 static const int overlayBackgroundBlue = 25;
256 static const int overlayBackgroundAlpha = 63;
257
258 static Color overlayBackgroundColor()
259 {
260     return Color(overlayBackgroundRed, overlayBackgroundGreen, overlayBackgroundBlue, overlayBackgroundAlpha);
261 }
262
263 void FindController::drawRect(PageOverlay*, GraphicsContext& graphicsContext, const IntRect& dirtyRect)
264 {
265     Vector<IntRect> rects = rectsForTextMatches();
266
267     // Draw the background.
268     graphicsContext.fillRect(dirtyRect, overlayBackgroundColor(), ColorSpaceSRGB);
269
270     graphicsContext.save();
271     graphicsContext.setShadow(FloatSize(shadowOffsetX, shadowOffsetY), shadowBlurRadius, Color::black, ColorSpaceSRGB);
272
273     graphicsContext.setFillColor(Color::white, ColorSpaceSRGB);
274
275     // Draw white frames around the holes.
276     for (size_t i = 0; i < rects.size(); ++i) {
277         IntRect whiteFrameRect = rects[i];
278         whiteFrameRect.inflate(1);
279
280         graphicsContext.fillRect(whiteFrameRect);
281     }
282
283     graphicsContext.restore();
284
285     graphicsContext.setFillColor(Color::transparent, ColorSpaceSRGB);
286
287     // Clear out the holes.
288     for (size_t i = 0; i < rects.size(); ++i)
289         graphicsContext.fillRect(rects[i]);
290 }
291
292 bool FindController::mouseEvent(PageOverlay* pageOverlay, const WebMouseEvent& mouseEvent)
293 {
294     // If we get a mouse down event inside the page overlay we should hide the find UI.
295     if (mouseEvent.type() == WebEvent::MouseDown) {
296         // Dismiss the overlay.
297         hideFindUI();
298     }
299
300     return false;
301 }
302
303 } // namespace WebKit