⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 278331 in webkit


Ignore:
Timestamp:
Jun 1, 2021, 3:36:56 PM (5 years ago)
Author:
Wenson Hsieh
Message:

[macOS] Data detector highlights should appear when hovering inside image overlays
https://bugs.webkit.org/show_bug.cgi?id=226507

Reviewed by Tim Horton.

Refactor some logic in ImageOverlayController, such that the overlay is installed if _either_ selection painting
is required, or data detector highlights for the image overlay host element exist; additionally, add plumbing to
allow the ImageOverlayController to install a page overlay when the element under the mouse is inside an image
overlay.

  • dom/Document.cpp:

(WebCore::Document::willBeRemovedFromFrame):

Drive-by fix: use imageOverlayControllerIfExists instead of imageOverlayController to avoid unnecessarily
creating a new ImageOverlayController when detaching a Document.

  • page/EventHandler.cpp:

(WebCore::EventHandler::clear):

Refactor logic for clearing out m_elementUnderMouse into a separate helper, and call it from these two places.
This new helper clears out m_elementUnderMouse and additionally notifies the page's image overlay controller
(only if it has already been constructed).

(WebCore::EventHandler::updateMouseEventTargetNode):

Call out to the page's image overlay controller when changing m_elementUnderMouse.

(WebCore::EventHandler::clearElementUnderMouse):

  • page/EventHandler.h:
  • page/ImageOverlayController.cpp:

(WebCore::ImageOverlayController::selectionQuadsDidChange):

Refactor ImageOverlayController so that it only installs its page overlay if either:

  1. Selection painting is needed for selected text inside an image overlay, or...
  2. The cursor is over an image overlay host element with data detection results.

To achieve this, we maintain a weak pointer to the image overlay host that contains the selection separately
from the weak pointer to the image overlay host that contains data detectors, and is the element currently under
the mouse cursor.

Drive-by fix: also ignore selection updates due to temporary selections triggered as a result of gathering
dictionary popup info.

(WebCore::ImageOverlayController::documentDetached):
(WebCore::ImageOverlayController::uninstallPageOverlay):
(WebCore::ImageOverlayController::uninstallPageOverlayIfNeeded):

Split this into two functions: uninstallPageOverlayIfNeeded, which removes and destroys the PageOverlay if
it is no longer needed (i.e. the overlay is required for neither selection painting nor data detectors).

(WebCore::ImageOverlayController::willMoveToPage):
(WebCore::ImageOverlayController::drawRect):
(WebCore::ImageOverlayController::elementUnderMouseDidChange):

  • page/ImageOverlayController.h:

Rename m_imageOverlayBounds to m_selectionClipRect to make it clear that this is only used during selection
painting, and rename m_overlaySelectionQuads to just m_selectionQuads for conciseness.

  • page/Page.h:

(WebCore::Page::imageOverlayControllerIfExists):

Add a version of this getter that does not initialize the ImageOverlayController if it didn't already exist. See
call sites in EventHandler and Document.

  • page/mac/ImageOverlayControllerMac.mm:

(WebCore::ImageOverlayController::clearDataDetectorHighlights):

Additionally clear out m_hostElementForDataDetectors.

(WebCore::ImageOverlayController::elementUnderMouseDidChange):

Update data detector highlights whenever the element under the mouse is over content inside an image overlay.
Note that we effectively ignore this method call in the case where we're clearing out m_elementUnderMouse for
a different Document than the one containing m_hostElementForDataDetectors, which prevents us from erroneously
hiding data detectors when m_elementUnderMouse is removed or otherwise cleared out in a different Document.

