FindController.cpp revision 2fc2651226baac27029e38c9d6ef883fa32084db
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 41using namespace WebCore; 42 43namespace WebKit { 44 45static 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 54FindController::FindController(WebPage* webPage) 55 : m_webPage(webPage) 56 , m_findPageOverlay(0) 57 , m_isShowingFindIndicator(false) 58{ 59} 60 61FindController::~FindController() 62{ 63} 64 65void 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 73static 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 83void 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 142void FindController::hideFindUI() 143{ 144 if (m_findPageOverlay) 145 m_webPage->uninstallPageOverlay(m_findPageOverlay); 146 147 hideFindIndicator(); 148} 149 150bool 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 201void 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 211Vector<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 235void FindController::pageOverlayDestroyed(PageOverlay*) 236{ 237} 238 239void 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 249void FindController::didMoveToWebPage(PageOverlay*, WebPage*) 250{ 251} 252 253static const float shadowOffsetX = 0.0; 254static const float shadowOffsetY = 1.0; 255static const float shadowBlurRadius = 2.0; 256static const float whiteFrameThickness = 1.0; 257 258static const int overlayBackgroundRed = 25; 259static const int overlayBackgroundGreen = 25; 260static const int overlayBackgroundBlue = 25; 261static const int overlayBackgroundAlpha = 63; 262 263static Color overlayBackgroundColor() 264{ 265 return Color(overlayBackgroundRed, overlayBackgroundGreen, overlayBackgroundBlue, overlayBackgroundAlpha); 266} 267 268void 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 297bool 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 309