Improve use of NeverDestroyed
[WebKit-https.git] / Source / WebCore / editing / FormatBlockCommand.cpp
1 /*
2  * Copyright (C) 2006 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 "FormatBlockCommand.h"
28
29 #include "Document.h"
30 #include "Editing.h"
31 #include "Element.h"
32 #include "HTMLElement.h"
33 #include "HTMLNames.h"
34 #include "Range.h"
35 #include "VisibleUnits.h"
36 #include <wtf/NeverDestroyed.h>
37
38 namespace WebCore {
39
40 using namespace HTMLNames;
41
42 static Node* enclosingBlockToSplitTreeTo(Node* startNode);
43 static bool isElementForFormatBlock(const QualifiedName& tagName);
44
45 static inline bool isElementForFormatBlock(Node* node)
46 {
47     return is<Element>(*node) && isElementForFormatBlock(downcast<Element>(*node).tagQName());
48 }
49
50 FormatBlockCommand::FormatBlockCommand(Document& document, const QualifiedName& tagName)
51     : ApplyBlockElementCommand(document, tagName)
52     , m_didApply(false)
53 {
54 }
55
56 void FormatBlockCommand::formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection)
57 {
58     if (!isElementForFormatBlock(tagName()))
59         return;
60     ApplyBlockElementCommand::formatSelection(startOfSelection, endOfSelection);
61     m_didApply = true;
62 }
63
64 void FormatBlockCommand::formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>& blockNode)
65 {
66     Node* nodeToSplitTo = enclosingBlockToSplitTreeTo(start.deprecatedNode());
67     ASSERT(nodeToSplitTo);
68     RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTreeToNode(*start.deprecatedNode(), *nodeToSplitTo);
69     RefPtr<Node> nodeAfterInsertionPosition = outerBlock;
70
71     RefPtr<Range> range = Range::create(document(), start, endOfSelection);
72     Element* refNode = enclosingBlockFlowElement(end);
73     Element* root = editableRootForPosition(start);
74     // Root is null for elements with contenteditable=false.
75     if (!root || !refNode)
76         return;
77     if (isElementForFormatBlock(refNode->tagQName()) && start == startOfBlock(start)
78         && (end == endOfBlock(end) || isNodeVisiblyContainedWithin(*refNode, *range))
79         && refNode != root && !root->isDescendantOf(*refNode)) {
80         // Already in a block element that only contains the current paragraph
81         if (refNode->hasTagName(tagName()))
82             return;
83         nodeAfterInsertionPosition = refNode;
84     }
85
86     if (!blockNode) {
87         // Create a new blockquote and insert it as a child of the root editable element. We accomplish
88         // this by splitting all parents of the current paragraph up to that point.
89         blockNode = createBlockElement();
90         insertNodeBefore(*blockNode, *nodeAfterInsertionPosition);
91     }
92
93     Position lastParagraphInBlockNode = blockNode->lastChild() ? positionAfterNode(blockNode->lastChild()) : Position();
94     bool wasEndOfParagraph = isEndOfParagraph(lastParagraphInBlockNode);
95
96     moveParagraphWithClones(start, end, blockNode.get(), outerBlock.get());
97
98     if (wasEndOfParagraph && !isEndOfParagraph(lastParagraphInBlockNode) && !isStartOfParagraph(lastParagraphInBlockNode))
99         insertBlockPlaceholder(lastParagraphInBlockNode);
100 }
101     
102 Element* FormatBlockCommand::elementForFormatBlockCommand(Range* range)
103 {
104     if (!range)
105         return nullptr;
106
107     Node* commonAncestor = range->commonAncestorContainer();
108     while (commonAncestor && !isElementForFormatBlock(commonAncestor))
109         commonAncestor = commonAncestor->parentNode();
110
111     if (!commonAncestor)
112         return nullptr;
113
114     Element* rootEditableElement = range->startContainer().rootEditableElement();
115     if (!rootEditableElement || commonAncestor->contains(rootEditableElement))
116         return nullptr;
117
118     return commonAncestor->isElementNode() ? downcast<Element>(commonAncestor) : nullptr;
119 }
120
121 bool isElementForFormatBlock(const QualifiedName& tagName)
122 {
123     static const auto blockTags = makeNeverDestroyed(HashSet<QualifiedName> {
124         addressTag,
125         articleTag,
126         asideTag,
127         blockquoteTag,
128         ddTag,
129         divTag,
130         dlTag,
131         dtTag,
132         footerTag,
133         h1Tag,
134         h2Tag,
135         h3Tag,
136         h4Tag,
137         h5Tag,
138         h6Tag,
139         headerTag,
140         hgroupTag,
141         mainTag,
142         navTag,
143         pTag,
144         preTag,
145         sectionTag,
146     });
147     return blockTags.get().contains(tagName);
148 }
149
150 Node* enclosingBlockToSplitTreeTo(Node* startNode)
151 {
152     Node* lastBlock = startNode;
153     for (Node* n = startNode; n; n = n->parentNode()) {
154         if (!n->hasEditableStyle())
155             return lastBlock;
156         if (isTableCell(n) || n->hasTagName(bodyTag) || !n->parentNode() || !n->parentNode()->hasEditableStyle() || isElementForFormatBlock(n))
157             return n;
158         if (isBlock(n))
159             lastBlock = n;
160         if (isListHTMLElement(n))
161             return n->parentNode()->hasEditableStyle() ? n->parentNode() : n;
162     }
163     return lastBlock;
164 }
165
166 }