Location:
trunk/Source/WebCore
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r278329 r278331  
     12021-06-01  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [macOS] Data detector highlights should appear when hovering inside image overlays
     4        https://bugs.webkit.org/show_bug.cgi?id=226507
     5
     6        Reviewed by Tim Horton.
     7
     8        Refactor some logic in ImageOverlayController, such that the overlay is installed if _either_ selection painting
     9        is required, or data detector highlights for the image overlay host element exist; additionally, add plumbing to
     10        allow the ImageOverlayController to install a page overlay when the element under the mouse is inside an image
     11        overlay.
     12
     13        * dom/Document.cpp:
     14        (WebCore::Document::willBeRemovedFromFrame):
     15
     16        Drive-by fix: use `imageOverlayControllerIfExists` instead of `imageOverlayController` to avoid unnecessarily
     17        creating a new ImageOverlayController when detaching a Document.
     18
     19        * page/EventHandler.cpp:
     20        (WebCore::EventHandler::clear):
     21
     22        Refactor logic for clearing out `m_elementUnderMouse` into a separate helper, and call it from these two places.
     23        This new helper clears out `m_elementUnderMouse` and additionally notifies the page's image overlay controller
     24        (only if it has already been constructed).
     25
     26        (WebCore::EventHandler::updateMouseEventTargetNode):
     27
     28        Call out to the page's image overlay controller when changing `m_elementUnderMouse`.
     29
     30        (WebCore::EventHandler::clearElementUnderMouse):
     31        * page/EventHandler.h:
     32        * page/ImageOverlayController.cpp:
     33        (WebCore::ImageOverlayController::selectionQuadsDidChange):
     34
     35        Refactor ImageOverlayController so that it only installs its page overlay if either:
     36        1. Selection painting is needed for selected text inside an image overlay, or...
     37        2. The cursor is over an image overlay host element with data detection results.
     38
     39        To achieve this, we maintain a weak pointer to the image overlay host that contains the selection separately
     40        from the weak pointer to the image overlay host that contains data detectors, and is the element currently under
     41        the mouse cursor.
     42
     43        Drive-by fix: also ignore selection updates due to temporary selections triggered as a result of gathering
     44        dictionary popup info.
     45
     46        (WebCore::ImageOverlayController::documentDetached):
     47        (WebCore::ImageOverlayController::uninstallPageOverlay):
     48        (WebCore::ImageOverlayController::uninstallPageOverlayIfNeeded):
     49
     50        Split this into two functions: `uninstallPageOverlayIfNeeded`, which removes and destroys the PageOverlay if
     51        it is no longer needed (i.e. the overlay is required for neither selection painting nor data detectors).
     52
     53        (WebCore::ImageOverlayController::willMoveToPage):
     54        (WebCore::ImageOverlayController::drawRect):
     55        (WebCore::ImageOverlayController::elementUnderMouseDidChange):
     56        * page/ImageOverlayController.h:
     57
     58        Rename `m_imageOverlayBounds` to `m_selectionClipRect` to make it clear that this is only used during selection
     59        painting, and rename `m_overlaySelectionQuads` to just `m_selectionQuads` for conciseness.
     60
     61        * page/Page.h:
     62        (WebCore::Page::imageOverlayControllerIfExists):
     63
     64        Add a version of this getter that does not initialize the ImageOverlayController if it didn't already exist. See
     65        call sites in EventHandler and Document.
     66
     67        * page/mac/ImageOverlayControllerMac.mm:
     68        (WebCore::ImageOverlayController::clearDataDetectorHighlights):
     69
     70        Additionally clear out `m_hostElementForDataDetectors`.
     71
     72        (WebCore::ImageOverlayController::elementUnderMouseDidChange):
     73
     74        Update data detector highlights whenever the element under the mouse is over content inside an image overlay.
     75        Note that we effectively ignore this method call in the case where we're clearing out `m_elementUnderMouse` for
     76        a different Document than the one containing `m_hostElementForDataDetectors`, which prevents us from erroneously
     77        hiding data detectors when `m_elementUnderMouse` is removed or otherwise cleared out in a different Document.
     78
    1792021-06-01  Chris Dumez  <cdumez@apple.com>
    280
  • trunk/Source/WebCore/dom/Document.cpp

    r278253 r278331  
    26222622        page->pointerLockController().documentDetached(*this);
    26232623#endif
    2624         page->imageOverlayController().documentDetached(*this);
     2624        if (auto* imageOverlayController = page->imageOverlayControllerIfExists())
     2625            imageOverlayController->documentDetached(*this);
    26252626        if (auto* validationMessageClient = page->validationMessageClient())
    26262627            validationMessageClient->documentDetached(*this);
  • trunk/Source/WebCore/page/EventHandler.cpp

    r278253 r278331  
    6666#include "HitTestResult.h"
    6767#include "Image.h"
     68#include "ImageOverlayController.h"
    6869#include "InspectorInstrumentation.h"
    6970#include "KeyboardEvent.h"
     
    371372#endif
    372373    m_resizeLayer = nullptr;
    373     m_elementUnderMouse = nullptr;
     374    clearElementUnderMouse();
    374375    m_lastElementUnderMouse = nullptr;
    375376    m_lastMouseMoveEventSubframe = nullptr;
     
    25482549#endif
    25492550
     2551    if (auto* page = m_frame.page())
     2552        page->imageOverlayController().elementUnderMouseDidChange(m_frame, m_elementUnderMouse.get());
     2553
    25502554    ASSERT_IMPLIES(m_elementUnderMouse, &m_elementUnderMouse->document() == m_frame.document());
    25512555    ASSERT_IMPLIES(m_lastElementUnderMouse, &m_lastElementUnderMouse->document() == m_frame.document());
     
    26072611            m_imageExtractionTimer.stop();
    26082612#endif
    2609             m_elementUnderMouse = nullptr;
     2613            clearElementUnderMouse();
    26102614        }
    26112615
    26122616        m_lastElementUnderMouse = m_elementUnderMouse;
    26132617    }
     2618}
     2619
     2620void EventHandler::clearElementUnderMouse()
     2621{
     2622    if (!m_elementUnderMouse)
     2623        return;
     2624
     2625    m_elementUnderMouse = nullptr;
     2626
     2627    auto* page = m_frame.page();
     2628    if (!page)
     2629        return;
     2630
     2631    auto* imageOverlayController = page->imageOverlayControllerIfExists();
     2632    if (!imageOverlayController)
     2633        return;
     2634
     2635    imageOverlayController->elementUnderMouseDidChange(m_frame, nullptr);
    26142636}
    26152637
  • trunk/Source/WebCore/page/EventHandler.h

    r278253 r278331  
    520520
    521521    void clearLatchedState();
     522    void clearElementUnderMouse();
    522523
    523524    bool shouldSendMouseEventsToInactiveWindows() const;
  • trunk/Source/WebCore/page/ImageOverlayController.cpp

    r278121 r278331  
    5959        return;
    6060
    61     if (frame.editor().ignoreSelectionChanges())
    62         return;
     61    if (frame.editor().ignoreSelectionChanges() || frame.editor().isGettingDictionaryPopupInfo())
     62        return;
     63
     64    m_hostElementForSelection = nullptr;
     65    m_selectionQuads.clear();
     66    m_selectionBackgroundColor = Color::transparentBlack;
     67    m_selectionClipRect = { };
    6368
    6469    auto overlayHost = ([&] () -> RefPtr<HTMLElement> {
     
    8792    }
    8893
    89     if (shouldUsePageOverlayToPaintSelection(*overlayHostRenderer)) {
    90         m_overlaySelectionQuads = quads;
    91         m_selectionBackgroundColor = overlayHostRenderer->selectionBackgroundColor();
    92     } else {
    93         m_overlaySelectionQuads.clear();
    94         m_selectionBackgroundColor = Color::transparentBlack;
    95     }
    96 
    97     m_imageOverlayBounds = overlayHostRenderer->absoluteBoundingBoxRect();
    98 
    99 #if PLATFORM(MAC)
    100     updateDataDetectorHighlights(*overlayHost);
    101 #endif
    102 
    103     if (auto& overlay = installPageOverlayIfNeeded(); !m_overlaySelectionQuads.isEmpty())
    104         overlay.setNeedsDisplay();
     94    if (!shouldUsePageOverlayToPaintSelection(*overlayHostRenderer)) {
     95        uninstallPageOverlayIfNeeded();
     96        return;
     97    }
     98
     99    m_hostElementForSelection = makeWeakPtr(*overlayHost);
     100    m_selectionQuads = quads;
     101    m_selectionBackgroundColor = overlayHostRenderer->selectionBackgroundColor();
     102    m_selectionClipRect = overlayHostRenderer->absoluteBoundingBoxRect();
     103
     104    installPageOverlayIfNeeded().setNeedsDisplay();
    105105}
    106106
     
    114114void ImageOverlayController::documentDetached(const Document& document)
    115115{
    116     if (&document == m_currentOverlayDocument)
    117         uninstallPageOverlayIfNeeded();
     116    if (m_hostElementForSelection && &document == &m_hostElementForSelection->document())
     117        m_hostElementForSelection = nullptr;
     118
     119#if PLATFORM(MAC)
     120    if (m_hostElementForDataDetectors && &document == &m_hostElementForDataDetectors->document())
     121        m_hostElementForDataDetectors = nullptr;
     122#endif
     123
     124    uninstallPageOverlayIfNeeded();
    118125}
    119126
     
    128135}
    129136
    130 void ImageOverlayController::uninstallPageOverlayIfNeeded()
    131 {
    132     m_imageOverlayBounds = { };
    133     m_overlaySelectionQuads.clear();
     137void ImageOverlayController::uninstallPageOverlay()
     138{
     139    m_hostElementForSelection = nullptr;
     140    m_selectionQuads.clear();
    134141    m_selectionBackgroundColor = Color::transparentBlack;
    135     m_currentOverlayDocument = nullptr;
     142    m_selectionClipRect = { };
    136143
    137144#if PLATFORM(MAC)
     
    146153}
    147154
     155void ImageOverlayController::uninstallPageOverlayIfNeeded()
     156{
     157    if (m_hostElementForSelection)
     158        return;
     159
     160#if PLATFORM(MAC)
     161    if (m_hostElementForDataDetectors)
     162        return;
     163#endif
     164
     165    uninstallPageOverlay();
     166}
     167
    148168void ImageOverlayController::willMoveToPage(PageOverlay&, Page* page)
    149169{
    150170    if (!page)
    151         uninstallPageOverlayIfNeeded();
     171        uninstallPageOverlay();
    152172}
    153173
     
    162182    context.clearRect(dirtyRect);
    163183
    164     if (m_overlaySelectionQuads.isEmpty())
     184    if (m_selectionQuads.isEmpty())
    165185        return;
    166186
    167187    Path coalescedSelectionPath;
    168     for (auto& quad : m_overlaySelectionQuads) {
     188    for (auto& quad : m_selectionQuads) {
    169189        coalescedSelectionPath.moveTo(quad.p1());
    170190        coalescedSelectionPath.addLineTo(quad.p2());
     
    176196
    177197    context.setFillColor(m_selectionBackgroundColor);
    178     context.clip(m_imageOverlayBounds);
     198    context.clip(m_selectionClipRect);
    179199    context.fillPath(coalescedSelectionPath);
    180200}
     
    187207}
    188208
     209void ImageOverlayController::elementUnderMouseDidChange(Frame&, Element*)
     210{
     211}
     212
    189213#endif // !PLATFORM(MAC)
    190214
  • trunk/Source/WebCore/page/ImageOverlayController.h

    r278121 r278331  
    3939
    4040class Document;
     41class Element;
    4142class Frame;
    4243class GraphicsContext;
     
    5859
    5960    void selectionQuadsDidChange(Frame&, const Vector<FloatQuad>&);
     61    void elementUnderMouseDidChange(Frame&, Element*);
     62
    6063    void documentDetached(const Document&);
    6164
     
    7073    PageOverlay& installPageOverlayIfNeeded();
    7174    void uninstallPageOverlayIfNeeded();
     75    void uninstallPageOverlay();
    7276
    7377#if PLATFORM(MAC)
     
    7983#endif
    8084
     85    void platformUpdateElementUnderMouse(Frame&, Element* elementUnderMouse);
    8186    bool platformHandleMouseEvent(const PlatformMouseEvent&);
    8287
    8388    WeakPtr<Page> m_page;
    8489    RefPtr<PageOverlay> m_overlay;
    85     WeakPtr<Document> m_currentOverlayDocument;
    86     Vector<FloatQuad> m_overlaySelectionQuads;
    87     LayoutRect m_imageOverlayBounds;
     90    WeakPtr<HTMLElement> m_hostElementForSelection;
     91    Vector<FloatQuad> m_selectionQuads;
     92    LayoutRect m_selectionClipRect;
    8893    Color m_selectionBackgroundColor { Color::transparentBlack };
    8994
     
    9297    Vector<ContainerAndHighlight> m_dataDetectorContainersAndHighlights;
    9398    RefPtr<DataDetectorHighlight> m_activeDataDetectorHighlight;
     99    WeakPtr<HTMLElement> m_hostElementForDataDetectors;
    94100#endif
    95101};
  • trunk/Source/WebCore/page/Page.h

    r278253 r278331  
    509509#endif
    510510    ImageOverlayController& imageOverlayController() { return *m_imageOverlayController; }
     511    ImageOverlayController* imageOverlayControllerIfExists() { return m_imageOverlayController.get(); }
    511512
    512513#if ENABLE(WHEEL_EVENT_LATCHING)
  • trunk/Source/WebCore/page/mac/ImageOverlayControllerMac.mm

    r278190 r278331  
    177177void ImageOverlayController::clearDataDetectorHighlights()
    178178{
     179    m_hostElementForDataDetectors = nullptr;
    179180    m_dataDetectorContainersAndHighlights.clear();
    180181    m_activeDataDetectorHighlight = nullptr;
    181182}
    182183
     184void ImageOverlayController::elementUnderMouseDidChange(Frame& frame, Element* elementUnderMouse)
     185{
     186    if (m_activeDataDetectorHighlight)
     187        return;
     188
     189    if (!elementUnderMouse && m_hostElementForDataDetectors && frame.document() != &m_hostElementForDataDetectors->document())
     190        return;
     191
     192    if (!elementUnderMouse || !HTMLElement::isInsideImageOverlay(*elementUnderMouse)) {
     193        m_hostElementForDataDetectors = nullptr;
     194        uninstallPageOverlayIfNeeded();
     195        return;
     196    }
     197
     198    auto shadowHost = elementUnderMouse->shadowHost();
     199    if (!is<HTMLElement>(shadowHost)) {
     200        ASSERT_NOT_REACHED();
     201        m_hostElementForDataDetectors = nullptr;
     202        uninstallPageOverlayIfNeeded();
     203        return;
     204    }
     205
     206    auto imageOverlayHost = makeRef(downcast<HTMLElement>(*shadowHost));
     207    if (!imageOverlayHost->hasImageOverlay()) {
     208        ASSERT_NOT_REACHED();
     209        m_hostElementForDataDetectors = nullptr;
     210        uninstallPageOverlayIfNeeded();
     211        return;
     212    }
     213
     214    if (m_hostElementForDataDetectors == imageOverlayHost.ptr())
     215        return;
     216
     217    updateDataDetectorHighlights(imageOverlayHost.get());
     218
     219    if (m_dataDetectorContainersAndHighlights.isEmpty()) {
     220        m_hostElementForDataDetectors = nullptr;
     221        uninstallPageOverlayIfNeeded();
     222        return;
     223    }
     224
     225    m_hostElementForDataDetectors = makeWeakPtr(imageOverlayHost.get());
     226    installPageOverlayIfNeeded();
     227}
     228
    183229} // namespace WebCore
    184230
Note: See TracChangeset for help on using the changeset viewer.