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