Changeset 267220 in webkit


Ignore:
Timestamp:
Sep 17, 2020, 6:45:00 PM (4 years ago)
Author:
Darin Adler
Message:

Selection API: Introduce LiveRangeSelectionEnabled, off by default
https://bugs.webkit.org/show_bug.cgi?id=216656

Reviewed by Sam Weinig.

Source/WebCore:

For interoperability, the Selection API requires behavior that is quite different
from how our Selection object has behaves historically. Specifically, the range
returned is a live range that update as the selection updates and the selection,
in turn, is updated if the returned range is modified.

This significant change will have compatibility impact on websites and other
content that assumes the legacy WebKit behavior, so we are developing it behind
a feature flag, LiveRangeSelectionEnabled.

This patch introduces the live range selection behind the flag, but leaves one
significant area unresolved, changing selection to keep track of the original
endpoints rather than only canonicalized endpoints, and a couple of loose ends,
lifetime of ride-along properties on the live range object, and updating test
expectations for the new behavior.

  • dom/CharacterData.cpp:

(WebCore::CharacterData::setData): Fixed timing of call to Document::textRemoved
by letting setDataAndUpdate do it; otherwise it can be called after the selection
has been updated, and the range gets updated twice.
(WebCore::CharacterData::appendData): Pass UpdateLiveRanges::No to setDataAndUpdate,
to preserve the existing "don't update ranges" behavior for now at least.
(WebCore::CharacterData::insertData): Fixed timing of call to Document::textInserted
by letting setDataAndUpdate do it, for the same reason as above.
(WebCore::CharacterData::deleteData): Ditto, for textRemoved.
(WebCore::CharacterData::replaceData): Ditto, for textRemoved and textInserted.
(WebCore::CharacterData::setDataAndUpdate): Added UpdateLiveRanges argument and
calls to textRemoved and textInserted, after setting data, but before other updates.

  • dom/CharacterData.h: Made setDataAndUpdate protected and added UpdateLiveRanges.
  • dom/Document.h: Added optimized version of the Node::contains function. When the

node in question is the document we can just check treeScope and isConnected
rather than walking up the tree.

  • dom/Node.cpp:

(WebCore::Node::isDescendantOf const): Moved special case for document to the top
of the function; seems a more important special case than disconnected nodes and
nodes with no children.
(WebCore::Node::isDescendantOrShadowDescendantOf const): Reworded the FIXME for clarity.
(WebCore::Node::contains const): Changed this to toke a reference instead of a pointer.
The pointer flavor is now inlined in the header.

  • dom/Node.h: Added an overload of contains that takes a reference, analogous to what

we already have for isDescendantOf.

  • dom/Position.cpp:

(WebCore::Position::Position): Removed too-strict assertions. When we start using
positions to represent arbitrary DOM positions for things outside editing we won't
want these assertions any more, and we hit them when we use Position in a more
straightforward way, like some cases in this patch.
(WebCore::Position::primaryDirection const): Added a missing null check. This case
is hit in some test cases in the new mode.

  • dom/Range.cpp: Removed some, but probably not all, of the unneeded headers.

(WebCore::Range::~Range): Added an assertion.
(WebCore::Range::updateAssociatedSelection): Added. Tells FrameSelection to update
the selection when this is the live range associated with the selection.
(WebCore::Range::updateDocument): Added an assertion.
(WebCore::Range::setStart): Call updateAssociatedSelection.
(WebCore::Range::setEnd): Ditto.
(WebCore::Range::collapse): Ditto.
(WebCore::Range::processContents): Call collapse here to share slightly more
code so we don't have to call updateAssociatedSelection here.
(WebCore::Range::checkNodeOffsetPair): Made this a static function so it can
be used outside the Range class. Also fixed indentation of the switch statement.
(WebCore::Range::selectNodeContents): Call updateAssociatedSelection and
updateDocument.
(WebCore::setBothEndpoints): Added.
(WebCore::Range::updateFromSelection): Added. Uses setBothEndpoints.
(WebCore::createLiveRange): Refactored to call setBothEndpoints.

  • dom/Range.h: Added didAssociateWithSelection, didDisassociateFromSelection,

updateFromSelection, updateAssociatedSelection, and m_isAssociatedWithSelection.
Also made checkNodeOffsetPair a public static member function instead of a
private non-static member function.

  • dom/Text.cpp:

(WebCore::Text::setDataAndUpdate): Update to pass along UpdateLiveRanges argument.

  • dom/Text.h: Ditto. Also made the override of setDataAndUpdate private.
  • editing/FrameSelection.cpp:

(WebCore::FrameSelection::setSelectionWithoutUpdatingAppearance): Call
updateAssociatedLiveRange after updating the selection.
(WebCore::containsEndpoints): Added. Used to check if a live range still has the
document as its root node so it can remain associated this with the selection.
(WebCore::FrameSelection::associatedLiveRange): Added.
(WebCore::FrameSelection::disassociateLiveRange): Added.
(WebCore::FrameSelection::associateLiveRange): Added.
(WebCore::FrameSelection::updateFromAssociatedLiveRange): Added.
(WebCore::FrameSelection::updateAssociatedLiveRange): Added.

  • editing/FrameSelection.h: Added associatedLiveRange, associateLiveRange,

disassociateLiveRange, updateFromAssociatedLiveRange, updateAssociatedLiveRange,
and m_associatedLiveRange. Also change m_document to a WeakPtr and do some
tidying up.

  • page/DOMSelection.cpp:

(WebCore::selectionShadowAncestor): Assert the live range setting is false,
since this code is not used in that case. Changed the return type to RefPtr.
Address the FIXME here by getting the document in a simpler way.
(WebCore::DOMSelection::create): Moved here from the header.
(WebCore::DOMSelection::frame const): Added. Returns a RefPtr. This gets rid
of the need for various instances of the "protector" pattern in this file.
(WebCore::DOMSelection::range const): Added. Returns the selected range, but
checks for the shadow tree case and returns null in that case.
(WebCore::DOMSelection::anchorPosition const): Redid as a member function.
(WebCore::DOMSelection::focusPosition const): Ditto.
(WebCore::DOMSelection::basePosition const): Ditto.
(WebCore::DOMSelection::extentPosition const): Ditto.
(WebCore::DOMSelection::anchorNode const): Rewrote to be simpler using the
functions above.
(WebCore::DOMSelection::anchorOffset const): Ditto.
(WebCore::DOMSelection::focusNode const): Ditto.
(WebCore::DOMSelection::focusOffset const): Ditto.
(WebCore::DOMSelection::baseNode const): Ditto.
(WebCore::DOMSelection::baseOffset const): Ditto.
(WebCore::DOMSelection::extentNode const): Ditto.
(WebCore::DOMSelection::extentOffset const): Ditto.
(WebCore::DOMSelection::isCollapsed const): Rewrote using DOMSelection::range
and SimpleRange::collapsed.
(WebCore::DOMSelection::type const): Updated since frame returns RefPtr.
(WebCore::DOMSelection::rangeCount const): Ditto.
(WebCore::DOMSelection::collapse): Added new corrected checking for special
cases, guarded by the setting.
(WebCore::DOMSelection::collapseToEnd): Updated since frame returns RefPtr.
way to do protection. Added a call to disassociateLiveRange. No need to put
under a setting guard since it does nothing if feature is not enabled.
(WebCore::DOMSelection::collapseToStart): Ditto.
(WebCore::DOMSelection::empty): Call removeAllRanges so we don't have two
copies of the same function to maintain.
(WebCore::DOMSelection::setBaseAndExtent): Use makeRefPtr, added corrected
checking for special cases guarded by the setting.
(WebCore::DOMSelection::setPosition): Call collapse so we don't have two
copies of the same function to maintain.
(WebCore::DOMSelection::modify): Updated since frame returns RefPtr.
(WebCore::DOMSelection::extend): Ditto. Also added corrected checking for
special cases guarded by the setting.
(WebCore::DOMSelection::getRangeAt): Added a version of this function that
simply returns the associated live range, creating one if needed, guarded
by the setting.
(WebCore::DOMSelection::removeAllRanges): Updated since frame returns RefPtr.
(WebCore::DOMSelection::addRange): Added a version of this function that
matches the specification, guarded by the setting.
(WebCore::DOMSelection::removeRange): Added.
(WebCore::DOMSelection::deleteFromDocument): Added a version of this
function that simply calls deleteContents on the associated live range,
guarded by the setting.
(WebCore::DOMSelection::containsNode const): Put the text node workaround
inside a setting check. I also figured out why workaround was added: it
works around unwanted range canonicalization in various tests, but also
introduces incorrect behavior. Also rewrote to use DOMSelection::range.
(WebCore::DOMSelection::toString): Use DOMSelection::range, guarded by
the setting, so we return empty string when selection is in the shadow tree.
(WebCore::DOMSelection::shadowAdjustedNode const): When the live range
setting is enabled, adjust by changing the node to nullptr, not finding an
ancestor in the document.
(WebCore::DOMSelection::shadowAdjustedOffset const): Ditto, but offset 0.
(WebCore::DOMSelection::isValidForPosition const): Assert that the live range
selection setting is disabled, because this incorrect check should only be
used to preserve legacy behavior until we are ready to turn it on.

  • page/DOMSelection.h: Updated since setBaseAndExtent, setPosition, and

collapse can raise exceptions, although they only do so when the live range
selection setting is on. Added removeRange. Moved the create function
out of the class definition into the .cpp file. Made return values of
baseNode, extentNode, anchorNode, focusNode, and shadowAdjustedNode RefPtr.
Made toString const. Added frame, range, anchorPosition, focusPosition,
basePosition, and extentPosition private functions. Removed visibleSelection.

  • page/DOMSelection.idl: Updated file to match a recent draft of the

Selection standard, reordering things to match the order they appear there.
Added removeRange, guarded by the setting. Also got rid of the
unncecessary "undefined" string defaults for the arguments to the modify
method since they have no effect on observed behavior anyway.

  • page/Settings.yaml: Added liveRangeSelectionEnabled.

Source/WebKit:

  • Shared/WebPreferencesInternal.yaml: Added LiveRangeSelectionEnabled.

LayoutTests:

  • editing/selection/move-to-line-boundary-clear-selection.html: Fix test that accidentally

relied on our non-standard behavior of clamping a too-high offset to a valid value. This
was not what we were trying to test. Without this change, the test fails in the new mode.
The offset was "5", which seems to be a character count, rather than "1", meaning "after
the text node".

  • editing/selection/toString-1.html: Ditto. The offset here was 3, but the HTML element

has only 2 children, the head and the body.

  • editing/selection/user-select-all-selection.html: Ditto. The code was passing the wrong

container by acccident, the parent of the text node rather than the text node.

