FindController.cpp 16 KB

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