#include "config.h"
#include "Range.h"
#include "RangeException.h"
#include "ClientRect.h"
#include "ClientRectList.h"
#include "DocumentFragment.h"
#include "FrameView.h"
#include "HTMLElement.h"
#include "NodeWithIndex.h"
#include "ProcessingInstruction.h"
#include "Text.h"
#include "TextIterator.h"
#include "VisiblePosition.h"
#include "htmlediting.h"
#include "markup.h"
#include "visible_units.h"
#include <stdio.h>
#include <wtf/text/CString.h>
#include <wtf/RefCountedLeakCounter.h>
namespace WebCore {
using namespace std;
#ifndef NDEBUG
static WTF::RefCountedLeakCounter rangeCounter("Range");
#endif
inline Range::Range(PassRefPtr<Document> ownerDocument)
: m_ownerDocument(ownerDocument)
, m_start(m_ownerDocument)
, m_end(m_ownerDocument)
{
#ifndef NDEBUG
rangeCounter.increment();
#endif
m_ownerDocument->attachRange(this);
}
PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument)
{
return adoptRef(new Range(ownerDocument));
}
inline Range::Range(PassRefPtr<Document> ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset)
: m_ownerDocument(ownerDocument)
, m_start(m_ownerDocument)
, m_end(m_ownerDocument)
{
#ifndef NDEBUG
rangeCounter.increment();
#endif
m_ownerDocument->attachRange(this);
ExceptionCode ec = 0;
setStart(startContainer, startOffset, ec);
ASSERT(!ec);
setEnd(endContainer, endOffset, ec);
ASSERT(!ec);
}
PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument, PassRefPtr<Node> startContainer, int startOffset, PassRefPtr<Node> endContainer, int endOffset)
{
return adoptRef(new Range(ownerDocument, startContainer, startOffset, endContainer, endOffset));
}
PassRefPtr<Range> Range::create(PassRefPtr<Document> ownerDocument, const Position& start, const Position& end)
{
return adoptRef(new Range(ownerDocument, start.node(), start.deprecatedEditingOffset(), end.node(), end.deprecatedEditingOffset()));
}
Range::~Range()
{
m_ownerDocument->detachRange(this);
#ifndef NDEBUG
rangeCounter.decrement();
#endif
}
Node* Range::startContainer(ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_start.container();
}
int Range::startOffset(ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_start.offset();
}
Node* Range::endContainer(ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_end.container();
}
int Range::endOffset(ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_end.offset();
}
Node* Range::commonAncestorContainer(ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
return commonAncestorContainer(m_start.container(), m_end.container());
}
Node* Range::commonAncestorContainer(Node* containerA, Node* containerB)
{
for (Node* parentA = containerA; parentA; parentA = parentA->parentNode()) {
for (Node* parentB = containerB; parentB; parentB = parentB->parentNode()) {
if (parentA == parentB)
return parentA;
}
}
return 0;
}
bool Range::collapsed(ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
return m_start == m_end;
}
void Range::setStart(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec)
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return;
}
ec = 0;
Node* childNode = checkNodeWOffset(refNode.get(), offset, ec);
if (ec)
return;
m_start.set(refNode, offset, childNode);
Node* endRootContainer = m_end.container();
while (endRootContainer->parentNode())
endRootContainer = endRootContainer->parentNode();
Node* startRootContainer = m_start.container();
while (startRootContainer->parentNode())
startRootContainer = startRootContainer->parentNode();
if (startRootContainer != endRootContainer)
collapse(true, ec);
else if (compareBoundaryPoints(m_start, m_end) > 0)
collapse(true, ec);
}
void Range::setEnd(PassRefPtr<Node> refNode, int offset, ExceptionCode& ec)
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
if (refNode->document() != m_ownerDocument) {
ec = WRONG_DOCUMENT_ERR;
return;
}
ec = 0;
Node* childNode = checkNodeWOffset(refNode.get(), offset, ec);
if (ec)
return;
m_end.set(refNode, offset, childNode);
Node* endRootContainer = m_end.container();
while (endRootContainer->parentNode())
endRootContainer = endRootContainer->parentNode();
Node* startRootContainer = m_start.container();
while (startRootContainer->parentNode())
startRootContainer = startRootContainer->parentNode();
if (startRootContainer != endRootContainer)
collapse(false, ec);
if (compareBoundaryPoints(m_start, m_end) > 0)
collapse(false, ec);
}
void Range::collapse(bool toStart, ExceptionCode& ec)
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
if (toStart)
m_end = m_start;
else
m_start = m_end;
}
bool Range::isPointInRange(Node* refNode, int offset, ExceptionCode& ec)
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return false;
}
if (!refNode) {
ec = HIERARCHY_REQUEST_ERR;
return false;
}
if (!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;
return compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset()) >= 0
&& compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset()) <= 0;
}
short Range::comparePoint(Node* refNode, int offset, ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
if (!refNode) {
ec = HIERARCHY_REQUEST_ERR;
return 0;
}
if (!refNode->attached() || 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_start.container(), m_start.offset()) < 0)
return -1;
if (compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset()) > 0)
return 1;
return 0;
}
Range::CompareResults Range::compareNode(Node* refNode, ExceptionCode& ec) const
{
if (!refNode) {
ec = NOT_FOUND_ERR;
return NODE_BEFORE;
}
if (!m_start.container() && refNode->attached()) {
ec = INVALID_STATE_ERR;
return NODE_BEFORE;
}
if (m_start.container() && !refNode->attached()) {
return NODE_BEFORE;
}
if (refNode->document() != m_ownerDocument) {
return NODE_BEFORE;
}
Node* parentNode = refNode->parentNode();
int nodeIndex = refNode->nodeIndex();
if (!parentNode) {
ec = NOT_FOUND_ERR;
return NODE_BEFORE;
}
if (comparePoint(parentNode, nodeIndex, ec) < 0) { if (comparePoint(parentNode, nodeIndex + 1, ec) > 0) return NODE_BEFORE_AND_AFTER;
return NODE_BEFORE; } else { if (comparePoint(parentNode, nodeIndex + 1, ec) > 0) return NODE_AFTER;
return NODE_INSIDE; }
}
short Range::compareBoundaryPoints(CompareHow how, const Range* sourceRange, ExceptionCode& ec) const
{
if (!m_start.container()) {
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_start, sourceRange->m_start);
case START_TO_END:
return compareBoundaryPoints(m_end, sourceRange->m_start);
case END_TO_END:
return compareBoundaryPoints(m_end, sourceRange->m_end);
case END_TO_START:
return compareBoundaryPoints(m_start, sourceRange->m_end);
}
ec = SYNTAX_ERR;
return 0;
}
short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB)
{
ASSERT(containerA);
ASSERT(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* commonAncestor = commonAncestorContainer(containerA, containerB);
if (!commonAncestor)
return 0;
Node* childA = containerA;
while (childA && childA->parentNode() != commonAncestor)
childA = childA->parentNode();
if (!childA)
childA = commonAncestor;
Node* childB = containerB;
while (childB && childB->parentNode() != commonAncestor)
childB = childB->parentNode();
if (!childB)
childB = commonAncestor;
if (childA == childB)
return 0;
Node* n = commonAncestor->firstChild();
while (n) {
if (n == childA)
return -1; if (n == childB)
return 1; n = n->nextSibling();
}
ASSERT_NOT_REACHED();
return 0;
}
short Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB)
{
return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset());
}
bool Range::boundaryPointsValid() const
{
return m_start.container() && compareBoundaryPoints(m_start, m_end) <= 0;
}
void Range::deleteContents(ExceptionCode& ec)
{
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_start.container() && refNode->attached())
|| (m_start.container() && !refNode->attached())
|| refNode->document() != m_ownerDocument) {
return false;
}
Node* parentNode = refNode->parentNode();
int nodeIndex = refNode->nodeIndex();
if (!parentNode) {
ec = NOT_FOUND_ERR;
return false;
}
if (comparePoint(parentNode, nodeIndex, ec) < 0 && comparePoint(parentNode, nodeIndex + 1, ec) < 0) { return false;
} else if (comparePoint(parentNode, nodeIndex, ec) > 0 && comparePoint(parentNode, nodeIndex + 1, ec) > 0) { return false;
}
return true; }
PassRefPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionCode& ec)
{
RefPtr<DocumentFragment> fragment;
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
fragment = DocumentFragment::create(m_ownerDocument.get());
ec = 0;
if (collapsed(ec))
return fragment.release();
if (ec)
return 0;
Node* commonRoot = commonAncestorContainer(ec);
if (ec)
return 0;
ASSERT(commonRoot);
Node* partialStart = 0;
if (m_start.container() != commonRoot) {
partialStart = m_start.container();
while (partialStart->parentNode() != commonRoot)
partialStart = partialStart->parentNode();
}
Node* partialEnd = 0;
if (m_end.container() != commonRoot) {
partialEnd = m_end.container();
while (partialEnd->parentNode() != commonRoot)
partialEnd = partialEnd->parentNode();
}
if (m_start.container() == m_end.container()) {
Node::NodeType startNodeType = m_start.container()->nodeType();
if (startNodeType == Node::TEXT_NODE || startNodeType == Node::CDATA_SECTION_NODE || startNodeType == Node::COMMENT_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_start.container()->cloneNode(true));
c->deleteData(m_end.offset(), c->length() - m_end.offset(), ec);
c->deleteData(0, m_start.offset(), ec);
fragment->appendChild(c.release(), ec);
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
static_cast<CharacterData*>(m_start.container())->deleteData(m_start.offset(), m_end.offset() - m_start.offset(), ec);
} else if (startNodeType == Node::PROCESSING_INSTRUCTION_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_start.container()->cloneNode(true));
c->setData(c->data().substring(m_start.offset(), m_end.offset() - m_start.offset()), ec);
fragment->appendChild(c.release(), ec);
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_start.container());
String data(pi->data());
data.remove(m_start.offset(), m_end.offset() - m_start.offset());
pi->setData(data, ec);
}
} else {
Node* n = m_start.container()->firstChild();
int i;
for (i = 0; n && i < m_start.offset(); i++) n = n->nextSibling();
int endOffset = m_end.offset();
while (n && i < 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_start.container()->removeChild(n, ec);
n = next;
i++;
}
}
return fragment.release();
}
RefPtr<Node> leftContents;
if (m_start.container() != commonRoot) {
Node::NodeType startNodeType = m_start.container()->nodeType();
if (startNodeType == Node::TEXT_NODE || startNodeType == Node::CDATA_SECTION_NODE || startNodeType == Node::COMMENT_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_start.container()->cloneNode(true));
c->deleteData(0, m_start.offset(), ec);
leftContents = c.release();
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
static_cast<CharacterData*>(m_start.container())->deleteData(
m_start.offset(), static_cast<CharacterData*>(m_start.container())->length() - m_start.offset(), ec);
} else if (startNodeType == Node::PROCESSING_INSTRUCTION_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_start.container()->cloneNode(true));
c->setData(c->data().substring(m_start.offset()), ec);
leftContents = c.release();
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_start.container());
String data(pi->data());
pi->setData(data.left(m_start.offset()), ec);
}
} else {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
leftContents = m_start.container()->cloneNode(false);
Node* n = m_start.container()->firstChild();
for (int i = 0; n && i < m_start.offset(); 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_start.container()->removeChild(n, ec);
n = next;
}
}
Node* leftParent = m_start.container()->parentNode();
Node* n = m_start.container()->nextSibling();
for (; leftParent != commonRoot; leftParent = leftParent->parentNode()) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<Node> leftContentsParent = leftParent->cloneNode(false);
leftContentsParent->appendChild(leftContents, ec);
leftContents = leftContentsParent;
}
Node* next;
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;
if (m_end.container() != commonRoot) {
Node::NodeType endNodeType = m_end.container()->nodeType();
if (endNodeType == Node::TEXT_NODE || endNodeType == Node::CDATA_SECTION_NODE || endNodeType == Node::COMMENT_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(m_end.container()->cloneNode(true));
c->deleteData(m_end.offset(), static_cast<CharacterData*>(m_end.container())->length() - m_end.offset(), ec);
rightContents = c;
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
static_cast<CharacterData*>(m_end.container())->deleteData(0, m_end.offset(), ec);
} else if (endNodeType == Node::PROCESSING_INSTRUCTION_NODE) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(m_end.container()->cloneNode(true));
c->setData(c->data().left(m_end.offset()), ec);
rightContents = c.release();
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(m_end.container());
pi->setData(pi->data().substring(m_end.offset()), ec);
}
} else {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
rightContents = m_end.container()->cloneNode(false);
Node* n = m_end.container()->firstChild();
if (n && m_end.offset()) {
for (int i = 0; i + 1 < m_end.offset(); i++) { 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_end.container()->removeChild(n, ec);
}
}
}
Node* rightParent = m_end.container()->parentNode();
Node* n = m_end.container()->previousSibling();
for (; rightParent != commonRoot; rightParent = rightParent->parentNode()) {
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<Node> rightContentsParent = rightParent->cloneNode(false);
rightContentsParent->appendChild(rightContents, ec);
rightContents = rightContentsParent;
}
Node* prev;
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_start.container() == commonRoot) {
processStart = m_start.container()->firstChild();
for (int i = 0; i < m_start.offset(); i++)
processStart = processStart->nextSibling();
} else {
processStart = m_start.container();
while (processStart->parentNode() != commonRoot)
processStart = processStart->parentNode();
processStart = processStart->nextSibling();
}
Node* processEnd; if (m_end.container() == commonRoot) {
processEnd = m_end.container()->firstChild();
for (int i = 0; i < m_end.offset(); i++)
processEnd = processEnd->nextSibling();
} else {
processEnd = m_end.container();
while (processEnd->parentNode() != commonRoot)
processEnd = processEnd->parentNode();
}
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
if (partialStart)
setStart(partialStart->parentNode(), partialStart->nodeIndex() + 1, ec);
else if (partialEnd)
setStart(partialEnd->parentNode(), partialEnd->nodeIndex(), ec);
if (ec)
return 0;
m_end = m_start;
}
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
commonRoot->removeChild(n, ec);
}
}
if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents)
fragment->appendChild(rightContents, ec);
return fragment.release();
}
PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec)
{
checkDeleteExtract(ec);
if (ec)
return 0;
return processContents(EXTRACT_CONTENTS, ec);
}
PassRefPtr<DocumentFragment> Range::cloneContents(ExceptionCode& ec)
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
return processContents(CLONE_CONTENTS, ec);
}
void Range::insertNode(PassRefPtr<Node> prpNewNode, ExceptionCode& ec)
{
RefPtr<Node> newNode = prpNewNode;
ec = 0;
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
if (!newNode) {
ec = NOT_FOUND_ERR;
return;
}
if (containedByReadOnly()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (newNode->document() != m_start.container()->document()) {
ec = WRONG_DOCUMENT_ERR;
return;
}
bool startIsText = m_start.container()->isTextNode();
if (startIsText && !m_start.container()->parentNode()) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
Node* checkAgainst;
if (startIsText)
checkAgainst = m_start.container()->parentNode();
else
checkAgainst = m_start.container();
Node::NodeType newNodeType = newNode->nodeType();
int numNewChildren;
if (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) {
numNewChildren = 0;
for (Node* c = newNode->firstChild(); c; c = c->nextSibling()) {
if (!checkAgainst->childTypeAllowed(c->nodeType())) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
++numNewChildren;
}
} else {
numNewChildren = 1;
if (!checkAgainst->childTypeAllowed(newNodeType)) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
}
for (Node* n = m_start.container(); n; n = n->parentNode()) {
if (n == newNode) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
}
if (newNodeType == Node::ATTRIBUTE_NODE || newNodeType == Node::ENTITY_NODE
|| newNodeType == Node::NOTATION_NODE || newNodeType == Node::DOCUMENT_NODE) {
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
bool collapsed = m_start == m_end;
if (startIsText) {
RefPtr<Text> newText = static_cast<Text*>(m_start.container())->splitText(m_start.offset(), ec);
if (ec)
return;
m_start.container()->parentNode()->insertBefore(newNode.release(), newText.get(), ec);
if (ec)
return;
if (collapsed)
m_end.setToBeforeChild(newText.get());
} else {
RefPtr<Node> lastChild;
if (collapsed)
lastChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? newNode->lastChild() : newNode;
int startOffset = m_start.offset();
m_start.container()->insertBefore(newNode.release(), m_start.container()->childNode(startOffset), ec);
if (ec)
return;
if (collapsed)
m_end.set(m_start.container(), startOffset + numNewChildren, lastChild.get());
}
}
String Range::toString(ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return String();
}
Vector<UChar> result;
Node* pastLast = pastLastNode();
for (Node* n = firstNode(); n != pastLast; n = n->traverseNextNode()) {
if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE) {
String data = static_cast<CharacterData*>(n)->data();
int length = data.length();
int start = (n == m_start.container()) ? min(max(0, m_start.offset()), length) : 0;
int end = (n == m_end.container()) ? min(max(start, m_end.offset()), length) : length;
result.append(data.characters() + start, end - start);
}
}
return String::adopt(result);
}
String Range::toHTML() const
{
return createMarkup(this);
}
String Range::text() const
{
if (!m_start.container())
return String();
m_start.container()->document()->updateLayout();
return plainText(this);
}
PassRefPtr<DocumentFragment> Range::createContextualFragment(const String& markup, ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
Node* element = m_start.container()->isElementNode() ? m_start.container() : m_start.container()->parentNode();
if (!element || !element->isHTMLElement()) {
ec = NOT_SUPPORTED_ERR;
return 0;
}
RefPtr<DocumentFragment> fragment = static_cast<HTMLElement*>(element)->createContextualFragment(markup);
if (!fragment) {
ec = NOT_SUPPORTED_ERR;
return 0;
}
return fragment.release();
}
void Range::detach(ExceptionCode& ec)
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
m_ownerDocument->detachRange(this);
m_start.clear();
m_end.clear();
}
Node* Range::checkNodeWOffset(Node* n, int offset, ExceptionCode& ec) const
{
switch (n->nodeType()) {
case Node::DOCUMENT_TYPE_NODE:
case Node::ENTITY_NODE:
case Node::NOTATION_NODE:
ec = RangeException::INVALID_NODE_TYPE_ERR;
return 0;
case Node::CDATA_SECTION_NODE:
case Node::COMMENT_NODE:
case Node::TEXT_NODE:
if (static_cast<unsigned>(offset) > static_cast<CharacterData*>(n)->length())
ec = INDEX_SIZE_ERR;
return 0;
case Node::PROCESSING_INSTRUCTION_NODE:
if (static_cast<unsigned>(offset) > static_cast<ProcessingInstruction*>(n)->data().length())
ec = INDEX_SIZE_ERR;
return 0;
case Node::ATTRIBUTE_NODE:
case Node::DOCUMENT_FRAGMENT_NODE:
case Node::DOCUMENT_NODE:
case Node::ELEMENT_NODE:
case Node::ENTITY_REFERENCE_NODE:
case Node::XPATH_NAMESPACE_NODE: {
if (!offset)
return 0;
Node* childBefore = n->childNode(offset - 1);
if (!childBefore)
ec = INDEX_SIZE_ERR;
return childBefore;
}
}
ASSERT_NOT_REACHED();
return 0;
}
void Range::checkNodeBA(Node* n, ExceptionCode& ec) const
{
switch (n->nodeType()) {
case Node::ATTRIBUTE_NODE:
case Node::DOCUMENT_FRAGMENT_NODE:
case Node::DOCUMENT_NODE:
case Node::ENTITY_NODE:
case Node::NOTATION_NODE:
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
case Node::CDATA_SECTION_NODE:
case Node::COMMENT_NODE:
case Node::DOCUMENT_TYPE_NODE:
case Node::ELEMENT_NODE:
case Node::ENTITY_REFERENCE_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::TEXT_NODE:
case Node::XPATH_NAMESPACE_NODE:
break;
}
Node* root = n;
while (Node* parent = root->parentNode())
root = parent;
switch (root->nodeType()) {
case Node::ATTRIBUTE_NODE:
case Node::DOCUMENT_NODE:
case Node::DOCUMENT_FRAGMENT_NODE:
break;
case Node::CDATA_SECTION_NODE:
case Node::COMMENT_NODE:
case Node::DOCUMENT_TYPE_NODE:
case Node::ELEMENT_NODE:
case Node::ENTITY_NODE:
case Node::ENTITY_REFERENCE_NODE:
case Node::NOTATION_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::TEXT_NODE:
case Node::XPATH_NAMESPACE_NODE:
if (root->isShadowNode())
break;
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
}
PassRefPtr<Range> Range::cloneRange(ExceptionCode& ec) const
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return 0;
}
return Range::create(m_ownerDocument, m_start.container(), m_start.offset(), m_end.container(), m_end.offset());
}
void Range::setStartAfter(Node* refNode, ExceptionCode& ec)
{
if (!m_start.container()) {
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_start.container()) {
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_start.container()) {
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_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
for (Node* anc = refNode->parentNode(); anc; anc = anc->parentNode()) {
switch (anc->nodeType()) {
case Node::ATTRIBUTE_NODE:
case Node::CDATA_SECTION_NODE:
case Node::COMMENT_NODE:
case Node::DOCUMENT_FRAGMENT_NODE:
case Node::DOCUMENT_NODE:
case Node::ELEMENT_NODE:
case Node::ENTITY_REFERENCE_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::TEXT_NODE:
case Node::XPATH_NAMESPACE_NODE:
break;
case Node::DOCUMENT_TYPE_NODE:
case Node::ENTITY_NODE:
case Node::NOTATION_NODE:
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
}
switch (refNode->nodeType()) {
case Node::CDATA_SECTION_NODE:
case Node::COMMENT_NODE:
case Node::DOCUMENT_TYPE_NODE:
case Node::ELEMENT_NODE:
case Node::ENTITY_REFERENCE_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::TEXT_NODE:
case Node::XPATH_NAMESPACE_NODE:
break;
case Node::ATTRIBUTE_NODE:
case Node::DOCUMENT_FRAGMENT_NODE:
case Node::DOCUMENT_NODE:
case Node::ENTITY_NODE:
case Node::NOTATION_NODE:
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
ec = 0;
setStartBefore(refNode, ec);
if (ec)
return;
setEndAfter(refNode, ec);
}
void Range::selectNodeContents(Node* refNode, ExceptionCode& ec)
{
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
if (!refNode) {
ec = NOT_FOUND_ERR;
return;
}
for (Node* n = refNode; n; n = n->parentNode()) {
switch (n->nodeType()) {
case Node::ATTRIBUTE_NODE:
case Node::CDATA_SECTION_NODE:
case Node::COMMENT_NODE:
case Node::DOCUMENT_FRAGMENT_NODE:
case Node::DOCUMENT_NODE:
case Node::ELEMENT_NODE:
case Node::ENTITY_REFERENCE_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::TEXT_NODE:
case Node::XPATH_NAMESPACE_NODE:
break;
case Node::DOCUMENT_TYPE_NODE:
case Node::ENTITY_NODE:
case Node::NOTATION_NODE:
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
}
}
m_start.setToStartOfNode(refNode);
m_end.setToEndOfNode(refNode);
}
void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec)
{
RefPtr<Node> newParent = passNewParent;
if (!m_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
if (!newParent) {
ec = NOT_FOUND_ERR;
return;
}
switch (newParent->nodeType()) {
case Node::ATTRIBUTE_NODE:
case Node::DOCUMENT_FRAGMENT_NODE:
case Node::DOCUMENT_NODE:
case Node::DOCUMENT_TYPE_NODE:
case Node::ENTITY_NODE:
case Node::NOTATION_NODE:
ec = RangeException::INVALID_NODE_TYPE_ERR;
return;
case Node::CDATA_SECTION_NODE:
case Node::COMMENT_NODE:
case Node::ELEMENT_NODE:
case Node::ENTITY_REFERENCE_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::TEXT_NODE:
case Node::XPATH_NAMESPACE_NODE:
break;
}
if (containedByReadOnly()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (newParent->document() != m_start.container()->document()) {
ec = WRONG_DOCUMENT_ERR;
return;
}
Node* parentOfNewParent = m_start.container();
if (parentOfNewParent->isCharacterDataNode())
parentOfNewParent = parentOfNewParent->parentNode();
if (!parentOfNewParent || !parentOfNewParent->childTypeAllowed(newParent->nodeType())) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
if (m_start.container() == newParent || m_start.container()->isDescendantOf(newParent.get())) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
Node* startNonTextContainer = m_start.container();
if (startNonTextContainer->nodeType() == Node::TEXT_NODE)
startNonTextContainer = startNonTextContainer->parentNode();
Node* endNonTextContainer = m_end.container();
if (endNonTextContainer->nodeType() == Node::TEXT_NODE)
endNonTextContainer = endNonTextContainer->parentNode();
if (startNonTextContainer != endNonTextContainer) {
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_start.container()) {
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 (!m_start.container()) {
ec = INVALID_STATE_ERR;
return;
}
ec = 0;
if (!commonAncestorContainer(ec) || ec)
return;
Node* pastLast = pastLastNode();
for (Node* n = firstNode(); n != pastLast; n = n->traverseNextNode()) {
if (n->isReadOnlyNode()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) {
ec = HIERARCHY_REQUEST_ERR;
return;
}
}
if (containedByReadOnly()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return;
}
}
bool Range::containedByReadOnly() const
{
for (Node* n = m_start.container(); n; n = n->parentNode()) {
if (n->isReadOnlyNode())
return true;
}
for (Node* n = m_end.container(); n; n = n->parentNode()) {
if (n->isReadOnlyNode())
return true;
}
return false;
}
Node* Range::firstNode() const
{
if (!m_start.container())
return 0;
if (m_start.container()->offsetInCharacters())
return m_start.container();
if (Node* child = m_start.container()->childNode(m_start.offset()))
return child;
if (!m_start.offset())
return m_start.container();
return m_start.container()->traverseNextSibling();
}
Position Range::editingStartPosition() const
{
VisiblePosition visiblePosition(m_start.container(), m_start.offset(), 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::shadowTreeRootNode() const
{
return startContainer() ? startContainer()->shadowTreeRootNode() : 0;
}
Node* Range::pastLastNode() const
{
if (!m_start.container() || !m_end.container())
return 0;
if (m_end.container()->offsetInCharacters())
return m_end.container()->traverseNextSibling();
if (Node* child = m_end.container()->childNode(m_end.offset()))
return child;
return m_end.container()->traverseNextSibling();
}
IntRect Range::boundingBox()
{
IntRect result;
Vector<IntRect> rects;
textRects(rects);
const size_t n = rects.size();
for (size_t i = 0; i < n; ++i)
result.unite(rects[i]);
return result;
}
void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight)
{
Node* startContainer = m_start.container();
Node* endContainer = m_end.container();
if (!startContainer || !endContainer)
return;
Node* stopNode = pastLastNode();
for (Node* node = firstNode(); node != stopNode; node = node->traverseNextNode()) {
RenderObject* r = node->renderer();
if (!r || !r->isText())
continue;
RenderText* renderText = toRenderText(r);
int startOffset = node == startContainer ? m_start.offset() : 0;
int endOffset = node == endContainer ? m_end.offset() : numeric_limits<int>::max();
renderText->absoluteRectsForRange(rects, startOffset, endOffset, useSelectionHeight);
}
}
void Range::textQuads(Vector<FloatQuad>& quads, bool useSelectionHeight)
{
Node* startContainer = m_start.container();
Node* endContainer = m_end.container();
if (!startContainer || !endContainer)
return;
Node* stopNode = pastLastNode();
for (Node* node = firstNode(); node != stopNode; node = node->traverseNextNode()) {
RenderObject* r = node->renderer();
if (!r || !r->isText())
continue;
RenderText* renderText = toRenderText(r);
int startOffset = node == startContainer ? m_start.offset() : 0;
int endOffset = node == endContainer ? m_end.offset() : numeric_limits<int>::max();
renderText->absoluteQuadsForRange(quads, startOffset, endOffset, useSelectionHeight);
}
}
#ifndef NDEBUG
#define FormatBufferSize 1024
void Range::formatForDebugger(char* buffer, unsigned length) const
{
String result;
String s;
if (!m_start.container() || !m_end.container())
result = "<empty>";
else {
char s[FormatBufferSize];
result += "from offset ";
result += String::number(m_start.offset());
result += " of ";
m_start.container()->formatForDebugger(s, FormatBufferSize);
result += s;
result += " to offset ";
result += String::number(m_end.offset());
result += " of ";
m_end.container()->formatForDebugger(s, FormatBufferSize);
result += s;
}
strncpy(buffer, result.utf8().data(), length - 1);
}
#undef FormatBufferSize
#endif
bool areRangesEqual(const Range* a, const Range* b)
{
if (a == b)
return true;
if (!a || !b)
return false;
return a->startPosition() == b->startPosition() && a->endPosition() == b->endPosition();
}
PassRefPtr<Range> rangeOfContents(Node* node)
{
ASSERT(node);
RefPtr<Range> range = Range::create(node->document());
int exception = 0;
range->selectNodeContents(node, exception);
return range.release();
}
int Range::maxStartOffset() const
{
if (!m_start.container())
return 0;
if (!m_start.container()->offsetInCharacters())
return m_start.container()->childNodeCount();
return m_start.container()->maxCharacterOffset();
}
int Range::maxEndOffset() const
{
if (!m_end.container())
return 0;
if (!m_end.container()->offsetInCharacters())
return m_end.container()->childNodeCount();
return m_end.container()->maxCharacterOffset();
}
static inline void boundaryNodeChildrenChanged(RangeBoundaryPoint& boundary, ContainerNode* container)
{
if (!boundary.childBefore())
return;
if (boundary.container() != container)
return;
boundary.invalidateOffset();
}
void Range::nodeChildrenChanged(ContainerNode* container)
{
ASSERT(container);
ASSERT(container->document() == m_ownerDocument);
boundaryNodeChildrenChanged(m_start, container);
boundaryNodeChildrenChanged(m_end, container);
}
static inline void boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint& boundary, ContainerNode* container)
{
for (Node* nodeToBeRemoved = container->firstChild(); nodeToBeRemoved; nodeToBeRemoved = nodeToBeRemoved->nextSibling()) {
if (boundary.childBefore() == nodeToBeRemoved) {
boundary.setToStartOfNode(container);
return;
}
for (Node* n = boundary.container(); n; n = n->parentNode()) {
if (n == nodeToBeRemoved) {
boundary.setToStartOfNode(container);
return;
}
}
}
}
void Range::nodeChildrenWillBeRemoved(ContainerNode* container)
{
ASSERT(container);
ASSERT(container->document() == m_ownerDocument);
boundaryNodeChildrenWillBeRemoved(m_start, container);
boundaryNodeChildrenWillBeRemoved(m_end, container);
}
static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node* nodeToBeRemoved)
{
if (boundary.childBefore() == nodeToBeRemoved) {
boundary.childBeforeWillBeRemoved();
return;
}
for (Node* n = boundary.container(); n; n = n->parentNode()) {
if (n == nodeToBeRemoved) {
boundary.setToBeforeChild(nodeToBeRemoved);
return;
}
}
}
void Range::nodeWillBeRemoved(Node* node)
{
ASSERT(node);
ASSERT(node->document() == m_ownerDocument);
ASSERT(node != m_ownerDocument);
ASSERT(node->parentNode());
boundaryNodeWillBeRemoved(m_start, node);
boundaryNodeWillBeRemoved(m_end, node);
}
static inline void boundaryTextInserted(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length)
{
if (boundary.container() != text)
return;
unsigned boundaryOffset = boundary.offset();
if (offset >= boundaryOffset)
return;
boundary.setOffset(boundaryOffset + length);
}
void Range::textInserted(Node* text, unsigned offset, unsigned length)
{
ASSERT(text);
ASSERT(text->document() == m_ownerDocument);
boundaryTextInserted(m_start, text, offset, length);
boundaryTextInserted(m_end, text, offset, length);
}
static inline void boundaryTextRemoved(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length)
{
if (boundary.container() != text)
return;
unsigned boundaryOffset = boundary.offset();
if (offset >= boundaryOffset)
return;
if (offset + length >= boundaryOffset)
boundary.setOffset(offset);
else
boundary.setOffset(boundaryOffset - length);
}
void Range::textRemoved(Node* text, unsigned offset, unsigned length)
{
ASSERT(text);
ASSERT(text->document() == m_ownerDocument);
boundaryTextRemoved(m_start, text, offset, length);
boundaryTextRemoved(m_end, text, offset, length);
}
static inline void boundaryTextNodesMerged(RangeBoundaryPoint& boundary, NodeWithIndex& oldNode, unsigned offset)
{
if (boundary.container() == oldNode.node())
boundary.set(oldNode.node()->previousSibling(), boundary.offset() + offset, 0);
else if (boundary.container() == oldNode.node()->parentNode() && boundary.offset() == oldNode.index())
boundary.set(oldNode.node()->previousSibling(), offset, 0);
}
void Range::textNodesMerged(NodeWithIndex& oldNode, unsigned offset)
{
ASSERT(oldNode.node());
ASSERT(oldNode.node()->document() == m_ownerDocument);
ASSERT(oldNode.node()->parentNode());
ASSERT(oldNode.node()->isTextNode());
ASSERT(oldNode.node()->previousSibling());
ASSERT(oldNode.node()->previousSibling()->isTextNode());
boundaryTextNodesMerged(m_start, oldNode, offset);
boundaryTextNodesMerged(m_end, oldNode, offset);
}
static inline void boundaryTextNodesSplit(RangeBoundaryPoint& boundary, Text* oldNode)
{
if (boundary.container() != oldNode)
return;
unsigned boundaryOffset = boundary.offset();
if (boundaryOffset <= oldNode->length())
return;
boundary.set(oldNode->nextSibling(), boundaryOffset - oldNode->length(), 0);
}
void Range::textNodeSplit(Text* oldNode)
{
ASSERT(oldNode);
ASSERT(oldNode->document() == m_ownerDocument);
ASSERT(oldNode->parentNode());
ASSERT(oldNode->isTextNode());
ASSERT(oldNode->nextSibling());
ASSERT(oldNode->nextSibling()->isTextNode());
boundaryTextNodesSplit(m_start, oldNode);
boundaryTextNodesSplit(m_end, oldNode);
}
void Range::expand(const String& unit, ExceptionCode& ec)
{
VisiblePosition start(startPosition());
VisiblePosition end(endPosition());
if (unit == "word") {
start = startOfWord(start);
end = endOfWord(end);
} else if (unit == "sentence") {
start = startOfSentence(start);
end = endOfSentence(end);
} else if (unit == "block") {
start = startOfParagraph(start);
end = endOfParagraph(end);
} else if (unit == "document") {
start = startOfDocument(start);
end = endOfDocument(end);
} else
return;
setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), ec);
setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), ec);
}
PassRefPtr<ClientRectList> Range::getClientRects() const
{
if (!m_start.container())
return 0;
m_ownerDocument->updateLayoutIgnorePendingStylesheets();
Vector<FloatQuad> quads;
getBorderAndTextQuads(quads);
return ClientRectList::create(quads);
}
PassRefPtr<ClientRect> Range::getBoundingClientRect() const
{
if (!m_start.container())
return 0;
m_ownerDocument->updateLayoutIgnorePendingStylesheets();
Vector<FloatQuad> quads;
getBorderAndTextQuads(quads);
if (quads.isEmpty())
return ClientRect::create();
IntRect result;
for (size_t i = 0; i < quads.size(); ++i)
result.unite(quads[i].enclosingBoundingBox());
return ClientRect::create(result);
}
static void adjustFloatQuadsForScrollAndAbsoluteZoom(Vector<FloatQuad>& quads, Document* document, RenderObject* renderer)
{
FrameView* view = document->view();
if (!view)
return;
IntRect visibleContentRect = view->visibleContentRect();
for (size_t i = 0; i < quads.size(); ++i) {
quads[i].move(-visibleContentRect.x(), -visibleContentRect.y());
adjustFloatQuadForAbsoluteZoom(quads[i], renderer);
}
}
void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const
{
Node* startContainer = m_start.container();
Node* endContainer = m_end.container();
Node* stopNode = pastLastNode();
HashSet<Node*> nodeSet;
for (Node* node = firstNode(); node != stopNode; node = node->traverseNextNode()) {
if (node->isElementNode())
nodeSet.add(node);
}
for (Node* node = firstNode(); node != stopNode; node = node->traverseNextNode()) {
if (node->isElementNode()) {
if (!nodeSet.contains(node->parentNode())) {
if (RenderBoxModelObject* renderBoxModelObject = static_cast<Element*>(node)->renderBoxModelObject()) {
Vector<FloatQuad> elementQuads;
renderBoxModelObject->absoluteQuads(elementQuads);
adjustFloatQuadsForScrollAndAbsoluteZoom(elementQuads, m_ownerDocument.get(), renderBoxModelObject);
quads.append(elementQuads);
}
}
} else if (node->isTextNode()) {
if (RenderObject* renderer = static_cast<Text*>(node)->renderer()) {
RenderText* renderText = toRenderText(renderer);
int startOffset = (node == startContainer) ? m_start.offset() : 0;
int endOffset = (node == endContainer) ? m_end.offset() : INT_MAX;
Vector<FloatQuad> textQuads;
renderText->absoluteQuadsForRange(textQuads, startOffset, endOffset);
adjustFloatQuadsForScrollAndAbsoluteZoom(textQuads, m_ownerDocument.get(), renderText);
quads.append(textQuads);
}
}
}
}
}
#ifndef NDEBUG
void showTree(const WebCore::Range* range)
{
if (range && range->boundaryPointsValid()) {
WebCore::Position start = range->startPosition();
WebCore::Position end = range->endPosition();
start.node()->showTreeAndMark(start.node(), "S", end.node(), "E");
fprintf(stderr, "start offset: %d, end offset: %d\n", start.deprecatedEditingOffset(), end.deprecatedEditingOffset());
}
}
#endif