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