diff options
Diffstat (limited to 'Source/WebCore/page/DOMSelection.cpp')
-rw-r--r-- | Source/WebCore/page/DOMSelection.cpp | 383 |
1 files changed, 161 insertions, 222 deletions
diff --git a/Source/WebCore/page/DOMSelection.cpp b/Source/WebCore/page/DOMSelection.cpp index b0875d97c..4a1bd66b2 100644 --- a/Source/WebCore/page/DOMSelection.cpp +++ b/Source/WebCore/page/DOMSelection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009, 2016 Apple Inc. All rights reserved. * Copyright (C) 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -35,38 +35,28 @@ #include "ExceptionCode.h" #include "Frame.h" #include "FrameSelection.h" -#include "Node.h" #include "Range.h" #include "TextIterator.h" -#include "TreeScope.h" #include "htmlediting.h" -#include <wtf/text/WTFString.h> namespace WebCore { -static Node* selectionShadowAncestor(Frame* frame) +static Node* selectionShadowAncestor(Frame& frame) { - Node* node = frame->selection().selection().base().anchorNode(); + auto* node = frame.selection().selection().base().anchorNode(); if (!node) - return 0; - + return nullptr; if (!node->isInShadowTree()) - return 0; - - return frame->document()->ancestorInThisScope(node); + return nullptr; + // FIXME: Unclear on why this needs to be the possibly null frame.document() instead of the never null node->document(). + return frame.document()->ancestorNodeInThisScope(node); } -DOMSelection::DOMSelection(const TreeScope* treeScope) - : DOMWindowProperty(treeScope->rootNode()->document().frame()) - , m_treeScope(treeScope) +DOMSelection::DOMSelection(Frame& frame) + : DOMWindowProperty(&frame) { } -void DOMSelection::clearTreeScope() -{ - m_treeScope = 0; -} - const VisibleSelection& DOMSelection::visibleSelection() const { ASSERT(m_frame); @@ -75,13 +65,13 @@ const VisibleSelection& DOMSelection::visibleSelection() const static Position anchorPosition(const VisibleSelection& selection) { - Position anchor = selection.isBaseFirst() ? selection.start() : selection.end(); + auto anchor = selection.isBaseFirst() ? selection.start() : selection.end(); return anchor.parentAnchoredEquivalent(); } static Position focusPosition(const VisibleSelection& selection) { - Position focus = selection.isBaseFirst() ? selection.end() : selection.start(); + auto focus = selection.isBaseFirst() ? selection.end() : selection.start(); return focus.parentAnchoredEquivalent(); } @@ -99,31 +89,27 @@ Node* DOMSelection::anchorNode() const { if (!m_frame) return 0; - return shadowAdjustedNode(anchorPosition(visibleSelection())); } -int DOMSelection::anchorOffset() const +unsigned DOMSelection::anchorOffset() const { if (!m_frame) return 0; - return shadowAdjustedOffset(anchorPosition(visibleSelection())); } Node* DOMSelection::focusNode() const { if (!m_frame) - return 0; - + return nullptr; return shadowAdjustedNode(focusPosition(visibleSelection())); } -int DOMSelection::focusOffset() const +unsigned DOMSelection::focusOffset() const { if (!m_frame) return 0; - return shadowAdjustedOffset(focusPosition(visibleSelection())); } @@ -131,15 +117,13 @@ Node* DOMSelection::baseNode() const { if (!m_frame) return 0; - return shadowAdjustedNode(basePosition(visibleSelection())); } -int DOMSelection::baseOffset() const +unsigned DOMSelection::baseOffset() const { if (!m_frame) return 0; - return shadowAdjustedOffset(basePosition(visibleSelection())); } @@ -147,21 +131,19 @@ Node* DOMSelection::extentNode() const { if (!m_frame) return 0; - return shadowAdjustedNode(extentPosition(visibleSelection())); } -int DOMSelection::extentOffset() const +unsigned DOMSelection::extentOffset() const { if (!m_frame) return 0; - return shadowAdjustedOffset(extentPosition(visibleSelection())); } bool DOMSelection::isCollapsed() const { - if (!m_frame || selectionShadowAncestor(m_frame)) + if (!m_frame || selectionShadowAncestor(*m_frame)) return true; return !m_frame->selection().isRange(); } @@ -169,72 +151,53 @@ bool DOMSelection::isCollapsed() const String DOMSelection::type() const { if (!m_frame) - return String(); - - FrameSelection& selection = m_frame->selection(); - - // This is a WebKit DOM extension, incompatible with an IE extension - // IE has this same attribute, but returns "none", "text" and "control" - // http://msdn.microsoft.com/en-us/library/ms534692(VS.85).aspx + return ASCIILiteral("None"); + auto& selection = m_frame->selection(); if (selection.isNone()) - return "None"; + return ASCIILiteral("None"); if (selection.isCaret()) - return "Caret"; - return "Range"; + return ASCIILiteral("Caret"); + return ASCIILiteral("Range"); } -int DOMSelection::rangeCount() const +unsigned DOMSelection::rangeCount() const { - if (!m_frame) - return 0; - return m_frame->selection().isNone() ? 0 : 1; + return !m_frame || m_frame->selection().isNone() ? 0 : 1; } -void DOMSelection::collapse(Node* node, int offset, ExceptionCode& ec) +void DOMSelection::collapse(Node* node, unsigned offset) { - if (!m_frame) - return; - - if (offset < 0) { - ec = INDEX_SIZE_ERR; - return; - } - if (!isValidForPosition(node)) return; - // FIXME: Eliminate legacy editing positions - m_frame->selection().moveTo(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM)); + Ref<Frame> protector(*m_frame); + m_frame->selection().moveTo(createLegacyEditingPosition(node, offset), DOWNSTREAM); } -void DOMSelection::collapseToEnd(ExceptionCode& ec) +ExceptionOr<void> DOMSelection::collapseToEnd() { if (!m_frame) - return; - - const VisibleSelection& selection = m_frame->selection().selection(); - - if (selection.isNone()) { - ec = INVALID_STATE_ERR; - return; - } + return { }; + auto& selection = m_frame->selection(); + if (selection.isNone()) + return Exception { INVALID_STATE_ERR }; - m_frame->selection().moveTo(VisiblePosition(selection.end(), DOWNSTREAM)); + Ref<Frame> protector(*m_frame); + selection.moveTo(selection.selection().end(), DOWNSTREAM); + return { }; } -void DOMSelection::collapseToStart(ExceptionCode& ec) +ExceptionOr<void> DOMSelection::collapseToStart() { if (!m_frame) - return; - - const VisibleSelection& selection = m_frame->selection().selection(); - - if (selection.isNone()) { - ec = INVALID_STATE_ERR; - return; - } + return { }; + auto& selection = m_frame->selection(); + if (selection.isNone()) + return Exception { INVALID_STATE_ERR }; - m_frame->selection().moveTo(VisiblePosition(selection.start(), DOWNSTREAM)); + Ref<Frame> protector(*m_frame); + selection.moveTo(selection.selection().start(), DOWNSTREAM); + return { }; } void DOMSelection::empty() @@ -244,40 +207,22 @@ void DOMSelection::empty() m_frame->selection().clear(); } -void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode& ec) +void DOMSelection::setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset) { - if (!m_frame) - return; - - if (baseOffset < 0 || extentOffset < 0) { - ec = INDEX_SIZE_ERR; - return; - } - if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) return; - // FIXME: Eliminate legacy editing positions - VisiblePosition visibleBase = VisiblePosition(createLegacyEditingPosition(baseNode, baseOffset), DOWNSTREAM); - VisiblePosition visibleExtent = VisiblePosition(createLegacyEditingPosition(extentNode, extentOffset), DOWNSTREAM); - - m_frame->selection().moveTo(visibleBase, visibleExtent); + Ref<Frame> protector(*m_frame); + m_frame->selection().moveTo(createLegacyEditingPosition(baseNode, baseOffset), createLegacyEditingPosition(extentNode, extentOffset), DOWNSTREAM); } -void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec) +void DOMSelection::setPosition(Node* node, unsigned offset) { - if (!m_frame) - return; - if (offset < 0) { - ec = INDEX_SIZE_ERR; - return; - } - if (!isValidForPosition(node)) return; - // FIXME: Eliminate legacy editing positions - m_frame->selection().moveTo(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM)); + Ref<Frame> protector(*m_frame); + m_frame->selection().moveTo(createLegacyEditingPosition(node, offset), DOWNSTREAM); } void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString) @@ -286,93 +231,84 @@ void DOMSelection::modify(const String& alterString, const String& directionStri return; FrameSelection::EAlteration alter; - if (equalIgnoringCase(alterString, "extend")) + if (equalLettersIgnoringASCIICase(alterString, "extend")) alter = FrameSelection::AlterationExtend; - else if (equalIgnoringCase(alterString, "move")) + else if (equalLettersIgnoringASCIICase(alterString, "move")) alter = FrameSelection::AlterationMove; else return; SelectionDirection direction; - if (equalIgnoringCase(directionString, "forward")) + if (equalLettersIgnoringASCIICase(directionString, "forward")) direction = DirectionForward; - else if (equalIgnoringCase(directionString, "backward")) + else if (equalLettersIgnoringASCIICase(directionString, "backward")) direction = DirectionBackward; - else if (equalIgnoringCase(directionString, "left")) + else if (equalLettersIgnoringASCIICase(directionString, "left")) direction = DirectionLeft; - else if (equalIgnoringCase(directionString, "right")) + else if (equalLettersIgnoringASCIICase(directionString, "right")) direction = DirectionRight; else return; TextGranularity granularity; - if (equalIgnoringCase(granularityString, "character")) + if (equalLettersIgnoringASCIICase(granularityString, "character")) granularity = CharacterGranularity; - else if (equalIgnoringCase(granularityString, "word")) + else if (equalLettersIgnoringASCIICase(granularityString, "word")) granularity = WordGranularity; - else if (equalIgnoringCase(granularityString, "sentence")) + else if (equalLettersIgnoringASCIICase(granularityString, "sentence")) granularity = SentenceGranularity; - else if (equalIgnoringCase(granularityString, "line")) + else if (equalLettersIgnoringASCIICase(granularityString, "line")) granularity = LineGranularity; - else if (equalIgnoringCase(granularityString, "paragraph")) + else if (equalLettersIgnoringASCIICase(granularityString, "paragraph")) granularity = ParagraphGranularity; - else if (equalIgnoringCase(granularityString, "lineboundary")) + else if (equalLettersIgnoringASCIICase(granularityString, "lineboundary")) granularity = LineBoundary; - else if (equalIgnoringCase(granularityString, "sentenceboundary")) + else if (equalLettersIgnoringASCIICase(granularityString, "sentenceboundary")) granularity = SentenceBoundary; - else if (equalIgnoringCase(granularityString, "paragraphboundary")) + else if (equalLettersIgnoringASCIICase(granularityString, "paragraphboundary")) granularity = ParagraphBoundary; - else if (equalIgnoringCase(granularityString, "documentboundary")) + else if (equalLettersIgnoringASCIICase(granularityString, "documentboundary")) granularity = DocumentBoundary; else return; + Ref<Frame> protector(*m_frame); m_frame->selection().modify(alter, direction, granularity); } -void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec) +ExceptionOr<void> DOMSelection::extend(Node& node, unsigned offset) { if (!m_frame) - return; + return { }; + if (offset > (node.offsetInCharacters() ? caretMaxOffset(node) : node.countChildNodes())) + return Exception { INDEX_SIZE_ERR }; + if (!isValidForPosition(&node)) + return { }; - if (!node) { - ec = TYPE_MISMATCH_ERR; - return; - } - - if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) { - ec = INDEX_SIZE_ERR; - return; - } - - if (!isValidForPosition(node)) - return; - - // FIXME: Eliminate legacy editing positions - m_frame->selection().setExtent(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM)); + Ref<Frame> protector(*m_frame); + m_frame->selection().setExtent(createLegacyEditingPosition(&node, offset), DOWNSTREAM); + return { }; } -PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec) +ExceptionOr<Ref<Range>> DOMSelection::getRangeAt(unsigned index) { - if (!m_frame) - return 0; + if (index >= rangeCount()) + return Exception { INDEX_SIZE_ERR }; - if (index < 0 || index >= rangeCount()) { - ec = INDEX_SIZE_ERR; - return 0; - } - - // If you're hitting this, you've added broken multi-range selection support + // If you're hitting this, you've added broken multi-range selection support. ASSERT(rangeCount() == 1); - if (Node* shadowAncestor = selectionShadowAncestor(m_frame)) { - ContainerNode* container = shadowAncestor->parentNodeGuaranteedHostFree(); - int offset = shadowAncestor->nodeIndex(); + if (auto* shadowAncestor = selectionShadowAncestor(*m_frame)) { + auto* container = shadowAncestor->parentNodeGuaranteedHostFree(); + unsigned offset = shadowAncestor->computeNodeIndex(); return Range::create(shadowAncestor->document(), container, offset, container, offset); } - const VisibleSelection& selection = m_frame->selection().selection(); - return selection.firstRange(); + auto firstRange = m_frame->selection().selection().firstRange(); + ASSERT(firstRange); + if (!firstRange) + return Exception { INDEX_SIZE_ERR }; + return firstRange.releaseNonNull(); } void DOMSelection::removeAllRanges() @@ -382,41 +318,49 @@ void DOMSelection::removeAllRanges() m_frame->selection().clear(); } -void DOMSelection::addRange(Range* r) +void DOMSelection::addRange(Range& range) { if (!m_frame) return; - if (!r) - return; - FrameSelection& selection = m_frame->selection(); + Ref<Frame> protector(*m_frame); + auto& selection = m_frame->selection(); if (selection.isNone()) { - selection.setSelection(VisibleSelection(r)); + selection.moveTo(&range); return; } - RefPtr<Range> range = selection.selection().toNormalizedRange(); - if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), IGNORE_EXCEPTION) == -1) { - // We don't support discontiguous selection. We don't do anything if r and range don't intersect. - if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), IGNORE_EXCEPTION) > -1) { - if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), IGNORE_EXCEPTION) == -1) - // The original range and r intersect. - selection.setSelection(VisibleSelection(r->startPosition(), range->endPosition(), DOWNSTREAM)); - else - // r contains the original range. - selection.setSelection(VisibleSelection(r)); + auto normalizedRange = selection.selection().toNormalizedRange(); + if (!normalizedRange) + return; + + auto result = range.compareBoundaryPoints(Range::START_TO_START, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() == -1) { + // We don't support discontiguous selection. We don't do anything if the two ranges don't intersect. + result = range.compareBoundaryPoints(Range::START_TO_END, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() > -1) { + result = range.compareBoundaryPoints(Range::END_TO_END, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() == -1) { + // The ranges intersect. + selection.moveTo(range.startPosition(), normalizedRange->endPosition(), DOWNSTREAM); + } else { + // The new range contains the original range. + selection.moveTo(&range); + } } } else { - // We don't support discontiguous selection. We don't do anything if r and range don't intersect. - ExceptionCode ec = 0; - if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) < 1 && !ec) { - if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), IGNORE_EXCEPTION) == -1) - // The original range contains r. - selection.setSelection(VisibleSelection(range.get())); - else - // The original range and r intersect. - selection.setSelection(VisibleSelection(range->startPosition(), r->endPosition(), DOWNSTREAM)); + // We don't support discontiguous selection. We don't do anything if the two ranges don't intersect. + result = range.compareBoundaryPoints(Range::END_TO_START, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() < 1) { + result = range.compareBoundaryPoints(Range::END_TO_END, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() == -1) { + // The original range contains the new range. + selection.moveTo(normalizedRange.get()); + } else { + // The ranges intersect. + selection.moveTo(normalizedRange->startPosition(), range.endPosition(), DOWNSTREAM); + } } } } @@ -426,84 +370,79 @@ void DOMSelection::deleteFromDocument() if (!m_frame) return; - FrameSelection& selection = m_frame->selection(); - + auto& selection = m_frame->selection(); if (selection.isNone()) return; - if (isCollapsed()) - selection.modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity); - - RefPtr<Range> selectedRange = selection.selection().toNormalizedRange(); - if (!selectedRange) + auto selectedRange = selection.selection().toNormalizedRange(); + if (!selectedRange || selectedRange->shadowRoot()) return; - selectedRange->deleteContents(ASSERT_NO_EXCEPTION); - - setBaseAndExtent(selectedRange->startContainer(ASSERT_NO_EXCEPTION), selectedRange->startOffset(), selectedRange->startContainer(), selectedRange->startOffset(), ASSERT_NO_EXCEPTION); + Ref<Frame> protector(*m_frame); + selectedRange->deleteContents(); + setBaseAndExtent(&selectedRange->startContainer(), selectedRange->startOffset(), &selectedRange->startContainer(), selectedRange->startOffset()); } -bool DOMSelection::containsNode(Node* n, bool allowPartial) const +bool DOMSelection::containsNode(Node& node, bool allowPartial) const { if (!m_frame) return false; - FrameSelection& selection = m_frame->selection(); - - if (!n || m_frame->document() != &n->document() || selection.isNone()) + auto& selection = m_frame->selection(); + if (m_frame->document() != &node.document() || selection.isNone()) return false; - RefPtr<Node> node = n; - RefPtr<Range> selectedRange = selection.selection().toNormalizedRange(); + Ref<Node> protectedNode(node); + auto selectedRange = selection.selection().toNormalizedRange(); - ContainerNode* parentNode = node->parentNode(); - if (!parentNode || !parentNode->inDocument()) + ContainerNode* parentNode = node.parentNode(); + if (!parentNode || !parentNode->isConnected()) return false; - unsigned nodeIndex = node->nodeIndex(); - - ExceptionCode ec = 0; - bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(), selectedRange->startOffset(), ec) >= 0 && !ec - && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(), selectedRange->endOffset(), ec) <= 0 && !ec; - ASSERT(!ec); - if (nodeFullySelected) + unsigned nodeIndex = node.computeNodeIndex(); + + auto startsResult = Range::compareBoundaryPoints(parentNode, nodeIndex, &selectedRange->startContainer(), selectedRange->startOffset()); + ASSERT(!startsResult.hasException()); + auto endsResult = Range::compareBoundaryPoints(parentNode, nodeIndex + 1, &selectedRange->endContainer(), selectedRange->endOffset()); + ASSERT(!endsResult.hasException()); + bool isNodeFullySelected = !startsResult.hasException() && startsResult.releaseReturnValue() >= 0 + && !endsResult.hasException() && endsResult.releaseReturnValue() <= 0; + if (isNodeFullySelected) return true; - bool nodeFullyUnselected = (Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(), selectedRange->endOffset(), ec) > 0 && !ec) - || (Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(), selectedRange->startOffset(), ec) < 0 && !ec); - ASSERT(!ec); - if (nodeFullyUnselected) + auto startEndResult = Range::compareBoundaryPoints(parentNode, nodeIndex, &selectedRange->endContainer(), selectedRange->endOffset()); + ASSERT(!startEndResult.hasException()); + auto endStartResult = Range::compareBoundaryPoints(parentNode, nodeIndex + 1, &selectedRange->startContainer(), selectedRange->startOffset()); + ASSERT(!endStartResult.hasException()); + bool isNodeFullyUnselected = (!startEndResult.hasException() && startEndResult.releaseReturnValue() > 0) + || (!endStartResult.hasException() && endStartResult.releaseReturnValue() < 0); + if (isNodeFullyUnselected) return false; - return allowPartial || node->isTextNode(); + return allowPartial || node.isTextNode(); } -void DOMSelection::selectAllChildren(Node* n, ExceptionCode& ec) +void DOMSelection::selectAllChildren(Node& node) { - if (!n) - return; - // This doesn't (and shouldn't) select text node characters. - setBaseAndExtent(n, 0, n, n->childNodeCount(), ec); + setBaseAndExtent(&node, 0, &node, node.countChildNodes()); } String DOMSelection::toString() { if (!m_frame) return String(); - return plainText(m_frame->selection().selection().toNormalizedRange().get()); } Node* DOMSelection::shadowAdjustedNode(const Position& position) const { if (position.isNull()) - return 0; - - Node* containerNode = position.containerNode(); - Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode); + return nullptr; + auto* containerNode = position.containerNode(); + auto* adjustedNode = m_frame->document()->ancestorNodeInThisScope(containerNode); if (!adjustedNode) - return 0; + return nullptr; if (containerNode == adjustedNode) return containerNode; @@ -511,26 +450,26 @@ Node* DOMSelection::shadowAdjustedNode(const Position& position) const return adjustedNode->parentNodeGuaranteedHostFree(); } -int DOMSelection::shadowAdjustedOffset(const Position& position) const +unsigned DOMSelection::shadowAdjustedOffset(const Position& position) const { if (position.isNull()) return 0; - Node* containerNode = position.containerNode(); - Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode); - + auto* containerNode = position.containerNode(); + auto* adjustedNode = m_frame->document()->ancestorNodeInThisScope(containerNode); if (!adjustedNode) return 0; if (containerNode == adjustedNode) return position.computeOffsetInContainerNode(); - return adjustedNode->nodeIndex(); + return adjustedNode->computeNodeIndex(); } bool DOMSelection::isValidForPosition(Node* node) const { - ASSERT(m_frame); + if (!m_frame) + return false; if (!node) return true; return &node->document() == m_frame->document(); |