Have is<>(T*) function do a null check on the pointer argument
[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 "Element.h"
28 #include "FormatBlockCommand.h"
29 #include "Document.h"
30 #include "ExceptionCodePlaceholder.h"
31 #include "htmlediting.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 static inline bool isElementForFormatBlock(Node* node)
45 {
46     return is<Element>(*node) && isElementForFormatBlock(downcast<Element>(*node).tagQName());
47 }
48
49 FormatBlockCommand::FormatBlockCommand(Document& document, const QualifiedName& tagName)
50     : ApplyBlockElementCommand(document, tagName)
51     , m_didApply(false)
52 {
53 }
54
55 void FormatBlockCommand::formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection)
56 {
57     if (!isElementForFormatBlock(tagName()))
58         return;
59     ApplyBlockElementCommand::formatSelection(startOfSelection, endOfSelection);
60     m_didApply = true;
61 }
62
63 void FormatBlockCommand::formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>& blockNode)
64 {
65     Node* nodeToSplitTo = enclosingBlockToSplitTreeTo(start.deprecatedNode());
66     RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTreeToNode(start.deprecatedNode(), nodeToSplitTo);
67     RefPtr<Node> nodeAfterInsertionPosition = outerBlock;
68
69     RefPtr<Range> range = Range::create(document(), start, endOfSelection);
70     Element* refNode = enclosingBlockFlowElement(end);
71     Element* root = editableRootForPosition(start);
72     // Root is null for elements with contenteditable=false.
73     if (!root || !refNode)
74         return;
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     if (wasEndOfParagraph && !isEndOfParagraph(lastParagraphInBlockNode) && !isStartOfParagraph(lastParagraphInBlockNode))
97         insertBlockPlaceholder(lastParagraphInBlockNode);
98 }
99     
100 Element* FormatBlockCommand::elementForFormatBlockCommand(Range* range)
101 {
102     if (!range)
103         return nullptr;
104
105     Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION);
106     while (commonAncestor && !isElementForFormatBlock(commonAncestor))
107         commonAncestor = commonAncestor->parentNode();
108
109     if (!commonAncestor)
110         return nullptr;
111
112     Element* rootEditableElement = range->startContainer()->rootEditableElement();
113     if (!rootEditableElement || commonAncestor->contains(rootEditableElement))
114         return nullptr;
115
116     return commonAncestor->isElementNode() ? downcast<Element>(commonAncestor) : nullptr;
117 }
118
119 bool isElementForFormatBlock(const QualifiedName& tagName)
120 {
121     static NeverDestroyed<HashSet<QualifiedName>> blockTags;
122     if (blockTags.get().isEmpty()) {
123         blockTags.get().add(addressTag);
124         blockTags.get().add(articleTag);
125         blockTags.get().add(asideTag);
126         blockTags.get().add(blockquoteTag);
127         blockTags.get().add(ddTag);
128         blockTags.get().add(divTag);
129         blockTags.get().add(dlTag);
130         blockTags.get().add(dtTag);
131         blockTags.get().add(footerTag);
132         blockTags.get().add(h1Tag);
133         blockTags.get().add(h2Tag);
134         blockTags.get().add(h3Tag);
135         blockTags.get().add(h4Tag);
136         blockTags.get().add(h5Tag);
137         blockTags.get().add(h6Tag);
138         blockTags.get().add(headerTag);
139         blockTags.get().add(hgroupTag);
140         blockTags.get().add(mainTag);
141         blockTags.get().add(navTag);
142         blockTags.get().add(pTag);
143         blockTags.get().add(preTag);
144         blockTags.get().add(sectionTag);
145     }
146     return blockTags.get().contains(tagName);
147 }
148
149 Node* enclosingBlockToSplitTreeTo(Node* startNode)
150 {
151     Node* lastBlock = startNode;
152     for (Node* n = startNode; n; n = n->parentNode()) {
153         if (!n->hasEditableStyle())
154             return lastBlock;
155         if (isTableCell(n) || n->hasTagName(bodyTag) || !n->parentNode() || !n->parentNode()->hasEditableStyle() || isElementForFormatBlock(n))
156             return n;
157         if (isBlock(n))
158             lastBlock = n;
159         if (isListElement(n))
160             return n->parentNode()->hasEditableStyle() ? n->parentNode() : n;
161     }
162     return lastBlock;
163 }
164
165 }