Compute document marker rects at use time instead of paint time
authortimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Sep 2015 19:36:37 +0000 (19:36 +0000)
committertimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Sep 2015 19:36:37 +0000 (19:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=149643

Reviewed by Darin Adler.

* dom/DocumentMarkerController.cpp:
(WebCore::DocumentMarkerController::DocumentMarkerController):
(WebCore::DocumentMarkerController::detach):
Store and zero the Document backpointer.

(WebCore::DocumentMarkerController::addMarker):
(WebCore::DocumentMarkerController::addTextMatchMarker):
(WebCore::DocumentMarkerController::addDictationPhraseWithAlternativesMarker):
(WebCore::DocumentMarkerController::addDictationResultMarker):
(WebCore::DocumentMarkerController::copyMarkers):
Remove some useless comments.

(WebCore::updateRenderedRectsForMarker):
Use Range to compute the (unclipped, absolute) text rects, and store them
on the RenderedDocumentMarker.

(WebCore::DocumentMarkerController::invalidateRectsForAllMarkers):
(WebCore::DocumentMarkerController::invalidateRectsForMarkersInNode):
Mark the relevant markers' rects as invalid, and notify the ChromeClient.

(WebCore::DocumentMarkerController::updateRectsForInvalidatedMarkersOfType):
Run through all markers, computing rects for ones that don't have valid rects.
We will do layout (once) if necessary. However, this is most commonly
called from a place where layout should already be up to date, so this
should not happen often. updateRenderedRectsForMarker also asserts
that layout is up to date.

(WebCore::DocumentMarkerController::renderedRectsForMarkers):
Retrieve all markers of the given type, clip them by overflow clip and
frame clip, and return them to the caller.

(WebCore::DocumentMarkerController::addMarker):
(WebCore::DocumentMarkerController::markerContainingPoint):
(WebCore::DocumentMarkerController::markersFor):
(WebCore::DocumentMarkerController::repaintMarkers):
(WebCore::DocumentMarkerController::shiftMarkers):
(DocumentMarkerController::setMarkersActive):
(DocumentMarkerController::hasMarkers):
Modern iteration.

(WebCore::DocumentMarkerController::invalidateRenderedRectsForMarkersInRect): Deleted.
* dom/DocumentMarkerController.h:

* dom/Document.cpp:
(WebCore::Document::Document):
Pass the Document to the DocumentMarkerController.

* dom/RenderedDocumentMarker.h:
(WebCore::RenderedDocumentMarker::contains):
(WebCore::RenderedDocumentMarker::setUnclippedAbsoluteRects):
(WebCore::RenderedDocumentMarker::unclippedAbsoluteRects):
(WebCore::RenderedDocumentMarker::invalidate):
(WebCore::RenderedDocumentMarker::isValid):
(WebCore::RenderedDocumentMarker::addRenderedRect): Deleted.
(WebCore::RenderedDocumentMarker::renderedRects): Deleted.
Instead of keeping "rendered" rects, keep unclipped rects in absolute
document coordinates. Invalidation will happen wholesale instead of
based on dirty rects, because computation no longer happens at paint
time, so it's safe to throw all of the rects away.
Keep track of whether we have valid rects or not.

* editing/Editor.cpp:
(WebCore::Editor::countMatchesForText):
We don't need to do a fake whole-document paint just to compute the
document marker rects. They are computed on demand, using layout information
instead of paint-time information.

* page/ChromeClient.h:
Remove an unnecessary semicolon.
Add didInvalidateDocumentMarkerRects, which DocumentMarkerController
will call whenever document marker rects are invalidated (and when e.g.
the find holes PageOverlay should repaint itself).

* page/FrameView.cpp:
(WebCore::FrameView::layout):
Invalidate document marker's layout-computed rects after layout is complete.
They'll be lazily recomputed when next requested.

(WebCore::FrameView::willPaintContents):
Remove the pre-painting invalidation of document marker rects.

* rendering/InlineTextBox.cpp:
(WebCore::InlineTextBox::paintTextMatchMarker):
(WebCore::InlineTextBox::paintDocumentMarker): Deleted.
(WebCore::InlineTextBox::computeRectForReplacementMarker): Deleted.
(WebCore::InlineTextBox::paintDocumentMarkers): Deleted.
Don't bother computing rendered rects for document markers at paint time.
Just do the actual painting work (which, for replacement markers, is
nothing at all).

* rendering/InlineTextBox.h:
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::updateLayerPositions):
(WebCore::RenderLayer::updateLayerPositionsAfterScroll):
When RenderLayers move, invalidate document marker rects.

* rendering/svg/SVGInlineFlowBox.cpp:
(WebCore::SVGInlineFlowBox::paint):
(WebCore::SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer): Deleted.
* rendering/svg/SVGInlineFlowBox.h:
* rendering/svg/SVGRootInlineBox.cpp:
(WebCore::SVGRootInlineBox::paint):
Don't bother computing rendered rects for document markers at paint time.

* testing/Internals.cpp:
(WebCore::markerTypeFrom):
(WebCore::markerTypesFrom):
Add markerTypeFrom, which returns MarkerType enum values for the input string
instead of a MarkerTypes class.

(WebCore::Internals::dumpMarkerRects):
Dump all markers for the given type, and request them from DocumentMarkerController
instead of from the RenderedDocumentMarker, because we want fully clipped
rects, which can only be obtained from DocumentMarkerController.

* editing/FrameSelection.cpp:
(WebCore::FrameSelection::getTextRectangles):
Avoid enclosing unnecessarily, to avoid inflating zero-width text rects
to have a non-zero width. Callers appear to be OK with non-integral positions.

* testing/Internals.h:
* testing/Internals.idl:

* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::didInvalidateDocumentMarkerRects):
* WebProcess/WebCoreSupport/WebChromeClient.h:
Plumb the notification about invalidated document marker rects through to FindController.

* WebProcess/WebPage/FindController.cpp:
(WebKit::FindController::updateFindUIAfterPageScroll):
Make a document-relative PageOverlay, so that we get fast scrolling.

(WebKit::FindController::rectsForTextMatchesInRect):
Return find hole rects in main-FrameView content coordinates, to
match the document-relative page overlay.
We'll only return rects that intersect the passed-in rect, which
comes from the dirty rect passed to drawRect.

(WebKit::FindController::drawRect):
Inflate the dirty rect by the border width so that we hit all relevant
markers in each tile. Otherwise, we would end up missing the border of a marker
that touched the edge of a tile.

(WebKit::FindController::didInvalidateDocumentMarkerRects):
Repaint (for now, the entire overlay) when document marker rects change.

* WebProcess/WebPage/FindController.h:

* WebView/WebHTMLView.mm:
(-[WebHTMLView rectsForTextMatches]):

* WebView.cpp:
(WebView::rectsForTextMatches):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@190363 268f45cc-cd09-0410-ab3c-d52691b4dbfc

27 files changed:
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/DocumentMarkerController.cpp
Source/WebCore/dom/DocumentMarkerController.h
Source/WebCore/dom/RenderedDocumentMarker.h
Source/WebCore/editing/Editor.cpp
Source/WebCore/editing/FrameSelection.cpp
Source/WebCore/page/ChromeClient.h
Source/WebCore/page/FrameView.cpp
Source/WebCore/rendering/InlineTextBox.cpp
Source/WebCore/rendering/InlineTextBox.h
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp
Source/WebCore/rendering/svg/SVGInlineFlowBox.h
Source/WebCore/rendering/svg/SVGRootInlineBox.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebKit/mac/ChangeLog
Source/WebKit/mac/WebView/WebHTMLView.mm
Source/WebKit/win/ChangeLog
Source/WebKit/win/WebView.cpp
Source/WebKit2/ChangeLog
Source/WebKit2/WebProcess/WebCoreSupport/WebChromeClient.cpp
Source/WebKit2/WebProcess/WebCoreSupport/WebChromeClient.h
Source/WebKit2/WebProcess/WebPage/FindController.cpp
Source/WebKit2/WebProcess/WebPage/FindController.h

index 5af17e8..f806976 100644 (file)
@@ -1,3 +1,133 @@
+2015-09-30  Timothy Horton  <timothy_horton@apple.com>
+
+        Compute document marker rects at use time instead of paint time
+        https://bugs.webkit.org/show_bug.cgi?id=149643
+
+        Reviewed by Darin Adler.
+
+        * dom/DocumentMarkerController.cpp:
+        (WebCore::DocumentMarkerController::DocumentMarkerController):
+        (WebCore::DocumentMarkerController::detach):
+        Store and zero the Document backpointer.
+
+        (WebCore::DocumentMarkerController::addMarker):
+        (WebCore::DocumentMarkerController::addTextMatchMarker):
+        (WebCore::DocumentMarkerController::addDictationPhraseWithAlternativesMarker):
+        (WebCore::DocumentMarkerController::addDictationResultMarker):
+        (WebCore::DocumentMarkerController::copyMarkers):
+        Remove some useless comments.
+
+        (WebCore::updateRenderedRectsForMarker):
+        Use Range to compute the (unclipped, absolute) text rects, and store them
+        on the RenderedDocumentMarker.
+
+        (WebCore::DocumentMarkerController::invalidateRectsForAllMarkers):
+        (WebCore::DocumentMarkerController::invalidateRectsForMarkersInNode):
+        Mark the relevant markers' rects as invalid, and notify the ChromeClient.
+
+        (WebCore::DocumentMarkerController::updateRectsForInvalidatedMarkersOfType):
+        Run through all markers, computing rects for ones that don't have valid rects.
+        We will do layout (once) if necessary. However, this is most commonly
+        called from a place where layout should already be up to date, so this
+        should not happen often. updateRenderedRectsForMarker also asserts
+        that layout is up to date.
+
+        (WebCore::DocumentMarkerController::renderedRectsForMarkers):
+        Retrieve all markers of the given type, clip them by overflow clip and
+        frame clip, and return them to the caller.
+
+        (WebCore::DocumentMarkerController::addMarker):
+        (WebCore::DocumentMarkerController::markerContainingPoint):
+        (WebCore::DocumentMarkerController::markersFor):
+        (WebCore::DocumentMarkerController::repaintMarkers):
+        (WebCore::DocumentMarkerController::shiftMarkers):
+        (DocumentMarkerController::setMarkersActive):
+        (DocumentMarkerController::hasMarkers):
+        Modern iteration.
+        
+        (WebCore::DocumentMarkerController::invalidateRenderedRectsForMarkersInRect): Deleted.
+        * dom/DocumentMarkerController.h:
+
+        * dom/Document.cpp:
+        (WebCore::Document::Document):
+        Pass the Document to the DocumentMarkerController.
+
+        * dom/RenderedDocumentMarker.h:
+        (WebCore::RenderedDocumentMarker::contains):
+        (WebCore::RenderedDocumentMarker::setUnclippedAbsoluteRects):
+        (WebCore::RenderedDocumentMarker::unclippedAbsoluteRects):
+        (WebCore::RenderedDocumentMarker::invalidate):
+        (WebCore::RenderedDocumentMarker::isValid):
+        (WebCore::RenderedDocumentMarker::addRenderedRect): Deleted.
+        (WebCore::RenderedDocumentMarker::renderedRects): Deleted.
+        Instead of keeping "rendered" rects, keep unclipped rects in absolute
+        document coordinates. Invalidation will happen wholesale instead of
+        based on dirty rects, because computation no longer happens at paint
+        time, so it's safe to throw all of the rects away.
+        Keep track of whether we have valid rects or not.
+
+        * editing/Editor.cpp:
+        (WebCore::Editor::countMatchesForText):
+        We don't need to do a fake whole-document paint just to compute the
+        document marker rects. They are computed on demand, using layout information
+        instead of paint-time information.
+
+        * page/ChromeClient.h:
+        Remove an unnecessary semicolon.
+        Add didInvalidateDocumentMarkerRects, which DocumentMarkerController
+        will call whenever document marker rects are invalidated (and when e.g.
+        the find holes PageOverlay should repaint itself).
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::layout):
+        Invalidate document marker's layout-computed rects after layout is complete.
+        They'll be lazily recomputed when next requested.
+
+        (WebCore::FrameView::willPaintContents):
+        Remove the pre-painting invalidation of document marker rects.
+
+        * rendering/InlineTextBox.cpp:
+        (WebCore::InlineTextBox::paintTextMatchMarker):
+        (WebCore::InlineTextBox::paintDocumentMarker): Deleted.
+        (WebCore::InlineTextBox::computeRectForReplacementMarker): Deleted.
+        (WebCore::InlineTextBox::paintDocumentMarkers): Deleted.
+        Don't bother computing rendered rects for document markers at paint time.
+        Just do the actual painting work (which, for replacement markers, is
+        nothing at all).
+
+        * rendering/InlineTextBox.h:
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::updateLayerPositions):
+        (WebCore::RenderLayer::updateLayerPositionsAfterScroll):
+        When RenderLayers move, invalidate document marker rects.
+
+        * rendering/svg/SVGInlineFlowBox.cpp:
+        (WebCore::SVGInlineFlowBox::paint):
+        (WebCore::SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer): Deleted.
+        * rendering/svg/SVGInlineFlowBox.h:
+        * rendering/svg/SVGRootInlineBox.cpp:
+        (WebCore::SVGRootInlineBox::paint):
+        Don't bother computing rendered rects for document markers at paint time.
+
+        * testing/Internals.cpp:
+        (WebCore::markerTypeFrom):
+        (WebCore::markerTypesFrom):
+        Add markerTypeFrom, which returns MarkerType enum values for the input string
+        instead of a MarkerTypes class.
+
+        (WebCore::Internals::dumpMarkerRects):
+        Dump all markers for the given type, and request them from DocumentMarkerController
+        instead of from the RenderedDocumentMarker, because we want fully clipped
+        rects, which can only be obtained from DocumentMarkerController.
+
+        * editing/FrameSelection.cpp:
+        (WebCore::FrameSelection::getTextRectangles):
+        Avoid enclosing unnecessarily, to avoid inflating zero-width text rects
+        to have a non-zero width. Callers appear to be OK with non-integral positions.
+
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2015-09-30  Eric Carlson  <eric.carlson@apple.com>
 
         REGRESSION(r190262): User media unit test failures after r190262
index 3b273c9..fb0b58f 100644 (file)
@@ -457,7 +457,7 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsig
     , m_frameElementsShouldIgnoreScrolling(false)
     , m_updateFocusAppearanceRestoresSelection(false)
     , m_ignoreDestructiveWriteCount(0)
-    , m_markers(std::make_unique<DocumentMarkerController>())
+    , m_markers(std::make_unique<DocumentMarkerController>(*this))
     , m_updateFocusAppearanceTimer(*this, &Document::updateFocusAppearanceTimerFired)
     , m_cssTarget(nullptr)
     , m_processingLoadEvent(false)
index bf42cc3..9695e63 100644 (file)
 #include "config.h"
 #include "DocumentMarkerController.h"
 
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "MainFrame.h"
 #include "NodeTraversal.h"
 #include "Range.h"
 #include "RenderBlockFlow.h"
+#include "RenderLayer.h"
 #include "RenderText.h"
 #include "RenderedDocumentMarker.h"
 #include "TextIterator.h"
@@ -42,8 +46,8 @@ inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerT
     return m_possiblyExistingMarkerTypes.intersects(types);
 }
 
-DocumentMarkerController::DocumentMarkerController()
-    : m_possiblyExistingMarkerTypes(0)
+DocumentMarkerController::DocumentMarkerController(Document& document)
+    : m_document(document)
 {
 }
 
@@ -59,7 +63,6 @@ void DocumentMarkerController::detach()
 
 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description)
 {
-    // Use a TextIterator to visit the potentially multiple nodes the range covers.
     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
         RefPtr<Range> textPiece = markedText.range();
         addMarker(&textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description));
@@ -68,7 +71,6 @@ void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerTyp
 
 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type)
 {
-    // Use a TextIterator to visit the potentially multiple nodes the range covers.
     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
         RefPtr<Range> textPiece = markedText.range();
         addMarker(&textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset()));
