Changeset 253857 in webkit


Ignore:
Timestamp:
Dec 20, 2019 5:33:10 PM (4 years ago)
Author:
Megan Gardner
Message:

Paint highlights specified in CSS Highlight API
https://bugs.webkit.org/show_bug.cgi?id=205318

Reviewed by Ryosuke Niwa.

LayoutTests/imported/w3c:

  • web-platform-tests/css/css-highlight-api/highlight-text-across-elements-expected.html: Added.
  • web-platform-tests/css/css-highlight-api/highlight-text-across-elements.html: Added.
  • web-platform-tests/css/css-highlight-api/highlight-text-expected.html: Added.
  • web-platform-tests/css/css-highlight-api/highlight-text.html: Added.

Source/WebCore:

Render highlights when present, similar to the way we render selection.

Tests: imported/w3c/web-platform-tests/css/css-highlight-api/highlight-text-across-elements.html

imported/w3c/web-platform-tests/css/css-highlight-api/highlight-text.html

  • Modules/highlight/HighlightMap.h:

(WebCore::HighlightMap::map const):

Add a getter for the internal HashMap.

  • rendering/InlineTextBox.cpp:

(WebCore::InlineTextBox::selectionState):
(WebCore::InlineTextBox::verifySelectionState const):
(WebCore::InlineTextBox::paint):
(WebCore::InlineTextBox::clampedStartEndForState const):
(WebCore::InlineTextBox::selectionStartEnd const):
(WebCore::InlineTextBox::highlightStartEnd const):
(WebCore::InlineTextBox::resolveStyleForMarkedText):

Use the highlight name from the HighlightRangeGroup to obtain the style from the renderer.

(WebCore::InlineTextBox::collectMarkedTextsForHighlights const):

Render the highlights when painting text. Determine if a highlight is present in the current RenderObject, and
add additional MarkedText to be rendered when painting

  • rendering/InlineTextBox.h:
  • rendering/MarkedText.cpp:

(WebCore::subdivide):

  • rendering/MarkedText.h:

(WebCore::MarkedText::operator== const):

Expand MarkedText to take a style name.

  • rendering/SelectionRangeData.cpp:

(WebCore::SelectionRangeData::setContext):
(WebCore::SelectionRangeData::selectionStateForRenderer):
(WebCore::SelectionRangeData::set):

  • rendering/SelectionRangeData.h:

Leverage SelectionRangeData for highlights.

Tools:

Expand MarkedText to take a style name.

  • TestWebKitAPI/Tests/WebCore/MarkedText.cpp:

(WebCore::operator<<):

