File indexing completed on 2024-05-05 16:11:34
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