compositeeditcommand.cpp
来自「linux下开源浏览器WebKit的源码,市面上的很多商用浏览器都是移植自Web」· C++ 代码 · 共 1,041 行 · 第 1/3 页
CPP
1,041 行
void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements){ if (selection.isRange()) applyCommandToComposite(DeleteSelectionCommand::create(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements));}void CompositeEditCommand::removeCSSProperty(PassRefPtr<CSSMutableStyleDeclaration> style, CSSPropertyID property){ applyCommandToComposite(RemoveCSSPropertyCommand::create(document(), style, property));}void CompositeEditCommand::removeNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute){ setNodeAttribute(element, attribute, AtomicString());}void CompositeEditCommand::setNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute, const AtomicString& value){ applyCommandToComposite(SetNodeAttributeCommand::create(element, attribute, value));}static inline bool isWhitespace(UChar c){ return c == noBreakSpace || c == ' ' || c == '\n' || c == '\t';}// FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc).void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position){ Node* node = position.node(); if (!node || !node->isTextNode()) return; Text* textNode = static_cast<Text*>(node); if (textNode->length() == 0) return; RenderObject* renderer = textNode->renderer(); if (renderer && !renderer->style()->collapseWhiteSpace()) return; String text = textNode->data(); ASSERT(!text.isEmpty()); int offset = position.offset(); // If neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing. if (!isWhitespace(text[offset])) { offset--; if (offset < 0 || !isWhitespace(text[offset])) return; } // Set upstream and downstream to define the extent of the whitespace surrounding text[offset]. int upstream = offset; while (upstream > 0 && isWhitespace(text[upstream - 1])) upstream--; int downstream = offset; while ((unsigned)downstream + 1 < text.length() && isWhitespace(text[downstream + 1])) downstream++; int length = downstream - upstream + 1; ASSERT(length > 0); VisiblePosition visibleUpstreamPos(Position(position.node(), upstream)); VisiblePosition visibleDownstreamPos(Position(position.node(), downstream + 1)); String string = text.substring(upstream, length); String rebalancedString = stringWithRebalancedWhitespace(string, // FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because // this function doesn't get all surrounding whitespace, just the whitespace in the current text node. isStartOfParagraph(visibleUpstreamPos) || upstream == 0, isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length() - 1); if (string != rebalancedString) replaceTextInNode(textNode, upstream, length, rebalancedString);}void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position){ Node* node = position.node(); if (!node || !node->isTextNode()) return; Text* textNode = static_cast<Text*>(node); if (textNode->length() == 0) return; RenderObject* renderer = textNode->renderer(); if (renderer && !renderer->style()->collapseWhiteSpace()) return; // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it. Position upstreamPos = position.upstream(); deleteInsignificantText(position.upstream(), position.downstream()); position = upstreamPos.downstream(); VisiblePosition visiblePos(position); VisiblePosition previousVisiblePos(visiblePos.previous()); Position previous(previousVisiblePos.deepEquivalent()); if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous.node()->isTextNode() && !previous.node()->hasTagName(brTag)) replaceTextInNode(static_cast<Text*>(previous.node()), previous.offset(), 1, nonBreakingSpaceString()); if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.node()->isTextNode() && !position.node()->hasTagName(brTag)) replaceTextInNode(static_cast<Text*>(position.node()), position.offset(), 1, nonBreakingSpaceString());}void CompositeEditCommand::rebalanceWhitespace(){ VisibleSelection selection = endingSelection(); if (selection.isNone()) return; rebalanceWhitespaceAt(selection.start()); if (selection.isRange()) rebalanceWhitespaceAt(selection.end());}void CompositeEditCommand::deleteInsignificantText(PassRefPtr<Text> textNode, unsigned start, unsigned end){ if (!textNode || start >= end) return; RenderText* textRenderer = toRenderText(textNode->renderer()); if (!textRenderer) return; InlineTextBox* box = textRenderer->firstTextBox(); if (!box) { // whole text node is empty removeNode(textNode); return; } unsigned length = textNode->length(); if (start >= length || end > length) return; unsigned removed = 0; InlineTextBox* prevBox = 0; String str; // This loop structure works to process all gaps preceding a box, // and also will look at the gap after the last box. while (prevBox || box) { unsigned gapStart = prevBox ? prevBox->start() + prevBox->len() : 0; if (end < gapStart) // No more chance for any intersections break; unsigned gapEnd = box ? box->start() : length; bool indicesIntersect = start <= gapEnd && end >= gapStart; int gapLen = gapEnd - gapStart; if (indicesIntersect && gapLen > 0) { gapStart = max(gapStart, start); gapEnd = min(gapEnd, end); if (str.isNull()) str = textNode->string()->substring(start, end - start); // remove text in the gap str.remove(gapStart - start - removed, gapLen); removed += gapLen; } prevBox = box; if (box) box = box->nextTextBox(); } if (!str.isNull()) { // Replace the text between start and end with our pruned version. if (!str.isEmpty()) replaceTextInNode(textNode, start, end - start, str); else { // Assert that we are not going to delete all of the text in the node. // If we were, that should have been done above with the call to // removeNode and return. ASSERT(start > 0 || end - start < textNode->length()); deleteTextFromNode(textNode, start, end - start); } }}void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end){ if (start.isNull() || end.isNull()) return; if (Range::compareBoundaryPoints(start, end) >= 0) return; Node* next; for (Node* node = start.node(); node; node = next) { next = node->traverseNextNode(); if (node->isTextNode()) { Text* textNode = static_cast<Text*>(node); int startOffset = node == start.node() ? start.offset() : 0; int endOffset = node == end.node() ? end.offset() : textNode->length(); deleteInsignificantText(textNode, startOffset, endOffset); } if (node == end.node()) break; }}void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos){ Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream(); deleteInsignificantText(pos, end);}PassRefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(PassRefPtr<Element> container){ if (!container) return 0; // Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964. ASSERT(container->renderer()); RefPtr<Node> placeholder = createBlockPlaceholderElement(document()); appendNode(placeholder, container); return placeholder.release();}PassRefPtr<Node> CompositeEditCommand::insertBlockPlaceholder(const Position& pos){ if (pos.isNull()) return 0; // Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964. ASSERT(pos.node()->renderer()); RefPtr<Node> placeholder = createBlockPlaceholderElement(document()); insertNodeAt(placeholder, pos); return placeholder.release();}PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* container){ if (!container) return 0; updateLayout(); RenderObject* renderer = container->renderer(); if (!renderer || !renderer->isBlockFlow()) return 0; // append the placeholder to make sure it follows // any unrendered blocks RenderBlock* block = toRenderBlock(renderer); if (block->height() == 0 || (block->isListItem() && block->isEmpty())) return appendBlockPlaceholder(container); return 0;}// Removes '\n's and brs that will collapse when content is inserted just before them.// FIXME: We shouldn't really have to remove placeholders, but removing them is a workaround for 9661.void CompositeEditCommand::removePlaceholderAt(const VisiblePosition& visiblePosition){ if (visiblePosition.isNull()) return; Position p = visiblePosition.deepEquivalent().downstream(); // If a br or '\n' is at the end of a block and not at the start of a paragraph, // then it is superfluous, so adding content before a br or '\n' that is at // the start of a paragraph will render it superfluous. // FIXME: This doesn't remove placeholders at the end of anonymous blocks. if (isEndOfBlock(visiblePosition) && isStartOfParagraph(visiblePosition)) { if (p.node()->hasTagName(brTag) && p.offset() == 0) removeNode(p.node()); else if (lineBreakExistsAtPosition(visiblePosition)) deleteTextFromNode(static_cast<Text*>(p.node()), p.offset(), 1); }}PassRefPtr<Node> CompositeEditCommand::insertNewDefaultParagraphElementAt(const Position& position){ RefPtr<Element> paragraphElement = createDefaultParagraphElement(document()); ExceptionCode ec; paragraphElement->appendChild(createBreakElement(document()), ec); insertNodeAt(paragraphElement, position); return paragraphElement.release();}// If the paragraph is not entirely within it's own block, create one and move the paragraph into // it, and return that block. Otherwise return 0.PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos){ if (pos.isNull()) return 0; updateLayout(); // It's strange that this function is responsible for verifying that pos has not been invalidated // by an earlier call to this function. The caller, applyBlockStyle, should do this. VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY); VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos)); VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos); VisiblePosition next = visibleParagraphEnd.next(); VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd; Position upstreamStart = visibleParagraphStart.deepEquivalent().upstream(); Position upstreamEnd = visibleEnd.deepEquivalent().upstream(); // If there are no VisiblePositions in the same block as pos then // upstreamStart will be outside the paragraph if (Range::compareBoundaryPoints(pos, upstreamStart) < 0) return 0; // Perform some checks to see if we need to perform work in this function. if (isBlock(upstreamStart.node())) { // If the block is the root editable element, always move content to a new block, // since it is illegal to modify attributes on the root editable element for editing. if (upstreamStart.node() == editableRootForPosition(upstreamStart)) { // If the block is the root editable element and there is nothing insde of it, create a new // block but don't try and move content into it, since there's nothing to move. if (upstreamStart == upstreamEnd) return insertNewDefaultParagraphElementAt(upstreamStart); } else if (isBlock(upstreamEnd.node())) { if (!upstreamEnd.node()->isDescendantOf(upstreamStart.node())) { // If the paragraph end is a descendant of paragraph start, then we need to run // the rest of this function. If not, we can bail here. return 0; } } else if (enclosingBlock(upstreamEnd.node()) != upstreamStart.node()) { // The visibleEnd. It must be an ancestor of the paragraph start. // We can bail as we have a full block to work with. ASSERT(upstreamStart.node()->isDescendantOf(enclosingBlock(upstreamEnd.node()))); return 0; } else if (isEndOfDocument(visibleEnd)) { // At the end of the document. We can bail here as well. return 0; } } RefPtr<Node> newBlock = insertNewDefaultParagraphElementAt(upstreamStart); moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(Position(newBlock.get(), 0))); return newBlock.release();}void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode){ if (!anchorNode) return;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?