Home | History | Annotate | Download | only in WebPage
      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