diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/dom/Range.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/dom/Range.cpp')
-rw-r--r-- | Source/WebCore/dom/Range.cpp | 1867 |
1 files changed, 710 insertions, 1157 deletions
diff --git a/Source/WebCore/dom/Range.cpp b/Source/WebCore/dom/Range.cpp index 226f63271..e0ed74888 100644 --- a/Source/WebCore/dom/Range.cpp +++ b/Source/WebCore/dom/Range.cpp @@ -27,16 +27,20 @@ #include "ClientRect.h" #include "ClientRectList.h" +#include "Comment.h" #include "DocumentFragment.h" +#include "Event.h" +#include "ExceptionCode.h" #include "Frame.h" #include "FrameView.h" +#include "HTMLBodyElement.h" +#include "HTMLDocument.h" #include "HTMLElement.h" +#include "HTMLHtmlElement.h" #include "HTMLNames.h" #include "NodeTraversal.h" #include "NodeWithIndex.h" -#include "Page.h" #include "ProcessingInstruction.h" -#include "RangeException.h" #include "RenderBoxModelObject.h" #include "RenderText.h" #include "ScopedEventQueue.h" @@ -60,6 +64,13 @@ using namespace HTMLNames; DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, rangeCounter, ("Range")); +enum ContentsProcessDirection { ProcessContentsForward, ProcessContentsBackward }; +enum class CoordinateSpace { Absolute, Client }; + +static ExceptionOr<void> processNodes(Range::ActionType, Vector<Ref<Node>>&, Node* oldContainer, RefPtr<Node> newContainer); +static ExceptionOr<RefPtr<Node>> processContentsBetweenOffsets(Range::ActionType, RefPtr<DocumentFragment>, RefPtr<Node> container, unsigned startOffset, unsigned endOffset); +static ExceptionOr<RefPtr<Node>> processAncestorsAndTheirSiblings(Range::ActionType, Node* container, ContentsProcessDirection, ExceptionOr<RefPtr<Node>>&& passedClonedContainer, Node* commonRoot); + inline Range::Range(Document& ownerDocument) : m_ownerDocument(ownerDocument) , m_start(&ownerDocument) @@ -72,12 +83,12 @@ inline Range::Range(Document& ownerDocument) m_ownerDocument->attachRange(this); } -PassRefPtr<Range> Range::create(Document& ownerDocument) +Ref<Range> Range::create(Document& ownerDocument) { - return adoptRef(new Range(ownerDocument)); + return adoptRef(*new Range(ownerDocument)); } -inline Range::Range(Document& ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset) +inline Range::Range(Document& ownerDocument, Node* startContainer, int startOffset, Node* endContainer, int endOffset) : m_ownerDocument(ownerDocument) , m_start(&ownerDocument) , m_end(&ownerDocument) @@ -90,37 +101,31 @@ inline Range::Range(Document& ownerDocument, PassRefPtr<Node> startContainer, in // Simply setting the containers and offsets directly would not do any of the checking // that setStart and setEnd do, so we call those functions. - setStart(startContainer, startOffset); - setEnd(endContainer, endOffset); -} - -PassRefPtr<Range> Range::create(Document& ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset) -{ - return adoptRef(new Range(ownerDocument, startContainer, startOffset, endContainer, endOffset)); + if (startContainer) + setStart(*startContainer, startOffset); + if (endContainer) + setEnd(*endContainer, endOffset); } -PassRefPtr<Range> Range::create(Document& ownerDocument, const Position& start, const Position& end) +Ref<Range> Range::create(Document& ownerDocument, RefPtr<Node>&& startContainer, int startOffset, RefPtr<Node>&& endContainer, int endOffset) { - return adoptRef(new Range(ownerDocument, start.containerNode(), start.computeOffsetInContainerNode(), end.containerNode(), end.computeOffsetInContainerNode())); + return adoptRef(*new Range(ownerDocument, startContainer.get(), startOffset, endContainer.get(), endOffset)); } -PassRefPtr<Range> Range::create(ScriptExecutionContext& context) +Ref<Range> Range::create(Document& ownerDocument, const Position& start, const Position& end) { - return adoptRef(new Range(toDocument(context))); + return adoptRef(*new Range(ownerDocument, start.containerNode(), start.computeOffsetInContainerNode(), end.containerNode(), end.computeOffsetInContainerNode())); } -#if PLATFORM(IOS) -PassRefPtr<Range> Range::create(Document& ownerDocument, const VisiblePosition& visibleStart, const VisiblePosition& visibleEnd) +Ref<Range> Range::create(Document& ownerDocument, const VisiblePosition& visibleStart, const VisiblePosition& visibleEnd) { Position start = visibleStart.deepEquivalent().parentAnchoredEquivalent(); Position end = visibleEnd.deepEquivalent().parentAnchoredEquivalent(); - return adoptRef(new Range(ownerDocument, start.anchorNode(), start.deprecatedEditingOffset(), end.anchorNode(), end.deprecatedEditingOffset())); + return adoptRef(*new Range(ownerDocument, start.anchorNode(), start.deprecatedEditingOffset(), end.anchorNode(), end.deprecatedEditingOffset())); } -#endif Range::~Range() { - // Always detach (even if we've already detached) to fix https://bugs.webkit.org/show_bug.cgi?id=26044 m_ownerDocument->detachRange(this); #ifndef NDEBUG @@ -130,64 +135,14 @@ Range::~Range() void Range::setDocument(Document& document) { - ASSERT(&m_ownerDocument.get() != &document); + ASSERT(m_ownerDocument.ptr() != &document); m_ownerDocument->detachRange(this); m_ownerDocument = document; - m_start.setToStartOfNode(&document); - m_end.setToStartOfNode(&document); + m_start.setToStartOfNode(document); + m_end.setToStartOfNode(document); m_ownerDocument->attachRange(this); } -Node* Range::startContainer(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_start.container(); -} - -int Range::startOffset(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_start.offset(); -} - -Node* Range::endContainer(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_end.container(); -} - -int Range::endOffset(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_end.offset(); -} - -Node* Range::commonAncestorContainer(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return commonAncestorContainer(m_start.container(), m_end.container()); -} - Node* Range::commonAncestorContainer(Node* containerA, Node* containerB) { for (Node* parentA = containerA; parentA; parentA = parentA->parentNode()) { @@ -196,17 +151,7 @@ Node* Range::commonAncestorContainer(Node* containerA, Node* containerB) return parentA; } } - return 0; -} - -bool Range::collapsed(ExceptionCode& ec) const -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return m_start == m_end; + return nullptr; } static inline bool checkForDifferentRootContainer(const RangeBoundaryPoint& start, const RangeBoundaryPoint& end) @@ -218,257 +163,210 @@ static inline bool checkForDifferentRootContainer(const RangeBoundaryPoint& star while (startRootContainer->parentNode()) startRootContainer = startRootContainer->parentNode(); - return startRootContainer != endRootContainer || (Range::compareBoundaryPoints(start, end, ASSERT_NO_EXCEPTION) > 0); + return startRootContainer != endRootContainer || Range::compareBoundaryPoints(start, end).releaseReturnValue() > 0; } -void Range::setStart(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec) +ExceptionOr<void> Range::setStart(Ref<Node>&& refNode, unsigned offset) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - bool didMoveDocument = false; if (&refNode->document() != &ownerDocument()) { setDocument(refNode->document()); didMoveDocument = true; } - ec = 0; - Node* childNode = checkNodeWOffset(refNode.get(), offset, ec); - if (ec) - return; + auto childNode = checkNodeWOffset(refNode, offset); + if (childNode.hasException()) + return childNode.releaseException(); - m_start.set(refNode, offset, childNode); + m_start.set(WTFMove(refNode), offset, childNode.releaseReturnValue()); if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end)) - collapse(true, ec); + collapse(true); + + return { }; } -void Range::setEnd(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec) +ExceptionOr<void> Range::setEnd(Ref<Node>&& refNode, unsigned offset) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - bool didMoveDocument = false; if (&refNode->document() != &ownerDocument()) { setDocument(refNode->document()); didMoveDocument = true; } - ec = 0; - Node* childNode = checkNodeWOffset(refNode.get(), offset, ec); - if (ec) - return; + auto childNode = checkNodeWOffset(refNode, offset); + if (childNode.hasException()) + return childNode.releaseException(); - m_end.set(refNode, offset, childNode); + m_end.set(WTFMove(refNode), offset, childNode.releaseReturnValue()); if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end)) - collapse(false, ec); + collapse(false); + + return { }; } -void Range::setStart(const Position& start, ExceptionCode& ec) +ExceptionOr<void> Range::setStart(const Position& start) { Position parentAnchored = start.parentAnchoredEquivalent(); - setStart(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), ec); + if (!parentAnchored.containerNode()) + return Exception { TypeError }; + return setStart(*parentAnchored.containerNode(), parentAnchored.offsetInContainerNode()); } -void Range::setEnd(const Position& end, ExceptionCode& ec) +ExceptionOr<void> Range::setEnd(const Position& end) { Position parentAnchored = end.parentAnchoredEquivalent(); - setEnd(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), ec); + if (!parentAnchored.containerNode()) + return Exception { TypeError }; + return setEnd(*parentAnchored.containerNode(), parentAnchored.offsetInContainerNode()); } -void Range::collapse(bool toStart, ExceptionCode& ec) +void Range::collapse(bool toStart) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - if (toStart) m_end = m_start; else m_start = m_end; } -bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec) +ExceptionOr<bool> Range::isPointInRange(Node& refNode, unsigned offset) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return false; - } - - if (!refNode) { - ec = HIERARCHY_REQUEST_ERR; + if (&refNode.document() != &ownerDocument()) return false; - } - if (!refNode->inDocument() || &refNode->document() != &ownerDocument()) { - return false; + auto checkNodeResult = checkNodeWOffset(refNode, offset); + if (checkNodeResult.hasException()) { + // DOM4 spec requires us to check whether refNode and start container have the same root first + // but we do it in the reverse order to avoid O(n) operation here in common case. + if (!commonAncestorContainer(&refNode, &startContainer())) + return false; + return checkNodeResult.releaseException(); } - ec = 0; - checkNodeWOffset(refNode, offset, ec); - if (ec) + auto startCompareResult = compareBoundaryPoints(&refNode, offset, &startContainer(), m_start.offset()); + if (!(!startCompareResult.hasException() && startCompareResult.releaseReturnValue() >= 0)) return false; - - return compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), ec) >= 0 && !ec - && compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), ec) <= 0 && !ec; + auto endCompareResult = compareBoundaryPoints(&refNode, offset, &endContainer(), m_end.offset()); + return !endCompareResult.hasException() && endCompareResult.releaseReturnValue() <= 0; } -short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec) const +ExceptionOr<short> Range::comparePoint(Node& refNode, unsigned offset) const { // http://developer.mozilla.org/en/docs/DOM:range.comparePoint // This method returns -1, 0 or 1 depending on if the point described by the // refNode node and an offset within the node is before, same as, or after the range respectively. + if (&refNode.document() != &ownerDocument()) + return Exception { WRONG_DOCUMENT_ERR }; - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - if (!refNode) { - ec = HIERARCHY_REQUEST_ERR; - return 0; - } - - if (!refNode->inDocument() || &refNode->document() != &ownerDocument()) { - ec = WRONG_DOCUMENT_ERR; - return 0; + auto checkNodeResult = checkNodeWOffset(refNode, offset); + if (checkNodeResult.hasException()) { + // DOM4 spec requires us to check whether refNode and start container have the same root first + // but we do it in the reverse order to avoid O(n) operation here in common case. + if (!refNode.isConnected() && !commonAncestorContainer(&refNode, &startContainer())) + return Exception { WRONG_DOCUMENT_ERR }; + return checkNodeResult.releaseException(); } - ec = 0; - checkNodeWOffset(refNode, offset, ec); - if (ec) - return 0; - // compare to start, and point comes before - if (compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), ec) < 0) + auto startCompareResult = compareBoundaryPoints(&refNode, offset, &startContainer(), m_start.offset()); + if (startCompareResult.hasException()) + return startCompareResult.releaseException(); + if (startCompareResult.releaseReturnValue() < 0) return -1; - if (ec) - return 0; - // compare to end, and point comes after - if (compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), ec) > 0 && !ec) + auto endCompareResult = compareBoundaryPoints(&refNode, offset, &endContainer(), m_end.offset()); + if (endCompareResult.hasException()) + return endCompareResult.releaseException(); + if (endCompareResult.releaseReturnValue() > 0) return 1; // point is in the middle of this range, or on the boundary points return 0; } -Range::CompareResults Range::compareNode(Node* refNode, ExceptionCode& ec) const +ExceptionOr<Range::CompareResults> Range::compareNode(Node& refNode) const { // http://developer.mozilla.org/en/docs/DOM:range.compareNode // This method returns 0, 1, 2, or 3 based on if the node is before, after, // before and after(surrounds), or inside the range, respectively - if (!refNode) { - ec = NOT_FOUND_ERR; - return NODE_BEFORE; - } - - if (!m_start.container() && refNode->inDocument()) { - ec = INVALID_STATE_ERR; - return NODE_BEFORE; - } - - if (m_start.container() && !refNode->inDocument()) { + if (!refNode.isConnected()) { // Firefox doesn't throw an exception for this case; it returns 0. return NODE_BEFORE; } - if (&refNode->document() != &ownerDocument()) { + if (&refNode.document() != &ownerDocument()) { // Firefox doesn't throw an exception for this case; it returns 0. return NODE_BEFORE; } - ContainerNode* parentNode = refNode->parentNode(); - int nodeIndex = refNode->nodeIndex(); - + auto* parentNode = refNode.parentNode(); if (!parentNode) { - // if the node is the top document we should return NODE_BEFORE_AND_AFTER - // but we throw to match firefox behavior - ec = NOT_FOUND_ERR; - return NODE_BEFORE; + // If the node is the top of the tree we should return NODE_BEFORE_AND_AFTER, + // but we throw to match firefox behavior. + return Exception { NOT_FOUND_ERR }; } + auto nodeIndex = refNode.computeNodeIndex(); - if (comparePoint(parentNode, nodeIndex, ec) < 0) { // starts before - if (comparePoint(parentNode, nodeIndex + 1, ec) > 0) // ends after the range - return NODE_BEFORE_AND_AFTER; - return NODE_BEFORE; // ends before or in the range - } else { // starts at or after the range start - if (comparePoint(parentNode, nodeIndex + 1, ec) > 0) // ends after the range - return NODE_AFTER; - return NODE_INSIDE; // ends inside the range - } + auto nodeStartCompareResult = comparePoint(*parentNode, nodeIndex); + if (nodeStartCompareResult.hasException()) + return nodeStartCompareResult.releaseException(); + auto nodeEndCompareResult = comparePoint(*parentNode, nodeIndex + 1); + if (nodeEndCompareResult.hasException()) + return nodeEndCompareResult.releaseException(); + + bool nodeStartsBeforeRange = nodeStartCompareResult.releaseReturnValue() < 0; + bool nodeEndsAfterRange = nodeEndCompareResult.releaseReturnValue() > 0; + + return nodeStartsBeforeRange + ? (nodeEndsAfterRange ? NODE_BEFORE_AND_AFTER : NODE_BEFORE) + : (nodeEndsAfterRange ? NODE_AFTER : NODE_INSIDE); } -short Range::compareBoundaryPoints(CompareHow how, const Range* sourceRange, ExceptionCode& ec) const +static inline Node* top(Node& node) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - if (!sourceRange) { - ec = NOT_FOUND_ERR; - return 0; - } + auto* top = &node; + while (auto* parent = top->parentNode()) + top = parent; + return top; +} - ec = 0; - Node* thisCont = commonAncestorContainer(ec); - if (ec) - return 0; - Node* sourceCont = sourceRange->commonAncestorContainer(ec); - if (ec) - return 0; +ExceptionOr<short> Range::compareBoundaryPoints(CompareHow how, const Range& sourceRange) const +{ + auto* thisContainer = commonAncestorContainer(); + auto* sourceContainer = sourceRange.commonAncestorContainer(); + if (!thisContainer || !sourceContainer || &thisContainer->document() != &sourceContainer->document() || top(*thisContainer) != top(*sourceContainer)) + return Exception { WRONG_DOCUMENT_ERR }; - if (&thisCont->document() != &sourceCont->document()) { - ec = WRONG_DOCUMENT_ERR; - return 0; + switch (how) { + case START_TO_START: + return compareBoundaryPoints(m_start, sourceRange.m_start); + case START_TO_END: + return compareBoundaryPoints(m_end, sourceRange.m_start); + case END_TO_END: + return compareBoundaryPoints(m_end, sourceRange.m_end); + case END_TO_START: + return compareBoundaryPoints(m_start, sourceRange.m_end); } - Node* thisTop = thisCont; - Node* sourceTop = sourceCont; - while (thisTop->parentNode()) - thisTop = thisTop->parentNode(); - while (sourceTop->parentNode()) - sourceTop = sourceTop->parentNode(); - if (thisTop != sourceTop) { // in different DocumentFragments - ec = WRONG_DOCUMENT_ERR; - return 0; - } + return Exception { SYNTAX_ERR }; +} +ExceptionOr<short> Range::compareBoundaryPointsForBindings(unsigned short how, const Range& sourceRange) const +{ switch (how) { - case START_TO_START: - return compareBoundaryPoints(m_start, sourceRange->m_start, ec); - case START_TO_END: - return compareBoundaryPoints(m_end, sourceRange->m_start, ec); - case END_TO_END: - return compareBoundaryPoints(m_end, sourceRange->m_end, ec); - case END_TO_START: - return compareBoundaryPoints(m_start, sourceRange->m_end, ec); + case START_TO_START: + case START_TO_END: + case END_TO_END: + case END_TO_START: + return compareBoundaryPoints(static_cast<CompareHow>(how), sourceRange); } - - ec = SYNTAX_ERR; - return 0; + return Exception { NOT_SUPPORTED_ERR }; } -short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB, ExceptionCode& ec) +ExceptionOr<short> Range::compareBoundaryPoints(Node* containerA, unsigned offsetA, Node* containerB, unsigned offsetB) { ASSERT(containerA); ASSERT(containerB); @@ -483,11 +381,10 @@ short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containe // case 1: both points have the same container if (containerA == containerB) { if (offsetA == offsetB) - return 0; // A is equal to B + return 0; // A is equal to B if (offsetA < offsetB) - return -1; // A is before B - else - return 1; // A is after B + return -1; // A is before B + return 1; // A is after B } // case 2: node C (container B or an ancestor) is a child node of A @@ -495,17 +392,15 @@ short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containe while (c && c->parentNode() != containerA) c = c->parentNode(); if (c) { - int offsetC = 0; + unsigned offsetC = 0; Node* n = containerA->firstChild(); while (n != c && offsetC < offsetA) { offsetC++; n = n->nextSibling(); } - if (offsetA <= offsetC) - return -1; // A is before B - else - return 1; // A is after B + return -1; // A is before B + return 1; // A is after B } // case 3: node C (container A or an ancestor) is a child node of B @@ -513,26 +408,22 @@ short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containe while (c && c->parentNode() != containerB) c = c->parentNode(); if (c) { - int offsetC = 0; + unsigned offsetC = 0; Node* n = containerB->firstChild(); while (n != c && offsetC < offsetB) { offsetC++; n = n->nextSibling(); } - if (offsetC < offsetB) - return -1; // A is before B - else - return 1; // A is after B + return -1; // A is before B + return 1; // A is after B } // case 4: containers A & B are siblings, or children of siblings // ### we need to do a traversal here instead - Node* commonAncestor = commonAncestorContainer(containerA, containerB); - if (!commonAncestor) { - ec = WRONG_DOCUMENT_ERR; - return 0; - } + auto* commonAncestor = commonAncestorContainer(containerA, containerB); + if (!commonAncestor) + return Exception { WRONG_DOCUMENT_ERR }; Node* childA = containerA; while (childA && childA->parentNode() != commonAncestor) childA = childA->parentNode(); @@ -561,65 +452,51 @@ short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containe return 0; } -short Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB, ExceptionCode& ec) +ExceptionOr<short> Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB) { - return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset(), ec); + return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset()); } bool Range::boundaryPointsValid() const { - ExceptionCode ec = 0; - return m_start.container() && compareBoundaryPoints(m_start, m_end, ec) <= 0 && !ec; + auto result = compareBoundaryPoints(m_start, m_end); + return !result.hasException() && result.releaseReturnValue() <= 0; } -void Range::deleteContents(ExceptionCode& ec) +ExceptionOr<void> Range::deleteContents() { - checkDeleteExtract(ec); - if (ec) - return; - - processContents(Delete, ec); + auto result = processContents(Delete); + if (result.hasException()) + return result.releaseException(); + return { }; } -bool Range::intersectsNode(Node* refNode, ExceptionCode& ec) +ExceptionOr<bool> Range::intersectsNode(Node& refNode) const { - // http://developer.mozilla.org/en/docs/DOM:range.intersectsNode - // Returns a bool if the node intersects the range. - - // Throw exception if the range is already detached. - if (!m_start.container()) { - ec = INVALID_STATE_ERR; + if (!refNode.isConnected() || &refNode.document() != &ownerDocument()) return false; - } - if (!refNode) { - ec = NOT_FOUND_ERR; - return false; - } - if (!refNode->inDocument() || &refNode->document() != &ownerDocument()) { - // Firefox doesn't throw an exception for these cases; it returns false. - return false; - } + ContainerNode* parentNode = refNode.parentNode(); + if (!parentNode) + return true; - ContainerNode* parentNode = refNode->parentNode(); - int nodeIndex = refNode->nodeIndex(); - - if (!parentNode) { - // if the node is the top document we should return NODE_BEFORE_AND_AFTER - // but we throw to match firefox behavior - ec = NOT_FOUND_ERR; - return false; - } + unsigned nodeIndex = refNode.computeNodeIndex(); - if (comparePoint(parentNode, nodeIndex, ec) < 0 && // starts before start - comparePoint(parentNode, nodeIndex + 1, ec) < 0) { // ends before start - return false; - } else if (comparePoint(parentNode, nodeIndex, ec) > 0 && // starts after end - comparePoint(parentNode, nodeIndex + 1, ec) > 0) { // ends after end - return false; - } - - return true; // all other cases + // If (parent, offset) is before end and (parent, offset + 1) is after start, return true. + // Otherwise, return false. + auto result = comparePoint(*parentNode, nodeIndex); + if (result.hasException()) + return result.releaseException(); + auto compareFirst = result.releaseReturnValue(); + result = comparePoint(*parentNode, nodeIndex + 1); + if (result.hasException()) + return result.releaseException(); + auto compareSecond = result.releaseReturnValue(); + + bool isFirstBeforeEnd = m_start == m_end ? compareFirst < 0 : compareFirst <= 0; + bool isSecondAfterStart = m_start == m_end ? compareSecond > 0 : compareSecond >= 0; + + return isFirstBeforeEnd && isSecondAfterStart; } static inline Node* highestAncestorUnderCommonRoot(Node* node, Node* commonRoot) @@ -655,53 +532,44 @@ static inline Node* childOfCommonRootBeforeOffset(Node* container, unsigned offs return container; } -static inline unsigned lengthOfContentsInNode(Node* node) +static inline unsigned lengthOfContentsInNode(Node& node) { // This switch statement must be consistent with that of Range::processContentsBetweenOffsets. - switch (node->nodeType()) { + switch (node.nodeType()) { + case Node::DOCUMENT_TYPE_NODE: + return 0; case Node::TEXT_NODE: case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: - return toCharacterData(node)->length(); case Node::PROCESSING_INSTRUCTION_NODE: - return toProcessingInstruction(node)->data().length(); + return downcast<CharacterData>(node).length(); case Node::ELEMENT_NODE: case Node::ATTRIBUTE_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::ENTITY_NODE: case Node::DOCUMENT_NODE: - case Node::DOCUMENT_TYPE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: - case Node::NOTATION_NODE: - case Node::XPATH_NAMESPACE_NODE: - return node->childNodeCount(); + return downcast<ContainerNode>(node).countChildNodes(); } ASSERT_NOT_REACHED(); return 0; } -PassRefPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionCode& ec) +ExceptionOr<RefPtr<DocumentFragment>> Range::processContents(ActionType action) { - typedef Vector<RefPtr<Node>> NodeVector; - RefPtr<DocumentFragment> fragment; if (action == Extract || action == Clone) fragment = DocumentFragment::create(ownerDocument()); - ec = 0; - if (collapsed(ec)) - return fragment.release(); - if (ec) - return 0; + if (collapsed()) + return WTFMove(fragment); - RefPtr<Node> commonRoot = commonAncestorContainer(ec); - if (ec) - return 0; + RefPtr<Node> commonRoot = commonAncestorContainer(); ASSERT(commonRoot); - if (m_start.container() == m_end.container()) { - processContentsBetweenOffsets(action, fragment, m_start.container(), m_start.offset(), m_end.offset(), ec); - return fragment; + if (&startContainer() == &endContainer()) { + auto result = processContentsBetweenOffsets(action, fragment, &startContainer(), m_start.offset(), m_end.offset()); + if (result.hasException()) + return result.releaseException(); + return WTFMove(fragment); } // Since mutation events can modify the range during the process, the boundary points need to be saved. @@ -734,14 +602,20 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception RefPtr<Node> leftContents; if (originalStart.container() != commonRoot && commonRoot->contains(originalStart.container())) { - leftContents = processContentsBetweenOffsets(action, 0, originalStart.container(), originalStart.offset(), lengthOfContentsInNode(originalStart.container()), ec); - leftContents = processAncestorsAndTheirSiblings(action, originalStart.container(), ProcessContentsForward, leftContents, commonRoot.get(), ec); + auto firstResult = processContentsBetweenOffsets(action, nullptr, originalStart.container(), originalStart.offset(), lengthOfContentsInNode(*originalStart.container())); + auto secondResult = processAncestorsAndTheirSiblings(action, originalStart.container(), ProcessContentsForward, WTFMove(firstResult), commonRoot.get()); + // FIXME: A bit peculiar that we silently ignore the exception here, but we do have at least some regression tests that rely on this behavior. + if (!secondResult.hasException()) + leftContents = secondResult.releaseReturnValue(); } RefPtr<Node> rightContents; - if (m_end.container() != commonRoot && commonRoot->contains(originalEnd.container())) { - rightContents = processContentsBetweenOffsets(action, 0, originalEnd.container(), 0, originalEnd.offset(), ec); - rightContents = processAncestorsAndTheirSiblings(action, originalEnd.container(), ProcessContentsBackward, rightContents, commonRoot.get(), ec); + if (&endContainer() != commonRoot && commonRoot->contains(originalEnd.container())) { + auto firstResult = processContentsBetweenOffsets(action, nullptr, originalEnd.container(), 0, originalEnd.offset()); + auto secondResult = processAncestorsAndTheirSiblings(action, originalEnd.container(), ProcessContentsBackward, WTFMove(firstResult), commonRoot.get()); + // FIXME: A bit peculiar that we silently ignore the exception here, but we do have at least some regression tests that rely on this behavior. + if (!secondResult.hasException()) + rightContents = secondResult.releaseReturnValue(); } // delete all children of commonRoot between the start and end container @@ -752,342 +626,326 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception // Collapse the range, making sure that the result is not within a node that was partially selected. if (action == Extract || action == Delete) { - if (partialStart && commonRoot->contains(partialStart.get())) - setStart(partialStart->parentNode(), partialStart->nodeIndex() + 1, ec); - else if (partialEnd && commonRoot->contains(partialEnd.get())) - setStart(partialEnd->parentNode(), partialEnd->nodeIndex(), ec); - if (ec) - return 0; + if (partialStart && commonRoot->contains(partialStart.get())) { + auto result = setStart(*partialStart->parentNode(), partialStart->computeNodeIndex() + 1); + if (result.hasException()) + return result.releaseException(); + } else if (partialEnd && commonRoot->contains(partialEnd.get())) { + auto result = setStart(*partialEnd->parentNode(), partialEnd->computeNodeIndex()); + if (result.hasException()) + return result.releaseException(); + } m_end = m_start; } // Now add leftContents, stuff in between, and rightContents to the fragment // (or just delete the stuff in between) - if ((action == Extract || action == Clone) && leftContents) - fragment->appendChild(leftContents, ec); + if ((action == Extract || action == Clone) && leftContents) { + auto result = fragment->appendChild(*leftContents); + if (result.hasException()) + return result.releaseException(); + } if (processStart) { - NodeVector nodes; - for (Node* n = processStart.get(); n && n != processEnd; n = n->nextSibling()) - nodes.append(n); - processNodes(action, nodes, commonRoot, fragment, ec); + Vector<Ref<Node>> nodes; + for (Node* node = processStart.get(); node && node != processEnd; node = node->nextSibling()) + nodes.append(*node); + auto result = processNodes(action, nodes, commonRoot.get(), fragment.get()); + if (result.hasException()) + return result.releaseException(); } - if ((action == Extract || action == Clone) && rightContents) - fragment->appendChild(rightContents, ec); + if ((action == Extract || action == Clone) && rightContents) { + auto result = fragment->appendChild(*rightContents); + if (result.hasException()) + return result.releaseException(); + } - return fragment.release(); + return WTFMove(fragment); } -static inline void deleteCharacterData(PassRefPtr<CharacterData> data, unsigned startOffset, unsigned endOffset, ExceptionCode& ec) +static inline ExceptionOr<void> deleteCharacterData(CharacterData& data, unsigned startOffset, unsigned endOffset) { - if (data->length() - endOffset) - data->deleteData(endOffset, data->length() - endOffset, ec); - if (startOffset) - data->deleteData(0, startOffset, ec); + if (data.length() - endOffset) { + auto result = data.deleteData(endOffset, data.length() - endOffset); + if (result.hasException()) + return result.releaseException(); + } + if (startOffset) { + auto result = data.deleteData(0, startOffset); + if (result.hasException()) + return result.releaseException(); + } + return { }; } -PassRefPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRefPtr<DocumentFragment> fragment, Node* container, unsigned startOffset, unsigned endOffset, ExceptionCode& ec) +static ExceptionOr<RefPtr<Node>> processContentsBetweenOffsets(Range::ActionType action, RefPtr<DocumentFragment> fragment, RefPtr<Node> container, unsigned startOffset, unsigned endOffset) { ASSERT(container); ASSERT(startOffset <= endOffset); + RefPtr<Node> result; + // This switch statement must be consistent with that of lengthOfContentsInNode. - RefPtr<Node> result; switch (container->nodeType()) { case Node::TEXT_NODE: case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: - endOffset = std::min(endOffset, static_cast<CharacterData*>(container)->length()); + endOffset = std::min(endOffset, downcast<CharacterData>(*container).length()); startOffset = std::min(startOffset, endOffset); - if (action == Extract || action == Clone) { - RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(container->cloneNode(true)); - deleteCharacterData(c, startOffset, endOffset, ec); + if (action == Range::Extract || action == Range::Clone) { + Ref<CharacterData> characters = downcast<CharacterData>(container->cloneNode(true).get()); + auto deleteResult = deleteCharacterData(characters, startOffset, endOffset); + if (deleteResult.hasException()) + return deleteResult.releaseException(); if (fragment) { result = fragment; - result->appendChild(c.release(), ec); + auto appendResult = result->appendChild(characters); + if (appendResult.hasException()) + return appendResult.releaseException(); } else - result = c.release(); + result = WTFMove(characters); + } + if (action == Range::Extract || action == Range::Delete) { + auto deleteResult = downcast<CharacterData>(*container).deleteData(startOffset, endOffset - startOffset); + if (deleteResult.hasException()) + return deleteResult.releaseException(); } - if (action == Extract || action == Delete) - toCharacterData(container)->deleteData(startOffset, endOffset - startOffset, ec); break; - case Node::PROCESSING_INSTRUCTION_NODE: - endOffset = std::min(endOffset, static_cast<ProcessingInstruction*>(container)->data().length()); + case Node::PROCESSING_INSTRUCTION_NODE: { + auto& instruction = downcast<ProcessingInstruction>(*container); + endOffset = std::min(endOffset, downcast<ProcessingInstruction>(*container).data().length()); startOffset = std::min(startOffset, endOffset); - if (action == Extract || action == Clone) { - RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(container->cloneNode(true)); - c->setData(c->data().substring(startOffset, endOffset - startOffset), ec); + if (action == Range::Extract || action == Range::Clone) { + Ref<ProcessingInstruction> processingInstruction = downcast<ProcessingInstruction>(container->cloneNode(true).get()); + processingInstruction->setData(processingInstruction->data().substring(startOffset, endOffset - startOffset)); if (fragment) { result = fragment; - result->appendChild(c.release(), ec); + auto appendResult = result->appendChild(processingInstruction); + if (appendResult.hasException()) + return appendResult.releaseException(); } else - result = c.release(); + result = WTFMove(processingInstruction); } - if (action == Extract || action == Delete) { - ProcessingInstruction* pi = toProcessingInstruction(container); - String data(pi->data()); + if (action == Range::Extract || action == Range::Delete) { + String data { instruction.data() }; data.remove(startOffset, endOffset - startOffset); - pi->setData(data, ec); + instruction.setData(data); } break; + } case Node::ELEMENT_NODE: case Node::ATTRIBUTE_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::ENTITY_NODE: case Node::DOCUMENT_NODE: case Node::DOCUMENT_TYPE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: - case Node::NOTATION_NODE: - case Node::XPATH_NAMESPACE_NODE: // FIXME: Should we assert that some nodes never appear here? - if (action == Extract || action == Clone) { + if (action == Range::Extract || action == Range::Clone) { if (fragment) result = fragment; else result = container->cloneNode(false); } - + Vector<Ref<Node>> nodes; Node* n = container->firstChild(); - Vector<RefPtr<Node>> nodes; for (unsigned i = startOffset; n && i; i--) n = n->nextSibling(); - for (unsigned i = startOffset; n && i < endOffset; i++, n = n->nextSibling()) - nodes.append(n); - - processNodes(action, nodes, container, result, ec); + for (unsigned i = startOffset; n && i < endOffset; i++, n = n->nextSibling()) { + if (action != Range::Delete && n->isDocumentTypeNode()) { + return Exception { HIERARCHY_REQUEST_ERR }; + } + nodes.append(*n); + } + auto processResult = processNodes(action, nodes, container.get(), result); + if (processResult.hasException()) + return processResult.releaseException(); break; } - return result.release(); + return WTFMove(result); } -void Range::processNodes(ActionType action, Vector<RefPtr<Node>>& nodes, PassRefPtr<Node> oldContainer, PassRefPtr<Node> newContainer, ExceptionCode& ec) +static ExceptionOr<void> processNodes(Range::ActionType action, Vector<Ref<Node>>& nodes, Node* oldContainer, RefPtr<Node> newContainer) { - for (unsigned i = 0; i < nodes.size(); i++) { + for (auto& node : nodes) { switch (action) { - case Delete: - oldContainer->removeChild(nodes[i].get(), ec); + case Range::Delete: { + auto result = oldContainer->removeChild(node); + if (result.hasException()) + return result.releaseException(); break; - case Extract: - newContainer->appendChild(nodes[i].release(), ec); // will remove n from its parent + } + case Range::Extract: { + auto result = newContainer->appendChild(node); // will remove node from its parent + if (result.hasException()) + return result.releaseException(); break; - case Clone: - newContainer->appendChild(nodes[i]->cloneNode(true), ec); + } + case Range::Clone: { + auto result = newContainer->appendChild(node->cloneNode(true)); + if (result.hasException()) + return result.releaseException(); break; } + } } + return { }; } -PassRefPtr<Node> Range::processAncestorsAndTheirSiblings(ActionType action, Node* container, ContentsProcessDirection direction, PassRefPtr<Node> passedClonedContainer, Node* commonRoot, ExceptionCode& ec) +ExceptionOr<RefPtr<Node>> processAncestorsAndTheirSiblings(Range::ActionType action, Node* container, ContentsProcessDirection direction, ExceptionOr<RefPtr<Node>>&& passedClonedContainer, Node* commonRoot) { - typedef Vector<RefPtr<Node>> NodeVector; + if (passedClonedContainer.hasException()) + return WTFMove(passedClonedContainer); - RefPtr<Node> clonedContainer = passedClonedContainer; - Vector<RefPtr<Node>> ancestors; - for (ContainerNode* n = container->parentNode(); n && n != commonRoot; n = n->parentNode()) - ancestors.append(n); + RefPtr<Node> clonedContainer = passedClonedContainer.releaseReturnValue(); + + Vector<Ref<ContainerNode>> ancestors; + for (ContainerNode* ancestor = container->parentNode(); ancestor && ancestor != commonRoot; ancestor = ancestor->parentNode()) + ancestors.append(*ancestor); RefPtr<Node> firstChildInAncestorToProcess = direction == ProcessContentsForward ? container->nextSibling() : container->previousSibling(); - for (Vector<RefPtr<Node>>::const_iterator it = ancestors.begin(); it != ancestors.end(); ++it) { - RefPtr<Node> ancestor = *it; - if (action == Extract || action == Clone) { - if (RefPtr<Node> clonedAncestor = ancestor->cloneNode(false)) { // Might have been removed already during mutation event. - clonedAncestor->appendChild(clonedContainer, ec); - clonedContainer = clonedAncestor; + for (auto& ancestor : ancestors) { + if (action == Range::Extract || action == Range::Clone) { + auto clonedAncestor = ancestor->cloneNode(false); // Might have been removed already during mutation event. + if (clonedContainer) { + auto result = clonedAncestor->appendChild(*clonedContainer); + if (result.hasException()) + return result.releaseException(); } + clonedContainer = WTFMove(clonedAncestor); } // Copy siblings of an ancestor of start/end containers // FIXME: This assertion may fail if DOM is modified during mutation event // FIXME: Share code with Range::processNodes - ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor); + ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor.ptr()); - NodeVector nodes; + Vector<Ref<Node>> nodes; for (Node* child = firstChildInAncestorToProcess.get(); child; child = (direction == ProcessContentsForward) ? child->nextSibling() : child->previousSibling()) - nodes.append(child); + nodes.append(*child); - for (NodeVector::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { - Node* child = it->get(); + for (auto& child : nodes) { switch (action) { - case Delete: - ancestor->removeChild(child, ec); + case Range::Delete: { + auto result = ancestor->removeChild(child); + if (result.hasException()) + return result.releaseException(); break; - case Extract: // will remove child from ancestor - if (direction == ProcessContentsForward) - clonedContainer->appendChild(child, ec); - else - clonedContainer->insertBefore(child, clonedContainer->firstChild(), ec); + } + case Range::Extract: // will remove child from ancestor + if (direction == ProcessContentsForward) { + auto result = clonedContainer->appendChild(child); + if (result.hasException()) + return result.releaseException(); + } else { + auto result = clonedContainer->insertBefore(child, clonedContainer->firstChild()); + if (result.hasException()) + return result.releaseException(); + } break; - case Clone: - if (direction == ProcessContentsForward) - clonedContainer->appendChild(child->cloneNode(true), ec); - else - clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild(), ec); + case Range::Clone: + if (direction == ProcessContentsForward) { + auto result = clonedContainer->appendChild(child->cloneNode(true)); + if (result.hasException()) + return result.releaseException(); + } else { + auto result = clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild()); + if (result.hasException()) + return result.releaseException(); + } break; } } firstChildInAncestorToProcess = direction == ProcessContentsForward ? ancestor->nextSibling() : ancestor->previousSibling(); } - return clonedContainer.release(); + return WTFMove(clonedContainer); } -PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec) +ExceptionOr<Ref<DocumentFragment>> Range::extractContents() { - checkDeleteExtract(ec); - if (ec) - return 0; - - return processContents(Extract, ec); + auto result = processContents(Extract); + if (result.hasException()) + return result.releaseException(); + return result.releaseReturnValue().releaseNonNull(); } -PassRefPtr<DocumentFragment> Range::cloneContents(ExceptionCode& ec) +ExceptionOr<Ref<DocumentFragment>> Range::cloneContents() { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return processContents(Clone, ec); + auto result = processContents(Clone); + if (result.hasException()) + return result.releaseException(); + return result.releaseReturnValue().releaseNonNull(); } -void Range::insertNode(PassRefPtr<Node> prpNewNode, ExceptionCode& ec) +ExceptionOr<void> Range::insertNode(Ref<Node>&& node) { - RefPtr<Node> newNode = prpNewNode; + auto startContainerNodeType = startContainer().nodeType(); - ec = 0; - - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } + if (startContainerNodeType == Node::COMMENT_NODE || startContainerNodeType == Node::PROCESSING_INSTRUCTION_NODE) + return Exception { HIERARCHY_REQUEST_ERR }; + bool startIsText = startContainerNodeType == Node::TEXT_NODE; + if (startIsText && !startContainer().parentNode()) + return Exception { HIERARCHY_REQUEST_ERR }; + if (node.ptr() == &startContainer()) + return Exception { HIERARCHY_REQUEST_ERR }; - if (!newNode) { - ec = NOT_FOUND_ERR; - return; - } + RefPtr<Node> referenceNode = startIsText ? &startContainer() : startContainer().traverseToChildAt(startOffset()); + Node* parentNode = referenceNode ? referenceNode->parentNode() : &startContainer(); + if (!is<ContainerNode>(parentNode)) + return Exception { HIERARCHY_REQUEST_ERR }; - // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of - // the Range is read-only. - if (containedByReadOnly()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } + Ref<ContainerNode> parent = downcast<ContainerNode>(*parentNode); - // HIERARCHY_REQUEST_ERR: Raised if the container of the start of the Range is of a type that - // does not allow children of the type of newNode or if newNode is an ancestor of the container. + auto result = parent->ensurePreInsertionValidity(node, referenceNode.get()); + if (result.hasException()) + return result.releaseException(); - // an extra one here - if a text node is going to split, it must have a parent to insert into - bool startIsText = m_start.container()->isTextNode(); - if (startIsText && !m_start.container()->parentNode()) { - ec = HIERARCHY_REQUEST_ERR; - return; + EventQueueScope scope; + if (startIsText) { + auto result = downcast<Text>(startContainer()).splitText(startOffset()); + if (result.hasException()) + return result.releaseException(); + referenceNode = result.releaseReturnValue(); } - // In the case where the container is a text node, we check against the container's parent, because - // text nodes get split up upon insertion. - Node* checkAgainst; - if (startIsText) - checkAgainst = m_start.container()->parentNode(); - else - checkAgainst = m_start.container(); - - Node::NodeType newNodeType = newNode->nodeType(); - int numNewChildren; - if (newNodeType == Node::DOCUMENT_FRAGMENT_NODE && !newNode->isShadowRoot()) { - // check each child node, not the DocumentFragment itself - numNewChildren = 0; - for (Node* c = newNode->firstChild(); c; c = c->nextSibling()) { - if (!checkAgainst->childTypeAllowed(c->nodeType())) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - ++numNewChildren; - } - } else { - numNewChildren = 1; - if (!checkAgainst->childTypeAllowed(newNodeType)) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - } + if (referenceNode == node.ptr()) + referenceNode = referenceNode->nextSibling(); - for (Node* n = m_start.container(); n; n = n->parentNode()) { - if (n == newNode) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - } + auto removeResult = node->remove(); + if (removeResult.hasException()) + return removeResult.releaseException(); - // INVALID_NODE_TYPE_ERR: Raised if newNode is an Attr, Entity, Notation, ShadowRoot or Document node. - switch (newNodeType) { - case Node::ATTRIBUTE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - case Node::DOCUMENT_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - default: - if (newNode->isShadowRoot()) { - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } - break; - } + unsigned newOffset = referenceNode ? referenceNode->computeNodeIndex() : parent->countChildNodes(); + if (is<DocumentFragment>(node.get())) + newOffset += downcast<DocumentFragment>(node.get()).countChildNodes(); + else + ++newOffset; - EventQueueScope scope; - bool collapsed = m_start == m_end; - RefPtr<Node> container; - if (startIsText) { - container = m_start.container(); - RefPtr<Text> newText = toText(container.get())->splitText(m_start.offset(), ec); - if (ec) - return; - - container = m_start.container(); - container->parentNode()->insertBefore(newNode.release(), newText.get(), ec); - if (ec) - return; + auto insertResult = parent->insertBefore(node, referenceNode.get()); + if (insertResult.hasException()) + return insertResult.releaseException(); - if (collapsed && newText->parentNode() == container && &container->document() == &ownerDocument()) - m_end.setToBeforeChild(newText.get()); - } else { - container = m_start.container(); - RefPtr<Node> firstInsertedChild = newNodeType == Node::DOCUMENT_FRAGMENT_NODE ? newNode->firstChild() : newNode; - RefPtr<Node> lastInsertedChild = newNodeType == Node::DOCUMENT_FRAGMENT_NODE ? newNode->lastChild() : newNode; - RefPtr<Node> childAfterInsertedContent = container->childNode(m_start.offset()); - container->insertBefore(newNode.release(), childAfterInsertedContent.get(), ec); - if (ec) - return; + if (collapsed()) + return setEnd(WTFMove(parent), newOffset); - if (collapsed && numNewChildren && &container->document() == &ownerDocument()) { - if (firstInsertedChild->parentNode() == container) - m_start.setToBeforeChild(firstInsertedChild.get()); - if (lastInsertedChild->parentNode() == container) - m_end.set(container, lastInsertedChild->nodeIndex() + 1, lastInsertedChild.get()); - } - } + return { }; } -String Range::toString(ExceptionCode& ec) const +String Range::toString() const { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return String(); - } - StringBuilder builder; Node* pastLast = pastLastNode(); - for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(n)) { - if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE) { - const String& data = static_cast<CharacterData*>(n)->data(); - int length = data.length(); - int start = (n == m_start.container()) ? std::min(std::max(0, m_start.offset()), length) : 0; - int end = (n == m_end.container()) ? std::min(std::max(start, m_end.offset()), length) : length; + for (Node* node = firstNode(); node != pastLast; node = NodeTraversal::next(*node)) { + auto type = node->nodeType(); + if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) { + auto& data = downcast<CharacterData>(*node).data(); + unsigned length = data.length(); + unsigned start = node == &startContainer() ? std::min(m_start.offset(), length) : 0U; + unsigned end = node == &endContainer() ? std::min(std::max(start, m_end.offset()), length) : length; builder.append(data, start, end - start); } } @@ -1102,536 +960,229 @@ String Range::toHTML() const String Range::text() const { - if (!m_start.container()) - return String(); - // We need to update layout, since plainText uses line boxes in the render tree. // FIXME: As with innerText, we'd like this to work even if there are no render objects. - m_start.container()->document().updateLayout(); + startContainer().document().updateLayout(); return plainText(this); } -PassRefPtr<DocumentFragment> Range::createContextualFragment(const String& markup, ExceptionCode& ec) +// https://w3c.github.io/DOM-Parsing/#widl-Range-createContextualFragment-DocumentFragment-DOMString-fragment +ExceptionOr<Ref<DocumentFragment>> Range::createContextualFragment(const String& markup) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - Node* element = m_start.container()->isElementNode() ? m_start.container() : m_start.container()->parentNode(); - if (!element || !element->isHTMLElement()) { - ec = NOT_SUPPORTED_ERR; - return 0; - } - - RefPtr<DocumentFragment> fragment = WebCore::createContextualFragment(markup, toHTMLElement(element), AllowScriptingContentAndDoNotMarkAlreadyStarted, ec); - if (!fragment) - return 0; - - return fragment.release(); + Node& node = startContainer(); + RefPtr<Element> element; + if (is<Document>(node) || is<DocumentFragment>(node)) + element = nullptr; + else if (is<Element>(node)) + element = &downcast<Element>(node); + else + element = node.parentElement(); + if (!element || (is<HTMLDocument>(element->document()) && is<HTMLHtmlElement>(*element))) + element = HTMLBodyElement::create(node.document()); + return WebCore::createContextualFragment(*element, markup, AllowScriptingContentAndDoNotMarkAlreadyStarted); } - -void Range::detach(ExceptionCode& ec) +void Range::detach() { - // Check first to see if we've already detached: - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - m_ownerDocument->detachRange(this); - - m_start.clear(); - m_end.clear(); + // This is now a no-op as per the DOM specification. } -Node* Range::checkNodeWOffset(Node* n, int offset, ExceptionCode& ec) const +ExceptionOr<Node*> Range::checkNodeWOffset(Node& node, unsigned offset) const { - switch (n->nodeType()) { + switch (node.nodeType()) { case Node::DOCUMENT_TYPE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return 0; + return Exception { INVALID_NODE_TYPE_ERR }; case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: case Node::TEXT_NODE: - if (static_cast<unsigned>(offset) > toCharacterData(n)->length()) - ec = INDEX_SIZE_ERR; - return 0; case Node::PROCESSING_INSTRUCTION_NODE: - if (static_cast<unsigned>(offset) > toProcessingInstruction(n)->data().length()) - ec = INDEX_SIZE_ERR; - return 0; + if (offset > downcast<CharacterData>(node).length()) + return Exception { INDEX_SIZE_ERR }; + return nullptr; case Node::ATTRIBUTE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: case Node::DOCUMENT_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::XPATH_NAMESPACE_NODE: { + case Node::ELEMENT_NODE: { if (!offset) - return 0; - Node* childBefore = n->childNode(offset - 1); + return nullptr; + Node* childBefore = node.traverseToChildAt(offset - 1); if (!childBefore) - ec = INDEX_SIZE_ERR; + return Exception { INDEX_SIZE_ERR }; return childBefore; } } ASSERT_NOT_REACHED(); - return 0; + return Exception { INVALID_NODE_TYPE_ERR }; } -void Range::checkNodeBA(Node* n, ExceptionCode& ec) const +Ref<Range> Range::cloneRange() const { - // INVALID_NODE_TYPE_ERR: Raised if the root container of refNode is not an - // Attr, Document, DocumentFragment or ShadowRoot node, or part of a SVG shadow DOM tree, - // or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation node. - - switch (n->nodeType()) { - case Node::ATTRIBUTE_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - case Node::DOCUMENT_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_TYPE_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - break; - } - - Node* root = n; - while (ContainerNode* parent = root->parentNode()) - root = parent; - - switch (root->nodeType()) { - case Node::ATTRIBUTE_NODE: - case Node::DOCUMENT_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - break; - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_TYPE_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::NOTATION_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } + return Range::create(ownerDocument(), &startContainer(), m_start.offset(), &endContainer(), m_end.offset()); } -PassRefPtr<Range> Range::cloneRange(ExceptionCode& ec) const +ExceptionOr<void> Range::setStartAfter(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return 0; - } - - return Range::create(ownerDocument(), m_start.container(), m_start.offset(), m_end.container(), m_end.offset()); + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; + return setStart(*refNode.parentNode(), refNode.computeNodeIndex() + 1); } -void Range::setStartAfter(Node* refNode, ExceptionCode& ec) +ExceptionOr<void> Range::setEndBefore(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - ec = 0; - checkNodeBA(refNode, ec); - if (ec) - return; - - setStart(refNode->parentNode(), refNode->nodeIndex() + 1, ec); + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; + return setEnd(*refNode.parentNode(), refNode.computeNodeIndex()); } -void Range::setEndBefore(Node* refNode, ExceptionCode& ec) +ExceptionOr<void> Range::setEndAfter(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - ec = 0; - checkNodeBA(refNode, ec); - if (ec) - return; - - setEnd(refNode->parentNode(), refNode->nodeIndex(), ec); + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; + return setEnd(*refNode.parentNode(), refNode.computeNodeIndex() + 1); } -void Range::setEndAfter(Node* refNode, ExceptionCode& ec) +ExceptionOr<void> Range::selectNode(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - ec = 0; - checkNodeBA(refNode, ec); - if (ec) - return; - - setEnd(refNode->parentNode(), refNode->nodeIndex() + 1, ec); -} - -void Range::selectNode(Node* refNode, ExceptionCode& ec) -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - // INVALID_NODE_TYPE_ERR: Raised if an ancestor of refNode is an Entity, Notation or - // DocumentType node or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation - // node. - for (ContainerNode* anc = refNode->parentNode(); anc; anc = anc->parentNode()) { - switch (anc->nodeType()) { - case Node::ATTRIBUTE_NODE: - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - case Node::DOCUMENT_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - break; - case Node::DOCUMENT_TYPE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } - } - - switch (refNode->nodeType()) { - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_TYPE_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - break; - case Node::ATTRIBUTE_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - case Node::DOCUMENT_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; - if (&ownerDocument() != &refNode->document()) - setDocument(refNode->document()); + if (&ownerDocument() != &refNode.document()) + setDocument(refNode.document()); - ec = 0; - setStartBefore(refNode, ec); - if (ec) - return; - setEndAfter(refNode, ec); + unsigned index = refNode.computeNodeIndex(); + auto result = setStart(*refNode.parentNode(), index); + if (result.hasException()) + return result.releaseException(); + return setEnd(*refNode.parentNode(), index + 1); } -void Range::selectNodeContents(Node* refNode, ExceptionCode& ec) +ExceptionOr<void> Range::selectNodeContents(Node& refNode) { - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } - - // INVALID_NODE_TYPE_ERR: Raised if refNode or an ancestor of refNode is an Entity, Notation - // or DocumentType node. - for (Node* n = refNode; n; n = n->parentNode()) { - switch (n->nodeType()) { - case Node::ATTRIBUTE_NODE: - case Node::CDATA_SECTION_NODE: - case Node::COMMENT_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - case Node::DOCUMENT_NODE: - case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::PROCESSING_INSTRUCTION_NODE: - case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: - break; - case Node::DOCUMENT_TYPE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; - } - } + if (refNode.isDocumentTypeNode()) + return Exception { INVALID_NODE_TYPE_ERR }; - if (&ownerDocument() != &refNode->document()) - setDocument(refNode->document()); + if (&ownerDocument() != &refNode.document()) + setDocument(refNode.document()); m_start.setToStartOfNode(refNode); m_end.setToEndOfNode(refNode); + + return { }; } -void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) +// https://dom.spec.whatwg.org/#dom-range-surroundcontents +ExceptionOr<void> Range::surroundContents(Node& newParent) { - RefPtr<Node> newParent = passNewParent; - - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } + Ref<Node> protectedNewParent(newParent); - if (!newParent) { - ec = NOT_FOUND_ERR; - return; - } + // Step 1: If a non-Text node is partially contained in the context object, then throw an InvalidStateError. + Node* startNonTextContainer = &startContainer(); + if (startNonTextContainer->nodeType() == Node::TEXT_NODE) + startNonTextContainer = startNonTextContainer->parentNode(); + Node* endNonTextContainer = &endContainer(); + if (endNonTextContainer->nodeType() == Node::TEXT_NODE) + endNonTextContainer = endNonTextContainer->parentNode(); + if (startNonTextContainer != endNonTextContainer) + return Exception { INVALID_STATE_ERR }; - // INVALID_NODE_TYPE_ERR: Raised if node is an Attr, Entity, DocumentType, Notation, - // Document, or DocumentFragment node. - switch (newParent->nodeType()) { + // Step 2: If newParent is a Document, DocumentType, or DocumentFragment node, then throw an InvalidNodeTypeError. + switch (newParent.nodeType()) { case Node::ATTRIBUTE_NODE: case Node::DOCUMENT_FRAGMENT_NODE: case Node::DOCUMENT_NODE: case Node::DOCUMENT_TYPE_NODE: - case Node::ENTITY_NODE: - case Node::NOTATION_NODE: - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; + return Exception { INVALID_NODE_TYPE_ERR }; case Node::CDATA_SECTION_NODE: case Node::COMMENT_NODE: case Node::ELEMENT_NODE: - case Node::ENTITY_REFERENCE_NODE: case Node::PROCESSING_INSTRUCTION_NODE: case Node::TEXT_NODE: - case Node::XPATH_NAMESPACE_NODE: break; } - // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of - // the Range is read-only. - if (containedByReadOnly()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - - // Raise a HIERARCHY_REQUEST_ERR if m_start.container() doesn't accept children like newParent. - Node* parentOfNewParent = m_start.container(); - - // If m_start.container() is a character data node, it will be split and it will be its parent that will - // need to accept newParent (or in the case of a comment, it logically "would" be inserted into the parent, - // although this will fail below for another reason). - if (parentOfNewParent->isCharacterDataNode()) - parentOfNewParent = parentOfNewParent->parentNode(); - if (!parentOfNewParent || !parentOfNewParent->childTypeAllowed(newParent->nodeType())) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - - if (newParent->contains(m_start.container())) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - - // FIXME: Do we need a check if the node would end up with a child node of a type not - // allowed by the type of node? - - // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-Text node. - Node* startNonTextContainer = m_start.container(); - if (startNonTextContainer->nodeType() == Node::TEXT_NODE) - startNonTextContainer = startNonTextContainer->parentNode(); - Node* endNonTextContainer = m_end.container(); - if (endNonTextContainer->nodeType() == Node::TEXT_NODE) - endNonTextContainer = endNonTextContainer->parentNode(); - if (startNonTextContainer != endNonTextContainer) { - ec = RangeException::BAD_BOUNDARYPOINTS_ERR; - return; - } - - ec = 0; - while (Node* n = newParent->firstChild()) { - toContainerNode(newParent.get())->removeChild(n, ec); - if (ec) - return; - } - RefPtr<DocumentFragment> fragment = extractContents(ec); - if (ec) - return; - insertNode(newParent, ec); - if (ec) - return; - newParent->appendChild(fragment.release(), ec); - if (ec) - return; - selectNode(newParent.get(), ec); -} - -void Range::setStartBefore(Node* refNode, ExceptionCode& ec) -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - if (!refNode) { - ec = NOT_FOUND_ERR; - return; - } + // Step 3: Let fragment be the result of extracting context object. + auto fragment = extractContents(); + if (fragment.hasException()) + return fragment.releaseException(); - ec = 0; - checkNodeBA(refNode, ec); - if (ec) - return; + // Step 4: If newParent has children, replace all with null within newParent. + if (newParent.hasChildNodes()) + downcast<ContainerNode>(newParent).replaceAllChildren(nullptr); - setStart(refNode->parentNode(), refNode->nodeIndex(), ec); -} + // Step 5: Insert newParent into context object. + auto insertResult = insertNode(newParent); + if (insertResult.hasException()) + return insertResult.releaseException(); -void Range::checkDeleteExtract(ExceptionCode& ec) -{ - if (!m_start.container()) { - ec = INVALID_STATE_ERR; - return; - } - - ec = 0; - if (!commonAncestorContainer(ec) || ec) - return; - - Node* pastLast = pastLastNode(); - for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(n)) { - if (n->isReadOnlyNode()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { - ec = HIERARCHY_REQUEST_ERR; - return; - } - } + // Step 6: Append fragment to newParent. + auto appendResult = newParent.appendChild(fragment.releaseReturnValue()); + if (appendResult.hasException()) + return appendResult.releaseException(); - if (containedByReadOnly()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } + // Step 7: Select newParent within context object. + return selectNode(newParent); } -bool Range::containedByReadOnly() const +ExceptionOr<void> Range::setStartBefore(Node& refNode) { - for (Node* n = m_start.container(); n; n = n->parentNode()) { - if (n->isReadOnlyNode()) - return true; - } - for (Node* n = m_end.container(); n; n = n->parentNode()) { - if (n->isReadOnlyNode()) - return true; - } - return false; + if (!refNode.parentNode()) + return Exception { INVALID_NODE_TYPE_ERR }; + return setStart(*refNode.parentNode(), refNode.computeNodeIndex()); } Node* Range::firstNode() const { - if (!m_start.container()) - return 0; - if (m_start.container()->offsetInCharacters()) - return m_start.container(); - if (isRendererReplacedElement(m_start.container()->renderer())) - return m_start.container(); - if (Node* child = m_start.container()->childNode(m_start.offset())) + if (startContainer().offsetInCharacters()) + return &startContainer(); + if (Node* child = startContainer().traverseToChildAt(m_start.offset())) return child; if (!m_start.offset()) - return m_start.container(); - return NodeTraversal::nextSkippingChildren(m_start.container()); + return &startContainer(); + return NodeTraversal::nextSkippingChildren(startContainer()); } ShadowRoot* Range::shadowRoot() const { - return startContainer() ? startContainer()->containingShadowRoot() : 0; + return startContainer().containingShadowRoot(); } Node* Range::pastLastNode() const { - if (!m_start.container() || !m_end.container()) - return 0; - if (m_end.container()->offsetInCharacters()) - return NodeTraversal::nextSkippingChildren(m_end.container()); - if (Node* child = m_end.container()->childNode(m_end.offset())) + if (endContainer().offsetInCharacters()) + return NodeTraversal::nextSkippingChildren(endContainer()); + if (Node* child = endContainer().traverseToChildAt(m_end.offset())) return child; - return NodeTraversal::nextSkippingChildren(m_end.container()); + return NodeTraversal::nextSkippingChildren(endContainer()); } -IntRect Range::boundingBox() const +IntRect Range::absoluteBoundingBox() const { IntRect result; Vector<IntRect> rects; - textRects(rects); - const size_t n = rects.size(); - for (size_t i = 0; i < n; ++i) - result.unite(rects[i]); + absoluteTextRects(rects); + for (auto& rect : rects) + result.unite(rect); return result; } -void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFixedPosition* inFixed) const +void Range::absoluteTextRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFixedPosition* inFixed) const { - Node* startContainer = m_start.container(); - Node* endContainer = m_end.container(); - - if (!startContainer || !endContainer) { - if (inFixed) - *inFixed = NotFixedPosition; - return; - } - bool allFixed = true; bool someFixed = false; Node* stopNode = pastLastNode(); - for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(node)) { - RenderObject* r = node->renderer(); - if (!r) + for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + RenderObject* renderer = node->renderer(); + if (!renderer) continue; bool isFixed = false; - if (r->isBR()) - r->absoluteRects(rects, flooredLayoutPoint(r->localToAbsolute())); - else if (r->isText()) { - int startOffset = node == startContainer ? m_start.offset() : 0; - int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max(); - rects.appendVector(toRenderText(r)->absoluteRectsForRange(startOffset, endOffset, useSelectionHeight, &isFixed)); + if (renderer->isBR()) + renderer->absoluteRects(rects, flooredLayoutPoint(renderer->localToAbsolute())); + else if (is<RenderText>(*renderer)) { + unsigned startOffset = node == &startContainer() ? m_start.offset() : 0; + unsigned endOffset = node == &endContainer() ? m_end.offset() : std::numeric_limits<unsigned>::max(); + rects.appendVector(downcast<RenderText>(*renderer).absoluteRectsForRange(startOffset, endOffset, useSelectionHeight, &isFixed)); } else continue; allFixed &= isFixed; @@ -1642,32 +1193,23 @@ void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFi *inFixed = allFixed ? EntirelyFixedPosition : (someFixed ? PartiallyFixedPosition : NotFixedPosition); } -void Range::textQuads(Vector<FloatQuad>& quads, bool useSelectionHeight, RangeInFixedPosition* inFixed) const +void Range::absoluteTextQuads(Vector<FloatQuad>& quads, bool useSelectionHeight, RangeInFixedPosition* inFixed) const { - Node* startContainer = m_start.container(); - Node* endContainer = m_end.container(); - - if (!startContainer || !endContainer) { - if (inFixed) - *inFixed = NotFixedPosition; - return; - } - bool allFixed = true; bool someFixed = false; Node* stopNode = pastLastNode(); - for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(node)) { - RenderObject* r = node->renderer(); - if (!r) + for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + RenderObject* renderer = node->renderer(); + if (!renderer) continue; bool isFixed = false; - if (r->isBR()) - r->absoluteQuads(quads, &isFixed); - else if (r->isText()) { - int startOffset = node == startContainer ? m_start.offset() : 0; - int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max(); - quads.appendVector(toRenderText(r)->absoluteQuadsForRange(startOffset, endOffset, useSelectionHeight, &isFixed)); + if (renderer->isBR()) + renderer->absoluteQuads(quads, &isFixed); + else if (is<RenderText>(*renderer)) { + unsigned startOffset = node == &startContainer() ? m_start.offset() : 0; + unsigned endOffset = node == &endContainer() ? m_end.offset() : std::numeric_limits<unsigned>::max(); + quads.appendVector(downcast<RenderText>(*renderer).absoluteQuadsForRange(startOffset, endOffset, useSelectionHeight, &isFixed)); } else continue; allFixed &= isFixed; @@ -1698,28 +1240,6 @@ static bool intervalsSufficientlyOverlap(int startA, int endA, int startB, int e return minEnd - maxStart >= sufficientOverlap * std::min(lengthA, lengthB); } -#ifndef NDEBUG -static void printRects(Vector<SelectionRect>& rects) -{ - size_t numberOfRects = rects.size(); - for (size_t i = 0; i < numberOfRects; ++i) { - fprintf(stderr, "%zu\t[%d, %d] - [%d, %d]\t%c %s\tis first: %s\tis last:%s\tcontains start: %s\tcontains end: %s\tline: %d\truby: %s\tcolumn: %d\n", - i, - rects[i].rect().x(), rects[i].rect().y(), rects[i].rect().width(), rects[i].rect().height(), - rects[i].isHorizontal() ? 'H' : 'V', - rects[i].direction() == LTR ? "LTR" : "RTL", - rects[i].isFirstOnLine() ? "yes" : "no", - rects[i].isLastOnLine() ? "yes" : "no", - rects[i].containsStart() ? "yes" : "no", - rects[i].containsEnd() ? "yes" : "no", - rects[i].lineNumber(), - rects[i].isRubyText() ? "yes": "no", - rects[i].columnNumber()); - } - fprintf(stderr, "--------------------------------------\n"); -} -#endif - static inline void adjustLineHeightOfSelectionRects(Vector<SelectionRect>& rects, size_t numberOfRects, int lineNumber, int lineTop, int lineHeight) { ASSERT(rects.size() >= numberOfRects); @@ -1735,7 +1255,7 @@ static inline void adjustLineHeightOfSelectionRects(Vector<SelectionRect>& rects static SelectionRect coalesceSelectionRects(const SelectionRect& original, const SelectionRect& previous) { - SelectionRect result(unionRect(previous.rect(), original.rect()), original.isHorizontal(), original.columnNumber()); + SelectionRect result(unionRect(previous.rect(), original.rect()), original.isHorizontal(), original.pageNumber()); result.setDirection(original.containsStart() || original.containsEnd() ? original.direction() : previous.direction()); result.setContainsStart(previous.containsStart() || original.containsStart()); result.setContainsEnd(previous.containsEnd() || original.containsEnd()); @@ -1746,26 +1266,23 @@ static SelectionRect coalesceSelectionRects(const SelectionRect& original, const // This function is similar in spirit to addLineBoxRects, but annotates the returned rectangles // with additional state which helps iOS draw selections in its unique way. -void Range::collectSelectionRects(Vector<SelectionRect>& rects) +int Range::collectSelectionRectsWithoutUnionInteriorLines(Vector<SelectionRect>& rects) { - if (!m_start.container() || !m_end.container()) - return; - - Node* startContainer = m_start.container(); - Node* endContainer = m_end.container(); + auto& startContainer = this->startContainer(); + auto& endContainer = this->endContainer(); int startOffset = m_start.offset(); int endOffset = m_end.offset(); Vector<SelectionRect> newRects; Node* stopNode = pastLastNode(); - bool hasFlippedWritingMode = startContainer->renderer() && startContainer->renderer()->style().isFlippedBlocksWritingMode(); + bool hasFlippedWritingMode = startContainer.renderer() && startContainer.renderer()->style().isFlippedBlocksWritingMode(); bool containsDifferentWritingModes = false; - for (Node* node = firstNode(); node && node != stopNode; node = NodeTraversal::next(node)) { + for (Node* node = firstNode(); node && node != stopNode; node = NodeTraversal::next(*node)) { RenderObject* renderer = node->renderer(); // Only ask leaf render objects for their line box rects. if (renderer && !renderer->firstChildSlow() && renderer->style().userSelect() != SELECT_NONE) { - bool isStartNode = renderer->node() == startContainer; - bool isEndNode = renderer->node() == endContainer; + bool isStartNode = renderer->node() == &startContainer; + bool isEndNode = renderer->node() == &endContainer; if (hasFlippedWritingMode != renderer->style().isFlippedBlocksWritingMode()) containsDifferentWritingModes = true; // FIXME: Sending 0 for the startOffset is a weird way of telling the renderer that the selection @@ -1777,28 +1294,22 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) int beginSelectionOffset = isStartNode ? startOffset : 0; int endSelectionOffset = isEndNode ? endOffset : std::numeric_limits<int>::max(); renderer->collectSelectionRects(newRects, beginSelectionOffset, endSelectionOffset); - size_t numberOfNewRects = newRects.size(); - for (size_t i = 0; i < numberOfNewRects; ++i) { - SelectionRect& selectionRect = newRects[i]; + for (auto& selectionRect : newRects) { if (selectionRect.containsStart() && !isStartNode) selectionRect.setContainsStart(false); if (selectionRect.containsEnd() && !isEndNode) selectionRect.setContainsEnd(false); if (selectionRect.logicalWidth() || selectionRect.logicalHeight()) - rects.append(newRects[i]); + rects.append(selectionRect); } newRects.shrink(0); } } -#ifndef NDEBUG - printRects(rects); -#endif - // The range could span over nodes with different writing modes. // If this is the case, we use the writing mode of the common ancestor. if (containsDifferentWritingModes) { - if (Node* ancestor = commonAncestorContainer(startContainer, endContainer)) + if (Node* ancestor = commonAncestorContainer(&startContainer, &endContainer)) hasFlippedWritingMode = ancestor->renderer()->style().isFlippedBlocksWritingMode(); } @@ -1811,10 +1322,10 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) // Only set the line break bit if the end of the range actually // extends all the way to include the <br>. VisiblePosition helps to // figure this out. - VisiblePosition endPosition(createLegacyEditingPosition(endContainer, endOffset), VP_DEFAULT_AFFINITY); + VisiblePosition endPosition(createLegacyEditingPosition(&endContainer, endOffset), VP_DEFAULT_AFFINITY); VisiblePosition brPosition(createLegacyEditingPosition(stopNode, 0), VP_DEFAULT_AFFINITY); if (endPosition == brPosition) - rects.last().setIsLineBreak(true); + rects.last().setIsLineBreak(true); } int lineTop = std::numeric_limits<int>::max(); @@ -1851,7 +1362,7 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) lineBottom = currentRectBottom; } else { lastLineBottom = lineBottom; - if (currentRectTop <= lastLineBottom && i && rects[i].columnNumber() == rects[i - 1].columnNumber()) { + if (currentRectTop <= lastLineBottom && i && rects[i].pageNumber() == rects[i - 1].pageNumber()) { lastLineTop = lineTop; lineBottom = lastLineTop; } else { @@ -1911,7 +1422,15 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) } else if (selectionRect.direction() == LTR && selectionRect.isLastOnLine()) selectionRect.setLogicalWidth(selectionRect.maxX() - selectionRect.logicalLeft()); } + + return maxLineNumber; +} +void Range::collectSelectionRects(Vector<SelectionRect>& rects) +{ + int maxLineNumber = collectSelectionRectsWithoutUnionInteriorLines(rects); + const size_t numberOfRects = rects.size(); + // Union all the rectangles on interior lines (i.e. not first or last). // On first and last lines, just avoid having overlaps by merging intersecting rectangles. Vector<SelectionRect> unionedRects; @@ -1942,13 +1461,13 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) // For iBooks, the interior lines may cross multiple horizontal pages. interiorUnionRect.unite(currentRect.rect()); } else { - unionedRects.append(SelectionRect(interiorUnionRect, currentRect.isHorizontal(), currentRect.columnNumber())); + unionedRects.append(SelectionRect(interiorUnionRect, currentRect.isHorizontal(), currentRect.pageNumber())); interiorUnionRect = currentRect.rect(); } } else { // Processing last line. if (!interiorUnionRect.isEmpty()) { - unionedRects.append(SelectionRect(interiorUnionRect, currentRect.isHorizontal(), currentRect.columnNumber())); + unionedRects.append(SelectionRect(interiorUnionRect, currentRect.isHorizontal(), currentRect.pageNumber())); interiorUnionRect = IntRect(); } @@ -1968,33 +1487,49 @@ void Range::collectSelectionRects(Vector<SelectionRect>& rects) } #endif -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void Range::formatForDebugger(char* buffer, unsigned length) const { StringBuilder result; - String s; - - if (!m_start.container() || !m_end.container()) - result.appendLiteral("<empty>"); - else { - const int FormatBufferSize = 1024; - char s[FormatBufferSize]; - result.appendLiteral("from offset "); - result.appendNumber(m_start.offset()); - result.appendLiteral(" of "); - m_start.container()->formatForDebugger(s, FormatBufferSize); - result.append(s); - result.appendLiteral(" to offset "); - result.appendNumber(m_end.offset()); - result.appendLiteral(" of "); - m_end.container()->formatForDebugger(s, FormatBufferSize); - result.append(s); - } + + const int FormatBufferSize = 1024; + char s[FormatBufferSize]; + result.appendLiteral("from offset "); + result.appendNumber(m_start.offset()); + result.appendLiteral(" of "); + startContainer().formatForDebugger(s, FormatBufferSize); + result.append(s); + result.appendLiteral(" to offset "); + result.appendNumber(m_end.offset()); + result.appendLiteral(" of "); + endContainer().formatForDebugger(s, FormatBufferSize); + result.append(s); strncpy(buffer, result.toString().utf8().data(), length - 1); } #endif +bool Range::contains(const Range& other) const +{ + if (commonAncestorContainer()->ownerDocument() != other.commonAncestorContainer()->ownerDocument()) + return false; + + auto startToStart = compareBoundaryPoints(Range::START_TO_START, other); + if (startToStart.hasException() || startToStart.releaseReturnValue() > 0) + return false; + + auto endToEnd = compareBoundaryPoints(Range::END_TO_END, other); + return !endToEnd.hasException() && endToEnd.releaseReturnValue() >= 0; +} + +bool Range::contains(const VisiblePosition& position) const +{ + RefPtr<Range> positionRange = makeRange(position, position); + if (!positionRange) + return false; + return contains(*positionRange); +} + bool areRangesEqual(const Range* a, const Range* b) { if (a == b) @@ -2004,30 +1539,40 @@ bool areRangesEqual(const Range* a, const Range* b) return a->startPosition() == b->startPosition() && a->endPosition() == b->endPosition(); } -PassRefPtr<Range> rangeOfContents(Node& node) +bool rangesOverlap(const Range* a, const Range* b) { - RefPtr<Range> range = Range::create(node.document()); - int exception = 0; - range->selectNodeContents(&node, exception); - return range.release(); -} + if (!a || !b) + return false; -int Range::maxStartOffset() const -{ - if (!m_start.container()) - return 0; - if (!m_start.container()->offsetInCharacters()) - return m_start.container()->childNodeCount(); - return m_start.container()->maxCharacterOffset(); + if (a == b) + return true; + + if (a->commonAncestorContainer()->ownerDocument() != b->commonAncestorContainer()->ownerDocument()) + return false; + + short startToStart = a->compareBoundaryPoints(Range::START_TO_START, *b).releaseReturnValue(); + short endToEnd = a->compareBoundaryPoints(Range::END_TO_END, *b).releaseReturnValue(); + + // First range contains the second range. + if (startToStart <= 0 && endToEnd >= 0) + return true; + + // End of first range is inside second range. + if (a->compareBoundaryPoints(Range::START_TO_END, *b).releaseReturnValue() >= 0 && endToEnd <= 0) + return true; + + // Start of first range is inside second range. + if (startToStart >= 0 && a->compareBoundaryPoints(Range::END_TO_START, *b).releaseReturnValue() <= 0) + return true; + + return false; } -int Range::maxEndOffset() const +Ref<Range> rangeOfContents(Node& node) { - if (!m_end.container()) - return 0; - if (!m_end.container()->offsetInCharacters()) - return m_end.container()->childNodeCount(); - return m_end.container()->maxCharacterOffset(); + auto range = Range::create(node.document()); + range->selectNodeContents(node); + return range; } static inline void boundaryNodeChildrenChanged(RangeBoundaryPoint& boundary, ContainerNode& container) @@ -2050,13 +1595,13 @@ static inline void boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint& boundar { for (Node* nodeToBeRemoved = container.firstChild(); nodeToBeRemoved; nodeToBeRemoved = nodeToBeRemoved->nextSibling()) { if (boundary.childBefore() == nodeToBeRemoved) { - boundary.setToStartOfNode(&container); + boundary.setToStartOfNode(container); return; } for (Node* n = boundary.container(); n; n = n->parentNode()) { if (n == nodeToBeRemoved) { - boundary.setToStartOfNode(&container); + boundary.setToStartOfNode(container); return; } } @@ -2070,27 +1615,26 @@ void Range::nodeChildrenWillBeRemoved(ContainerNode& container) boundaryNodeChildrenWillBeRemoved(m_end, container); } -static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node* nodeToBeRemoved) +static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node& nodeToBeRemoved) { - if (boundary.childBefore() == nodeToBeRemoved) { + if (boundary.childBefore() == &nodeToBeRemoved) { boundary.childBeforeWillBeRemoved(); return; } for (Node* n = boundary.container(); n; n = n->parentNode()) { - if (n == nodeToBeRemoved) { + if (n == &nodeToBeRemoved) { boundary.setToBeforeChild(nodeToBeRemoved); return; } } } -void Range::nodeWillBeRemoved(Node* node) +void Range::nodeWillBeRemoved(Node& node) { - ASSERT(node); - ASSERT(&node->document() == &ownerDocument()); - ASSERT(node != &ownerDocument()); - ASSERT(node->parentNode()); + ASSERT(&node.document() == &ownerDocument()); + ASSERT(&node != &ownerDocument()); + ASSERT(node.parentNode()); boundaryNodeWillBeRemoved(m_start, node); boundaryNodeWillBeRemoved(m_end, node); } @@ -2137,9 +1681,9 @@ void Range::textRemoved(Node* text, unsigned offset, unsigned length) static inline void boundaryTextNodesMerged(RangeBoundaryPoint& boundary, NodeWithIndex& oldNode, unsigned offset) { if (boundary.container() == oldNode.node()) - boundary.set(oldNode.node()->previousSibling(), boundary.offset() + offset, 0); - else if (boundary.container() == oldNode.node()->parentNode() && boundary.offset() == oldNode.index()) - boundary.set(oldNode.node()->previousSibling(), offset, 0); + boundary.set(*oldNode.node()->previousSibling(), boundary.offset() + offset, 0); + else if (boundary.container() == oldNode.node()->parentNode() && static_cast<int>(boundary.offset()) == oldNode.index()) + boundary.set(*oldNode.node()->previousSibling(), offset, 0); } void Range::textNodesMerged(NodeWithIndex& oldNode, unsigned offset) @@ -2156,30 +1700,42 @@ void Range::textNodesMerged(NodeWithIndex& oldNode, unsigned offset) static inline void boundaryTextNodesSplit(RangeBoundaryPoint& boundary, Text* oldNode) { - if (boundary.container() != oldNode) + auto* parent = oldNode->parentNode(); + if (boundary.container() == oldNode) { + unsigned splitOffset = oldNode->length(); + unsigned boundaryOffset = boundary.offset(); + if (boundaryOffset > splitOffset) { + if (parent) + boundary.set(*oldNode->nextSibling(), boundaryOffset - splitOffset, 0); + else + boundary.setOffset(splitOffset); + } return; - unsigned boundaryOffset = boundary.offset(); - if (boundaryOffset <= oldNode->length()) + } + if (!parent) return; - boundary.set(oldNode->nextSibling(), boundaryOffset - oldNode->length(), 0); + if (boundary.container() == parent && boundary.childBefore() == oldNode) { + auto* newChild = oldNode->nextSibling(); + ASSERT(newChild); + boundary.setToAfterChild(*newChild); + } } void Range::textNodeSplit(Text* oldNode) { ASSERT(oldNode); ASSERT(&oldNode->document() == &ownerDocument()); - ASSERT(oldNode->parentNode()); ASSERT(oldNode->isTextNode()); - ASSERT(oldNode->nextSibling()); - ASSERT(oldNode->nextSibling()->isTextNode()); + ASSERT(!oldNode->parentNode() || oldNode->nextSibling()); + ASSERT(!oldNode->parentNode() || oldNode->nextSibling()->isTextNode()); boundaryTextNodesSplit(m_start, oldNode); boundaryTextNodesSplit(m_end, oldNode); } -void Range::expand(const String& unit, ExceptionCode& ec) +ExceptionOr<void> Range::expand(const String& unit) { - VisiblePosition start(startPosition()); - VisiblePosition end(endPosition()); + VisiblePosition start { startPosition() }; + VisiblePosition end { endPosition() }; if (unit == "word") { start = startOfWord(start); end = endOfWord(end); @@ -2193,97 +1749,94 @@ void Range::expand(const String& unit, ExceptionCode& ec) start = startOfDocument(start); end = endOfDocument(end); } else - return; - setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), ec); - setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), ec); + return { }; + + auto* startContainer = start.deepEquivalent().containerNode(); + if (!startContainer) + return Exception { TypeError }; + auto result = setStart(*startContainer, start.deepEquivalent().computeOffsetInContainerNode()); + if (result.hasException()) + return result.releaseException(); + auto* endContainer = end.deepEquivalent().containerNode(); + if (!endContainer) + return Exception { TypeError }; + return setEnd(*endContainer, end.deepEquivalent().computeOffsetInContainerNode()); } -PassRefPtr<ClientRectList> Range::getClientRects() const +Ref<ClientRectList> Range::getClientRects() const { - if (!m_start.container()) - return ClientRectList::create(); - - ownerDocument().updateLayoutIgnorePendingStylesheets(); - - Vector<FloatQuad> quads; - getBorderAndTextQuads(quads); - - return ClientRectList::create(quads); + return ClientRectList::create(borderAndTextQuads(CoordinateSpace::Client)); } -PassRefPtr<ClientRect> Range::getBoundingClientRect() const +Ref<ClientRect> Range::getBoundingClientRect() const { - return ClientRect::create(boundingRect()); + return ClientRect::create(boundingRect(CoordinateSpace::Client)); } -void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const +Vector<FloatQuad> Range::borderAndTextQuads(CoordinateSpace space) const { - Node* startContainer = m_start.container(); - Node* endContainer = m_end.container(); + Vector<FloatQuad> quads; + + ownerDocument().updateLayoutIgnorePendingStylesheets(); + Node* stopNode = pastLastNode(); HashSet<Node*> selectedElementsSet; - for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(node)) { - if (node->isElementNode()) + for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + if (is<Element>(*node)) selectedElementsSet.add(node); } // Don't include elements that are only partially selected. - Node* lastNode = m_end.childBefore() ? m_end.childBefore() : endContainer; + Node* lastNode = m_end.childBefore() ? m_end.childBefore() : &endContainer(); for (Node* parent = lastNode->parentNode(); parent; parent = parent->parentNode()) selectedElementsSet.remove(parent); - for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(node)) { - if (node->isElementNode() && selectedElementsSet.contains(node) && !selectedElementsSet.contains(node->parentNode())) { - if (RenderBoxModelObject* renderBoxModelObject = toElement(node)->renderBoxModelObject()) { + for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + if (is<Element>(*node) && selectedElementsSet.contains(node) && !selectedElementsSet.contains(node->parentNode())) { + if (auto* renderer = downcast<Element>(*node).renderBoxModelObject()) { Vector<FloatQuad> elementQuads; - renderBoxModelObject->absoluteQuads(elementQuads); - ownerDocument().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(elementQuads, renderBoxModelObject->style()); - + renderer->absoluteQuads(elementQuads); + if (space == CoordinateSpace::Client) + node->document().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(elementQuads, renderer->style()); quads.appendVector(elementQuads); } - } else if (node->isTextNode()) { - if (RenderObject* renderer = toText(node)->renderer()) { - const RenderText& renderText = toRenderText(*renderer); - int startOffset = (node == startContainer) ? m_start.offset() : 0; - int endOffset = (node == endContainer) ? m_end.offset() : INT_MAX; - - auto textQuads = renderText.absoluteQuadsForRange(startOffset, endOffset); - ownerDocument().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(textQuads, renderText.style()); - + } else if (is<Text>(*node)) { + if (auto* renderer = downcast<Text>(*node).renderer()) { + unsigned startOffset = node == &startContainer() ? m_start.offset() : 0; + unsigned endOffset = node == &endContainer() ? m_end.offset() : std::numeric_limits<unsigned>::max(); + auto textQuads = renderer->absoluteQuadsForRange(startOffset, endOffset); + if (space == CoordinateSpace::Client) + node->document().adjustFloatQuadsForScrollAndAbsoluteZoomAndFrameScale(textQuads, renderer->style()); quads.appendVector(textQuads); } } } + + return quads; } -FloatRect Range::boundingRect() const +FloatRect Range::boundingRect(CoordinateSpace space) const { - if (!m_start.container()) - return FloatRect(); - - ownerDocument().updateLayoutIgnorePendingStylesheets(); - - Vector<FloatQuad> quads; - getBorderAndTextQuads(quads); - if (quads.isEmpty()) - return FloatRect(); - FloatRect result; - for (size_t i = 0; i < quads.size(); ++i) - result.unite(quads[i].boundingBox()); - + for (auto& quad : borderAndTextQuads(space)) + result.unite(quad.boundingBox()); return result; } +FloatRect Range::absoluteBoundingRect() const +{ + return boundingRect(CoordinateSpace::Absolute); +} + } // namespace WebCore -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void showTree(const WebCore::Range* range) { if (range && range->boundaryPointsValid()) { - range->startContainer()->showTreeAndMark(range->startContainer(), "S", range->endContainer(), "E"); + range->startContainer().showTreeAndMark(&range->startContainer(), "S", &range->endContainer(), "E"); fprintf(stderr, "start offset: %d, end offset: %d\n", range->startOffset(), range->endOffset()); } } |