VisibleSelection visibleSelectionForRangeInputElement(Element* element, int start, int end) { if (DOMSupport::toTextControlElement(element)) { RenderTextControl* textRender = toRenderTextControl(element->renderer()); if (!textRender) return VisibleSelection(); VisiblePosition startPosition = textRender->visiblePositionForIndex(start); VisiblePosition endPosition; if (start == end) endPosition = startPosition; else endPosition = textRender->visiblePositionForIndex(end); return VisibleSelection(startPosition, endPosition); } // Must be content editable, generate the range. RefPtr<Range> selectionRange = TextIterator::rangeFromLocationAndLength(element, start, end - start); if (start == end) return VisibleSelection(selectionRange.get()->startPosition(), DOWNSTREAM); VisiblePosition visibleStart(selectionRange->startPosition(), DOWNSTREAM); VisiblePosition visibleEnd(selectionRange->endPosition(), SEL_DEFAULT_AFFINITY); return VisibleSelection(visibleStart, visibleEnd); }
bool VisibleSelection::isAll(EditingBoundaryCrossingRule rule) const { return !nonBoundaryShadowTreeRootNode() && visibleStart().previous(rule).isNull() && visibleEnd().next(rule).isNull(); }
// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange? // FIXME: At least, annotation and style info should probably not be included in range.markupString() static String createMarkupInternal(Document& document, const Range* range, const Range* updatedRange, Vector<RawPtr<Node> >* nodes, EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAncestor) { ASSERT(range); ASSERT(updatedRange); DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, ("<br class=\"" AppleInterchangeNewline "\">")); bool collapsed = updatedRange->collapsed(); if (collapsed) return emptyString(); Node* commonAncestor = updatedRange->commonAncestorContainer(); if (!commonAncestor) return emptyString(); document.updateLayoutIgnorePendingStylesheets(); // FIXME(sky): Remove this variable. HTMLElement* fullySelectedRoot = 0; // FIXME: Do this for all fully selected blocks, not just the body. HTMLElement* specialCommonAncestor = highestAncestorToWrapMarkup(updatedRange, shouldAnnotate, constrainingAncestor); StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, updatedRange, specialCommonAncestor); Node* pastEnd = updatedRange->pastLastNode(); Node* startNode = updatedRange->firstNode(); VisiblePosition visibleStart(updatedRange->startPosition(), VP_DEFAULT_AFFINITY); VisiblePosition visibleEnd(updatedRange->endPosition(), VP_DEFAULT_AFFINITY); if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleStart)) { if (visibleStart == visibleEnd.previous()) return interchangeNewlineString; accumulator.appendString(interchangeNewlineString); startNode = visibleStart.next().deepEquivalent().deprecatedNode(); if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0, ASSERT_NO_EXCEPTION) >= 0) return interchangeNewlineString; } Node* lastClosed = accumulator.serializeNodes(startNode, pastEnd); if (specialCommonAncestor && lastClosed) { // Also include all of the ancestors of lastClosed up to this special ancestor. for (ContainerNode* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) { if (ancestor == fullySelectedRoot && !convertBlocksToInlines) { RefPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot); if (fullySelectedRootStyle->style()) { // Reset the CSS properties to avoid an assertion error in addStyleMarkup(). // This assertion is caused at least when we select all text of a <body> element whose // 'text-decoration' property is "inherit", and copy it. if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyTextDecoration)) fullySelectedRootStyle->style()->setProperty(CSSPropertyTextDecoration, CSSValueNone); if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyWebkitTextDecorationsInEffect)) fullySelectedRootStyle->style()->setProperty(CSSPropertyWebkitTextDecorationsInEffect, CSSValueNone); accumulator.wrapWithStyleNode(fullySelectedRootStyle->style(), document, true); } } else { accumulator.wrapWithNode(*ancestor, convertBlocksToInlines); } if (nodes) nodes->append(ancestor); if (ancestor == specialCommonAncestor) break; } } // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally. if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous())) accumulator.appendString(interchangeNewlineString); return accumulator.takeResults(); }
bool VisibleSelection::isAll(StayInEditableContent stayInEditableContent) const { return !shadowTreeRootNode() && visibleStart().previous(stayInEditableContent).isNull() && visibleEnd().next(stayInEditableContent).isNull(); }
void TypingCommand::deleteKeyPressed(TextGranularity granularity) { Selection selectionToDelete; Selection selectionAfterUndo; switch (endingSelection().state()) { case Selection::RANGE: selectionToDelete = endingSelection(); selectionAfterUndo = selectionToDelete; break; case Selection::CARET: { m_smartDelete = false; SelectionController selection; selection.setSelection(endingSelection()); selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity); // When the caret is at the start of the editable area in an empty list item, break out of the list item. if (endingSelection().visibleStart().previous(true).isNull()) { if (breakOutOfEmptyListItem()) { typingAddedToOpenCommand(); return; } } VisiblePosition visibleStart(endingSelection().visibleStart()); // If the caret is at the start of a paragraph after a table, move content into the last table cell. if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(true))) { // Unless the caret is just before a table. We don't want to move a table into the last table cell. if (isLastPositionBeforeTable(visibleStart)) return; // Extend the selection backward into the last cell, then deletion will handle the move. selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity); // If the caret is just after a table, select the table and don't delete anything. } else if (Node* table = isFirstPositionAfterTable(visibleStart)) { setEndingSelection(Selection(Position(table, 0), endingSelection().start(), DOWNSTREAM)); typingAddedToOpenCommand(); return; } selectionToDelete = selection.selection(); if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start()) selectionAfterUndo = selectionToDelete; else // It's a little tricky to compute what the starting selection would have been in the original document. // We can't let the Selection class's validation kick in or it'll adjust for us based on // the current state of the document and we'll get the wrong result. selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent()); break; } case Selection::NONE: ASSERT_NOT_REACHED(); break; } if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) { // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion. // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete // more text than you insert. In that case all of the text that was around originally should be selected. if (m_openedByBackwardDelete) setStartingSelection(selectionAfterUndo); CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete); setSmartDelete(false); typingAddedToOpenCommand(); } }