1/* 2 * Copyright (C) 2012 Apple 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 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 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 "SimplifyMarkupCommand.h" 28 29#include "NodeRenderStyle.h" 30#include "NodeTraversal.h" 31#include "RenderInline.h" 32#include "RenderObject.h" 33#include "RenderStyle.h" 34 35namespace WebCore { 36 37SimplifyMarkupCommand::SimplifyMarkupCommand(Document& document, Node* firstNode, Node* nodeAfterLast) 38 : CompositeEditCommand(document) 39 , m_firstNode(firstNode) 40 , m_nodeAfterLast(nodeAfterLast) 41{ 42} 43 44void SimplifyMarkupCommand::doApply() 45{ 46 Node* rootNode = m_firstNode->parentNode(); 47 Vector<RefPtr<Node>> nodesToRemove; 48 49 document().updateLayoutIgnorePendingStylesheets(); 50 51 // Walk through the inserted nodes, to see if there are elements that could be removed 52 // without affecting the style. The goal is to produce leaner markup even when starting 53 // from a verbose fragment. 54 // We look at inline elements as well as non top level divs that don't have attributes. 55 for (Node* node = m_firstNode.get(); node && node != m_nodeAfterLast; node = NodeTraversal::next(node)) { 56 if (node->firstChild() || (node->isTextNode() && node->nextSibling())) 57 continue; 58 59 Node* startingNode = node->parentNode(); 60 RenderStyle* startingStyle = startingNode->renderStyle(); 61 if (!startingStyle) 62 continue; 63 Node* currentNode = startingNode; 64 Node* topNodeWithStartingStyle = 0; 65 while (currentNode != rootNode) { 66 if (currentNode->parentNode() != rootNode && isRemovableBlock(currentNode)) 67 nodesToRemove.append(currentNode); 68 69 currentNode = currentNode->parentNode(); 70 if (!currentNode) 71 break; 72 73 if (!currentNode->renderer() || !currentNode->renderer()->isRenderInline() || toRenderInline(currentNode->renderer())->alwaysCreateLineBoxes()) 74 continue; 75 76 if (currentNode->firstChild() != currentNode->lastChild()) { 77 topNodeWithStartingStyle = 0; 78 break; 79 } 80 81 unsigned context; 82 if (currentNode->renderStyle()->diff(startingStyle, context) == StyleDifferenceEqual) 83 topNodeWithStartingStyle = currentNode; 84 85 } 86 if (topNodeWithStartingStyle) { 87 for (Node* node = startingNode; node != topNodeWithStartingStyle; node = node->parentNode()) 88 nodesToRemove.append(node); 89 } 90 } 91 92 // we perform all the DOM mutations at once. 93 for (size_t i = 0; i < nodesToRemove.size(); ++i) { 94 // FIXME: We can do better by directly moving children from nodesToRemove[i]. 95 int numPrunedAncestors = pruneSubsequentAncestorsToRemove(nodesToRemove, i); 96 if (numPrunedAncestors < 0) 97 continue; 98 removeNodePreservingChildren(nodesToRemove[i], AssumeContentIsAlwaysEditable); 99 i += numPrunedAncestors; 100 } 101} 102 103int SimplifyMarkupCommand::pruneSubsequentAncestorsToRemove(Vector<RefPtr<Node>>& nodesToRemove, size_t startNodeIndex) 104{ 105 size_t pastLastNodeToRemove = startNodeIndex + 1; 106 for (; pastLastNodeToRemove < nodesToRemove.size(); ++pastLastNodeToRemove) { 107 if (nodesToRemove[pastLastNodeToRemove - 1]->parentNode() != nodesToRemove[pastLastNodeToRemove]) 108 break; 109 if (nodesToRemove[pastLastNodeToRemove]->firstChild() != nodesToRemove[pastLastNodeToRemove]->lastChild()) 110 break; 111 } 112 113 Node* highestAncestorToRemove = nodesToRemove[pastLastNodeToRemove - 1].get(); 114 RefPtr<ContainerNode> parent = highestAncestorToRemove->parentNode(); 115 if (!parent) // Parent has already been removed. 116 return -1; 117 118 if (pastLastNodeToRemove == startNodeIndex + 1) 119 return 0; 120 121 removeNode(nodesToRemove[startNodeIndex], AssumeContentIsAlwaysEditable); 122 insertNodeBefore(nodesToRemove[startNodeIndex], highestAncestorToRemove, AssumeContentIsAlwaysEditable); 123 removeNode(highestAncestorToRemove, AssumeContentIsAlwaysEditable); 124 125 return pastLastNodeToRemove - startNodeIndex - 1; 126} 127 128} // namespace WebCore 129