2011-02-07 Anders Carlsson <andersca@apple.com>
[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     IntRect selectionRect = enclosingIntRect(selectedFrame->selection()->bounds());
156
157     // We want the selection rect in window coordinates.
158     IntRect selectionRectInWindowCoordinates = selectedFrame->view()->contentsToWindow(selectionRect);
159     
160     Vector<FloatRect> textRects;
161     selectedFrame->selection()->getClippedVisibleTextRectangles(textRects);
162
163     // Create a backing store and paint the find indicator text into it.
164     RefPtr<ShareableBitmap> findIndicatorTextBackingStore = ShareableBitmap::createShareable(selectionRect.size());
165     if (!findIndicatorTextBackingStore)
166         return false;
167     
168     OwnPtr<GraphicsContext> graphicsContext = findIndicatorTextBackingStore->createGraphicsContext();
169
170     IntRect paintRect = selectionRect;
171     paintRect.move(selectedFrame->view()->frameRect().x(), selectedFrame->view()->frameRect().y());
172     paintRect.move(-selectedFrame->view()->scrollOffset());
173
174     graphicsContext->translate(-paintRect.x(), -paintRect.y());
175     selectedFrame->view()->setPaintBehavior(PaintBehaviorSelectionOnly | PaintBehaviorForceBlackText | PaintBehaviorFlattenCompositingLayers);
176     selectedFrame->document()->updateLayout();
177
178     selectedFrame->view()->paint(graphicsContext.get(), paintRect);
179     selectedFrame->view()->setPaintBehavior(PaintBehaviorNormal);
180     
181     SharedMemory::Handle handle;
182     if (!findIndicatorTextBackingStore->createHandle(handle))
183         return false;
184
185     // We want the text rects in selection rect coordinates.
186     Vector<FloatRect> textRectsInSelectionRectCoordinates;
187     
188     for (size_t i = 0; i < textRects.size(); ++i) {
189         IntRect textRectInSelectionRectCoordinates = selectedFrame->view()->contentsToWindow(enclosingIntRect(textRects[i]));
190         textRectInSelectionRectCoordinates.move(-selectionRectInWindowCoordinates.x(), -selectionRectInWindowCoordinates.y());
191
192         textRectsInSelectionRectCoordinates.append(textRectInSelectionRectCoordinates);
193     }            
194     
195     m_webPage->send(Messages::WebPageProxy::SetFindIndicator(selectionRectInWindowCoordinates, textRectsInSelectionRectCoordinates, handle, !isShowingOverlay));
196     m_isShowingFindIndicator = true;
197
198     return true;
199 }
200
201 void FindController::hideFindIndicator()
202 {
203     if (!m_isShowingFindIndicator)
204         return;
205
206     SharedMemory::Handle handle;
207     m_webPage->send(Messages::WebPageProxy::SetFindIndicator(FloatRect(), Vector<FloatRect>(), handle, false));
208     m_isShowingFindIndicator = false;
209 }
210
211 Vector<IntRect> FindController::rectsForTextMatches()
212 {
213     Vector<IntRect> rects;
214
215     for (Frame* frame = m_webPage->corePage()->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
216         Document* document = frame->document();
217         if (!document)
218             continue;
219
220         IntRect visibleRect = frame->view()->visibleContentRect();
221         Vector<IntRect> frameRects = document->markers()->renderedRectsForMarkers(DocumentMarker::TextMatch);
222         IntPoint frameOffset(-frame->view()->scrollOffset().width(), -frame->view()->scrollOffset().height());
223         frameOffset = frame->view()->convertToContainingWindow(frameOffset);
224
225         for (Vector<IntRect>::iterator it = frameRects.begin(), end = frameRects.end(); it != end; ++it) {
226             it->intersect(visibleRect);
227             it->move(frameOffset.x(), frameOffset.y());
228             rects.append(*it);
229         }
230     }
231
232     return rects;
233 }
234
235 void FindController::pageOverlayDestroyed(PageOverlay*)
236 {
237 }
238
239 void FindController::willMoveToWebPage(PageOverlay*, WebPage* webPage)
240 {
241     if (webPage)
242         return;
243
244     // The page overlay is moving away from the web page, reset it.
245     ASSERT(m_findPageOverlay);
246     m_findPageOverlay = 0;
247 }
248     
249 void FindController::didMoveToWebPage(PageOverlay*, WebPage*)
250 {
251 }
252
253 static const float shadowOffsetX = 0.0;
254 static const float shadowOffsetY = 1.0;
255 static const float shadowBlurRadius = 2.0;
256 static const float whiteFrameThickness = 1.0;
257
258 static const int overlayBackgroundRed = 25;
259 static const int overlayBackgroundGreen = 25;
260 static const int overlayBackgroundBlue = 25;
261 static const int overlayBackgroundAlpha = 63;
262
263 static Color overlayBackgroundColor()
264 {
265     return Color(overlayBackgroundRed, overlayBackgroundGreen, overlayBackgroundBlue, overlayBackgroundAlpha);
266 }
267
268 void FindController::drawRect(PageOverlay*, GraphicsContext& graphicsContext, const IntRect& dirtyRect)
269 {
270     Vector<IntRect> rects = rectsForTextMatches();
271
272     // Draw the background.
273     graphicsContext.fillRect(dirtyRect, overlayBackgroundColor(), ColorSpaceSRGB);
274
275     graphicsContext.save();
276     graphicsContext.setShadow(FloatSize(shadowOffsetX, shadowOffsetY), shadowBlurRadius, Color::black, ColorSpaceSRGB);
277
278     graphicsContext.setFillColor(Color::white, ColorSpaceSRGB);
279
280     // Draw white frames around the holes.
281     for (size_t i = 0; i < rects.size(); ++i) {
282         IntRect whiteFrameRect = rects[i];
283         whiteFrameRect.inflate(1);
284
285         graphicsContext.fillRect(whiteFrameRect);
286     }
287
288     graphicsContext.restore();
289
290     graphicsContext.setFillColor(Color::transparent, ColorSpaceSRGB);
291
292     // Clear out the holes.
293     for (size_t i = 0; i < rects.size(); ++i)
294         graphicsContext.fillRect(rects[i]);
295 }
296
297 bool FindController::mouseEvent(PageOverlay* pageOverlay, const WebMouseEvent& mouseEvent)
298 {
299     // If we get a mouse down event inside the page overlay we should hide the find UI.
300     if (mouseEvent.type() == WebEvent::MouseDown) {
301         // Dismiss the overlay.
302         hideFindUI();
303     }
304
305     return false;
306 }
307
308 } // namespace WebKit