summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/dom/Range.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/dom/Range.cpp')
-rw-r--r--Source/WebCore/dom/Range.cpp268
1 files changed, 148 insertions, 120 deletions
diff --git a/Source/WebCore/dom/Range.cpp b/Source/WebCore/dom/Range.cpp
index e224843..a0370fa 100644
--- a/Source/WebCore/dom/Range.cpp
+++ b/Source/WebCore/dom/Range.cpp
@@ -607,7 +607,48 @@ static inline Node* highestAncestorUnderCommonRoot(Node* node, Node* commonRoot)
return node;
}
-static inline unsigned lengthOfContentsInNode() { return numeric_limits<unsigned>::max(); }
+static inline Node* childOfCommonRootBeforeOffset(Node* container, unsigned offset, Node* commonRoot)
+{
+ ASSERT(container);
+ ASSERT(commonRoot);
+ ASSERT(commonRoot->contains(container));
+
+ if (container == commonRoot) {
+ container = container->firstChild();
+ for (unsigned i = 0; container && i < offset; i++)
+ container = container->nextSibling();
+ } else {
+ while (container->parentNode() != commonRoot)
+ container = container->parentNode();
+ }
+
+ return container;
+}
+
+static inline unsigned lengthOfContentsInNode(Node* node)
+{
+ // This switch statement must be consistent with that of Range::processContentsBetweenOffsets.
+ switch (node->nodeType()) {
+ case Node::TEXT_NODE:
+ case Node::CDATA_SECTION_NODE:
+ case Node::COMMENT_NODE:
+ return static_cast<CharacterData*>(node)->length();
+ case Node::PROCESSING_INSTRUCTION_NODE:
+ return static_cast<ProcessingInstruction*>(node)->data().length();
+ case Node::ELEMENT_NODE:
+ case Node::ATTRIBUTE_NODE:
+ case Node::ENTITY_REFERENCE_NODE:
+ case Node::ENTITY_NODE:
+ case Node::DOCUMENT_NODE:
+ case Node::DOCUMENT_TYPE_NODE:
+ case Node::DOCUMENT_FRAGMENT_NODE:
+ case Node::NOTATION_NODE:
+ case Node::XPATH_NAMESPACE_NODE:
+ return node->childNodeCount();
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
PassRefPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionCode& ec)
{
@@ -656,85 +697,21 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception
RefPtr<Node> leftContents;
if (m_start.container() != commonRoot) {
- leftContents = processContentsBetweenOffsets(action, 0, m_start.container(), m_start.offset(), lengthOfContentsInNode(), ec);
-
- NodeVector ancestorNodes;
- for (ContainerNode* n = m_start.container()->parentNode(); n && n != commonRoot; n = n->parentNode())
- ancestorNodes.append(n);
- RefPtr<Node> n = m_start.container()->nextSibling();
- for (NodeVector::const_iterator it = ancestorNodes.begin(); it != ancestorNodes.end(); it++) {
- Node* leftParent = it->get();
- if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
- RefPtr<Node> leftContentsParent = leftParent->cloneNode(false);
- if (leftContentsParent) { // Might have been removed already during mutation event.
- leftContentsParent->appendChild(leftContents, ec);
- leftContents = leftContentsParent;
- }
- }
-
- RefPtr<Node> next;
- for (; n; n = next) {
- next = n->nextSibling();
- if (action == EXTRACT_CONTENTS)
- leftContents->appendChild(n.get(), ec); // will remove n from leftParent
- else if (action == CLONE_CONTENTS)
- leftContents->appendChild(n->cloneNode(true), ec);
- else
- leftParent->removeChild(n.get(), ec);
- }
- n = leftParent->nextSibling();
- }
+ leftContents = processContentsBetweenOffsets(action, 0, m_start.container(), m_start.offset(), lengthOfContentsInNode(m_start.container()), ec);
+ leftContents = processAncestorsAndTheirSiblings(action, m_start.container(), ProcessContentsForward, leftContents, commonRoot, ec);
}
RefPtr<Node> rightContents;
if (m_end.container() != commonRoot) {
rightContents = processContentsBetweenOffsets(action, 0, m_end.container(), 0, m_end.offset(), ec);
-
- ContainerNode* 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); // will remove n from its parent
- else if (action == CLONE_CONTENTS)
- rightContents->insertBefore(n->cloneNode(true), rightContents->firstChild(), ec);
- else
- rightParent->removeChild(n, ec);
- }
- n = rightParent->previousSibling();
- }
+ rightContents = processAncestorsAndTheirSiblings(action, m_end.container(), ProcessContentsBackward, rightContents, commonRoot, ec);
}
// delete all children of commonRoot between the start and end container
-
- Node* processStart; // child of commonRoot
- 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();
+ Node* processStart = childOfCommonRootBeforeOffset(m_start.container(), m_start.offset(), commonRoot);
+ if (m_start.container() != commonRoot) // processStart contains nodes before m_start.
processStart = processStart->nextSibling();
- }
- Node* processEnd; // child of commonRoot
- 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();
- }
+ Node* processEnd = childOfCommonRootBeforeOffset(m_end.container(), m_end.offset(), commonRoot);
// Collapse the range, making sure that the result is not within a node that was partially selected.
if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
@@ -757,15 +734,7 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception
NodeVector nodes;
for (Node* n = processStart; n && n != processEnd; n = n->nextSibling())
nodes.append(n);
- for (NodeVector::const_iterator it = nodes.begin(); it != nodes.end(); it++) {
- Node* n = it->get();
- if (action == EXTRACT_CONTENTS)
- fragment->appendChild(n, ec); // will remove from commonRoot
- else if (action == CLONE_CONTENTS)
- fragment->appendChild(n->cloneNode(true), ec);
- else
- commonRoot->removeChild(n, ec);
- }
+ processNodes(action, nodes, commonRoot, fragment, ec);
}
if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents)
@@ -774,26 +743,30 @@ PassRefPtr<DocumentFragment> Range::processContents(ActionType action, Exception
return fragment.release();
}
+static inline void deleteCharacterData(PassRefPtr<CharacterData> data, unsigned startOffset, unsigned endOffset, ExceptionCode& ec)
+{
+ if (data->length() - endOffset)
+ data->deleteData(endOffset, data->length() - endOffset, ec);
+ if (startOffset)
+ data->deleteData(0, startOffset, ec);
+}
+
PassRefPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRefPtr<DocumentFragment> fragment,
Node* container, unsigned startOffset, unsigned endOffset, ExceptionCode& ec)
{
ASSERT(container);
ASSERT(startOffset <= endOffset);
-
- RefPtr<Node> result;
+
+ // This switch statement must be consistent with that of lengthOfContentsInNode.
+ RefPtr<Node> result;
switch (container->nodeType()) {
case Node::TEXT_NODE:
case Node::CDATA_SECTION_NODE:
case Node::COMMENT_NODE:
- ASSERT(endOffset <= static_cast<CharacterData*>(container)->length() || endOffset == lengthOfContentsInNode());
- if (endOffset == lengthOfContentsInNode())
- endOffset = static_cast<CharacterData*>(container)->length();
+ ASSERT(endOffset <= static_cast<CharacterData*>(container)->length());
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<CharacterData> c = static_pointer_cast<CharacterData>(container->cloneNode(true));
- if (c->length() - endOffset)
- c->deleteData(endOffset, c->length() - endOffset, ec);
- if (startOffset)
- c->deleteData(0, startOffset, ec);
+ deleteCharacterData(c, startOffset, endOffset, ec);
if (fragment) {
result = fragment;
result->appendChild(c.release(), ec);
@@ -804,9 +777,7 @@ PassRefPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRef
static_cast<CharacterData*>(container)->deleteData(startOffset, endOffset - startOffset, ec);
break;
case Node::PROCESSING_INSTRUCTION_NODE:
- ASSERT(endOffset <= static_cast<ProcessingInstruction*>(container)->data().length() || endOffset == lengthOfContentsInNode());
- if (endOffset == lengthOfContentsInNode())
- endOffset = static_cast<ProcessingInstruction*>(container)->data().length();
+ ASSERT(endOffset <= static_cast<ProcessingInstruction*>(container)->data().length());
if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
RefPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(container->cloneNode(true));
c->setData(c->data().substring(startOffset, endOffset - startOffset), ec);
@@ -847,23 +818,76 @@ PassRefPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRef
for (unsigned i = startOffset; n && i < endOffset; i++, n = n->nextSibling())
nodes.append(n);
- for (unsigned i = 0; i < nodes.size(); i++) {
+ processNodes(action, nodes, container, result, ec);
+ break;
+ }
+
+ return result;
+}
+
+void Range::processNodes(ActionType action, Vector<RefPtr<Node> >& nodes, PassRefPtr<Node> oldContainer, PassRefPtr<Node> newContainer, ExceptionCode& ec)
+{
+ for (unsigned i = 0; i < nodes.size(); i++) {
+ switch (action) {
+ case DELETE_CONTENTS:
+ oldContainer->removeChild(nodes[i].get(), ec);
+ break;
+ case EXTRACT_CONTENTS:
+ newContainer->appendChild(nodes[i].release(), ec); // will remove n from its parent
+ break;
+ case CLONE_CONTENTS:
+ newContainer->appendChild(nodes[i]->cloneNode(true), ec);
+ break;
+ }
+ }
+}
+
+PassRefPtr<Node> Range::processAncestorsAndTheirSiblings(ActionType action, Node* container, ContentsProcessDirection direction, PassRefPtr<Node> passedClonedContainer, Node* commonRoot, ExceptionCode& ec)
+{
+ RefPtr<Node> clonedContainer = passedClonedContainer;
+ Vector<RefPtr<Node> > ancestors;
+ for (ContainerNode* n = container->parentNode(); n && n != commonRoot; n = n->parentNode())
+ ancestors.append(n);
+
+ RefPtr<Node> firstChildInAncestorToProcess = direction == ProcessContentsForward ? container->nextSibling() : container->previousSibling();
+ for (Vector<RefPtr<Node> >::const_iterator it = ancestors.begin(); it != ancestors.end(); it++) {
+ RefPtr<Node> ancestor = *it;
+ if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
+ if (RefPtr<Node> clonedAncestor = ancestor->cloneNode(false)) { // Might have been removed already during mutation event.
+ clonedAncestor->appendChild(clonedContainer, ec);
+ clonedContainer = clonedAncestor;
+ }
+ }
+
+ // Copy siblings of an ancestor of start/end containers
+ // FIXME: This assertion may fail if DOM is modified during mutation event
+ // FIXME: Share code with Range::processNodes
+ ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor);
+ RefPtr<Node> next;
+ for (Node* child = firstChildInAncestorToProcess.get(); child; child = next.get()) {
+ next = direction == ProcessContentsForward ? child->nextSibling() : child->previousSibling();
switch (action) {
case DELETE_CONTENTS:
- container->removeChild(nodes[i].get(), ec);
+ ancestor->removeChild(child, ec);
break;
- case EXTRACT_CONTENTS:
- result->appendChild(nodes[i].release(), ec); // will remove n from its parent
+ case EXTRACT_CONTENTS: // will remove child from ancestor
+ if (direction == ProcessContentsForward)
+ clonedContainer->appendChild(child, ec);
+ else
+ clonedContainer->insertBefore(child, clonedContainer->firstChild(), ec);
break;
case CLONE_CONTENTS:
- result->appendChild(nodes[i]->cloneNode(true), ec);
+ if (direction == ProcessContentsForward)
+ clonedContainer->appendChild(child->cloneNode(true), ec);
+ else
+ clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild(), ec);
break;
}
}
- break;
+ firstChildInAncestorToProcess = direction == ProcessContentsForward ? ancestor->nextSibling() : ancestor->previousSibling();
}
- return result;
+ return clonedContainer;
}
PassRefPtr<DocumentFragment> Range::extractContents(ExceptionCode& ec)
@@ -1544,7 +1568,7 @@ Position Range::editingStartPosition() const
// It is important to skip certain irrelevant content at the start of the selection, so we do not wind up
// with a spurious "mixed" style.
- VisiblePosition visiblePosition(m_start.container(), m_start.offset(), VP_DEFAULT_AFFINITY);
+ VisiblePosition visiblePosition = Position(m_start.container(), m_start.offset(), Position::PositionIsOffsetInAnchor);
if (visiblePosition.isNull())
return Position();
@@ -1879,22 +1903,8 @@ PassRefPtr<ClientRectList> Range::getClientRects() const
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();
-
- FloatRect result;
- for (size_t i = 0; i < quads.size(); ++i)
- result.unite(quads[i].boundingBox());
-
- return ClientRect::create(result);
+ FloatRect rect = boundingRect();
+ return rect.isEmpty() ? 0 : ClientRect::create(rect);
}
static void adjustFloatQuadsForScrollAndAbsoluteZoom(Vector<FloatQuad>& quads, Document* document, RenderObject* renderer)
@@ -1949,6 +1959,26 @@ void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const
}
}
+
+FloatRect Range::boundingRect() const
+{
+ if (!m_start.container())
+ return FloatRect();
+
+ m_ownerDocument->updateLayoutIgnorePendingStylesheets();
+
+ Vector<FloatQuad> quads;
+ getBorderAndTextQuads(quads);
+ if (quads.isEmpty())
+ return FloatRect();
+
+ FloatRect result;
+ for (size_t i = 0; i < quads.size(); ++i)
+ result.unite(quads[i].boundingBox());
+
+ return result;
+}
+
} // namespace WebCore
#ifndef NDEBUG
@@ -1956,10 +1986,8 @@ void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const
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());
+ range->startContainer()->showTreeAndMark(range->startContainer(), "S", range->endContainer(), "E");
+ fprintf(stderr, "start offset: %d, end offset: %d\n", range->startOffset(), range->endOffset());
}
}