diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
commit | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch) | |
tree | 4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/dom/Range.cpp | |
parent | 9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff) | |
download | external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/dom/Range.cpp')
-rw-r--r-- | WebCore/dom/Range.cpp | 1289 |
1 files changed, 704 insertions, 585 deletions
diff --git a/WebCore/dom/Range.cpp b/WebCore/dom/Range.cpp index 176c07b..afd563e 100644 --- a/WebCore/dom/Range.cpp +++ b/WebCore/dom/Range.cpp @@ -1,9 +1,9 @@ -/** +/* * (C) 1999 Lars Knoll (knoll@kde.org) * (C) 2000 Gunnstein Lye (gunnstein@netcom.no) * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) * (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,19 +23,23 @@ #include "config.h" #include "Range.h" +#include "RangeException.h" +#include "CString.h" #include "Document.h" #include "DocumentFragment.h" #include "ExceptionCode.h" #include "HTMLElement.h" #include "HTMLNames.h" +#include "NodeWithIndex.h" #include "ProcessingInstruction.h" -#include "RangeException.h" #include "RenderBlock.h" #include "Text.h" #include "TextIterator.h" #include "markup.h" #include "visible_units.h" +#include <stdio.h> +#include <wtf/RefCountedLeakCounter.h> namespace WebCore { @@ -43,162 +47,140 @@ using namespace std; using namespace HTMLNames; #ifndef NDEBUG -class RangeCounter { -public: - static unsigned count; - ~RangeCounter() - { - if (count) - fprintf(stderr, "LEAK: %u Range\n", count); - } -}; -unsigned RangeCounter::count = 0; -static RangeCounter rangeCounter; +static WTF::RefCountedLeakCounter rangeCounter("Range"); #endif -Range::Range(Document* ownerDocument) +inline Range::Range(PassRefPtr<Document> ownerDocument) : m_ownerDocument(ownerDocument) - , m_startContainer(ownerDocument) - , m_startOffset(0) - , m_endContainer(ownerDocument) - , m_endOffset(0) - , m_detached(false) + , m_start(m_ownerDocument) + , m_end(m_ownerDocument) { #ifndef NDEBUG - ++RangeCounter::count; + rangeCounter.increment(); #endif + + m_ownerDocument->attachRange(this); +} + +PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument) +{ + return adoptRef(new Range(ownerDocument)); } -Range::Range(Document* ownerDocument, - Node* startContainer, int startOffset, - Node* endContainer, int endOffset) +inline Range::Range(PassRefPtr<Document> ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset) : m_ownerDocument(ownerDocument) - , m_startContainer(ownerDocument) - , m_startOffset(0) - , m_endContainer(ownerDocument) - , m_endOffset(0) - , m_detached(false) + , m_start(m_ownerDocument) + , m_end(m_ownerDocument) { #ifndef NDEBUG - ++RangeCounter::count; + rangeCounter.increment(); #endif + + m_ownerDocument->attachRange(this); + // Simply setting the containers and offsets directly would not do any of the checking - // that setStart and setEnd do, so we must call those functions. + // that setStart and setEnd do, so we call those functions. ExceptionCode ec = 0; setStart(startContainer, startOffset, ec); - ASSERT(ec == 0); + ASSERT(!ec); setEnd(endContainer, endOffset, ec); - ASSERT(ec == 0); + ASSERT(!ec); } -Range::Range(Document* ownerDocument, const Position& start, const Position& end) - : m_ownerDocument(ownerDocument) - , m_startContainer(ownerDocument) - , m_startOffset(0) - , m_endContainer(ownerDocument) - , m_endOffset(0) - , m_detached(false) +PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset) { -#ifndef NDEBUG - ++RangeCounter::count; -#endif - // Simply setting the containers and offsets directly would not do any of the checking - // that setStart and setEnd do, so we must call those functions. - ExceptionCode ec = 0; - setStart(start.node(), start.offset(), ec); - ASSERT(ec == 0); - setEnd(end.node(), end.offset(), ec); - ASSERT(ec == 0); + return adoptRef(new Range(ownerDocument, startContainer, startOffset, endContainer, endOffset)); +} + +PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument, const Position& start, const Position& end) +{ + return adoptRef(new Range(ownerDocument, start.container.get(), start.posOffset, end.container.get(), end.posOffset)); } Range::~Range() { + if (m_start.container()) + m_ownerDocument->detachRange(this); + #ifndef NDEBUG - --RangeCounter::count; + rangeCounter.decrement(); #endif } -Node *Range::startContainer(ExceptionCode& ec) const +Node* Range::startContainer(ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - return m_startContainer.get(); + return m_start.container(); } int Range::startOffset(ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - return m_startOffset; + return m_start.offset(); } -Node *Range::endContainer(ExceptionCode& ec) const +Node* Range::endContainer(ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - return m_endContainer.get(); + return m_end.container(); } int Range::endOffset(ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - return m_endOffset; + return m_end.offset(); } -Node *Range::commonAncestorContainer(ExceptionCode& ec) const +Node* Range::commonAncestorContainer(ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - Node *com = commonAncestorContainer(m_startContainer.get(), m_endContainer.get()); - if (!com) // should never happen - ec = WRONG_DOCUMENT_ERR; - return com; + return commonAncestorContainer(m_start.container(), m_end.container()); } -Node *Range::commonAncestorContainer(Node *containerA, Node *containerB) +Node* Range::commonAncestorContainer(Node* containerA, Node* containerB) { - Node *parentStart; - - for (parentStart = containerA; parentStart; parentStart = parentStart->parentNode()) { - Node *parentEnd = containerB; - while (parentEnd && (parentStart != parentEnd)) - parentEnd = parentEnd->parentNode(); - if (parentStart == parentEnd) - break; + for (Node* parentA = containerA; parentA; parentA = parentA->parentNode()) { + for (Node* parentB = containerB; parentB; parentB = parentB->parentNode()) { + if (parentA == parentB) + return parentA; + } } - - return parentStart; + return 0; } bool Range::collapsed(ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - return (m_startContainer == m_endContainer && m_startOffset == m_endOffset); + return m_start == m_end; } -void Range::setStart( Node *refNode, int offset, ExceptionCode& ec) +void Range::setStart(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -214,30 +196,29 @@ void Range::setStart( Node *refNode, int offset, ExceptionCode& ec) } ec = 0; - checkNodeWOffset(refNode, offset, ec); + Node* childNode = checkNodeWOffset(refNode.get(), offset, ec); if (ec) return; - m_startContainer = refNode; - m_startOffset = offset; + m_start.set(refNode, offset, childNode); // check if different root container - Node* endRootContainer = m_endContainer.get(); + Node* endRootContainer = m_end.container(); while (endRootContainer->parentNode()) endRootContainer = endRootContainer->parentNode(); - Node* startRootContainer = m_startContainer.get(); + Node* startRootContainer = m_start.container(); while (startRootContainer->parentNode()) startRootContainer = startRootContainer->parentNode(); if (startRootContainer != endRootContainer) collapse(true, ec); // check if new start after end - else if (compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) > 0) + else if (compareBoundaryPoints(m_start.container(), m_start.offset(), m_end.container(), m_end.offset()) > 0) collapse(true, ec); } -void Range::setEnd( Node *refNode, int offset, ExceptionCode& ec) +void Range::setEnd(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -253,41 +234,37 @@ void Range::setEnd( Node *refNode, int offset, ExceptionCode& ec) } ec = 0; - checkNodeWOffset(refNode, offset, ec); + Node* childNode = checkNodeWOffset(refNode.get(), offset, ec); if (ec) return; - m_endContainer = refNode; - m_endOffset = offset; + m_end.set(refNode, offset, childNode); // check if different root container - Node* endRootContainer = m_endContainer.get(); + Node* endRootContainer = m_end.container(); while (endRootContainer->parentNode()) endRootContainer = endRootContainer->parentNode(); - Node* startRootContainer = m_startContainer.get(); + Node* startRootContainer = m_start.container(); while (startRootContainer->parentNode()) startRootContainer = startRootContainer->parentNode(); if (startRootContainer != endRootContainer) collapse(false, ec); // check if new end before start - if (compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) > 0) + if (compareBoundaryPoints(m_start.container(), m_start.offset(), m_end.container(), m_end.offset()) > 0) collapse(false, ec); } -void Range::collapse( bool toStart, ExceptionCode& ec) +void Range::collapse(bool toStart, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } - if (toStart) { // collapse to start - m_endContainer = m_startContainer; - m_endOffset = m_startOffset; - } else { // collapse to end - m_startContainer = m_endContainer; - m_startOffset = m_endOffset; - } + if (toStart) + m_end = m_start; + else + m_start = m_end; } bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec) @@ -297,13 +274,13 @@ bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec) return false; } - if (m_detached && refNode->attached()) { + if (!m_start.container() && refNode->attached()) { ec = INVALID_STATE_ERR; return false; } - if (!m_detached && !refNode->attached()) { - // firefox doesn't throw an exception for this case; it returns false + if (m_start.container() && !refNode->attached()) { + // Firefox doesn't throw an exception for this case; it returns false. return false; } @@ -317,12 +294,8 @@ bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec) if (ec) return false; - // point is not before the start and not after the end - if ((compareBoundaryPoints(refNode, offset, m_startContainer.get(), m_startOffset) != -1) && - (compareBoundaryPoints(refNode, offset, m_endContainer.get(), m_endOffset) != 1)) - return true; - else - return false; + return compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset()) >= 0 + && compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset()) <= 0; } short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec) @@ -336,13 +309,13 @@ short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec) return 0; } - if (m_detached && refNode->attached()) { + if (!m_start.container() && refNode->attached()) { ec = INVALID_STATE_ERR; return 0; } - if (!m_detached && !refNode->attached()) { - // firefox doesn't throw an exception for this case; it returns -1 + if (m_start.container() && !refNode->attached()) { + // Firefox doesn't throw an exception for this case; it returns -1. return -1; } @@ -357,16 +330,15 @@ short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec) return 0; // compare to start, and point comes before - if (compareBoundaryPoints(refNode, offset, m_startContainer.get(), m_startOffset) == -1) + if (compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset()) < 0) return -1; // compare to end, and point comes after - else if (compareBoundaryPoints(refNode, offset, m_endContainer.get(), m_endOffset) == 1) + if (compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset()) > 0) return 1; - + // point is in the middle of this range, or on the boundary points - else - return 0; + return 0; } Range::CompareResults Range::compareNode(Node* refNode, ExceptionCode& ec) @@ -380,23 +352,23 @@ Range::CompareResults Range::compareNode(Node* refNode, ExceptionCode& ec) return NODE_BEFORE; } - if (m_detached && refNode->attached()) { + if (!m_start.container() && refNode->attached()) { ec = INVALID_STATE_ERR; return NODE_BEFORE; } - if (!m_detached && !refNode->attached()) { - // firefox doesn't throw an exception for this case; it returns 0 + if (m_start.container() && !refNode->attached()) { + // Firefox doesn't throw an exception for this case; it returns 0. return NODE_BEFORE; } if (refNode->document() != m_ownerDocument) { - // firefox doesn't throw an exception for this case; it returns 0 + // Firefox doesn't throw an exception for this case; it returns 0. return NODE_BEFORE; } Node* parentNode = refNode->parentNode(); - unsigned nodeIndex = refNode->nodeIndex(); + int nodeIndex = refNode->nodeIndex(); if (!parentNode) { // if the node is the top document we should return NODE_BEFORE_AND_AFTER @@ -405,21 +377,21 @@ Range::CompareResults Range::compareNode(Node* refNode, ExceptionCode& ec) return NODE_BEFORE; } - if (comparePoint(parentNode, nodeIndex, ec) == -1) { // starts before - if (comparePoint(parentNode, nodeIndex + 1, ec) == 1) // ends after the range + 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) == 1) // ends after the range + if (comparePoint(parentNode, nodeIndex + 1, ec) > 0) // ends after the range return NODE_AFTER; return NODE_INSIDE; // ends inside the range } } -short Range::compareBoundaryPoints(CompareHow how, const Range *sourceRange, ExceptionCode& ec) const +short Range::compareBoundaryPoints(CompareHow how, const Range* sourceRange, ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } @@ -430,10 +402,10 @@ short Range::compareBoundaryPoints(CompareHow how, const Range *sourceRange, Exc } ec = 0; - Node *thisCont = commonAncestorContainer(ec); + Node* thisCont = commonAncestorContainer(ec); if (ec) return 0; - Node *sourceCont = sourceRange->commonAncestorContainer(ec); + Node* sourceCont = sourceRange->commonAncestorContainer(ec); if (ec) return 0; @@ -442,8 +414,8 @@ short Range::compareBoundaryPoints(CompareHow how, const Range *sourceRange, Exc return 0; } - Node *thisTop = thisCont; - Node *sourceTop = sourceCont; + Node* thisTop = thisCont; + Node* sourceTop = sourceCont; while (thisTop->parentNode()) thisTop = thisTop->parentNode(); while (sourceTop->parentNode()) @@ -453,31 +425,26 @@ short Range::compareBoundaryPoints(CompareHow how, const Range *sourceRange, Exc return 0; } - switch(how) - { - case START_TO_START: - return compareBoundaryPoints( m_startContainer.get(), m_startOffset, - sourceRange->startContainer(ec), sourceRange->startOffset(ec) ); - break; - case START_TO_END: - return compareBoundaryPoints( m_startContainer.get(), m_startOffset, - sourceRange->endContainer(ec), sourceRange->endOffset(ec) ); - break; - case END_TO_END: - return compareBoundaryPoints( m_endContainer.get(), m_endOffset, - sourceRange->endContainer(ec), sourceRange->endOffset(ec) ); - break; - case END_TO_START: - return compareBoundaryPoints( m_endContainer.get(), m_endOffset, - sourceRange->startContainer(ec), sourceRange->startOffset(ec) ); - break; - default: - ec = SYNTAX_ERR; - return 0; + switch (how) { + case START_TO_START: + return compareBoundaryPoints(m_start.container(), m_start.offset(), + sourceRange->m_start.container(), sourceRange->m_start.offset()); + case START_TO_END: + return compareBoundaryPoints(m_end.container(), m_end.offset(), + sourceRange->m_start.container(), sourceRange->m_start.offset()); + case END_TO_END: + return compareBoundaryPoints(m_end.container(), m_end.offset(), + sourceRange->m_end.container(), sourceRange->m_end.offset()); + case END_TO_START: + return compareBoundaryPoints(m_start.container(), m_start.offset(), + sourceRange->m_end.container(), sourceRange->m_end.offset()); } + + ec = SYNTAX_ERR; + return 0; } -short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *containerB, int offsetB ) +short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB) { ASSERT(containerA && containerB); if (!containerA) @@ -487,7 +454,7 @@ short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *contain // see DOM2 traversal & range section 2.5 // case 1: both points have the same container - if(containerA == containerB) { + if (containerA == containerB) { if (offsetA == offsetB) return 0; // A is equal to B if (offsetA < offsetB) @@ -497,12 +464,12 @@ short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *contain } // case 2: node C (container B or an ancestor) is a child node of A - Node *c = containerB; + Node* c = containerB; while (c && c->parentNode() != containerA) c = c->parentNode(); if (c) { int offsetC = 0; - Node *n = containerA->firstChild(); + Node* n = containerA->firstChild(); while (n != c && offsetC < offsetA) { offsetC++; n = n->nextSibling(); @@ -520,13 +487,13 @@ short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *contain c = c->parentNode(); if (c) { int offsetC = 0; - Node *n = containerB->firstChild(); + Node* n = containerB->firstChild(); while (n != c && offsetC < offsetB) { offsetC++; n = n->nextSibling(); } - if(offsetC < offsetB) + if (offsetC < offsetB) return -1; // A is before B else return 1; // A is after B @@ -534,24 +501,24 @@ short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *contain // case 4: containers A & B are siblings, or children of siblings // ### we need to do a traversal here instead - Node *cmnRoot = commonAncestorContainer(containerA,containerB); - if (!cmnRoot) + Node* commonAncestor = commonAncestorContainer(containerA, containerB); + if (!commonAncestor) return 0; - Node *childA = containerA; - while (childA && childA->parentNode() != cmnRoot) + Node* childA = containerA; + while (childA && childA->parentNode() != commonAncestor) childA = childA->parentNode(); if (!childA) - childA = cmnRoot; - Node *childB = containerB; - while (childB && childB->parentNode() != cmnRoot) + childA = commonAncestor; + Node* childB = containerB; + while (childB && childB->parentNode() != commonAncestor) childB = childB->parentNode(); if (!childB) - childB = cmnRoot; + childB = commonAncestor; if (childA == childB) return 0; // A is equal to B - Node *n = cmnRoot->firstChild(); + Node* n = commonAncestor->firstChild(); while (n) { if (n == childA) return -1; // A is before B @@ -561,32 +528,27 @@ short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *contain } // Should never reach this point. - ASSERT(0); + ASSERT_NOT_REACHED(); return 0; } -short Range::compareBoundaryPoints( const Position &a, const Position &b ) +short Range::compareBoundaryPoints(const Position& a, const Position& b) { - return compareBoundaryPoints(a.node(), a.offset(), b.node(), b.offset()); + return compareBoundaryPoints(a.container.get(), a.posOffset, b.container.get(), b.posOffset); } bool Range::boundaryPointsValid() const { - return m_startContainer && m_endContainer && compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) <= 0; + return m_start.container() && compareBoundaryPoints(m_start.container(), m_start.offset(), m_end.container(), m_end.offset()) <= 0; } -void Range::deleteContents(ExceptionCode& ec) { - if (m_detached) { - ec = INVALID_STATE_ERR; - return; - } - - ec = 0; +void Range::deleteContents(ExceptionCode& ec) +{ checkDeleteExtract(ec); if (ec) return; - processContents(DELETE_CONTENTS,ec); + processContents(DELETE_CONTENTS, ec); } bool Range::intersectsNode(Node* refNode, ExceptionCode& ec) @@ -599,14 +561,15 @@ bool Range::intersectsNode(Node* refNode, ExceptionCode& ec) return false; } - if (m_detached && refNode->attached() || - !m_detached && !refNode->attached() || - refNode->document() != m_ownerDocument) - // firefox doesn't throw an exception for these case; it returns false + if (!m_start.container() && refNode->attached() + || m_start.container() && !refNode->attached() + || refNode->document() != m_ownerDocument) { + // Firefox doesn't throw an exception for these cases; it returns false. return false; + } Node* parentNode = refNode->parentNode(); - unsigned nodeIndex = refNode->nodeIndex(); + int nodeIndex = refNode->nodeIndex(); if (!parentNode) { // if the node is the top document we should return NODE_BEFORE_AND_AFTER @@ -615,23 +578,21 @@ bool Range::intersectsNode(Node* refNode, ExceptionCode& ec) return false; } - if (comparePoint(parentNode, nodeIndex, ec) == -1 && // starts before start - comparePoint(parentNode, nodeIndex + 1, ec) == -1) { // ends before start + 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) == 1 && // starts after end - comparePoint(parentNode, nodeIndex + 1, ec) == 1) { // ends after end + } 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 + return true; // all other cases } -PassRefPtr<DocumentFragment> Range::processContents ( ActionType action, ExceptionCode& ec) +PassRefPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionCode& ec) { - // ### when mutation events are implemented, we will have to take into account - // situations where the tree is being transformed while we delete - ugh! - - // ### perhaps disable node deletion notification for this range while we do this? + // FIXME: To work properly with mutation events, we will have to take into account + // situations where the tree is being transformed while we work on it - ugh! RefPtr<DocumentFragment> fragment; if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) @@ -643,155 +604,143 @@ PassRefPtr<DocumentFragment> Range::processContents ( ActionType action, Excepti if (ec) return 0; - Node *cmnRoot = commonAncestorContainer(ec); + Node* commonRoot = commonAncestorContainer(ec); if (ec) return 0; + ASSERT(commonRoot); // what is the highest node that partially selects the start of the range? - Node *partialStart = 0; - if (m_startContainer != cmnRoot) { - partialStart = m_startContainer.get(); - while (partialStart->parentNode() != cmnRoot) + Node* partialStart = 0; + if (m_start.container() != commonRoot) { + partialStart = m_start.container(); + while (partialStart->parentNode() != commonRoot) partialStart = partialStart->parentNode(); } // what is the highest node that partially selects the end of the range? - Node *partialEnd = 0; - if (m_endContainer != cmnRoot) { - partialEnd = m_endContainer.get(); - while (partialEnd->parentNode() != cmnRoot) + Node* partialEnd = 0; + if (m_end.container() != commonRoot) { + partialEnd = m_end.container(); + while (partialEnd->parentNode() != commonRoot) partialEnd = partialEnd->parentNode(); } // Simple case: the start and end containers are the same. We just grab // everything >= start offset and < end offset - if (m_startContainer == m_endContainer) { - if(m_startContainer->nodeType() == Node::TEXT_NODE || - m_startContainer->nodeType() == Node::CDATA_SECTION_NODE || - m_startContainer->nodeType() == Node::COMMENT_NODE) { - + if (m_start.container() == m_end.container()) { + Node::NodeType startNodeType = m_start.container()->nodeType(); + if (startNodeType == Node::TEXT_NODE || startNodeType == Node::CDATA_SECTION_NODE || startNodeType == Node::COMMENT_NODE) { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { - RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_startContainer->cloneNode(true)); - c->deleteData(m_endOffset, c->length() - m_endOffset, ec); - c->deleteData(0, m_startOffset, ec); + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_start.container()->cloneNode(true)); + c->deleteData(m_end.offset(), c->length() - m_end.offset(), ec); + c->deleteData(0, m_start.offset(), ec); fragment->appendChild(c.release(), ec); } - if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { - static_cast<CharacterData*>(m_startContainer.get())->deleteData(m_startOffset,m_endOffset-m_startOffset,ec); - m_startContainer->document()->updateLayout(); - } - } - else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterData*>(m_start.container())->deleteData(m_start.offset(), m_end.offset() - m_start.offset(), ec); + } else if (startNodeType == Node::PROCESSING_INSTRUCTION_NODE) { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { - RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_startContainer->cloneNode(true)); - c->setData(c->data().substring(m_startOffset, m_endOffset - m_startOffset), ec); + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_start.container()->cloneNode(true)); + c->setData(c->data().substring(m_start.offset(), m_end.offset() - m_start.offset()), ec); fragment->appendChild(c.release(), ec); } if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { - ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_startContainer.get()); + ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_start.container()); String data(pi->data()); - data.remove(m_startOffset, m_endOffset - m_startOffset); + data.remove(m_start.offset(), m_end.offset() - m_start.offset()); pi->setData(data, ec); } - } - else { - Node *n = m_startContainer->firstChild(); - unsigned i; - for (i = 0; n && i < m_startOffset; i++) // skip until m_startOffset + } else { + Node* n = m_start.container()->firstChild(); + int i; + for (i = 0; n && i < m_start.offset(); i++) // skip until start offset n = n->nextSibling(); - while (n && i < m_endOffset) { // delete until m_endOffset - Node *next = n->nextSibling(); + int endOffset = m_end.offset(); + while (n && i < endOffset) { // delete until end offset + Node* next = n->nextSibling(); if (action == EXTRACT_CONTENTS) - fragment->appendChild(n,ec); // will remove n from it's parent + fragment->appendChild(n, ec); // will remove n from its parent else if (action == CLONE_CONTENTS) - fragment->appendChild(n->cloneNode(true),ec); + fragment->appendChild(n->cloneNode(true), ec); else - m_startContainer->removeChild(n,ec); + m_start.container()->removeChild(n, ec); n = next; i++; } } - if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) - collapse(true,ec); return fragment.release(); } // Complex case: Start and end containers are different. // There are three possiblities here: - // 1. Start container == cmnRoot (End container must be a descendant) - // 2. End container == cmnRoot (Start container must be a descendant) - // 3. Neither is cmnRoot, they are both descendants + // 1. Start container == commonRoot (End container must be a descendant) + // 2. End container == commonRoot (Start container must be a descendant) + // 3. Neither is commonRoot, they are both descendants // // In case 3, we grab everything after the start (up until a direct child - // of cmnRoot) into leftContents, and everything before the end (up until - // a direct child of cmnRoot) into rightContents. Then we process all - // cmnRoot children between leftContents and rightContents + // of commonRoot) into leftContents, and everything before the end (up until + // a direct child of commonRoot) into rightContents. Then we process all + // commonRoot children between leftContents and rightContents // // In case 1 or 2, we skip either processing of leftContents or rightContents, // in which case the last lot of nodes either goes from the first or last - // child of cmnRoot. + // child of commonRoot. // // These are deleted, cloned, or extracted (i.e. both) depending on action. RefPtr<Node> leftContents; - if (m_startContainer != cmnRoot) { + if (m_start.container() != commonRoot) { // process the left-hand side of the range, up until the last ancestor of - // m_startContainer before cmnRoot - if(m_startContainer->nodeType() == Node::TEXT_NODE || - m_startContainer->nodeType() == Node::CDATA_SECTION_NODE || - m_startContainer->nodeType() == Node::COMMENT_NODE) { - + // start container before commonRoot + Node::NodeType startNodeType = m_start.container()->nodeType(); + if (startNodeType == Node::TEXT_NODE || startNodeType == Node::CDATA_SECTION_NODE || startNodeType == Node::COMMENT_NODE) { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { - RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_startContainer->cloneNode(true)); - c->deleteData(0, m_startOffset, ec); + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_start.container()->cloneNode(true)); + c->deleteData(0, m_start.offset(), ec); leftContents = c.release(); } - if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { - static_cast<CharacterData*>(m_startContainer.get())->deleteData( - m_startOffset, static_cast<CharacterData*>(m_startContainer.get())->length() - m_startOffset, ec); - m_startContainer->document()->updateLayout(); - } - } - else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterData*>(m_start.container())->deleteData( + m_start.offset(), static_cast<CharacterData*>(m_start.container())->length() - m_start.offset(), ec); + } else if (startNodeType == Node::PROCESSING_INSTRUCTION_NODE) { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { - RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_startContainer->cloneNode(true)); - c->setData(c->data().substring(m_startOffset), ec); + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_start.container()->cloneNode(true)); + c->setData(c->data().substring(m_start.offset()), ec); leftContents = c.release(); } if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { - ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_startContainer.get()); + ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_start.container()); String data(pi->data()); - pi->setData(data.left(m_startOffset), ec); + pi->setData(data.left(m_start.offset()), ec); } - } - else { + } else { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) - leftContents = m_startContainer->cloneNode(false); - Node *n = m_startContainer->firstChild(); - for (unsigned i = 0; n && i < m_startOffset; i++) // skip until m_startOffset + leftContents = m_start.container()->cloneNode(false); + Node* n = m_start.container()->firstChild(); + for (int i = 0; n && i < m_start.offset(); i++) // skip until start offset n = n->nextSibling(); while (n) { // process until end - Node *next = n->nextSibling(); + Node* next = n->nextSibling(); if (action == EXTRACT_CONTENTS) - leftContents->appendChild(n,ec); // will remove n from m_startContainer + leftContents->appendChild(n, ec); // will remove n from start container else if (action == CLONE_CONTENTS) - leftContents->appendChild(n->cloneNode(true),ec); + leftContents->appendChild(n->cloneNode(true), ec); else - m_startContainer->removeChild(n,ec); + m_start.container()->removeChild(n, ec); n = next; } } - Node *leftParent = m_startContainer->parentNode(); - Node *n = m_startContainer->nextSibling(); - for (; leftParent != cmnRoot; leftParent = leftParent->parentNode()) { + Node* leftParent = m_start.container()->parentNode(); + Node* n = m_start.container()->nextSibling(); + for (; leftParent != commonRoot; leftParent = leftParent->parentNode()) { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { RefPtr<Node> leftContentsParent = leftParent->cloneNode(false); leftContentsParent->appendChild(leftContents,ec); leftContents = leftContentsParent; } - Node *next; + Node* next; for (; n; n = next) { next = n->nextSibling(); if (action == EXTRACT_CONTENTS) @@ -805,107 +754,96 @@ PassRefPtr<DocumentFragment> Range::processContents ( ActionType action, Excepti } } - RefPtr<Node> rightContents = 0; - if (m_endContainer != cmnRoot) { + RefPtr<Node> rightContents; + if (m_end.container() != commonRoot) { // delete the right-hand side of the range, up until the last ancestor of - // m_endContainer before cmnRoot - if(m_endContainer->nodeType() == Node::TEXT_NODE || - m_endContainer->nodeType() == Node::CDATA_SECTION_NODE || - m_endContainer->nodeType() == Node::COMMENT_NODE) { - + // end container before commonRoot + Node::NodeType endNodeType = m_end.container()->nodeType(); + if (endNodeType == Node::TEXT_NODE || endNodeType == Node::CDATA_SECTION_NODE || endNodeType == Node::COMMENT_NODE) { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { - RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_endContainer->cloneNode(true)); - c->deleteData(m_endOffset, static_cast<CharacterData*>(m_endContainer.get())->length() - m_endOffset, ec); + RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_end.container()->cloneNode(true)); + c->deleteData(m_end.offset(), static_cast<CharacterData*>(m_end.container())->length() - m_end.offset(), ec); rightContents = c; } - if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { - static_cast<CharacterData*>(m_endContainer.get())->deleteData(0, m_endOffset, ec); - m_startContainer->document()->updateLayout(); - } - } - else if (m_endContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterData*>(m_end.container())->deleteData(0, m_end.offset(), ec); + } else if (endNodeType == Node::PROCESSING_INSTRUCTION_NODE) { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { - RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_endContainer->cloneNode(true)); - c->setData(c->data().left(m_endOffset), ec); + RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_end.container()->cloneNode(true)); + c->setData(c->data().left(m_end.offset()), ec); rightContents = c.release(); } if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { - ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_endContainer.get()); - pi->setData(pi->data().substring(m_endOffset), ec); + ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_end.container()); + pi->setData(pi->data().substring(m_end.offset()), ec); } - } - else { + } else { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) - rightContents = m_endContainer->cloneNode(false); - Node *n = m_endContainer->firstChild(); - if (n && m_endOffset) { - for (unsigned i = 0; i+1 < m_endOffset; i++) { // skip to m_endOffset - Node *next = n->nextSibling(); + rightContents = m_end.container()->cloneNode(false); + Node* n = m_end.container()->firstChild(); + if (n && m_end.offset()) { + for (int i = 0; i + 1 < m_end.offset(); i++) { // skip to end.offset() + Node* next = n->nextSibling(); if (!next) break; n = next; } - Node *prev; + Node* prev; for (; n; n = prev) { prev = n->previousSibling(); if (action == EXTRACT_CONTENTS) - rightContents->insertBefore(n,rightContents->firstChild(),ec); // will remove n from it's parent + rightContents->insertBefore(n, rightContents->firstChild(), ec); // will remove n from its parent else if (action == CLONE_CONTENTS) - rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),ec); + rightContents->insertBefore(n->cloneNode(true), rightContents->firstChild(), ec); else - m_endContainer->removeChild(n,ec); + m_end.container()->removeChild(n, ec); } } } - Node *rightParent = m_endContainer->parentNode(); - Node *n = m_endContainer->previousSibling(); - for (; rightParent != cmnRoot; rightParent = rightParent->parentNode()) { + Node* rightParent = m_end.container()->parentNode(); + Node* n = m_end.container()->previousSibling(); + for (; rightParent != commonRoot; rightParent = rightParent->parentNode()) { if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { RefPtr<Node> rightContentsParent = rightParent->cloneNode(false); rightContentsParent->appendChild(rightContents,ec); rightContents = rightContentsParent; } - - Node *prev; + Node* prev; for (; n; n = prev) { prev = n->previousSibling(); if (action == EXTRACT_CONTENTS) - rightContents->insertBefore(n,rightContents->firstChild(),ec); // will remove n from it's parent + rightContents->insertBefore(n, rightContents->firstChild(), ec); // will remove n from its parent else if (action == CLONE_CONTENTS) - rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),ec); + rightContents->insertBefore(n->cloneNode(true), rightContents->firstChild(), ec); else - rightParent->removeChild(n,ec); + rightParent->removeChild(n, ec); } n = rightParent->previousSibling(); } } - // delete all children of cmnRoot between the start and end container + // delete all children of commonRoot between the start and end container - Node *processStart; // child of cmnRooot - if (m_startContainer == cmnRoot) { - unsigned i; - processStart = m_startContainer->firstChild(); - for (i = 0; i < m_startOffset; i++) + Node* processStart; // child of commonRoot + if (m_start.container() == commonRoot) { + processStart = m_start.container()->firstChild(); + for (int i = 0; i < m_start.offset(); i++) processStart = processStart->nextSibling(); - } - else { - processStart = m_startContainer.get(); - while (processStart->parentNode() != cmnRoot) + } else { + processStart = m_start.container(); + while (processStart->parentNode() != commonRoot) processStart = processStart->parentNode(); processStart = processStart->nextSibling(); } - Node *processEnd; // child of cmnRooot - if (m_endContainer == cmnRoot) { - unsigned i; - processEnd = m_endContainer->firstChild(); - for (i = 0; i < m_endOffset; i++) + Node* processEnd; // child of commonRoot + if (m_end.container() == commonRoot) { + processEnd = m_end.container()->firstChild(); + for (int i = 0; i < m_end.offset(); i++) processEnd = processEnd->nextSibling(); - } - else { - processEnd = m_endContainer.get(); - while (processEnd->parentNode() != cmnRoot) + } else { + processEnd = m_end.container(); + while (processEnd->parentNode() != commonRoot) processEnd = processEnd->parentNode(); } @@ -913,75 +851,54 @@ PassRefPtr<DocumentFragment> Range::processContents ( ActionType action, Excepti // (or just delete the stuff in between) if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents) - fragment->appendChild(leftContents,ec); + fragment->appendChild(leftContents, ec); - Node *next; - Node *n; + Node* next; + Node* n; if (processStart) { for (n = processStart; n && n != processEnd; n = next) { next = n->nextSibling(); - if (action == EXTRACT_CONTENTS) - fragment->appendChild(n,ec); // will remove from cmnRoot + fragment->appendChild(n, ec); // will remove from commonRoot else if (action == CLONE_CONTENTS) - fragment->appendChild(n->cloneNode(true),ec); + fragment->appendChild(n->cloneNode(true), ec); else - cmnRoot->removeChild(n,ec); + commonRoot->removeChild(n, ec); } } if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents) - fragment->appendChild(rightContents,ec); - - // collapse to the proper position - see spec section 2.6 - if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { - if (!partialStart && !partialEnd) - collapse(true,ec); - else if (partialStart) { - m_startContainer = partialStart->parentNode(); - m_endContainer = partialStart->parentNode(); - m_startOffset = m_endOffset = partialStart->nodeIndex()+1; - } - else if (partialEnd) { - m_startContainer = partialEnd->parentNode(); - m_endContainer = partialEnd->parentNode(); - m_startOffset = m_endOffset = partialEnd->nodeIndex(); - } - } + fragment->appendChild(rightContents, ec); + return fragment.release(); } - PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec) { - if (m_detached) { - ec = INVALID_STATE_ERR; - return 0; - } - - ec = 0; checkDeleteExtract(ec); if (ec) return 0; - return processContents(EXTRACT_CONTENTS,ec); + return processContents(EXTRACT_CONTENTS, ec); } -PassRefPtr<DocumentFragment> Range::cloneContents( int &ec ) +PassRefPtr<DocumentFragment> Range::cloneContents(ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - return processContents(CLONE_CONTENTS,ec); + return processContents(CLONE_CONTENTS, ec); } -void Range::insertNode(PassRefPtr<Node> newNode, ExceptionCode& ec) +void Range::insertNode(PassRefPtr<Node> prpNewNode, ExceptionCode& ec) { + RefPtr<Node> newNode = prpNewNode; + ec = 0; - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -1000,46 +917,50 @@ void Range::insertNode(PassRefPtr<Node> newNode, ExceptionCode& ec) // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were // not created from the same document. - if (newNode->document() != m_startContainer->document()) { + if (newNode->document() != m_start.container()->document()) { ec = WRONG_DOCUMENT_ERR; return; } - // 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. // an extra one here - if a text node is going to split, it must have a parent to insert into - if (m_startContainer->nodeType() == Node::TEXT_NODE && !m_startContainer->parentNode()) { + bool startIsText = m_start.container()->isTextNode(); + if (startIsText && !m_start.container()->parentNode()) { ec = HIERARCHY_REQUEST_ERR; return; } // 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 (m_startContainer->nodeType() == Node::TEXT_NODE) - checkAgainst = m_startContainer->parentNode(); + Node* checkAgainst; + if (startIsText) + checkAgainst = m_start.container()->parentNode(); else - checkAgainst = m_startContainer.get(); + checkAgainst = m_start.container(); - if (newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { + Node::NodeType newNodeType = newNode->nodeType(); + int numNewChildren; + if (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) { // check each child node, not the DocumentFragment itself - Node *c; - for (c = newNode->firstChild(); c; c = c->nextSibling()) { + numNewChildren = 0; + for (Node* c = newNode->firstChild(); c; c = c->nextSibling()) { if (!checkAgainst->childTypeAllowed(c->nodeType())) { ec = HIERARCHY_REQUEST_ERR; return; } + ++numNewChildren; } } else { - if (!checkAgainst->childTypeAllowed(newNode->nodeType())) { + numNewChildren = 1; + if (!checkAgainst->childTypeAllowed(newNodeType)) { ec = HIERARCHY_REQUEST_ERR; return; } } - for (Node *n = m_startContainer.get(); n; n = n->parentNode()) { + for (Node* n = m_start.container(); n; n = n->parentNode()) { if (n == newNode) { ec = HIERARCHY_REQUEST_ERR; return; @@ -1047,57 +968,58 @@ void Range::insertNode(PassRefPtr<Node> newNode, ExceptionCode& ec) } // INVALID_NODE_TYPE_ERR: Raised if newNode is an Attr, Entity, Notation, or Document node. - if (newNode->nodeType() == Node::ATTRIBUTE_NODE || - newNode->nodeType() == Node::ENTITY_NODE || - newNode->nodeType() == Node::NOTATION_NODE || - newNode->nodeType() == Node::DOCUMENT_NODE) { + if (newNodeType == Node::ATTRIBUTE_NODE || newNodeType == Node::ENTITY_NODE + || newNodeType == Node::NOTATION_NODE || newNodeType == Node::DOCUMENT_NODE) { ec = RangeException::INVALID_NODE_TYPE_ERR; return; } - unsigned endOffsetDelta = 0; - if (m_startContainer->nodeType() == Node::TEXT_NODE || - m_startContainer->nodeType() == Node::CDATA_SECTION_NODE) { - RefPtr<Text> newText = static_cast<Text*>(m_startContainer.get())->splitText(m_startOffset, ec); + bool collapsed = m_start == m_end; + if (startIsText) { + RefPtr<Text> newText = static_cast<Text*>(m_start.container())->splitText(m_start.offset(), ec); if (ec) return; - - if (m_startContainer == m_endContainer) - endOffsetDelta = -m_startOffset; - - m_startContainer->parentNode()->insertBefore(newNode, newText.get(), ec); + m_start.container()->parentNode()->insertBefore(newNode.release(), newText.get(), ec); if (ec) return; - m_endContainer = newText; + + // This special case doesn't seem to match the DOM specification, but it's currently required + // to pass Acid3. We might later decide to remove this. + if (collapsed) + m_end.setToChild(newText.get()); } else { - if (m_startContainer == m_endContainer) { - bool isFragment = newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE; - endOffsetDelta = isFragment ? newNode->childNodeCount() : 1; - } + RefPtr<Node> lastChild; + if (collapsed) + lastChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? newNode->lastChild() : newNode; - m_startContainer->insertBefore(newNode, m_startContainer->childNode(m_startOffset), ec); + int startOffset = m_start.offset(); + m_start.container()->insertBefore(newNode.release(), m_start.container()->childNode(startOffset), ec); if (ec) return; + + // This special case doesn't seem to match the DOM specification, but it's currently required + // to pass Acid3. We might later decide to remove this. + if (collapsed) + m_end.set(m_start.container(), startOffset + numNewChildren, lastChild.get()); } - m_endOffset += endOffsetDelta; } String Range::toString(ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return String(); } Vector<UChar> result; - Node* pastEnd = pastEndNode(); - for (Node* n = startNode(); n != pastEnd; n = n->traverseNextNode()) { + Node* pastLast = pastLastNode(); + for (Node* n = firstNode(); n != pastLast; n = n->traverseNextNode()) { if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE) { String data = static_cast<CharacterData*>(n)->data(); - unsigned length = data.length(); - unsigned start = (n == m_startContainer) ? min(m_startOffset, length) : 0; - unsigned end = (n == m_endContainer) ? min(max(start, m_endOffset), length) : length; + int length = data.length(); + int start = (n == m_start.container()) ? min(max(0, m_start.offset()), length) : 0; + int end = (n == m_end.container()) ? min(max(start, m_end.offset()), length) : length; result.append(data.characters() + start, end - start); } } @@ -1112,31 +1034,30 @@ String Range::toHTML() const String Range::text() const { - if (m_detached) + 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_startContainer->document()->updateLayout(); + m_start.container()->document()->updateLayout(); - // FIXME: Maybe DOMRange constructor should take const DOMRange*; if it did we would not need this const_cast. - return plainText(const_cast<Range *>(this)); + return plainText(this); } -PassRefPtr<DocumentFragment> Range::createContextualFragment(const String &html, ExceptionCode& ec) const +PassRefPtr<DocumentFragment> Range::createContextualFragment(const String& markup, ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - Node* htmlElement = m_startContainer->isHTMLElement() ? m_startContainer.get() : m_startContainer->parentNode(); - if (!htmlElement || !htmlElement->isHTMLElement()) { + 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 = static_cast<HTMLElement*>(htmlElement)->createContextualFragment(html); + RefPtr<DocumentFragment> fragment = static_cast<HTMLElement*>(element)->createContextualFragment(markup); if (!fragment) { ec = NOT_SUPPORTED_ERR; return 0; @@ -1148,88 +1069,117 @@ PassRefPtr<DocumentFragment> Range::createContextualFragment(const String &html, void Range::detach(ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } - m_startContainer = 0; - m_endContainer = 0; - m_detached = true; -} + m_ownerDocument->detachRange(this); -bool Range::isDetached() const -{ - return m_detached; + m_start.clear(); + m_end.clear(); } -void Range::checkNodeWOffset(Node* n, int offset, ExceptionCode& ec) const +Node* Range::checkNodeWOffset(Node* n, int offset, ExceptionCode& ec) const { - if (offset < 0) - ec = INDEX_SIZE_ERR; - // no return here - switch (n->nodeType()) { + case Node::DOCUMENT_TYPE_NODE: case Node::ENTITY_NODE: case Node::NOTATION_NODE: - case Node::DOCUMENT_TYPE_NODE: ec = RangeException::INVALID_NODE_TYPE_ERR; - break; - case Node::TEXT_NODE: - case Node::COMMENT_NODE: + return 0; case Node::CDATA_SECTION_NODE: - if ((unsigned)offset > static_cast<CharacterData*>(n)->length()) + case Node::COMMENT_NODE: + case Node::TEXT_NODE: + if (static_cast<unsigned>(offset) > static_cast<CharacterData*>(n)->length()) ec = INDEX_SIZE_ERR; - break; + return 0; case Node::PROCESSING_INSTRUCTION_NODE: - if ((unsigned)offset > static_cast<ProcessingInstruction*>(n)->data().length()) + if (static_cast<unsigned>(offset) > static_cast<ProcessingInstruction*>(n)->data().length()) ec = INDEX_SIZE_ERR; - break; - default: - if ((unsigned)offset > n->childNodeCount()) + return 0; + 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: { + if (!offset) + return 0; + Node* childBefore = n->childNode(offset - 1); + if (!childBefore) ec = INDEX_SIZE_ERR; - break; + return childBefore; + } } + ASSERT_NOT_REACHED(); + return 0; } -void Range::checkNodeBA( Node *n, ExceptionCode& ec) const +void Range::checkNodeBA(Node* n, ExceptionCode& ec) const { // INVALID_NODE_TYPE_ERR: Raised if the root container of refNode is not an // Attr, Document or DocumentFragment node or part of a shadow DOM tree // or if refNode is a Document, DocumentFragment, Attr, Entity, or Notation node. - Node *root = n; - while (root->parentNode()) - root = root->parentNode(); - if (!(root->nodeType() == Node::ATTRIBUTE_NODE || - root->nodeType() == Node::DOCUMENT_NODE || - root->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || - root->isShadowNode())) { - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; + + 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; } - if( n->nodeType() == Node::DOCUMENT_NODE || - n->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || - n->nodeType() == Node::ATTRIBUTE_NODE || - n->nodeType() == Node::ENTITY_NODE || - n->nodeType() == Node::NOTATION_NODE ) - ec = RangeException::INVALID_NODE_TYPE_ERR; + Node* root = n; + while (Node* 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: + if (root->isShadowNode()) + break; + ec = RangeException::INVALID_NODE_TYPE_ERR; + return; + } } PassRefPtr<Range> Range::cloneRange(ExceptionCode& ec) const { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return 0; } - return new Range(m_ownerDocument.get(), m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset); + return Range::create(m_ownerDocument, m_start.container(), m_start.offset(), m_end.container(), m_end.offset()); } -void Range::setStartAfter( Node *refNode, ExceptionCode& ec) +void Range::setStartAfter(Node* refNode, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -1245,16 +1195,16 @@ void Range::setStartAfter( Node *refNode, ExceptionCode& ec) } ec = 0; - checkNodeBA( refNode, ec ); + checkNodeBA(refNode, ec); if (ec) return; setStart(refNode->parentNode(), refNode->nodeIndex() + 1, ec); } -void Range::setEndBefore( Node *refNode, ExceptionCode& ec) +void Range::setEndBefore(Node* refNode, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -1277,9 +1227,9 @@ void Range::setEndBefore( Node *refNode, ExceptionCode& ec) setEnd(refNode->parentNode(), refNode->nodeIndex(), ec); } -void Range::setEndAfter( Node *refNode, ExceptionCode& ec) +void Range::setEndAfter(Node* refNode, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -1303,9 +1253,9 @@ void Range::setEndAfter( Node *refNode, ExceptionCode& ec) } -void Range::selectNode( Node *refNode, ExceptionCode& ec) +void Range::selectNode(Node* refNode, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -1318,37 +1268,56 @@ void Range::selectNode( Node *refNode, ExceptionCode& ec) // INVALID_NODE_TYPE_ERR: Raised if an ancestor of refNode is an Entity, Notation or // DocumentType node or if refNode is a Document, DocumentFragment, Attr, Entity, or Notation // node. - Node *anc; - for (anc = refNode->parentNode(); anc; anc = anc->parentNode()) { - if (anc->nodeType() == Node::ENTITY_NODE || - anc->nodeType() == Node::NOTATION_NODE || - anc->nodeType() == Node::DOCUMENT_TYPE_NODE) { - - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; + for (Node* 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; } } - if (refNode->nodeType() == Node::DOCUMENT_NODE || - refNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || - refNode->nodeType() == Node::ATTRIBUTE_NODE || - refNode->nodeType() == Node::ENTITY_NODE || - refNode->nodeType() == 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; } ec = 0; - setStartBefore( refNode, ec ); + setStartBefore(refNode, ec); if (ec) return; - setEndAfter( refNode, ec ); + setEndAfter(refNode, ec); } -void Range::selectNodeContents( Node *refNode, ExceptionCode& ec) +void Range::selectNodeContents(Node* refNode, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -1360,28 +1329,36 @@ void Range::selectNodeContents( Node *refNode, ExceptionCode& ec) // INVALID_NODE_TYPE_ERR: Raised if refNode or an ancestor of refNode is an Entity, Notation // or DocumentType node. - Node *n; - for (n = refNode; n; n = n->parentNode()) { - if (n->nodeType() == Node::ENTITY_NODE || - n->nodeType() == Node::NOTATION_NODE || - n->nodeType() == Node::DOCUMENT_TYPE_NODE) { - - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; + 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; } } - m_startContainer = refNode; - m_startOffset = 0; - m_endContainer = refNode; - m_endOffset = refNode->offsetInCharacters() ? refNode->maxCharacterOffset() : refNode->childNodeCount(); + m_start.setToStart(refNode); + m_end.setToEnd(refNode); } void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) { RefPtr<Node> newParent = passNewParent; - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -1393,14 +1370,23 @@ void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) // INVALID_NODE_TYPE_ERR: Raised if node is an Attr, Entity, DocumentType, Notation, // Document, or DocumentFragment node. - if( newParent->nodeType() == Node::ATTRIBUTE_NODE || - newParent->nodeType() == Node::ENTITY_NODE || - newParent->nodeType() == Node::NOTATION_NODE || - newParent->nodeType() == Node::DOCUMENT_TYPE_NODE || - newParent->nodeType() == Node::DOCUMENT_NODE || - newParent->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { - ec = RangeException::INVALID_NODE_TYPE_ERR; - return; + 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; + 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 @@ -1412,42 +1398,45 @@ void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were // not created from the same document. - if (newParent->document() != m_startContainer->document()) { + if (newParent->document() != m_start.container()->document()) { ec = WRONG_DOCUMENT_ERR; return; } - // Raise a HIERARCHY_REQUEST_ERR if m_startContainer doesn't accept children like newParent. - Node* parentOfNewParent = m_startContainer.get(); - // If m_startContainer is a textNode, it will be split and it will be its parent that will - // need to accept newParent. - if (parentOfNewParent->isTextNode()) + // 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->childTypeAllowed(newParent->nodeType())) { ec = HIERARCHY_REQUEST_ERR; return; } - if (m_startContainer == newParent || m_startContainer->isDescendantOf(newParent.get())) { + if (m_start.container() == newParent || m_start.container()->isDescendantOf(newParent.get())) { ec = HIERARCHY_REQUEST_ERR; return; } - // ### check if node would end up with a child node of a type not allowed by the type of node + // 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. - if (!m_startContainer->offsetInCharacters()) { - if (m_startOffset > 0 && m_startOffset < m_startContainer->childNodeCount()) { + // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-Text node. + if (m_start.container()->nodeType() != Node::TEXT_NODE) { + if (m_start.offset() > 0 && m_start.offset() < maxStartOffset()) { ec = RangeException::BAD_BOUNDARYPOINTS_ERR; return; } } - if (!m_endContainer->offsetInCharacters()) { - if (m_endOffset > 0 && m_endOffset < m_endContainer->childNodeCount()) { + if (m_end.container()->nodeType() != Node::TEXT_NODE) { + if (m_end.offset() > 0 && m_end.offset() < maxEndOffset()) { ec = RangeException::BAD_BOUNDARYPOINTS_ERR; return; } - } + } ec = 0; while (Node* n = newParent->firstChild()) { @@ -1467,9 +1456,9 @@ void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) selectNode(newParent.get(), ec); } -void Range::setStartBefore( Node *refNode, ExceptionCode& ec) +void Range::setStartBefore(Node* refNode, ExceptionCode& ec) { - if (m_detached) { + if (!m_start.container()) { ec = INVALID_STATE_ERR; return; } @@ -1494,16 +1483,22 @@ void Range::setStartBefore( Node *refNode, ExceptionCode& ec) void Range::checkDeleteExtract(ExceptionCode& ec) { + if (!m_start.container()) { + ec = INVALID_STATE_ERR; + return; + } + + ec = 0; if (!commonAncestorContainer(ec) || ec) return; - Node *pastEnd = pastEndNode(); - for (Node *n = startNode(); n != pastEnd; n = n->traverseNextNode()) { + Node* pastLast = pastLastNode(); + for (Node* n = firstNode(); n != pastLast; n = n->traverseNextNode()) { if (n->isReadOnlyNode()) { ec = NO_MODIFICATION_ALLOWED_ERR; return; } - if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { // ### is this for only directly under the DF, or anywhere? + if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { ec = HIERARCHY_REQUEST_ERR; return; } @@ -1517,40 +1512,28 @@ void Range::checkDeleteExtract(ExceptionCode& ec) bool Range::containedByReadOnly() const { - Node *n; - for (n = m_startContainer.get(); n; n = n->parentNode()) { + for (Node* n = m_start.container(); n; n = n->parentNode()) { if (n->isReadOnlyNode()) return true; } - for (n = m_endContainer.get(); n; n = n->parentNode()) { + for (Node* n = m_end.container(); n; n = n->parentNode()) { if (n->isReadOnlyNode()) return true; } return false; } -Position Range::startPosition() const -{ - return Position(m_startContainer.get(), m_startOffset); -} - -Position Range::endPosition() const +Node* Range::firstNode() const { - return Position(m_endContainer.get(), m_endOffset); -} - -Node *Range::startNode() const -{ - if (!m_startContainer) + if (!m_start.container()) return 0; - if (m_startContainer->offsetInCharacters()) - return m_startContainer.get(); - Node *child = m_startContainer->childNode(m_startOffset); - if (child) + if (m_start.container()->offsetInCharacters()) + return m_start.container(); + if (Node* child = m_start.container()->childNode(m_start.offset())) return child; - if (m_startOffset == 0) - return m_startContainer.get(); - return m_startContainer->traverseNextSibling(); + if (!m_start.offset()) + return m_start.container(); + return m_start.container()->traverseNextSibling(); } Position Range::editingStartPosition() const @@ -1560,7 +1543,7 @@ Position Range::editingStartPosition() const // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up // with a spurious "mixed" style. - VisiblePosition visiblePosition(m_startContainer.get(), m_startOffset, VP_DEFAULT_AFFINITY); + VisiblePosition visiblePosition(m_start.container(), m_start.offset(), VP_DEFAULT_AFFINITY); if (visiblePosition.isNull()) return Position(); @@ -1579,16 +1562,20 @@ Position Range::editingStartPosition() const return visiblePosition.deepEquivalent().downstream(); } -Node *Range::pastEndNode() const +Node* Range::shadowTreeRootNode() const +{ + return startContainer() ? startContainer()->shadowTreeRootNode() : 0; +} + +Node* Range::pastLastNode() const { - if (!m_startContainer || !m_endContainer) + if (!m_start.container() || !m_end.container()) return 0; - if (m_endContainer->offsetInCharacters()) - return m_endContainer->traverseNextSibling(); - Node *child = m_endContainer->childNode(m_endOffset); - if (child) + if (m_end.container()->offsetInCharacters()) + return m_end.container()->traverseNextSibling(); + if (Node* child = m_end.container()->childNode(m_end.offset())) return child; - return m_endContainer->traverseNextSibling(); + return m_end.container()->traverseNextSibling(); } IntRect Range::boundingBox() @@ -1604,11 +1591,11 @@ IntRect Range::boundingBox() void Range::addLineBoxRects(Vector<IntRect>& rects, bool useSelectionHeight) { - if (!m_startContainer || !m_endContainer) + if (!m_start.container() || !m_end.container()) return; - RenderObject* start = m_startContainer->renderer(); - RenderObject* end = m_endContainer->renderer(); + RenderObject* start = m_start.container()->renderer(); + RenderObject* end = m_end.container()->renderer(); if (!start || !end) return; @@ -1616,8 +1603,8 @@ void Range::addLineBoxRects(Vector<IntRect>& rects, bool useSelectionHeight) for (RenderObject* r = start; r && r != stop; r = r->nextInPreOrder()) { // only ask leaf render objects for their line box rects if (!r->firstChild()) { - int startOffset = r == start ? m_startOffset : 0; - int endOffset = r == end ? m_endOffset : UINT_MAX; + int startOffset = r == start ? m_start.offset() : 0; + int endOffset = r == end ? m_end.offset() : INT_MAX; r->addLineBoxRects(rects, startOffset, endOffset, useSelectionHeight); } } @@ -1625,59 +1612,191 @@ void Range::addLineBoxRects(Vector<IntRect>& rects, bool useSelectionHeight) #ifndef NDEBUG #define FormatBufferSize 1024 -void Range::formatForDebugger(char *buffer, unsigned length) const +void Range::formatForDebugger(char* buffer, unsigned length) const { String result; String s; - if (!m_startContainer || !m_endContainer) + if (!m_start.container() || !m_end.container()) result = "<empty>"; else { char s[FormatBufferSize]; result += "from offset "; - result += String::number(m_startOffset); + result += String::number(m_start.offset()); result += " of "; - m_startContainer->formatForDebugger(s, FormatBufferSize); + m_start.container()->formatForDebugger(s, FormatBufferSize); result += s; result += " to offset "; - result += String::number(m_endOffset); + result += String::number(m_end.offset()); result += " of "; - m_endContainer->formatForDebugger(s, FormatBufferSize); + m_end.container()->formatForDebugger(s, FormatBufferSize); result += s; } - strncpy(buffer, result.deprecatedString().latin1(), length - 1); + strncpy(buffer, result.utf8().data(), length - 1); } #undef FormatBufferSize #endif -bool operator==(const Range &a, const Range &b) +bool operator==(const Range& a, const Range& b) { if (&a == &b) return true; - // Not strictly legal C++, but in practice this can happen, and works fine with GCC. + // Not strictly legal C++, but in practice this can happen, and this check works + // fine with GCC to detect such cases and return false rather than crashing. if (!&a || !&b) return false; - bool ad = a.isDetached(); - bool bd = b.isDetached(); - if (ad && bd) - return true; - if (ad || bd) - return false; - int exception = 0; - return a.startContainer(exception) == b.startContainer(exception) - && a.endContainer(exception) == b.endContainer(exception) - && a.startOffset(exception) == b.startOffset(exception) - && a.endOffset(exception) == b.endOffset(exception); + return a.startPosition() == b.startPosition() && a.endPosition() == b.endPosition(); } PassRefPtr<Range> rangeOfContents(Node* node) { ASSERT(node); - RefPtr<Range> range = new Range(node->document()); + RefPtr<Range> range = Range::create(node->document()); int exception = 0; range->selectNodeContents(node, exception); return range.release(); } +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(); +} + +int Range::maxEndOffset() const +{ + if (!m_end.container()) + return 0; + if (!m_end.container()->offsetInCharacters()) + return m_end.container()->childNodeCount(); + return m_end.container()->maxCharacterOffset(); +} + +static inline void boundaryNodeChildrenChanged(RangeBoundaryPoint& boundary, ContainerNode* container) +{ + if (!boundary.childBefore()) + return; + if (boundary.container() != container) + return; + boundary.invalidateOffset(); +} + +void Range::nodeChildrenChanged(ContainerNode* container) +{ + ASSERT(container); + ASSERT(container->document() == m_ownerDocument); + boundaryNodeChildrenChanged(m_start, container); + boundaryNodeChildrenChanged(m_end, container); +} + +static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node* nodeToBeRemoved) +{ + if (boundary.childBefore() == nodeToBeRemoved) { + boundary.childBeforeWillBeRemoved(); + return; + } + + for (Node* n = boundary.container(); n; n = n->parentNode()) { + if (n == nodeToBeRemoved) { + boundary.setToChild(nodeToBeRemoved); + return; + } + } +} + +void Range::nodeWillBeRemoved(Node* node) +{ + ASSERT(node); + ASSERT(node->document() == m_ownerDocument); + ASSERT(node != m_ownerDocument); + ASSERT(node->parentNode()); + boundaryNodeWillBeRemoved(m_start, node); + boundaryNodeWillBeRemoved(m_end, node); +} + +static inline void boundaryTextInserted(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length) +{ + if (boundary.container() != text) + return; + unsigned boundaryOffset = boundary.offset(); + if (offset >= boundaryOffset) + return; + boundary.setOffset(boundaryOffset + length); +} + +void Range::textInserted(Node* text, unsigned offset, unsigned length) +{ + ASSERT(text); + ASSERT(text->document() == m_ownerDocument); + boundaryTextInserted(m_start, text, offset, length); + boundaryTextInserted(m_end, text, offset, length); +} + +static inline void boundaryTextRemoved(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length) +{ + if (boundary.container() != text) + return; + unsigned boundaryOffset = boundary.offset(); + if (offset >= boundaryOffset) + return; + if (offset + length >= boundaryOffset) + boundary.setOffset(offset); + else + boundary.setOffset(boundaryOffset - length); +} + +void Range::textRemoved(Node* text, unsigned offset, unsigned length) +{ + ASSERT(text); + ASSERT(text->document() == m_ownerDocument); + boundaryTextRemoved(m_start, text, offset, length); + boundaryTextRemoved(m_end, text, offset, 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); +} + +void Range::textNodesMerged(NodeWithIndex& oldNode, unsigned offset) +{ + ASSERT(oldNode.node()); + ASSERT(oldNode.node()->document() == m_ownerDocument); + ASSERT(oldNode.node()->parentNode()); + ASSERT(oldNode.node()->isTextNode()); + ASSERT(oldNode.node()->previousSibling()); + ASSERT(oldNode.node()->previousSibling()->isTextNode()); + boundaryTextNodesMerged(m_start, oldNode, offset); + boundaryTextNodesMerged(m_end, oldNode, offset); +} + +static inline void boundaryTextNodesSplit(RangeBoundaryPoint& boundary, Text* oldNode) +{ + if (boundary.container() != oldNode) + return; + unsigned boundaryOffset = boundary.offset(); + if (boundaryOffset <= oldNode->length()) + return; + boundary.set(oldNode->nextSibling(), boundaryOffset - oldNode->length(), 0); +} + +void Range::textNodeSplit(Text* oldNode) +{ + ASSERT(oldNode); + ASSERT(oldNode->document() == m_ownerDocument); + ASSERT(oldNode->parentNode()); + ASSERT(oldNode->isTextNode()); + ASSERT(oldNode->nextSibling()); + ASSERT(oldNode->nextSibling()->isTextNode()); + boundaryTextNodesSplit(m_start, oldNode); + boundaryTextNodesSplit(m_end, oldNode); +} + } |