Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebCore / editing / SimplifyMarkupCommand.cpp
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
35 namespace WebCore {
36
37 SimplifyMarkupCommand::SimplifyMarkupCommand(Document& document, Node* firstNode, Node* nodeAfterLast)
38     : CompositeEditCommand(document)
39     , m_firstNode(firstNode)
40     , m_nodeAfterLast(nodeAfterLast)
41 {
42 }
43     
44 void SimplifyMarkupCommand::doApply()
45 {
46     Node* rootNode = m_firstNode->parentNode();
47     Vector<Ref<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         auto* startingStyle = startingNode->renderStyle();
61         if (!startingStyle)
62             continue;
63         Node* currentNode = startingNode;
64         Node* topNodeWithStartingStyle = nullptr;
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             auto* renderer = currentNode->renderer();
74             if (!is<RenderInline>(renderer) || downcast<RenderInline>(*renderer).alwaysCreateLineBoxes())
75                 continue;
76             
77             if (currentNode->firstChild() != currentNode->lastChild()) {
78                 topNodeWithStartingStyle = nullptr;
79                 break;
80             }
81             
82             OptionSet<StyleDifferenceContextSensitiveProperty> contextSensitiveProperties;
83             if (currentNode->renderStyle()->diff(*startingStyle, contextSensitiveProperties) == StyleDifference::Equal)
84                 topNodeWithStartingStyle = currentNode;
85             
86         }
87         if (topNodeWithStartingStyle) {
88             for (Node* node = startingNode; node && node != topNodeWithStartingStyle; node = node->parentNode())
89                 nodesToRemove.append(*node);
90         }
91     }
92
93     // we perform all the DOM mutations at once.
94     for (size_t i = 0; i < nodesToRemove.size(); ++i) {
95         // FIXME: We can do better by directly moving children from nodesToRemove[i].
96         int numPrunedAncestors = pruneSubsequentAncestorsToRemove(nodesToRemove, i);
97         if (numPrunedAncestors < 0)
98             continue;
99         removeNodePreservingChildren(nodesToRemove[i], AssumeContentIsAlwaysEditable);
100         i += numPrunedAncestors;
101     }
102 }
103
104 int SimplifyMarkupCommand::pruneSubsequentAncestorsToRemove(Vector<Ref<Node>>& nodesToRemove, size_t startNodeIndex)
105 {
106     size_t pastLastNodeToRemove = startNodeIndex + 1;
107     for (; pastLastNodeToRemove < nodesToRemove.size(); ++pastLastNodeToRemove) {
108         if (nodesToRemove[pastLastNodeToRemove - 1]->parentNode() != nodesToRemove[pastLastNodeToRemove].ptr())
109             break;
110         if (nodesToRemove[pastLastNodeToRemove]->firstChild() != nodesToRemove[pastLastNodeToRemove]->lastChild())
111             break;
112     }
113
114     Node* highestAncestorToRemove = nodesToRemove[pastLastNodeToRemove - 1].ptr();
115     RefPtr<ContainerNode> parent = highestAncestorToRemove->parentNode();
116     if (!parent) // Parent has already been removed.
117         return -1;
118     
119     if (pastLastNodeToRemove == startNodeIndex + 1)
120         return 0;
121
122     removeNode(nodesToRemove[startNodeIndex], AssumeContentIsAlwaysEditable);
123     insertNodeBefore(nodesToRemove[startNodeIndex].copyRef(), *highestAncestorToRemove, AssumeContentIsAlwaysEditable);
124     removeNode(*highestAncestorToRemove, AssumeContentIsAlwaysEditable);
125
126     return pastLastNodeToRemove - startNodeIndex - 1;
127 }
128
129 } // namespace WebCore