Location:
trunk
Files:
23 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r267218 r267220  
     12020-09-16  Darin Adler  <darin@apple.com>
     2
     3        Selection API: Introduce LiveRangeSelectionEnabled, off by default
     4        https://bugs.webkit.org/show_bug.cgi?id=216656
     5
     6        Reviewed by Sam Weinig.
     7
     8        * editing/selection/move-to-line-boundary-clear-selection.html: Fix test that accidentally
     9        relied on our non-standard behavior of clamping a too-high offset to a valid value. This
     10        was not what we were trying to test. Without this change, the test fails in the new mode.
     11        The offset was "5", which seems to be a character count, rather than "1", meaning "after
     12        the text node".
     13        * editing/selection/toString-1.html: Ditto. The offset here was 3, but the HTML element
     14        has only 2 children, the head and the body.
     15        * editing/selection/user-select-all-selection.html: Ditto. The code was passing the wrong
     16        container by acccident, the parent of the text node rather than the text node.
     17
    1182020-09-17  Chris Dumez  <cdumez@apple.com>
    219
  • trunk/LayoutTests/editing/selection/move-to-line-boundary-clear-selection.html

    r197024 r267220  
    1818shouldBe('getSelection().toString()', "''");
    1919getSelection().empty();
    20 getSelection().setPosition(span, 5);
     20getSelection().setPosition(span, 1);
    2121getSelection().modify("extend", "backward", "line");
    2222if (window.testRunner)
  • trunk/LayoutTests/editing/selection/toString-1.html

    r120173 r267220  
    2121       
    2222        if (selection.setBaseAndExtent)
    23             selection.setBaseAndExtent(bodyElement, 1, htmlElement, 3);
     23            selection.setBaseAndExtent(bodyElement, 1, htmlElement, 2);
    2424        else
    2525            throw("Couldn't set a selection.");
  • trunk/LayoutTests/editing/selection/user-select-all-selection.html

    r155276 r267220  
    5050function placeCaretBeforeUserSelectAllElement(){
    5151    var userSelectAllElement = document.getElementById("allArea");
    52     execSetSelectionCommand(userSelectAllElement.previousSibling.firstChild, userSelectAllElement.previousSibling.textContent.length, userSelectAllElement.previousSibling, userSelectAllElement.previousSibling.textContent.length);
     52    execSetSelectionCommand(userSelectAllElement.previousSibling.firstChild, userSelectAllElement.previousSibling.textContent.length, userSelectAllElement.previousSibling.firstChild, userSelectAllElement.previousSibling.textContent.length);
    5353}
    5454
  • trunk/Source/WebCore/ChangeLog

    r267218 r267220  
     12020-09-16  Darin Adler  <darin@apple.com>
     2
     3        Selection API: Introduce LiveRangeSelectionEnabled, off by default
     4        https://bugs.webkit.org/show_bug.cgi?id=216656
     5
     6        Reviewed by Sam Weinig.
     7
     8        For interoperability, the Selection API requires behavior that is quite different
     9        from how our Selection object has behaves historically. Specifically, the range
     10        returned is a live range that update as the selection updates and the selection,
     11        in turn, is updated if the returned range is modified.
     12
     13        This significant change will have compatibility impact on websites and other
     14        content that assumes the legacy  WebKit behavior, so we are developing it behind
     15        a feature flag, LiveRangeSelectionEnabled.
     16
     17        This patch introduces the live range selection behind the flag, but leaves one
     18        significant area unresolved, changing selection to keep track of the original
     19        endpoints rather than only canonicalized endpoints, and a couple of loose ends,
     20        lifetime of ride-along properties on the live range object, and updating test
     21        expectations for the new behavior.
     22
     23        * dom/CharacterData.cpp:
     24        (WebCore::CharacterData::setData): Fixed timing of call to Document::textRemoved
     25        by letting setDataAndUpdate do it; otherwise it can be called after the selection
     26        has been updated, and the range gets updated twice.
     27        (WebCore::CharacterData::appendData): Pass UpdateLiveRanges::No to setDataAndUpdate,
     28        to preserve the existing "don't update ranges" behavior for now at least.
     29        (WebCore::CharacterData::insertData): Fixed timing of call to Document::textInserted
     30        by letting setDataAndUpdate do it, for the same reason as above.
     31        (WebCore::CharacterData::deleteData): Ditto, for textRemoved.
     32        (WebCore::CharacterData::replaceData): Ditto, for textRemoved and textInserted.
     33        (WebCore::CharacterData::setDataAndUpdate): Added UpdateLiveRanges argument and
     34        calls to textRemoved and textInserted, after setting data, but before other updates.
     35
     36        * dom/CharacterData.h: Made setDataAndUpdate protected and added UpdateLiveRanges.
     37
     38        * dom/Document.h: Added optimized version of the Node::contains function. When the
     39        node in question is the document we can just check treeScope and isConnected
     40        rather than walking up the tree.
     41
     42        * dom/Node.cpp:
     43        (WebCore::Node::isDescendantOf const): Moved special case for document to the top
     44        of the function; seems a more important special case than disconnected nodes and
     45        nodes with no children.
     46        (WebCore::Node::isDescendantOrShadowDescendantOf const): Reworded the FIXME for clarity.
     47        (WebCore::Node::contains const): Changed this to toke a reference instead of a pointer.
     48        The pointer flavor is now inlined in the header.
     49
     50        * dom/Node.h: Added an overload of contains that takes a reference, analogous to what
     51        we already have for isDescendantOf.
     52
     53        * dom/Position.cpp:
     54        (WebCore::Position::Position): Removed too-strict assertions. When we start using
     55        positions to represent arbitrary DOM positions for things outside editing we won't
     56        want these assertions any more, and we hit them when we use Position in a more
     57        straightforward way, like some cases in this patch.
     58        (WebCore::Position::primaryDirection const): Added a missing null check. This case
     59        is hit in some test cases in the new mode.
     60
     61        * dom/Range.cpp: Removed some, but probably not all, of the unneeded headers.
     62        (WebCore::Range::~Range): Added an assertion.
     63        (WebCore::Range::updateAssociatedSelection): Added. Tells FrameSelection to update
     64        the selection when this is the live range associated with the selection.
     65        (WebCore::Range::updateDocument): Added an assertion.
     66        (WebCore::Range::setStart): Call updateAssociatedSelection.
     67        (WebCore::Range::setEnd): Ditto.
     68        (WebCore::Range::collapse): Ditto.
     69        (WebCore::Range::processContents): Call collapse here to share slightly more
     70        code so we don't have to call updateAssociatedSelection here.
     71        (WebCore::Range::checkNodeOffsetPair): Made this a static function so it can
     72        be used outside the Range class. Also fixed indentation of the switch statement.
     73        (WebCore::Range::selectNodeContents): Call updateAssociatedSelection and
     74        updateDocument.
     75        (WebCore::setBothEndpoints): Added.
     76        (WebCore::Range::updateFromSelection): Added. Uses setBothEndpoints.
     77        (WebCore::createLiveRange): Refactored to call setBothEndpoints.
     78
     79        * dom/Range.h: Added didAssociateWithSelection, didDisassociateFromSelection,
     80        updateFromSelection, updateAssociatedSelection, and m_isAssociatedWithSelection.
     81        Also made checkNodeOffsetPair a public static member function instead of a
     82        private non-static member function.
     83
     84        * dom/Text.cpp:
     85        (WebCore::Text::setDataAndUpdate): Update to pass along UpdateLiveRanges argument.
     86        * dom/Text.h: Ditto. Also made the override of setDataAndUpdate private.
     87
     88        * editing/FrameSelection.cpp:
     89        (WebCore::FrameSelection::setSelectionWithoutUpdatingAppearance): Call
     90        updateAssociatedLiveRange after updating the selection.
     91        (WebCore::containsEndpoints): Added. Used to check if a live range still has the
     92        document as its root node so it can remain associated this with the selection.
     93        (WebCore::FrameSelection::associatedLiveRange): Added.
     94        (WebCore::FrameSelection::disassociateLiveRange): Added.
     95        (WebCore::FrameSelection::associateLiveRange): Added.
     96        (WebCore::FrameSelection::updateFromAssociatedLiveRange): Added.
     97        (WebCore::FrameSelection::updateAssociatedLiveRange): Added.
     98
     99        * editing/FrameSelection.h: Added associatedLiveRange, associateLiveRange,
     100        disassociateLiveRange, updateFromAssociatedLiveRange, updateAssociatedLiveRange,
     101        and m_associatedLiveRange. Also change m_document to a WeakPtr and do some
     102        tidying up.
     103
     104        * page/DOMSelection.cpp:
     105        (WebCore::selectionShadowAncestor): Assert the live range setting is false,
     106        since this code is not used in that case. Changed the return type to RefPtr.
     107        Address the FIXME here by getting the document in a simpler way.
     108        (WebCore::DOMSelection::create): Moved here from the header.
     109        (WebCore::DOMSelection::frame const): Added. Returns a RefPtr. This gets rid
     110        of the need for various instances of the "protector" pattern in this file.
     111        (WebCore::DOMSelection::range const): Added. Returns the selected range, but
     112        checks for the shadow tree case and returns null in that case.
     113        (WebCore::DOMSelection::anchorPosition const): Redid as a member function.
     114        (WebCore::DOMSelection::focusPosition const): Ditto.
     115        (WebCore::DOMSelection::basePosition const): Ditto.
     116        (WebCore::DOMSelection::extentPosition const): Ditto.
     117        (WebCore::DOMSelection::anchorNode const): Rewrote to be simpler using the
     118        functions above.
     119        (WebCore::DOMSelection::anchorOffset const): Ditto.
     120        (WebCore::DOMSelection::focusNode const): Ditto.
     121        (WebCore::DOMSelection::focusOffset const): Ditto.
     122        (WebCore::DOMSelection::baseNode const): Ditto.
     123        (WebCore::DOMSelection::baseOffset const): Ditto.
     124        (WebCore::DOMSelection::extentNode const): Ditto.
     125        (WebCore::DOMSelection::extentOffset const): Ditto.
     126        (WebCore::DOMSelection::isCollapsed const): Rewrote using DOMSelection::range
     127        and SimpleRange::collapsed.
     128        (WebCore::DOMSelection::type const): Updated since frame returns RefPtr.
     129        (WebCore::DOMSelection::rangeCount const): Ditto.
     130        (WebCore::DOMSelection::collapse): Added new corrected checking for special
     131        cases, guarded by the setting.
     132        (WebCore::DOMSelection::collapseToEnd): Updated since frame returns RefPtr.
     133        way to do protection. Added a call to disassociateLiveRange. No need to put
     134        under a setting guard since it does nothing if feature is not enabled.
     135        (WebCore::DOMSelection::collapseToStart): Ditto.
     136        (WebCore::DOMSelection::empty): Call removeAllRanges so we don't have two
     137        copies of the same function to maintain.
     138        (WebCore::DOMSelection::setBaseAndExtent): Use makeRefPtr, added corrected
     139        checking for special cases guarded by the setting.
     140        (WebCore::DOMSelection::setPosition): Call collapse so we don't have two
     141        copies of the same function to maintain.
     142        (WebCore::DOMSelection::modify): Updated since frame returns RefPtr.
     143        (WebCore::DOMSelection::extend): Ditto. Also added corrected checking for
     144        special cases guarded by the setting.
     145        (WebCore::DOMSelection::getRangeAt): Added a version of this function that
     146        simply returns the associated live range, creating one if needed, guarded
     147        by the setting.
     148        (WebCore::DOMSelection::removeAllRanges): Updated since frame returns RefPtr.
     149        (WebCore::DOMSelection::addRange): Added a version of this function that
     150        matches the specification, guarded by the setting.
     151        (WebCore::DOMSelection::removeRange): Added.
     152        (WebCore::DOMSelection::deleteFromDocument): Added a version of this
     153        function that simply calls deleteContents on the associated live range,
     154        guarded by the setting.
     155        (WebCore::DOMSelection::containsNode const): Put the text node workaround
     156        inside a setting check. I also figured out why workaround was added: it
     157        works around unwanted range canonicalization in various tests, but also
     158        introduces incorrect behavior. Also rewrote to use DOMSelection::range.
     159        (WebCore::DOMSelection::toString): Use DOMSelection::range, guarded by
     160        the setting, so we return empty string when selection is in the shadow tree.
     161        (WebCore::DOMSelection::shadowAdjustedNode const): When the live range
     162        setting is enabled, adjust by changing the node to nullptr, not finding an
     163        ancestor in the document.
     164        (WebCore::DOMSelection::shadowAdjustedOffset const): Ditto, but offset 0.
     165        (WebCore::DOMSelection::isValidForPosition const): Assert that the live range
     166        selection setting is disabled, because this incorrect check should only be
     167        used to preserve legacy behavior until we are ready to turn it on.
     168
     169        * page/DOMSelection.h: Updated since setBaseAndExtent, setPosition, and
     170        collapse can raise exceptions, although they only do so when the live range
     171        selection setting is on. Added removeRange. Moved the create function
     172        out of the class definition into the .cpp file. Made return values of
     173        baseNode, extentNode, anchorNode, focusNode, and shadowAdjustedNode RefPtr.
     174        Made toString const. Added frame, range, anchorPosition, focusPosition,
     175        basePosition, and extentPosition private functions. Removed visibleSelection.
     176
     177        * page/DOMSelection.idl: Updated file to match a recent draft of the
     178        Selection standard, reordering things to match the order they appear there.
     179        Added removeRange, guarded by the setting. Also got rid of the
     180        unncecessary "undefined" string defaults for the arguments to the modify
     181        method since they have no effect on observed behavior anyway.
     182
     183        * page/Settings.yaml: Added liveRangeSelectionEnabled.
     184
    11852020-09-17  Chris Dumez  <cdumez@apple.com>
    2186
  • trunk/Source/WebCore/dom/CharacterData.cpp

    r259930 r267220  
    6464
    6565    setDataAndUpdate(nonNullData, 0, oldLength, nonNullData.length());
    66     document().textRemoved(*this, 0, oldLength);
    6766}
    6867
     
    118117void CharacterData::appendData(const String& data)
    119118{
    120     String newStr = m_data;
    121     newStr.append(data);
    122 
    123     setDataAndUpdate(newStr, m_data.length(), 0, data.length());
    124 
    125     // FIXME: Should we call textInserted here?
     119    setDataAndUpdate(m_data + data, m_data.length(), 0, data.length(), UpdateLiveRanges::No);
    126120}
    127121
     
    131125        return Exception { IndexSizeError };
    132126
    133     String newStr = m_data;
    134     newStr.insert(data, offset);
    135 
    136     setDataAndUpdate(newStr, offset, 0, data.length());
    137 
    138     document().textInserted(*this, offset, data.length());
     127    String newData = m_data;
     128    newData.insert(data, offset);
     129    setDataAndUpdate(newData, offset, 0, data.length());
    139130
    140131    return { };
     
    148139    count = std::min(count, length() - offset);
    149140
    150     String newStr = m_data;
    151     newStr.remove(offset, count);
    152 
    153     setDataAndUpdate(newStr, offset, count, 0);
    154 
    155     document().textRemoved(*this, offset, count);
     141    String newData = m_data;
     142    newData.remove(offset, count);
     143    setDataAndUpdate(newData, offset, count, 0);
    156144
    157145    return { };
     
    165153    count = std::min(count, length() - offset);
    166154
    167     String newStr = m_data;
    168     newStr.remove(offset, count);
    169     newStr.insert(data, offset);
    170 
    171     setDataAndUpdate(newStr, offset, count, data.length());
    172 
    173     // update the markers for spell checking and grammar checking
    174     document().textRemoved(*this, offset, count);
    175     document().textInserted(*this, offset, data.length());
     155    String newData = m_data;
     156    newData.remove(offset, count);
     157    newData.insert(data, offset);
     158    setDataAndUpdate(newData, offset, count, data.length());
    176159
    177160    return { };
     
    189172}
    190173
    191 void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength)
     174void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength, UpdateLiveRanges shouldUpdateLiveRanges)
    192175{
    193176    String oldData = m_data;
    194177    m_data = newData;
     178
     179    if (oldLength && shouldUpdateLiveRanges != UpdateLiveRanges::No)
     180        document().textRemoved(*this, offsetOfReplacedData, oldLength);
     181    if (newLength && shouldUpdateLiveRanges != UpdateLiveRanges::No)
     182        document().textInserted(*this, offsetOfReplacedData, newLength);
    195183
    196184    ASSERT(!renderer() || is<Text>(*this));
  • trunk/Source/WebCore/dom/CharacterData.h

    r266776 r267220  
    3434
    3535    WEBCORE_EXPORT void setData(const String&);
    36     virtual void setDataAndUpdate(const String&, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength);
    3736    unsigned length() const { return m_data.length(); }
    3837    WEBCORE_EXPORT ExceptionOr<String> substringData(unsigned offset, unsigned count);
     
    6160    void dispatchModifiedEvent(const String& oldValue);
    6261
     62    enum class UpdateLiveRanges : bool { No, Yes };
     63    virtual void setDataAndUpdate(const String&, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength, UpdateLiveRanges = UpdateLiveRanges::Yes);
     64
    6365private:
    6466    String nodeValue() const final;
  • trunk/Source/WebCore/dom/Document.h

    r266295 r267220  
    15921592    void canvasDestroyed(CanvasBase&) final;
    15931593
     1594    bool contains(const Node& node) const { return this == &node.treeScope() && node.isConnected(); }
     1595    bool contains(const Node* node) const { return node && contains(*node); }
     1596
    15941597protected:
    15951598    enum ConstructionFlags { Synthesized = 1, NonRenderedPlaceholder = 1 << 1 };
  • trunk/Source/WebCore/dom/Node.cpp

    r267175 r267220  
    33 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
    44 *           (C) 2001 Dirk Mueller (mueller@kde.org)
    5  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
     5 * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
    66 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
    77 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
     
    10211021bool Node::isDescendantOf(const Node& other) const
    10221022{
    1023     // Return true if other is an ancestor of this, otherwise false
     1023    // Return true if other is an ancestor of this.
     1024    if (other.isDocumentNode())
     1025        return &treeScope().rootNode() == &other && !isDocumentNode() && isConnected();
    10241026    if (!other.hasChildNodes() || isConnected() != other.isConnected())
    10251027        return false;
    1026     if (other.isDocumentNode())
    1027         return &document() == &other && !isDocumentNode() && isConnected();
    1028     for (const auto* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
     1028    for (auto ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
    10291029        if (ancestor == &other)
    10301030            return true;
     
    10351035bool Node::isDescendantOrShadowDescendantOf(const Node* other) const
    10361036{
    1037     // FIXME: This element's shadow tree's host could be inside another shadow tree.
    1038     // This function doesn't handle that case correctly. Maybe share code with
    1039     // the containsIncludingShadowDOM function?
     1037    // FIXME: Correctly handle the case where the shadow host is itself in a shadow tree.
    10401038    return other && (isDescendantOf(*other) || other->contains(shadowHost()));
    10411039}
    10421040
    1043 bool Node::contains(const Node* node) const
    1044 {
    1045     return this == node || (node && node->isDescendantOf(*this));
     1041bool Node::contains(const Node& node) const
     1042{
     1043    return this == &node || node.isDescendantOf(*this);
    10461044}
    10471045
  • trunk/Source/WebCore/dom/Node.h

    r267175 r267220  
    33 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
    44 *           (C) 2001 Dirk Mueller (mueller@kde.org)
    5  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
     5 * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
    66 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
    77 *
     
    372372    WEBCORE_EXPORT bool isDescendantOf(const Node&) const;
    373373    bool isDescendantOf(const Node* other) const { return other && isDescendantOf(*other); }
     374    WEBCORE_EXPORT bool contains(const Node&) const;
     375    bool contains(const Node* other) const { return other && contains(*other); }
    374376
    375377    bool isDescendantOrShadowDescendantOf(const Node*) const;
    376     WEBCORE_EXPORT bool contains(const Node*) const;
    377378    WEBCORE_EXPORT bool containsIncludingShadowDOM(const Node*) const;
    378379
  • trunk/Source/WebCore/dom/Position.cpp

    r266986 r267220  
    131131    , m_isLegacyEditingPosition(false)
    132132{
    133     ASSERT(!m_anchorNode || !editingIgnoresContent(*m_anchorNode));
    134     ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
    135133    ASSERT(anchorType == PositionIsOffsetInAnchor);
    136134}
     
    13631361TextDirection Position::primaryDirection() const
    13641362{
    1365     if (!m_anchorNode->renderer())
     1363    if (!m_anchorNode || !m_anchorNode->renderer())
    13661364        return TextDirection::LTR;
    13671365    if (auto* blockFlow = lineageOfType<RenderBlockFlow>(*m_anchorNode->renderer()).first())
  • trunk/Source/WebCore/dom/Range.cpp

    r266986 r267220  
    44 * (C) 2000 Frederik Holljen (frederik.holljen@hig.no)
    55 * (C) 2001 Peter Kelly (pmk@post.com)
    6  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
     6 * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
    77 * Copyright (C) 2011 Motorola Mobility. All rights reserved.
    88 *
     
    3030#include "DOMRectList.h"
    3131#include "DocumentFragment.h"
    32 #include "Editing.h"
    3332#include "Event.h"
    3433#include "Frame.h"
     34#include "FrameSelection.h"
    3535#include "FrameView.h"
    3636#include "GeometryUtilities.h"
    3737#include "HTMLBodyElement.h"
    38 #include "HTMLElement.h"
    3938#include "HTMLHtmlElement.h"
    4039#include "HTMLNames.h"
     
    4241#include "NodeWithIndex.h"
    4342#include "ProcessingInstruction.h"
    44 #include "RenderBlock.h"
    45 #include "RenderBoxModelObject.h"
    46 #include "RenderText.h"
    47 #include "RenderView.h"
    4843#include "ScopedEventQueue.h"
    4944#include "TextIterator.h"
    50 #include "VisiblePosition.h"
    5145#include "VisibleUnits.h"
    5246#include "markup.h"
     
    5650#include <wtf/text/StringBuilder.h>
    5751
    58 #if PLATFORM(IOS_FAMILY)
    59 #include "SelectionRect.h"
    60 #endif
    61 
    6252namespace WebCore {
    6353
     
    9181Range::~Range()
    9282{
     83    ASSERT(!m_isAssociatedWithSelection);
     84
    9385    m_ownerDocument->detachRange(*this);
    9486
     
    9890}
    9991
     92void Range::updateAssociatedSelection()
     93{
     94    if (m_isAssociatedWithSelection)
     95        m_ownerDocument->selection().updateFromAssociatedLiveRange();
     96}
     97
    10098void Range::updateDocument()
    10199{
     
    103101    if (m_ownerDocument.ptr() == &document)
    104102        return;
     103    ASSERT(!m_isAssociatedWithSelection);
    105104    m_ownerDocument->detachRange(*this);
    106105    m_ownerDocument = document;
     
    117116    if (!is_lteq(documentOrder(makeBoundaryPoint(m_start), makeBoundaryPoint(m_end))))
    118117        m_end = m_start;
     118    updateAssociatedSelection();
    119119    updateDocument();
    120120    return { };
     
    130130    if (!is_lteq(documentOrder(makeBoundaryPoint(m_start), makeBoundaryPoint(m_end))))
    131131        m_start = m_end;
     132    updateAssociatedSelection();
    132133    updateDocument();
    133134    return { };
     
    140141    else
    141142        m_start = m_end;
     143    updateAssociatedSelection();
    142144}
    143145
     
    369371                return result.releaseException();
    370372        }
    371         m_end = m_start;
     373        collapse(true);
    372374    }
    373375
     
    696698}
    697699
    698 ExceptionOr<Node*> Range::checkNodeOffsetPair(Node& node, unsigned offset) const
     700ExceptionOr<Node*> Range::checkNodeOffsetPair(Node& node, unsigned offset)
    699701{
    700702    switch (node.nodeType()) {
    701         case Node::DOCUMENT_TYPE_NODE:
    702             return Exception { InvalidNodeTypeError };
    703         case Node::CDATA_SECTION_NODE:
    704         case Node::COMMENT_NODE:
    705         case Node::TEXT_NODE:
    706         case Node::PROCESSING_INSTRUCTION_NODE:
    707             if (offset > downcast<CharacterData>(node).length())
    708                 return Exception { IndexSizeError };
     703    case Node::DOCUMENT_TYPE_NODE:
     704        return Exception { InvalidNodeTypeError };
     705    case Node::CDATA_SECTION_NODE:
     706    case Node::COMMENT_NODE:
     707    case Node::TEXT_NODE:
     708    case Node::PROCESSING_INSTRUCTION_NODE:
     709        if (offset > downcast<CharacterData>(node).length())
     710            return Exception { IndexSizeError };
     711        return nullptr;
     712    case Node::ATTRIBUTE_NODE:
     713    case Node::DOCUMENT_FRAGMENT_NODE:
     714    case Node::DOCUMENT_NODE:
     715    case Node::ELEMENT_NODE:
     716        if (!offset)
    709717            return nullptr;
    710         case Node::ATTRIBUTE_NODE:
    711         case Node::DOCUMENT_FRAGMENT_NODE:
    712         case Node::DOCUMENT_NODE:
    713         case Node::ELEMENT_NODE: {
    714             if (!offset)
    715                 return nullptr;
    716             Node* childBefore = node.traverseToChildAt(offset - 1);
    717             if (!childBefore)
    718                 return Exception { IndexSizeError };
    719             return childBefore;
    720         }
     718        auto childBefore = node.traverseToChildAt(offset - 1);
     719        if (!childBefore)
     720            return Exception { IndexSizeError };
     721        return childBefore;
    721722    }
    722723    ASSERT_NOT_REACHED();
     
    772773    if (node.isDocumentTypeNode())
    773774        return Exception { InvalidNodeTypeError };
    774 
    775775    m_start.setToBeforeContents(node);
    776776    m_end.setToAfterContents(node);
     777    updateAssociatedSelection();
     778    updateDocument();
    777779    return { };
    778780}
     
    10331035}
    10341036
     1037static void setBothEndpoints(Range& range, const SimpleRange& value)
     1038{
     1039    auto startContainer = value.start.container;
     1040    range.setStart(WTFMove(startContainer), value.start.offset);
     1041    auto endContainer = value.end.container;
     1042    range.setEnd(WTFMove(endContainer), value.end.offset);
     1043}
     1044
     1045void Range::updateFromSelection(const SimpleRange& value)
     1046{
     1047    ASSERT(m_isAssociatedWithSelection);
     1048    m_isAssociatedWithSelection = false;
     1049    setBothEndpoints(*this, value);
     1050    m_isAssociatedWithSelection = true;
     1051}
     1052
    10351053SimpleRange makeSimpleRange(const Range& range)
    10361054{
     
    10581076{
    10591077    auto result = Range::create(range.start.document());
    1060     auto startContainer = range.start.container;
    1061     result->setStart(WTFMove(startContainer), range.start.offset);
    1062     auto endContainer = range.end.container;
    1063     result->setEnd(WTFMove(endContainer), range.end.offset);
     1078    setBothEndpoints(result, range);
    10641079    return result;
    10651080}
  • trunk/Source/WebCore/dom/Range.h

    r266986 r267220  
    100100    void textNodeSplit(Text& oldNode);
    101101
     102    void didAssociateWithSelection() { m_isAssociatedWithSelection = true; }
     103    void didDisassociateFromSelection() { m_isAssociatedWithSelection = false; }
     104    void updateFromSelection(const SimpleRange&);
     105
     106    static ExceptionOr<Node*> checkNodeOffsetPair(Node&, unsigned offset);
     107
    102108#if ENABLE(TREE_DEBUGGING)
    103109    String debugDescription() const;
     
    110116
    111117    void updateDocument();
    112     ExceptionOr<Node*> checkNodeOffsetPair(Node&, unsigned offset) const;
     118    void updateAssociatedSelection();
    113119    ExceptionOr<RefPtr<DocumentFragment>> processContents(ActionType);
    114120
     
    116122    RangeBoundaryPoint m_start;
    117123    RangeBoundaryPoint m_end;
     124    bool m_isAssociatedWithSelection { false };
    118125};
    119126
  • trunk/Source/WebCore/dom/Text.cpp

    r266986 r267220  
    246246}
    247247
    248 void Text::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength)
     248void Text::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength, UpdateLiveRanges updateLiveRanges)
    249249{
    250250    auto oldData = data();
    251     CharacterData::setDataAndUpdate(newData, offsetOfReplacedData, oldLength, newLength);
    252 
     251    CharacterData::setDataAndUpdate(newData, offsetOfReplacedData, oldLength, newLength, updateLiveRanges);
     252
     253    // FIXME: Does not seem correct to do this for 0 offset only.
    253254    if (!offsetOfReplacedData) {
    254255        auto* textManipulationController = document().textManipulationControllerIfExists();
  • trunk/Source/WebCore/dom/Text.h

    r266986 r267220  
    5858    String debugDescription() const final;
    5959
    60     void setDataAndUpdate(const String&, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength) final;
    61 
    6260protected:
    6361    Text(Document& document, const String& data, ConstructionType type)
     
    7169    Ref<Node> cloneNodeInternal(Document&, CloningOperation) override;
    7270    bool childTypeAllowed(NodeType) const override;
     71    void setDataAndUpdate(const String&, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength, UpdateLiveRanges) final;
    7372
    7473    virtual Ref<Text> virtualCreate(const String&);
  • trunk/Source/WebCore/editing/FrameSelection.cpp

    r266986 r267220  
    11/*
    2  * Copyright (C) 2004, 2008, 2009, 2010, 2014-2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    150150
    151151FrameSelection::FrameSelection(Document* document)
    152     : m_document(document)
    153     , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
     152    : m_document(makeWeakPtr(document))
    154153    , m_granularity(TextGranularity::CharacterGranularity)
    155154#if ENABLE(TEXT_CARET)
     
    170169#endif
    171170{
    172     if (shouldAlwaysUseDirectionalSelection(m_document))
     171    if (shouldAlwaysUseDirectionalSelection(m_document.get()))
    173172        m_selection.setIsDirectional(true);
    174173
     
    249248        clearCaretRect();
    250249    else
    251         updateCaretRect(document, m_position);
     250        updateCaretRect(*document, m_position);
    252251}
    253252
     
    293292{
    294293    VisibleSelection newSelection = passedNewSelection;
    295     bool isDirectional = shouldAlwaysUseDirectionalSelection(m_document) || newSelection.isDirectional();
     294    bool isDirectional = shouldAlwaysUseDirectionalSelection(m_document.get()) || newSelection.isDirectional();
    296295
    297296    VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
     
    315314    if (m_selection == newSelection || !shouldChangeSelection(newSelection))
    316315        return;
    317 
    318316   
    319317    AXTextStateChangeIntent intent;
     
    331329
    332330    VisibleSelection newSelection = newSelectionPossiblyWithoutDirection;
    333     if (shouldAlwaysUseDirectionalSelection(m_document))
     331    if (shouldAlwaysUseDirectionalSelection(m_document.get()))
    334332        newSelection.setIsDirectional(true);
    335333
    336334    if (!m_document || !m_document->frame()) {
    337335        m_selection = newSelection;
     336        updateAssociatedLiveRange();
    338337        return false;
    339338    }
     
    369368
    370369    m_selection = newSelection;
     370    updateAssociatedLiveRange();
    371371
    372372    // Selection offsets should increase when LF is inserted before the caret in InsertLineBreakCommand. See <https://webkit.org/b/56061>.
     
    391391    // Always clear the x position used for vertical arrow navigation.
    392392    // It will be restored by the vertical arrow navigation code if necessary.
    393     m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
     393    m_xPosForVerticalArrowNavigation = WTF::nullopt;
    394394    selectFrameElementInParentIfFullySelected();
    395395    m_document->editor().respondToChangedSelection(oldSelection, options);
     
    405405    LOG_WITH_STREAM(Selection, stream << "FrameSelection::setSelection " << selection);
    406406
    407     RefPtr<Document> protector(m_document);
     407    auto protectedDocument = makeRefPtr(m_document.get());
    408408    if (!setSelectionWithoutUpdatingAppearance(selection, options, align, granularity))
    409409        return;
     
    420420    m_pendingSelectionUpdate = true;
    421421
    422     if (m_document->hasPendingStyleRecalc())
    423         return;
    424 
    425     FrameView* frameView = m_document->view();
     422    if (protectedDocument->hasPendingStyleRecalc())
     423        return;
     424
     425    auto frameView = protectedDocument->view();
    426426    if (frameView && frameView->layoutContext().isLayoutPending())
    427427        return;
     
    430430
    431431    if (options & IsUserTriggered) {
    432         if (auto* client = m_document->editor().client())
     432        if (auto* client = protectedDocument->editor().client())
    433433            client->didEndUserTriggeredSelectionChanges();
    434434    }
     
    805805        break;
    806806    case TextGranularity::LineGranularity:
    807         pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
     807        pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(Extent));
    808808        break;
    809809    case TextGranularity::ParagraphGranularity:
    810         pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
     810        pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(Extent));
    811811        break;
    812812    case TextGranularity::DocumentGranularity:
     
    918918        pos = currentPosition;
    919919        if (!isRange() || !isStartOfLine(pos))
    920             pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
     920            pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(Start));
    921921        break;
    922922    }
    923923    case TextGranularity::ParagraphGranularity:
    924         pos = nextParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
     924        pos = nextParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(Start));
    925925        break;
    926926    case TextGranularity::DocumentGranularity:
     
    10261026        break;
    10271027    case TextGranularity::LineGranularity:
    1028         pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
     1028        pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(Extent));
    10291029        break;
    10301030    case TextGranularity::ParagraphGranularity:
    1031         pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
     1031        pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(Extent));
    10321032        break;
    10331033    case TextGranularity::SentenceBoundary:
     
    11341134        break;
    11351135    case TextGranularity::LineGranularity:
    1136         pos = previousLinePosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
     1136        pos = previousLinePosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(Start));
    11371137        break;
    11381138    case TextGranularity::ParagraphGranularity:
    1139         pos = previousParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
     1139        pos = previousParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(Start));
    11401140        break;
    11411141    case TextGranularity::SentenceBoundary:
     
    13791379    // Some of the above operations set an xPosForVerticalArrowNavigation.
    13801380    // Setting a selection will clear it, so save it to possibly restore later.
    1381     // Note: the START position type is arbitrary because it is unused, it would be
     1381    // Note: the Start position type is arbitrary because it is unused, it would be
    13821382    // the requested position type if there were no xPosForVerticalArrowNavigation set.
    1383     LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
    1384     m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_document) || alter == AlterationExtend);
     1383    LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(Start);
     1384    m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_document.get()) || alter == AlterationExtend);
    13851385
    13861386    switch (alter) {
     
    14601460    case AlterationMove:
    14611461        pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
    1462         xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
     1462        xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? Start : End);
    14631463        m_selection.setAffinity(direction == DirectionUp ? Affinity::Upstream : Affinity::Downstream);
    14641464        break;
    14651465    case AlterationExtend:
    14661466        pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
    1467         xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
     1467        xPos = lineDirectionPointForBlockDirectionNavigation(Extent);
    14681468        m_selection.setAffinity(Affinity::Downstream);
    14691469        break;
     
    15151515        m_granularity = TextGranularity::CharacterGranularity;
    15161516
    1517     m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_document) || alter == AlterationExtend);
     1517    m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_document.get()) || alter == AlterationExtend);
    15181518
    15191519    return true;
    15201520}
    15211521
    1522 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
    1523 {
    1524     LayoutUnit x;
    1525 
     1522LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(PositionType type)
     1523{
    15261524    if (isNone())
    1527         return x;
    1528 
    1529     Position pos;
     1525        return 0;
     1526
     1527    // FIXME: Can we use visibleStart/End/Extent?
     1528    Position position;
    15301529    switch (type) {
    1531     case START:
    1532         pos = m_selection.start();
    1533         break;
    1534     case END:
    1535         pos = m_selection.end();
    1536         break;
    1537     case BASE:
    1538         pos = m_selection.base();
    1539         break;
    1540     case EXTENT:
    1541         pos = m_selection.extent();
    1542         break;
    1543     }
    1544 
    1545     Frame* frame = pos.anchorNode()->document().frame();
    1546     if (!frame)
    1547         return x;
    1548        
    1549     if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
    1550         VisiblePosition visiblePosition(pos, m_selection.affinity());
    1551         // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
    1552         // after the selection is created and before this function is called.
    1553         x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
    1554         m_xPosForVerticalArrowNavigation = x;
    1555     } else
    1556         x = m_xPosForVerticalArrowNavigation;
    1557        
     1530    case Start:
     1531        position = m_selection.start();
     1532        break;
     1533    case End:
     1534        position = m_selection.end();
     1535        break;
     1536    case Extent:
     1537        position = m_selection.extent();
     1538        break;
     1539    }
     1540
     1541    // FIXME: Why is this check needed? What's the harm in doing a little more work without a frame?
     1542    if (!position.anchorNode()->document().frame())
     1543        return 0;
     1544
     1545    // FIXME: Can we do this before getting the position from the selection?
     1546    if (m_xPosForVerticalArrowNavigation)
     1547        return *m_xPosForVerticalArrowNavigation;
     1548
     1549    // VisiblePosition creation can fail here if a node containing the selection becomes
     1550    // visibility:hidden after the selection is created and before this function is called.
     1551    VisiblePosition visiblePosition(position, m_selection.affinity());
     1552    auto x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
     1553    m_xPosForVerticalArrowNavigation = { x };
    15581554    return x;
    15591555}
     
    16271623}
    16281624
    1629 bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
    1630 {
    1631     document->updateLayoutIgnorePendingStylesheets();
     1625bool CaretBase::updateCaretRect(Document& document, const VisiblePosition& caretPosition)
     1626{
     1627    document.updateLayoutIgnorePendingStylesheets();
    16321628    m_caretRectNeedsUpdate = false;
    16331629    RenderBlock* renderer;
     
    16881684        else {
    16891685            VisiblePosition visibleStart = m_selection.visibleStart();
    1690             if (updateCaretRect(m_document, visibleStart)) {
     1686            if (updateCaretRect(*m_document, visibleStart)) {
    16911687                caretNode = visibleStart.deepEquivalent().deprecatedNode();
    16921688                m_absCaretBoundsDirty = true;
     
    21272123    // If the caret moved, stop the blink timer so we can restart with a
    21282124    // black caret in the new location.
    2129     if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_document))
     2125    if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_document.get()))
    21302126        m_caretBlinkTimer.stop();
    21312127
     
    27772773    }
    27782774}
     2775
    27792776#endif // PLATFORM(IOS_FAMILY)
    27802777
     2778static bool containsEndpoints(const WeakPtr<Document>& document, const Optional<SimpleRange>& range)
     2779{
     2780    return document && range && document->contains(range->start.container) && document->contains(range->end.container);
     2781}
     2782
     2783static bool containsEndpoints(const WeakPtr<Document>& document, const Range& liveRange)
     2784{
     2785    // Only need to check the start container because live ranges enforce the invariant that start and end have a common ancestor.
     2786    return document && document->contains(liveRange.startContainer());
     2787}
     2788
     2789RefPtr<Range> FrameSelection::associatedLiveRange()
     2790{
     2791    if (!m_associatedLiveRange) {
     2792        if (auto range = m_selection.firstRange(); containsEndpoints(m_document, range)) {
     2793            m_associatedLiveRange = createLiveRange(*range);
     2794            m_associatedLiveRange->didAssociateWithSelection();
     2795        }
     2796    }
     2797    return m_associatedLiveRange;
     2798}
     2799
     2800void FrameSelection::disassociateLiveRange()
     2801{
     2802    if (auto previouslyAssociatedLiveRange = std::exchange(m_associatedLiveRange, nullptr))
     2803        previouslyAssociatedLiveRange->didDisassociateFromSelection();
     2804    // FIXME: Need additional code to keep the live range object's wrapper alive to preserve any JavaScript properties on it.
     2805}
     2806
     2807void FrameSelection::associateLiveRange(Range& liveRange)
     2808{
     2809    disassociateLiveRange();
     2810    m_associatedLiveRange = &liveRange;
     2811    liveRange.didAssociateWithSelection();
     2812    updateFromAssociatedLiveRange();
     2813}
     2814
     2815void FrameSelection::updateFromAssociatedLiveRange()
     2816{
     2817    ASSERT(m_associatedLiveRange);
     2818    if (!containsEndpoints(m_document, *m_associatedLiveRange))
     2819        disassociateLiveRange();
     2820    else
     2821        setSelection(makeSimpleRange(*m_associatedLiveRange));
     2822    // FIXME: Normalization done by setSelection will be visible next time updateAssociatedLiveRange is called. Instead the Selection API specification calls for allowing non-normalized selection ranges.
     2823}
     2824
     2825void FrameSelection::updateAssociatedLiveRange()
     2826{
     2827    auto range = m_selection.firstRange();
     2828    if (!containsEndpoints(m_document, range))
     2829        disassociateLiveRange();
     2830    else if (m_associatedLiveRange)
     2831        m_associatedLiveRange->updateFromSelection(*range);
     2832}
     2833
    27812834}
    27822835
    27832836#if ENABLE(TREE_DEBUGGING)
    27842837
    2785 void showTree(const WebCore::FrameSelection& sel)
    2786 {
    2787     sel.showTreeForThis();
    2788 }
    2789 
    2790 void showTree(const WebCore::FrameSelection* sel)
    2791 {
    2792     if (sel)
    2793         sel->showTreeForThis();
    2794 }
    2795 
    2796 #endif
     2838void showTree(const WebCore::FrameSelection& selection)
     2839{
     2840    selection.showTreeForThis();
     2841}
     2842
     2843void showTree(const WebCore::FrameSelection* selection)
     2844{
     2845    if (selection)
     2846        selection->showTreeForThis();
     2847}
     2848
     2849#endif
  • trunk/Source/WebCore/editing/FrameSelection.h

    r266986 r267220  
    11/*
    2  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
     2 * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727
    2828#include "AXTextStateChangeIntent.h"
     29#include "Color.h"
    2930#include "EditingStyle.h"
    3031#include "Element.h"
     
    3536#include "VisibleSelection.h"
    3637#include <wtf/Noncopyable.h>
    37 
    38 #if PLATFORM(IOS_FAMILY)
    39 #include "Color.h"
    40 #endif
    4138
    4239namespace WebCore {
     
    5249class VisiblePosition;
    5350
    54 enum EUserTriggered { NotUserTriggered = 0, UserTriggered = 1 };
    55 
    56 enum RevealExtentOption {
    57     RevealExtent,
    58     DoNotRevealExtent
    59 };
     51enum EUserTriggered : bool { NotUserTriggered, UserTriggered };
     52enum RevealExtentOption : bool { RevealExtent, DoNotRevealExtent };
    6053
    6154class CaretBase {
     
    7063    void invalidateCaretRect(Node*, bool caretRectChanged = false);
    7164    void clearCaretRect();
    72     bool updateCaretRect(Document*, const VisiblePosition& caretPosition);
     65    bool updateCaretRect(Document&, const VisiblePosition& caretPosition);
    7366    bool shouldRepaintCaret(const RenderView*, bool isContentEditable) const;
    7467    void paintCaret(const Node&, GraphicsContext&, const LayoutPoint&, const LayoutRect& clipRect) const;
     
    119112public:
    120113    enum EAlteration { AlterationMove, AlterationExtend };
    121     enum CursorAlignOnScroll { AlignCursorOnScrollIfNeeded,
    122                                AlignCursorOnScrollAlways };
     114    enum CursorAlignOnScroll { AlignCursorOnScrollIfNeeded, AlignCursorOnScrollAlways };
    123115    enum SetSelectionOption {
    124116        FireSelectEvent = 1 << 0,
     
    132124        RevealSelectionUpToMainFrame = 1 << 8,
    133125    };
    134     static constexpr OptionSet<SetSelectionOption> defaultSetSelectionOptions(EUserTriggered userTriggered = NotUserTriggered)
    135     {
    136         OptionSet<SetSelectionOption> options { CloseTyping, ClearTypingStyle };
    137         if (userTriggered == UserTriggered)
    138             options.add({ RevealSelection, FireSelectEvent, IsUserTriggered });
    139         return options;
    140     }
     126    static constexpr OptionSet<SetSelectionOption> defaultSetSelectionOptions(EUserTriggered = NotUserTriggered);
    141127
    142128    WEBCORE_EXPORT explicit FrameSelection(Document* = nullptr);
     
    181167    void setExtent(const Position&, Affinity, EUserTriggered = NotUserTriggered);
    182168
    183     // Return the renderer that is responsible for painting the caret (in the selection start node)
     169    // Return the renderer that is responsible for painting the caret (in the selection start node).
    184170    RenderBlock* caretRendererWithoutUpdatingLayout() const;
    185171
    186     // Bounds of (possibly transformed) caret in absolute coords
     172    // Bounds of possibly-transformed caret in absolute coordinates.
    187173    WEBCORE_EXPORT IntRect absoluteCaretBounds(bool* insideFixed = nullptr);
    188174    void setCaretRectNeedsUpdate() { CaretBase::setCaretRectNeedsUpdate(); }
     
    208194    bool isCaretBlinkingSuspended() const { return m_isCaretBlinkingSuspended; }
    209195
    210     // Focus
    211196    void setFocused(bool);
    212197    bool isFocused() const { return m_focused; }
     
    214199    void pageActivationChanged();
    215200
    216     // Painting.
    217201    WEBCORE_EXPORT void updateAppearance();
    218202
     
    240224    void setUpdateAppearanceEnabled(bool enabled) { m_updateAppearanceEnabled = enabled; }
    241225    void suppressScrolling() { ++m_scrollingSuppressCount; }
    242     void restoreScrolling()
    243     {
    244         ASSERT(m_scrollingSuppressCount);
    245         --m_scrollingSuppressCount;
    246     }
     226    void restoreScrolling();
    247227#endif
    248228
     
    272252    void setShouldShowBlockCursor(bool);
    273253
     254    RefPtr<Range> associatedLiveRange();
     255    void associateLiveRange(Range&);
     256    void disassociateLiveRange();
     257    void updateFromAssociatedLiveRange();
     258
    274259private:
    275     enum EPositionType { START, END, BASE, EXTENT };
    276 
    277260    void updateAndRevealSelection(const AXTextStateChangeIntent&);
    278261    void updateDataDetectorsForSelection();
     
    298281    VisiblePosition modifyMovingBackward(TextGranularity, bool* reachedBoundary = nullptr);
    299282
    300     LayoutUnit lineDirectionPointForBlockDirectionNavigation(EPositionType);
     283    enum PositionType : uint8_t { Start, End, Extent };
     284    LayoutUnit lineDirectionPointForBlockDirectionNavigation(PositionType);
    301285
    302286    AXTextStateChangeIntent textSelectionIntent(EAlteration, SelectionDirection, TextGranularity);
    303 #if ENABLE(ACCESSIBILITY)
    304287    void notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&);
    305 #else
    306     void notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&) { }
    307 #endif
    308288
    309289    void updateSelectionCachesIfSelectionIsInsideTextFormControl(EUserTriggered);
     
    329309#endif
    330310
    331     Document* m_document;
    332 
    333     LayoutUnit m_xPosForVerticalArrowNavigation;
    334 
     311    void updateAssociatedLiveRange();
     312
     313    WeakPtr<Document> m_document;
     314    RefPtr<Range> m_associatedLiveRange;
     315    Optional<LayoutUnit> m_xPosForVerticalArrowNavigation;
    335316    VisibleSelection m_selection;
    336     VisiblePosition m_originalBase; // Used to store base before the adjustment at bidi boundary
    337     TextGranularity m_granularity;
     317    VisiblePosition m_originalBase; // Used to store base before the adjustment at bidi boundary.
     318    TextGranularity m_granularity { TextGranularity::CharacterGranularity };
    338319
    339320    RefPtr<Node> m_previousCaretNode; // The last node which painted the caret. Retained for clearing the old caret when it moves.
     
    368349};
    369350
     351constexpr auto FrameSelection::defaultSetSelectionOptions(EUserTriggered userTriggered) -> OptionSet<SetSelectionOption>
     352{
     353    OptionSet<SetSelectionOption> options { CloseTyping, ClearTypingStyle };
     354    if (userTriggered == UserTriggered)
     355        options.add({ RevealSelection, FireSelectEvent, IsUserTriggered });
     356    return options;
     357}
     358
    370359inline EditingStyle* FrameSelection::typingStyle() const
    371360{
     
    378367}
    379368
    380 #if !(PLATFORM(COCOA) || USE(ATK))
    381 #if ENABLE(ACCESSIBILITY)
     369#if !(ENABLE(ACCESSIBILITY) && (PLATFORM(COCOA) || USE(ATK)))
     370
    382371inline void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&)
    383372{
    384373}
    385 #endif
     374
     375#endif
     376
     377#if PLATFORM(IOS_FAMILY)
     378
     379inline void FrameSelection::restoreScrolling()
     380{
     381    ASSERT(m_scrollingSuppressCount);
     382    --m_scrollingSuppressCount;
     383}
     384
    386385#endif
    387386
     
    389388
    390389#if ENABLE(TREE_DEBUGGING)
     390
    391391// Outside the WebCore namespace for ease of invocation from the debugger.
    392392void showTree(const WebCore::FrameSelection&);
    393393void showTree(const WebCore::FrameSelection*);
    394 #endif
     394
     395#endif
  • trunk/Source/WebCore/page/DOMSelection.cpp

    r266618 r267220  
    11/*
    2  * Copyright (C) 2007, 2009, 2016 Apple Inc. All rights reserved.
     2 * Copyright (C) 2007-2020 Apple Inc. All rights reserved.
    33 * Copyright (C) 2012 Google Inc. All rights reserved.
    44 *
     
    3636#include "FrameSelection.h"
    3737#include "Range.h"
     38#include "Settings.h"
    3839#include "TextIterator.h"
    3940
    4041namespace WebCore {
    4142
    42 static Node* selectionShadowAncestor(Frame& frame)
    43 {
     43static RefPtr<Node> selectionShadowAncestor(Frame& frame)
     44{
     45    ASSERT(!frame.settings().liveRangeSelectionEnabled());
    4446    auto* node = frame.selection().selection().base().anchorNode();
    45     if (!node)
     47    if (!node || !node->isInShadowTree())
    4648        return nullptr;
    47     if (!node->isInShadowTree())
    48         return nullptr;
    49     // FIXME: Unclear on why this needs to be the possibly null frame.document() instead of the never null node->document().
    50     return frame.document()->ancestorNodeInThisScope(node);
     49    return node->document().ancestorNodeInThisScope(node);
    5150}
    5251
     
    5655}
    5756
    58 const VisibleSelection& DOMSelection::visibleSelection() const
    59 {
    60     ASSERT(frame());
    61     return frame()->selection().selection();
    62 }
    63 
    64 static Position anchorPosition(const VisibleSelection& selection)
    65 {
    66     auto anchor = selection.isBaseFirst() ? selection.start() : selection.end();
    67     return anchor.parentAnchoredEquivalent();
    68 }
    69 
    70 static Position focusPosition(const VisibleSelection& selection)
    71 {
    72     auto focus = selection.isBaseFirst() ? selection.end() : selection.start();
    73     return focus.parentAnchoredEquivalent();
    74 }
    75 
    76 static Position basePosition(const VisibleSelection& selection)
    77 {
    78     return selection.base().parentAnchoredEquivalent();
    79 }
    80 
    81 static Position extentPosition(const VisibleSelection& selection)
    82 {
    83     return selection.extent().parentAnchoredEquivalent();
    84 }
    85 
    86 Node* DOMSelection::anchorNode() const
    87 {
    88     if (!frame())
    89         return nullptr;
    90     return shadowAdjustedNode(anchorPosition(visibleSelection()));
     57Ref<DOMSelection> DOMSelection::create(DOMWindow& window)
     58{
     59    return adoptRef(*new DOMSelection(window));
     60}
     61
     62RefPtr<Frame> DOMSelection::frame() const
     63{
     64    return DOMWindowProperty::frame();
     65}
     66
     67Optional<SimpleRange> DOMSelection::range() const
     68{
     69    auto frame = this->frame();
     70    if (!frame)
     71        return WTF::nullopt;
     72    auto range = frame->selection().selection().firstRange();
     73    if (!range || range->start.container->isInShadowTree())
     74        return WTF::nullopt;
     75    return range;
     76}
     77
     78Position DOMSelection::anchorPosition() const
     79{
     80    auto frame = this->frame();
     81    if (!frame)
     82        return { };
     83    auto& selection = frame->selection().selection();
     84    return (selection.isBaseFirst() ? selection.start() : selection.end()).parentAnchoredEquivalent();
     85}
     86
     87Position DOMSelection::focusPosition() const
     88{
     89    auto frame = this->frame();
     90    if (!frame)
     91        return { };
     92    auto& selection = frame->selection().selection();
     93    return (selection.isBaseFirst() ? selection.end() : selection.start()).parentAnchoredEquivalent();
     94}
     95
     96Position DOMSelection::basePosition() const
     97{
     98    auto frame = this->frame();
     99    if (!frame)
     100        return { };
     101    return frame->selection().selection().base().parentAnchoredEquivalent();
     102}
     103
     104Position DOMSelection::extentPosition() const
     105{
     106    auto frame = this->frame();
     107    if (!frame)
     108        return { };
     109    return frame->selection().selection().extent().parentAnchoredEquivalent();
     110}
     111
     112RefPtr<Node> DOMSelection::anchorNode() const
     113{
     114    return shadowAdjustedNode(anchorPosition());
    91115}
    92116
    93117unsigned DOMSelection::anchorOffset() const
    94118{
    95     if (!frame())
    96         return 0;
    97     return shadowAdjustedOffset(anchorPosition(visibleSelection()));
    98 }
    99 
    100 Node* DOMSelection::focusNode() const
    101 {
    102     if (!frame())
    103         return nullptr;
    104     return shadowAdjustedNode(focusPosition(visibleSelection()));
     119    return shadowAdjustedOffset(anchorPosition());
     120}
     121
     122RefPtr<Node> DOMSelection::focusNode() const
     123{
     124    return shadowAdjustedNode(focusPosition());
    105125}
    106126
    107127unsigned DOMSelection::focusOffset() const
    108128{
    109     if (!frame())
    110         return 0;
    111     return shadowAdjustedOffset(focusPosition(visibleSelection()));
    112 }
    113 
    114 Node* DOMSelection::baseNode() const
    115 {
    116     if (!frame())
    117         return nullptr;
    118     return shadowAdjustedNode(basePosition(visibleSelection()));
     129    return shadowAdjustedOffset(focusPosition());
     130}
     131
     132RefPtr<Node> DOMSelection::baseNode() const
     133{
     134    return shadowAdjustedNode(basePosition());
    119135}
    120136
    121137unsigned DOMSelection::baseOffset() const
    122138{
    123     if (!frame())
    124         return 0;
    125     return shadowAdjustedOffset(basePosition(visibleSelection()));
    126 }
    127 
    128 Node* DOMSelection::extentNode() const
    129 {
    130     if (!frame())
    131         return nullptr;
    132     return shadowAdjustedNode(extentPosition(visibleSelection()));
     139    return shadowAdjustedOffset(basePosition());
     140}
     141
     142RefPtr<Node> DOMSelection::extentNode() const
     143{
     144    return shadowAdjustedNode(extentPosition());
    133145}
    134146
    135147unsigned DOMSelection::extentOffset() const
    136148{
    137     if (!frame())
    138         return 0;
    139     return shadowAdjustedOffset(extentPosition(visibleSelection()));
     149    return shadowAdjustedOffset(extentPosition());
    140150}
    141151
    142152bool DOMSelection::isCollapsed() const
    143153{
    144     auto* frame = this->frame();
    145     if (!frame || selectionShadowAncestor(*frame))
     154    auto frame = this->frame();
     155    if (!frame)
    146156        return true;
    147     return !frame->selection().isRange();
     157    auto range = this->range();
     158    return !range || range->collapsed();
    148159}
    149160
    150161String DOMSelection::type() const
    151162{
    152     auto* frame = this->frame();
     163    auto frame = this->frame();
    153164    if (!frame)
    154165        return "None"_s;
     
    163174unsigned DOMSelection::rangeCount() const
    164175{
    165     auto* frame = this->frame();
     176    auto frame = this->frame();
    166177    return !frame || frame->selection().isNone() ? 0 : 1;
    167178}
    168179
    169 void DOMSelection::collapse(Node* node, unsigned offset)
    170 {
    171     if (!isValidForPosition(node))
    172         return;
    173 
    174     Ref<Frame> protectedFrame(*frame());
    175     protectedFrame->selection().moveTo(createLegacyEditingPosition(node, offset), Affinity::Downstream);
     180ExceptionOr<void> DOMSelection::collapse(Node* node, unsigned offset)
     181{
     182    auto frame = this->frame();
     183    if (!frame)
     184        return { };
     185    if (frame->settings().liveRangeSelectionEnabled()) {
     186        if (!node) {
     187            removeAllRanges();
     188            return { };
     189        }
     190        auto& document = *frame->document();
     191        if (!document.contains(*node))
     192            return { };
     193        if (auto result = Range::checkNodeOffsetPair(*node, offset); result.hasException())
     194            return result.releaseException();
     195    } else {
     196        if (!isValidForPosition(node))
     197            return { };
     198    }
     199    auto& selection = frame->selection();
     200    selection.disassociateLiveRange();
     201    selection.moveTo(makeContainerOffsetPosition(node, offset), Affinity::Downstream);
     202    return { };
    176203}
    177204
    178205ExceptionOr<void> DOMSelection::collapseToEnd()
    179206{
    180     auto* frame = this->frame();
     207    auto frame = this->frame();
    181208    if (!frame)
    182209        return { };
     
    184211    if (selection.isNone())
    185212        return Exception { InvalidStateError };
    186 
    187     Ref<Frame> protector(*frame);
     213    selection.disassociateLiveRange();
    188214    selection.moveTo(selection.selection().end(), Affinity::Downstream);
    189215    return { };
     
    192218ExceptionOr<void> DOMSelection::collapseToStart()
    193219{
    194     auto* frame = this->frame();
     220    auto frame = this->frame();
    195221    if (!frame)
    196222        return { };
     
    198224    if (selection.isNone())
    199225        return Exception { InvalidStateError };
    200 
    201     Ref<Frame> protector(*frame);
     226    selection.disassociateLiveRange();
    202227    selection.moveTo(selection.selection().start(), Affinity::Downstream);
    203228    return { };
     
    206231void DOMSelection::empty()
    207232{
    208     auto* frame = this->frame();
    209     if (!frame)
    210         return;
    211     frame->selection().clear();
    212 }
    213 
    214 void DOMSelection::setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset)
    215 {
    216     if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode))
    217         return;
    218 
    219     Ref<Frame> protectedFrame(*frame());
    220     protectedFrame->selection().moveTo(createLegacyEditingPosition(baseNode, baseOffset), createLegacyEditingPosition(extentNode, extentOffset), Affinity::Downstream);
    221 }
    222 
    223 void DOMSelection::setPosition(Node* node, unsigned offset)
    224 {
    225     if (!isValidForPosition(node))
    226         return;
    227 
    228     Ref<Frame> protectedFrame(*frame());
    229     protectedFrame->selection().moveTo(createLegacyEditingPosition(node, offset), Affinity::Downstream);
     233    removeAllRanges();
     234}
     235
     236ExceptionOr<void> DOMSelection::setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset)
     237{
     238    auto frame = this->frame();
     239    if (!frame)
     240        return { };
     241    if (frame->settings().liveRangeSelectionEnabled()) {
     242        // FIXME: We should do this by making the arguments non-nullable in the IDL file, once liveRangeSelectionEnabled is always true.
     243        if (!baseNode || !extentNode)
     244            return Exception { TypeError };
     245        auto& document = *frame->document();
     246        if (!document.contains(*baseNode) || !document.contains(*extentNode))
     247            return { };
     248        if (auto result = Range::checkNodeOffsetPair(*baseNode, baseOffset); result.hasException())
     249            return result.releaseException();
     250        if (auto result = Range::checkNodeOffsetPair(*extentNode, extentOffset); result.hasException())
     251            return result.releaseException();
     252    } else {
     253        if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode))
     254            return { };
     255    }
     256    auto& selection = frame->selection();
     257    selection.disassociateLiveRange();
     258    selection.moveTo(makeContainerOffsetPosition(baseNode, baseOffset), makeContainerOffsetPosition(extentNode, extentOffset), Affinity::Downstream);
     259    return { };
     260}
     261
     262ExceptionOr<void> DOMSelection::setPosition(Node* node, unsigned offset)
     263{
     264    return collapse(node, offset);
    230265}
    231266
    232267void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
    233268{
    234     auto* frame = this->frame();
    235     if (!frame)
    236         return;
    237 
    238269    FrameSelection::EAlteration alter;
    239270    if (equalLettersIgnoringASCIICase(alterString, "extend"))
     
    278309        return;
    279310
    280     Ref<Frame> protector(*frame);
    281     frame->selection().modify(alter, direction, granularity);
     311    if (auto frame = this->frame())
     312        frame->selection().modify(alter, direction, granularity);
    282313}
    283314
    284315ExceptionOr<void> DOMSelection::extend(Node& node, unsigned offset)
    285316{
    286     auto frame = makeRefPtr(this->frame());
    287     if (!frame)
    288         return { };
    289     if (offset > node.length())
    290         return Exception { IndexSizeError };
    291     if (!isValidForPosition(&node))
    292         return { };
    293     frame->selection().setExtent(createLegacyEditingPosition(&node, offset), Affinity::Downstream);
     317    auto frame = this->frame();
     318    if (!frame)
     319        return { };
     320    if (frame->settings().liveRangeSelectionEnabled()) {
     321        if (!frame->document()->contains(node))
     322            return { };
     323        if (auto result = Range::checkNodeOffsetPair(node, offset); result.hasException())
     324            return result.releaseException();
     325    } else {
     326        if (offset > node.length())
     327            return Exception { IndexSizeError };
     328        if (!isValidForPosition(&node))
     329            return { };
     330    }
     331    auto& selection = frame->selection();
     332    selection.disassociateLiveRange();
     333    selection.setExtent(makeContainerOffsetPosition(&node, offset), Affinity::Downstream);
    294334    return { };
    295335}
     
    299339    if (index >= rangeCount())
    300340        return Exception { IndexSizeError };
    301     auto& frame = *this->frame();
     341    auto frame = this->frame().releaseNonNull();
     342    if (frame->settings().liveRangeSelectionEnabled())
     343        return frame->selection().associatedLiveRange().releaseNonNull();
    302344    if (auto shadowAncestor = selectionShadowAncestor(frame))
    303345        return createLiveRange(makeSimpleRange(*makeBoundaryPointBeforeNode(*shadowAncestor)));
    304     return createLiveRange(*frame.selection().selection().firstRange());
     346    return createLiveRange(*frame->selection().selection().firstRange());
    305347}
    306348
    307349void DOMSelection::removeAllRanges()
    308350{
    309     auto* frame = this->frame();
     351    auto frame = this->frame();
    310352    if (!frame)
    311353        return;
     
    315357void DOMSelection::addRange(Range& liveRange)
    316358{
    317     auto frame = makeRefPtr(this->frame());
    318     if (!frame)
    319         return;
    320 
     359    auto frame = this->frame();
     360    if (!frame)
     361        return;
     362    auto& selection = frame->selection();
     363    if (frame->settings().liveRangeSelectionEnabled()) {
     364        if (selection.isNone())
     365            selection.associateLiveRange(liveRange);
     366        return;
     367    }
    321368    auto range = makeSimpleRange(liveRange);
    322     auto& selection = frame->selection();
    323 
    324369    if (auto selectedRange = selection.selection().toNormalizedRange()) {
    325370        if (!selectedRange->start.container->containingShadowRoot() && intersects(*selectedRange, range))
     
    327372        return;
    328373    }
    329 
    330374    selection.setSelection(range);
    331375}
    332376
     377ExceptionOr<void> DOMSelection::removeRange(Range& liveRange)
     378{
     379    auto frame = this->frame();
     380    if (!frame)
     381        return { };
     382    ASSERT(frame->settings().liveRangeSelectionEnabled());
     383    if (&liveRange != frame->selection().associatedLiveRange())
     384        return Exception { NotFoundError };
     385    removeAllRanges();
     386    return { };
     387}
     388
    333389void DOMSelection::deleteFromDocument()
    334390{
    335     auto frame = makeRefPtr(this->frame());
    336     if (!frame)
    337         return;
    338 
     391    auto frame = this->frame();
     392    if (!frame)
     393        return;
     394    if (frame->settings().liveRangeSelectionEnabled()) {
     395        if (auto range = frame->selection().associatedLiveRange())
     396            range->deleteContents();
     397        return;
     398    }
    339399    auto selectedRange = frame->selection().selection().toNormalizedRange();
    340400    if (!selectedRange || selectedRange->start.container->containingShadowRoot())
    341401        return;
    342 
    343402    createLiveRange(*selectedRange)->deleteContents();
    344403    frame->selection().setSelectedRange(makeSimpleRange(selectedRange->start), Affinity::Downstream, FrameSelection::ShouldCloseTyping::No);
     
    347406bool DOMSelection::containsNode(Node& node, bool allowPartial) const
    348407{
    349     // FIXME: This behavior does not match what the selection API standard specifies.
    350     if (node.isTextNode())
     408    // FIXME: This is wrong, and was added to work around anomalies caused when we canonicalize selection endpoints. We should fix that and remove this.
     409    if (node.isTextNode() && !node.document().settings().liveRangeSelectionEnabled())
    351410        allowPartial = true;
    352 
    353     auto* frame = this->frame();
    354     if (!frame)
    355         return false;
    356 
    357     auto selectedRange = frame->selection().selection().firstRange();
    358     if (!selectedRange || selectedRange->start.container->containingShadowRoot())
    359         return false;
    360 
    361     return allowPartial ? intersects(*selectedRange, node) : contains(*selectedRange, node);
     411    auto range = this->range();
     412    return range && (allowPartial ? intersects(*range, node) : contains(*range, node));
    362413}
    363414
     
    368419}
    369420
    370 String DOMSelection::toString()
    371 {
    372     auto* frame = this->frame();
     421String DOMSelection::toString() const
     422{
     423    auto frame = this->frame();
    373424    if (!frame)
    374425        return String();
    375     auto range = frame->selection().selection().toNormalizedRange();
     426    if (frame->settings().liveRangeSelectionEnabled()) {
     427        auto range = this->range();
     428        return range ? plainText(*range) : emptyString();
     429    }
     430    auto range = frame->selection().selection().firstRange();
    376431    return range ? plainText(*range) : emptyString();
    377432}
    378433
    379 Node* DOMSelection::shadowAdjustedNode(const Position& position) const
     434RefPtr<Node> DOMSelection::shadowAdjustedNode(const Position& position) const
    380435{
    381436    if (position.isNull())
    382437        return nullptr;
     438
     439    if (frame()->settings().liveRangeSelectionEnabled()) {
     440        auto node = position.containerNode();
     441        return !node || node->isInShadowTree() ? nullptr : node;
     442    }
    383443
    384444    auto* containerNode = position.containerNode();
     
    398458        return 0;
    399459
     460    if (frame()->settings().liveRangeSelectionEnabled())
     461        return shadowAdjustedNode(position) ? position.computeOffsetInContainerNode() : 0;
     462
    400463    auto* containerNode = position.containerNode();
    401464    auto* adjustedNode = frame()->document()->ancestorNodeInThisScope(containerNode);
     
    411474bool DOMSelection::isValidForPosition(Node* node) const
    412475{
    413     auto* frame = this->frame();
     476    auto frame = this->frame();
     477    ASSERT(!frame->settings().liveRangeSelectionEnabled());
    414478    return frame && (!node || &node->document() == frame->document());
    415479}
  • trunk/Source/WebCore/page/DOMSelection.h

    r236917 r267220  
    11/*
    2  * Copyright (C) 2007 Apple Inc. All rights reserved.
     2 * Copyright (C) 2007-2020 Apple Inc. All rights reserved.
    33 * Copyright (C) 2012 Google Inc. All rights reserved.
    44 *
     
    4343class VisibleSelection;
    4444
     45struct SimpleRange;
     46
    4547class DOMSelection : public RefCounted<DOMSelection>, public DOMWindowProperty {
    4648public:
    47     static Ref<DOMSelection> create(DOMWindow& window) { return adoptRef(*new DOMSelection(window)); }
     49    static Ref<DOMSelection> create(DOMWindow&);
    4850
    49     Node* baseNode() const;
    50     Node* extentNode() const;
     51    RefPtr<Node> baseNode() const;
     52    RefPtr<Node> extentNode() const;
    5153    unsigned baseOffset() const;
    5254    unsigned extentOffset() const;
    5355    String type() const;
    54     void setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset);
    55     void setPosition(Node*, unsigned offset);
     56    ExceptionOr<void> setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset);
     57    ExceptionOr<void> setPosition(Node*, unsigned offset);
    5658    void modify(const String& alter, const String& direction, const String& granularity);
    5759
     
    5961    // reflect the direction in which the selection was made by the user.
    6062    // The base and extent are different, because they don't reflect expansion.
    61     Node* anchorNode() const;
     63    RefPtr<Node> anchorNode() const;
    6264    unsigned anchorOffset() const;
    63     Node* focusNode() const;
     65    RefPtr<Node> focusNode() const;
    6466    unsigned focusOffset() const;
    6567    bool isCollapsed() const;
    6668    unsigned rangeCount() const;
    67     void collapse(Node*, unsigned offset);
     69    ExceptionOr<void> collapse(Node*, unsigned offset);
    6870    ExceptionOr<void> collapseToEnd();
    6971    ExceptionOr<void> collapseToStart();
     
    7274    void removeAllRanges();
    7375    void addRange(Range&);
     76    ExceptionOr<void> removeRange(Range&);
    7477    void deleteFromDocument();
    7578    bool containsNode(Node&, bool partlyContained) const;
    7679    void selectAllChildren(Node&);
    7780
    78     String toString();
     81    String toString() const;
    7982
    8083    void empty();
     
    8386    explicit DOMSelection(DOMWindow&);
    8487
    85     // Convenience method for accessors, caller must null-check frame().
    86     const VisibleSelection& visibleSelection() const;
     88    // FIXME: Change DOMWindowProperty::frame to return RefPtr and then delete this.
     89    RefPtr<Frame> frame() const;
     90    Optional<SimpleRange> range() const;
    8791
    88     Node* shadowAdjustedNode(const Position&) const;
     92    Position anchorPosition() const;
     93    Position focusPosition() const;
     94    Position basePosition() const;
     95    Position extentPosition() const;
     96
     97    RefPtr<Node> shadowAdjustedNode(const Position&) const;
    8998    unsigned shadowAdjustedOffset(const Position&) const;
    9099
  • trunk/Source/WebCore/page/DOMSelection.idl

    r266311 r267220  
    4242    readonly attribute unsigned long rangeCount;
    4343
    44     undefined collapse(Node? node, optional unsigned long offset = 0);
     44    readonly attribute DOMString type;
     45
     46    [MayThrowException] Range getRangeAt(unsigned long index);
     47    undefined addRange(Range range);
     48    [EnabledBySetting=LiveRangeSelection, MayThrowException] undefined removeRange(Range range);
     49    undefined removeAllRanges();
     50
     51    undefined empty();
     52
     53    [MayThrowException] undefined collapse(Node? node, optional unsigned long offset = 0);
     54    [MayThrowException] undefined setPosition(Node? node, optional unsigned long offset = 0);
     55
     56    [MayThrowException] undefined collapseToStart();
    4557    [MayThrowException] undefined collapseToEnd();
    46     [MayThrowException] undefined collapseToStart();
    47 
    48     [CEReactions] undefined deleteFromDocument();
    49     boolean containsNode(Node node, optional boolean allowPartial = false);
    50     undefined selectAllChildren(Node node);
    5158
    5259    [MayThrowException] undefined extend(Node node, optional unsigned long offset = 0);
    5360
    54     [MayThrowException] Range getRangeAt(unsigned long index);
    55     undefined removeAllRanges();
    56     undefined addRange(Range range);
     61    [MayThrowException] undefined setBaseAndExtent(Node? baseNode, unsigned long baseOffset, Node? extentNode, unsigned long extentOffset);
     62    undefined selectAllChildren(Node node);
     63
     64    [CEReactions] undefined deleteFromDocument();
     65    boolean containsNode(Node node, optional boolean allowPartialContainment = false);
    5766
    5867    [NotEnumerable] DOMString toString();
    5968
    60     readonly attribute DOMString type;
     69    // Non-standard methods and attributes.
    6170
    62     undefined setBaseAndExtent(Node? baseNode, unsigned long baseOffset, Node? extentNode, unsigned long extentOffset);
    63     undefined setPosition(Node? node, optional unsigned long offset = 0);
    64 
    65     undefined empty();
    66 
    67     // FIXME: The following operation should be implemented.
    68     // undefined removeRange(Range range);
    69 
    70     // FIXME: Using "undefined" as default parameter value is wrong.
    71     undefined modify(optional DOMString alter = "undefined", optional DOMString direction = "undefined", optional DOMString granularity = "undefined");
     71    undefined modify(optional DOMString alter, optional DOMString direction, optional DOMString granularity);
    7272
    7373    readonly attribute Node? baseNode;
  • trunk/Source/WebCore/page/Settings.yaml

    r267095 r267220  
    10731073  initial: false
    10741074
    1075 
    10761075isPostLoadCPUUsageMeasurementEnabled:
    10771076  initial: defaultPostLoadCPUUsageMeasurementEnabled
     
    10881087isPerActivityStateCPUUsageMeasurementEnabled:
    10891088  initial: defaultPerActivityStateCPUUsageMeasurementEnabled
     1089
     1090liveRangeSelectionEnabled:
     1091  initial: false
    10901092
    10911093# Deprecated
  • trunk/Source/WebKit/ChangeLog

    r267215 r267220  
     12020-09-16  Darin Adler  <darin@apple.com>
     2
     3        Selection API: Introduce LiveRangeSelectionEnabled, off by default
     4        https://bugs.webkit.org/show_bug.cgi?id=216656
     5
     6        Reviewed by Sam Weinig.
     7
     8        * Shared/WebPreferencesInternal.yaml: Added LiveRangeSelectionEnabled.
     9
    1102020-09-17  Tim Horton  <timothy_horton@apple.com>
    211
  • trunk/Source/WebKit/Shared/WebPreferencesInternal.yaml

    r267194 r267220  
    401401  humanReadableDescription: "Do all media loading and playback in the GPU Process"
    402402  webcoreName: useGPUProcessForMedia
     403
     404LiveRangeSelectionEnabled:
     405  type: bool
     406  defaultValue: false
     407  humanReadableName: "Live Ranges in Selection"
     408  humanReadableDescription: "Live range behavior for ranges in the Selection object"
     409  category: internal
Note: See TracChangeset for help on using the changeset viewer.