File indexing completed on 2024-05-05 12:16:39

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
0005  * (C) 2001-2003 Dirk Mueller (mueller@kde.org)
0006  * (C) 2000 Gunnstein Lye (gunnstein@netcom.no)
0007  * (C) 2000 Frederik Holljen (frederik.holljen@hig.no)
0008  * (C) 2001 Peter Kelly (pmk@post.com)
0009  * Copyright (C) 2003-2008 Apple Computer, Inc.
0010  *
0011  * This library is free software; you can redistribute it and/or
0012  * modify it under the terms of the GNU Library General Public
0013  * License as published by the Free Software Foundation; either
0014  * version 2 of the License, or (at your option) any later version.
0015  *
0016  * This library is distributed in the hope that it will be useful,
0017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0019  * Library General Public License for more details.
0020  *
0021  * You should have received a copy of the GNU Library General Public License
0022  * along with this library; see the file COPYING.LIB.  If not, write to
0023  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0024  * Boston, MA 02110-1301, USA.
0025  */
0026 
0027 #include "dom2_rangeimpl.h"
0028 
0029 #include <dom/dom_exception.h>
0030 #include "dom_docimpl.h"
0031 #include "dom_textimpl.h"
0032 #include "dom_xmlimpl.h"
0033 #include <html/html_elementimpl.h>
0034 
0035 using namespace DOM;
0036 
0037 RangeImpl::RangeImpl(DocumentImpl *_ownerDocument)
0038 {
0039     m_ownerDocument = _ownerDocument;
0040     m_ownerDocument->ref();
0041     m_startContainer = _ownerDocument;
0042     m_startContainer->ref();
0043     m_endContainer = _ownerDocument;
0044     m_endContainer->ref();
0045     m_startOffset = 0;
0046     m_endOffset = 0;
0047     m_detached = false;
0048 }
0049 
0050 RangeImpl::RangeImpl(DocumentImpl *_ownerDocument,
0051                      NodeImpl *_startContainer, long _startOffset,
0052                      NodeImpl *_endContainer, long _endOffset)
0053 {
0054     m_ownerDocument = _ownerDocument;
0055     m_ownerDocument->ref();
0056     m_startContainer = _startContainer;
0057     m_startContainer->ref();
0058     m_startOffset = _startOffset;
0059     m_endContainer = _endContainer;
0060     m_endContainer->ref();
0061     m_endOffset = _endOffset;
0062     m_detached = false;
0063 }
0064 
0065 RangeImpl::~RangeImpl()
0066 {
0067     m_ownerDocument->deref();
0068     int exceptioncode = 0;
0069     if (!m_detached) {
0070         detach(exceptioncode);
0071     }
0072 }
0073 
0074 NodeImpl *RangeImpl::startContainer(int &exceptioncode) const
0075 {
0076     if (m_detached) {
0077         exceptioncode = DOMException::INVALID_STATE_ERR;
0078         return nullptr;
0079     }
0080 
0081     return m_startContainer;
0082 }
0083 
0084 long RangeImpl::startOffset(int &exceptioncode) const
0085 {
0086     if (m_detached) {
0087         exceptioncode = DOMException::INVALID_STATE_ERR;
0088         return 0;
0089     }
0090 
0091     return m_startOffset;
0092 }
0093 
0094 NodeImpl *RangeImpl::endContainer(int &exceptioncode) const
0095 {
0096     if (m_detached) {
0097         exceptioncode = DOMException::INVALID_STATE_ERR;
0098         return nullptr;
0099     }
0100 
0101     return m_endContainer;
0102 }
0103 
0104 long RangeImpl::endOffset(int &exceptioncode) const
0105 {
0106     if (m_detached) {
0107         exceptioncode = DOMException::INVALID_STATE_ERR;
0108         return 0;
0109     }
0110 
0111     return m_endOffset;
0112 }
0113 
0114 NodeImpl *RangeImpl::commonAncestorContainer(int &exceptioncode)
0115 {
0116     if (m_detached) {
0117         exceptioncode = DOMException::INVALID_STATE_ERR;
0118         return nullptr;
0119     }
0120 
0121     NodeImpl *com = commonAncestorContainer(m_startContainer, m_endContainer);
0122     if (!com) { //  should never happen
0123         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
0124     }
0125     return com;
0126 }
0127 
0128 NodeImpl *RangeImpl::commonAncestorContainer(NodeImpl *containerA, NodeImpl *containerB)
0129 {
0130     NodeImpl *parentStart;
0131 
0132     for (parentStart = containerA; parentStart; parentStart = parentStart->parentNode()) {
0133         NodeImpl *parentEnd = containerB;
0134         while (parentEnd && (parentStart != parentEnd)) {
0135             parentEnd = parentEnd->parentNode();
0136         }
0137 
0138         if (parentStart == parentEnd) {
0139             break;
0140         }
0141     }
0142 
0143     return parentStart;
0144 }
0145 
0146 bool RangeImpl::collapsed(int &exceptioncode) const
0147 {
0148     if (m_detached) {
0149         exceptioncode = DOMException::INVALID_STATE_ERR;
0150         return 0;
0151     }
0152 
0153     return (m_startContainer == m_endContainer && m_startOffset == m_endOffset);
0154 }
0155 
0156 void RangeImpl::setStart(NodeImpl *refNode, long offset, int &exceptioncode)
0157 {
0158     if (m_detached) {
0159         exceptioncode = DOMException::INVALID_STATE_ERR;
0160         return;
0161     }
0162 
0163     if (!refNode) {
0164         exceptioncode = DOMException::NOT_FOUND_ERR;
0165         return;
0166     }
0167 
0168     if (refNode->document() != m_ownerDocument) {
0169         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
0170         return;
0171     }
0172 
0173     checkNodeWOffset(refNode, offset, exceptioncode);
0174     if (exceptioncode) {
0175         return;
0176     }
0177 
0178     setStartContainer(refNode);
0179     m_startOffset = offset;
0180 
0181     // check if different root container
0182     NodeImpl *endRootContainer = m_endContainer;
0183     while (endRootContainer->parentNode()) {
0184         endRootContainer = endRootContainer->parentNode();
0185     }
0186     NodeImpl *startRootContainer = m_startContainer;
0187     while (startRootContainer->parentNode()) {
0188         startRootContainer = startRootContainer->parentNode();
0189     }
0190     if (startRootContainer != endRootContainer) {
0191         collapse(true, exceptioncode);
0192     }
0193     // check if new start after end
0194     else if (compareBoundaryPoints(m_startContainer, m_startOffset, m_endContainer, m_endOffset) > 0) {
0195         collapse(true, exceptioncode);
0196     }
0197 }
0198 
0199 void RangeImpl::setEnd(NodeImpl *refNode, long offset, int &exceptioncode)
0200 {
0201     if (m_detached) {
0202         exceptioncode = DOMException::INVALID_STATE_ERR;
0203         return;
0204     }
0205 
0206     if (!refNode) {
0207         exceptioncode = DOMException::NOT_FOUND_ERR;
0208         return;
0209     }
0210 
0211     if (refNode->document() != m_ownerDocument) {
0212         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
0213         return;
0214     }
0215 
0216     checkNodeWOffset(refNode, offset, exceptioncode);
0217     if (exceptioncode) {
0218         return;
0219     }
0220 
0221     setEndContainer(refNode);
0222     m_endOffset = offset;
0223 
0224     // check if different root container
0225     NodeImpl *endRootContainer = m_endContainer;
0226     while (endRootContainer->parentNode()) {
0227         endRootContainer = endRootContainer->parentNode();
0228     }
0229     NodeImpl *startRootContainer = m_startContainer;
0230     while (startRootContainer->parentNode()) {
0231         startRootContainer = startRootContainer->parentNode();
0232     }
0233     if (startRootContainer != endRootContainer) {
0234         collapse(false, exceptioncode);
0235     }
0236     // check if new end before start
0237     if (compareBoundaryPoints(m_startContainer, m_startOffset, m_endContainer, m_endOffset) > 0) {
0238         collapse(false, exceptioncode);
0239     }
0240 }
0241 
0242 void RangeImpl::collapse(bool toStart, int &exceptioncode)
0243 {
0244     if (m_detached) {
0245         exceptioncode = DOMException::INVALID_STATE_ERR;
0246         return;
0247     }
0248 
0249     if (toStart) {  // collapse to start
0250         setEndContainer(m_startContainer);
0251         m_endOffset = m_startOffset;
0252     } else {        // collapse to end
0253         setStartContainer(m_endContainer);
0254         m_startOffset = m_endOffset;
0255     }
0256 }
0257 
0258 short RangeImpl::compareBoundaryPoints(Range::CompareHow how, RangeImpl *sourceRange, int &exceptioncode)
0259 {
0260     if (m_detached) {
0261         exceptioncode = DOMException::INVALID_STATE_ERR;
0262         return 0;
0263     }
0264 
0265     if (!sourceRange) {
0266         exceptioncode = DOMException::NOT_FOUND_ERR;
0267         return 0;
0268     }
0269 
0270     NodeImpl *thisCont = commonAncestorContainer(exceptioncode);
0271     NodeImpl *sourceCont = sourceRange->commonAncestorContainer(exceptioncode);
0272     if (exceptioncode) {
0273         return 0;
0274     }
0275 
0276     if (thisCont->document() != sourceCont->document()) {
0277         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
0278         return 0;
0279     }
0280 
0281     NodeImpl *thisTop = thisCont;
0282     NodeImpl *sourceTop = sourceCont;
0283     while (thisTop->parentNode()) {
0284         thisTop = thisTop->parentNode();
0285     }
0286     while (sourceTop->parentNode()) {
0287         sourceTop = sourceTop->parentNode();
0288     }
0289     if (thisTop != sourceTop) { // in different DocumentFragments
0290         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
0291         return 0;
0292     }
0293 
0294     switch (how) {
0295     case Range::START_TO_START:
0296         return compareBoundaryPoints(m_startContainer, m_startOffset,
0297                                      sourceRange->startContainer(exceptioncode), sourceRange->startOffset(exceptioncode));
0298         break;
0299     case Range::START_TO_END:
0300         return compareBoundaryPoints(m_endContainer, m_endOffset,
0301                                      sourceRange->startContainer(exceptioncode), sourceRange->startOffset(exceptioncode));
0302         break;
0303     case Range::END_TO_END:
0304         return compareBoundaryPoints(m_endContainer, m_endOffset,
0305                                      sourceRange->endContainer(exceptioncode), sourceRange->endOffset(exceptioncode));
0306         break;
0307     case Range::END_TO_START:
0308         return compareBoundaryPoints(m_startContainer, m_startOffset,
0309                                      sourceRange->endContainer(exceptioncode), sourceRange->endOffset(exceptioncode));
0310         break;
0311     default:
0312         exceptioncode = DOMException::SYNTAX_ERR;
0313         return 0;
0314     }
0315 }
0316 
0317 short RangeImpl::compareBoundaryPoints(NodeImpl *containerA, long offsetA, NodeImpl *containerB, long offsetB)
0318 {
0319     // see DOM2 traversal & range section 2.5
0320 
0321     // case 1: both points have the same container
0322     if (containerA == containerB) {
0323         if (offsetA == offsetB) {
0324             return 0;    // A is equal to B
0325         }
0326         if (offsetA < offsetB) {
0327             return -1;    // A is before B
0328         } else {
0329             return 1;    // A is after B
0330         }
0331     }
0332 
0333     // case 2: node C (container B or an ancestor) is a child node of A
0334     NodeImpl *c = containerB;
0335     while (c && c->parentNode() != containerA) {
0336         c = c->parentNode();
0337     }
0338     if (c) {
0339         int offsetC = 0;
0340         NodeImpl *n = containerA->firstChild();
0341         while (n != c) {
0342             offsetC++;
0343             n = n->nextSibling();
0344         }
0345 
0346         if (offsetA <= offsetC) {
0347             return -1;    // A is before B
0348         } else {
0349             return 1;    // A is after B
0350         }
0351     }
0352 
0353     // case 3: node C (container A or an ancestor) is a child node of B
0354     c = containerA;
0355     while (c && c->parentNode() != containerB) {
0356         c = c->parentNode();
0357     }
0358     if (c) {
0359         int offsetC = 0;
0360         NodeImpl *n = containerB->firstChild();
0361         while (n != c) {
0362             offsetC++;
0363             n = n->nextSibling();
0364         }
0365 
0366         if (offsetC < offsetB) {
0367             return -1;    // A is before B
0368         } else {
0369             return 1;    // A is after B
0370         }
0371     }
0372 
0373     // case 4: containers A & B are siblings, or children of siblings
0374     // ### we need to do a traversal here instead
0375     NodeImpl *cmnRoot = commonAncestorContainer(containerA, containerB);
0376     if (!cmnRoot) {
0377         return -1;    // Whatever...
0378     }
0379     NodeImpl *childA = containerA;
0380     while (childA->parentNode() != cmnRoot) {
0381         childA = childA->parentNode();
0382     }
0383     NodeImpl *childB = containerB;
0384     while (childB->parentNode() != cmnRoot) {
0385         childB = childB->parentNode();
0386     }
0387 
0388     NodeImpl *n = cmnRoot->firstChild();
0389     int i = 0;
0390     int childAOffset = -1;
0391     int childBOffset = -1;
0392     while (childAOffset < 0 || childBOffset < 0) {
0393         if (n == childA) {
0394             childAOffset = i;
0395         }
0396         if (n == childB) {
0397             childBOffset = i;
0398         }
0399         n = n->nextSibling();
0400         i++;
0401     }
0402 
0403     if (childAOffset == childBOffset) {
0404         return 0;    // A is equal to B
0405     }
0406     if (childAOffset < childBOffset) {
0407         return -1;    // A is before B
0408     } else {
0409         return 1;    // A is after B
0410     }
0411 }
0412 
0413 bool RangeImpl::boundaryPointsValid()
0414 {
0415     short valid =  compareBoundaryPoints(m_startContainer, m_startOffset,
0416                                          m_endContainer, m_endOffset);
0417     if (valid == 1) {
0418         return false;
0419     } else {
0420         return true;
0421     }
0422 
0423 }
0424 
0425 void RangeImpl::deleteContents(int &exceptioncode)
0426 {
0427     if (m_detached) {
0428         exceptioncode = DOMException::INVALID_STATE_ERR;
0429         return;
0430     }
0431 
0432     checkDeleteExtract(exceptioncode);
0433     if (exceptioncode) {
0434         return;
0435     }
0436 
0437     processContents(DELETE_CONTENTS, exceptioncode);
0438 }
0439 
0440 DocumentFragmentImpl *RangeImpl::processContents(ActionType action, int &exceptioncode)
0441 {
0442     // ### when mutation events are implemented, we will have to take into account
0443     // situations where the tree is being transformed while we delete - ugh!
0444 
0445     // ### perhaps disable node deletion notification for this range while we do this?
0446 
0447     // shortcut for special case
0448     if (collapsed(exceptioncode) || exceptioncode) {
0449         if (action == CLONE_CONTENTS || action == EXTRACT_CONTENTS) {
0450             return new DocumentFragmentImpl(m_ownerDocument);
0451         }
0452         return nullptr;
0453     }
0454 
0455     NodeImpl *cmnRoot = commonAncestorContainer(exceptioncode);
0456     if (exceptioncode) {
0457         return nullptr;
0458     }
0459 
0460     // what is the highest node that partially selects the start of the range?
0461     NodeImpl *partialStart = nullptr;
0462     if (m_startContainer != cmnRoot) {
0463         partialStart = m_startContainer;
0464         while (partialStart->parentNode() != cmnRoot) {
0465             partialStart = partialStart->parentNode();
0466         }
0467     }
0468 
0469     // what is the highest node that partially selects the end of the range?
0470     NodeImpl *partialEnd = nullptr;
0471     if (m_endContainer != cmnRoot) {
0472         partialEnd = m_endContainer;
0473         while (partialEnd->parentNode() != cmnRoot) {
0474             partialEnd = partialEnd->parentNode();
0475         }
0476     }
0477 
0478     DocumentFragmentImpl *fragment = nullptr;
0479     if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
0480         fragment = new DocumentFragmentImpl(m_ownerDocument);
0481     }
0482 
0483     // Simple case: the start and end containers are the same. We just grab
0484     // everything >= start offset and < end offset
0485     if (m_startContainer == m_endContainer) {
0486         if (m_startContainer->nodeType() == Node::TEXT_NODE ||
0487                 m_startContainer->nodeType() == Node::CDATA_SECTION_NODE ||
0488                 m_startContainer->nodeType() == Node::COMMENT_NODE) {
0489 
0490             if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
0491                 RefPtr<CharacterDataImpl> c = static_pointer_cast<CharacterDataImpl>(m_startContainer->cloneNode(true));
0492                 c->deleteData(m_endOffset, static_cast<CharacterDataImpl *>(m_startContainer)->length() - m_endOffset, exceptioncode);
0493                 c->deleteData(0, m_startOffset, exceptioncode);
0494                 fragment->appendChild(c.get(), exceptioncode);
0495             }
0496             if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
0497                 static_cast<CharacterDataImpl *>(m_startContainer)->deleteData(m_startOffset, m_endOffset - m_startOffset, exceptioncode);
0498             }
0499         } else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
0500             // ### operate just on data ?
0501         } else {
0502             NodeImpl *n = m_startContainer->firstChild();
0503             unsigned long i;
0504             for (i = 0; n && i < m_startOffset; i++) { // skip until m_startOffset
0505                 n = n->nextSibling();
0506             }
0507             while (n && i < m_endOffset) { // delete until m_endOffset
0508                 NodeImpl *next = n->nextSibling();
0509                 if (action == EXTRACT_CONTENTS) {
0510                     fragment->appendChild(n, exceptioncode);    // will remove n from its parent
0511                 } else if (action == CLONE_CONTENTS) {
0512                     fragment->appendChild(n->cloneNode(true).get(), exceptioncode);
0513                 } else {
0514                     m_startContainer->removeChild(n, exceptioncode);
0515                 }
0516                 n = next;
0517                 i++;
0518             }
0519         }
0520         collapse(true, exceptioncode);
0521         return fragment;
0522     }
0523 
0524     // Complex case: Start and end containers are different.
0525     // There are three possiblities here:
0526     // 1. Start container == cmnRoot (End container must be a descendant)
0527     // 2. End container == cmnRoot (Start container must be a descendant)
0528     // 3. Neither is cmnRoot, they are both descendants
0529     //
0530     // In case 3, we grab everything after the start (up until a direct child
0531     // of cmnRoot) into leftContents, and everything before the end (up until
0532     // a direct child of cmnRoot) into rightContents. Then we process all
0533     // cmnRoot children between leftContents and rightContents
0534     //
0535     // In case 1 or 2, we skip either processing of leftContents or rightContents,
0536     // in which case the last lot of nodes either goes from the first or last
0537     // child of cmnRoot.
0538     //
0539     // These are deleted, cloned, or extracted (i.e. both) depending on action.
0540 
0541     RefPtr<NodeImpl> leftContents = nullptr;
0542     if (m_startContainer != cmnRoot) {
0543         // process the left-hand side of the range, up until the last ancestor of
0544         // m_startContainer before cmnRoot
0545         if (m_startContainer->nodeType() == Node::TEXT_NODE ||
0546                 m_startContainer->nodeType() == Node::CDATA_SECTION_NODE ||
0547                 m_startContainer->nodeType() == Node::COMMENT_NODE) {
0548 
0549             if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
0550                 RefPtr<CharacterDataImpl> c = static_pointer_cast<CharacterDataImpl>(m_startContainer->cloneNode(true));
0551                 c->deleteData(0, m_startOffset, exceptioncode);
0552                 leftContents = c;
0553             }
0554             if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
0555                 static_cast<CharacterDataImpl *>(m_startContainer)->deleteData(
0556                     m_startOffset, static_cast<CharacterDataImpl *>(m_startContainer)->length() - m_startOffset, exceptioncode);
0557         } else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
0558             // ### operate just on data ?
0559             // leftContents = ...
0560         } else {
0561             if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
0562                 leftContents = m_startContainer->cloneNode(false);
0563             }
0564             NodeImpl *n = m_startContainer->firstChild();
0565             for (unsigned long i = 0; n && i < m_startOffset; i++) { // skip until m_startOffset
0566                 n = n->nextSibling();
0567             }
0568             while (n) { // process until end
0569                 NodeImpl *next = n->nextSibling();
0570                 if (action == EXTRACT_CONTENTS) {
0571                     leftContents->appendChild(n, exceptioncode);    // will remove n from m_startContainer
0572                 } else if (action == CLONE_CONTENTS) {
0573                     leftContents->appendChild(n->cloneNode(true).get(), exceptioncode);
0574                 } else {
0575                     m_startContainer->removeChild(n, exceptioncode);
0576                 }
0577                 n = next;
0578             }
0579         }
0580 
0581         NodeImpl *leftParent = m_startContainer->parentNode();
0582         NodeImpl *n = m_startContainer->nextSibling();
0583         for (; leftParent != cmnRoot; leftParent = leftParent->parentNode()) {
0584             if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
0585                 RefPtr<NodeImpl> leftContentsParent = leftParent->cloneNode(false);
0586                 leftContentsParent->appendChild(leftContents.get(), exceptioncode);
0587                 leftContents = leftContentsParent;
0588             }
0589 
0590             NodeImpl *next;
0591             for (; n; n = next) {
0592                 next = n->nextSibling();
0593                 if (action == EXTRACT_CONTENTS) {
0594                     leftContents->appendChild(n, exceptioncode);    // will remove n from leftParent
0595                 } else if (action == CLONE_CONTENTS) {
0596                     leftContents->appendChild(n->cloneNode(true).get(), exceptioncode);
0597                 } else {
0598                     leftParent->removeChild(n, exceptioncode);
0599                 }
0600             }
0601             n = leftParent->nextSibling();
0602         }
0603     }
0604 
0605     RefPtr<NodeImpl> rightContents = nullptr;
0606     if (m_endContainer != cmnRoot) {
0607         // delete the right-hand side of the range, up until the last ancestor of
0608         // m_endContainer before cmnRoot
0609         if (m_endContainer->nodeType() == Node::TEXT_NODE ||
0610                 m_endContainer->nodeType() == Node::CDATA_SECTION_NODE ||
0611                 m_endContainer->nodeType() == Node::COMMENT_NODE) {
0612 
0613             if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
0614                 RefPtr<CharacterDataImpl> c =
0615                     static_pointer_cast<CharacterDataImpl>(m_endContainer->cloneNode(true));
0616                 c->deleteData(m_endOffset, static_cast<CharacterDataImpl *>(m_endContainer)->length() - m_endOffset, exceptioncode);
0617                 rightContents = c;
0618             }
0619             if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
0620                 static_cast<CharacterDataImpl *>(m_endContainer)->deleteData(0, m_endOffset, exceptioncode);
0621             }
0622         } else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
0623             // ### operate just on data ?
0624             // rightContents = ...
0625         } else {
0626             if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
0627                 rightContents = m_endContainer->cloneNode(false);
0628             }
0629             NodeImpl *n = m_endContainer->firstChild();
0630             if (n && m_endOffset) {
0631                 for (unsigned long i = 0; i + 1 < m_endOffset; i++) { // skip to m_endOffset
0632                     NodeImpl *next = n->nextSibling();
0633                     if (!next) {
0634                         break;
0635                     }
0636                     n = next;
0637                 }
0638                 NodeImpl *prev;
0639                 for (; n; n = prev) {
0640                     prev = n->previousSibling();
0641                     if (action == EXTRACT_CONTENTS) {
0642                         rightContents->insertBefore(n, rightContents->firstChild(), exceptioncode);    // will remove n from its parent
0643                     } else if (action == CLONE_CONTENTS) {
0644                         rightContents->insertBefore(n->cloneNode(true).get(), rightContents->firstChild(), exceptioncode);
0645                     } else {
0646                         m_endContainer->removeChild(n, exceptioncode);
0647                     }
0648                 }
0649             }
0650         }
0651 
0652         NodeImpl *rightParent = m_endContainer->parentNode();
0653         NodeImpl *n = m_endContainer->previousSibling();
0654         for (; rightParent != cmnRoot; rightParent = rightParent->parentNode()) {
0655             if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
0656                 RefPtr<NodeImpl> rightContentsParent = rightParent->cloneNode(false);
0657                 rightContentsParent->appendChild(rightContents.get(), exceptioncode);
0658                 rightContents = rightContentsParent;
0659             }
0660 
0661             NodeImpl *prev;
0662             for (; n; n = prev) {
0663                 prev = n->previousSibling();
0664                 if (action == EXTRACT_CONTENTS) {
0665                     rightContents->insertBefore(n, rightContents->firstChild(), exceptioncode);    // will remove n from its parent
0666                 } else if (action == CLONE_CONTENTS) {
0667                     rightContents->insertBefore(n->cloneNode(true).get(), rightContents->firstChild(), exceptioncode);
0668                 } else {
0669                     rightParent->removeChild(n, exceptioncode);
0670                 }
0671 
0672             }
0673             n = rightParent->previousSibling();
0674         }
0675     }
0676 
0677     // delete all children of cmnRoot between the start and end container
0678 
0679     NodeImpl *processStart; // child of cmnRooot
0680     if (m_startContainer == cmnRoot) {
0681         unsigned long i;
0682         processStart = m_startContainer->firstChild();
0683         for (i = 0; i < m_startOffset; i++) {
0684             processStart = processStart->nextSibling();
0685         }
0686     } else {
0687         processStart = m_startContainer;
0688         while (processStart->parentNode() != cmnRoot) {
0689             processStart = processStart->parentNode();
0690         }
0691         processStart = processStart->nextSibling();
0692     }
0693     NodeImpl *processEnd; // child of cmnRooot
0694     if (m_endContainer == cmnRoot) {
0695         unsigned long i;
0696         processEnd = m_endContainer->firstChild();
0697         for (i = 0; i < m_endOffset; i++) {
0698             processEnd = processEnd->nextSibling();
0699         }
0700     } else {
0701         processEnd = m_endContainer;
0702         while (processEnd->parentNode() != cmnRoot) {
0703             processEnd = processEnd->parentNode();
0704         }
0705     }
0706 
0707     // Now add leftContents, stuff in between, and rightContents to the fragment
0708     // (or just delete the stuff in between)
0709 
0710     if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents) {
0711         fragment->appendChild(leftContents.get(), exceptioncode);
0712     }
0713 
0714     NodeImpl *next;
0715     NodeImpl *n;
0716     if (processStart) {
0717         for (n = processStart; n && n != processEnd; n = next) {
0718             next = n->nextSibling();
0719 
0720             if (action == EXTRACT_CONTENTS) {
0721                 fragment->appendChild(n, exceptioncode);    // will remove from cmnRoot
0722             } else if (action == CLONE_CONTENTS) {
0723                 fragment->appendChild(n->cloneNode(true).get(), exceptioncode);
0724             } else {
0725                 cmnRoot->removeChild(n, exceptioncode);
0726             }
0727         }
0728     }
0729 
0730     if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents) {
0731         fragment->appendChild(rightContents.get(), exceptioncode);
0732     }
0733 
0734     // collapse to the proper position - see spec section 2.6
0735     if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
0736         if (!partialStart && !partialEnd) {
0737             collapse(true, exceptioncode);
0738         } else if (partialStart) {
0739             setStartContainer(partialStart->parentNode());
0740             setEndContainer(partialStart->parentNode());
0741             m_startOffset = m_endOffset = partialStart->nodeIndex() + 1;
0742         } else if (partialEnd) {
0743             setStartContainer(partialEnd->parentNode());
0744             setEndContainer(partialEnd->parentNode());
0745             m_startOffset = m_endOffset = partialEnd->nodeIndex();
0746         }
0747     }
0748     return fragment;
0749 }
0750 
0751 DocumentFragmentImpl *RangeImpl::extractContents(int &exceptioncode)
0752 {
0753     if (m_detached) {
0754         exceptioncode = DOMException::INVALID_STATE_ERR;
0755         return nullptr;
0756     }
0757 
0758     checkDeleteExtract(exceptioncode);
0759     if (exceptioncode) {
0760         return nullptr;
0761     }
0762 
0763     return processContents(EXTRACT_CONTENTS, exceptioncode);
0764 }
0765 
0766 DocumentFragmentImpl *RangeImpl::cloneContents(int &exceptioncode)
0767 {
0768     if (m_detached) {
0769         exceptioncode = DOMException::INVALID_STATE_ERR;
0770         return nullptr;
0771     }
0772 
0773     return processContents(CLONE_CONTENTS, exceptioncode);
0774 }
0775 
0776 void RangeImpl::insertNode(NodeImpl *newNode, int &exceptioncode)
0777 {
0778     if (m_detached) {
0779         exceptioncode = DOMException::INVALID_STATE_ERR;
0780         return;
0781     }
0782 
0783     if (!newNode) {
0784         exceptioncode = DOMException::NOT_FOUND_ERR;
0785         return;
0786     }
0787 
0788     // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of
0789     // the Range is read-only.
0790     NodeImpl *n = m_startContainer;
0791     while (n && !n->isReadOnly()) {
0792         n = n->parentNode();
0793     }
0794     if (n) {
0795         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
0796         return;
0797     }
0798 
0799     n = m_endContainer;
0800     while (n && !n->isReadOnly()) {
0801         n = n->parentNode();
0802     }
0803     if (n) {
0804         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
0805         return;
0806     }
0807 
0808     // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were
0809     // not created from the same document.
0810     if (newNode->document() != m_startContainer->document()) {
0811         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
0812         return;
0813     }
0814 
0815     // HIERARCHY_REQUEST_ERR: Raised if the container of the start of the Range is of a type that
0816     // does not allow children of the type of newNode or if newNode is an ancestor of the container.
0817 
0818     // an extra one here - if a text node is going to split, it must have a parent to insert into
0819     if (m_startContainer->nodeType() == Node::TEXT_NODE && !m_startContainer->parentNode()) {
0820         exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
0821         return;
0822     }
0823 
0824     // In the case where the container is a text node, we check against the container's parent, because
0825     // text nodes get split up upon insertion.
0826     NodeImpl *checkAgainst;
0827     if (m_startContainer->nodeType() == Node::TEXT_NODE) {
0828         checkAgainst = m_startContainer->parentNode();
0829     } else {
0830         checkAgainst = m_startContainer;
0831     }
0832 
0833     if (newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
0834         // check each child node, not the DocumentFragment itself
0835         NodeImpl *c;
0836         for (c = newNode->firstChild(); c; c = c->nextSibling()) {
0837             if (!checkAgainst->childTypeAllowed(c->nodeType())) {
0838                 exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
0839                 return;
0840             }
0841         }
0842     } else {
0843         if (!checkAgainst->childTypeAllowed(newNode->nodeType())) {
0844             exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
0845             return;
0846         }
0847     }
0848 
0849     for (n = m_startContainer; n; n = n->parentNode()) {
0850         if (n == newNode) {
0851             exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
0852             return;
0853         }
0854     }
0855 
0856     // INVALID_NODE_TYPE_ERR: Raised if newNode is an Attr, Entity, Notation, or Document node.
0857     if (newNode->nodeType() == Node::ATTRIBUTE_NODE ||
0858             newNode->nodeType() == Node::ENTITY_NODE ||
0859             newNode->nodeType() == Node::NOTATION_NODE ||
0860             newNode->nodeType() == Node::DOCUMENT_NODE) {
0861         exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET;
0862         return;
0863     }
0864 
0865     long endOffsetDelta = 0;
0866     if (m_startContainer->nodeType() == Node::TEXT_NODE ||
0867             m_startContainer->nodeType() == Node::CDATA_SECTION_NODE) {
0868         // ### leaks on exceptions
0869         TextImpl *newText = static_cast<TextImpl *>(m_startContainer)->splitText(m_startOffset, exceptioncode);
0870         if (exceptioncode) {
0871             return;
0872         }
0873 
0874         if (m_startContainer == m_endContainer) {
0875             endOffsetDelta = -(long)m_startOffset;
0876             setEndContainer(newText);
0877             // ### what about border cases where start or end are at
0878             // ### the margins of the text?
0879         }
0880 
0881         m_startContainer->parentNode()->insertBefore(newNode, newText, exceptioncode);
0882         if (exceptioncode) {
0883             return;
0884         }
0885     } else {
0886         if (m_startContainer == m_endContainer) {
0887             bool isFragment = newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE;
0888             endOffsetDelta = isFragment ? newNode->childNodeCount() : 1;
0889         }
0890 
0891         m_startContainer->insertBefore(newNode, m_startContainer->childNode(m_startOffset), exceptioncode);
0892         if (exceptioncode) {
0893             return;
0894         }
0895     }
0896     m_endOffset += endOffsetDelta;
0897 }
0898 
0899 DOMString RangeImpl::toString(int &exceptioncode)
0900 {
0901     if (m_detached) {
0902         exceptioncode = DOMException::INVALID_STATE_ERR;
0903         return DOMString();
0904     }
0905 
0906     DOMString text = "";
0907     NodeImpl *n = m_startContainer;
0908 
0909     /* This function converts a dom range to the plain text string that the user would see in this
0910      * portion of rendered html.
0911      *
0912      * There are several ways ranges can be used.
0913      *
0914      * The simplest is the start and endContainer is a text node.  The start and end offset is the
0915      * number of characters into the text to remove/truncate.
0916      *
0917      * The next case is the start and endContainer is, well, a container, such a P tag or DIV tag.
0918      * In this case the start and end offset is the number of children into the container to start
0919      * from and end at.
0920      *
0921      * The other cases are different arrangements of the first two.
0922      *
0923      * pseudo code:
0924      *
0925      * if start container is not text:
0926      *     count through the children to find where we start (m_startOffset children)
0927      *
0928      * loop from the start position:
0929      *     if the current node is text, add the text to our variable 'text', truncating/removing if at the end/start.
0930      *
0931      *     if the node has children, step to the first child.
0932      *     if the node has no children but does have siblings, step to the next sibling
0933      *     until we find a sibling, go to next the parent but:
0934      *         make sure this sibling isn't past the end of where we are supposed to go. (position > endOffset and the parent is the endContainer)
0935      *
0936      */
0937 
0938     if (m_startContainer == m_endContainer && m_startOffset >= m_endOffset) {
0939         return text;
0940     }
0941 
0942     if (n->firstChild()) {
0943         n = n->firstChild();
0944         int current_offset = m_startOffset;
0945         while (current_offset-- && n) {
0946             n = n->nextSibling();
0947         }
0948     }
0949 
0950     while (n) {
0951         if (n == m_endContainer && m_endOffset == 0) {
0952             break;
0953         }
0954         if (n->nodeType() == DOM::Node::TEXT_NODE ||
0955                 n->nodeType() == DOM::Node::CDATA_SECTION_NODE) {
0956 
0957             DOMString str = static_cast<TextImpl *>(n)->string();
0958             if (n == m_endContainer || n == m_startContainer) {
0959                 str = str.copy();    //copy if we are going to modify.
0960             }
0961 
0962             if (n == m_endContainer) {
0963                 str.truncate(m_endOffset);
0964             }
0965             if (n == m_startContainer) {
0966                 str.remove(0, m_startOffset);
0967             }
0968             text += str;
0969             if (n == m_endContainer) {
0970                 break;
0971             }
0972         }
0973 
0974         NodeImpl *next = n->firstChild();
0975         if (!next) {
0976             next = n->nextSibling();
0977         }
0978 
0979         while (!next && n->parentNode()) {
0980             if (n == m_endContainer) {
0981                 return text;
0982             }
0983             n = n->parentNode();
0984             if (n == m_endContainer) {
0985                 return text;
0986             }
0987             next = n->nextSibling();
0988         }
0989 
0990         if (n->parentNode() == m_endContainer) {
0991             if (!next) {
0992                 break;
0993             }
0994             unsigned long current_offset = 0;
0995             NodeImpl *it = n;
0996             while ((it = it->previousSibling())) {
0997                 ++current_offset;
0998             }
0999             if (current_offset >= m_endOffset) {
1000                 break;
1001             }
1002         }
1003 
1004         n = next;
1005     }
1006     return text;
1007 }
1008 
1009 DOMString RangeImpl::toHTML(int &exceptioncode)
1010 {
1011     bool hasHtmlTag = false;
1012     bool hasBodyTag = false;
1013     //FIXME:  What is this section of code below exactly?  Do I want it here?
1014     if (m_detached) {
1015         exceptioncode = DOMException::INVALID_STATE_ERR;
1016         return DOMString();
1017     }
1018     DOMString text = "";
1019     NodeImpl *n = m_startContainer;
1020     int num_tables = 0;
1021     bool in_li = false; //whether we have an li in the text, without an ol/ul
1022     int depth_difference = 0;
1023     int lowest_depth_difference = 0;
1024 
1025     if (m_startContainer == m_endContainer && m_startOffset >= m_endOffset) {
1026         return text;
1027     }
1028 
1029     while (n) {
1030         /* First, we could have an tag <tagname key=value>otherstuff</tagname> */
1031         if (n->nodeType() == DOM::Node::ELEMENT_NODE) {
1032             int elementId = static_cast<ElementImpl *>(n)->id();
1033             if (elementId == ID_TABLE) {
1034                 num_tables++;
1035             }
1036             if (elementId == ID_BODY) {
1037                 hasBodyTag = true;
1038             }
1039             if (elementId == ID_HTML) {
1040                 hasHtmlTag = true;
1041             }
1042             if (elementId == ID_LI) {
1043                 in_li = true;
1044             }
1045             if (num_tables == 0 && (elementId == ID_TD || elementId == ID_TR || elementId == ID_TH || elementId == ID_TBODY || elementId == ID_TFOOT || elementId == ID_THEAD)) {
1046                 num_tables++;
1047             }
1048             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.
1049                 text += static_cast<ElementImpl *>(n)->openTagStartToString(true /*safely expand img urls*/); // adds "<tagname key=value"
1050                 if (n->hasChildNodes()) {
1051                     depth_difference++;
1052                     text += ">";
1053                 } else {
1054                     text += "/>";
1055                 }
1056             }
1057         } else if (n->nodeType() == DOM::Node::TEXT_NODE ||
1058                    n->nodeType() == DOM::Node::CDATA_SECTION_NODE) {
1059             if (n->nodeType() == DOM::Node::CDATA_SECTION_NODE) {
1060                 text += "<![CDATA[ ";
1061             }
1062             long long startOffset = (n == m_startContainer) ? (long long)m_startOffset : -1;
1063             long long endOffset = (n == m_endContainer) ? (long long) m_endOffset : -1;
1064             text += static_cast<TextImpl *>(n)->toString(startOffset, endOffset); //Note this should always work since CDataImpl inherits TextImpl
1065             if (n->nodeType() == DOM::Node::CDATA_SECTION_NODE) {
1066                 text += " ]]>";
1067             }
1068             if (n == m_endContainer) {
1069                 break;
1070             }
1071         }
1072         if (n->parentNode() == m_endContainer && !n->nextSibling()) {
1073             break;
1074         }
1075 
1076         //if (n == m_endContainer) break;
1077         NodeImpl *next = n->firstChild();
1078         if (next) {
1079             if (n == m_startContainer) {
1080                 //This is the start of our selection, so we have to move to where we have started selecting.
1081                 //For example, if 'n' is "hello <img src='hello.png'> how are you? <img src='goodbye.png'>"
1082                 //then this has four children.  If our selection started on the image, then we need to start from there.
1083                 unsigned long current_offset = 0;
1084                 while (current_offset < m_startOffset && next) {
1085                     next = next->nextSibling();
1086                     ++current_offset;
1087                 }
1088             }
1089         } else {
1090             next = n->nextSibling();
1091 
1092             if (n->parentNode() == m_endContainer) {
1093                 unsigned long current_offset = 1;
1094                 NodeImpl *it = n;
1095                 while ((it = it->previousSibling())) {
1096                     ++current_offset;
1097                 }
1098 
1099                 if (current_offset >= m_endOffset) {
1100                     break;
1101                 }
1102             }
1103         }
1104 
1105         while (!next && n->parentNode()) {
1106             n = n->parentNode();
1107             if (n->nodeType() == DOM::Node::ELEMENT_NODE) {
1108                 text += "</";
1109                 text += static_cast<ElementImpl *>(n)->nonCaseFoldedTagName();
1110                 int elementId = static_cast<ElementImpl *>(n)->id();
1111                 if (elementId == ID_TABLE) {
1112                     num_tables--;
1113                 }
1114                 depth_difference--;
1115                 if (lowest_depth_difference > depth_difference) {
1116                     lowest_depth_difference = depth_difference;
1117                 }
1118                 if (num_tables == 0 && (elementId == ID_TD || elementId == ID_TR || elementId == ID_TH || elementId == ID_TBODY || elementId == ID_TFOOT || elementId == ID_THEAD)) {
1119                     num_tables--;
1120                 }
1121                 if (elementId == ID_OL || elementId == ID_UL) {
1122                     in_li = false;
1123                 }
1124                 text += ">";
1125             }
1126             next = n->nextSibling();
1127         }
1128         n = next;
1129     }
1130 
1131     //We have the html in the selection.  But now we need to properly add the opening and closing tags.
1132     //For example say we have:   "Hello <b>Mr. John</b> How are you?"  and we select "John" or even
1133     //"John</b> How"  and copy.  We want to return "<b>John</b>" and "<b>John</b> How" respectively
1134 
1135     //To do this, we need to go up the tree from the start, and prepend those tags.
1136     //Imagine our selection was this:
1137     //
1138     //  hello</b></p><p>there
1139     //
1140     //  The difference in depths between the start and end is -1, and the lowest depth
1141     //  difference from the starting point is -2
1142     //
1143     //  So from the start of the selection, we want to go down to the lowest_depth_difference
1144     //  and prepend those tags.  (<p><b>)
1145     //
1146     //  From the end of the selection, we want to also go down to the lowest_depth_difference.
1147     //  We know the depth of the end of the selection - i.e. depth_difference.
1148     //
1149     //
1150     n = m_startContainer;
1151     int startdepth = 0; //by definition - we are counting from zero.
1152     while ((n = n->parentNode()) && startdepth > lowest_depth_difference) {
1153         if (n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right?
1154             switch (static_cast<ElementImpl *>(n)->id()) {
1155             case ID_TABLE:
1156                 num_tables--;
1157                 break;
1158             case ID_BODY:
1159                 hasBodyTag = true;
1160                 break;
1161             case ID_HTML:
1162                 hasHtmlTag = true;
1163                 break;
1164             case ID_LI:
1165                 in_li = true;
1166                 break;
1167             }
1168             text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/) + DOMString(">") + text; // prepends "<tagname key=value>"
1169         }
1170         startdepth--;
1171     }
1172     n = m_endContainer;
1173     while (depth_difference > lowest_depth_difference && (n = n->parentNode())) {
1174         if (n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right?
1175             switch (static_cast<ElementImpl *>(n)->id()) {
1176             case ID_TABLE:
1177                 num_tables++;
1178                 break;
1179             case ID_OL:
1180             case ID_UL:
1181                 in_li = false;
1182                 break;
1183             }
1184             text += "</";
1185             text += static_cast<ElementImpl *>(n)->nonCaseFoldedTagName();
1186             text += ">";
1187         }
1188         depth_difference--;
1189     }
1190 
1191     // Now our text string is the same depth on both sides, with nothing lower (in other words all the
1192     // tags in it match up.)  This also means that the end value for n in the first loop is a sibling of the
1193     // end value for n in the second loop.
1194     //
1195     // We now need to go down the tree, and for certain tags, add them in on both ends of the text.
1196     // For example, if have:  "<b>hello</b>"  and we select "ll", then we want to go down the tree and
1197     // add "<b>" and "</b>" to it, to produce "<b>ll</b>".
1198     //
1199     // I just guessed at which tags you'd want to keep (bold, italic etc) and which you wouldn't (tables etc).
1200     // It's just wild guessing.  feel free to change.
1201     //
1202     // Note we can carry on with the value of n
1203     if (n) {
1204         while ((n = n->parentNode())) {
1205             if (n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right?
1206                 int elementId = static_cast<ElementImpl *>(n)->id();
1207                 switch (elementId) {
1208                 case ID_TABLE:
1209                 case ID_TD:
1210                 case ID_TR:
1211                 case ID_TH:
1212                 case ID_TBODY:
1213                 case ID_TFOOT:
1214                 case ID_THEAD:
1215                     if (num_tables > 0) {
1216                         if (elementId == ID_TABLE) {
1217                             num_tables--;
1218                         }
1219                         text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/) + DOMString(">") + text;
1220                         text += "</";
1221                         text += static_cast<ElementImpl *>(n)->nonCaseFoldedTagName();
1222                         text += ">";
1223 
1224                     }
1225                     break;
1226 
1227                 case ID_LI:
1228                     if (!in_li) {
1229                         break;
1230                     }
1231                     text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/) + DOMString(">") + text;
1232                     text += "</";
1233                     text += static_cast<ElementImpl *>(n)->nonCaseFoldedTagName();
1234                     text += ">";
1235                     break;
1236 
1237                 case ID_UL:
1238                 case ID_OL:
1239                     if (!in_li) {
1240                         break;
1241                     }
1242                     in_li = false;
1243                 case ID_B:
1244                 case ID_I:
1245                 case ID_U:
1246                 case ID_FONT:
1247                 case ID_S:
1248                 case ID_STRONG:
1249                 case ID_STRIKE:
1250                 case ID_DEL:
1251                 case ID_A:
1252                 case ID_H1:
1253                 case ID_H2:
1254                 case ID_H3:
1255                 case ID_H4:
1256                 case ID_H5:
1257                     //should small, etc be here?   so hard to decide.  this is such a hack :(
1258                     //There's probably tons of others you'd want here.
1259                     text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/) + DOMString(">") + text;
1260                     text += "</";
1261                     text += static_cast<ElementImpl *>(n)->nonCaseFoldedTagName();
1262                     text += ">";
1263                     break;
1264                 }
1265             }
1266         }
1267     }
1268 
1269     if (!hasBodyTag) {
1270         text = DOMString("<body>") + text + DOMString("</body>");
1271     } else if (!hasHtmlTag) {
1272         text = DOMString("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1273                          "<head>\n"
1274                          "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
1275                          "<meta name=\"Generator\" content=\"KHTML, the KDE Web Page Viewer\" />\n"
1276                          "</head>\n") +
1277                text +
1278                DOMString("</html>");
1279     }
1280     text = DOMString("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n") + text;
1281 
1282     return text;
1283 
1284 }
1285 
1286 DocumentFragment RangeImpl::createContextualFragment(const DOMString &html, int &exceptioncode)
1287 {
1288     if (m_detached) {
1289         exceptioncode = DOMException::INVALID_STATE_ERR;
1290         return DocumentFragment();
1291     }
1292 
1293     DOM::NodeImpl *start = m_startContainer;
1294 
1295     if (start->isDocumentNode()) {
1296         start = static_cast<DocumentImpl *>(start)->documentElement();
1297     }
1298 
1299     if (!start || !start->isHTMLElement()) {
1300         exceptioncode = DOMException::NOT_SUPPORTED_ERR;
1301         return DocumentFragment();
1302     }
1303 
1304     HTMLElementImpl *e = static_cast<HTMLElementImpl *>(start);
1305     DocumentFragment fragment = e->createContextualFragment(html);
1306     if (fragment.isNull()) {
1307         exceptioncode = DOMException::NOT_SUPPORTED_ERR;
1308         return DocumentFragment();
1309     }
1310 
1311     return fragment;
1312 }
1313 
1314 void RangeImpl::detach(int &exceptioncode)
1315 {
1316     if (m_detached) {
1317         exceptioncode = DOMException::INVALID_STATE_ERR;
1318         return;
1319     }
1320 
1321     if (m_startContainer) {
1322         m_startContainer->deref();
1323     }
1324     m_startContainer = nullptr;
1325     if (m_endContainer) {
1326         m_endContainer->deref();
1327     }
1328     m_endContainer = nullptr;
1329     m_detached = true;
1330 }
1331 
1332 bool RangeImpl::isDetached() const
1333 {
1334     return m_detached;
1335 }
1336 
1337 void RangeImpl::checkNodeWOffset(NodeImpl *n, int offset, int &exceptioncode) const
1338 {
1339     if (offset < 0) {
1340         exceptioncode = DOMException::INDEX_SIZE_ERR;
1341     }
1342 
1343     switch (n->nodeType()) {
1344     case Node::ENTITY_NODE:
1345     case Node::NOTATION_NODE:
1346     case Node::DOCUMENT_TYPE_NODE:
1347         exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET;
1348         break;
1349     case Node::TEXT_NODE:
1350     case Node::COMMENT_NODE:
1351     case Node::CDATA_SECTION_NODE:
1352         if ((unsigned long)offset > static_cast<CharacterDataImpl *>(n)->length()) {
1353             exceptioncode = DOMException::INDEX_SIZE_ERR;
1354         }
1355         break;
1356     case Node::PROCESSING_INSTRUCTION_NODE:
1357         // ### are we supposed to check with just data or the whole contents?
1358         if ((unsigned long)offset > static_cast<ProcessingInstructionImpl *>(n)->data().length()) {
1359             exceptioncode = DOMException::INDEX_SIZE_ERR;
1360         }
1361         break;
1362     default:
1363         if ((unsigned long)offset > n->childNodeCount()) {
1364             exceptioncode = DOMException::INDEX_SIZE_ERR;
1365         }
1366         break;
1367     }
1368 }
1369 
1370 void RangeImpl::checkNodeBA(NodeImpl *n, int &exceptioncode) const
1371 {
1372     // INVALID_NODE_TYPE_ERR: Raised if the root container of refNode is not an
1373     // Attr, Document or DocumentFragment node or if refNode is a Document,
1374     // DocumentFragment, Attr, Entity, or Notation node.
1375     NodeImpl *root = n;
1376     while (root->parentNode()) {
1377         root = root->parentNode();
1378     }
1379     if (!(root->nodeType() == Node::ATTRIBUTE_NODE ||
1380             root->nodeType() == Node::DOCUMENT_NODE ||
1381             root->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)) {
1382         exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET;
1383         return;
1384     }
1385 
1386     if (n->nodeType() == Node::DOCUMENT_NODE ||
1387             n->nodeType() == Node::DOCUMENT_FRAGMENT_NODE ||
1388             n->nodeType() == Node::ATTRIBUTE_NODE ||
1389             n->nodeType() == Node::ENTITY_NODE ||
1390             n->nodeType() == Node::NOTATION_NODE) {
1391         exceptioncode = RangeException::INVALID_NODE_TYPE_ERR  + RangeException::_EXCEPTION_OFFSET;
1392     }
1393 
1394 }
1395 
1396 RangeImpl *RangeImpl::cloneRange(int &exceptioncode)
1397 {
1398     if (m_detached) {
1399         exceptioncode = DOMException::INVALID_STATE_ERR;
1400         return nullptr;
1401     }
1402 
1403     return new RangeImpl(m_ownerDocument, m_startContainer, m_startOffset, m_endContainer, m_endOffset);
1404 }
1405 
1406 void RangeImpl::setStartAfter(NodeImpl *refNode, int &exceptioncode)
1407 {
1408     if (m_detached) {
1409         exceptioncode = DOMException::INVALID_STATE_ERR;
1410         return;
1411     }
1412 
1413     if (!refNode) {
1414         exceptioncode = DOMException::NOT_FOUND_ERR;
1415         return;
1416     }
1417 
1418     if (refNode->document() != m_ownerDocument) {
1419         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
1420         return;
1421     }
1422 
1423     checkNodeBA(refNode, exceptioncode);
1424     if (exceptioncode) {
1425         return;
1426     }
1427 
1428     setStart(refNode->parentNode(), refNode->nodeIndex() + 1, exceptioncode);
1429 }
1430 
1431 void RangeImpl::setEndBefore(NodeImpl *refNode, int &exceptioncode)
1432 {
1433     if (m_detached) {
1434         exceptioncode = DOMException::INVALID_STATE_ERR;
1435         return;
1436     }
1437 
1438     if (!refNode) {
1439         exceptioncode = DOMException::NOT_FOUND_ERR;
1440         return;
1441     }
1442 
1443     if (refNode->document() != m_ownerDocument) {
1444         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
1445         return;
1446     }
1447 
1448     checkNodeBA(refNode, exceptioncode);
1449     if (exceptioncode) {
1450         return;
1451     }
1452 
1453     setEnd(refNode->parentNode(), refNode->nodeIndex(), exceptioncode);
1454 }
1455 
1456 void RangeImpl::setEndAfter(NodeImpl *refNode, int &exceptioncode)
1457 {
1458     if (m_detached) {
1459         exceptioncode = DOMException::INVALID_STATE_ERR;
1460         return;
1461     }
1462 
1463     if (!refNode) {
1464         exceptioncode = DOMException::NOT_FOUND_ERR;
1465         return;
1466     }
1467 
1468     if (refNode->document() != m_ownerDocument) {
1469         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
1470         return;
1471     }
1472 
1473     checkNodeBA(refNode, exceptioncode);
1474     if (exceptioncode) {
1475         return;
1476     }
1477 
1478     setEnd(refNode->parentNode(), refNode->nodeIndex() + 1, exceptioncode);
1479 
1480 }
1481 
1482 void RangeImpl::selectNode(NodeImpl *refNode, int &exceptioncode)
1483 {
1484     if (m_detached) {
1485         exceptioncode = DOMException::INVALID_STATE_ERR;
1486         return;
1487     }
1488 
1489     if (!refNode) {
1490         exceptioncode = DOMException::NOT_FOUND_ERR;
1491         return;
1492     }
1493 
1494     // INVALID_NODE_TYPE_ERR: Raised if an ancestor of refNode is an Entity, Notation or
1495     // DocumentType node or if refNode is a Document, DocumentFragment, Attr, Entity, or Notation
1496     // node.
1497     NodeImpl *anc;
1498     for (anc = refNode->parentNode(); anc; anc = anc->parentNode()) {
1499         if (anc->nodeType() == Node::ENTITY_NODE ||
1500                 anc->nodeType() == Node::NOTATION_NODE ||
1501                 anc->nodeType() == Node::DOCUMENT_TYPE_NODE) {
1502 
1503             exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET;
1504             return;
1505         }
1506     }
1507 
1508     if (refNode->nodeType() == Node::DOCUMENT_NODE ||
1509             refNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE ||
1510             refNode->nodeType() == Node::ATTRIBUTE_NODE ||
1511             refNode->nodeType() == Node::ENTITY_NODE ||
1512             refNode->nodeType() == Node::NOTATION_NODE) {
1513 
1514         exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET;
1515         return;
1516     }
1517 
1518     setStartBefore(refNode, exceptioncode);
1519     if (exceptioncode) {
1520         return;
1521     }
1522     setEndAfter(refNode, exceptioncode);
1523 }
1524 
1525 void RangeImpl::selectNodeContents(NodeImpl *refNode, int &exceptioncode)
1526 {
1527     if (m_detached) {
1528         exceptioncode = DOMException::INVALID_STATE_ERR;
1529         return;
1530     }
1531 
1532     if (!refNode) {
1533         exceptioncode = DOMException::NOT_FOUND_ERR;
1534         return;
1535     }
1536 
1537     // INVALID_NODE_TYPE_ERR: Raised if refNode or an ancestor of refNode is an Entity, Notation
1538     // or DocumentType node.
1539     NodeImpl *n;
1540     for (n = refNode; n; n = n->parentNode()) {
1541         if (n->nodeType() == Node::ENTITY_NODE ||
1542                 n->nodeType() == Node::NOTATION_NODE ||
1543                 n->nodeType() == Node::DOCUMENT_TYPE_NODE) {
1544 
1545             exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET;
1546             return;
1547         }
1548     }
1549 
1550     setStartContainer(refNode);
1551     m_startOffset = 0;
1552     setEndContainer(refNode);
1553     m_endOffset = maxEndOffset();
1554 }
1555 
1556 void RangeImpl::surroundContents(NodeImpl *newParent, int &exceptioncode)
1557 {
1558     if (m_detached) {
1559         exceptioncode = DOMException::INVALID_STATE_ERR;
1560         return;
1561     }
1562 
1563     if (!newParent) {
1564         exceptioncode = DOMException::NOT_FOUND_ERR;
1565         return;
1566     }
1567 
1568     // INVALID_NODE_TYPE_ERR: Raised if node is an Attr, Entity, DocumentType, Notation,
1569     // Document, or DocumentFragment node.
1570     if (newParent->nodeType() == Node::ATTRIBUTE_NODE ||
1571             newParent->nodeType() == Node::ENTITY_NODE ||
1572             newParent->nodeType() == Node::NOTATION_NODE ||
1573             newParent->nodeType() == Node::DOCUMENT_TYPE_NODE ||
1574             newParent->nodeType() == Node::DOCUMENT_NODE ||
1575             newParent->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
1576         exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET;
1577         return;
1578     }
1579 
1580     // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of
1581     // the Range is read-only.
1582     if (readOnly()) {
1583         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
1584         return;
1585     }
1586 
1587     NodeImpl *n = m_startContainer;
1588     while (n && !n->isReadOnly()) {
1589         n = n->parentNode();
1590     }
1591     if (n) {
1592         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
1593         return;
1594     }
1595 
1596     n = m_endContainer;
1597     while (n && !n->isReadOnly()) {
1598         n = n->parentNode();
1599     }
1600     if (n) {
1601         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
1602         return;
1603     }
1604 
1605     // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were
1606     // not created from the same document.
1607     if (newParent->document() != m_startContainer->document()) {
1608         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
1609         return;
1610     }
1611 
1612     // HIERARCHY_REQUEST_ERR: Raised if the container of the start of the Range is of a type that
1613     // does not allow children of the type of newParent or if newParent is an ancestor of the container
1614     // or if node would end up with a child node of a type not allowed by the type of node.
1615 
1616     // If m_startContainer is a character data node, it will be split and it will be its parent that will
1617     // need to accept newParent (or in the case of a comment, it logically "would" be inserted into the parent,
1618     // although this will fail below for another reason).
1619 
1620     NodeImpl *parentOfNewParent = m_startContainer;
1621 
1622     if (parentOfNewParent->offsetInCharacters()) {
1623         parentOfNewParent = parentOfNewParent->parentNode();
1624     }
1625 
1626     if (!parentOfNewParent->childTypeAllowed(newParent->nodeType())) {
1627         exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
1628         return;
1629     }
1630 
1631     for (n = m_startContainer; n; n = n->parentNode()) {
1632         if (n == newParent) {
1633             exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
1634             return;
1635         }
1636     }
1637 
1638     // ### check if node would end up with a child node of a type not allowed by the type of node
1639 
1640     // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-text node.
1641     if (m_startContainer->nodeType() != Node::TEXT_NODE &&
1642             m_startContainer->nodeType() != Node::CDATA_SECTION_NODE) {
1643 
1644         if (m_startOffset > 0 && m_startOffset < maxStartOffset()) {
1645             exceptioncode = RangeException::BAD_BOUNDARYPOINTS_ERR + RangeException::_EXCEPTION_OFFSET;
1646             return;
1647         }
1648     }
1649 
1650     if (m_endContainer->nodeType() != Node::TEXT_NODE &&
1651             m_endContainer->nodeType() != Node::CDATA_SECTION_NODE) {
1652 
1653         if (m_endOffset > 0 && m_endOffset < maxEndOffset()) {
1654             exceptioncode = RangeException::BAD_BOUNDARYPOINTS_ERR + RangeException::_EXCEPTION_OFFSET;
1655             return;
1656         }
1657     }
1658 
1659     while (newParent->firstChild()) {
1660         newParent->removeChild(newParent->firstChild(), exceptioncode);
1661         if (exceptioncode) {
1662             return;
1663         }
1664     }
1665     DocumentFragmentImpl *fragment = extractContents(exceptioncode);
1666     if (exceptioncode) {
1667         return;
1668     }
1669     insertNode(newParent, exceptioncode);
1670     if (exceptioncode) {
1671         return;
1672     }
1673     newParent->appendChild(fragment, exceptioncode);
1674     if (exceptioncode) {
1675         return;
1676     }
1677     selectNode(newParent, exceptioncode);
1678 }
1679 
1680 unsigned long RangeImpl::maxStartOffset() const
1681 {
1682     if (!m_startContainer) {
1683         return 0;
1684     }
1685     if (!m_startContainer->offsetInCharacters()) {
1686         return m_startContainer->childNodeCount();
1687     }
1688     return m_startContainer->maxCharacterOffset();
1689 }
1690 
1691 unsigned long RangeImpl::maxEndOffset() const
1692 {
1693     if (!m_endContainer) {
1694         return 0;
1695     }
1696     if (!m_endContainer->offsetInCharacters()) {
1697         return m_endContainer->childNodeCount();
1698     }
1699     return m_endContainer->maxCharacterOffset();
1700 }
1701 
1702 void RangeImpl::setStartBefore(NodeImpl *refNode, int &exceptioncode)
1703 {
1704     if (m_detached) {
1705         exceptioncode = DOMException::INVALID_STATE_ERR;
1706         return;
1707     }
1708 
1709     if (!refNode) {
1710         exceptioncode = DOMException::NOT_FOUND_ERR;
1711         return;
1712     }
1713 
1714     if (refNode->document() != m_ownerDocument) {
1715         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
1716         return;
1717     }
1718 
1719     checkNodeBA(refNode, exceptioncode);
1720     if (exceptioncode) {
1721         return;
1722     }
1723 
1724     setStart(refNode->parentNode(), refNode->nodeIndex(), exceptioncode);
1725 }
1726 
1727 void RangeImpl::setStartContainer(NodeImpl *_startContainer)
1728 {
1729     if (m_startContainer == _startContainer) {
1730         return;
1731     }
1732 
1733     if (m_startContainer) {
1734         m_startContainer->deref();
1735     }
1736     m_startContainer = _startContainer;
1737     if (m_startContainer) {
1738         m_startContainer->ref();
1739     }
1740 }
1741 
1742 void RangeImpl::setEndContainer(NodeImpl *_endContainer)
1743 {
1744     if (m_endContainer == _endContainer) {
1745         return;
1746     }
1747 
1748     if (m_endContainer) {
1749         m_endContainer->deref();
1750     }
1751     m_endContainer = _endContainer;
1752     if (m_endContainer) {
1753         m_endContainer->ref();
1754     }
1755 }
1756 
1757 void RangeImpl::checkDeleteExtract(int &exceptioncode)
1758 {
1759 
1760     NodeImpl *start;
1761     if (m_startContainer->nodeType() != Node::TEXT_NODE &&
1762             m_startContainer->nodeType() != Node::CDATA_SECTION_NODE &&
1763             m_startContainer->nodeType() != Node::COMMENT_NODE &&
1764             m_startContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) {
1765 
1766         start = m_startContainer->childNode(m_startOffset);
1767         if (!start) {
1768             if (m_startContainer->lastChild()) {
1769                 start = m_startContainer->lastChild()->traverseNextNode();
1770             } else {
1771                 start = m_startContainer->traverseNextNode();
1772             }
1773         }
1774     } else {
1775         start = m_startContainer;
1776     }
1777 
1778     NodeImpl *end;
1779     if (m_endContainer->nodeType() != Node::TEXT_NODE &&
1780             m_endContainer->nodeType() != Node::CDATA_SECTION_NODE &&
1781             m_endContainer->nodeType() != Node::COMMENT_NODE &&
1782             m_endContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) {
1783 
1784         end = m_endContainer->childNode(m_endOffset);
1785         if (!end) {
1786             if (m_endContainer->lastChild()) {
1787                 end = m_endContainer->lastChild()->traverseNextNode();
1788             } else {
1789                 end = m_endContainer->traverseNextNode();
1790             }
1791         }
1792     } else {
1793         end = m_endContainer;
1794     }
1795 
1796     NodeImpl *n;
1797     for (n = start; n && n != end; n = n->traverseNextNode()) {
1798         if (n->isReadOnly()) {
1799             exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
1800             return;
1801         }
1802         if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { // ### is this for only directly under the DF, or anywhere?
1803             exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
1804             return;
1805         }
1806     }
1807 
1808     if (containedByReadOnly()) {
1809         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
1810         return;
1811     }
1812 }
1813 
1814 bool RangeImpl::containedByReadOnly()
1815 {
1816     NodeImpl *n;
1817     for (n = m_startContainer; n; n = n->parentNode()) {
1818         if (n->isReadOnly()) {
1819             return true;
1820         }
1821     }
1822     for (n = m_endContainer; n; n = n->parentNode()) {
1823         if (n->isReadOnly()) {
1824             return true;
1825         }
1826     }
1827     return false;
1828 }
1829