Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(181)

Side by Side Diff: Source/core/editing/BreakBlockquoteCommand.cpp

Issue 1294543005: Move execCommand related files in core/editing/ related files into core/editing/commands/ (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: 2015-08-18T14:20:58 Rebase for merging code style fixes Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2005 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/BreakBlockquoteCommand.h"
28
29 #include "core/HTMLNames.h"
30 #include "core/dom/NodeTraversal.h"
31 #include "core/dom/Text.h"
32 #include "core/editing/EditingUtilities.h"
33 #include "core/editing/VisiblePosition.h"
34 #include "core/html/HTMLBRElement.h"
35 #include "core/html/HTMLElement.h"
36 #include "core/html/HTMLQuoteElement.h"
37 #include "core/layout/LayoutListItem.h"
38
39 namespace blink {
40
41 using namespace HTMLNames;
42
43 namespace {
44
45 bool isFirstVisiblePositionInNode(const VisiblePosition& visiblePosition, const ContainerNode* node)
46 {
47 if (visiblePosition.isNull())
48 return false;
49
50 if (!visiblePosition.deepEquivalent().computeContainerNode()->isDescendantOf (node))
51 return false;
52
53 VisiblePosition previous = visiblePosition.previous();
54 return previous.isNull() || !previous.deepEquivalent().anchorNode()->isDesce ndantOf(node);
55 }
56
57 bool isLastVisiblePositionInNode(const VisiblePosition& visiblePosition, const C ontainerNode* node)
58 {
59 if (visiblePosition.isNull())
60 return false;
61
62 if (!visiblePosition.deepEquivalent().computeContainerNode()->isDescendantOf (node))
63 return false;
64
65 VisiblePosition next = visiblePosition.next();
66 return next.isNull() || !next.deepEquivalent().anchorNode()->isDescendantOf( node);
67 }
68
69 } // namespace
70
71 BreakBlockquoteCommand::BreakBlockquoteCommand(Document& document)
72 : CompositeEditCommand(document)
73 {
74 }
75
76 void BreakBlockquoteCommand::doApply()
77 {
78 if (endingSelection().isNone())
79 return;
80
81 // Delete the current selection.
82 if (endingSelection().isRange())
83 deleteSelection(false, false);
84
85 // This is a scenario that should never happen, but we want to
86 // make sure we don't dereference a null pointer below.
87
88 ASSERT(!endingSelection().isNone());
89
90 if (endingSelection().isNone())
91 return;
92
93 VisiblePosition visiblePos = endingSelection().visibleStart();
94
95 // pos is a position equivalent to the caret. We use downstream() so that p os will
96 // be in the first node that we need to move (there are a few exceptions to this, see below).
97 Position pos = endingSelection().start().downstream();
98
99 // Find the top-most blockquote from the start.
100 HTMLQuoteElement* topBlockquote = toHTMLQuoteElement(highestEnclosingNodeOfT ype(pos, isMailHTMLBlockquoteElement));
101 if (!topBlockquote || !topBlockquote->parentNode())
102 return;
103
104 RefPtrWillBeRawPtr<HTMLBRElement> breakElement = createBreakElement(document ());
105
106 bool isLastVisPosInNode = isLastVisiblePositionInNode(visiblePos, topBlockqu ote);
107
108 // If the position is at the beginning of the top quoted content, we don't n eed to break the quote.
109 // Instead, insert the break before the blockquote, unless the position is a s the end of the the quoted content.
110 if (isFirstVisiblePositionInNode(visiblePos, topBlockquote) && !isLastVisPos InNode) {
111 insertNodeBefore(breakElement.get(), topBlockquote);
112 setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get( )), DOWNSTREAM, endingSelection().isDirectional()));
113 rebalanceWhitespace();
114 return;
115 }
116
117 // Insert a break after the top blockquote.
118 insertNodeAfter(breakElement.get(), topBlockquote);
119
120 // If we're inserting the break at the end of the quoted content, we don't n eed to break the quote.
121 if (isLastVisPosInNode) {
122 setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get( )), DOWNSTREAM, endingSelection().isDirectional()));
123 rebalanceWhitespace();
124 return;
125 }
126
127 // Don't move a line break just after the caret. Doing so would create an e xtra, empty paragraph
128 // in the new blockquote.
129 if (lineBreakExistsAtVisiblePosition(visiblePos))
130 pos = pos.next();
131
132 // Adjust the position so we don't split at the beginning of a quote.
133 while (isFirstVisiblePositionInNode(VisiblePosition(pos), toHTMLQuoteElement (enclosingNodeOfType(pos, isMailHTMLBlockquoteElement))))
134 pos = pos.previous();
135
136 // startNode is the first node that we need to move to the new blockquote.
137 Node* startNode = pos.anchorNode();
138 ASSERT(startNode);
139
140 // Split at pos if in the middle of a text node.
141 if (startNode->isTextNode()) {
142 Text* textNode = toText(startNode);
143 int textOffset = pos.computeOffsetInContainerNode();
144 if ((unsigned)textOffset >= textNode->length()) {
145 startNode = NodeTraversal::next(*startNode);
146 ASSERT(startNode);
147 } else if (textOffset > 0) {
148 splitTextNode(textNode, textOffset);
149 }
150 } else if (pos.computeEditingOffset() > 0) {
151 Node* childAtOffset = NodeTraversal::childAt(*startNode, pos.computeEdit ingOffset());
152 startNode = childAtOffset ? childAtOffset : NodeTraversal::next(*startNo de);
153 ASSERT(startNode);
154 }
155
156 // If there's nothing inside topBlockquote to move, we're finished.
157 if (!startNode->isDescendantOf(topBlockquote)) {
158 setEndingSelection(VisibleSelection(VisiblePosition(firstPositionInOrBef oreNode(startNode)), endingSelection().isDirectional()));
159 return;
160 }
161
162 // Build up list of ancestors in between the start node and the top blockquo te.
163 WillBeHeapVector<RefPtrWillBeMember<Element>> ancestors;
164 for (Element* node = startNode->parentElement(); node && node != topBlockquo te; node = node->parentElement())
165 ancestors.append(node);
166
167 // Insert a clone of the top blockquote after the break.
168 RefPtrWillBeRawPtr<Element> clonedBlockquote = topBlockquote->cloneElementWi thoutChildren();
169 insertNodeAfter(clonedBlockquote.get(), breakElement.get());
170
171 // Clone startNode's ancestors into the cloned blockquote.
172 // On exiting this loop, clonedAncestor is the lowest ancestor
173 // that was cloned (i.e. the clone of either ancestors.last()
174 // or clonedBlockquote if ancestors is empty).
175 RefPtrWillBeRawPtr<Element> clonedAncestor = clonedBlockquote;
176 for (size_t i = ancestors.size(); i != 0; --i) {
177 RefPtrWillBeRawPtr<Element> clonedChild = ancestors[i - 1]->cloneElement WithoutChildren();
178 // Preserve list item numbering in cloned lists.
179 if (isHTMLOListElement(*clonedChild)) {
180 Node* listChildNode = i > 1 ? ancestors[i - 2].get() : startNode;
181 // The first child of the cloned list might not be a list item eleme nt,
182 // find the first one so that we know where to start numbering.
183 while (listChildNode && !isHTMLLIElement(*listChildNode))
184 listChildNode = listChildNode->nextSibling();
185 if (isListItem(listChildNode))
186 setNodeAttribute(clonedChild, startAttr, AtomicString::number(to LayoutListItem(listChildNode->layoutObject())->value()));
187 }
188
189 appendNode(clonedChild.get(), clonedAncestor.get());
190 clonedAncestor = clonedChild;
191 }
192
193 moveRemainingSiblingsToNewParent(startNode, 0, clonedAncestor);
194
195 if (!ancestors.isEmpty()) {
196 // Split the tree up the ancestor chain until the topBlockquote
197 // Throughout this loop, clonedParent is the clone of ancestor's parent.
198 // This is so we can clone ancestor's siblings and place the clones
199 // into the clone corresponding to the ancestor's parent.
200 RefPtrWillBeRawPtr<Element> ancestor = nullptr;
201 RefPtrWillBeRawPtr<Element> clonedParent = nullptr;
202 for (ancestor = ancestors.first(), clonedParent = clonedAncestor->parent Element();
203 ancestor && ancestor != topBlockquote;
204 ancestor = ancestor->parentElement(), clonedParent = clonedParent->p arentElement())
205 moveRemainingSiblingsToNewParent(ancestor->nextSibling(), 0, clonedP arent);
206
207 // If the startNode's original parent is now empty, remove it
208 Element* originalParent = ancestors.first().get();
209 if (!originalParent->hasChildren())
210 removeNode(originalParent);
211 }
212
213 // Make sure the cloned block quote renders.
214 addBlockPlaceholderIfNeeded(clonedBlockquote.get());
215
216 // Put the selection right before the break.
217 setEndingSelection(VisibleSelection(positionBeforeNode(breakElement.get()), DOWNSTREAM, endingSelection().isDirectional()));
218 rebalanceWhitespace();
219 }
220
221 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698