📄 visible_units.cpp
字号:
/* * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * 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 "config.h"#include "visible_units.h"#include "Document.h"#include "Element.h"#include "HTMLNames.h"#include "RenderBlock.h"#include "RenderLayer.h"#include "TextBoundaries.h"#include "TextBreakIterator.h"#include "TextIterator.h"#include "VisiblePosition.h"#include "htmlediting.h"namespace WebCore {using namespace HTMLNames;static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned)){ Position pos = c.deepEquivalent(); Node *n = pos.node(); if (!n) return VisiblePosition(); Document *d = n->document(); Node *de = d->documentElement(); if (!de) return VisiblePosition(); Node *boundary = n->enclosingBlockFlowElement(); if (!boundary) return VisiblePosition(); bool isContentEditable = boundary->isContentEditable(); while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) boundary = boundary->parentNode(); Position start = rangeCompliantEquivalent(Position(boundary, 0)); Position end = rangeCompliantEquivalent(pos); RefPtr<Range> searchRange = Range::create(d); int exception = 0; searchRange->setStart(start.node(), start.offset(), exception); searchRange->setEnd(end.node(), end.offset(), exception); ASSERT(!exception); if (exception) return VisiblePosition(); SimplifiedBackwardsTextIterator it(searchRange.get()); Vector<UChar, 1024> string; unsigned next = 0; bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; while (!it.atEnd()) { // iterate to get chunks until the searchFunction returns a non-zero value. if (!inTextSecurityMode) string.prepend(it.characters(), it.length()); else { // Treat bullets used in the text security mode as regular characters when looking for boundaries String iteratorString(it.characters(), it.length()); iteratorString = iteratorString.impl()->secure('x'); string.prepend(iteratorString.characters(), iteratorString.length()); } next = searchFunction(string.data(), string.size()); if (next != 0) break; it.advance(); } if (it.atEnd() && next == 0) { pos = it.range()->startPosition(); } else if (next != 0) { Node *node = it.range()->startContainer(exception); if (node->isTextNode() || (node->renderer() && node->renderer()->isBR())) // The next variable contains a usable index into a text node pos = Position(node, next); else { // Use the end of the found range, the start is not guaranteed to // be correct. Position end = it.range()->endPosition(); VisiblePosition boundary(end); unsigned i = it.length() - next; while (i--) boundary = boundary.previous(); return boundary; } } return VisiblePosition(pos, DOWNSTREAM);}static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned)){ Position pos = c.deepEquivalent(); Node *n = pos.node(); if (!n) return VisiblePosition(); Document *d = n->document(); Node *de = d->documentElement(); if (!de) return VisiblePosition(); Node *boundary = n->enclosingBlockFlowElement(); if (!boundary) return VisiblePosition(); bool isContentEditable = boundary->isContentEditable(); while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) boundary = boundary->parentNode(); RefPtr<Range> searchRange(d->createRange()); Position start(rangeCompliantEquivalent(pos)); ExceptionCode ec = 0; searchRange->selectNodeContents(boundary, ec); searchRange->setStart(start.node(), start.offset(), ec); TextIterator it(searchRange.get(), true); Vector<UChar, 1024> string; unsigned next = 0; bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; while (!it.atEnd()) { // Keep asking the iterator for chunks until the search function // returns an end value not equal to the length of the string passed to it. if (!inTextSecurityMode) string.append(it.characters(), it.length()); else { // Treat bullets used in the text security mode as regular characters when looking for boundaries String iteratorString(it.characters(), it.length()); iteratorString = iteratorString.impl()->secure('x'); string.append(iteratorString.characters(), iteratorString.length()); } next = searchFunction(string.data(), string.size()); if (next != string.size()) break; it.advance(); } if (it.atEnd() && next == string.size()) { pos = it.range()->startPosition(); } else if (next != 0) { // Use the character iterator to translate the next value into a DOM position. CharacterIterator charIt(searchRange.get(), true); charIt.advance(next - 1); pos = charIt.range()->endPosition(); // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593) VisiblePosition visPos = VisiblePosition(pos); if (visPos == VisiblePosition(charIt.range()->startPosition())) pos = visPos.next(true).deepEquivalent(); } // generate VisiblePosition, use UPSTREAM affinity if possible return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);}// ---------static unsigned startWordBoundary(const UChar* characters, unsigned length){ int start, end; findWordBoundary(characters, length, length, &start, &end); return start;}VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side){ // FIXME: This returns a null VP for c at the start of the document // and side == LeftWordIfOnBoundary VisiblePosition p = c; if (side == RightWordIfOnBoundary) { // at paragraph end, the startofWord is the current position if (isEndOfParagraph(c)) return c; p = c.next(); if (p.isNull()) return c; } return previousBoundary(p, startWordBoundary);}static unsigned endWordBoundary(const UChar* characters, unsigned length){ int start, end; findWordBoundary(characters, length, 0, &start, &end); return end;}VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side){ VisiblePosition p = c; if (side == LeftWordIfOnBoundary) { if (isStartOfParagraph(c)) return c; p = c.previous(); if (p.isNull()) return c; } else if (isEndOfParagraph(c)) return c; return nextBoundary(p, endWordBoundary);}static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length){ return findNextWordFromIndex(characters, length, length, false);}VisiblePosition previousWordPosition(const VisiblePosition &c){ VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary); return c.honorEditableBoundaryAtOrAfter(prev);}static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length){ return findNextWordFromIndex(characters, length, 0, true);}VisiblePosition nextWordPosition(const VisiblePosition &c){ VisiblePosition next = nextBoundary(c, nextWordPositionBoundary); return c.honorEditableBoundaryAtOrBefore(next);}// ---------static RootInlineBox *rootBoxForLine(const VisiblePosition &c){ Position p = c.deepEquivalent(); Node *node = p.node(); if (!node) return 0; RenderObject *renderer = node->renderer(); if (!renderer) return 0; InlineBox* box; int offset; c.getInlineBoxAndOffset(box, offset); return box ? box->root() : 0;}static VisiblePosition positionAvoidingFirstPositionInTable(const VisiblePosition& c){ // return table offset 0 instead of the first VisiblePosition inside the table VisiblePosition previous = c.previous(); if (isLastPositionBeforeTable(previous)) return previous; return c;}static VisiblePosition startPositionForLine(const VisiblePosition& c){ if (c.isNull()) return VisiblePosition(); RootInlineBox *rootBox = rootBoxForLine(c); if (!rootBox) { // There are VisiblePositions at offset 0 in blocks without // RootInlineBoxes, like empty editable blocks and bordered blocks. Position p = c.deepEquivalent(); if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0) return positionAvoidingFirstPositionInTable(c); return VisiblePosition(); } // Generated content (e.g. list markers and CSS :before and :after // pseudoelements) have no corresponding DOM element, and so cannot be // represented by a VisiblePosition. Use whatever follows instead. InlineBox *startBox = rootBox->firstLeafChild(); Node *startNode; while (1) { if (!startBox) return VisiblePosition(); RenderObject *startRenderer = startBox->renderer(); if (!startRenderer) return VisiblePosition(); startNode = startRenderer->node(); if (startNode) break; startBox = startBox->nextLeafChild(); } int startOffset = 0; if (startBox->isInlineTextBox()) { InlineTextBox *startTextBox = static_cast<InlineTextBox *>(startBox); startOffset = startTextBox->start(); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -