OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 22 matching lines...) Expand all Loading... |
33 #include "core/editing/VisibleUnits.h" | 33 #include "core/editing/VisibleUnits.h" |
34 #include "core/editing/htmlediting.h" | 34 #include "core/editing/htmlediting.h" |
35 #include "core/html/HTMLBRElement.h" | 35 #include "core/html/HTMLBRElement.h" |
36 #include "core/html/HTMLElement.h" | 36 #include "core/html/HTMLElement.h" |
37 #include "core/rendering/RenderObject.h" | 37 #include "core/rendering/RenderObject.h" |
38 | 38 |
39 namespace blink { | 39 namespace blink { |
40 | 40 |
41 using namespace HTMLNames; | 41 using namespace HTMLNames; |
42 | 42 |
43 static bool isListOrIndentBlockquote(const Node* node) | 43 static bool isHTMLListOrBlockquoteElement(const Node* node) |
44 { | 44 { |
45 return node && (isHTMLUListElement(*node) || isHTMLOListElement(*node) || no
de->hasTagName(blockquoteTag)); | 45 if (!node || !node->isHTMLElement()) |
| 46 return false; |
| 47 const HTMLElement& element = toHTMLElement(*node); |
| 48 return isHTMLUListElement(element) || isHTMLOListElement(element) || element
.hasTagName(blockquoteTag); |
46 } | 49 } |
47 | 50 |
48 IndentOutdentCommand::IndentOutdentCommand(Document& document, EIndentType typeO
fAction) | 51 IndentOutdentCommand::IndentOutdentCommand(Document& document, EIndentType typeO
fAction) |
49 : ApplyBlockElementCommand(document, blockquoteTag, "margin: 0 0 0 40px; bor
der: none; padding: 0px;") | 52 : ApplyBlockElementCommand(document, blockquoteTag, "margin: 0 0 0 40px; bor
der: none; padding: 0px;") |
50 , m_typeOfAction(typeOfAction) | 53 , m_typeOfAction(typeOfAction) |
51 { | 54 { |
52 } | 55 } |
53 | 56 |
54 bool IndentOutdentCommand::tryIndentingAsListItem(const Position& start, const P
osition& end) | 57 bool IndentOutdentCommand::tryIndentingAsListItem(const Position& start, const P
osition& end) |
55 { | 58 { |
56 // If our selection is not inside a list, bail out. | 59 // If our selection is not inside a list, bail out. |
57 RefPtrWillBeRawPtr<Node> lastNodeInSelectedParagraph = start.deprecatedNode(
); | 60 RefPtrWillBeRawPtr<Node> lastNodeInSelectedParagraph = start.deprecatedNode(
); |
58 RefPtrWillBeRawPtr<Element> listNode = enclosingList(lastNodeInSelectedParag
raph.get()); | 61 RefPtrWillBeRawPtr<HTMLElement> listElement = enclosingList(lastNodeInSelect
edParagraph.get()); |
59 if (!listNode) | 62 if (!listElement) |
60 return false; | 63 return false; |
61 | 64 |
62 // Find the block that we want to indent. If it's not a list item (e.g., a
div inside a list item), we bail out. | 65 // Find the block that we want to indent. If it's not a list item (e.g., a
div inside a list item), we bail out. |
63 RefPtrWillBeRawPtr<Element> selectedListItem = enclosingBlock(lastNodeInSele
ctedParagraph.get()); | 66 RefPtrWillBeRawPtr<Element> selectedListItem = enclosingBlock(lastNodeInSele
ctedParagraph.get()); |
64 | 67 |
65 // FIXME: we need to deal with the case where there is no li (malformed HTML
) | 68 // FIXME: we need to deal with the case where there is no li (malformed HTML
) |
66 if (!selectedListItem || !isHTMLLIElement(*selectedListItem)) | 69 if (!isHTMLLIElement(selectedListItem)) |
67 return false; | 70 return false; |
68 | 71 |
69 // FIXME: previousElementSibling does not ignore non-rendered content like <
span></span>. Should we? | 72 // FIXME: previousElementSibling does not ignore non-rendered content like <
span></span>. Should we? |
70 RefPtrWillBeRawPtr<Element> previousList = ElementTraversal::previousSibling
(*selectedListItem); | 73 RefPtrWillBeRawPtr<Element> previousList = ElementTraversal::previousSibling
(*selectedListItem); |
71 RefPtrWillBeRawPtr<Element> nextList = ElementTraversal::nextSibling(*select
edListItem); | 74 RefPtrWillBeRawPtr<Element> nextList = ElementTraversal::nextSibling(*select
edListItem); |
72 | 75 |
73 // We should calculate visible range in list item because inserting new | 76 // We should calculate visible range in list item because inserting new |
74 // list element will change visibility of list item, e.g. :first-child | 77 // list element will change visibility of list item, e.g. :first-child |
75 // CSS selector. | 78 // CSS selector. |
76 RefPtrWillBeRawPtr<Element> newList = document().createElement(listNode->tag
QName(), false); | 79 RefPtrWillBeRawPtr<HTMLElement> newList = toHTMLElement(document().createEle
ment(listElement->tagQName(), false).get()); |
77 insertNodeBefore(newList, selectedListItem.get()); | 80 insertNodeBefore(newList, selectedListItem.get()); |
78 | 81 |
79 // We should clone all the children of the list item for indenting purposes.
However, in case the current | 82 // We should clone all the children of the list item for indenting purposes.
However, in case the current |
80 // selection does not encompass all its children, we need to explicitally ha
ndle the same. The original | 83 // selection does not encompass all its children, we need to explicitally ha
ndle the same. The original |
81 // list item too would require proper deletion in that case. | 84 // list item too would require proper deletion in that case. |
82 if (end.anchorNode() == selectedListItem.get() || end.anchorNode()->isDescen
dantOf(selectedListItem->lastChild())) { | 85 if (end.anchorNode() == selectedListItem.get() || end.anchorNode()->isDescen
dantOf(selectedListItem->lastChild())) { |
83 moveParagraphWithClones(VisiblePosition(start), VisiblePosition(end), ne
wList.get(), selectedListItem.get()); | 86 moveParagraphWithClones(VisiblePosition(start), VisiblePosition(end), ne
wList.get(), selectedListItem.get()); |
84 } else { | 87 } else { |
85 moveParagraphWithClones(VisiblePosition(start), VisiblePosition(position
AfterNode(selectedListItem->lastChild())), newList.get(), selectedListItem.get()
); | 88 moveParagraphWithClones(VisiblePosition(start), VisiblePosition(position
AfterNode(selectedListItem->lastChild())), newList.get(), selectedListItem.get()
); |
86 removeNode(selectedListItem.get()); | 89 removeNode(selectedListItem.get()); |
87 } | 90 } |
88 | 91 |
89 if (canMergeLists(previousList.get(), newList.get())) | 92 if (canMergeLists(previousList.get(), newList.get())) |
90 mergeIdenticalElements(previousList.get(), newList.get()); | 93 mergeIdenticalElements(previousList.get(), newList.get()); |
91 if (canMergeLists(newList.get(), nextList.get())) | 94 if (canMergeLists(newList.get(), nextList.get())) |
92 mergeIdenticalElements(newList.get(), nextList.get()); | 95 mergeIdenticalElements(newList.get(), nextList.get()); |
93 | 96 |
94 return true; | 97 return true; |
95 } | 98 } |
96 | 99 |
97 void IndentOutdentCommand::indentIntoBlockquote(const Position& start, const Pos
ition& end, RefPtrWillBeRawPtr<HTMLElement>& targetBlockquote) | 100 void IndentOutdentCommand::indentIntoBlockquote(const Position& start, const Pos
ition& end, RefPtrWillBeRawPtr<HTMLElement>& targetBlockquote) |
98 { | 101 { |
99 Node* enclosingCell = enclosingNodeOfType(start, &isTableCell); | 102 Element* enclosingCell = toElement(enclosingNodeOfType(start, &isTableCell))
; |
100 Node* nodeToSplitTo; | 103 Element* elementToSplitTo; |
101 if (enclosingCell) | 104 if (enclosingCell) |
102 nodeToSplitTo = enclosingCell; | 105 elementToSplitTo = enclosingCell; |
103 else if (enclosingList(start.containerNode())) | 106 else if (enclosingList(start.containerNode())) |
104 nodeToSplitTo = enclosingBlock(start.containerNode()); | 107 elementToSplitTo = enclosingBlock(start.containerNode()); |
105 else | 108 else |
106 nodeToSplitTo = editableRootForPosition(start); | 109 elementToSplitTo = editableRootForPosition(start); |
107 | 110 |
108 if (!nodeToSplitTo) | 111 if (!elementToSplitTo) |
109 return; | 112 return; |
110 | 113 |
111 RefPtrWillBeRawPtr<Node> outerBlock = (start.containerNode() == nodeToSplitT
o) ? start.containerNode() : splitTreeToNode(start.containerNode(), nodeToSplitT
o).get(); | 114 RefPtrWillBeRawPtr<Node> outerBlock = (start.containerNode() == elementToSpl
itTo) ? start.containerNode() : splitTreeToNode(start.containerNode(), elementTo
SplitTo).get(); |
112 | 115 |
113 VisiblePosition startOfContents(start); | 116 VisiblePosition startOfContents(start); |
114 if (!targetBlockquote) { | 117 if (!targetBlockquote) { |
115 // Create a new blockquote and insert it as a child of the root editable
element. We accomplish | 118 // Create a new blockquote and insert it as a child of the root editable
element. We accomplish |
116 // this by splitting all parents of the current paragraph up to that poi
nt. | 119 // this by splitting all parents of the current paragraph up to that poi
nt. |
117 targetBlockquote = createBlockElement(); | 120 targetBlockquote = createBlockElement(); |
118 if (outerBlock == start.containerNode()) | 121 if (outerBlock == start.containerNode()) |
119 insertNodeAt(targetBlockquote, start); | 122 insertNodeAt(targetBlockquote, start); |
120 else | 123 else |
121 insertNodeBefore(targetBlockquote, outerBlock); | 124 insertNodeBefore(targetBlockquote, outerBlock); |
122 startOfContents = VisiblePosition(positionInParentAfterNode(*targetBlock
quote)); | 125 startOfContents = VisiblePosition(positionInParentAfterNode(*targetBlock
quote)); |
123 } | 126 } |
124 | 127 |
125 VisiblePosition endOfContents(end); | 128 VisiblePosition endOfContents(end); |
126 if (startOfContents.isNull() || endOfContents.isNull()) | 129 if (startOfContents.isNull() || endOfContents.isNull()) |
127 return; | 130 return; |
128 moveParagraphWithClones(startOfContents, endOfContents, targetBlockquote.get
(), outerBlock.get()); | 131 moveParagraphWithClones(startOfContents, endOfContents, targetBlockquote.get
(), outerBlock.get()); |
129 } | 132 } |
130 | 133 |
131 void IndentOutdentCommand::outdentParagraph() | 134 void IndentOutdentCommand::outdentParagraph() |
132 { | 135 { |
133 VisiblePosition visibleStartOfParagraph = startOfParagraph(endingSelection()
.visibleStart()); | 136 VisiblePosition visibleStartOfParagraph = startOfParagraph(endingSelection()
.visibleStart()); |
134 VisiblePosition visibleEndOfParagraph = endOfParagraph(visibleStartOfParagra
ph); | 137 VisiblePosition visibleEndOfParagraph = endOfParagraph(visibleStartOfParagra
ph); |
135 | 138 |
136 Node* enclosingNode = enclosingNodeOfType(visibleStartOfParagraph.deepEquiva
lent(), &isListOrIndentBlockquote); | 139 HTMLElement* enclosingElement = toHTMLElement(enclosingNodeOfType(visibleSta
rtOfParagraph.deepEquivalent(), &isHTMLListOrBlockquoteElement)); |
137 if (!enclosingNode || !enclosingNode->parentNode()->hasEditableStyle()) // W
e can't outdent if there is no place to go! | 140 if (!enclosingElement || !enclosingElement->parentNode()->hasEditableStyle()
) // We can't outdent if there is no place to go! |
138 return; | 141 return; |
139 | 142 |
140 // Use InsertListCommand to remove the selection from the list | 143 // Use InsertListCommand to remove the selection from the list |
141 if (isHTMLOListElement(*enclosingNode)) { | 144 if (isHTMLOListElement(*enclosingElement)) { |
142 applyCommandToComposite(InsertListCommand::create(document(), InsertList
Command::OrderedList)); | 145 applyCommandToComposite(InsertListCommand::create(document(), InsertList
Command::OrderedList)); |
143 return; | 146 return; |
144 } | 147 } |
145 if (isHTMLUListElement(*enclosingNode)) { | 148 if (isHTMLUListElement(*enclosingElement)) { |
146 applyCommandToComposite(InsertListCommand::create(document(), InsertList
Command::UnorderedList)); | 149 applyCommandToComposite(InsertListCommand::create(document(), InsertList
Command::UnorderedList)); |
147 return; | 150 return; |
148 } | 151 } |
149 | 152 |
150 // The selection is inside a blockquote i.e. enclosingNode is a blockquote | 153 // The selection is inside a blockquote i.e. enclosingNode is a blockquote |
151 VisiblePosition positionInEnclosingBlock = VisiblePosition(firstPositionInNo
de(enclosingNode)); | 154 VisiblePosition positionInEnclosingBlock = VisiblePosition(firstPositionInNo
de(enclosingElement)); |
152 // If the blockquote is inline, the start of the enclosing block coincides w
ith | 155 // If the blockquote is inline, the start of the enclosing block coincides w
ith |
153 // positionInEnclosingBlock. | 156 // positionInEnclosingBlock. |
154 VisiblePosition startOfEnclosingBlock = (enclosingNode->renderer() && enclos
ingNode->renderer()->isInline()) ? positionInEnclosingBlock : startOfBlock(posit
ionInEnclosingBlock); | 157 VisiblePosition startOfEnclosingBlock = (enclosingElement->renderer() && enc
losingElement->renderer()->isInline()) ? positionInEnclosingBlock : startOfBlock
(positionInEnclosingBlock); |
155 VisiblePosition lastPositionInEnclosingBlock = VisiblePosition(lastPositionI
nNode(enclosingNode)); | 158 VisiblePosition lastPositionInEnclosingBlock = VisiblePosition(lastPositionI
nNode(enclosingElement)); |
156 VisiblePosition endOfEnclosingBlock = endOfBlock(lastPositionInEnclosingBloc
k); | 159 VisiblePosition endOfEnclosingBlock = endOfBlock(lastPositionInEnclosingBloc
k); |
157 if (visibleStartOfParagraph == startOfEnclosingBlock && | 160 if (visibleStartOfParagraph == startOfEnclosingBlock && |
158 visibleEndOfParagraph == endOfEnclosingBlock) { | 161 visibleEndOfParagraph == endOfEnclosingBlock) { |
159 // The blockquote doesn't contain anything outside the paragraph, so it
can be totally removed. | 162 // The blockquote doesn't contain anything outside the paragraph, so it
can be totally removed. |
160 Node* splitPoint = enclosingNode->nextSibling(); | 163 Node* splitPoint = enclosingElement->nextSibling(); |
161 removeNodePreservingChildren(enclosingNode); | 164 removeNodePreservingChildren(enclosingElement); |
162 // outdentRegion() assumes it is operating on the first paragraph of an
enclosing blockquote, but if there are multiply nested blockquotes and we've | 165 // outdentRegion() assumes it is operating on the first paragraph of an
enclosing blockquote, but if there are multiply nested blockquotes and we've |
163 // just removed one, then this assumption isn't true. By splitting the n
ext containing blockquote after this node, we keep this assumption true | 166 // just removed one, then this assumption isn't true. By splitting the n
ext containing blockquote after this node, we keep this assumption true |
164 if (splitPoint) { | 167 if (splitPoint) { |
165 if (ContainerNode* splitPointParent = splitPoint->parentNode()) { | 168 if (Element* splitPointParent = splitPoint->parentElement()) { |
166 if (splitPointParent->hasTagName(blockquoteTag) | 169 if (splitPointParent->hasTagName(blockquoteTag) |
167 && !splitPoint->hasTagName(blockquoteTag) | 170 && !splitPoint->hasTagName(blockquoteTag) |
168 && splitPointParent->parentNode()->hasEditableStyle()) // We
can't outdent if there is no place to go! | 171 && splitPointParent->parentNode()->hasEditableStyle()) // We
can't outdent if there is no place to go! |
169 splitElement(toElement(splitPointParent), splitPoint); | 172 splitElement(splitPointParent, splitPoint); |
170 } | 173 } |
171 } | 174 } |
172 | 175 |
173 document().updateLayoutIgnorePendingStylesheets(); | 176 document().updateLayoutIgnorePendingStylesheets(); |
174 visibleStartOfParagraph = VisiblePosition(visibleStartOfParagraph.deepEq
uivalent()); | 177 visibleStartOfParagraph = VisiblePosition(visibleStartOfParagraph.deepEq
uivalent()); |
175 visibleEndOfParagraph = VisiblePosition(visibleEndOfParagraph.deepEquiva
lent()); | 178 visibleEndOfParagraph = VisiblePosition(visibleEndOfParagraph.deepEquiva
lent()); |
176 if (visibleStartOfParagraph.isNotNull() && !isStartOfParagraph(visibleSt
artOfParagraph)) | 179 if (visibleStartOfParagraph.isNotNull() && !isStartOfParagraph(visibleSt
artOfParagraph)) |
177 insertNodeAt(createBreakElement(document()), visibleStartOfParagraph
.deepEquivalent()); | 180 insertNodeAt(createBreakElement(document()), visibleStartOfParagraph
.deepEquivalent()); |
178 if (visibleEndOfParagraph.isNotNull() && !isEndOfParagraph(visibleEndOfP
aragraph)) | 181 if (visibleEndOfParagraph.isNotNull() && !isEndOfParagraph(visibleEndOfP
aragraph)) |
179 insertNodeAt(createBreakElement(document()), visibleEndOfParagraph.d
eepEquivalent()); | 182 insertNodeAt(createBreakElement(document()), visibleEndOfParagraph.d
eepEquivalent()); |
180 | 183 |
181 return; | 184 return; |
182 } | 185 } |
183 RefPtrWillBeRawPtr<Node> splitBlockquoteNode = enclosingNode; | 186 RefPtrWillBeRawPtr<Node> splitBlockquoteNode = enclosingElement; |
184 if (Node* enclosingBlockFlow = enclosingBlock(visibleStartOfParagraph.deepEq
uivalent().deprecatedNode())) { | 187 if (Element* enclosingBlockFlow = enclosingBlock(visibleStartOfParagraph.dee
pEquivalent().deprecatedNode())) { |
185 if (enclosingBlockFlow != enclosingNode) { | 188 if (enclosingBlockFlow != enclosingElement) { |
186 splitBlockquoteNode = splitTreeToNode(enclosingBlockFlow, enclosingN
ode, true); | 189 splitBlockquoteNode = splitTreeToNode(enclosingBlockFlow, enclosingE
lement, true); |
187 } else { | 190 } else { |
188 // We split the blockquote at where we start outdenting. | 191 // We split the blockquote at where we start outdenting. |
189 Node* highestInlineNode = highestEnclosingNodeOfType(visibleStartOfP
aragraph.deepEquivalent(), isInline, CannotCrossEditingBoundary, enclosingBlockF
low); | 192 Node* highestInlineNode = highestEnclosingNodeOfType(visibleStartOfP
aragraph.deepEquivalent(), isInline, CannotCrossEditingBoundary, enclosingBlockF
low); |
190 splitElement(toElement(enclosingNode), (highestInlineNode) ? highest
InlineNode : visibleStartOfParagraph.deepEquivalent().deprecatedNode()); | 193 splitElement(enclosingElement, highestInlineNode ? highestInlineNode
: visibleStartOfParagraph.deepEquivalent().deprecatedNode()); |
191 } | 194 } |
192 } | 195 } |
193 VisiblePosition startOfParagraphToMove(startOfParagraph(visibleStartOfParagr
aph)); | 196 VisiblePosition startOfParagraphToMove(startOfParagraph(visibleStartOfParagr
aph)); |
194 VisiblePosition endOfParagraphToMove(endOfParagraph(visibleEndOfParagraph)); | 197 VisiblePosition endOfParagraphToMove(endOfParagraph(visibleEndOfParagraph)); |
195 if (startOfParagraphToMove.isNull() || endOfParagraphToMove.isNull()) | 198 if (startOfParagraphToMove.isNull() || endOfParagraphToMove.isNull()) |
196 return; | 199 return; |
197 RefPtrWillBeRawPtr<HTMLBRElement> placeholder = createBreakElement(document(
)); | 200 RefPtrWillBeRawPtr<HTMLBRElement> placeholder = createBreakElement(document(
)); |
198 insertNodeBefore(placeholder, splitBlockquoteNode); | 201 insertNodeBefore(placeholder, splitBlockquoteNode); |
199 moveParagraph(startOfParagraphToMove, endOfParagraphToMove, VisiblePosition(
positionBeforeNode(placeholder.get())), true); | 202 moveParagraph(startOfParagraphToMove, endOfParagraphToMove, VisiblePosition(
positionBeforeNode(placeholder.get())), true); |
200 } | 203 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 | 249 |
247 void IndentOutdentCommand::formatRange(const Position& start, const Position& en
d, const Position&, RefPtrWillBeRawPtr<HTMLElement>& blockquoteForNextIndent) | 250 void IndentOutdentCommand::formatRange(const Position& start, const Position& en
d, const Position&, RefPtrWillBeRawPtr<HTMLElement>& blockquoteForNextIndent) |
248 { | 251 { |
249 if (tryIndentingAsListItem(start, end)) | 252 if (tryIndentingAsListItem(start, end)) |
250 blockquoteForNextIndent = nullptr; | 253 blockquoteForNextIndent = nullptr; |
251 else | 254 else |
252 indentIntoBlockquote(start, end, blockquoteForNextIndent); | 255 indentIntoBlockquote(start, end, blockquoteForNextIndent); |
253 } | 256 } |
254 | 257 |
255 } | 258 } |
OLD | NEW |