VisibleSelection::VisibleSelection(const VisiblePosition& base, const VisiblePosition& extent) : m_base(base.deepEquivalent()) , m_extent(extent.deepEquivalent()) , m_affinity(base.affinity()) { validate(); }
static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition) { // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line. // So let's update the position to include that. VisiblePosition tempPosition; VisiblePosition startPosition = visiblePosition; Position p; RenderObject* renderer; while (true) { tempPosition = startPosition.previous(); if (tempPosition.isNull()) break; p = tempPosition.deepEquivalent(); if (!p.node()) break; renderer = p.node()->renderer(); if (!renderer || renderer->isRenderBlock() && !p.offset()) break; InlineBox* box; int ignoredCaretOffset; p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset); if (box) break; startPosition = tempPosition; } return startPosition; }
VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const { if (visiblePos1.isNull() || visiblePos2.isNull()) return VisiblePositionRange(); VisiblePosition startPos; VisiblePosition endPos; bool alreadyInOrder; // upstream is ordered before downstream for the same position if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM) alreadyInOrder = false; // use selection order to see if the positions are in order else alreadyInOrder = Selection(visiblePos1, visiblePos2).isBaseFirst(); if (alreadyInOrder) { startPos = visiblePos1; endPos = visiblePos2; } else { startPos = visiblePos2; endPos = visiblePos1; } return VisiblePositionRange(startPos, endPos); }
void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos) { // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence. // This also allows callers to check for failure by looking at textMarkerData upon return. memset(&textMarkerData, 0, sizeof(TextMarkerData)); if (visiblePos.isNull()) return; Position deepPos = visiblePos.deepEquivalent(); Node* domNode = deepPos.deprecatedNode(); ASSERT(domNode); if (!domNode) return; if (domNode->isHTMLElement()) { HTMLInputElement* inputElement = domNode->toInputElement(); if (inputElement && inputElement->isPasswordField()) return; } // find or create an accessibility object for this node AXObjectCache* cache = domNode->document()->axObjectCache(); RefPtr<AccessibilityObject> obj = cache->getOrCreate(domNode); textMarkerData.axID = obj.get()->axObjectID(); textMarkerData.node = domNode; textMarkerData.offset = deepPos.deprecatedEditingOffset(); textMarkerData.affinity = visiblePos.affinity(); cache->setNodeInUse(domNode); }
VisibleSelection::VisibleSelection(const VisiblePosition& pos) : m_base(pos.deepEquivalent()) , m_extent(pos.deepEquivalent()) , m_affinity(pos.affinity()) { validate(); }
Selection::Selection(const VisiblePosition& base, const VisiblePosition& extent) : m_base(base.deepEquivalent()) , m_extent(extent.deepEquivalent()) , m_affinity(base.affinity()) , m_granularity(CharacterGranularity) { validate(); }
Selection::Selection(const VisiblePosition& pos) : m_base(pos.deepEquivalent()) , m_extent(pos.deepEquivalent()) , m_affinity(pos.affinity()) , m_granularity(CharacterGranularity) { validate(); }
VisibleSelection::VisibleSelection(const VisiblePosition& pos, bool selectOnlyLetters) : m_base(pos.deepEquivalent()) , m_extent(pos.deepEquivalent()) , m_affinity(pos.affinity()) , m_selectOnlyLetters(selectOnlyLetters) { validate(); }
VisibleSelection::VisibleSelection(const VisiblePosition& pos, bool isDirectional) : m_base(pos.deepEquivalent()) , m_extent(pos.deepEquivalent()) , m_affinity(pos.affinity()) , m_isDirectional(isDirectional) { validate(); }
bool visiblePositionsOnDifferentLines(const VisiblePosition &pos1, const VisiblePosition &pos2) { if (pos1.isNull() || pos2.isNull()) return false; if (pos1 == pos2) return false; Position p1 = pos1.deepEquivalent(); Position p2 = pos2.deepEquivalent(); RenderObject *r1 = p1.node()->renderer(); RenderObject *r2 = p2.node()->renderer(); if (r1->isBlockFlow() || r2->isBlockFlow()) return r1 == r2 ? false : true; InlineBox *b1 = r1 ? r1->inlineBox(p1.offset(), pos1.affinity()) : 0; InlineBox *b2 = r2 ? r2->inlineBox(p2.offset(), pos2.affinity()) : 0; return (b1 && b2 && b1->root() != b2->root()); }
VisibleSelection::VisibleSelection(const VisiblePosition& base, const VisiblePosition& extent, bool isDirectional) : m_base(base.deepEquivalent()) , m_extent(extent.deepEquivalent()) , m_affinity(base.affinity()) , m_changeObserver(nullptr) , m_isDirectional(isDirectional) { validate(); }
void setAffinityUsingLinePosition(VisiblePosition &pos) { // When not moving across line wrap, make sure to end up with DOWNSTREAM affinity. if (pos.isNotNull() && pos.affinity() == UPSTREAM) { VisiblePosition temp(pos); temp.setAffinity(DOWNSTREAM); if (!visiblePositionsOnDifferentLines(temp, pos)) pos.setAffinity(DOWNSTREAM); } }
void SelectionController::moveTo(const VisiblePosition &pos) { setSelection(Selection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity())); m_needsLayout = true; }
void SelectionController::setExtent(const VisiblePosition &pos) { setSelection(Selection(m_sel.base(), pos.deepEquivalent(), pos.affinity())); m_needsLayout = true; }
RenderedPosition::RenderedPosition(const VisiblePosition& position) : RenderedPosition(position.deepEquivalent(), position.affinity()) { }
SelectionController::SelectionController(const VisiblePosition &visiblePos) : m_needsLayout(true) , m_modifyBiasSet(false) { setSelection(Selection(visiblePos.deepEquivalent(), visiblePos.deepEquivalent(), visiblePos.affinity())); }
void DeleteSelectionCommand::initializePositionData() { initializeStartEnd(); Node* startCell = enclosingTableCell(m_upstreamStart.node()); Node* endCell = enclosingTableCell(m_downstreamEnd.node()); // Don't move content between parts of a table or between table and non-table content. // FIXME: This isn't right. A borderless table with two rows and a single column would appear as two paragraphs. if ((startCell || endCell) && endCell != startCell) m_mergeBlocksAfterDelete = false; // Usually the start and the end of the selection to delete are pulled together as a result of the deletion. // Sometimes they aren't (like when no merge is requested), so we must choose one position to hold the caret // and receive the placeholder after deletion. VisiblePosition visibleEnd(m_downstreamEnd); if (m_mergeBlocksAfterDelete && !isEndOfParagraph(visibleEnd)) m_endingPosition = m_downstreamEnd; else m_endingPosition = m_downstreamStart; // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity()); m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY); if (m_smartDelete) { // skip smart delete if the selection to delete already starts or ends with whitespace Position pos = VisiblePosition(m_upstreamStart, m_selectionToDelete.affinity()).deepEquivalent(); bool skipSmartDelete = pos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull(); if (!skipSmartDelete) skipSmartDelete = m_downstreamEnd.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull(); // extend selection upstream if there is whitespace there bool hasLeadingWhitespaceBeforeAdjustment = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity(), true).isNotNull(); if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) { VisiblePosition visiblePos = VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY).previous(); pos = visiblePos.deepEquivalent(); // Expand out one character upstream for smart delete and recalculate // positions based on this change. m_upstreamStart = pos.upstream(); m_downstreamStart = pos.downstream(); m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(visiblePos.affinity()); } // trailing whitespace is only considered for smart delete if there is no leading // whitespace, as in the case where you double-click the first word of a paragraph. if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment && m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull()) { // Expand out one character downstream for smart delete and recalculate // positions based on this change. pos = VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY).next().deepEquivalent(); m_upstreamEnd = pos.upstream(); m_downstreamEnd = pos.downstream(); m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY); } } // // Handle setting start and end blocks and the start node. // m_startBlock = m_downstreamStart.node()->enclosingBlockFlowOrTableElement(); m_endBlock = m_upstreamEnd.node()->enclosingBlockFlowOrTableElement(); }
SelectionController::SelectionController(const VisiblePosition &base, const VisiblePosition &extent) : m_needsLayout(true) , m_modifyBiasSet(false) { setSelection(Selection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity())); }
bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition) { return updateCaretRect(document, PositionWithAffinity(caretPosition.deepEquivalent(), caretPosition.affinity())); }
void DeleteSelectionCommand::initializePositionData() { Position start, end; initializeStartEnd(start, end); m_upstreamStart = start.upstream(); m_downstreamStart = start.downstream(); m_upstreamEnd = end.upstream(); m_downstreamEnd = end.downstream(); m_startRoot = editableRootForPosition(start); m_endRoot = editableRootForPosition(end); m_startTableRow = enclosingNodeOfType(start, &isTableRow); m_endTableRow = enclosingNodeOfType(end, &isTableRow); // Don't move content out of a table cell. // If the cell is non-editable, enclosingNodeOfType won't return it by default, so // tell that function that we don't care if it returns non-editable nodes. Node* startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell, CanCrossEditingBoundary); Node* endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell, CanCrossEditingBoundary); // FIXME: This isn't right. A borderless table with two rows and a single column would appear as two paragraphs. if (endCell && endCell != startCell) m_mergeBlocksAfterDelete = false; // Usually the start and the end of the selection to delete are pulled together as a result of the deletion. // Sometimes they aren't (like when no merge is requested), so we must choose one position to hold the caret // and receive the placeholder after deletion. VisiblePosition visibleEnd(m_downstreamEnd); if (m_mergeBlocksAfterDelete && !isEndOfParagraph(visibleEnd)) m_endingPosition = m_downstreamEnd; else m_endingPosition = m_downstreamStart; // We don't want to merge into a block if it will mean changing the quote level of content after deleting // selections that contain a whole number paragraphs plus a line break, since it is unclear to most users // that such a selection actually ends at the start of the next paragraph. This matches TextEdit behavior // for indented paragraphs. // Only apply this rule if the endingSelection is a range selection. If it is a caret, then other operations have created // the selection we're deleting (like the process of creating a selection to delete during a backspace), and the user isn't in the situation described above. if (numEnclosingMailBlockquotes(start) != numEnclosingMailBlockquotes(end) && isStartOfParagraph(visibleEnd) && isStartOfParagraph(VisiblePosition(start)) && endingSelection().isRange()) { m_mergeBlocksAfterDelete = false; m_pruneStartBlockIfNecessary = true; } // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity()); m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY); if (m_smartDelete) { // skip smart delete if the selection to delete already starts or ends with whitespace Position pos = VisiblePosition(m_upstreamStart, m_selectionToDelete.affinity()).deepEquivalent(); bool skipSmartDelete = pos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull(); if (!skipSmartDelete) skipSmartDelete = m_downstreamEnd.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull(); // extend selection upstream if there is whitespace there bool hasLeadingWhitespaceBeforeAdjustment = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity(), true).isNotNull(); if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) { VisiblePosition visiblePos = VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY).previous(); pos = visiblePos.deepEquivalent(); // Expand out one character upstream for smart delete and recalculate // positions based on this change. m_upstreamStart = pos.upstream(); m_downstreamStart = pos.downstream(); m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(visiblePos.affinity()); setStartingSelectionOnSmartDelete(m_upstreamStart, m_upstreamEnd); } // trailing whitespace is only considered for smart delete if there is no leading // whitespace, as in the case where you double-click the first word of a paragraph. if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment && m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull()) { // Expand out one character downstream for smart delete and recalculate // positions based on this change. pos = VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY).next().deepEquivalent(); m_upstreamEnd = pos.upstream(); m_downstreamEnd = pos.downstream(); m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY); setStartingSelectionOnSmartDelete(m_downstreamStart, m_downstreamEnd); } } // We must pass call parentAnchoredEquivalent on the positions since some editing positions // that appear inside their nodes aren't really inside them. [hr, 0] is one example. // FIXME: parentAnchoredEquivalent should eventually be moved into enclosing element getters // like the one below, since editing functions should obviously accept editing positions. // FIXME: Passing false to enclosingNodeOfType tells it that it's OK to return a non-editable // node. This was done to match existing behavior, but it seems wrong. m_startBlock = enclosingNodeOfType(m_downstreamStart.parentAnchoredEquivalent(), &isBlock, CanCrossEditingBoundary); m_endBlock = enclosingNodeOfType(m_upstreamEnd.parentAnchoredEquivalent(), &isBlock, CanCrossEditingBoundary); }
void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent) { setSelection(Selection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity())); m_needsLayout = true; }