@@ -89,27 +91,17 @@ void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset,
 
 void DocumentMarkerController::addTextMatchMarker(const Range* range, bool activeMatch)
 {
-    // Use a TextIterator to visit the potentially multiple nodes the range covers.
     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
         RefPtr<Range> textPiece = markedText.range();
         unsigned startOffset = textPiece->startOffset();
         unsigned endOffset = textPiece->endOffset();
         addMarker(&textPiece->startContainer(), DocumentMarker(startOffset, endOffset, activeMatch));
-        if (endOffset > startOffset) {
-            // Rendered rects for markers in WebKit are not populated until each time
-            // the markers are painted. However, we need it to happen sooner, because
-            // the whole purpose of tickmarks on the scrollbar is to show where
-            // matches off-screen are (that haven't been painted yet).
-            Vector<RenderedDocumentMarker*> markers = markersFor(&textPiece->startContainer());
-            markers[markers.size() - 1]->addRenderedRect(range->absoluteBoundingBox());
-        }
     }
 }
 
 #if PLATFORM(IOS)
 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description, const Vector<String>& interpretations, const RetainPtr<id>& metadata)
 {
-    // Use a TextIterator to visit the potentially multiple nodes the range covers.
     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
         RefPtr<Range> textPiece = markedText.range();
         addMarker(&textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description, interpretations, metadata));
@@ -123,7 +115,6 @@ void DocumentMarkerController::addDictationPhraseWithAlternativesMarker(Range* r
         return;
 
     size_t numberOfAlternatives = interpretations.size() - 1;
-    // Use a TextIterator to visit the potentially multiple nodes the range covers.
     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
         RefPtr<Range> textPiece = markedText.range();
         DocumentMarker marker(DocumentMarker::DictationPhraseWithAlternatives, textPiece->startOffset(), textPiece->endOffset(), "", Vector<String>(numberOfAlternatives), RetainPtr<id>());
@@ -135,7 +126,6 @@ void DocumentMarkerController::addDictationPhraseWithAlternativesMarker(Range* r
 
 void DocumentMarkerController::addDictationResultMarker(Range* range, const RetainPtr<id>& metadata)
 {
-    // Use a TextIterator to visit the potentially multiple nodes the range covers.
     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
         RefPtr<Range> textPiece = markedText.range();
         addMarker(&textPiece->startContainer(), DocumentMarker(DocumentMarker::DictationResult, textPiece->startOffset(), textPiece->endOffset(), String(), Vector<String>(), metadata));
@@ -157,6 +147,150 @@ void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::Marke
     }
 }
 
+static void updateRenderedRectsForMarker(RenderedDocumentMarker& marker, Node& node)
+{
+    ASSERT(!node.document().view() || !node.document().view()->needsLayout());
+
+    // FIXME: We should refactor this so that we don't use Range (because we only have one Node), but still share code with absoluteTextQuads().
+    RefPtr<Range> markerRange = Range::create(node.document(), &node, marker.startOffset(), &node, marker.endOffset());
+    if (!markerRange)
+        return;
+    Vector<FloatQuad> absoluteMarkerQuads;
+    markerRange->absoluteTextQuads(absoluteMarkerQuads, true);
+
+    Vector<FloatRect> absoluteMarkerRects;
+    absoluteMarkerRects.reserveInitialCapacity(absoluteMarkerQuads.size());
+    for (const auto& quad : absoluteMarkerQuads)
+        absoluteMarkerRects.append(quad.boundingBox());
+
+    marker.setUnclippedAbsoluteRects(absoluteMarkerRects);
+}
+
+void DocumentMarkerController::invalidateRectsForAllMarkers()
+{
+    if (!hasMarkers())
+        return;
+
+    for (auto& markers : m_markers.values()) {
+        for (auto& marker : *markers)
+            marker.invalidate();
+    }
+
+    if (Page* page = m_document.page())
+        page->chrome().client().didInvalidateDocumentMarkerRects();
+}
+
+void DocumentMarkerController::invalidateRectsForMarkersInNode(Node& node)
+{
+    if (!hasMarkers())
+        return;
+
+    MarkerList* markers = m_markers.get(&node);
+    ASSERT(markers);
+
+    for (auto& marker : *markers)
+        marker.invalidate();
+
+    if (Page* page = m_document.page())
+        page->chrome().client().didInvalidateDocumentMarkerRects();
+}
+
+static void updateMainFrameLayoutIfNeeded(Document& document)
+{
+    Frame* frame = document.frame();
+    if (!frame)
+        return;
+
+    FrameView* mainFrameView = frame->mainFrame().view();
+    if (!mainFrameView)
+        return;
+
+    mainFrameView->updateLayoutAndStyleIfNeededRecursive();
+}
+
+void DocumentMarkerController::updateRectsForInvalidatedMarkersOfType(DocumentMarker::MarkerType markerType)
+{
+    if (!possiblyHasMarkers(markerType))
+        return;
+    ASSERT(!(m_markers.isEmpty()));
+
+    bool needsLayoutIfAnyRectsAreDirty = true;
+
+    for (auto& nodeAndMarkers : m_markers) {
+        Node& node = *nodeAndMarkers.key;
+        for (auto& marker : *nodeAndMarkers.value) {
+            if (marker.type() != markerType)
+                continue;
+
+            if (marker.isValid())
+                continue;
+
+            // We'll do up to one layout per call if we have any dirty markers.
+            if (needsLayoutIfAnyRectsAreDirty) {
+                updateMainFrameLayoutIfNeeded(m_document);
+                needsLayoutIfAnyRectsAreDirty = false;
+            }
+
+            updateRenderedRectsForMarker(marker, node);
+        }
+    }
+}
+
+Vector<FloatRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
+{
+    Vector<FloatRect> result;
+
+    if (!possiblyHasMarkers(markerType))
+        return result;
+    ASSERT(!(m_markers.isEmpty()));
+
+    Frame* frame = m_document.frame();
+    if (!frame)
+        return result;
+    FrameView* frameView = frame->view();
+    if (!frameView)
+        return result;
+
+    updateRectsForInvalidatedMarkersOfType(markerType);
+
+    bool isSubframe = !frame->isMainFrame();
+    IntRect subframeClipRect;
+    if (isSubframe)
+        subframeClipRect = frameView->windowToContents(frameView->windowClipRect());
+
+    for (auto& nodeAndMarkers : m_markers) {
+        Node& node = *nodeAndMarkers.key;
+        FloatRect overflowClipRect;
+        if (RenderObject* renderer = node.renderer())
+            overflowClipRect = renderer->absoluteClippedOverflowRect();
+        for (auto& marker : *nodeAndMarkers.value) {
+            if (marker.type() != markerType)
+                continue;
+
+            auto renderedRects = marker.unclippedAbsoluteRects();
+
+            // Clip document markers by their overflow clip.
+            if (node.renderer()) {
+                for (auto& rect : renderedRects)
+                    rect.intersect(overflowClipRect);
+            }
+
+            // Clip subframe document markers by their frame.
+            if (isSubframe) {
+                for (auto& rect : renderedRects)
+                    rect.intersect(subframeClipRect);
+            }
+
+            for (const auto& rect : renderedRects) {
+                if (!rect.isEmpty())
+                    result.append(rect);
+            }
+        }
+    }
+    
+    return result;
+}
+
 // Markers are stored in order sorted by their start offset.
 // Markers of the same type do not overlap each other.
 
@@ -233,9 +367,10 @@ void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMa
         list->insert(i, RenderedDocumentMarker(toInsert));
     }
 
-    // repaint the affected node
     if (node->renderer())
         node->renderer()->repaint();
+
+    invalidateRectsForMarkersInNode(*node);
 }
 
 // copies markers from srcNode to dstNode, applying the specified shift delta to the copies.  The shift is
@@ -277,7 +412,6 @@ void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset,
         addMarker(dstNode, marker);
     }
 
-    // repaint the affected node
     if (docDirty && dstNode->renderer())
         dstNode->renderer()->repaint();
 }
@@ -343,7 +477,6 @@ void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, i
             m_possiblyExistingMarkerTypes = 0;
     }
 
-    // repaint the affected node
     if (docDirty && node->renderer())
         node->renderer()->repaint();
 }
