📄 htmlediting.cpp
字号:
/* * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "config.h"#include "htmlediting.h"#include "CharacterNames.h"#include "Document.h"#include "EditingText.h"#include "HTMLBRElement.h"#include "HTMLDivElement.h"#include "HTMLElementFactory.h"#include "HTMLInterchange.h"#include "HTMLLIElement.h"#include "HTMLNames.h"#include "HTMLOListElement.h"#include "HTMLUListElement.h"#include "PositionIterator.h"#include "RenderObject.h"#include "Range.h"#include "VisibleSelection.h"#include "Text.h"#include "TextIterator.h"#include "VisiblePosition.h"#include "visible_units.h"#include <wtf/StdLibExtras.h>#if ENABLE(WML)#include "WMLNames.h"#endifusing namespace std;namespace WebCore {using namespace HTMLNames;// Atomic means that the node has no children, or has children which are ignored for the// purposes of editing.bool isAtomicNode(const Node *node){ return node && (!node->hasChildNodes() || editingIgnoresContent(node));}// Returns true for nodes that either have no content, or have content that is ignored (skipped // over) while editing. There are no VisiblePositions inside these nodes.bool editingIgnoresContent(const Node* node){ return !canHaveChildrenForEditing(node) && !node->isTextNode();}bool canHaveChildrenForEditing(const Node* node){ return !node->hasTagName(hrTag) && !node->hasTagName(brTag) && !node->hasTagName(imgTag) && !node->hasTagName(buttonTag) && !node->hasTagName(inputTag) && !node->hasTagName(textareaTag) && !node->hasTagName(objectTag) && !node->hasTagName(iframeTag) && !node->hasTagName(embedTag) && !node->hasTagName(appletTag) && !node->hasTagName(selectTag) &&#if ENABLE(WML) !node->hasTagName(WMLNames::doTag) &&#endif !node->isTextNode();}// Compare two positions, taking into account the possibility that one or both// could be inside a shadow tree. Only works for non-null values.int comparePositions(const Position& a, const Position& b){ Node* nodeA = a.node(); ASSERT(nodeA); Node* nodeB = b.node(); ASSERT(nodeB); int offsetA = a.offset(); int offsetB = b.offset(); Node* shadowAncestorA = nodeA->shadowAncestorNode(); if (shadowAncestorA == nodeA) shadowAncestorA = 0; Node* shadowAncestorB = nodeB->shadowAncestorNode(); if (shadowAncestorB == nodeB) shadowAncestorB = 0; int bias = 0; if (shadowAncestorA != shadowAncestorB) { if (shadowAncestorA) { nodeA = shadowAncestorA; offsetA = 0; bias = 1; } if (shadowAncestorB) { nodeB = shadowAncestorB; offsetB = 0; bias = -1; } } int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB); return result ? result : bias;}Node* highestEditableRoot(const Position& position){ Node* node = position.node(); if (!node) return 0; Node* highestRoot = editableRootForPosition(position); if (!highestRoot) return 0; node = highestRoot; while (node) { if (node->isContentEditable()) highestRoot = node; if (node->hasTagName(bodyTag)) break; node = node->parentNode(); } return highestRoot;}Node* lowestEditableAncestor(Node* node){ if (!node) return 0; Node *lowestRoot = 0; while (node) { if (node->isContentEditable()) return node->rootEditableElement(); if (node->hasTagName(bodyTag)) break; node = node->parentNode(); } return lowestRoot;}bool isEditablePosition(const Position& p){ Node* node = p.node(); if (!node) return false; if (node->renderer() && node->renderer()->isTable()) node = node->parentNode(); return node->isContentEditable();}bool isRichlyEditablePosition(const Position& p){ Node* node = p.node(); if (!node) return false; if (node->renderer() && node->renderer()->isTable()) node = node->parentNode(); return node->isContentRichlyEditable();}Element* editableRootForPosition(const Position& p){ Node* node = p.node(); if (!node) return 0; if (node->renderer() && node->renderer()->isTable()) node = node->parentNode(); return node->rootEditableElement();}bool isContentEditable(const Node* node){ return node->isContentEditable();}Position nextCandidate(const Position& position){ PositionIterator p = position; while (!p.atEnd()) { p.increment(); if (p.isCandidate()) return p; } return Position();}Position nextVisuallyDistinctCandidate(const Position& position){ Position p = position; Position downstreamStart = p.downstream(); while (!p.atEnd()) { p = p.next(Character); if (p.isCandidate() && p.downstream() != downstreamStart) return p; } return Position();}Position previousCandidate(const Position& position){ PositionIterator p = position; while (!p.atStart()) { p.decrement(); if (p.isCandidate()) return p; } return Position();}Position previousVisuallyDistinctCandidate(const Position& position){ Position p = position; Position downstreamStart = p.downstream(); while (!p.atStart()) { p = p.previous(Character); if (p.isCandidate() && p.downstream() != downstreamStart) return p; } return Position();}VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot){ // position falls before highestRoot. if (comparePositions(position, Position(highestRoot, 0)) == -1 && highestRoot->isContentEditable()) return VisiblePosition(Position(highestRoot, 0)); Position p = position; if (Node* shadowAncestor = p.node()->shadowAncestorNode()) if (shadowAncestor != p.node()) p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p); if (p.node() && !p.node()->isDescendantOf(highestRoot)) return VisiblePosition(); return VisiblePosition(p);}VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot){ // When position falls after highestRoot, the result is easy to compute. if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1) return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot))); Position p = position; if (Node* shadowAncestor = p.node()->shadowAncestorNode()) if (shadowAncestor != p.node()) p = Position(shadowAncestor, 0); while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p); if (p.node() && !p.node()->isDescendantOf(highestRoot)) return VisiblePosition(); return VisiblePosition(p);}// Whether or not content before and after this node will collapse onto the same line as it.bool isBlock(const Node* node){ return node && node->renderer() && !node->renderer()->isInline();}// FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.// FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the // block that contains the table and not the table, and this function should be the only one responsible for // knowing about these kinds of special cases.Node* enclosingBlock(Node* node){ return static_cast<Element*>(enclosingNodeOfType(Position(node, 0), isBlock));}// Internally editing uses "invalid" positions for historical reasons. For// example, in <div><img /></div>, Editing might use (img, 1) for the position// after <img>, but we have to convert that to (div, 1) before handing the// position to a Range object. Ideally all internal positions should// be "range compliant" for simplicity.Position rangeCompliantEquivalent(const Position& pos){ if (pos.isNull()) return Position(); Node* node = pos.node(); if (pos.offset() <= 0) { if (node->parentNode() && (editingIgnoresContent(node) || isTableElement(node))) return positionBeforeNode(node); return Position(node, 0); } if (node->offsetInCharacters()) return Position(node, min(node->maxCharacterOffset(), pos.offset())); int maxCompliantOffset = node->childNodeCount(); if (pos.offset() > maxCompliantOffset) { if (node->parentNode()) return positionAfterNode(node);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -