| 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 |
| 11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
| 12 * | 12 * |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (IndentOutdentCommandINCLUDING, BUT NOT L
IMITED TO, | 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 */ | 24 */ |
| 25 | 25 |
| 26 #include "core/editing/commands/IndentOutdentCommand.h" | 26 #include "core/editing/commands/IndentOutdentCommand.h" |
| 27 | 27 |
| 28 #include "core/HTMLNames.h" | 28 #include "core/HTMLNames.h" |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 64 | 64 |
| 65 bool IndentOutdentCommand::tryIndentingAsListItem(const Position& start, | 65 bool IndentOutdentCommand::tryIndentingAsListItem(const Position& start, |
| 66 const Position& end, | 66 const Position& end, |
| 67 EditingState* editingState) { | 67 EditingState* editingState) { |
| 68 // If our selection is not inside a list, bail out. | 68 // If our selection is not inside a list, bail out. |
| 69 Node* lastNodeInSelectedParagraph = start.anchorNode(); | 69 Node* lastNodeInSelectedParagraph = start.anchorNode(); |
| 70 HTMLElement* listElement = enclosingList(lastNodeInSelectedParagraph); | 70 HTMLElement* listElement = enclosingList(lastNodeInSelectedParagraph); |
| 71 if (!listElement) | 71 if (!listElement) |
| 72 return false; | 72 return false; |
| 73 | 73 |
| 74 // Find the block that we want to indent. If it's not a list item (e.g., a di
v inside a list item), we bail out. | 74 // Find the block that we want to indent. If it's not a list item (e.g., a |
| 75 // div inside a list item), we bail out. |
| 75 Element* selectedListItem = enclosingBlock(lastNodeInSelectedParagraph); | 76 Element* selectedListItem = enclosingBlock(lastNodeInSelectedParagraph); |
| 76 | 77 |
| 77 // FIXME: we need to deal with the case where there is no li (malformed HTML) | 78 // FIXME: we need to deal with the case where there is no li (malformed HTML) |
| 78 if (!isHTMLLIElement(selectedListItem)) | 79 if (!isHTMLLIElement(selectedListItem)) |
| 79 return false; | 80 return false; |
| 80 | 81 |
| 81 // FIXME: previousElementSibling does not ignore non-rendered content like <sp
an></span>. Should we? | 82 // FIXME: previousElementSibling does not ignore non-rendered content like |
| 83 // <span></span>. Should we? |
| 82 Element* previousList = ElementTraversal::previousSibling(*selectedListItem); | 84 Element* previousList = ElementTraversal::previousSibling(*selectedListItem); |
| 83 Element* nextList = ElementTraversal::nextSibling(*selectedListItem); | 85 Element* nextList = ElementTraversal::nextSibling(*selectedListItem); |
| 84 | 86 |
| 85 // We should calculate visible range in list item because inserting new | 87 // We should calculate visible range in list item because inserting new |
| 86 // list element will change visibility of list item, e.g. :first-child | 88 // list element will change visibility of list item, e.g. :first-child |
| 87 // CSS selector. | 89 // CSS selector. |
| 88 HTMLElement* newList = toHTMLElement( | 90 HTMLElement* newList = toHTMLElement( |
| 89 document().createElement(listElement->tagQName(), CreatedByCloneNode)); | 91 document().createElement(listElement->tagQName(), CreatedByCloneNode)); |
| 90 insertNodeBefore(newList, selectedListItem, editingState); | 92 insertNodeBefore(newList, selectedListItem, editingState); |
| 91 if (editingState->isAborted()) | 93 if (editingState->isAborted()) |
| 92 return false; | 94 return false; |
| 93 | 95 |
| 94 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 96 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 95 | 97 |
| 96 // We should clone all the children of the list item for indenting purposes. H
owever, in case the current | 98 // We should clone all the children of the list item for indenting purposes. |
| 97 // selection does not encompass all its children, we need to explicitally hand
le the same. The original | 99 // However, in case the current selection does not encompass all its children, |
| 98 // list item too would require proper deletion in that case. | 100 // we need to explicitally handle the same. The original list item too would |
| 101 // require proper deletion in that case. |
| 99 if (end.anchorNode() == selectedListItem || | 102 if (end.anchorNode() == selectedListItem || |
| 100 end.anchorNode()->isDescendantOf(selectedListItem->lastChild())) { | 103 end.anchorNode()->isDescendantOf(selectedListItem->lastChild())) { |
| 101 moveParagraphWithClones(createVisiblePosition(start), | 104 moveParagraphWithClones(createVisiblePosition(start), |
| 102 createVisiblePosition(end), newList, | 105 createVisiblePosition(end), newList, |
| 103 selectedListItem, editingState); | 106 selectedListItem, editingState); |
| 104 } else { | 107 } else { |
| 105 moveParagraphWithClones( | 108 moveParagraphWithClones( |
| 106 createVisiblePosition(start), | 109 createVisiblePosition(start), |
| 107 VisiblePosition::afterNode(selectedListItem->lastChild()), newList, | 110 VisiblePosition::afterNode(selectedListItem->lastChild()), newList, |
| 108 selectedListItem, editingState); | 111 selectedListItem, editingState); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 144 return; | 147 return; |
| 145 | 148 |
| 146 Node* outerBlock = | 149 Node* outerBlock = |
| 147 (start.computeContainerNode() == elementToSplitTo) | 150 (start.computeContainerNode() == elementToSplitTo) |
| 148 ? start.computeContainerNode() | 151 ? start.computeContainerNode() |
| 149 : splitTreeToNode(start.computeContainerNode(), elementToSplitTo); | 152 : splitTreeToNode(start.computeContainerNode(), elementToSplitTo); |
| 150 | 153 |
| 151 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 154 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 152 VisiblePosition startOfContents = createVisiblePosition(start); | 155 VisiblePosition startOfContents = createVisiblePosition(start); |
| 153 if (!targetBlockquote) { | 156 if (!targetBlockquote) { |
| 154 // Create a new blockquote and insert it as a child of the root editable ele
ment. We accomplish | 157 // Create a new blockquote and insert it as a child of the root editable |
| 155 // this by splitting all parents of the current paragraph up to that point. | 158 // element. We accomplish this by splitting all parents of the current |
| 159 // paragraph up to that point. |
| 156 targetBlockquote = createBlockElement(); | 160 targetBlockquote = createBlockElement(); |
| 157 if (outerBlock == start.computeContainerNode()) { | 161 if (outerBlock == start.computeContainerNode()) { |
| 158 // When we apply indent to an empty <blockquote>, we should call insertNod
eAfter(). | 162 // When we apply indent to an empty <blockquote>, we should call |
| 159 // See http://crbug.com/625802 for more details. | 163 // insertNodeAfter(). See http://crbug.com/625802 for more details. |
| 160 if (outerBlock->hasTagName(blockquoteTag)) | 164 if (outerBlock->hasTagName(blockquoteTag)) |
| 161 insertNodeAfter(targetBlockquote, outerBlock, editingState); | 165 insertNodeAfter(targetBlockquote, outerBlock, editingState); |
| 162 else | 166 else |
| 163 insertNodeAt(targetBlockquote, start, editingState); | 167 insertNodeAt(targetBlockquote, start, editingState); |
| 164 } else | 168 } else |
| 165 insertNodeBefore(targetBlockquote, outerBlock, editingState); | 169 insertNodeBefore(targetBlockquote, outerBlock, editingState); |
| 166 if (editingState->isAborted()) | 170 if (editingState->isAborted()) |
| 167 return; | 171 return; |
| 168 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 172 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 169 startOfContents = VisiblePosition::inParentAfterNode(*targetBlockquote); | 173 startOfContents = VisiblePosition::inParentAfterNode(*targetBlockquote); |
| 170 } | 174 } |
| 171 | 175 |
| 172 VisiblePosition endOfContents = createVisiblePosition(end); | 176 VisiblePosition endOfContents = createVisiblePosition(end); |
| 173 if (startOfContents.isNull() || endOfContents.isNull()) | 177 if (startOfContents.isNull() || endOfContents.isNull()) |
| 174 return; | 178 return; |
| 175 moveParagraphWithClones(startOfContents, endOfContents, targetBlockquote, | 179 moveParagraphWithClones(startOfContents, endOfContents, targetBlockquote, |
| 176 outerBlock, editingState); | 180 outerBlock, editingState); |
| 177 } | 181 } |
| 178 | 182 |
| 179 void IndentOutdentCommand::outdentParagraph(EditingState* editingState) { | 183 void IndentOutdentCommand::outdentParagraph(EditingState* editingState) { |
| 180 VisiblePosition visibleStartOfParagraph = | 184 VisiblePosition visibleStartOfParagraph = |
| 181 startOfParagraph(endingSelection().visibleStart()); | 185 startOfParagraph(endingSelection().visibleStart()); |
| 182 VisiblePosition visibleEndOfParagraph = | 186 VisiblePosition visibleEndOfParagraph = |
| 183 endOfParagraph(visibleStartOfParagraph); | 187 endOfParagraph(visibleStartOfParagraph); |
| 184 | 188 |
| 185 HTMLElement* enclosingElement = toHTMLElement( | 189 HTMLElement* enclosingElement = toHTMLElement( |
| 186 enclosingNodeOfType(visibleStartOfParagraph.deepEquivalent(), | 190 enclosingNodeOfType(visibleStartOfParagraph.deepEquivalent(), |
| 187 &isHTMLListOrBlockquoteElement)); | 191 &isHTMLListOrBlockquoteElement)); |
| 188 if (!enclosingElement || | 192 // We can't outdent if there is no place to go! |
| 189 !hasEditableStyle( | 193 if (!enclosingElement || !hasEditableStyle(*enclosingElement->parentNode())) |
| 190 *enclosingElement | |
| 191 ->parentNode())) // We can't outdent if there is no place to go! | |
| 192 return; | 194 return; |
| 193 | 195 |
| 194 // Use InsertListCommand to remove the selection from the list | 196 // Use InsertListCommand to remove the selection from the list |
| 195 if (isHTMLOListElement(*enclosingElement)) { | 197 if (isHTMLOListElement(*enclosingElement)) { |
| 196 applyCommandToComposite( | 198 applyCommandToComposite( |
| 197 InsertListCommand::create(document(), InsertListCommand::OrderedList), | 199 InsertListCommand::create(document(), InsertListCommand::OrderedList), |
| 198 editingState); | 200 editingState); |
| 199 return; | 201 return; |
| 200 } | 202 } |
| 201 if (isHTMLUListElement(*enclosingElement)) { | 203 if (isHTMLUListElement(*enclosingElement)) { |
| 202 applyCommandToComposite( | 204 applyCommandToComposite( |
| 203 InsertListCommand::create(document(), InsertListCommand::UnorderedList), | 205 InsertListCommand::create(document(), InsertListCommand::UnorderedList), |
| 204 editingState); | 206 editingState); |
| 205 return; | 207 return; |
| 206 } | 208 } |
| 207 | 209 |
| 208 // The selection is inside a blockquote i.e. enclosingNode is a blockquote | 210 // The selection is inside a blockquote i.e. enclosingNode is a blockquote |
| 209 VisiblePosition positionInEnclosingBlock = | 211 VisiblePosition positionInEnclosingBlock = |
| 210 VisiblePosition::firstPositionInNode(enclosingElement); | 212 VisiblePosition::firstPositionInNode(enclosingElement); |
| 211 // If the blockquote is inline, the start of the enclosing block coincides wit
h | 213 // If the blockquote is inline, the start of the enclosing block coincides |
| 212 // positionInEnclosingBlock. | 214 // with positionInEnclosingBlock. |
| 213 VisiblePosition startOfEnclosingBlock = | 215 VisiblePosition startOfEnclosingBlock = |
| 214 (enclosingElement->layoutObject() && | 216 (enclosingElement->layoutObject() && |
| 215 enclosingElement->layoutObject()->isInline()) | 217 enclosingElement->layoutObject()->isInline()) |
| 216 ? positionInEnclosingBlock | 218 ? positionInEnclosingBlock |
| 217 : startOfBlock(positionInEnclosingBlock); | 219 : startOfBlock(positionInEnclosingBlock); |
| 218 VisiblePosition lastPositionInEnclosingBlock = | 220 VisiblePosition lastPositionInEnclosingBlock = |
| 219 VisiblePosition::lastPositionInNode(enclosingElement); | 221 VisiblePosition::lastPositionInNode(enclosingElement); |
| 220 VisiblePosition endOfEnclosingBlock = | 222 VisiblePosition endOfEnclosingBlock = |
| 221 endOfBlock(lastPositionInEnclosingBlock); | 223 endOfBlock(lastPositionInEnclosingBlock); |
| 222 if (visibleStartOfParagraph.deepEquivalent() == | 224 if (visibleStartOfParagraph.deepEquivalent() == |
| 223 startOfEnclosingBlock.deepEquivalent() && | 225 startOfEnclosingBlock.deepEquivalent() && |
| 224 visibleEndOfParagraph.deepEquivalent() == | 226 visibleEndOfParagraph.deepEquivalent() == |
| 225 endOfEnclosingBlock.deepEquivalent()) { | 227 endOfEnclosingBlock.deepEquivalent()) { |
| 226 // The blockquote doesn't contain anything outside the paragraph, so it can
be totally removed. | 228 // The blockquote doesn't contain anything outside the paragraph, so it can |
| 229 // be totally removed. |
| 227 Node* splitPoint = enclosingElement->nextSibling(); | 230 Node* splitPoint = enclosingElement->nextSibling(); |
| 228 removeNodePreservingChildren(enclosingElement, editingState); | 231 removeNodePreservingChildren(enclosingElement, editingState); |
| 229 if (editingState->isAborted()) | 232 if (editingState->isAborted()) |
| 230 return; | 233 return; |
| 231 // outdentRegion() assumes it is operating on the first paragraph of an encl
osing blockquote, but if there are multiply nested blockquotes and we've | 234 // outdentRegion() assumes it is operating on the first paragraph of an |
| 232 // just removed one, then this assumption isn't true. By splitting the next
containing blockquote after this node, we keep this assumption true | 235 // enclosing blockquote, but if there are multiply nested blockquotes and |
| 236 // we've just removed one, then this assumption isn't true. By splitting the |
| 237 // next containing blockquote after this node, we keep this assumption true |
| 233 if (splitPoint) { | 238 if (splitPoint) { |
| 234 if (Element* splitPointParent = splitPoint->parentElement()) { | 239 if (Element* splitPointParent = splitPoint->parentElement()) { |
| 240 // We can't outdent if there is no place to go! |
| 235 if (splitPointParent->hasTagName(blockquoteTag) && | 241 if (splitPointParent->hasTagName(blockquoteTag) && |
| 236 !splitPoint->hasTagName(blockquoteTag) && | 242 !splitPoint->hasTagName(blockquoteTag) && |
| 237 hasEditableStyle( | 243 hasEditableStyle(*splitPointParent->parentNode())) |
| 238 *splitPointParent | |
| 239 ->parentNode())) // We can't outdent if there is no place
to go! | |
| 240 splitElement(splitPointParent, splitPoint); | 244 splitElement(splitPointParent, splitPoint); |
| 241 } | 245 } |
| 242 } | 246 } |
| 243 | 247 |
| 244 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 248 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 245 visibleStartOfParagraph = | 249 visibleStartOfParagraph = |
| 246 createVisiblePosition(visibleStartOfParagraph.deepEquivalent()); | 250 createVisiblePosition(visibleStartOfParagraph.deepEquivalent()); |
| 247 if (visibleStartOfParagraph.isNotNull() && | 251 if (visibleStartOfParagraph.isNotNull() && |
| 248 !isStartOfParagraph(visibleStartOfParagraph)) { | 252 !isStartOfParagraph(visibleStartOfParagraph)) { |
| 249 insertNodeAt(HTMLBRElement::create(document()), | 253 insertNodeAt(HTMLBRElement::create(document()), |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 insertNodeBefore(placeholder, splitBlockquoteNode, editingState); | 304 insertNodeBefore(placeholder, splitBlockquoteNode, editingState); |
| 301 if (editingState->isAborted()) | 305 if (editingState->isAborted()) |
| 302 return; | 306 return; |
| 303 | 307 |
| 304 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 308 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 305 moveParagraph(startOfParagraphToMove, endOfParagraphToMove, | 309 moveParagraph(startOfParagraphToMove, endOfParagraphToMove, |
| 306 VisiblePosition::beforeNode(placeholder), editingState, | 310 VisiblePosition::beforeNode(placeholder), editingState, |
| 307 PreserveSelection); | 311 PreserveSelection); |
| 308 } | 312 } |
| 309 | 313 |
| 310 // FIXME: We should merge this function with ApplyBlockElementCommand::formatSel
ection | 314 // FIXME: We should merge this function with |
| 315 // ApplyBlockElementCommand::formatSelection |
| 311 void IndentOutdentCommand::outdentRegion( | 316 void IndentOutdentCommand::outdentRegion( |
| 312 const VisiblePosition& startOfSelection, | 317 const VisiblePosition& startOfSelection, |
| 313 const VisiblePosition& endOfSelection, | 318 const VisiblePosition& endOfSelection, |
| 314 EditingState* editingState) { | 319 EditingState* editingState) { |
| 315 VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection); | 320 VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection); |
| 316 VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection); | 321 VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection); |
| 317 | 322 |
| 318 if (endOfCurrentParagraph.deepEquivalent() == | 323 if (endOfCurrentParagraph.deepEquivalent() == |
| 319 endOfLastParagraph.deepEquivalent()) { | 324 endOfLastParagraph.deepEquivalent()) { |
| 320 outdentParagraph(editingState); | 325 outdentParagraph(editingState); |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 383 else | 388 else |
| 384 indentIntoBlockquote(start, end, blockquoteForNextIndent, editingState); | 389 indentIntoBlockquote(start, end, blockquoteForNextIndent, editingState); |
| 385 } | 390 } |
| 386 | 391 |
| 387 InputEvent::InputType IndentOutdentCommand::inputType() const { | 392 InputEvent::InputType IndentOutdentCommand::inputType() const { |
| 388 return m_typeOfAction == Indent ? InputEvent::InputType::Indent | 393 return m_typeOfAction == Indent ? InputEvent::InputType::Indent |
| 389 : InputEvent::InputType::Outdent; | 394 : InputEvent::InputType::Outdent; |
| 390 } | 395 } |
| 391 | 396 |
| 392 } // namespace blink | 397 } // namespace blink |
| OLD | NEW |