summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/editing/SelectionController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/editing/SelectionController.cpp')
-rw-r--r--Source/WebCore/editing/SelectionController.cpp164
1 files changed, 109 insertions, 55 deletions
diff --git a/Source/WebCore/editing/SelectionController.cpp b/Source/WebCore/editing/SelectionController.cpp
index a574b5a..65f9062 100644
--- a/Source/WebCore/editing/SelectionController.cpp
+++ b/Source/WebCore/editing/SelectionController.cpp
@@ -91,34 +91,53 @@ SelectionController::SelectionController(Frame* frame, bool isDragCaretControlle
void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered, CursorAlignOnScroll align)
{
- setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered, align);
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
+ setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), options, align);
}
void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered)
{
- setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), true, true, userTriggered);
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
+ setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), options);
}
void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered)
{
- setSelection(VisibleSelection(pos, affinity), true, true, userTriggered);
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
+ setSelection(VisibleSelection(pos, affinity), options);
}
void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered)
{
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity);
- setSelection(selection, true, true, userTriggered);
+ setSelection(selection, options);
}
void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered)
{
- setSelection(VisibleSelection(base, extent, affinity), true, true, userTriggered);
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
+ setSelection(VisibleSelection(base, extent, affinity), options);
}
-void SelectionController::setSelection(const VisibleSelection& s, bool closeTyping, bool shouldClearTypingStyle, bool userTriggered, CursorAlignOnScroll align, TextGranularity granularity, DirectionalityPolicy directionalityPolicy)
+void SelectionController::setSelection(const VisibleSelection& s, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity, DirectionalityPolicy directionalityPolicy)
{
m_granularity = granularity;
+ bool closeTyping = options & CloseTyping;
+ bool shouldClearTypingStyle = options & ClearTypingStyle;
+ bool userTriggered = options & UserTriggered;
+
setIsDirectional(directionalityPolicy == MakeDirectionalSelection);
if (m_isDragCaretController) {
@@ -134,7 +153,7 @@ void SelectionController::setSelection(const VisibleSelection& s, bool closeTypi
return;
}
- Node* baseNode = s.base().node();
+ Node* baseNode = s.base().deprecatedNode();
Document* document = 0;
if (baseNode)
document = baseNode->document();
@@ -142,10 +161,10 @@ void SelectionController::setSelection(const VisibleSelection& s, bool closeTypi
// <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at SelectionController::setSelection
// if document->frame() == m_frame we can get into an infinite loop
if (document && document->frame() && document->frame() != m_frame && document != m_frame->document()) {
- document->frame()->selection()->setSelection(s, closeTyping, shouldClearTypingStyle, userTriggered);
+ document->frame()->selection()->setSelection(s, options);
return;
}
-
+
if (closeTyping)
TypingCommand::closeTyping(m_frame->editor()->lastEditCommand());
@@ -171,7 +190,7 @@ void SelectionController::setSelection(const VisibleSelection& s, bool closeTypi
m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation;
selectFrameElementInParentIfFullySelected();
notifyRendererOfSelectionChange(userTriggered);
- m_frame->editor()->respondToChangedSelection(oldSelection, closeTyping);
+ m_frame->editor()->respondToChangedSelection(oldSelection, options);
if (userTriggered) {
ScrollAlignment alignment;
@@ -184,21 +203,22 @@ void SelectionController::setSelection(const VisibleSelection& s, bool closeTypi
}
notifyAccessibilityForSelectionChange();
+ m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
}
static bool removingNodeRemovesPosition(Node* node, const Position& position)
{
- if (!position.node())
+ if (!position.deprecatedNode())
return false;
- if (position.node() == node)
+ if (position.deprecatedNode() == node)
return true;
if (!node->isElementNode())
return false;
Element* element = static_cast<Element*>(node);
- return element->contains(position.node()) || element->contains(position.node()->shadowAncestorNode());
+ return element->contains(position.deprecatedNode()) || element->contains(position.deprecatedNode()->shadowAncestorNode());
}
void SelectionController::nodeWillBeRemoved(Node *node)
@@ -246,14 +266,14 @@ void SelectionController::respondToNodeModification(Node* node, bool baseRemoved
}
if (clearRenderTreeSelection) {
- RefPtr<Document> document = m_selection.start().node()->document();
+ RefPtr<Document> document = m_selection.start().anchorNode()->document();
document->updateStyleIfNeeded();
if (RenderView* view = toRenderView(document->renderer()))
view->clearSelection();
}
if (clearDOMTreeSelection)
- setSelection(VisibleSelection(), false, false);
+ setSelection(VisibleSelection(), 0);
}
enum EndPointType { EndPointIsStart, EndPointIsEnd };
@@ -263,12 +283,16 @@ static bool shouldRemovePositionAfterAdoptingTextReplacement(Position& position,
if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
return false;
- if (static_cast<unsigned>(position.offsetInContainerNode()) > offset && static_cast<unsigned>(position.offsetInContainerNode()) < offset + oldLength)
+ ASSERT(position.offsetInContainerNode() >= 0);
+ unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
+ if (positionOffset > offset && positionOffset < offset + oldLength)
return true;
- if ((type == EndPointIsStart && static_cast<unsigned>(position.offsetInContainerNode()) >= offset + oldLength)
- || (type == EndPointIsEnd && static_cast<unsigned>(position.offsetInContainerNode()) > offset + oldLength))
- position.moveToOffset(position.offsetInContainerNode() - oldLength + newLength);
+ // Adjust the offset if the position is after or at the end of the deleted contents (positionOffset >= offset + oldLength)
+ // to avoid having a stale offset except when the position is the end of selection and nothing is deleted, in which case,
+ // adjusting offset results in incorrectly extending the selection until the end of newly inserted contents.
+ if ((positionOffset > offset + oldLength) || (positionOffset == offset + oldLength && (type == EndPointIsStart || oldLength)))
+ position.moveToOffset(positionOffset - oldLength + newLength);
return false;
}
@@ -290,14 +314,18 @@ void SelectionController::textWillBeReplaced(CharacterData* node, unsigned offse
if ((base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end())
&& !shouldRemoveStart && !shouldRemoveEnd) {
+ VisibleSelection newSelection;
if (!shouldRemoveBase && !shouldRemoveExtent)
- m_selection.setWithoutValidation(base, extent);
+ newSelection.setWithoutValidation(base, extent);
else {
- if (m_selection.isBaseFirst())
- m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
+ if (newSelection.isBaseFirst())
+ newSelection.setWithoutValidation(start, end);
else
- m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
+ newSelection.setWithoutValidation(end, start);
}
+ m_frame->document()->updateLayout();
+ setSelection(newSelection, 0);
+ return;
}
respondToNodeModification(node, shouldRemoveBase, shouldRemoveExtent, shouldRemoveStart, shouldRemoveEnd);
@@ -359,7 +387,7 @@ void SelectionController::willBeModified(EAlteration alter, SelectionDirection d
TextDirection SelectionController::directionOfEnclosingBlock()
{
- Node* enclosingBlockNode = enclosingBlock(m_selection.extent().node());
+ Node* enclosingBlockNode = enclosingBlock(m_selection.extent().deprecatedNode());
if (!enclosingBlockNode)
return LTR;
RenderObject* renderer = enclosingBlockNode->renderer();
@@ -413,11 +441,16 @@ VisiblePosition SelectionController::modifyExtendingRight(TextGranularity granul
else
pos = previousWordPosition(pos);
break;
+ case LineBoundary:
+ if (directionOfEnclosingBlock() == LTR)
+ pos = modifyExtendingForward(granularity);
+ else
+ pos = modifyExtendingBackward(granularity);
+ break;
case SentenceGranularity:
case LineGranularity:
case ParagraphGranularity:
case SentenceBoundary:
- case LineBoundary:
case ParagraphBoundary:
case DocumentBoundary:
// FIXME: implement all of the above?
@@ -425,7 +458,7 @@ VisiblePosition SelectionController::modifyExtendingRight(TextGranularity granul
}
return pos;
}
-
+
VisiblePosition SelectionController::modifyExtendingForward(TextGranularity granularity)
{
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
@@ -566,11 +599,16 @@ VisiblePosition SelectionController::modifyExtendingLeft(TextGranularity granula
else
pos = nextWordPosition(pos);
break;
+ case LineBoundary:
+ if (directionOfEnclosingBlock() == LTR)
+ pos = modifyExtendingBackward(granularity);
+ else
+ pos = modifyExtendingForward(granularity);
+ break;
case SentenceGranularity:
case LineGranularity:
case ParagraphGranularity:
case SentenceBoundary:
- case LineBoundary:
case ParagraphBoundary:
case DocumentBoundary:
pos = modifyExtendingBackward(granularity);
@@ -767,7 +805,8 @@ bool SelectionController::modify(EAlteration alter, SelectionDirection direction
if (!m_frame || !m_frame->editor()->behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
setExtent(position, userTriggered);
else {
- if (direction == DirectionForward || direction == DirectionRight)
+ TextDirection textDirection = directionOfEnclosingBlock();
+ if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
setEnd(position, userTriggered);
else
setStart(position, userTriggered);
@@ -905,7 +944,7 @@ int SelectionController::xPosForVerticalArrowNavigation(EPositionType type)
break;
}
- Frame* frame = pos.node()->document()->frame();
+ Frame* frame = pos.anchorNode()->document()->frame();
if (!frame)
return x;
@@ -945,22 +984,34 @@ void SelectionController::setEnd(const VisiblePosition &pos, bool userTriggered)
void SelectionController::setBase(const VisiblePosition &pos, bool userTriggered)
{
- setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity()), true, true, userTriggered);
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
+ setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity()), options);
}
void SelectionController::setExtent(const VisiblePosition &pos, bool userTriggered)
{
- setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered);
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
+ setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity()), options);
}
void SelectionController::setBase(const Position &pos, EAffinity affinity, bool userTriggered)
{
- setSelection(VisibleSelection(pos, m_selection.extent(), affinity), true, true, userTriggered);
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
+ setSelection(VisibleSelection(pos, m_selection.extent(), affinity), options);
}
void SelectionController::setExtent(const Position &pos, EAffinity affinity, bool userTriggered)
{
- setSelection(VisibleSelection(m_selection.base(), pos, affinity), true, true, userTriggered);
+ SetSelectionOptions options = CloseTyping | ClearTypingStyle;
+ if (userTriggered)
+ options |= UserTriggered;
+ setSelection(VisibleSelection(m_selection.base(), pos, affinity), options);
}
void SelectionController::setCaretRectNeedsUpdate(bool flag)
@@ -970,19 +1021,19 @@ void SelectionController::setCaretRectNeedsUpdate(bool flag)
void SelectionController::updateCaretRect()
{
- if (isNone() || !m_selection.start().node()->inDocument() || !m_selection.end().node()->inDocument()) {
+ if (isNone() || !m_selection.start().anchorNode()->inDocument() || !m_selection.end().anchorNode()->inDocument()) {
m_caretRect = IntRect();
return;
}
- m_selection.start().node()->document()->updateStyleIfNeeded();
+ m_selection.start().anchorNode()->document()->updateStyleIfNeeded();
m_caretRect = IntRect();
if (isCaret()) {
VisiblePosition pos(m_selection.start(), m_selection.affinity());
if (pos.isNotNull()) {
- ASSERT(pos.deepEquivalent().node()->renderer());
+ ASSERT(pos.deepEquivalent().deprecatedNode()->renderer());
// First compute a rect local to the renderer at the selection start
RenderObject* renderer;
@@ -1016,7 +1067,7 @@ void SelectionController::updateCaretRect()
RenderObject* SelectionController::caretRenderer() const
{
- Node* node = m_selection.start().node();
+ Node* node = m_selection.start().deprecatedNode();
if (!node)
return 0;
@@ -1123,7 +1174,7 @@ void SelectionController::invalidateCaretRect()
if (!isCaret())
return;
- Document* d = m_selection.start().node()->document();
+ Document* d = m_selection.start().anchorNode()->document();
// recomputeCaretRect will always return false for the drag caret,
// because its m_frame is always 0.
@@ -1205,9 +1256,9 @@ void SelectionController::debugRenderer(RenderObject *r, bool selected) const
int textLength = text.length();
if (selected) {
int offset = 0;
- if (r->node() == m_selection.start().node())
+ if (r->node() == m_selection.start().deprecatedNode())
offset = m_selection.start().deprecatedEditingOffset();
- else if (r->node() == m_selection.end().node())
+ else if (r->node() == m_selection.end().deprecatedNode())
offset = m_selection.end().deprecatedEditingOffset();
int pos;
@@ -1309,7 +1360,7 @@ void SelectionController::selectFrameElementInParentIfFullySelected()
return;
// Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
- Element* ownerElement = m_frame->document()->ownerElement();
+ Element* ownerElement = m_frame->ownerElement();
if (!ownerElement)
return;
ContainerNode* ownerElementParent = ownerElement->parentNode();
@@ -1322,8 +1373,8 @@ void SelectionController::selectFrameElementInParentIfFullySelected()
// Create compute positions before and after the element.
unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
- VisiblePosition beforeOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex, SEL_DEFAULT_AFFINITY));
- VisiblePosition afterOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex + 1, VP_UPSTREAM_IF_POSSIBLE));
+ VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
+ VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
// Focus on the parent frame, and then select from before this element to after.
VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
@@ -1394,15 +1445,18 @@ bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, boo
return false;
// FIXME: Can we provide extentAffinity?
- VisiblePosition visibleStart(startContainer, startOffset, collapsed ? affinity : DOWNSTREAM);
- VisiblePosition visibleEnd(endContainer, endOffset, SEL_DEFAULT_AFFINITY);
- setSelection(VisibleSelection(visibleStart, visibleEnd), closeTyping);
+ VisiblePosition visibleStart(Position(startContainer, startOffset, Position::PositionIsOffsetInAnchor), collapsed ? affinity : DOWNSTREAM);
+ VisiblePosition visibleEnd(Position(endContainer, endOffset, Position::PositionIsOffsetInAnchor), SEL_DEFAULT_AFFINITY);
+ SetSelectionOptions options = ClearTypingStyle;
+ if (closeTyping)
+ options |= CloseTyping;
+ setSelection(VisibleSelection(visibleStart, visibleEnd), options);
return true;
}
bool SelectionController::isInPasswordField() const
{
- Node* startNode = start().node();
+ Node* startNode = start().deprecatedNode();
if (!startNode)
return false;
@@ -1548,8 +1602,8 @@ void SelectionController::updateAppearance()
// We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
// because we don't yet notify the SelectionController of text removal.
if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
- RenderObject* startRenderer = startPos.node()->renderer();
- RenderObject* endRenderer = endPos.node()->renderer();
+ RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
+ RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
}
}
@@ -1650,7 +1704,7 @@ void SelectionController::paintDragCaret(GraphicsContext* p, int tx, int ty, con
#if ENABLE(TEXT_CARET)
SelectionController* dragCaretController = m_frame->page()->dragCaretController();
ASSERT(dragCaretController->selection().isCaret());
- if (dragCaretController->selection().start().node()->document()->frame() == m_frame)
+ if (dragCaretController->selection().start().anchorNode()->document()->frame() == m_frame)
dragCaretController->paintCaret(p, tx, ty, clipRect);
#else
UNUSED_PARAM(p);
@@ -1724,7 +1778,7 @@ HTMLFormElement* SelectionController::currentForm() const
// Start looking either at the active (first responder) node, or where the selection is.
Node* start = m_frame->document()->focusedNode();
if (!start)
- start = this->start().node();
+ start = this->start().deprecatedNode();
// Try walking up the node tree to find a form element.
Node* node;
@@ -1755,12 +1809,12 @@ void SelectionController::revealSelection(const ScrollAlignment& alignment, bool
}
Position start = this->start();
- ASSERT(start.node());
- if (start.node() && start.node()->renderer()) {
+ ASSERT(start.deprecatedNode());
+ if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
// FIXME: This code only handles scrolling the startContainer's layer, but
// the selection rect could intersect more than just that.
// See <rdar://problem/4799899>.
- if (RenderLayer* layer = start.node()->renderer()->enclosingLayer()) {
+ if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) {
layer->scrollRectToVisible(rect, false, alignment, alignment);
updateAppearance();
}
@@ -1774,7 +1828,7 @@ void SelectionController::setSelectionFromNone()
Document* document = m_frame->document();
bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
- if (!isNone() || !(m_frame->isContentEditable() || caretBrowsing))
+ if (!isNone() || !(document->inDesignMode() || caretBrowsing))
return;
Node* node = document->documentElement();