1 /* 2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "HTMLNames.h" 28 #include "bindings/v8/ExceptionStatePlaceholder.h" 29 #include "core/dom/Document.h" 30 #include "core/dom/Element.h" 31 #include "core/dom/Range.h" 32 #include "core/editing/FormatBlockCommand.h" 33 #include "core/editing/VisibleUnits.h" 34 #include "core/editing/htmlediting.h" 35 #include "core/html/HTMLElement.h" 36 37 namespace WebCore { 38 39 using namespace HTMLNames; 40 41 static Node* enclosingBlockToSplitTreeTo(Node* startNode); 42 static bool isElementForFormatBlock(const QualifiedName& tagName); 43 static inline bool isElementForFormatBlock(Node* node) 44 { 45 return node->isElementNode() && isElementForFormatBlock(toElement(node)->tagQName()); 46 } 47 48 FormatBlockCommand::FormatBlockCommand(Document& document, const QualifiedName& tagName) 49 : ApplyBlockElementCommand(document, tagName) 50 , m_didApply(false) 51 { 52 } 53 54 void FormatBlockCommand::formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection) 55 { 56 if (!isElementForFormatBlock(tagName())) 57 return; 58 ApplyBlockElementCommand::formatSelection(startOfSelection, endOfSelection); 59 m_didApply = true; 60 } 61 62 void FormatBlockCommand::formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>& blockNode) 63 { 64 Element* refNode = enclosingBlockFlowElement(end); 65 Element* root = editableRootForPosition(start); 66 // Root is null for elements with contenteditable=false. 67 if (!root || !refNode) 68 return; 69 70 Node* nodeToSplitTo = enclosingBlockToSplitTreeTo(start.deprecatedNode()); 71 RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTreeToNode(start.deprecatedNode(), nodeToSplitTo); 72 RefPtr<Node> nodeAfterInsertionPosition = outerBlock; 73 RefPtr<Range> range = Range::create(document(), start, endOfSelection); 74 75 if (isElementForFormatBlock(refNode->tagQName()) && start == startOfBlock(start) 76 && (end == endOfBlock(end) || isNodeVisiblyContainedWithin(refNode, range.get())) 77 && refNode != root && !root->isDescendantOf(refNode)) { 78 // Already in a block element that only contains the current paragraph 79 if (refNode->hasTagName(tagName())) 80 return; 81 nodeAfterInsertionPosition = refNode; 82 } 83 84 if (!blockNode) { 85 // Create a new blockquote and insert it as a child of the root editable element. We accomplish 86 // this by splitting all parents of the current paragraph up to that point. 87 blockNode = createBlockElement(); 88 insertNodeBefore(blockNode, nodeAfterInsertionPosition); 89 } 90 91 Position lastParagraphInBlockNode = blockNode->lastChild() ? positionAfterNode(blockNode->lastChild()) : Position(); 92 bool wasEndOfParagraph = isEndOfParagraph(lastParagraphInBlockNode); 93 94 moveParagraphWithClones(start, end, blockNode.get(), outerBlock.get()); 95 96 // Copy the inline style of the original block element to the newly created block-style element. 97 if (outerBlock.get() != nodeAfterInsertionPosition.get() && toHTMLElement(nodeAfterInsertionPosition.get())->hasAttribute(styleAttr)) 98 blockNode->setAttribute(styleAttr, toHTMLElement(nodeAfterInsertionPosition.get())->getAttribute(styleAttr)); 99 100 if (wasEndOfParagraph && !isEndOfParagraph(lastParagraphInBlockNode) && !isStartOfParagraph(lastParagraphInBlockNode)) 101 insertBlockPlaceholder(lastParagraphInBlockNode); 102 } 103 104 Element* FormatBlockCommand::elementForFormatBlockCommand(Range* range) 105 { 106 if (!range) 107 return 0; 108 109 Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION); 110 while (commonAncestor && !isElementForFormatBlock(commonAncestor)) 111 commonAncestor = commonAncestor->parentNode(); 112 113 if (!commonAncestor) 114 return 0; 115 116 Element* rootEditableElement = range->startContainer()->rootEditableElement(); 117 if (!rootEditableElement || commonAncestor->contains(rootEditableElement)) 118 return 0; 119 120 return commonAncestor->isElementNode() ? toElement(commonAncestor) : 0; 121 } 122 123 bool isElementForFormatBlock(const QualifiedName& tagName) 124 { 125 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, blockTags, ()); 126 if (blockTags.isEmpty()) { 127 blockTags.add(addressTag); 128 blockTags.add(articleTag); 129 blockTags.add(asideTag); 130 blockTags.add(blockquoteTag); 131 blockTags.add(ddTag); 132 blockTags.add(divTag); 133 blockTags.add(dlTag); 134 blockTags.add(dtTag); 135 blockTags.add(footerTag); 136 blockTags.add(h1Tag); 137 blockTags.add(h2Tag); 138 blockTags.add(h3Tag); 139 blockTags.add(h4Tag); 140 blockTags.add(h5Tag); 141 blockTags.add(h6Tag); 142 blockTags.add(headerTag); 143 blockTags.add(hgroupTag); 144 blockTags.add(mainTag); 145 blockTags.add(navTag); 146 blockTags.add(pTag); 147 blockTags.add(preTag); 148 blockTags.add(sectionTag); 149 } 150 return blockTags.contains(tagName); 151 } 152 153 Node* enclosingBlockToSplitTreeTo(Node* startNode) 154 { 155 Node* lastBlock = startNode; 156 for (Node* n = startNode; n; n = n->parentNode()) { 157 if (!n->rendererIsEditable()) 158 return lastBlock; 159 if (isTableCell(n) || n->hasTagName(bodyTag) || !n->parentNode() || !n->parentNode()->rendererIsEditable() || isElementForFormatBlock(n)) 160 return n; 161 if (isBlock(n)) 162 lastBlock = n; 163 if (isListElement(n)) 164 return n->parentNode()->rendererIsEditable() ? n->parentNode() : n; 165 } 166 return lastBlock; 167 } 168 169 } 170