@@ -351,19 +484,13 @@ void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, i
 DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType markerType)
 {
     if (!possiblyHasMarkers(markerType))
-        return 0;
+        return nullptr;
     ASSERT(!(m_markers.isEmpty()));
 
-    // outer loop: process each node that contains any markers
-    MarkerMap::iterator end = m_markers.end();
-    for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
-        // inner loop; process each marker in this node
-        MarkerList* list = nodeIterator->value.get();
-        unsigned markerCount = list->size();
-        for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
-            RenderedDocumentMarker& marker = list->at(markerIndex);
+    updateRectsForInvalidatedMarkersOfType(markerType);
 
-            // skip marker that is wrong type
+    for (auto& nodeAndMarkers : m_markers) {
+        for (auto& marker : *nodeAndMarkers.value) {
             if (marker.type() != markerType)
                 continue;
 
@@ -372,7 +499,7 @@ DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoin
         }
     }
 
-    return 0;
+    return nullptr;
 }
 
 Vector<RenderedDocumentMarker*> DocumentMarkerController::markersFor(Node* node, DocumentMarker::MarkerTypes markerTypes)
@@ -382,9 +509,9 @@ Vector<RenderedDocumentMarker*> DocumentMarkerController::markersFor(Node* node,
     if (!list)
         return result;
 
-    for (size_t i = 0; i < list->size(); ++i) {
-        if (markerTypes.contains(list->at(i).type()))
-            result.append(&(list->at(i)));
+    for (auto& marker : *list) {
+        if (markerTypes.contains(marker.type()))
+            result.append(&marker);
     }
 
     return result;
@@ -415,34 +542,6 @@ Vector<RenderedDocumentMarker*> DocumentMarkerController::markersInRange(Range*
     return foundMarkers;
 }
 
-Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
-{
-    Vector<IntRect> result;
-
-    if (!possiblyHasMarkers(markerType))
-        return result;
-    ASSERT(!(m_markers.isEmpty()));
-
-    // outer loop: process each node
-    MarkerMap::iterator end = m_markers.end();
-    for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
-        // inner loop; process each marker in this node
-        MarkerList* list = nodeIterator->value.get();
-        unsigned markerCount = list->size();
-        for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
-            const RenderedDocumentMarker& marker = list->at(markerIndex);
-
-            // skip marker that is wrong type
-            if (marker.type() != markerType)
-                continue;
-
-            result.appendVector(marker.renderedRects());
-        }
-    }
-
-    return result;
-}
-
 void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes)
 {
     if (!possiblyHasMarkers(markerTypes))
@@ -527,10 +626,7 @@ void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes marker
         // inner loop: process each marker in the current node
         MarkerList* list = i->value.get();
         bool nodeNeedsRepaint = false;
-        for (size_t i = 0; i != list->size(); ++i) {
-            DocumentMarker marker = list->at(i);
-
-            // skip nodes that are not of the specified type
+        for (auto& marker : *list) {
             if (markerTypes.contains(marker.type())) {
                 nodeNeedsRepaint = true;
                 break;
@@ -546,19 +642,6 @@ void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes marker
     }
 }
 
-void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const LayoutRect& r)
-{
-    // outer loop: process each markered node in the document
-    MarkerMap::iterator end = m_markers.end();
-    for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
-
-        // inner loop: process each rect in the current node
-        MarkerList* list = i->value.get();
-        for (size_t listIndex = 0; listIndex < list->size(); ++listIndex)
-            list->at(listIndex).invalidate(r);
-    }
-}
-
 void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta)
 {
     if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
@@ -569,9 +652,10 @@ void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, in
     if (!list)
         return;
 
-    bool docDirty = false;
+    bool didShiftMarker = false;
     for (size_t i = 0; i != list->size(); ) {
         RenderedDocumentMarker& marker = list->at(i);
+        // FIXME: How can this possibly be iOS-specific code?
 #if PLATFORM(IOS)
         int targetStartOffset = marker.startOffset() + delta;
         int targetEndOffset = marker.endOffset() + delta;
@@ -583,10 +667,7 @@ void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, in
         if (marker.startOffset() >= startOffset) {
             ASSERT((int)marker.startOffset() + delta >= 0);
             marker.shiftOffsets(delta);
-            docDirty = true;
-
-            // Marker moved, so previously-computed rendered rectangle is now invalid
-            marker.invalidate();
+            didShiftMarker = true;
 #if !PLATFORM(IOS)
         }
 #else
@@ -598,18 +679,18 @@ void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, in
                 continue;
             }
             marker.setEndOffset(targetEndOffset < node->maxCharacterOffset() ? targetEndOffset : node->maxCharacterOffset());
-            docDirty = true;
-
-            // Marker moved, so previously-computed rendered rectangle is now invalid
-            marker.invalidate();
+            didShiftMarker = true;
         }
 #endif
         ++i;
     }
 
-    // repaint the affected node
-    if (docDirty && node->renderer())
-        node->renderer()->repaint();
+    if (didShiftMarker) {
+        invalidateRectsForMarkersInNode(*node);
+
+        if (node->renderer())
+            node->renderer()->repaint();
+    }
 }
 
 void DocumentMarkerController::setMarkersActive(Range* range, bool active)
@@ -636,10 +717,8 @@ void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset
     if (!list)
         return;
 
-    bool docDirty = false;
-    for (size_t i = 0; i != list->size(); ++i) {
-        DocumentMarker& marker = list->at(i);
-
+    bool didActivateMarker = false;
+    for (auto& marker : *list) {
         // Markers are returned in order, so stop if we are now past the specified range.
         if (marker.startOffset() >= endOffset)
             break;
@@ -649,11 +728,10 @@ void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset
             continue;
 
         marker.setActiveMatch(active);
-        docDirty = true;
+        didActivateMarker = true;
     }
 
-    // repaint the affected node
-    if (docDirty && node->renderer())
+    if (didActivateMarker && node->renderer())
         node->renderer()->repaint();
 }
 
index 594ee69..1c90f7d 100644 (file)
@@ -35,6 +35,7 @@
 
 namespace WebCore {
 
+class Document;
 class LayoutPoint;
 class LayoutRect;
 class Node;
@@ -45,7 +46,7 @@ class DocumentMarkerController {
     WTF_MAKE_NONCOPYABLE(DocumentMarkerController); WTF_MAKE_FAST_ALLOCATED;
 public:
 
-    DocumentMarkerController();
+    DocumentMarkerController(Document&);
     ~DocumentMarkerController();
 
     void detach();
@@ -78,17 +79,22 @@ public:
     WEBCORE_EXPORT void removeMarkers(DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers());
     void removeMarkers(Node*, DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers());
     void repaintMarkers(DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers());
-    void invalidateRenderedRectsForMarkersInRect(const LayoutRect&);
     void shiftMarkers(Node*, unsigned startOffset, int delta);
     void setMarkersActive(Range*, bool);
     void setMarkersActive(Node*, unsigned startOffset, unsigned endOffset, bool);
 
-    DocumentMarker* markerContainingPoint(const LayoutPoint&, DocumentMarker::MarkerType);
     WEBCORE_EXPORT Vector<RenderedDocumentMarker*> markersFor(Node*, DocumentMarker::MarkerTypes = DocumentMarker::AllMarkers());
     WEBCORE_EXPORT Vector<RenderedDocumentMarker*> markersInRange(Range*, DocumentMarker::MarkerTypes);
-    WEBCORE_EXPORT Vector<IntRect> renderedRectsForMarkers(DocumentMarker::MarkerType);
     void clearDescriptionOnMarkersIntersectingRange(Range*, DocumentMarker::MarkerTypes);
 
+    WEBCORE_EXPORT void updateRectsForInvalidatedMarkersOfType(DocumentMarker::MarkerType);
+
+    void invalidateRectsForAllMarkers();
+    void invalidateRectsForMarkersInNode(Node&);
+
+    DocumentMarker* markerContainingPoint(const LayoutPoint&, DocumentMarker::MarkerType);
+    WEBCORE_EXPORT Vector<FloatRect> renderedRectsForMarkers(DocumentMarker::MarkerType);
+
 #if ENABLE(TREE_DEBUGGING)
     void showMarkers() const;
 #endif
@@ -103,7 +109,8 @@ private:
 
     MarkerMap m_markers;
     // Provide a quick way to determine whether a particular marker type is absent without going through the map.
-    DocumentMarker::MarkerTypes m_possiblyExistingMarkerTypes;
+    DocumentMarker::MarkerTypes m_possiblyExistingMarkerTypes { 0 };
+    Document& m_document;
 };
 
 } // namespace WebCore
