diff options
Diffstat (limited to 'khtml/xml/dom2_rangeimpl.cpp')
-rw-r--r-- | khtml/xml/dom2_rangeimpl.cpp | 1640 |
1 files changed, 1640 insertions, 0 deletions
diff --git a/khtml/xml/dom2_rangeimpl.cpp b/khtml/xml/dom2_rangeimpl.cpp new file mode 100644 index 000000000..b160ce23f --- /dev/null +++ b/khtml/xml/dom2_rangeimpl.cpp @@ -0,0 +1,1640 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * (C) 1999-2003 Lars Knoll (knoll@kde.org) + * (C) 2001-2003 Dirk Mueller (mueller@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) 2003 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "dom/dom_exception.h" +#include "dom_docimpl.h" +#include "dom2_rangeimpl.h" +#include "dom_textimpl.h" +#include "dom_xmlimpl.h" +#include "html/html_elementimpl.h" +#include "misc/htmltags.h" + +using namespace DOM; + + +RangeImpl::RangeImpl(DocumentImpl *_ownerDocument) +{ + m_ownerDocument = _ownerDocument; + m_ownerDocument->ref(); + m_startContainer = _ownerDocument; + m_startContainer->ref(); + m_endContainer = _ownerDocument; + m_endContainer->ref(); + m_startOffset = 0; + m_endOffset = 0; + m_detached = false; +} + +RangeImpl::RangeImpl(DocumentImpl *_ownerDocument, + NodeImpl *_startContainer, long _startOffset, + NodeImpl *_endContainer, long _endOffset) +{ + m_ownerDocument = _ownerDocument; + m_ownerDocument->ref(); + m_startContainer = _startContainer; + m_startContainer->ref(); + m_startOffset = _startOffset; + m_endContainer = _endContainer; + m_endContainer->ref(); + m_endOffset = _endOffset; + m_detached = false; +} + +RangeImpl::~RangeImpl() +{ + m_ownerDocument->deref(); + int exceptioncode = 0; + if (!m_detached) + detach(exceptioncode); +} + +NodeImpl *RangeImpl::startContainer(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return m_startContainer; +} + +long RangeImpl::startOffset(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return m_startOffset; +} + +NodeImpl *RangeImpl::endContainer(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return m_endContainer; +} + +long RangeImpl::endOffset(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return m_endOffset; +} + +NodeImpl *RangeImpl::commonAncestorContainer(int &exceptioncode) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + NodeImpl *com = commonAncestorContainer(m_startContainer,m_endContainer); + if (!com) // should never happen + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return com; +} + +NodeImpl *RangeImpl::commonAncestorContainer(NodeImpl *containerA, NodeImpl *containerB) +{ + NodeImpl *parentStart; + + for (parentStart = containerA; parentStart; parentStart = parentStart->parentNode()) { + NodeImpl *parentEnd = containerB; + while( parentEnd && (parentStart != parentEnd) ) + parentEnd = parentEnd->parentNode(); + + if(parentStart == parentEnd) break; + } + + return parentStart; +} + +bool RangeImpl::collapsed(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return (m_startContainer == m_endContainer && m_startOffset == m_endOffset); +} + +void RangeImpl::setStart( NodeImpl *refNode, long offset, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeWOffset( refNode, offset, exceptioncode ); + if (exceptioncode) + return; + + setStartContainer(refNode); + m_startOffset = offset; + + // check if different root container + NodeImpl *endRootContainer = m_endContainer; + while (endRootContainer->parentNode()) + endRootContainer = endRootContainer->parentNode(); + NodeImpl *startRootContainer = m_startContainer; + while (startRootContainer->parentNode()) + startRootContainer = startRootContainer->parentNode(); + if (startRootContainer != endRootContainer) + collapse(true,exceptioncode); + // check if new start after end + else if (compareBoundaryPoints(m_startContainer,m_startOffset,m_endContainer,m_endOffset) > 0) + collapse(true,exceptioncode); +} + +void RangeImpl::setEnd( NodeImpl *refNode, long offset, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeWOffset( refNode, offset, exceptioncode ); + if (exceptioncode) + return; + + setEndContainer(refNode); + m_endOffset = offset; + + // check if different root container + NodeImpl *endRootContainer = m_endContainer; + while (endRootContainer->parentNode()) + endRootContainer = endRootContainer->parentNode(); + NodeImpl *startRootContainer = m_startContainer; + while (startRootContainer->parentNode()) + startRootContainer = startRootContainer->parentNode(); + if (startRootContainer != endRootContainer) + collapse(false,exceptioncode); + // check if new end before start + if (compareBoundaryPoints(m_startContainer,m_startOffset,m_endContainer,m_endOffset) > 0) + collapse(false,exceptioncode); +} + +void RangeImpl::collapse( bool toStart, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if( toStart ) // collapse to start + { + setEndContainer(m_startContainer); + m_endOffset = m_startOffset; + } + else // collapse to end + { + setStartContainer(m_endContainer); + m_startOffset = m_endOffset; + } +} + +short RangeImpl::compareBoundaryPoints( Range::CompareHow how, RangeImpl *sourceRange, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + if (!sourceRange) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return 0; + } + + NodeImpl *thisCont = commonAncestorContainer(exceptioncode); + NodeImpl *sourceCont = sourceRange->commonAncestorContainer(exceptioncode); + if (exceptioncode) + return 0; + + if (thisCont->getDocument() != sourceCont->getDocument()) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return 0; + } + + NodeImpl *thisTop = thisCont; + NodeImpl *sourceTop = sourceCont; + while (thisTop->parentNode()) + thisTop = thisTop->parentNode(); + while (sourceTop->parentNode()) + sourceTop = sourceTop->parentNode(); + if (thisTop != sourceTop) { // in different DocumentFragments + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return 0; + } + + switch(how) + { + case Range::START_TO_START: + return compareBoundaryPoints( m_startContainer, m_startOffset, + sourceRange->startContainer(exceptioncode), sourceRange->startOffset(exceptioncode) ); + break; + case Range::START_TO_END: + return compareBoundaryPoints( m_startContainer, m_startOffset, + sourceRange->endContainer(exceptioncode), sourceRange->endOffset(exceptioncode) ); + break; + case Range::END_TO_END: + return compareBoundaryPoints( m_endContainer, m_endOffset, + sourceRange->endContainer(exceptioncode), sourceRange->endOffset(exceptioncode) ); + break; + case Range::END_TO_START: + return compareBoundaryPoints( m_endContainer, m_endOffset, + sourceRange->startContainer(exceptioncode), sourceRange->startOffset(exceptioncode) ); + break; + default: + exceptioncode = DOMException::SYNTAX_ERR; + return 0; + } +} + +short RangeImpl::compareBoundaryPoints( NodeImpl *containerA, long offsetA, NodeImpl *containerB, long offsetB ) +{ + // see DOM2 traversal & range section 2.5 + + // case 1: both points have the same container + if( containerA == containerB ) + { + if( offsetA == offsetB ) return 0; // A is equal to B + if( offsetA < offsetB ) return -1; // A is before B + else return 1; // A is after B + } + + // case 2: node C (container B or an ancestor) is a child node of A + NodeImpl *c = containerB; + while (c && c->parentNode() != containerA) + c = c->parentNode(); + if (c) { + int offsetC = 0; + NodeImpl* n = containerA->firstChild(); + while (n != c) { + offsetC++; + n = n->nextSibling(); + } + + if( offsetA <= offsetC ) return -1; // A is before B + else return 1; // A is after B + } + + // case 3: node C (container A or an ancestor) is a child node of B + c = containerA; + while (c && c->parentNode() != containerB) + c = c->parentNode(); + if (c) { + int offsetC = 0; + NodeImpl* n = containerB->firstChild(); + while (n != c) { + offsetC++; + n = n->nextSibling(); + } + + if( offsetC < offsetB ) return -1; // A is before B + else 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 + NodeImpl *cmnRoot = commonAncestorContainer(containerA,containerB); + if (!cmnRoot) return -1; // Whatever... + NodeImpl *childA = containerA; + while (childA->parentNode() != cmnRoot) + childA = childA->parentNode(); + NodeImpl *childB = containerB; + while (childB->parentNode() != cmnRoot) + childB = childB->parentNode(); + + NodeImpl *n = cmnRoot->firstChild(); + int i = 0; + int childAOffset = -1; + int childBOffset = -1; + while (childAOffset < 0 || childBOffset < 0) { + if (n == childA) + childAOffset = i; + if (n == childB) + childBOffset = i; + n = n->nextSibling(); + i++; + } + + if( childAOffset == childBOffset ) return 0; // A is equal to B + if( childAOffset < childBOffset ) return -1; // A is before B + else return 1; // A is after B +} + +bool RangeImpl::boundaryPointsValid( ) +{ + short valid = compareBoundaryPoints( m_startContainer, m_startOffset, + m_endContainer, m_endOffset ); + if( valid == 1 ) return false; + else return true; + +} + +void RangeImpl::deleteContents( int &exceptioncode ) { + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + checkDeleteExtract(exceptioncode); + if (exceptioncode) + return; + + processContents(DELETE_CONTENTS,exceptioncode); +} + +DocumentFragmentImpl *RangeImpl::processContents ( ActionType action, int &exceptioncode ) +{ + // ### 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? + + if (collapsed(exceptioncode)) + return 0; + if (exceptioncode) + return 0; + + NodeImpl *cmnRoot = commonAncestorContainer(exceptioncode); + if (exceptioncode) + return 0; + + // what is the highest node that partially selects the start of the range? + NodeImpl *partialStart = 0; + if (m_startContainer != cmnRoot) { + partialStart = m_startContainer; + while (partialStart->parentNode() != cmnRoot) + partialStart = partialStart->parentNode(); + } + + // what is the highest node that partially selects the end of the range? + NodeImpl *partialEnd = 0; + if (m_endContainer != cmnRoot) { + partialEnd = m_endContainer; + while (partialEnd->parentNode() != cmnRoot) + partialEnd = partialEnd->parentNode(); + } + + DocumentFragmentImpl *fragment = 0; + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + fragment = new DocumentFragmentImpl(m_ownerDocument); + + // 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 (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + CharacterDataImpl *c = static_cast<CharacterDataImpl*>(m_startContainer->cloneNode(true)); + c->deleteData(m_endOffset,static_cast<CharacterDataImpl*>(m_startContainer)->length()-m_endOffset,exceptioncode); + c->deleteData(0,m_startOffset,exceptioncode); + fragment->appendChild(c,exceptioncode); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterDataImpl*>(m_startContainer)->deleteData(m_startOffset,m_endOffset-m_startOffset,exceptioncode); + } + else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + // ### operate just on data ? + } + else { + NodeImpl *n = m_startContainer->firstChild(); + unsigned long i; + for(i = 0; i < m_startOffset; i++) // skip until m_startOffset + n = n->nextSibling(); + while (n && i < m_endOffset) { // delete until m_endOffset + NodeImpl *next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + fragment->appendChild(n,exceptioncode); // will remove n from its parent + else if (action == CLONE_CONTENTS) + fragment->appendChild(n->cloneNode(true),exceptioncode); + else + m_startContainer->removeChild(n,exceptioncode); + n = next; + i++; + } + } + collapse(true,exceptioncode); + return fragment; + } + + // 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 + // + // 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 + // + // 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. + // + // These are deleted, cloned, or extracted (i.e. both) depending on action. + + NodeImpl *leftContents = 0; + if (m_startContainer != cmnRoot) { + // 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) { + + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + CharacterDataImpl *c = static_cast<CharacterDataImpl*>(m_startContainer->cloneNode(true)); + c->deleteData(0,m_startOffset,exceptioncode); + leftContents = c; + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterDataImpl*>(m_startContainer)->deleteData( + m_startOffset,static_cast<CharacterDataImpl*>(m_startContainer)->length()-m_startOffset,exceptioncode); + } + else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + // ### operate just on data ? + // leftContents = ... + } + else { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + leftContents = m_startContainer->cloneNode(false); + NodeImpl *n = m_startContainer->firstChild(); + unsigned long i; + for(i = 0; i < m_startOffset; i++) // skip until m_startOffset + n = n->nextSibling(); + while (n) { // process until end + NodeImpl *next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + leftContents->appendChild(n,exceptioncode); // will remove n from m_startContainer + else if (action == CLONE_CONTENTS) + leftContents->appendChild(n->cloneNode(true),exceptioncode); + else + m_startContainer->removeChild(n,exceptioncode); + n = next; + } + } + + NodeImpl *leftParent = m_startContainer->parentNode(); + NodeImpl *n = m_startContainer->nextSibling(); + for (; leftParent != cmnRoot; leftParent = leftParent->parentNode()) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + NodeImpl *leftContentsParent = leftParent->cloneNode(false); + leftContentsParent->appendChild(leftContents,exceptioncode); + leftContents = leftContentsParent; + } + + NodeImpl *next; + for (; n; n = next ) { + next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + leftContents->appendChild(n,exceptioncode); // will remove n from leftParent + else if (action == CLONE_CONTENTS) + leftContents->appendChild(n->cloneNode(true),exceptioncode); + else + leftParent->removeChild(n,exceptioncode); + } + n = leftParent->nextSibling(); + } + } + + NodeImpl *rightContents = 0; + if (m_endContainer != cmnRoot) { + // 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) { + + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + CharacterDataImpl *c = static_cast<CharacterDataImpl*>(m_endContainer->cloneNode(true)); + c->deleteData(m_endOffset,static_cast<CharacterDataImpl*>(m_endContainer)->length()-m_endOffset,exceptioncode); + rightContents = c; + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast<CharacterDataImpl*>(m_endContainer)->deleteData(0,m_endOffset,exceptioncode); + } + else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + // ### operate just on data ? + // rightContents = ... + } + else { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + rightContents = m_endContainer->cloneNode(false); + NodeImpl *n = m_endContainer->firstChild(); + unsigned long i; + for(i = 0; i+1 < m_endOffset; i++) // skip to m_endOffset + n = n->nextSibling(); + NodeImpl *prev; + for (; n; n = prev ) { + prev = n->previousSibling(); + if (action == EXTRACT_CONTENTS) + rightContents->insertBefore(n,rightContents->firstChild(),exceptioncode); // will remove n from its parent + else if (action == CLONE_CONTENTS) + rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),exceptioncode); + else + m_endContainer->removeChild(n,exceptioncode); + } + } + + NodeImpl *rightParent = m_endContainer->parentNode(); + NodeImpl *n = m_endContainer->previousSibling(); + for (; rightParent != cmnRoot; rightParent = rightParent->parentNode()) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + NodeImpl *rightContentsParent = rightParent->cloneNode(false); + rightContentsParent->appendChild(rightContents,exceptioncode); + rightContents = rightContentsParent; + } + + NodeImpl *prev; + for (; n; n = prev ) { + prev = n->previousSibling(); + if (action == EXTRACT_CONTENTS) + rightContents->insertBefore(n,rightContents->firstChild(),exceptioncode); // will remove n from its parent + else if (action == CLONE_CONTENTS) + rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),exceptioncode); + else + rightParent->removeChild(n,exceptioncode); + + } + n = rightParent->previousSibling(); + } + } + + // delete all children of cmnRoot between the start and end container + + NodeImpl *processStart; // child of cmnRooot + if (m_startContainer == cmnRoot) { + unsigned long i; + processStart = m_startContainer->firstChild(); + for (i = 0; i < m_startOffset; i++) + processStart = processStart->nextSibling(); + } + else { + processStart = m_startContainer; + while (processStart->parentNode() != cmnRoot) + processStart = processStart->parentNode(); + processStart = processStart->nextSibling(); + } + NodeImpl *processEnd; // child of cmnRooot + if (m_endContainer == cmnRoot) { + unsigned long i; + processEnd = m_endContainer->firstChild(); + for (i = 0; i < m_endOffset; i++) + processEnd = processEnd->nextSibling(); + } + else { + processEnd = m_endContainer; + while (processEnd->parentNode() != cmnRoot) + processEnd = processEnd->parentNode(); + } + + // Now add leftContents, stuff in between, and rightContents to the fragment + // (or just delete the stuff in between) + + if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents) + fragment->appendChild(leftContents,exceptioncode); + + NodeImpl *next; + NodeImpl *n; + if (processStart) { + for (n = processStart; n && n != processEnd; n = next) { + next = n->nextSibling(); + + if (action == EXTRACT_CONTENTS) + fragment->appendChild(n,exceptioncode); // will remove from cmnRoot + else if (action == CLONE_CONTENTS) + fragment->appendChild(n->cloneNode(true),exceptioncode); + else + cmnRoot->removeChild(n,exceptioncode); + } + } + + if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents) + fragment->appendChild(rightContents,exceptioncode); + + // collapse to the proper position - see spec section 2.6 + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + if (!partialStart && !partialEnd) + collapse(true,exceptioncode); + else if (partialStart) { + setStartContainer(partialStart->parentNode()); + setEndContainer(partialStart->parentNode()); + m_startOffset = m_endOffset = partialStart->nodeIndex()+1; + } + else if (partialEnd) { + setStartContainer(partialEnd->parentNode()); + setEndContainer(partialEnd->parentNode()); + m_startOffset = m_endOffset = partialEnd->nodeIndex(); + } + } + return fragment; +} + + +DocumentFragmentImpl *RangeImpl::extractContents( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + checkDeleteExtract(exceptioncode); + if (exceptioncode) + return 0; + + return processContents(EXTRACT_CONTENTS,exceptioncode); +} + +DocumentFragmentImpl *RangeImpl::cloneContents( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return processContents(CLONE_CONTENTS,exceptioncode); +} + +void RangeImpl::insertNode( NodeImpl *newNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of + // the Range is read-only. + NodeImpl *n = m_startContainer; + while (n && !n->isReadOnly()) + n = n->parentNode(); + if (n) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + n = m_endContainer; + while (n && !n->isReadOnly()) + n = n->parentNode(); + if (n) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were + // not created from the same document. + if (newNode->getDocument() != m_startContainer->getDocument()) { + exceptioncode = DOMException::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()) { + exceptioncode = DOMException::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. + NodeImpl *checkAgainst; + if (m_startContainer->nodeType() == Node::TEXT_NODE) + checkAgainst = m_startContainer->parentNode(); + else + checkAgainst = m_startContainer; + + if (newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { + // check each child node, not the DocumentFragment itself + NodeImpl *c; + for (c = newNode->firstChild(); c; c = c->nextSibling()) { + if (!checkAgainst->childTypeAllowed(c->nodeType())) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + } + else { + if (!checkAgainst->childTypeAllowed(newNode->nodeType())) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + + for (n = m_startContainer; n; n = n->parentNode()) { + if (n == newNode) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + + // 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) { + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + + if( m_startContainer->nodeType() == Node::TEXT_NODE || + m_startContainer->nodeType() == Node::CDATA_SECTION_NODE ) + { + TextImpl *newText = static_cast<TextImpl*>(m_startContainer)->splitText(m_startOffset,exceptioncode); + if (exceptioncode) + return; + m_startContainer->parentNode()->insertBefore( newNode, newText, exceptioncode ); + } + else { + m_startContainer->insertBefore( newNode, m_startContainer->childNode( m_startOffset ), exceptioncode ); + } +} + +DOMString RangeImpl::toString( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return DOMString(); + } + + DOMString text = ""; + NodeImpl *n = m_startContainer; + + /* This function converts a dom range to the plain text string that the user would see in this + * portion of rendered html. + * + * There are several ways ranges can be used. + * + * The simplest is the start and endContainer is a text node. The start and end offset is the + * number of characters into the text to remove/truncate. + * + * The next case is the start and endContainer is, well, a container, such a P tag or DIV tag. + * In this case the start and end offset is the number of children into the container to start + * from and end at. + * + * The other cases are different arrangements of the first two. + * + * psuedo code: + * + * if start container is not text: + * count through the children to find where we start (m_startOffset children) + * + * loop from the start position: + * if the current node is text, add the text to our variable 'text', truncating/removing if at the end/start. + * + * if the node has children, step to the first child. + * if the node has no children but does have siblings, step to the next sibling + * until we find a sibling, go to next the parent but: + * make sure this sibling isn't past the end of where we are supposed to go. (position > endOffset and the parent is the endContainer) + * + */ + + + if( m_startContainer == m_endContainer && m_startOffset >= m_endOffset) + return text; + + + if(n->firstChild()) { + n = n->firstChild(); + int current_offset = m_startOffset; + while(current_offset-- && n) { + n = n->nextSibling(); + } + } + + while(n) { + if(n->nodeType() == DOM::Node::TEXT_NODE || + n->nodeType() == DOM::Node::CDATA_SECTION_NODE) { + + DOMString str; + str = static_cast<TextImpl *>(n)->string(); + if( n == m_endContainer || n == m_startContainer) + str = str.copy(); //copy if we are going to modify. + + if (n == m_endContainer) + str.truncate(m_endOffset); + if (n == m_startContainer) + str.remove(0,m_startOffset); + text += str; + if (n == m_endContainer) + break; + } + + + NodeImpl *next = n->firstChild(); + if(!next) + next = n->nextSibling(); + + while( !next && n->parentNode() ) { + if (n == m_endContainer) return text; + n = n->parentNode(); + if (n == m_endContainer) return text; + next = n->nextSibling(); + } + + if(n->parentNode() == m_endContainer) { + if(!next) break; + unsigned long current_offset = 0; + NodeImpl *it = n; + while((it = it->previousSibling())) ++current_offset; + if(current_offset >= m_endOffset) { + break; + } + } + + n = next; + } + return text; +} + +DOMString RangeImpl::toHTML( int &exceptioncode ) +{ + bool hasHtmlTag = false; + bool hasBodyTag = false; + //FIXME: What is this section of code below exactly? Do I want it here? + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return DOMString(); + } + DOMString text = ""; + NodeImpl *n = m_startContainer; + int num_tables=0; + bool in_li = false; //whether we have an li in the text, without an ol/ul + int depth_difference = 0; + int lowest_depth_difference = 0; + + if( m_startContainer == m_endContainer && m_startOffset >= m_endOffset) + return text; + + while(n) { + /* First, we could have an tag <tagname key=value>otherstuff</tagname> */ + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { + int elementId = static_cast<ElementImpl *>(n)->id(); + if(elementId == ID_TABLE) num_tables++; + if(elementId == ID_BODY) hasBodyTag = true; + if(elementId == ID_HTML) hasHtmlTag = true; + if(elementId == ID_LI) in_li=true; + if(num_tables==0 && ( elementId == ID_TD || elementId == ID_TR || elementId == ID_TH || elementId == ID_TBODY || elementId == ID_TFOOT || elementId == ID_THEAD)) num_tables++; + if(!( !n->hasChildNodes() && (elementId == ID_H1 || elementId == ID_H2 || elementId == ID_H3 || elementId == ID_H4 || elementId ==ID_H5))) { //Don't add <h1/> etc. Just skip these nodes just to make the output html a bit nicer. + text += static_cast<ElementImpl *>(n)->openTagStartToString(true /*safely expand img urls*/); // adds "<tagname key=value" + if(n->hasChildNodes()) { + depth_difference++; + text += ">"; + } else { + text += "/>"; + } + } + } else + if(n->nodeType() == DOM::Node::TEXT_NODE || + n->nodeType() == DOM::Node::CDATA_SECTION_NODE) { + if(n->nodeType() == DOM::Node::CDATA_SECTION_NODE) text += "<![CDATA[ "; + long long startOffset = (n == m_startContainer)?(long long)m_startOffset:-1; + long long endOffset = (n == m_endContainer)?(long long) m_endOffset:-1; + text += static_cast<TextImpl *>(n)->toString(startOffset, endOffset); //Note this should always work since CDataImpl inherits TextImpl + if(n->nodeType() == DOM::Node::CDATA_SECTION_NODE) text += " ]]>"; + if(n == m_endContainer) { + break; + } + } + if(n->parentNode() == m_endContainer && !n->nextSibling()) { + break; + } + + //if (n == m_endContainer) break; + NodeImpl *next = n->firstChild(); + if(next) { + if(n == m_startContainer) { + //This is the start of our selection, so we have to move to where we have started selecting. + //For example, if 'n' is "hello <img src='hello.png'> how are you? <img src='goodbye.png'>" + //then this has four children. If our selection started on the image, then we need to start from there. + unsigned long current_offset = 0; + while(current_offset < m_startOffset && next) { + next = next->nextSibling(); + ++current_offset; + } + } + } else { + next = n->nextSibling(); + + if(n->parentNode() == m_endContainer) { + unsigned long current_offset = 1; + NodeImpl *it = n; + while((it = it->previousSibling())) ++current_offset; + + if(current_offset >= m_endOffset) { + break; + } + } + } + + while( !next && n->parentNode() ) { + n = n->parentNode(); + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { + text += "</"; + text += static_cast<ElementImpl *>(n)->tagName(); + int elementId = static_cast<ElementImpl *>(n)->id(); + if(elementId == ID_TABLE) num_tables--; + depth_difference--; + if(lowest_depth_difference > depth_difference) lowest_depth_difference=depth_difference; + if(num_tables==0 && ( elementId == ID_TD || elementId == ID_TR || elementId == ID_TH || elementId == ID_TBODY || elementId == ID_TFOOT || elementId == ID_THEAD)) num_tables--; + if(elementId == ID_OL || elementId == ID_UL) in_li=false; + text += ">"; + } + next = n->nextSibling(); + } + n = next; + } + + //We have the html in the selection. But now we need to properly add the opening and closing tags. + //For example say we have: "Hello <b>Mr. John</b> How are you?" and we select "John" or even + //"John</b> How" and copy. We want to return "<b>John</b>" and "<b>John</b> How" respectively + + //To do this, we need to go up the tree from the start, and prepend those tags. + //Imagine our selection was this: + // + // hello</b></p><p>there + // + // The difference in depths between the start and end is -1, and the lowest depth + // difference from the starting point is -2 + // + // So from the start of the selection, we want to go down to the lowest_depth_difference + // and prepend those tags. (<p><b>) + // + // From the end of the selection, we want to also go down to the lowest_depth_difference. + // We know the depth of the end of the selection - i.e. depth_difference. + // + // + n = m_startContainer; + int startdepth = 0; //by definition - we are counting from zero. + while((n = n->parentNode()) && startdepth>lowest_depth_difference) { + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right? + switch (static_cast<ElementImpl *>(n)->id()) { + case ID_TABLE: + num_tables--; + break; + case ID_BODY: + hasBodyTag = true; + break; + case ID_HTML: + hasHtmlTag = true; + break; + case ID_LI: + in_li = true; + break; + } + text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/)+">" +text; // prepends "<tagname key=value>" + } + startdepth--; + } + n = m_endContainer; + while( depth_difference>lowest_depth_difference && (n = n->parentNode())) { + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right? + switch (static_cast<ElementImpl *>(n)->id()) { + case ID_TABLE: + num_tables++; + break; + case ID_OL: + case ID_UL: + in_li=false; + break; + } + text += "</"; + text += static_cast<ElementImpl *>(n)->tagName(); + text += ">"; + } + depth_difference--; + } + + // Now our text string is the same depth on both sides, with nothing lower (in other words all the + // tags in it match up.) This also means that the end value for n in the first loop is a sibling of the + // end value for n in the second loop. + // + // We now need to go down the tree, and for certain tags, add them in on both ends of the text. + // For example, if have: "<b>hello</b>" and we select "ll", then we want to go down the tree and + // add "<b>" and "</b>" to it, to produce "<b>ll</b>". + // + // I just guessed at which tags you'd want to keep (bold, italic etc) and which you wouldn't (tables etc). + // It's just wild guessing. feel free to change. + // + // Note we can carry on with the value of n + if(n) { + while((n = n->parentNode())) { + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right? + int elementId = static_cast<ElementImpl *>(n)->id(); + switch (elementId) { + case ID_TABLE: + case ID_TD: + case ID_TR: + case ID_TH: + case ID_TBODY: + case ID_TFOOT: + case ID_THEAD: + if(num_tables>0) { + if(elementId == ID_TABLE) num_tables--; + text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/)+">" +text; + text += "</"; + text += static_cast<ElementImpl *>(n)->tagName(); + text += ">"; + + } + break; + + case ID_LI: + if(!in_li) break; + text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/)+">" +text; + text += "</"; + text += static_cast<ElementImpl *>(n)->tagName(); + text += ">"; + break; + + case ID_UL: + case ID_OL: + if(!in_li) break; + in_li = false; + case ID_B: + case ID_I: + case ID_U: + case ID_FONT: + case ID_S: + case ID_STRONG: + case ID_STRIKE: + case ID_DEL: + case ID_A: + case ID_H1: + case ID_H2: + case ID_H3: + case ID_H4: + case ID_H5: + //should small, etc be here? so hard to decide. this is such a hack :( + //There's probably tons of others you'd want here. + text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/)+">" +text; + text += "</"; + text += static_cast<ElementImpl *>(n)->tagName(); + text += ">"; + break; + } + } + } + } + + + if(!hasBodyTag) text = DOMString("<body>") + text + "</body>"; + else if(!hasHtmlTag) { + text = DOMString("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" + "<head>\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" + "<meta name=\"Generator\" content=\"KHTML, the KDE Web Page Viewer\" />\n" + "</head>\n") + + text + + "</html>"; + } + text = DOMString("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n") + text; + + return text; + +} + +DocumentFragment RangeImpl::createContextualFragment ( const DOMString &html, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return DocumentFragment(); + } + + if (! m_startContainer->isHTMLElement()) { + exceptioncode = DOMException::NOT_SUPPORTED_ERR; + return DocumentFragment(); + } + + HTMLElementImpl *e = static_cast<HTMLElementImpl *>(m_startContainer); + DocumentFragment fragment = e->createContextualFragment(html); + if (fragment.isNull()) { + exceptioncode = DOMException::NOT_SUPPORTED_ERR; + return DocumentFragment(); + } + + return fragment; +} + + +void RangeImpl::detach( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (m_startContainer) + m_startContainer->deref(); + m_startContainer = 0; + if (m_endContainer) + m_endContainer->deref(); + m_endContainer = 0; + m_detached = true; +} + +bool RangeImpl::isDetached() const +{ + return m_detached; +} + +void RangeImpl::checkNodeWOffset( NodeImpl *n, int offset, int &exceptioncode) const +{ + if( offset < 0 ) { + exceptioncode = DOMException::INDEX_SIZE_ERR; + } + + switch (n->nodeType()) { + case Node::ENTITY_NODE: + case Node::NOTATION_NODE: + case Node::DOCUMENT_TYPE_NODE: + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + break; + case Node::TEXT_NODE: + case Node::COMMENT_NODE: + case Node::CDATA_SECTION_NODE: + if ( (unsigned long)offset > static_cast<CharacterDataImpl*>(n)->length() ) + exceptioncode = DOMException::INDEX_SIZE_ERR; + break; + case Node::PROCESSING_INSTRUCTION_NODE: + // ### are we supposed to check with just data or the whole contents? + if ( (unsigned long)offset > static_cast<ProcessingInstructionImpl*>(n)->data().length() ) + exceptioncode = DOMException::INDEX_SIZE_ERR; + break; + default: + if ( (unsigned long)offset > n->childNodeCount() ) + exceptioncode = DOMException::INDEX_SIZE_ERR; + break; + } +} + +void RangeImpl::checkNodeBA( NodeImpl *n, int &exceptioncode ) const +{ + // INVALID_NODE_TYPE_ERR: Raised if the root container of refNode is not an + // Attr, Document or DocumentFragment node or if refNode is a Document, + // DocumentFragment, Attr, Entity, or Notation node. + NodeImpl *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)) { + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + + 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 ) + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + +} + +RangeImpl *RangeImpl::cloneRange(int &exceptioncode) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return new RangeImpl(m_ownerDocument,m_startContainer,m_startOffset,m_endContainer,m_endOffset); +} + +void RangeImpl::setStartAfter( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeBA( refNode, exceptioncode ); + if (exceptioncode) + return; + + setStart( refNode->parentNode(), refNode->nodeIndex()+1, exceptioncode ); +} + +void RangeImpl::setEndBefore( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeBA( refNode, exceptioncode ); + if (exceptioncode) + return; + + setEnd( refNode->parentNode(), refNode->nodeIndex(), exceptioncode ); +} + +void RangeImpl::setEndAfter( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeBA( refNode, exceptioncode ); + if (exceptioncode) + return; + + setEnd( refNode->parentNode(), refNode->nodeIndex()+1, exceptioncode ); + +} + +void RangeImpl::selectNode( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::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, Attr, Entity, or Notation + // node. + NodeImpl *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) { + + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + 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) { + + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + + setStartBefore( refNode, exceptioncode ); + if (exceptioncode) + return; + setEndAfter( refNode, exceptioncode ); +} + +void RangeImpl::selectNodeContents( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + // INVALID_NODE_TYPE_ERR: Raised if refNode or an ancestor of refNode is an Entity, Notation + // or DocumentType node. + NodeImpl *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) { + + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + } + + setStartContainer(refNode); + m_startOffset = 0; + setEndContainer(refNode); + m_endOffset = refNode->childNodeCount(); +} + +void RangeImpl::surroundContents( NodeImpl *newParent, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if( !newParent ) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + // 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) { + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of + // the Range is read-only. + if (readOnly()) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + NodeImpl *n = m_startContainer; + while (n && !n->isReadOnly()) + n = n->parentNode(); + if (n) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + n = m_endContainer; + while (n && !n->isReadOnly()) + n = n->parentNode(); + if (n) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were + // not created from the same document. + if (newParent->getDocument() != m_startContainer->getDocument()) { + exceptioncode = DOMException::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 newParent or if newParent is an ancestor of the container + // or if node would end up with a child node of a type not allowed by the type of node. + if (!m_startContainer->childTypeAllowed(newParent->nodeType())) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + + for (n = m_startContainer; n; n = n->parentNode()) { + if (n == newParent) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + + // ### check if 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->nodeType() != Node::TEXT_NODE && + m_startContainer->nodeType() != Node::COMMENT_NODE && + m_startContainer->nodeType() != Node::CDATA_SECTION_NODE && + m_startContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) { + + if (m_startOffset > 0 && m_startOffset < m_startContainer->childNodeCount()) { + exceptioncode = RangeException::BAD_BOUNDARYPOINTS_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + } + + if (m_endContainer->nodeType() != Node::TEXT_NODE && + m_endContainer->nodeType() != Node::COMMENT_NODE && + m_endContainer->nodeType() != Node::CDATA_SECTION_NODE && + m_endContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) { + + if (m_endOffset > 0 && m_endOffset < m_endContainer->childNodeCount()) { + exceptioncode = RangeException::BAD_BOUNDARYPOINTS_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + } + + while (newParent->firstChild()) { + newParent->removeChild(newParent->firstChild(),exceptioncode); + if (exceptioncode) + return; + } + DocumentFragmentImpl *fragment = extractContents(exceptioncode); + if (exceptioncode) + return; + insertNode( newParent, exceptioncode ); + if (exceptioncode) + return; + newParent->appendChild( fragment, exceptioncode ); + if (exceptioncode) + return; + selectNode( newParent, exceptioncode ); +} + +void RangeImpl::setStartBefore( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeBA( refNode, exceptioncode ); + if (exceptioncode) + return; + + setStart( refNode->parentNode(), refNode->nodeIndex(), exceptioncode ); +} + +void RangeImpl::setStartContainer(NodeImpl *_startContainer) +{ + if (m_startContainer == _startContainer) + return; + + if (m_startContainer) + m_startContainer->deref(); + m_startContainer = _startContainer; + if (m_startContainer) + m_startContainer->ref(); +} + +void RangeImpl::setEndContainer(NodeImpl *_endContainer) +{ + if (m_endContainer == _endContainer) + return; + + if (m_endContainer) + m_endContainer->deref(); + m_endContainer = _endContainer; + if (m_endContainer) + m_endContainer->ref(); +} + +void RangeImpl::checkDeleteExtract(int &exceptioncode) { + + NodeImpl *start; + if (m_startContainer->nodeType() != Node::TEXT_NODE && + m_startContainer->nodeType() != Node::CDATA_SECTION_NODE && + m_startContainer->nodeType() != Node::COMMENT_NODE && + m_startContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) { + + start = m_startContainer->childNode(m_startOffset); + if (!start) { + if (m_startContainer->lastChild()) + start = m_startContainer->lastChild()->traverseNextNode(); + else + start = m_startContainer->traverseNextNode(); + } + } + else + start = m_startContainer; + + NodeImpl *end; + if (m_endContainer->nodeType() != Node::TEXT_NODE && + m_endContainer->nodeType() != Node::CDATA_SECTION_NODE && + m_endContainer->nodeType() != Node::COMMENT_NODE && + m_endContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) { + + end = m_endContainer->childNode(m_endOffset); + if (!end) { + if (m_endContainer->lastChild()) + end = m_endContainer->lastChild()->traverseNextNode(); + else + end = m_endContainer->traverseNextNode(); + } + } + else + end = m_endContainer; + + NodeImpl *n; + for (n = start; n != end; n = n->traverseNextNode()) { + if (n->isReadOnly()) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { // ### is this for only directly under the DF, or anywhere? + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + + if (containedByReadOnly()) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } +} + + +bool RangeImpl::containedByReadOnly() { + NodeImpl *n; + for (n = m_startContainer; n; n = n->parentNode()) { + if (n->isReadOnly()) + return true; + } + for (n = m_endContainer; n; n = n->parentNode()) { + if (n->isReadOnly()) + return true; + } + return false; +} + + + + + + + + |