diff options
Diffstat (limited to 'src/3rdparty/webkit/WebCore/editing/IndentOutdentCommand.cpp')
-rw-r--r-- | src/3rdparty/webkit/WebCore/editing/IndentOutdentCommand.cpp | 169 |
1 files changed, 96 insertions, 73 deletions
diff --git a/src/3rdparty/webkit/WebCore/editing/IndentOutdentCommand.cpp b/src/3rdparty/webkit/WebCore/editing/IndentOutdentCommand.cpp index 890cff233..42dd515fb 100644 --- a/src/3rdparty/webkit/WebCore/editing/IndentOutdentCommand.cpp +++ b/src/3rdparty/webkit/WebCore/editing/IndentOutdentCommand.cpp @@ -33,6 +33,7 @@ #include "InsertLineBreakCommand.h" #include "InsertListCommand.h" #include "Range.h" +#include "DocumentFragment.h" #include "SplitElementCommand.h" #include "TextIterator.h" #include "htmlediting.h" @@ -57,18 +58,9 @@ static PassRefPtr<HTMLBlockquoteElement> createIndentBlockquoteElement(Document* return element.release(); } -static bool isIndentBlockquote(const Node* node) -{ - if (!node || !node->hasTagName(blockquoteTag) || !node->isElementNode()) - return false; - - const Element* elem = static_cast<const Element*>(node); - return elem->getAttribute(classAttr) == indentBlockquoteString(); -} - static bool isListOrIndentBlockquote(const Node* node) { - return node && (node->hasTagName(ulTag) || node->hasTagName(olTag) || isIndentBlockquote(node)); + return node && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(blockquoteTag)); } IndentOutdentCommand::IndentOutdentCommand(Document* document, EIndentType typeOfAction, int marginInPixels) @@ -76,37 +68,6 @@ IndentOutdentCommand::IndentOutdentCommand(Document* document, EIndentType typeO { } -// This function is a workaround for moveParagraph's tendency to strip blockquotes. It updates lastBlockquote to point to the -// correct level for the current paragraph, and returns a pointer to a placeholder br where the insertion should be performed. -PassRefPtr<Element> IndentOutdentCommand::prepareBlockquoteLevelForInsertion(const VisiblePosition& currentParagraph, RefPtr<Element>& lastBlockquote) -{ - int currentBlockquoteLevel = 0; - int lastBlockquoteLevel = 0; - Node* node = currentParagraph.deepEquivalent().node(); - while ((node = enclosingNodeOfType(Position(node->parentNode(), 0), &isIndentBlockquote))) - currentBlockquoteLevel++; - node = lastBlockquote.get(); - while ((node = enclosingNodeOfType(Position(node->parentNode(), 0), &isIndentBlockquote))) - lastBlockquoteLevel++; - while (currentBlockquoteLevel > lastBlockquoteLevel) { - RefPtr<Element> newBlockquote = createIndentBlockquoteElement(document()); - appendNode(newBlockquote, lastBlockquote); - lastBlockquote = newBlockquote; - lastBlockquoteLevel++; - } - while (currentBlockquoteLevel < lastBlockquoteLevel) { - lastBlockquote = static_cast<Element*>(enclosingNodeOfType(Position(lastBlockquote->parentNode(), 0), isIndentBlockquote)); - lastBlockquoteLevel--; - } - RefPtr<Element> placeholder = createBreakElement(document()); - appendNode(placeholder, lastBlockquote); - // Add another br before the placeholder if it collapsed. - VisiblePosition visiblePos(Position(placeholder.get(), 0)); - if (!isStartOfParagraph(visiblePos)) - insertNodeBefore(createBreakElement(document()), placeholder); - return placeholder.release(); -} - bool IndentOutdentCommand::tryIndentingAsListItem(const VisiblePosition& endOfCurrentParagraph) { // If our selection is not inside a list, bail out. @@ -115,20 +76,19 @@ bool IndentOutdentCommand::tryIndentingAsListItem(const VisiblePosition& endOfCu if (!listNode) return false; - HTMLElement* selectedListItem = enclosingListChild(lastNodeInSelectedParagraph); + // Find the list item enclosing the current paragraph + Element* selectedListItem = static_cast<Element*>(enclosingBlock(endOfCurrentParagraph.deepEquivalent().node())); + // FIXME: we need to deal with the case where there is no li (malformed HTML) + if (!selectedListItem->hasTagName(liTag)) + return false; // FIXME: previousElementSibling does not ignore non-rendered content like <span></span>. Should we? Element* previousList = selectedListItem->previousElementSibling(); Element* nextList = selectedListItem->nextElementSibling(); RefPtr<Element> newList = document()->createElement(listNode->tagQName(), false); - RefPtr<Element> newListItem = selectedListItem->cloneElementWithoutChildren(); - RefPtr<Element> placeholder = createBreakElement(document()); insertNodeBefore(newList, selectedListItem); - appendNode(newListItem, newList); - appendNode(placeholder, newListItem); - - moveParagraph(startOfParagraph(endOfCurrentParagraph), endOfCurrentParagraph, VisiblePosition(Position(placeholder, 0)), true); + appendParagraphIntoNode(visiblePositionBeforeNode(selectedListItem), visiblePositionAfterNode(selectedListItem), newList.get()); if (canMergeLists(previousList, newList.get())) mergeIdenticalElements(previousList, newList); @@ -138,29 +98,27 @@ bool IndentOutdentCommand::tryIndentingAsListItem(const VisiblePosition& endOfCu return true; } -void IndentOutdentCommand::indentIntoBlockquote(const VisiblePosition& endOfCurrentParagraph, const VisiblePosition& endOfNextParagraph, RefPtr<Element>& targetBlockquote) +void IndentOutdentCommand::indentIntoBlockquote(const VisiblePosition& startOfCurrentParagraph, const VisiblePosition& endOfCurrentParagraph, RefPtr<Element>& targetBlockquote, Node* nodeToSplitTo) { Node* enclosingCell = 0; if (!targetBlockquote) { - // Create a new blockquote and insert it as a child of the root editable element. We accomplish + // Create a new blockquote and insert it as a child of the enclosing block element. We accomplish // this by splitting all parents of the current paragraph up to that point. targetBlockquote = createIndentBlockquoteElement(document()); - Position start = startOfParagraph(endOfCurrentParagraph).deepEquivalent(); - enclosingCell = enclosingNodeOfType(start, &isTableCell); - Node* nodeToSplitTo = enclosingCell ? enclosingCell : editableRootForPosition(start); - RefPtr<Node> startOfNewBlock = splitTreeToNode(start.node(), nodeToSplitTo); + if (isTableCell(nodeToSplitTo)) + enclosingCell = nodeToSplitTo; + RefPtr<Node> startOfNewBlock = splitTreeToNode(startOfCurrentParagraph.deepEquivalent().node(), nodeToSplitTo); insertNodeBefore(targetBlockquote, startOfNewBlock); } - RefPtr<Element> insertionPoint = prepareBlockquoteLevelForInsertion(endOfCurrentParagraph, targetBlockquote); + VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next()); + appendParagraphIntoNode(startOfCurrentParagraph, endOfCurrentParagraph, targetBlockquote.get()); // Don't put the next paragraph in the blockquote we just created for this paragraph unless // the next paragraph is in the same cell. if (enclosingCell && enclosingCell != enclosingNodeOfType(endOfNextParagraph.deepEquivalent(), &isTableCell)) targetBlockquote = 0; - - moveParagraph(startOfParagraph(endOfCurrentParagraph), endOfCurrentParagraph, VisiblePosition(Position(insertionPoint, 0)), true); } bool IndentOutdentCommand::isAtUnsplittableElement(const Position& pos) const @@ -169,13 +127,64 @@ bool IndentOutdentCommand::isAtUnsplittableElement(const Position& pos) const return node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell); } +// Enclose all nodes between start and end by newParent, which is a sibling node of nodes between start and end +// FIXME: moveParagraph is overly complicated. We need to clean up moveParagraph so that it uses appendParagraphIntoNode +// or prepare more specialized functions and delete moveParagraph +void IndentOutdentCommand::appendParagraphIntoNode(const VisiblePosition& start, const VisiblePosition& end, Node* newParent) +{ + ASSERT(newParent); + ASSERT(newParent->isContentEditable()); + ASSERT(isStartOfParagraph(start) && isEndOfParagraph(end)); + + Position endOfParagraph = end.deepEquivalent().downstream(); + Node* insertionPoint = newParent->lastChild();// Remember the place to put br later + // Look for the beginning of the last paragraph in newParent + Node* startOfLastParagraph = startOfParagraph(Position(newParent, newParent->childNodeCount())).deepEquivalent().node(); + if (startOfLastParagraph && !startOfLastParagraph->isDescendantOf(newParent)) + startOfLastParagraph = 0; + + // Extend the range so that we can append wrapping nodes as well if they're containd within the paragraph + ExceptionCode ec = 0; + RefPtr<Range> selectedRange = createRange(document(), start, end, ec); + RefPtr<Range> extendedRange = extendRangeToWrappingNodes(selectedRange, selectedRange.get(), newParent->parentNode()); + newParent->appendChild(extendedRange->extractContents(ec), ec); + + // If the start of paragraph didn't change by appending nodes, we should insert br to seperate the paragraphs. + Node* startOfNewParagraph = startOfParagraph(Position(newParent, newParent->childNodeCount())).deepEquivalent().node(); + if (startOfNewParagraph == startOfLastParagraph) { + if (insertionPoint) + newParent->insertBefore(createBreakElement(document()), insertionPoint->nextSibling(), ec); + else + newParent->appendChild(createBreakElement(document()), ec); + } + + // Remove unnecessary br from the place where we moved the paragraph from + removeUnnecessaryLineBreakAt(endOfParagraph); +} + +void IndentOutdentCommand::removeUnnecessaryLineBreakAt(const Position& endOfParagraph) +{ + // If there is something in this paragraph, then don't remove br. + if (!isStartOfParagraph(endOfParagraph) || !isEndOfParagraph(endOfParagraph)) + return; + + // We only care about br at the end of paragraph + Node* br = endOfParagraph.node(); + Node* parentNode = br->parentNode(); + + // If the node isn't br or the parent node is empty, then don't remove. + if (!br->hasTagName(brTag) || isVisiblyAdjacent(positionBeforeNode(parentNode), positionAfterNode(parentNode))) + return; + + removeNodeAndPruneAncestors(br); +} + void IndentOutdentCommand::indentRegion() { VisibleSelection selection = selectionForParagraphIteration(endingSelection()); VisiblePosition startOfSelection = selection.visibleStart(); VisiblePosition endOfSelection = selection.visibleEnd(); - int startIndex = indexForVisiblePosition(startOfSelection); - int endIndex = indexForVisiblePosition(endOfSelection); + RefPtr<Range> selectedRange = selection.firstRange(); ASSERT(!startOfSelection.isNull()); ASSERT(!endOfSelection.isNull()); @@ -200,10 +209,24 @@ void IndentOutdentCommand::indentRegion() VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next()); if (tryIndentingAsListItem(endOfCurrentParagraph)) blockquoteForNextIndent = 0; - else - indentIntoBlockquote(endOfCurrentParagraph, endOfNextParagraph, blockquoteForNextIndent); - // blockquoteForNextIndent maybe updated - // this is due to the way prepareBlockquoteLevelForInsertion was designed. + else { + VisiblePosition startOfCurrentParagraph = startOfParagraph(endOfCurrentParagraph); + Node* blockNode = enclosingBlock(endOfCurrentParagraph.deepEquivalent().node()); + // extend the region so that it contains all the ancestor blocks within the selection + ExceptionCode ec; + Element* unsplittableNode = unsplittableElementForPosition(endOfCurrentParagraph.deepEquivalent()); + RefPtr<Range> originalRange = createRange(document(), endOfCurrentParagraph, endOfCurrentParagraph, ec); + RefPtr<Range> extendedRange = extendRangeToWrappingNodes(originalRange, selectedRange.get(), unsplittableNode); + if (originalRange != extendedRange) { + ExceptionCode ec = 0; + endOfCurrentParagraph = endOfParagraph(extendedRange->endPosition().previous()); + blockNode = enclosingBlock(extendedRange->commonAncestorContainer(ec)); + } + + endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next()); + indentIntoBlockquote(startOfCurrentParagraph, endOfCurrentParagraph, blockquoteForNextIndent, blockNode); + // blockquoteForNextIndent will be updated in the function + } // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextParagraph.deepEquivalent().node() // If somehow we did, return to prevent crashes. if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().node()->inDocument()) { @@ -212,11 +235,7 @@ void IndentOutdentCommand::indentRegion() } endOfCurrentParagraph = endOfNextParagraph; } - - RefPtr<Range> startRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), startIndex, 0, true); - RefPtr<Range> endRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endIndex, 0, true); - if (startRange && endRange) - setEndingSelection(VisibleSelection(startRange->startPosition(), endRange->startPosition(), DOWNSTREAM)); + } void IndentOutdentCommand::outdentParagraph() @@ -238,7 +257,7 @@ void IndentOutdentCommand::outdentParagraph() return; } - // The selection is inside a blockquote + // The selection is inside a blockquote i.e. enclosingNode is a blockquote VisiblePosition positionInEnclosingBlock = VisiblePosition(Position(enclosingNode, 0)); VisiblePosition startOfEnclosingBlock = startOfBlock(positionInEnclosingBlock); VisiblePosition lastPositionInEnclosingBlock = VisiblePosition(Position(enclosingNode, enclosingNode->childNodeCount())); @@ -252,8 +271,8 @@ void IndentOutdentCommand::outdentParagraph() // just removed one, then this assumption isn't true. By splitting the next containing blockquote after this node, we keep this assumption true if (splitPoint) { if (Node* splitPointParent = splitPoint->parentNode()) { - if (isIndentBlockquote(splitPointParent) - && !isIndentBlockquote(splitPoint) + if (splitPointParent->hasTagName(blockquoteTag) + && !splitPoint->hasTagName(blockquoteTag) && isContentEditable(splitPointParent->parentNode())) // We can't outdent if there is no place to go! splitElement(static_cast<Element*>(splitPointParent), splitPoint); } @@ -269,10 +288,14 @@ void IndentOutdentCommand::outdentParagraph() return; } - Node* enclosingBlockFlow = enclosingBlockFlowElement(visibleStartOfParagraph); + Node* enclosingBlockFlow = enclosingBlock(visibleStartOfParagraph.deepEquivalent().node()); RefPtr<Node> splitBlockquoteNode = enclosingNode; if (enclosingBlockFlow != enclosingNode) - splitBlockquoteNode = splitTreeToNode(enclosingBlockFlowElement(visibleStartOfParagraph), enclosingNode, true); + splitBlockquoteNode = splitTreeToNode(enclosingBlockFlow, enclosingNode, true); + else { + // We split the blockquote at where we start outdenting. + splitElement(static_cast<Element*>(enclosingNode), visibleStartOfParagraph.deepEquivalent().node()); + } RefPtr<Node> placeholder = createBreakElement(document()); insertNodeBefore(placeholder, splitBlockquoteNode); moveParagraph(startOfParagraph(visibleStartOfParagraph), endOfParagraph(visibleEndOfParagraph), VisiblePosition(Position(placeholder.get(), 0)), true); |