index 8998c66..dc48c3a 100644 (file)
@@ -39,30 +39,39 @@ public:
     {
     }
 
-    bool contains(const LayoutPoint& point) const
+    bool contains(const FloatPoint& point) const
     {
-        for (const auto& rect : m_renderedRects) {
+        ASSERT(m_isValid);
+        for (const auto& rect : m_rects) {
             if (rect.contains(point))
                 return true;
         }
         return false;
     }
 
-    void addRenderedRect(const LayoutRect& r) { m_renderedRects.append(r); }
-    const Vector<LayoutRect, 1>& renderedRects() const { return m_renderedRects; }
-    void invalidate(const LayoutRect& r)
+    void setUnclippedAbsoluteRects(Vector<FloatRect>& rects)
     {
-        for (const auto& rect : m_renderedRects) {
-            if (rect.intersects(r)) {
-                invalidate();
-                return;
-            }
-        }
+        m_isValid = true;
+        m_rects = rects;
+    }
+
+    const Vector<FloatRect, 1>& unclippedAbsoluteRects() const
+    {
+        ASSERT(m_isValid);
+        return m_rects;
     }
-    void invalidate() { m_renderedRects.clear(); }
+
+    void invalidate()
+    {
+        m_isValid = false;
+        m_rects.clear();
+    }
+
+    bool isValid() const { return m_isValid; }
 
 private:
-    Vector<LayoutRect, 1> m_renderedRects;
+    Vector<FloatRect, 1> m_rects;
+    bool m_isValid { false };
 };
 
 
index 44b2ac7..9840fe5 100644 (file)
@@ -3230,25 +3230,6 @@ unsigned Editor::countMatchesForText(const String& target, Range* range, FindOpt
             searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->countChildNodes(), IGNORE_EXCEPTION);
     } while (true);
 
-    if (markMatches || matches) {
-        // Do a "fake" paint in order to execute the code that computes the rendered rect for each text match.
-        if (m_frame.view() && m_frame.contentRenderer()) {
-            document().updateLayout(); // Ensure layout is up to date.
-            // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
-            // FIXME: this should probably look at paintsEntireContents()
-            LayoutRect visibleRect = m_frame.view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
-            if (!visibleRect.isEmpty()) {
-                GraphicsContext context((PlatformGraphicsContext*)0);
-                context.setPaintingDisabled(true);
-
-                PaintBehavior oldBehavior = m_frame.view()->paintBehavior();
-                m_frame.view()->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers);
-                m_frame.view()->paintContents(context, enclosingIntRect(visibleRect));
-                m_frame.view()->setPaintBehavior(oldBehavior);
-            }
-        }
-    }
-
     return matchCount;
 }
 
index 5c66d66..630b00f 100644 (file)
@@ -2103,7 +2103,7 @@ void FrameSelection::getTextRectangles(Vector<FloatRect>& rectangles, TextRectan
     range->absoluteTextQuads(quads, textRectHeight == TextRectangleHeight::SelectionHeight);
 
     for (const auto& quad : quads)
-        rectangles.append(quad.enclosingBoundingBox());
+        rectangles.append(quad.boundingBox());
 }
 
 // Scans logically forward from "start", including any child frames.
index 137c186..bb41def 100644 (file)
@@ -458,9 +458,11 @@ public:
     virtual void imageOrMediaDocumentSizeChanged(const WebCore::IntSize&) { }
 
 #if ENABLE(VIDEO) && USE(GSTREAMER)
-    virtual void requestInstallMissingMediaPlugins(const String& /*details*/, const String& /*description*/, MediaPlayerRequestInstallMissingPluginsCallback&) { };
+    virtual void requestInstallMissingMediaPlugins(const String& /*details*/, const String& /*description*/, MediaPlayerRequestInstallMissingPluginsCallback&) { }
 #endif
 
+    virtual void didInvalidateDocumentMarkerRects() { }
+
 protected:
     virtual ~ChromeClient() { }
 };
index 215c2ef..4955d01 100644 (file)
@@ -1480,6 +1480,8 @@ void FrameView::layout(bool allowSubtree)
     if (document.hasListenerType(Document::OVERFLOWCHANGED_LISTENER))
         updateOverflowStatus(layoutWidth() < contentsWidth(), layoutHeight() < contentsHeight());
 
+    frame().document()->markers().invalidateRectsForAllMarkers();
+
     if (!m_postLayoutTasksTimer.isActive()) {
         if (!m_inSynchronousPostLayout) {
             if (inChildFrameLayoutWithFrameFlattening)
@@ -3925,7 +3927,7 @@ void FrameView::setWasScrolledByUser(bool wasScrolledByUser)
     adjustTiledBackingCoverage();
 }
 
-void FrameView::willPaintContents(GraphicsContext& context, const IntRect& dirtyRect, PaintingState& paintingState)
+void FrameView::willPaintContents(GraphicsContext& context, const IntRect&, PaintingState& paintingState)
 {
     Document* document = frame().document();
 
@@ -3951,9 +3953,6 @@ void FrameView::willPaintContents(GraphicsContext& context, const IntRect& dirty
         if (parentView->paintBehavior() & PaintBehaviorFlattenCompositingLayers)
             m_paintBehavior |= PaintBehaviorFlattenCompositingLayers;
     }
-    
-    if (m_paintBehavior == PaintBehaviorNormal)
-        document->markers().invalidateRenderedRectsForMarkersInRect(dirtyRect);
 
     if (document->printing())
         m_paintBehavior |= PaintBehaviorFlattenCompositingLayers;
index 8ab2ac0..021a1ab 100644 (file)
@@ -1098,14 +1098,6 @@ void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoi
         IntRect markerRect = enclosingIntRect(selectionRect);
         start = markerRect.x() - startPoint.x();
         width = markerRect.width();
-        
-        // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
-        // display a toolTip. We don't do this for misspelling markers.
-        if (grammar || isDictationMarker) {
-            markerRect.move(-boxOrigin.x(), -boxOrigin.y());
-            markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
-            marker.addRenderedRect(markerRect);
-        }
     }
     
     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
@@ -1130,56 +1122,27 @@ void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoi
 
 void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, RenderedDocumentMarker& marker, const RenderStyle& style, const FontCascade& font)
 {
-    LayoutUnit selectionHeight = this->selectionHeight();
+    if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
+        return;
+
+    Color color = marker.activeMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
+    GraphicsContextStateSaver stateSaver(context);
+    updateGraphicsContext(context, TextPaintStyle(color, style.colorSpace())); // Don't draw text at all!
+
+    // Use same y positioning and height as for selection, so that when the selection and this highlight are on
+    // the same word there are no pieces sticking out.
+    LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
+    LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, this->selectionHeight());
 
     int sPos = std::max<int>(marker.startOffset() - m_start, 0);
     int ePos = std::min<int>(marker.endOffset() - m_start, m_len);
     TextRun run = constructTextRun(style, font);
+    font.adjustSelectionRectForText(run, selectionRect, sPos, ePos);
 
-    // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates.
-    // FIXME: figure out how renderedRect and selectionRect are different.
-    LayoutRect renderedRect = LayoutRect(LayoutPoint(x(), selectionTop()), FloatSize(0, selectionHeight));
-    font.adjustSelectionRectForText(run, renderedRect, sPos, ePos);
-    IntRect markerRect = enclosingIntRect(renderedRect);
-    markerRect = renderer().localToAbsoluteQuad(FloatQuad(markerRect)).enclosingBoundingBox();
-    markerRect.intersect(enclosingIntRect(renderer().absoluteClippedOverflowRect()));
-
-    if (markerRect.isEmpty())
+    if (selectionRect.isEmpty())
         return;
 
