blob: 4378ba59eb550f94552ca480dcf5fce72ffa1fb9 [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All
* rights reserved.
* Copyright (C) 2005 Alexey Proskuryakov.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "core/editing/PlainTextRange.h"
#include "core/dom/ContainerNode.h"
#include "core/dom/Document.h"
#include "core/dom/Range.h"
#include "core/editing/EphemeralRange.h"
#include "core/editing/VisiblePosition.h"
#include "core/editing/VisibleUnits.h"
#include "core/editing/iterators/TextIterator.h"
namespace blink {
PlainTextRange::PlainTextRange() : m_start(kNotFound), m_end(kNotFound) {}
PlainTextRange::PlainTextRange(int location)
: m_start(location), m_end(location) {
DCHECK_GE(location, 0);
}
PlainTextRange::PlainTextRange(int start, int end)
: m_start(start), m_end(end) {
DCHECK_GE(start, 0);
DCHECK_GE(end, 0);
DCHECK_LE(start, end);
}
EphemeralRange PlainTextRange::createRange(const ContainerNode& scope) const {
return createRangeFor(scope, ForGeneric);
}
EphemeralRange PlainTextRange::createRangeForSelection(
const ContainerNode& scope) const {
return createRangeFor(scope, ForSelection);
}
EphemeralRange PlainTextRange::createRangeFor(const ContainerNode& scope,
GetRangeFor getRangeFor) const {
DCHECK(isNotNull());
size_t docTextPosition = 0;
bool startRangeFound = false;
Position textRunStartPosition;
Position textRunEndPosition;
TextIteratorBehaviorFlags behaviorFlags =
TextIteratorEmitsObjectReplacementCharacter;
if (getRangeFor == ForSelection)
behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions;
auto range = EphemeralRange::rangeOfContents(scope);
TextIterator it(range.startPosition(), range.endPosition(), behaviorFlags);
// FIXME: the atEnd() check shouldn't be necessary, workaround for
// <http://bugs.webkit.org/show_bug.cgi?id=6289>.
if (!start() && !length() && it.atEnd())
return EphemeralRange(Position(it.currentContainer(), 0));
Position resultStart = Position(&scope.document(), 0);
Position resultEnd = resultStart;
for (; !it.atEnd(); it.advance()) {
int len = it.length();
textRunStartPosition = it.startPositionInCurrentContainer();
textRunEndPosition = it.endPositionInCurrentContainer();
bool foundStart =
start() >= docTextPosition && start() <= docTextPosition + len;
bool foundEnd = end() >= docTextPosition && end() <= docTextPosition + len;
// Fix textRunRange->endPosition(), but only if foundStart || foundEnd,
// because it is only in those cases that textRunRange is used.
if (foundEnd) {
// FIXME: This is a workaround for the fact that the end of a run
// is often at the wrong position for emitted '\n's or if the
// layoutObject of the current node is a replaced element.
if (len == 1 &&
(it.characterAt(0) == '\n' || it.isInsideAtomicInlineElement())) {
it.advance();
if (!it.atEnd()) {
textRunEndPosition = it.startPositionInCurrentContainer();
} else {
Position runEnd =
nextPositionOf(createVisiblePosition(textRunStartPosition))
.deepEquivalent();
if (runEnd.isNotNull())
textRunEndPosition = runEnd;
}
}
}
if (foundStart) {
startRangeFound = true;
if (textRunStartPosition.computeContainerNode()->isTextNode()) {
int offset = start() - docTextPosition;
resultStart =
Position(textRunStartPosition.computeContainerNode(),
offset + textRunStartPosition.offsetInContainerNode());
} else {
if (start() == docTextPosition)
resultStart = textRunStartPosition;
else
resultStart = textRunEndPosition;
}
}
if (foundEnd) {
if (textRunStartPosition.computeContainerNode()->isTextNode()) {
int offset = end() - docTextPosition;
resultEnd =
Position(textRunStartPosition.computeContainerNode(),
offset + textRunStartPosition.offsetInContainerNode());
} else {
if (end() == docTextPosition)
resultEnd = textRunStartPosition;
else
resultEnd = textRunEndPosition;
}
docTextPosition += len;
break;
}
docTextPosition += len;
}
if (!startRangeFound)
return EphemeralRange();
if (length() && end() > docTextPosition) { // end() is out of bounds
resultEnd = textRunEndPosition;
}
return EphemeralRange(resultStart.toOffsetInAnchor(),
resultEnd.toOffsetInAnchor());
}
PlainTextRange PlainTextRange::create(const ContainerNode& scope,
const EphemeralRange& range) {
if (range.isNull())
return PlainTextRange();
// The critical assumption is that this only gets called with ranges that
// concentrate on a given area containing the selection root. This is done
// because of text fields and textareas. The DOM for those is not
// directly in the document DOM, so ensure that the range does not cross a
// boundary of one of those.
Node* startContainer = range.startPosition().computeContainerNode();
if (startContainer != &scope && !startContainer->isDescendantOf(&scope))
return PlainTextRange();
Node* endContainer = range.endPosition().computeContainerNode();
if (endContainer != scope && !endContainer->isDescendantOf(&scope))
return PlainTextRange();
DocumentLifecycle::DisallowTransitionScope disallowTransition(
scope.document().lifecycle());
size_t start = TextIterator::rangeLength(
Position(&const_cast<ContainerNode&>(scope), 0), range.startPosition());
size_t end = TextIterator::rangeLength(
Position(&const_cast<ContainerNode&>(scope), 0), range.endPosition());
return PlainTextRange(start, end);
}
PlainTextRange PlainTextRange::create(const ContainerNode& scope,
const Range& range) {
return create(scope, EphemeralRange(&range));
}
} // namespace blink