#include "config.h"
#include "Range.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "ExceptionCode.h"
#include "HTMLElement.h"
#include "HTMLNames.h"
#include "ProcessingInstruction.h"
#include "RangeException.h"
#include "RenderBlock.h"
#include "Text.h"
#include "TextIterator.h"
#include "markup.h"
#include "visible_units.h"
namespace WebCore {
using namespace std;
using namespace HTMLNames;
#ifndef NDEBUG
class RangeCounter {
public:
static unsigned count;
~RangeCounter()
{
if (count)
fprintf(stderr, "LEAK: %u Range\n", count);
}
};
unsigned RangeCounter::count = 0;
static RangeCounter rangeCounter;
#endif
Range::Range(Document* ownerDocument)
: m_ownerDocument(ownerDocument)
, m_startContainer(ownerDocument)
, m_startOffset(0)
, m_endContainer(ownerDocument)
, m_endOffset(0)
, m_detached(false)
{
#ifndef NDEBUG
++RangeCounter::count;
#endif
}
Range::Range(Document* ownerDocument,
Node* startContainer, int startOffset,
Node* endContainer, int endOffset)
: m_ownerDocument(ownerDocument)
, m_startContainer(ownerDocument)
, m_startOffset(0)
, m_endContainer(ownerDocument)
, m_endOffset(0)
, m_detached(false)
{
#ifndef NDEBUG
++RangeCounter::count;
#endif
ExceptionCode ec = 0;
setStart(startContainer, startOffset, ec);
ASSERT(ec == 0);
setEnd(endContainer, endOffset, ec);
ASSERT(ec == 0);
}
Range::Range(Document* ownerDocument, const Position& start, const Position& end)
: m_ownerDocument(ownerDocument)
, m_startContainer(ownerDocument)
, m_startOffset(0)
, m_endContainer(ownerDocument)
, m_endOffset(0)
, m_detached(false)
{
#ifndef NDEBUG
++RangeCounter::count;
#endif
ExceptionCode ec = 0;
setStart(start.node(), start.offset(), ec);
ASSERT(ec == 0);
setEnd(end.node(), end.offset(), ec);
ASSERT(ec == 0);
}
Range::~Range()
{
#ifndef NDEBUG
--RangeCounter::count;
#endif
}
Node *Range::startContainer(ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_startContainer.get();
}
int Range::startOffset(ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_startOffset;
}
Node *Range::endContainer(ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_endContainer.get();
}
int Range::endOffset(ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_endOffset;
}
Node *Range::commonAncestorContainer(ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
Node *com = commonAncestorContainer(m_startContainer.get(), m_endContainer.get());
if (!com) ec = WRONG_DOCUMENT_ERR;
return com;
}
Node *Range::commonAncestorContainer(Node *containerA, Node *containerB)
{
Node *parentStart;
for (parentStart = containerA; parentStart; parentStart = parentStart->parentNode()) {
Node *parentEnd = containerB;
while (parentEnd && (parentStart != parentEnd))
parentEnd = parentEnd->parentNode();
if (parentStart == parentEnd)
break;
}
return parentStart;
}
bool Range::collapsed(ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
return (m_startContainer == m_endContainer && m_startOffset == m_endOffset);
}
void Range::setStart( Node *refNode, int offset, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return;
}
ec = 0;
checkNodeWOffset(refNode, offset, ec);
if (ec)
return;
m_startContainer = refNode;
m_startOffset = offset;
Node* endRootContainer = m_endContainer.get();
while (endRootContainer->parentNode())
endRootContainer = endRootContainer->parentNode();
Node* startRootContainer = m_startContainer.get();
while (startRootContainer->parentNode())
startRootContainer = startRootContainer->parentNode();
if (startRootContainer != endRootContainer)
collapse(true, ec);
else if (compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) > 0)
collapse(true, ec);
}
void Range::setEnd( Node *refNode, int offset, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return;
}
ec = 0;
checkNodeWOffset(refNode, offset, ec);
if (ec)
return;
m_endContainer = refNode;
m_endOffset = offset;
Node* endRootContainer = m_endContainer.get();
while (endRootContainer->parentNode())
endRootContainer = endRootContainer->parentNode();
Node* startRootContainer = m_startContainer.get();
while (startRootContainer->parentNode())
startRootContainer = startRootContainer->parentNode();
if (startRootContainer != endRootContainer)
collapse(false, ec);
if (compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) > 0)
collapse(false, ec);
}
void Range::collapse( bool toStart, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (toStart) { m_endContainer = m_startContainer;
m_endOffset = m_startOffset;
} else { m_startContainer = m_endContainer;
m_startOffset = m_endOffset;
}
}
bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec)
{
if (!refNode) {
ec = NOT_FOUND_ERR;
return false;
}
if (m_detached && refNode->attached()) {
ec = INVALID_STATE_ERR;
return false;
}
if (!m_detached && !refNode->attached()) {
return false;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return false;
}
ec = 0;
checkNodeWOffset(refNode, offset, ec);
if (ec)
return false;
if ((compareBoundaryPoints(refNode, offset, m_startContainer.get(), m_startOffset) != -1) &&
(compareBoundaryPoints(refNode, offset, m_endContainer.get(), m_endOffset) != 1))
return true;
else
return false;
}
short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec)
{
if (!refNode) {
ec = NOT_FOUND_ERR;
return 0;
}
if (m_detached && refNode->attached()) {
ec = INVALID_STATE_ERR;
return 0;
}
if (!m_detached && !refNode->attached()) {
return -1;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return 0;
}
ec = 0;
checkNodeWOffset(refNode, offset, ec);
if (ec)
return 0;
if (compareBoundaryPoints(refNode, offset, m_startContainer.get(), m_startOffset) == -1)
return -1;
else if (compareBoundaryPoints(refNode, offset, m_endContainer.get(), m_endOffset) == 1)
return 1;
else
return 0;
}
Range::CompareResults Range::compareNode(Node* refNode, ExceptionCode& ec)
{
if (!refNode) {
ec = NOT_FOUND_ERR;
return NODE_BEFORE;
}
if (m_detached && refNode->attached()) {
ec = INVALID_STATE_ERR;
return NODE_BEFORE;
}
if (!m_detached && !refNode->attached()) {
return NODE_BEFORE;
}
if (refNode->document() != m_ownerDocument) {
return NODE_BEFORE;
}
Node* parentNode = refNode->parentNode();
unsigned nodeIndex = refNode->nodeIndex();
if (!parentNode) {
ec = NOT_FOUND_ERR;
return NODE_BEFORE;
}
if (comparePoint(parentNode, nodeIndex, ec) == -1) { if (comparePoint(parentNode, nodeIndex + 1, ec) == 1) return NODE_BEFORE_AND_AFTER;
return NODE_BEFORE; } else { if (comparePoint(parentNode, nodeIndex + 1, ec) == 1) return NODE_AFTER;
return NODE_INSIDE; }
}
short Range::compareBoundaryPoints(CompareHow how, const Range *sourceRange, ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
if (!sourceRange) {
ec = NOT_FOUND_ERR;
return 0;
}
ec = 0;
Node *thisCont = commonAncestorContainer(ec);
if (ec)
return 0;
Node *sourceCont = sourceRange->commonAncestorContainer(ec);
if (ec)
return 0;
if (thisCont->document() != sourceCont->document()) {
ec = WRONG_DOCUMENT_ERR;
return 0;
}
Node *thisTop = thisCont;
Node *sourceTop = sourceCont;
while (thisTop->parentNode())
thisTop = thisTop->parentNode();
while (sourceTop->parentNode())
sourceTop = sourceTop->parentNode();
if (thisTop != sourceTop) { ec = WRONG_DOCUMENT_ERR;
return 0;
}
switch(how)
{
case START_TO_START:
return compareBoundaryPoints( m_startContainer.get(), m_startOffset,
sourceRange->startContainer(ec), sourceRange->startOffset(ec) );
break;
case START_TO_END:
return compareBoundaryPoints( m_startContainer.get(), m_startOffset,
sourceRange->endContainer(ec), sourceRange->endOffset(ec) );
break;
case END_TO_END:
return compareBoundaryPoints( m_endContainer.get(), m_endOffset,
sourceRange->endContainer(ec), sourceRange->endOffset(ec) );
break;
case END_TO_START:
return compareBoundaryPoints( m_endContainer.get(), m_endOffset,
sourceRange->startContainer(ec), sourceRange->startOffset(ec) );
break;
default:
ec = SYNTAX_ERR;
return 0;
}
}
short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *containerB, int offsetB )
{
ASSERT(containerA && containerB);
if (!containerA)
return -1;
if (!containerB)
return 1;
if(containerA == containerB) {
if (offsetA == offsetB)
return 0; if (offsetA < offsetB)
return -1; else
return 1; }
Node *c = containerB;
while (c && c->parentNode() != containerA)
c = c->parentNode();
if (c) {
int offsetC = 0;
Node *n = containerA->firstChild();
while (n != c && offsetC < offsetA) {
offsetC++;
n = n->nextSibling();
}
if (offsetA <= offsetC)
return -1; else
return 1; }
c = containerA;
while (c && c->parentNode() != containerB)
c = c->parentNode();
if (c) {
int offsetC = 0;
Node *n = containerB->firstChild();
while (n != c && offsetC < offsetB) {
offsetC++;
n = n->nextSibling();
}
if(offsetC < offsetB)
return -1; else
return 1; }
Node *cmnRoot = commonAncestorContainer(containerA,containerB);
if (!cmnRoot)
return 0;
Node *childA = containerA;
while (childA && childA->parentNode() != cmnRoot)
childA = childA->parentNode();
if (!childA)
childA = cmnRoot;
Node *childB = containerB;
while (childB && childB->parentNode() != cmnRoot)
childB = childB->parentNode();
if (!childB)
childB = cmnRoot;
if (childA == childB)
return 0;
Node *n = cmnRoot->firstChild();
while (n) {
if (n == childA)
return -1; if (n == childB)
return 1; n = n->nextSibling();
}
ASSERT(0);
return 0;
}
short Range::compareBoundaryPoints( const Position &a, const Position &b )
{
return compareBoundaryPoints(a.node(), a.offset(), b.node(), b.offset());
}
bool Range::boundaryPointsValid() const
{
return m_startContainer && m_endContainer && compareBoundaryPoints(m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset) <= 0;
}
void Range::deleteContents(ExceptionCode& ec) {
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
ec = 0;
checkDeleteExtract(ec);
if (ec)
return;
processContents(DELETE_CONTENTS,ec);
}
bool Range::intersectsNode(Node* refNode, ExceptionCode& ec)
{
if (!refNode) {
ec = NOT_FOUND_ERR;
return false;
}
if (m_detached && refNode->attached() ||
!m_detached && !refNode->attached() ||
refNode->document() != m_ownerDocument)
return false;
Node* parentNode = refNode->parentNode();
unsigned nodeIndex = refNode->nodeIndex();
if (!parentNode) {
ec = NOT_FOUND_ERR;
return false;
}
if (comparePoint(parentNode, nodeIndex, ec) == -1 && comparePoint(parentNode, nodeIndex + 1, ec) == -1) { return false;
} else if(comparePoint(parentNode, nodeIndex, ec) == 1 && comparePoint(parentNode, nodeIndex + 1, ec) == 1) { return false;
}
return true; }
PassRefPtr<DocumentFragment> Range::processContents ( ActionType action, ExceptionCode& ec)
{
RefPtr<DocumentFragment> fragment;
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
fragment = new DocumentFragment(m_ownerDocument.get());
ec = 0;
if (collapsed(ec))
return fragment.release();
if (ec)
return 0;
Node *cmnRoot = commonAncestorContainer(ec);
if (ec)
return 0;
Node *partialStart = 0;
if (m_startContainer != cmnRoot) {
partialStart = m_startContainer.get();
while (partialStart->parentNode() != cmnRoot)
partialStart = partialStart->parentNode();
}
Node *partialEnd = 0;
if (m_endContainer != cmnRoot) {
partialEnd = m_endContainer.get();
while (partialEnd->parentNode() != cmnRoot)
partialEnd = partialEnd->parentNode();
}
if (m_startContainer == m_endContainer) {
if(m_startContainer->nodeType() == Node::TEXT_NODE ||
m_startContainer->nodeType() == Node::CDATA_SECTION_NODE ||
m_startContainer->nodeType() == Node::COMMENT_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_startContainer->cloneNode(true));
c->deleteData(m_endOffset, c->length() - m_endOffset, ec);
c->deleteData(0, m_startOffset, ec);
fragment->appendChild(c.release(), ec);
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
static_cast<CharacterData*>(m_startContainer.get())->deleteData(m_startOffset,m_endOffset-m_startOffset,ec);
m_startContainer->document()->updateLayout();
}
}
else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_startContainer->cloneNode(true));
c->setData(c->data().substring(m_startOffset, m_endOffset - m_startOffset), ec);
fragment->appendChild(c.release(), ec);
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_startContainer.get());
String data(pi->data());
data.remove(m_startOffset, m_endOffset - m_startOffset);
pi->setData(data, ec);
}
}
else {
Node *n = m_startContainer->firstChild();
unsigned i;
for (i = 0; n && i < m_startOffset; i++) n = n->nextSibling();
while (n && i < m_endOffset) { Node *next = n->nextSibling();
if (action == EXTRACT_CONTENTS)
fragment->appendChild(n,ec); else if (action == CLONE_CONTENTS)
fragment->appendChild(n->cloneNode(true),ec);
else
m_startContainer->removeChild(n,ec);
n = next;
i++;
}
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
collapse(true,ec);
return fragment.release();
}
RefPtr<Node> leftContents;
if (m_startContainer != cmnRoot) {
if(m_startContainer->nodeType() == Node::TEXT_NODE ||
m_startContainer->nodeType() == Node::CDATA_SECTION_NODE ||
m_startContainer->nodeType() == Node::COMMENT_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_startContainer->cloneNode(true));
c->deleteData(0, m_startOffset, ec);
leftContents = c.release();
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
static_cast<CharacterData*>(m_startContainer.get())->deleteData(
m_startOffset, static_cast<CharacterData*>(m_startContainer.get())->length() - m_startOffset, ec);
m_startContainer->document()->updateLayout();
}
}
else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_startContainer->cloneNode(true));
c->setData(c->data().substring(m_startOffset), ec);
leftContents = c.release();
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_startContainer.get());
String data(pi->data());
pi->setData(data.left(m_startOffset), ec);
}
}
else {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
leftContents = m_startContainer->cloneNode(false);
Node *n = m_startContainer->firstChild();
for (unsigned i = 0; n && i < m_startOffset; i++) n = n->nextSibling();
while (n) { Node *next = n->nextSibling();
if (action == EXTRACT_CONTENTS)
leftContents->appendChild(n,ec); else if (action == CLONE_CONTENTS)
leftContents->appendChild(n->cloneNode(true),ec);
else
m_startContainer->removeChild(n,ec);
n = next;
}
}
Node *leftParent = m_startContainer->parentNode();
Node *n = m_startContainer->nextSibling();
for (; leftParent != cmnRoot; leftParent = leftParent->parentNode()) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<Node> leftContentsParent = leftParent->cloneNode(false);
leftContentsParent->appendChild(leftContents,ec);
leftContents = leftContentsParent;
}
Node *next;
for (; n; n = next) {
next = n->nextSibling();
if (action == EXTRACT_CONTENTS)
leftContents->appendChild(n,ec); else if (action == CLONE_CONTENTS)
leftContents->appendChild(n->cloneNode(true),ec);
else
leftParent->removeChild(n,ec);
}
n = leftParent->nextSibling();
}
}
RefPtr<Node> rightContents = 0;
if (m_endContainer != cmnRoot) {
if(m_endContainer->nodeType() == Node::TEXT_NODE ||
m_endContainer->nodeType() == Node::CDATA_SECTION_NODE ||
m_endContainer->nodeType() == Node::COMMENT_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_endContainer->cloneNode(true));
c->deleteData(m_endOffset, static_cast<CharacterData*>(m_endContainer.get())->length() - m_endOffset, ec);
rightContents = c;
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
static_cast<CharacterData*>(m_endContainer.get())->deleteData(0, m_endOffset, ec);
m_startContainer->document()->updateLayout();
}
}
else if (m_endContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_endContainer->cloneNode(true));
c->setData(c->data().left(m_endOffset), ec);
rightContents = c.release();
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
ProcessingInstruction* pi= static_cast<ProcessingInstruction*>(m_endContainer.get());
pi->setData(pi->data().substring(m_endOffset), ec);
}
}
else {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
rightContents = m_endContainer->cloneNode(false);
Node *n = m_endContainer->firstChild();
if (n && m_endOffset) {
for (unsigned i = 0; i+1 < m_endOffset; i++) { Node *next = n->nextSibling();
if (!next)
break;
n = next;
}
Node *prev;
for (; n; n = prev) {
prev = n->previousSibling();
if (action == EXTRACT_CONTENTS)
rightContents->insertBefore(n,rightContents->firstChild(),ec); else if (action == CLONE_CONTENTS)
rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),ec);
else
m_endContainer->removeChild(n,ec);
}
}
}
Node *rightParent = m_endContainer->parentNode();
Node *n = m_endContainer->previousSibling();
for (; rightParent != cmnRoot; rightParent = rightParent->parentNode()) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<Node> rightContentsParent = rightParent->cloneNode(false);
rightContentsParent->appendChild(rightContents,ec);
rightContents = rightContentsParent;
}
Node *prev;
for (; n; n = prev) {
prev = n->previousSibling();
if (action == EXTRACT_CONTENTS)
rightContents->insertBefore(n,rightContents->firstChild(),ec); else if (action == CLONE_CONTENTS)
rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),ec);
else
rightParent->removeChild(n,ec);
}
n = rightParent->previousSibling();
}
}
Node *processStart; if (m_startContainer == cmnRoot) {
unsigned i;
processStart = m_startContainer->firstChild();
for (i = 0; i < m_startOffset; i++)
processStart = processStart->nextSibling();
}
else {
processStart = m_startContainer.get();
while (processStart->parentNode() != cmnRoot)
processStart = processStart->parentNode();
processStart = processStart->nextSibling();
}
Node *processEnd; if (m_endContainer == cmnRoot) {
unsigned i;
processEnd = m_endContainer->firstChild();
for (i = 0; i < m_endOffset; i++)
processEnd = processEnd->nextSibling();
}
else {
processEnd = m_endContainer.get();
while (processEnd->parentNode() != cmnRoot)
processEnd = processEnd->parentNode();
}
if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents)
fragment->appendChild(leftContents,ec);
Node *next;
Node *n;
if (processStart) {
for (n = processStart; n && n != processEnd; n = next) {
next = n->nextSibling();
if (action == EXTRACT_CONTENTS)
fragment->appendChild(n,ec); else if (action == CLONE_CONTENTS)
fragment->appendChild(n->cloneNode(true),ec);
else
cmnRoot->removeChild(n,ec);
}
}
if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents)
fragment->appendChild(rightContents,ec);
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
if (!partialStart && !partialEnd)
collapse(true,ec);
else if (partialStart) {
m_startContainer = partialStart->parentNode();
m_endContainer = partialStart->parentNode();
m_startOffset = m_endOffset = partialStart->nodeIndex()+1;
}
else if (partialEnd) {
m_startContainer = partialEnd->parentNode();
m_endContainer = partialEnd->parentNode();
m_startOffset = m_endOffset = partialEnd->nodeIndex();
}
}
return fragment.release();
}
PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
ec = 0;
checkDeleteExtract(ec);
if (ec)
return 0;
return processContents(EXTRACT_CONTENTS,ec);
}
PassRefPtr<DocumentFragment> Range::cloneContents( int &ec )
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
return processContents(CLONE_CONTENTS,ec);
}
void Range::insertNode(PassRefPtr<Node> newNode, ExceptionCode& ec)
{
ec = 0;
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!newNode) {
ec = NOT_FOUND_ERR;
return;
}
if (containedByReadOnly()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (newNode->document() != m_startContainer->document()) {
ec = WRONG_DOCUMENT_ERR;
return;
}
if (m_startContainer->nodeType() == Node::TEXT_NODE && !m_startContainer->parentNode()) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
Node *checkAgainst;
if (m_startContainer->nodeType() == Node::TEXT_NODE)
checkAgainst = m_startContainer->parentNode();
else
checkAgainst = m_startContainer.get();
if (newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
Node *c;
for (c = newNode->firstChild(); c; c = c->nextSibling()) {
if (!checkAgainst->childTypeAllowed(c->nodeType())) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
}
} else {
if (!checkAgainst->childTypeAllowed(newNode->nodeType())) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
}
for (Node *n = m_startContainer.get(); n; n = n->parentNode()) {
if (n == newNode) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
}
if (newNode->nodeType() == Node::ATTRIBUTE_NODE ||
newNode->nodeType() == Node::ENTITY_NODE ||
newNode->nodeType() == Node::NOTATION_NODE ||
newNode->nodeType() == Node::DOCUMENT_NODE) {
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
unsigned endOffsetDelta = 0;
if (m_startContainer->nodeType() == Node::TEXT_NODE ||
m_startContainer->nodeType() == Node::CDATA_SECTION_NODE) {
RefPtr<Text> newText = static_cast<Text*>(m_startContainer.get())->splitText(m_startOffset, ec);
if (ec)
return;
if (m_startContainer == m_endContainer)
endOffsetDelta = -m_startOffset;
m_startContainer->parentNode()->insertBefore(newNode, newText.get(), ec);
if (ec)
return;
m_endContainer = newText;
} else {
if (m_startContainer == m_endContainer) {
bool isFragment = newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE;
endOffsetDelta = isFragment ? newNode->childNodeCount() : 1;
}
m_startContainer->insertBefore(newNode, m_startContainer->childNode(m_startOffset), ec);
if (ec)
return;
}
m_endOffset += endOffsetDelta;
}
String Range::toString(ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return String();
}
Vector<UChar> result;
Node* pastEnd = pastEndNode();
for (Node* n = startNode(); n != pastEnd; n = n->traverseNextNode()) {
if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE) {
String data = static_cast<CharacterData*>(n)->data();
unsigned length = data.length();
unsigned start = (n == m_startContainer) ? min(m_startOffset, length) : 0;
unsigned end = (n == m_endContainer) ? min(max(start, m_endOffset), length) : length;
result.append(data.characters() + start, end - start);
}
}
return String::adopt(result);
}
String Range::toHTML() const
{
return createMarkup(this);
}
String Range::text() const
{
if (m_detached)
return String();
m_startContainer->document()->updateLayout();
return plainText(const_cast<Range *>(this));
}
PassRefPtr<DocumentFragment> Range::createContextualFragment(const String &html, ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
Node* htmlElement = m_startContainer->isHTMLElement() ? m_startContainer.get() : m_startContainer->parentNode();
if (!htmlElement || !htmlElement->isHTMLElement()) {
ec = NOT_SUPPORTED_ERR;
return 0;
}
RefPtr<DocumentFragment> fragment = static_cast<HTMLElement*>(htmlElement)->createContextualFragment(html);
if (!fragment) {
ec = NOT_SUPPORTED_ERR;
return 0;
}
return fragment.release();
}
void Range::detach(ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
m_startContainer = 0;
m_endContainer = 0;
m_detached = true;
}
bool Range::isDetached() const
{
return m_detached;
}
void Range::checkNodeWOffset(Node* n, int offset, ExceptionCode& ec) const
{
if (offset < 0)
ec = INDEX_SIZE_ERR;
switch (n->nodeType()) {
case Node::ENTITY_NODE:
case Node::NOTATION_NODE:
case Node::DOCUMENT_TYPE_NODE:
ec = RangeException::INVALID_NODE_TYPE_ERR;
break;
case Node::TEXT_NODE:
case Node::COMMENT_NODE:
case Node::CDATA_SECTION_NODE:
if ((unsigned)offset > static_cast<CharacterData*>(n)->length())
ec = INDEX_SIZE_ERR;
break;
case Node::PROCESSING_INSTRUCTION_NODE:
if ((unsigned)offset > static_cast<ProcessingInstruction*>(n)->data().length())
ec = INDEX_SIZE_ERR;
break;
default:
if ((unsigned)offset > n->childNodeCount())
ec = INDEX_SIZE_ERR;
break;
}
}
void Range::checkNodeBA( Node *n, ExceptionCode& ec) const
{
Node *root = n;
while (root->parentNode())
root = root->parentNode();
if (!(root->nodeType() == Node::ATTRIBUTE_NODE ||
root->nodeType() == Node::DOCUMENT_NODE ||
root->nodeType() == Node::DOCUMENT_FRAGMENT_NODE ||
root->isShadowNode())) {
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
if( n->nodeType() == Node::DOCUMENT_NODE ||
n->nodeType() == Node::DOCUMENT_FRAGMENT_NODE ||
n->nodeType() == Node::ATTRIBUTE_NODE ||
n->nodeType() == Node::ENTITY_NODE ||
n->nodeType() == Node::NOTATION_NODE )
ec = RangeException::INVALID_NODE_TYPE_ERR;
}
PassRefPtr<Range> Range::cloneRange(ExceptionCode& ec) const
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return 0;
}
return new Range(m_ownerDocument.get(), m_startContainer.get(), m_startOffset, m_endContainer.get(), m_endOffset);
}
void Range::setStartAfter( Node *refNode, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return;
}
ec = 0;
checkNodeBA( refNode, ec );
if (ec)
return;
setStart(refNode->parentNode(), refNode->nodeIndex() + 1, ec);
}
void Range::setEndBefore( Node *refNode, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return;
}
ec = 0;
checkNodeBA(refNode, ec);
if (ec)
return;
setEnd(refNode->parentNode(), refNode->nodeIndex(), ec);
}
void Range::setEndAfter( Node *refNode, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return;
}
ec = 0;
checkNodeBA(refNode, ec);
if (ec)
return;
setEnd(refNode->parentNode(), refNode->nodeIndex() + 1, ec);
}
void Range::selectNode( Node *refNode, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
Node *anc;
for (anc = refNode->parentNode(); anc; anc = anc->parentNode()) {
if (anc->nodeType() == Node::ENTITY_NODE ||
anc->nodeType() == Node::NOTATION_NODE ||
anc->nodeType() == Node::DOCUMENT_TYPE_NODE) {
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
}
if (refNode->nodeType() == Node::DOCUMENT_NODE ||
refNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE ||
refNode->nodeType() == Node::ATTRIBUTE_NODE ||
refNode->nodeType() == Node::ENTITY_NODE ||
refNode->nodeType() == Node::NOTATION_NODE) {
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
ec = 0;
setStartBefore( refNode, ec );
if (ec)
return;
setEndAfter( refNode, ec );
}
void Range::selectNodeContents( Node *refNode, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
Node *n;
for (n = refNode; n; n = n->parentNode()) {
if (n->nodeType() == Node::ENTITY_NODE ||
n->nodeType() == Node::NOTATION_NODE ||
n->nodeType() == Node::DOCUMENT_TYPE_NODE) {
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
}
m_startContainer = refNode;
m_startOffset = 0;
m_endContainer = refNode;
m_endOffset = refNode->offsetInCharacters() ? refNode->maxCharacterOffset() : refNode->childNodeCount();
}
void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec)
{
RefPtr<Node> newParent = passNewParent;
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!newParent) {
ec = NOT_FOUND_ERR;
return;
}
if( newParent->nodeType() == Node::ATTRIBUTE_NODE ||
newParent->nodeType() == Node::ENTITY_NODE ||
newParent->nodeType() == Node::NOTATION_NODE ||
newParent->nodeType() == Node::DOCUMENT_TYPE_NODE ||
newParent->nodeType() == Node::DOCUMENT_NODE ||
newParent->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
if (containedByReadOnly()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (newParent->document() != m_startContainer->document()) {
ec = WRONG_DOCUMENT_ERR;
return;
}
Node* parentOfNewParent = m_startContainer.get();
if (parentOfNewParent->isTextNode())
parentOfNewParent = parentOfNewParent->parentNode();
if (!parentOfNewParent->childTypeAllowed(newParent->nodeType())) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
if (m_startContainer == newParent || m_startContainer->isDescendantOf(newParent.get())) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
if (!m_startContainer->offsetInCharacters()) {
if (m_startOffset > 0 && m_startOffset < m_startContainer->childNodeCount()) {
ec = RangeException::BAD_BOUNDARYPOINTS_ERR;
return;
}
}
if (!m_endContainer->offsetInCharacters()) {
if (m_endOffset > 0 && m_endOffset < m_endContainer->childNodeCount()) {
ec = RangeException::BAD_BOUNDARYPOINTS_ERR;
return;
}
}
ec = 0;
while (Node* n = newParent->firstChild()) {
newParent->removeChild(n, ec);
if (ec)
return;
}
RefPtr<DocumentFragment> fragment = extractContents(ec);
if (ec)
return;
insertNode(newParent, ec);
if (ec)
return;
newParent->appendChild(fragment.release(), ec);
if (ec)
return;
selectNode(newParent.get(), ec);
}
void Range::setStartBefore( Node *refNode, ExceptionCode& ec)
{
if (m_detached) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return;
}
ec = 0;
checkNodeBA(refNode, ec);
if (ec)
return;
setStart(refNode->parentNode(), refNode->nodeIndex(), ec);
}
void Range::checkDeleteExtract(ExceptionCode& ec)
{
if (!commonAncestorContainer(ec) || ec)
return;
Node *pastEnd = pastEndNode();
for (Node *n = startNode(); n != pastEnd; n = n->traverseNextNode()) {
if (n->isReadOnlyNode()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { ec = HIERARCHY_REQUEST_ERR;
return;
}
}
if (containedByReadOnly()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return;
}
}
bool Range::containedByReadOnly() const
{
Node *n;
for (n = m_startContainer.get(); n; n = n->parentNode()) {
if (n->isReadOnlyNode())
return true;
}
for (n = m_endContainer.get(); n; n = n->parentNode()) {
if (n->isReadOnlyNode())
return true;
}
return false;
}
Position Range::startPosition() const
{
return Position(m_startContainer.get(), m_startOffset);
}
Position Range::endPosition() const
{
return Position(m_endContainer.get(), m_endOffset);
}
Node *Range::startNode() const
{
if (!m_startContainer)
return 0;
if (m_startContainer->offsetInCharacters())
return m_startContainer.get();
Node *child = m_startContainer->childNode(m_startOffset);
if (child)
return child;
if (m_startOffset == 0)
return m_startContainer.get();
return m_startContainer->traverseNextSibling();
}
Position Range::editingStartPosition() const
{
VisiblePosition visiblePosition(m_startContainer.get(), m_startOffset, VP_DEFAULT_AFFINITY);
if (visiblePosition.isNull())
return Position();
ExceptionCode ec = 0;
if (collapsed(ec))
return visiblePosition.deepEquivalent();
if (isEndOfParagraph(visiblePosition))
return visiblePosition.next().deepEquivalent().downstream();
return visiblePosition.deepEquivalent().downstream();
}
Node *Range::pastEndNode() const
{
if (!m_startContainer || !m_endContainer)
return 0;
if (m_endContainer->offsetInCharacters())
return m_endContainer->traverseNextSibling();
Node *child = m_endContainer->childNode(m_endOffset);
if (child)
return child;
return m_endContainer->traverseNextSibling();
}
IntRect Range::boundingBox()
{
IntRect result;
Vector<IntRect> rects;
addLineBoxRects(rects);
const size_t n = rects.size();
for (size_t i = 0; i < n; ++i)
result.unite(rects[i]);
return result;
}
void Range::addLineBoxRects(Vector<IntRect>& rects, bool useSelectionHeight)
{
if (!m_startContainer || !m_endContainer)
return;
RenderObject* start = m_startContainer->renderer();
RenderObject* end = m_endContainer->renderer();
if (!start || !end)
return;
RenderObject* stop = end->nextInPreOrderAfterChildren();
for (RenderObject* r = start; r && r != stop; r = r->nextInPreOrder()) {
if (!r->firstChild()) {
int startOffset = r == start ? m_startOffset : 0;
int endOffset = r == end ? m_endOffset : UINT_MAX;
r->addLineBoxRects(rects, startOffset, endOffset, useSelectionHeight);
}
}
}
#ifndef NDEBUG
#define FormatBufferSize 1024
void Range::formatForDebugger(char *buffer, unsigned length) const
{
String result;
String s;
if (!m_startContainer || !m_endContainer)
result = "<empty>";
else {
char s[FormatBufferSize];
result += "from offset ";
result += String::number(m_startOffset);
result += " of ";
m_startContainer->formatForDebugger(s, FormatBufferSize);
result += s;
result += " to offset ";
result += String::number(m_endOffset);
result += " of ";
m_endContainer->formatForDebugger(s, FormatBufferSize);
result += s;
}
strncpy(buffer, result.deprecatedString().latin1(), length - 1);
}
#undef FormatBufferSize
#endif
bool operator==(const Range &a, const Range &b)
{
if (&a == &b)
return true;
if (!&a || !&b)
return false;
bool ad = a.isDetached();
bool bd = b.isDetached();
if (ad && bd)
return true;
if (ad || bd)
return false;
int exception = 0;
return a.startContainer(exception) == b.startContainer(exception)
&& a.endContainer(exception) == b.endContainer(exception)
&& a.startOffset(exception) == b.startOffset(exception)
&& a.endOffset(exception) == b.endOffset(exception);
}
PassRefPtr<Range> rangeOfContents(Node* node)
{
ASSERT(node);
RefPtr<Range> range = new Range(node->document());
int exception = 0;
range->selectNodeContents(node, exception);
return range.release();
}
}