-    marker.addRenderedRect(markerRect);
-
-    // Optionally highlight the text
-    if (renderer().frame().editor().markedTextMatchesAreHighlighted()) {
-        Color color = marker.activeMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
-        GraphicsContextStateSaver stateSaver(context);
-        updateGraphicsContext(context, TextPaintStyle(color, style.colorSpace())); // Don't draw text at all!
-
-        // Use same y positioning and height as for selection, so that when the selection and this highlight are on
-        // the same word there are no pieces sticking out.
-        LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
-        LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, selectionHeight);
-        font.adjustSelectionRectForText(run, selectionRect, sPos, ePos);
-        context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), color, style.colorSpace());
-    }
-}
-
-void InlineTextBox::computeRectForReplacementMarker(RenderedDocumentMarker& marker, const RenderStyle& style, const FontCascade& font)
-{
-    // Replacement markers are not actually drawn, but their rects need to be computed for hit testing.
-    LayoutUnit top = selectionTop();
-    LayoutUnit h = selectionHeight();
-    
-    int sPos = std::max(marker.startOffset() - m_start, (unsigned)0);
-    int ePos = std::min(marker.endOffset() - m_start, (unsigned)m_len);
-    TextRun run = constructTextRun(style, font);
-
-    // Compute and store the rect associated with this marker.
-    LayoutRect selectionRect = LayoutRect(LayoutPoint(x(), top), LayoutSize(0, h));
-    font.adjustSelectionRectForText(run, selectionRect, sPos, ePos);
-    IntRect markerRect = enclosingIntRect(selectionRect);
-    markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
-    marker.addRenderedRect(markerRect);
+    context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), color, style.colorSpace());
 }
     
 void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin, const RenderStyle& style, const FontCascade& font, bool background)
@@ -1246,7 +1209,6 @@ void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPo
                 paintTextMatchMarker(context, boxOrigin, *marker, style, font);
                 break;
             case DocumentMarker::Replacement:
-                computeRectForReplacementMarker(*marker, style, font);
                 break;
 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
             case DocumentMarker::TelephoneNumber:
index 7409d6d..7d01003 100644 (file)
@@ -164,8 +164,6 @@ private:
     void paintDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, RenderedDocumentMarker&, const RenderStyle&, const FontCascade&, bool grammar);
     void paintTextMatchMarker(GraphicsContext&, const FloatPoint& boxOrigin, RenderedDocumentMarker&, const RenderStyle&, const FontCascade&);
 
-    void computeRectForReplacementMarker(RenderedDocumentMarker&, const RenderStyle&, const FontCascade&);
-
     ExpansionBehavior expansionBehavior() const;
 
     void behavesLikeText() const = delete;
index 7ea1da4..567294f 100644 (file)
@@ -51,6 +51,7 @@
 #include "DebugPageOverlays.h"
 #include "Document.h"
 #include "DocumentEventQueue.h"
+#include "DocumentMarkerController.h"
 #include "Element.h"
 #include "EventHandler.h"
 #include "FEColorMatrix.h"
@@ -576,6 +577,8 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay
 
     if (geometryMap)
         geometryMap->popMappingsToAncestor(parent());
+
+    renderer().document().markers().invalidateRectsForAllMarkers();
 }
 
 LayoutRect RenderLayer::repaintRectIncludingNonCompositingDescendants() const
@@ -875,6 +878,8 @@ void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap
 
     if (geometryMap)
         geometryMap->popMappingsToAncestor(parent());
+
+    renderer().document().markers().invalidateRectsForAllMarkers();
 }
 
 void RenderLayer::positionNewlyCreatedOverflowControls()
index d89bcfe..2d5079e 100644 (file)
@@ -23,9 +23,7 @@
 #include "config.h"
 #include "SVGInlineFlowBox.h"
 
-#include "DocumentMarkerController.h"
 #include "GraphicsContext.h"
-#include "RenderedDocumentMarker.h"
 #include "SVGInlineTextBox.h"
 #include "SVGRenderingContext.h"
 
@@ -52,12 +50,8 @@ void SVGInlineFlowBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffse
 
     SVGRenderingContext renderingContext(renderer(), paintInfo, SVGRenderingContext::SaveGraphicsContext);
     if (renderingContext.isRenderingPrepared()) {
-        for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
-            if (is<SVGInlineTextBox>(*child))
-                computeTextMatchMarkerRectForRenderer(&downcast<SVGInlineTextBox>(*child).renderer());
-
+        for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
             child->paint(paintInfo, paintOffset, 0, 0);
-        }
     }
 }
 
@@ -72,60 +66,4 @@ FloatRect SVGInlineFlowBox::calculateBoundaries() const
     return childRect;
 }
 
-void SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(RenderSVGInlineText* textRenderer)
-{
-    ASSERT(textRenderer);
-
-    Text& textNode = textRenderer->textNode();
-    if (!textNode.inDocument())
-        return;
-
-    RenderStyle& style = textRenderer->style();
-
-    AffineTransform fragmentTransform;
-    Vector<RenderedDocumentMarker*> markers = textRenderer->document().markers().markersFor(&textNode);
-    for (auto* marker : markers) {
-        // SVG is only interessted in the TextMatch marker, for now.
-        if (marker->type() != DocumentMarker::TextMatch)
-            continue;
-
-        FloatRect markerRect;
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            if (!is<SVGInlineTextBox>(*box))
-                continue;
-
-            auto& textBox = downcast<SVGInlineTextBox>(*box);
-
-            int markerStartPosition = std::max<int>(marker->startOffset() - textBox.start(), 0);
-            int markerEndPosition = std::min<int>(marker->endOffset() - textBox.start(), textBox.len());
-
-            if (markerStartPosition >= markerEndPosition)
-                continue;
-
-            int fragmentStartPosition = 0;
-            int fragmentEndPosition = 0;
-
-            const Vector<SVGTextFragment>& fragments = textBox.textFragments();
-            unsigned textFragmentsSize = fragments.size();
-            for (unsigned i = 0; i < textFragmentsSize; ++i) {
-                const SVGTextFragment& fragment = fragments.at(i);
-
-                fragmentStartPosition = markerStartPosition;
-                fragmentEndPosition = markerEndPosition;
-                if (!textBox.mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
-                    continue;
-
-                FloatRect fragmentRect = textBox.selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, &style);
-                fragment.buildFragmentTransform(fragmentTransform);
-                if (!fragmentTransform.isIdentity())
-                    fragmentRect = fragmentTransform.mapRect(fragmentRect);
-
-                markerRect.unite(fragmentRect);
-            }
-        }
-
-        marker->addRenderedRect(textRenderer->localToAbsoluteQuad(markerRect).enclosingBoundingBox());
-    }
-}
-
 } // namespace WebCore
index 0dccc8f..0613bc0 100644 (file)
@@ -42,7 +42,6 @@ public:
 
     void setLogicalHeight(float h) { m_logicalHeight = h; }
     void paintSelectionBackground(PaintInfo&);
-    static void computeTextMatchMarkerRectForRenderer(RenderSVGInlineText*);
 
 private:
     virtual bool isSVGInlineFlowBox() const override { return true; }
index 8b8014b..49bab64 100644 (file)
@@ -66,12 +66,8 @@ void SVGRootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffse
 
     SVGRenderingContext renderingContext(renderSVGText(), paintInfo, SVGRenderingContext::SaveGraphicsContext);
     if (renderingContext.isRenderingPrepared()) {
-        for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
-            if (is<SVGInlineTextBox>(*child))
-                SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(&downcast<SVGInlineTextBox>(*child).renderer());
-
+        for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
             child->paint(paintInfo, paintOffset, 0, 0);
-        }
     }
 }
 
index 8301a9a..26feb7b 100644 (file)
@@ -281,36 +281,48 @@ bool InspectorStubFrontend::sendMessageToFrontend(const String& message)
     return InspectorClient::doDispatchMessageOnFrontendPage(frontendPage(), message);
 }
 
