PassRefPtr<Range> Selection::toRange() const { if (isNone()) return 0; // Make sure we have an updated layout since this function is called // in the course of running edit commands which modify the DOM. // Failing to call this can result in equivalentXXXPosition calls returning // incorrect results. m_start.node()->document()->updateLayout(); Position s, e; if (isCaret()) { // If the selection is a caret, move the range start upstream. This helps us match // the conventions of text editors tested, which make style determinations based // on the character before the caret, if any. s = rangeCompliantEquivalent(m_start.upstream()); e = s; } else { // If the selection is a range, select the minimum range that encompasses the selection. // Again, this is to match the conventions of text editors tested, which make style // determinations based on the first character of the selection. // For instance, this operation helps to make sure that the "X" selected below is the // only thing selected. The range should not be allowed to "leak" out to the end of the // previous text node, or to the beginning of the next text node, each of which has a // different style. // // On a treasure map, <b>X</b> marks the spot. // ^ selected // ASSERT(isRange()); s = m_start.downstream(); e = m_end.upstream(); if (Range::compareBoundaryPoints(s.node(), s.offset(), e.node(), e.offset()) > 0) { // Make sure the start is before the end. // The end can wind up before the start if collapsed whitespace is the only thing selected. Position tmp = s; s = e; e = tmp; } s = rangeCompliantEquivalent(s); e = rangeCompliantEquivalent(e); } ExceptionCode ec = 0; RefPtr<Range> result(new Range(s.node()->document())); result->setStart(s.node(), s.offset(), ec); if (ec) { LOG_ERROR("Exception setting Range start from Selection: %d", ec); return 0; } result->setEnd(e.node(), e.offset(), ec); if (ec) { LOG_ERROR("Exception setting Range end from Selection: %d", ec); return 0; } return result.release(); }
PassRefPtr<Range> VisibleSelection::toNormalizedRange() const { if (isNone()) return 0; // Make sure we have an updated layout since this function is called // in the course of running edit commands which modify the DOM. // Failing to call this can result in equivalentXXXPosition calls returning // incorrect results. m_start.anchorNode()->document().updateLayout(); // Check again, because updating layout can clear the selection. if (isNone()) return 0; Position s, e; if (isCaret()) { // If the selection is a caret, move the range start upstream. This helps us match // the conventions of text editors tested, which make style determinations based // on the character before the caret, if any. s = m_start.upstream().parentAnchoredEquivalent(); e = s; } else { // If the selection is a range, select the minimum range that encompasses the selection. // Again, this is to match the conventions of text editors tested, which make style // determinations based on the first character of the selection. // For instance, this operation helps to make sure that the "X" selected below is the // only thing selected. The range should not be allowed to "leak" out to the end of the // previous text node, or to the beginning of the next text node, each of which has a // different style. // // On a treasure map, <b>X</b> marks the spot. // ^ selected // ASSERT(isRange()); s = m_start.downstream(); e = m_end.upstream(); if (comparePositions(s, e) > 0) { // Make sure the start is before the end. // The end can wind up before the start if collapsed whitespace is the only thing selected. Position tmp = s; s = e; e = tmp; } s = s.parentAnchoredEquivalent(); e = e.parentAnchoredEquivalent(); } if (!s.containerNode() || !e.containerNode()) return 0; // VisibleSelections are supposed to always be valid. This constructor will ASSERT // if a valid range could not be created, which is fine for this callsite. return Range::create(s.anchorNode()->document(), s, e); }