mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
188 lines
7.5 KiB
C++
188 lines
7.5 KiB
C++
/*
|
|
* 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 "sky/engine/core/editing/PlainTextRange.h"
|
|
|
|
#include "sky/engine/core/dom/ContainerNode.h"
|
|
#include "sky/engine/core/dom/Document.h"
|
|
#include "sky/engine/core/dom/Range.h"
|
|
#include "sky/engine/core/editing/TextIterator.h"
|
|
#include "sky/engine/core/editing/VisiblePosition.h"
|
|
|
|
namespace blink {
|
|
|
|
PlainTextRange::PlainTextRange()
|
|
: m_start(kNotFound)
|
|
, m_end(kNotFound)
|
|
{
|
|
}
|
|
|
|
PlainTextRange::PlainTextRange(int location)
|
|
: m_start(location)
|
|
, m_end(location)
|
|
{
|
|
ASSERT(location >= 0);
|
|
}
|
|
|
|
PlainTextRange::PlainTextRange(int start, int end)
|
|
: m_start(start)
|
|
, m_end(end)
|
|
{
|
|
ASSERT(start >= 0);
|
|
ASSERT(end >= 0);
|
|
ASSERT(start <= end);
|
|
}
|
|
|
|
PassRefPtr<Range> PlainTextRange::createRange(const ContainerNode& scope) const
|
|
{
|
|
return createRangeFor(scope, ForGeneric);
|
|
}
|
|
|
|
PassRefPtr<Range> PlainTextRange::createRangeForSelection(const ContainerNode& scope) const
|
|
{
|
|
return createRangeFor(scope, ForSelection);
|
|
}
|
|
|
|
PassRefPtr<Range> PlainTextRange::createRangeFor(const ContainerNode& scope, GetRangeFor getRangeFor) const
|
|
{
|
|
ASSERT(isNotNull());
|
|
|
|
RefPtr<Range> resultRange = scope.document().createRange();
|
|
|
|
size_t docTextPosition = 0;
|
|
bool startRangeFound = false;
|
|
|
|
RefPtr<Range> textRunRange = nullptr;
|
|
|
|
TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacementCharacter;
|
|
if (getRangeFor == ForSelection)
|
|
behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions;
|
|
TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), 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()) {
|
|
textRunRange = it.range();
|
|
|
|
resultRange->setStart(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION);
|
|
resultRange->setEnd(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION);
|
|
|
|
return resultRange.release();
|
|
}
|
|
|
|
for (; !it.atEnd(); it.advance()) {
|
|
int len = it.length();
|
|
textRunRange = it.range();
|
|
|
|
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
|
|
// renderer of the current node is a replaced element.
|
|
if (len == 1 && (it.characterAt(0) == '\n' || it.isInsideReplacedElement())) {
|
|
it.advance();
|
|
if (!it.atEnd()) {
|
|
RefPtr<Range> range = it.range();
|
|
textRunRange->setEnd(range->startContainer(), range->startOffset(), ASSERT_NO_EXCEPTION);
|
|
} else {
|
|
Position runStart = textRunRange->startPosition();
|
|
Position runEnd = VisiblePosition(runStart).next().deepEquivalent();
|
|
if (runEnd.isNotNull())
|
|
textRunRange->setEnd(runEnd.containerNode(), runEnd.computeOffsetInContainerNode(), ASSERT_NO_EXCEPTION);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (foundStart) {
|
|
startRangeFound = true;
|
|
if (textRunRange->startContainer()->isTextNode()) {
|
|
int offset = start() - docTextPosition;
|
|
resultRange->setStart(textRunRange->startContainer(), offset + textRunRange->startOffset(), IGNORE_EXCEPTION);
|
|
} else {
|
|
if (start() == docTextPosition)
|
|
resultRange->setStart(textRunRange->startContainer(), textRunRange->startOffset(), IGNORE_EXCEPTION);
|
|
else
|
|
resultRange->setStart(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
|
|
}
|
|
}
|
|
|
|
if (foundEnd) {
|
|
if (textRunRange->startContainer()->isTextNode()) {
|
|
int offset = end() - docTextPosition;
|
|
resultRange->setEnd(textRunRange->startContainer(), offset + textRunRange->startOffset(), IGNORE_EXCEPTION);
|
|
} else {
|
|
if (end() == docTextPosition)
|
|
resultRange->setEnd(textRunRange->startContainer(), textRunRange->startOffset(), IGNORE_EXCEPTION);
|
|
else
|
|
resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
|
|
}
|
|
docTextPosition += len;
|
|
break;
|
|
}
|
|
docTextPosition += len;
|
|
}
|
|
|
|
if (!startRangeFound)
|
|
return nullptr;
|
|
|
|
if (length() && end() > docTextPosition) { // end() is out of bounds
|
|
resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
|
|
}
|
|
|
|
return resultRange.release();
|
|
}
|
|
|
|
PlainTextRange PlainTextRange::create(const ContainerNode& scope, const Range& range)
|
|
{
|
|
if (!range.startContainer())
|
|
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.
|
|
if (range.startContainer() != &scope && !range.startContainer()->isDescendantOf(&scope))
|
|
return PlainTextRange();
|
|
if (range.endContainer() != scope && !range.endContainer()->isDescendantOf(&scope))
|
|
return PlainTextRange();
|
|
|
|
RefPtr<Range> testRange = Range::create(scope.document(), const_cast<ContainerNode*>(&scope), 0, range.startContainer(), range.startOffset());
|
|
ASSERT(testRange->startContainer() == &scope);
|
|
size_t start = TextIterator::rangeLength(testRange.get());
|
|
|
|
testRange->setEnd(range.endContainer(), range.endOffset(), IGNORE_EXCEPTION);
|
|
ASSERT(testRange->startContainer() == &scope);
|
|
size_t end = TextIterator::rangeLength(testRange.get());
|
|
|
|
return PlainTextRange(start, end);
|
|
}
|
|
|
|
}
|