-static bool markerTypesFrom(const String& markerType, DocumentMarker::MarkerTypes& result)
+static bool markerTypeFrom(const String& markerType, DocumentMarker::MarkerType& result)
 {
-    if (markerType.isEmpty() || equalIgnoringCase(markerType, "all"))
-        result = DocumentMarker::AllMarkers();
-    else if (equalIgnoringCase(markerType, "Spelling"))
-        result =  DocumentMarker::Spelling;
+    if (equalIgnoringCase(markerType, "Spelling"))
+        result = DocumentMarker::Spelling;
     else if (equalIgnoringCase(markerType, "Grammar"))
-        result =  DocumentMarker::Grammar;
+        result = DocumentMarker::Grammar;
     else if (equalIgnoringCase(markerType, "TextMatch"))
-        result =  DocumentMarker::TextMatch;
+        result = DocumentMarker::TextMatch;
     else if (equalIgnoringCase(markerType, "Replacement"))
-        result =  DocumentMarker::Replacement;
+        result = DocumentMarker::Replacement;
     else if (equalIgnoringCase(markerType, "CorrectionIndicator"))
-        result =  DocumentMarker::CorrectionIndicator;
+        result = DocumentMarker::CorrectionIndicator;
     else if (equalIgnoringCase(markerType, "RejectedCorrection"))
-        result =  DocumentMarker::RejectedCorrection;
+        result = DocumentMarker::RejectedCorrection;
     else if (equalIgnoringCase(markerType, "Autocorrected"))
-        result =  DocumentMarker::Autocorrected;
+        result = DocumentMarker::Autocorrected;
     else if (equalIgnoringCase(markerType, "SpellCheckingExemption"))
-        result =  DocumentMarker::SpellCheckingExemption;
+        result = DocumentMarker::SpellCheckingExemption;
     else if (equalIgnoringCase(markerType, "DeletedAutocorrection"))
-        result =  DocumentMarker::DeletedAutocorrection;
+        result = DocumentMarker::DeletedAutocorrection;
     else if (equalIgnoringCase(markerType, "DictationAlternatives"))
-        result =  DocumentMarker::DictationAlternatives;
+        result = DocumentMarker::DictationAlternatives;
 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
     else if (equalIgnoringCase(markerType, "TelephoneNumber"))
-        result =  DocumentMarker::TelephoneNumber;
+        result = DocumentMarker::TelephoneNumber;
 #endif
     else
         return false;
+    
+    return true;
+}
+
+static bool markerTypesFrom(const String& markerType, DocumentMarker::MarkerTypes& result)
+{
+    DocumentMarker::MarkerType singularResult;
+
+    if (markerType.isEmpty() || equalIgnoringCase(markerType, "all"))
+        result = DocumentMarker::AllMarkers();
+    else if (markerTypeFrom(markerType, singularResult))
+        result = singularResult;
+    else
+        return false;
 
     return true;
 }
@@ -1043,23 +1055,29 @@ String Internals::markerDescriptionForNode(Node* node, const String& markerType,
     return marker->description();
 }
 
