// FIXME: We should merge this function with // ApplyBlockElementCommand::formatSelection void IndentOutdentCommand::outdentRegion( const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection, EditingState* editingState) { VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection); VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection); if (endOfCurrentParagraph.deepEquivalent() == endOfLastParagraph.deepEquivalent()) { outdentParagraph(editingState); return; } Position originalSelectionEnd = endingSelection().end(); Position endAfterSelection = endOfParagraph(nextPositionOf(endOfLastParagraph)).deepEquivalent(); while (endOfCurrentParagraph.deepEquivalent() != endAfterSelection) { PositionWithAffinity endOfNextParagraph = endOfParagraph(nextPositionOf(endOfCurrentParagraph)) .toPositionWithAffinity(); if (endOfCurrentParagraph.deepEquivalent() == endOfLastParagraph.deepEquivalent()) { SelectionInDOMTree::Builder builder; if (originalSelectionEnd.isNotNull()) builder.collapse(originalSelectionEnd); setEndingSelection(builder.build()); } else { setEndingSelection(SelectionInDOMTree::Builder() .collapse(endOfCurrentParagraph.deepEquivalent()) .build()); } outdentParagraph(editingState); if (editingState->isAborted()) return; // outdentParagraph could move more than one paragraph if the paragraph // is in a list item. As a result, endAfterSelection and endOfNextParagraph // could refer to positions no longer in the document. if (endAfterSelection.isNotNull() && !endAfterSelection.isConnected()) break; document().updateStyleAndLayoutIgnorePendingStylesheets(); if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.isConnected()) { endOfCurrentParagraph = createVisiblePosition(endingSelection().end()); endOfNextParagraph = endOfParagraph(nextPositionOf(endOfCurrentParagraph)) .toPositionWithAffinity(); } endOfCurrentParagraph = createVisiblePosition(endOfNextParagraph); } }
TEST_F(SelectionTest, caret) { setBodyContent("<div id='sample'>abcdef</div>"); Element* sample = document().getElementById("sample"); Position position(Position(sample->firstChild(), 2)); SelectionInDOMTree::Builder builder; builder.collapse(position); const SelectionInDOMTree& selection = builder.build(); EXPECT_EQ(TextAffinity::Downstream, selection.affinity()); EXPECT_EQ(CharacterGranularity, selection.granularity()); EXPECT_FALSE(selection.hasTrailingWhitespace()); EXPECT_FALSE(selection.isDirectional()); EXPECT_FALSE(selection.isNone()); EXPECT_EQ(position, selection.base()); EXPECT_EQ(position, selection.extent()); }
void DOMSelection::collapseToStart(ExceptionState& exceptionState) { if (!isAvailable()) return; const VisibleSelection& selection = frame()->selection().selection(); if (selection.isNone()) { exceptionState.throwDOMException(InvalidStateError, "there is no selection."); return; } // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets // needs to be audited. See http://crbug.com/590369 for more details. // In the long term, we should change FrameSelection::setSelection to take a // parameter that does not require clean layout, so that modifying selection // no longer performs synchronous layout by itself. frame()->document()->updateStyleAndLayoutIgnorePendingStylesheets(); SelectionInDOMTree::Builder builder; builder.collapse(selection.start()); frame()->selection().setSelection(createVisibleSelection(builder.build())); }