Location:
trunk
Files:
13 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r253855 r253857  
     12019-12-20  Megan Gardner  <megan_gardner@apple.com>
     2
     3        Paint highlights specified in CSS Highlight API
     4        https://bugs.webkit.org/show_bug.cgi?id=205318
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        * web-platform-tests/css/css-highlight-api/highlight-text-across-elements-expected.html: Added.
     9        * web-platform-tests/css/css-highlight-api/highlight-text-across-elements.html: Added.
     10        * web-platform-tests/css/css-highlight-api/highlight-text-expected.html: Added.
     11        * web-platform-tests/css/css-highlight-api/highlight-text.html: Added.
     12
    1132019-12-20  Chris Dumez  <cdumez@apple.com>
    214
  • trunk/Source/WebCore/ChangeLog

    r253856 r253857  
     12019-12-20  Megan Gardner  <megan_gardner@apple.com>
     2
     3        Paint highlights specified in CSS Highlight API
     4        https://bugs.webkit.org/show_bug.cgi?id=205318
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        Render highlights when present, similar to the way we render selection.
     9
     10        Tests: imported/w3c/web-platform-tests/css/css-highlight-api/highlight-text-across-elements.html
     11               imported/w3c/web-platform-tests/css/css-highlight-api/highlight-text.html
     12
     13        * Modules/highlight/HighlightMap.h:
     14        (WebCore::HighlightMap::map const):
     15
     16        Add a getter for the internal HashMap.
     17
     18        * rendering/InlineTextBox.cpp:
     19        (WebCore::InlineTextBox::selectionState):
     20        (WebCore::InlineTextBox::verifySelectionState const):
     21        (WebCore::InlineTextBox::paint):
     22        (WebCore::InlineTextBox::clampedStartEndForState const):
     23        (WebCore::InlineTextBox::selectionStartEnd const):
     24        (WebCore::InlineTextBox::highlightStartEnd const):
     25        (WebCore::InlineTextBox::resolveStyleForMarkedText):
     26
     27        Use the highlight name from the HighlightRangeGroup to obtain the style from the renderer.
     28
     29        (WebCore::InlineTextBox::collectMarkedTextsForHighlights const):
     30
     31        Render the highlights when painting text. Determine if a highlight is present in the current RenderObject, and
     32        add additional MarkedText to be rendered when painting
     33
     34        * rendering/InlineTextBox.h:
     35        * rendering/MarkedText.cpp:
     36        (WebCore::subdivide):
     37        * rendering/MarkedText.h:
     38        (WebCore::MarkedText::operator== const):
     39
     40        Expand MarkedText to take a style name.
     41
     42        * rendering/SelectionRangeData.cpp:
     43        (WebCore::SelectionRangeData::setContext):
     44        (WebCore::SelectionRangeData::selectionStateForRenderer):
     45        (WebCore::SelectionRangeData::set):
     46        * rendering/SelectionRangeData.h:
     47
     48        Leverage SelectionRangeData for highlights.
     49
    1502019-12-20  Chris Dumez  <cdumez@apple.com>
    251
  • trunk/Source/WebCore/Modules/highlight/HighlightMap.h

    r253309 r253857  
    4646   
    4747    RefPtr<HighlightRangeGroup> getGroupForKey(const String& key);
     48    const HashMap<String, Ref<HighlightRangeGroup>>& map() const { return m_map; }
    4849   
    4950private:
  • trunk/Source/WebCore/dom/Document.cpp

    r253653 r253857  
    780780
    781781    clearScriptedAnimationController();
     782   
     783    if (m_highlightMap)
     784        m_highlightMap->clear();
    782785
    783786    m_pendingScrollEventTargetList = nullptr;
  • trunk/Source/WebCore/rendering/InlineTextBox.cpp

    r250341 r253857  
    2929#include "DocumentMarkerController.h"
    3030#include "Editor.h"
     31#include "ElementRuleCollector.h"
    3132#include "EllipsisBox.h"
    3233#include "EventRegion.h"
    3334#include "Frame.h"
    3435#include "GraphicsContext.h"
     36#include "HighlightMap.h"
    3537#include "HitTestResult.h"
    3638#include "ImageBuffer.h"
     
    4749#include "RenderView.h"
    4850#include "RenderedDocumentMarker.h"
     51#include "RuntimeEnabledFeatures.h"
     52#include "SelectionRangeData.h"
    4953#include "Text.h"
    5054#include "TextDecorationPainter.h"
     
    153157RenderObject::SelectionState InlineTextBox::selectionState()
    154158{
    155     RenderObject::SelectionState state = renderer().selectionState();
     159    auto state = verifySelectionState(renderer().selectionState(), renderer().view().selection());
     160   
     161    // FIXME: this code mutates selection state, but it's used at a simple getter elsewhere
     162    // in this file. This code should likely live in SelectionRangeData, or somewhere else.
     163    // <rdar://problem/58125978>
     164    // https://bugs.webkit.org/show_bug.cgi?id=205528
     165    // If there are ellipsis following, make sure their selection is updated.
     166    if (m_truncation != cNoTruncation && root().ellipsisBox()) {
     167        EllipsisBox* ellipsis = root().ellipsisBox();
     168        if (state != RenderObject::SelectionNone) {
     169            auto [selectionStart, selectionEnd] = selectionStartEnd();
     170            // The ellipsis should be considered to be selected if the end of
     171            // the selection is past the beginning of the truncation and the
     172            // beginning of the selection is before or at the beginning of the
     173            // truncation.
     174            ellipsis->setSelectionState(selectionEnd >= m_truncation && selectionStart <= m_truncation ?
     175                RenderObject::SelectionInside : RenderObject::SelectionNone);
     176        } else
     177            ellipsis->setSelectionState(RenderObject::SelectionNone);
     178    }
     179   
     180    return state;
     181}
     182
     183RenderObject::SelectionState InlineTextBox::verifySelectionState(RenderObject::SelectionState state, SelectionRangeData& selection) const
     184{
    156185    if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
    157         auto& selection = renderer().view().selection();
    158186        auto startPos = selection.startPosition();
    159187        auto endPos = selection.endPosition();
     
    175203        else if (state == RenderObject::SelectionBoth)
    176204            state = RenderObject::SelectionNone;
    177     }
    178 
    179     // If there are ellipsis following, make sure their selection is updated.
    180     if (m_truncation != cNoTruncation && root().ellipsisBox()) {
    181         EllipsisBox* ellipsis = root().ellipsisBox();
    182         if (state != RenderObject::SelectionNone) {
    183             auto [selectionStart, selectionEnd] = selectionStartEnd();
    184             // The ellipsis should be considered to be selected if the end of
    185             // the selection is past the beginning of the truncation and the
    186             // beginning of the selection is before or at the beginning of the
    187             // truncation.
    188             ellipsis->setSelectionState(selectionEnd >= m_truncation && selectionStart <= m_truncation ?
    189                 RenderObject::SelectionInside : RenderObject::SelectionNone);
    190         } else
    191             ellipsis->setSelectionState(RenderObject::SelectionNone);
    192205    }
    193206
     
    525538
    526539        Vector<MarkedText> markedTexts = collectMarkedTextsForDocumentMarkers(TextPaintPhase::Background);
     540        auto highlightMarkedTexts = collectMarkedTextsForHighlights(TextPaintPhase::Background);
     541        if (!highlightMarkedTexts.isEmpty())
     542            markedTexts.appendVector(WTFMove(highlightMarkedTexts));
    527543#if ENABLE(TEXT_SELECTION)
    528544        if (haveSelection && !useCustomUnderlines && !context.paintingDisabled()) {
     
    558574        if (!isPrinting) {
    559575            markedTexts.appendVector(collectMarkedTextsForDocumentMarkers(TextPaintPhase::Foreground));
     576            auto highlightMarkedTexts = collectMarkedTextsForHighlights(TextPaintPhase::Foreground);
     577            if (!highlightMarkedTexts.isEmpty())
     578                markedTexts.appendVector(WTFMove(highlightMarkedTexts));
    560579
    561580            bool shouldPaintDraggedContent = !(paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection));
     
    653672}
    654673
    655 std::pair<unsigned, unsigned> InlineTextBox::selectionStartEnd() const
    656 {
    657     auto selectionState = renderer().selectionState();
     674std::pair<unsigned, unsigned> InlineTextBox::clampedStartEndForState(unsigned start, unsigned end, RenderObject::SelectionState selectionState) const
     675{
    658676    if (selectionState == RenderObject::SelectionInside)
    659677        return { 0, clampedOffset(m_start + m_len) };
    660678   
    661     auto start = renderer().view().selection().startPosition();
    662     auto end = renderer().view().selection().endPosition();
    663679    if (selectionState == RenderObject::SelectionStart)
    664680        end = renderer().text().length();
     
    666682        start = 0;
    667683    return { clampedOffset(start), clampedOffset(end) };
     684}
     685
     686std::pair<unsigned, unsigned> InlineTextBox::selectionStartEnd() const
     687{
     688    auto selectionState = renderer().selectionState();
     689   
     690    return clampedStartEndForState(renderer().view().selection().startPosition(), renderer().view().selection().endPosition(), selectionState);
     691}
     692
     693std::pair<unsigned, unsigned> InlineTextBox::highlightStartEnd(SelectionRangeData &rangeData) const
     694{
     695    auto state = rangeData.selectionStateForRenderer(renderer());
     696    state = verifySelectionState(state, rangeData);
     697   
     698    if (state == RenderObject::SelectionNone)
     699        return {0, 0};
     700   
     701    return clampedStartEndForState(rangeData.startPosition(), rangeData.endPosition(), state);
    668702}
    669703
     
    770804    case MarkedText::SpellingError:
    771805    case MarkedText::Unmarked:
     806        break;
     807    case MarkedText::Highlight:
     808        if (auto renderStyle = parent()->renderer().getUncachedPseudoStyle({ PseudoId::Highlight, markedText.highlightName }, &parent()->renderer().style())) {
     809            style.backgroundColor = renderStyle->backgroundColor();
     810            style.textStyles.fillColor = renderStyle->computedStrokeColor();
     811            style.textStyles.strokeColor = renderStyle->computedStrokeColor();
     812           
     813            auto color = renderStyle->visitedDependentColorWithColorFilter(CSSPropertyWebkitTextFillColor);
     814            auto decorationStyle = renderStyle->textDecorationStyle();
     815            auto decorations = renderStyle->textDecorationsInEffect();
     816
     817            if (decorations.containsAny({ TextDecoration::Underline, TextDecoration::Overline, TextDecoration::LineThrough })) {
     818                style.textDecorationStyles.underlineColor = color;
     819                style.textDecorationStyles.underlineStyle = decorationStyle;
     820            }
     821        }
    772822        break;
    773823    case MarkedText::DraggedContent:
     
    9641014}
    9651015
     1016
     1017Vector<MarkedText> InlineTextBox::collectMarkedTextsForHighlights(TextPaintPhase phase) const
     1018{
     1019    if (!RuntimeEnabledFeatures::sharedFeatures().highlightAPIEnabled())
     1020        return { };
     1021    ASSERT_ARG(phase, phase == TextPaintPhase::Background || phase == TextPaintPhase::Foreground || phase == TextPaintPhase::Decoration);
     1022    UNUSED_PARAM(phase);
     1023    if (!renderer().textNode())
     1024        return { };
     1025
     1026    Vector<MarkedText> markedTexts;
     1027    auto& parentRenderer = parent()->renderer();
     1028    auto& parentStyle = parentRenderer.style();
     1029    for (auto& [highlightKey, highlightGroup] : renderer().document().highlightMap().map()) {
     1030        auto renderStyle = parentRenderer.getUncachedPseudoStyle({ PseudoId::Highlight, highlightKey }, &parentStyle);
     1031        if (!renderStyle)
     1032            continue;
     1033        for (auto& staticRange : highlightGroup->ranges()) {
     1034            Position startPos = createLegacyEditingPosition(staticRange->startContainer(), staticRange->startOffset());
     1035            Position endPos = createLegacyEditingPosition(staticRange->endContainer(), staticRange->endOffset());
     1036
     1037            if (startPos.isNotNull() && endPos.isNotNull()) {
     1038                RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
     1039                int startOffset = startPos.deprecatedEditingOffset();
     1040                RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
     1041                int endOffset = endPos.deprecatedEditingOffset();
     1042                ASSERT(startOffset >= 0 && endOffset >= 0);
     1043                if (!startRenderer || !endRenderer)
     1044                    continue;
     1045                auto highlightData = SelectionRangeData(renderer().view());
     1046                highlightData.setContext({startRenderer, endRenderer, static_cast<unsigned>(startOffset), static_cast<unsigned>(endOffset)});
     1047                auto [highlightStart, highlightEnd] = highlightStartEnd(highlightData);
     1048                if (highlightStart < highlightEnd)
     1049                    markedTexts.append({ highlightStart, highlightEnd, MarkedText::Highlight, nullptr, highlightKey });
     1050            }
     1051        }
     1052    }
     1053    return markedTexts;
     1054}
     1055
    9661056FloatPoint InlineTextBox::textOriginFromBoxRect(const FloatRect& boxRect) const
    9671057{
  • trunk/Source/WebCore/rendering/InlineTextBox.h

    r249160 r253857  
    122122    bool isSelected(unsigned startPosition, unsigned endPosition) const;
    123123    std::pair<unsigned, unsigned> selectionStartEnd() const;
     124    std::pair<unsigned, unsigned> highlightStartEnd(SelectionRangeData&) const;
    124125
    125126protected:
     
    133134    void extractLine() final;
    134135    void attachLine() final;
     136   
     137    RenderObject::SelectionState verifySelectionState(RenderObject::SelectionState, SelectionRangeData&) const;
     138    std::pair<unsigned, unsigned> clampedStartEndForState(unsigned, unsigned, RenderObject::SelectionState) const;
    135139
    136140public:
     
    170174    Vector<MarkedText> collectMarkedTextsForDraggedContent();
    171175    Vector<MarkedText> collectMarkedTextsForDocumentMarkers(TextPaintPhase) const;
     176    Vector<MarkedText> collectMarkedTextsForHighlights(TextPaintPhase) const;
    172177
    173178    MarkedTextStyle computeStyleForUnmarkedMarkedText(const PaintInfo&) const;
  • trunk/Source/WebCore/rendering/MarkedText.cpp

    r239427 r253857  
    7575                }
    7676                if (frontmost)
    77                     result.append({ offsetSoFar, offsets[i].value, offsets[*frontmost].markedText->type, offsets[*frontmost].markedText->marker });
     77                    result.append({ offsetSoFar, offsets[i].value, offsets[*frontmost].markedText->type, offsets[*frontmost].markedText->marker, offsets[*frontmost].markedText->highlightName });
    7878            } else {
    7979                // The appended marked texts may not be in paint order. We will fix this up at the end of this function.
    8080                for (unsigned j = 0; j < i; ++j) {
    8181                    if (!processedMarkedTexts.contains(offsets[j].markedText))
    82                         result.append({ offsetSoFar, offsets[i].value, offsets[j].markedText->type, offsets[j].markedText->marker });
     82                        result.append({ offsetSoFar, offsets[i].value, offsets[j].markedText->type, offsets[j].markedText->marker, offsets[j].markedText->highlightName });
    8383                }
    8484            }
  • trunk/Source/WebCore/rendering/MarkedText.h

    r237266 r253857  
    2727
    2828#include <wtf/Vector.h>
     29#include <wtf/text/WTFString.h>
    2930
    3031namespace WebCore {
     
    4142        TextMatch,
    4243        DictationAlternatives,
     44        Highlight,
    4345#if PLATFORM(IOS_FAMILY)
    4446        // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS_FAMILY)-guard.
     
    5254    Type type;
    5355    const RenderedDocumentMarker* marker { nullptr };
     56    String highlightName { };
    5457
    5558    bool isEmpty() const { return endOffset <= startOffset; }
     
    5760    bool operator==(const MarkedText& other) const
    5861    {
    59         return startOffset == other.startOffset && endOffset == other.endOffset && type == other.type && marker == other.marker;
     62        return startOffset == other.startOffset && endOffset == other.endOffset && type == other.type && marker == other.marker && highlightName == other.highlightName;
    6063    }
    6164};
  • trunk/Source/WebCore/rendering/SelectionRangeData.cpp

    r248846 r253857  
    155155}
    156156
     157void SelectionRangeData::setContext(const Context& context)
     158{
     159    ASSERT(context.start() && context.end());
     160    m_selectionContext = context;
     161}
     162
     163RenderObject::SelectionState SelectionRangeData::selectionStateForRenderer(RenderObject& renderer)
     164{
     165    // FIXME: we shouldln't have to check that a renderer is a descendant of the render node
     166    // from the range. This is likely because we aren't using VisiblePositions yet.
     167    // Planned fix in a followup: <rdar://problem/58095923>
     168    // https://bugs.webkit.org/show_bug.cgi?id=205529
     169   
     170    if (&renderer == m_selectionContext.start() || renderer.isDescendantOf(m_selectionContext.start())) {
     171        if (m_selectionContext.start() && m_selectionContext.end() && m_selectionContext.start() == m_selectionContext.end())
     172            return RenderObject::SelectionBoth;
     173        if (m_selectionContext.start())
     174            return RenderObject::SelectionStart;
     175    }
     176    if (&renderer == m_selectionContext.end() || renderer.isDescendantOf(m_selectionContext.end()))
     177        return RenderObject::SelectionEnd;
     178
     179    RenderObject* selectionEnd = nullptr;
     180    auto* selectionDataEnd = m_selectionContext.end();
     181    if (selectionDataEnd)
     182        selectionEnd = rendererAfterPosition(*selectionDataEnd, m_selectionContext.endPosition().value());
     183    SelectionIterator selectionIterator(m_selectionContext.start());
     184    for (auto* currentRenderer = m_selectionContext.start(); currentRenderer && currentRenderer != m_selectionContext.end(); currentRenderer = selectionIterator.next()) {
     185        if (currentRenderer == m_selectionContext.start() || currentRenderer == m_selectionContext.end())
     186            continue;
     187        if (!currentRenderer->canBeSelectionLeaf())
     188            continue;
     189        if (&renderer == currentRenderer)
     190            return RenderObject::SelectionInside;
     191    }
     192    return RenderObject::SelectionNone;
     193   
     194}
     195
    157196void SelectionRangeData::set(const Context& selection, RepaintMode blockRepaintMode)
    158197{
    159     // Make sure both our start and end objects are defined.
    160     // Check www.msnbc.com and try clicking around to find the case where this happened.
    161198    if ((selection.start() && !selection.end()) || (selection.end() && !selection.start()))
    162199        return;
  • trunk/Source/WebCore/rendering/SelectionRangeData.h

    r243844 r253857  
    7272        Optional<unsigned> m_endPosition;
    7373    };
    74 
     74   
     75    void setContext(const Context&);
     76   
    7577    enum class RepaintMode { NewXOROld, NewMinusOld, Nothing };
    7678    void set(const Context&, RepaintMode = RepaintMode::NewXOROld);
     
    8789    IntRect boundsClippedToVisibleContent() const { return collectBounds(ClipToVisibleContent::Yes); }
    8890    void repaint() const;
     91   
     92    RenderObject::SelectionState selectionStateForRenderer(RenderObject&);
    8993
    9094private:
  • trunk/Tools/ChangeLog

    r253845 r253857  
     12019-12-20  Megan Gardner  <megan_gardner@apple.com>
     2
     3        Paint highlights specified in CSS Highlight API
     4        https://bugs.webkit.org/show_bug.cgi?id=205318
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        Expand MarkedText to take a style name.
     9
     10        * TestWebKitAPI/Tests/WebCore/MarkedText.cpp:
     11        (WebCore::operator<<):
     12
    1132019-12-20  Jonathan Bedard  <jbedard@apple.com>
    214
  • trunk/Tools/TestWebKitAPI/Tests/WebCore/MarkedText.cpp

    r237266 r253857  
    5656    case MarkedText::TextMatch:
    5757        return os << "TextMatch";
     58    case MarkedText::Highlight:
     59        return os << "Highlight";
    5860    case MarkedText::Unmarked:
    5961        return os << "Unmarked";
Note: See TracChangeset for help on using the changeset viewer.