-String Internals::dumpMarkerRectsForNode(Node* node, const String& markerType, unsigned index, ExceptionCode& ec)
+String Internals::dumpMarkerRects(const String& markerTypeString, ExceptionCode& ec)
 {
-    RenderedDocumentMarker* marker = markerAt(node, markerType, index, ec);
-    if (!marker)
+    DocumentMarker::MarkerType markerType;
+    if (!markerTypeFrom(markerTypeString, markerType)) {
+        ec = SYNTAX_ERR;
         return String();
+    }
+
+    contextDocument()->markers().updateRectsForInvalidatedMarkersOfType(markerType);
+    auto rects = contextDocument()->markers().renderedRectsForMarkers(markerType);
+
     StringBuilder rectString;
-    rectString.append("marker rects: ");
-    for (const auto& rect : marker->renderedRects()) {
-        rectString.append("(");
-        rectString.appendNumber(rect.x().toFloat());
-        rectString.append(", ");
-        rectString.appendNumber(rect.y().toFloat());
-        rectString.append(", ");
-        rectString.appendNumber(rect.width().toFloat());
-        rectString.append(", ");
-        rectString.appendNumber(rect.height().toFloat());
-        rectString.append(") ");
+    rectString.appendLiteral("marker rects: ");
+    for (const auto& rect : rects) {
+        rectString.append('(');
+        rectString.appendNumber(rect.x());
+        rectString.appendLiteral(", ");
+        rectString.appendNumber(rect.y());
+        rectString.appendLiteral(", ");
+        rectString.appendNumber(rect.width());
+        rectString.appendLiteral(", ");
+        rectString.appendNumber(rect.height());
+        rectString.appendLiteral(") ");
     }
     return rectString.toString();
 }
index 5c816eb..12a0fce 100644 (file)
@@ -158,7 +158,7 @@ public:
     unsigned markerCountForNode(Node*, const String&, ExceptionCode&);
     RefPtr<Range> markerRangeForNode(Node*, const String& markerType, unsigned index, ExceptionCode&);
     String markerDescriptionForNode(Node*, const String& markerType, unsigned index, ExceptionCode&);
-    String dumpMarkerRectsForNode(Node*, const String& markerType, unsigned index, ExceptionCode&);
+    String dumpMarkerRects(const String& markerType, ExceptionCode&);
     void addTextMatchMarker(const Range*, bool isActive);
     void setMarkedTextMatchesAreHighlighted(bool, ExceptionCode&);
 
index be6e0d2..d133a53 100644 (file)
@@ -131,7 +131,7 @@ enum MediaControlEvent {
     [RaisesException] unsigned long markerCountForNode(Node node, DOMString markerType);
     [RaisesException] Range markerRangeForNode(Node node, DOMString markerType, unsigned long index);
     [RaisesException] DOMString markerDescriptionForNode(Node node, DOMString markerType, unsigned long index);
-    [RaisesException] DOMString dumpMarkerRectsForNode(Node node, DOMString markerType, unsigned long index);
+    [RaisesException] DOMString dumpMarkerRects(DOMString markerType);
     void addTextMatchMarker(Range range, boolean isActive);
     [RaisesException] void setMarkedTextMatchesAreHighlighted(boolean flag);
 
index c55c425..f4e81a3 100644 (file)
@@ -1,3 +1,13 @@
+2015-09-30  Timothy Horton  <timothy_horton@apple.com>
+
+        Compute document marker rects at use time instead of paint time
+        https://bugs.webkit.org/show_bug.cgi?id=149643
+
+        Reviewed by Darin Adler.
+
+        * WebView/WebHTMLView.mm:
+        (-[WebHTMLView rectsForTextMatches]):
+
 2015-09-16  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         printing does not use minimum page zoom factor
index e46b1b9..1f1c482 100644 (file)
@@ -6862,7 +6862,7 @@ static CGImageRef selectionImage(Frame* frame, bool forceBlackText)
     if (!document)
         return [NSArray array];
 
-    Vector<IntRect> rects = document->markers().renderedRectsForMarkers(DocumentMarker::TextMatch);
+    Vector<FloatRect> rects = document->markers().renderedRectsForMarkers(DocumentMarker::TextMatch);
     unsigned count = rects.size();
     NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
     for (unsigned index = 0; index < count; ++index)
index 293ea20..e3b6e0d 100644 (file)
@@ -1,3 +1,13 @@
+2015-09-30  Timothy Horton  <timothy_horton@apple.com>
+
+        Compute document marker rects at use time instead of paint time
+        https://bugs.webkit.org/show_bug.cgi?id=149643
+
+        Reviewed by Darin Adler.
+
+        * WebView.cpp:
+        (WebView::rectsForTextMatches):
+
 2015-09-25  Per Arne Vollan  <peavo@outlook.com>
 
         [WinCairo] Incorrect position for windowless plugins.
index 2bb967a..0362360 100644 (file)
@@ -3699,15 +3699,15 @@ HRESULT WebView::rectsForTextMatches(_COM_Outptr_opt_ IEnumTextMatches** pmatche
     do {
         if (Document* document = frame->document()) {
             IntRect visibleRect = frame->view()->visibleContentRect();
-            Vector<IntRect> frameRects = document->markers().renderedRectsForMarkers(DocumentMarker::TextMatch);
+            Vector<FloatRect> frameRects = document->markers().renderedRectsForMarkers(DocumentMarker::TextMatch);
             IntPoint frameOffset(-frame->view()->scrollOffset().width(), -frame->view()->scrollOffset().height());
             frameOffset = frame->view()->convertToContainingWindow(frameOffset);
 
-            Vector<IntRect>::iterator end = frameRects.end();
-            for (Vector<IntRect>::iterator it = frameRects.begin(); it < end; it++) {
+            Vector<FloatRect>::iterator end = frameRects.end();
+            for (Vector<FloatRect>::iterator it = frameRects.begin(); it < end; it++) {
                 it->intersect(visibleRect);
                 it->move(frameOffset.x(), frameOffset.y());
-                allRects.append(*it);
+                allRects.append(enclosingIntRect(*it));
             }
         }
         frame = incrementFrame(frame, true, false);
index 08dbd99..179bea7 100644 (file)
@@ -1,3 +1,35 @@
+2015-09-30  Timothy Horton  <timothy_horton@apple.com>
+
+        Compute document marker rects at use time instead of paint time
+        https://bugs.webkit.org/show_bug.cgi?id=149643
+
+        Reviewed by Darin Adler.
+
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::didInvalidateDocumentMarkerRects):
+        * WebProcess/WebCoreSupport/WebChromeClient.h:
+        Plumb the notification about invalidated document marker rects through to FindController.
+
+        * WebProcess/WebPage/FindController.cpp:
+        (WebKit::FindController::updateFindUIAfterPageScroll):
+        Make a document-relative PageOverlay, so that we get fast scrolling.
+
+        (WebKit::FindController::rectsForTextMatchesInRect):
+        Return find hole rects in main-FrameView content coordinates, to 
+        match the document-relative page overlay.
+        We'll only return rects that intersect the passed-in rect, which
+        comes from the dirty rect passed to drawRect.
+
+        (WebKit::FindController::drawRect):
+        Inflate the dirty rect by the border width so that we hit all relevant
+        markers in each tile. Otherwise, we would end up missing the border of a marker
+        that touched the edge of a tile.
+
+        (WebKit::FindController::didInvalidateDocumentMarkerRects):
+        Repaint (for now, the entire overlay) when document marker rects change.
+
+        * WebProcess/WebPage/FindController.h:
+
 2015-09-30  Carlos Alberto Lopez Perez  <clopez@igalia.com>
 
         [GTK] Build error with -DENABLE_SPELLCHECK=OFF
index 190a47c..b1e38c6 100644 (file)
@@ -1177,4 +1177,9 @@ void WebChromeClient::requestInstallMissingMediaPlugins(const String& details, c
 #endif
 #endif // ENABLE(VIDEO)
 
+void WebChromeClient::didInvalidateDocumentMarkerRects()
+{
+    m_page->findController().didInvalidateDocumentMarkerRects();
+}
+
 } // namespace WebKit
index 70e2b1e..3b3a5e1 100644 (file)
@@ -332,6 +332,8 @@ private:
 #endif
 #endif
 
+    virtual void didInvalidateDocumentMarkerRects() override;
+
     String m_cachedToolTip;
     mutable RefPtr<WebFrame> m_cachedFrameSetLargestFrame;
     mutable bool m_cachedMainFrameHasHorizontalScrollbar;
index 25c7556..303e1b7 100644 (file)
@@ -191,7 +191,7 @@ void FindController::updateFindUIAfterPageScroll(bool found, const String& strin
             m_webPage->mainFrame()->pageOverlayController().uninstallPageOverlay(m_findPageOverlay, PageOverlay::FadeMode::Fade);
     } else {
         if (!m_findPageOverlay) {
-            RefPtr<PageOverlay> findPageOverlay = PageOverlay::create(*this);
+            RefPtr<PageOverlay> findPageOverlay = PageOverlay::create(*this, PageOverlay::OverlayType::Document);
             m_findPageOverlay = findPageOverlay.get();
             m_webPage->mainFrame()->pageOverlayController().installPageOverlay(findPageOverlay.release(), PageOverlay::FadeMode::Fade);
         }
@@ -394,28 +394,26 @@ void FindController::redraw()
     updateFindIndicator(*selectedFrame, isShowingOverlay(), false);
 }
 
-Vector<IntRect> FindController::rectsForTextMatches()
+Vector<IntRect> FindController::rectsForTextMatchesInRect(IntRect clipRect)
 {
     Vector<IntRect> rects;
 
+    FrameView* mainFrameView = m_webPage->corePage()->mainFrame().view();
+
     for (Frame* frame = &m_webPage->corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
         Document* document = frame->document();
         if (!document)
             continue;
 
-        IntRect visibleRect = frame->view()->visibleContentRect();
-        Vector<IntRect> frameRects = document->markers().renderedRectsForMarkers(DocumentMarker::TextMatch);
-        IntPoint frameOffset(-frame->view()->documentScrollOffsetRelativeToViewOrigin().width(), -frame->view()->documentScrollOffsetRelativeToViewOrigin().height());
-        frameOffset = frame->view()->convertToContainingWindow(frameOffset);
-
-        for (Vector<IntRect>::iterator it = frameRects.begin(), end = frameRects.end(); it != end; ++it) {
-            it->intersect(visibleRect);
+        for (FloatRect rect : document->markers().renderedRectsForMarkers(DocumentMarker::TextMatch)) {
+            if (!frame->isMainFrame())
+                rect = mainFrameView->windowToContents(frame->view()->contentsToWindow(enclosingIntRect(rect)));
+            rect.intersect(clipRect);
 
-            if (it->isEmpty())
+            if (rect.isEmpty())
                 continue;
 
-            it->moveBy(frameOffset);
-            rects.append(*it);
+            rects.append(rect);
         }
     }
 
@@ -453,9 +451,13 @@ const float shadowColorAlpha = 0.5;
 
 void FindController::drawRect(PageOverlay&, GraphicsContext& graphicsContext, const IntRect& dirtyRect)
 {
+    const int borderWidth = 1;
+
     Color overlayBackgroundColor(0.1f, 0.1f, 0.1f, 0.25f);
 
-    Vector<IntRect> rects = rectsForTextMatches();
+    IntRect borderInflatedDirtyRect = dirtyRect;
+    borderInflatedDirtyRect.inflate(borderWidth);
+    Vector<IntRect> rects = rectsForTextMatchesInRect(borderInflatedDirtyRect);
 
     // Draw the background.
     graphicsContext.fillRect(dirtyRect, overlayBackgroundColor, ColorSpaceSRGB);
@@ -469,7 +471,7 @@ void FindController::drawRect(PageOverlay&, GraphicsContext& graphicsContext, co
         // Draw white frames around the holes.
         for (auto& rect : rects) {
             IntRect whiteFrameRect = rect;
-            whiteFrameRect.inflate(1);
+            whiteFrameRect.inflate(borderWidth);
             graphicsContext.fillRect(whiteFrameRect);
         }
     }
@@ -497,4 +499,10 @@ bool FindController::mouseEvent(PageOverlay&, const PlatformMouseEvent& mouseEve
     return false;
 }
 
+void FindController::didInvalidateDocumentMarkerRects()
+{
+    if (m_findPageOverlay)
+        m_findPageOverlay->setNeedsDisplay();
+}
+
 } // namespace WebKit
index bbd324e..924af53 100644 (file)
@@ -67,6 +67,7 @@ public:
     bool isShowingOverlay() const { return m_isShowingFindIndicator && m_findPageOverlay; }
 
     void deviceScaleFactorDidChange();
+    void didInvalidateDocumentMarkerRects();
 
     void redraw();
 
@@ -78,7 +79,7 @@ private:
     virtual bool mouseEvent(WebCore::PageOverlay&, const WebCore::PlatformMouseEvent&);
     virtual void drawRect(WebCore::PageOverlay&, WebCore::GraphicsContext&, const WebCore::IntRect& dirtyRect);
 
-    Vector<WebCore::IntRect> rectsForTextMatches();
+    Vector<WebCore::IntRect> rectsForTextMatchesInRect(WebCore::IntRect clipRect);
     bool updateFindIndicator(WebCore::Frame& selectedFrame, bool isShowingOverlay, bool shouldAnimate = true);
 
     void updateFindUIAfterPageScroll(bool found, const String&, FindOptions, unsigned maxMatchCount);