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 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 55 FindController::FindController(WebPage* webPage) 56 : m_webPage(webPage) 57 , m_findPageOverlay(0) 58 , m_isShowingFindIndicator(false) 59 { 60 } 61 62 FindController::~FindController() 63 { 64 } 65 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 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 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 153 void FindController::hideFindUI() 154 { 155 if (m_findPageOverlay) 156 m_webPage->uninstallPageOverlay(m_findPageOverlay, true); 157 158 hideFindIndicator(); 159 } 160 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 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 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 250 void FindController::pageOverlayDestroyed(PageOverlay*) 251 { 252 } 253 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 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 278 static Color overlayBackgroundColor(float fractionFadedIn) 279 { 280 return Color(overlayBackgroundRed, overlayBackgroundGreen, overlayBackgroundBlue, overlayBackgroundAlpha * fractionFadedIn); 281 } 282 283 static Color holeShadowColor(float fractionFadedIn) 284 { 285 return Color(0.0f, 0.0f, 0.0f, fractionFadedIn); 286 } 287 288 static Color holeFillColor(float fractionFadedIn) 289 { 290 return Color(1.0f, 1.0f, 1.0f, fractionFadedIn); 291 } 292 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 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