📄 range.cpp
字号:
}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; } // INVALID_NODE_TYPE_ERR: Raised if node is an Attr, Entity, DocumentType, Notation, // Document, or DocumentFragment node. 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; } // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of // the Range is read-only. if (containedByReadOnly()) { ec = NO_MODIFICATION_ALLOWED_ERR; return; } // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were // not created from the same document. if (newParent->document() != m_start.container()->document()) { ec = WRONG_DOCUMENT_ERR; return; } // Raise a HIERARCHY_REQUEST_ERR if m_start.container() doesn't accept children like newParent. Node* parentOfNewParent = m_start.container(); // If m_start.container() is a character data node, it will be split and it will be its parent that will // need to accept newParent (or in the case of a comment, it logically "would" be inserted into the parent, // although this will fail below for another reason). if (parentOfNewParent->isCharacterDataNode()) parentOfNewParent = parentOfNewParent->parentNode(); if (!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; } // FIXME: Do we need a check if the node would end up with a child node of a type not // allowed by the type of node? // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-Text node. 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{ // This function is used by range style computations to avoid bugs like: // <rdar://problem/4017641> REGRESSION (Mail): you can only bold/unbold a selection starting from end of line once // 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); if (visiblePosition.isNull()) return Position(); ExceptionCode ec = 0; // if the selection is a caret, just return the position, since the style // behind us is relevant if (collapsed(ec)) return visiblePosition.deepEquivalent(); // if the selection starts just before a paragraph break, skip over it if (isEndOfParagraph(visiblePosition)) return visiblePosition.next().deepEquivalent().downstream(); // otherwise, make sure to be at the start of the first selected node, // instead of possibly at the end of the last node before the selection 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; 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_start.container() || !m_end.container()) return; RenderObject* start = m_start.container()->renderer(); RenderObject* end = m_end.container()->renderer(); if (!start || !end) return; RenderObject* stop = end->nextInPreOrderAfterChildren(); for (RenderObject* r = start; r && r != stop; r = r->nextInPreOrder()) { // only ask leaf render objects for their line box rects if (!r->firstChild()) { int startOffset = r == start ? m_start.offset() : 0; int endOffset = r == end ? m_end.offset() : INT_MAX; r->absoluteRectsForRange(rects, startOffset, endOffset, useSelectionHeight); } }}#ifndef NDEBUG#define FormatBufferSize 1024void 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#endifbool operator==(const Range& a, const Range& b){ if (&a == &b) return true; // Not strictly legal C++, but in practice this can happen, and this check works // fine with GCC to detect such cases and return false rather than crashing. 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 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.setToChild(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);}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -