コード例 #1
0
ファイル: SelectionModifier.cpp プロジェクト: mirror/chromium
VisiblePosition SelectionModifier::modifyMovingForward(
    TextGranularity granularity) {
  VisiblePosition pos;
  // FIXME: Stay in editable content for the less common granularities.
  switch (granularity) {
    case CharacterGranularity:
      if (m_selection.isRange())
        pos = createVisiblePosition(m_selection.end(), m_selection.affinity());
      else
        pos = nextPositionOf(
            createVisiblePosition(m_selection.extent(), m_selection.affinity()),
            CanSkipOverEditingBoundary);
      break;
    case WordGranularity:
      pos = nextWordPositionForPlatform(
          createVisiblePosition(m_selection.extent(), m_selection.affinity()));
      break;
    case SentenceGranularity:
      pos = nextSentencePosition(
          createVisiblePosition(m_selection.extent(), m_selection.affinity()));
      break;
    case LineGranularity: {
      // down-arrowing from a range selection that ends at the start of a line
      // needs to leave the selection at that line start (no need to call
      // nextLinePosition!)
      pos = endForPlatform();
      if (!m_selection.isRange() || !isStartOfLine(pos))
        pos = nextLinePosition(
            pos, lineDirectionPointForBlockDirectionNavigation(START));
      break;
    }
    case ParagraphGranularity:
      pos = nextParagraphPosition(
          endForPlatform(),
          lineDirectionPointForBlockDirectionNavigation(START));
      break;
    case SentenceBoundary:
      pos = endOfSentence(endForPlatform());
      break;
    case LineBoundary:
      pos = logicalEndOfLine(endForPlatform());
      break;
    case ParagraphBoundary:
      pos = endOfParagraph(endForPlatform());
      break;
    case DocumentBoundary:
      pos = endForPlatform();
      if (isEditablePosition(pos.deepEquivalent()))
        pos = endOfEditableContent(pos);
      else
        pos = endOfDocument(pos);
      break;
  }
  return pos;
}
コード例 #2
0
ファイル: SelectionModifier.cpp プロジェクト: mirror/chromium
VisiblePosition SelectionModifier::modifyMovingBackward(
    TextGranularity granularity) {
  VisiblePosition pos;
  switch (granularity) {
    case CharacterGranularity:
      if (m_selection.isRange())
        pos =
            createVisiblePosition(m_selection.start(), m_selection.affinity());
      else
        pos = previousPositionOf(
            createVisiblePosition(m_selection.extent(), m_selection.affinity()),
            CanSkipOverEditingBoundary);
      break;
    case WordGranularity:
      pos = previousWordPosition(
          createVisiblePosition(m_selection.extent(), m_selection.affinity()));
      break;
    case SentenceGranularity:
      pos = previousSentencePosition(
          createVisiblePosition(m_selection.extent(), m_selection.affinity()));
      break;
    case LineGranularity:
      pos = previousLinePosition(
          startForPlatform(),
          lineDirectionPointForBlockDirectionNavigation(START));
      break;
    case ParagraphGranularity:
      pos = previousParagraphPosition(
          startForPlatform(),
          lineDirectionPointForBlockDirectionNavigation(START));
      break;
    case SentenceBoundary:
      pos = startOfSentence(startForPlatform());
      break;
    case LineBoundary:
      pos = logicalStartOfLine(startForPlatform());
      break;
    case ParagraphBoundary:
      pos = startOfParagraph(startForPlatform());
      break;
    case DocumentBoundary:
      pos = startForPlatform();
      if (isEditablePosition(pos.deepEquivalent()))
        pos = startOfEditableContent(pos);
      else
        pos = startOfDocument(pos);
      break;
  }
  return pos;
}
コード例 #3
0
ファイル: SelectionModifier.cpp プロジェクト: mirror/chromium
VisiblePosition SelectionModifier::modifyExtendingBackward(
    TextGranularity granularity) {
  VisiblePosition pos =
      createVisiblePosition(m_selection.extent(), m_selection.affinity());

  // Extending a selection backward by word or character from just after a table
  // selects the table.  This "makes sense" from the user perspective, esp. when
  // deleting. It was done here instead of in VisiblePosition because we want
  // VPs to iterate over everything.
  switch (granularity) {
    case CharacterGranularity:
      pos = previousPositionOf(pos, CanSkipOverEditingBoundary);
      break;
    case WordGranularity:
      pos = previousWordPosition(pos);
      break;
    case SentenceGranularity:
      pos = previousSentencePosition(pos);
      break;
    case LineGranularity:
      pos = previousLinePosition(
          pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
      break;
    case ParagraphGranularity:
      pos = previousParagraphPosition(
          pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
      break;
    case SentenceBoundary:
      pos = startOfSentence(startForPlatform());
      break;
    case LineBoundary:
      pos = logicalStartOfLine(startForPlatform());
      break;
    case ParagraphBoundary:
      pos = startOfParagraph(startForPlatform());
      break;
    case DocumentBoundary:
      pos = startForPlatform();
      if (isEditablePosition(pos.deepEquivalent()))
        pos = startOfEditableContent(pos);
      else
        pos = startOfDocument(pos);
      break;
  }
  adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
  return pos;
}
コード例 #4
0
ファイル: SelectionModifier.cpp プロジェクト: mirror/chromium
VisiblePosition SelectionModifier::modifyExtendingForward(
    TextGranularity granularity) {
  VisiblePosition pos =
      createVisiblePosition(m_selection.extent(), m_selection.affinity());
  switch (granularity) {
    case CharacterGranularity:
      pos = nextPositionOf(pos, CanSkipOverEditingBoundary);
      break;
    case WordGranularity:
      pos = nextWordPositionForPlatform(pos);
      break;
    case SentenceGranularity:
      pos = nextSentencePosition(pos);
      break;
    case LineGranularity:
      pos = nextLinePosition(
          pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
      break;
    case ParagraphGranularity:
      pos = nextParagraphPosition(
          pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
      break;
    case SentenceBoundary:
      pos = endOfSentence(endForPlatform());
      break;
    case LineBoundary:
      pos = logicalEndOfLine(endForPlatform());
      break;
    case ParagraphBoundary:
      pos = endOfParagraph(endForPlatform());
      break;
    case DocumentBoundary:
      pos = endForPlatform();
      if (isEditablePosition(pos.deepEquivalent()))
        pos = endOfEditableContent(pos);
      else
        pos = endOfDocument(pos);
      break;
  }
  adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
  return pos;
}
コード例 #5
0
bool VisibleSelection::isContentEditable() const
{
    return isEditablePosition(start());
}
コード例 #6
0
void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries()
{
    if (m_base.isNull() || m_start.isNull() || m_end.isNull())
        return;

    // Early return in the caret case (the state hasn't actually been set yet, so we can't use isCaret()) to avoid the
    // expense of computing highestEditableRoot.
    if (m_base == m_start && m_base == m_end)
        return;

    Node* baseRoot = highestEditableRoot(m_base);
    Node* startRoot = highestEditableRoot(m_start);
    Node* endRoot = highestEditableRoot(m_end);
    
    Node* baseEditableAncestor = lowestEditableAncestor(m_base.containerNode());
    
    // The base, start and end are all in the same region.  No adjustment necessary.
    if (baseRoot == startRoot && baseRoot == endRoot)
        return;
    
    // The selection is based in editable content.
    if (baseRoot) {
        // If the start is outside the base's editable root, cap it at the start of that root.
        // If the start is in non-editable content that is inside the base's editable root, put it
        // at the first editable position after start inside the base's editable root.
        if (startRoot != baseRoot) {
            VisiblePosition first = firstEditablePositionAfterPositionInRoot(m_start, baseRoot);
            m_start = first.deepEquivalent();
            if (m_start.isNull()) {
                ASSERT_NOT_REACHED();
                m_start = m_end;
            }
        }
        // If the end is outside the base's editable root, cap it at the end of that root.
        // If the end is in non-editable content that is inside the base's root, put it
        // at the last editable position before the end inside the base's root.
        if (endRoot != baseRoot) {
            VisiblePosition last = lastEditablePositionBeforePositionInRoot(m_end, baseRoot);
            m_end = last.deepEquivalent();
            if (m_end.isNull())
                m_end = m_start;
        }
    // The selection is based in non-editable content.
    } else {
        // FIXME: Non-editable pieces inside editable content should be atomic, in the same way that editable
        // pieces in non-editable content are atomic.
    
        // The selection ends in editable content or non-editable content inside a different editable ancestor, 
        // move backward until non-editable content inside the same lowest editable ancestor is reached.
        Node* endEditableAncestor = lowestEditableAncestor(m_end.containerNode());
        if (endRoot || endEditableAncestor != baseEditableAncestor) {
            
            Position p = previousVisuallyDistinctCandidate(m_end);
            Node* shadowAncestor = endRoot ? endRoot->shadowHost() : 0;
            if (p.isNull() && shadowAncestor)
                p = positionAfterNode(shadowAncestor);
            while (p.isNotNull() && !(lowestEditableAncestor(p.containerNode()) == baseEditableAncestor && !isEditablePosition(p))) {
                Node* root = editableRootForPosition(p);
                shadowAncestor = root ? root->shadowHost() : 0;
                p = isAtomicNode(p.containerNode()) ? positionInParentBeforeNode(p.containerNode()) : previousVisuallyDistinctCandidate(p);
                if (p.isNull() && shadowAncestor)
                    p = positionAfterNode(shadowAncestor);
            }
            VisiblePosition previous(p);

            if (previous.isNull()) {
                // The selection crosses an Editing boundary.  This is a
                // programmer error in the editing code.  Happy debugging!
                ASSERT_NOT_REACHED();
                m_base = Position();
                m_extent = Position();
                validate();
                return;
            }
            m_end = previous.deepEquivalent();
        }

        // The selection starts in editable content or non-editable content inside a different editable ancestor, 
        // move forward until non-editable content inside the same lowest editable ancestor is reached.
        Node* startEditableAncestor = lowestEditableAncestor(m_start.containerNode());      
        if (startRoot || startEditableAncestor != baseEditableAncestor) {
            Position p = nextVisuallyDistinctCandidate(m_start);
            Node* shadowAncestor = startRoot ? startRoot->shadowHost() : 0;
            if (p.isNull() && shadowAncestor)
                p = positionBeforeNode(shadowAncestor);
            while (p.isNotNull() && !(lowestEditableAncestor(p.containerNode()) == baseEditableAncestor && !isEditablePosition(p))) {
                Node* root = editableRootForPosition(p);
                shadowAncestor = root ? root->shadowHost() : 0;
                p = isAtomicNode(p.containerNode()) ? positionInParentAfterNode(p.containerNode()) : nextVisuallyDistinctCandidate(p);
                if (p.isNull() && shadowAncestor)
                    p = positionBeforeNode(shadowAncestor);
            }
            VisiblePosition next(p);
            
            if (next.isNull()) {
                // The selection crosses an Editing boundary.  This is a
                // programmer error in the editing code.  Happy debugging!
                ASSERT_NOT_REACHED();
                m_base = Position();
                m_extent = Position();
                validate();
                return;
            }
            m_start = next.deepEquivalent();
        }
    }
    
    // Correct the extent if necessary.
    if (baseEditableAncestor != lowestEditableAncestor(m_extent.containerNode()))
        m_extent = m_baseIsFirst ? m_end : m_start;
}
コード例 #7
0
bool VisibleSelection::rendererIsEditable() const
{
    return isEditablePosition(start(), ContentIsEditable, DoNotUpdateStyle);
}
コード例 #8
0
ファイル: SpellChecker.cpp プロジェクト: Jamesducque/mojo
void SpellChecker::advanceToNextMisspelling(bool startBeforeSelection)
{
    // The basic approach is to search in two phases - from the selection end to the end of the doc, and
    // then we wrap and search from the doc start to (approximately) where we started.

    // Start at the end of the selection, search to edge of document. Starting at the selection end makes
    // repeated "check spelling" commands work.
    VisibleSelection selection(m_frame.selection().selection());
    RefPtr<Range> spellingSearchRange(rangeOfContents(m_frame.document()));

    bool startedWithSelection = false;
    if (selection.start().deprecatedNode()) {
        startedWithSelection = true;
        if (startBeforeSelection) {
            VisiblePosition start(selection.visibleStart());
            // We match AppKit's rule: Start 1 character before the selection.
            VisiblePosition oneBeforeStart = start.previous();
            setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
        } else {
            setStart(spellingSearchRange.get(), selection.visibleEnd());
        }
    }

    Position position = spellingSearchRange->startPosition();
    if (!isEditablePosition(position)) {
        // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the
        // selection is editable.
        // This can happen in Mail for a mix of non-editable and editable content (like Stationary),
        // when spell checking the whole document before sending the message.
        // In that case the document might not be editable, but there are editable pockets that need to be spell checked.

        position = firstEditableVisiblePositionAfterPositionInRoot(position, m_frame.document()).deepEquivalent();
        if (position.isNull())
            return;

        Position rangeCompliantPosition = position.parentAnchoredEquivalent();
        spellingSearchRange->setStart(rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), IGNORE_EXCEPTION);
        startedWithSelection = false; // won't need to wrap
    }

    // topNode defines the whole range we want to operate on
    ContainerNode* topNode = highestEditableRoot(position);
    // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
    spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), IGNORE_EXCEPTION);

    // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
    // at a word boundary. Going back by one char and then forward by a word does the trick.
    if (startedWithSelection) {
        VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
        if (oneBeforeStart.isNotNull())
            setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
        // else we were already at the start of the editable node
    }

    if (spellingSearchRange->collapsed())
        return; // nothing to search in

    // We go to the end of our first range instead of the start of it, just to be sure
    // we don't get foiled by any word boundary problems at the start. It means we might
    // do a tiny bit more searching.
    Node* searchEndNodeAfterWrap = spellingSearchRange->endContainer();
    int searchEndOffsetAfterWrap = spellingSearchRange->endOffset();

    int misspellingOffset = 0;
    GrammarDetail grammarDetail;
    int grammarPhraseOffset = 0;
    RefPtr<Range> grammarSearchRange = nullptr;
    String badGrammarPhrase;
    String misspelledWord;

    bool isSpelling = true;
    int foundOffset = 0;
    String foundItem;
    RefPtr<Range> firstMisspellingRange = nullptr;
    if (unifiedTextCheckerEnabled()) {
        grammarSearchRange = spellingSearchRange->cloneRange();
        foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
        if (isSpelling) {
            misspelledWord = foundItem;
            misspellingOffset = foundOffset;
        } else {
            badGrammarPhrase = foundItem;
            grammarPhraseOffset = foundOffset;
        }
    } else {
        misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
        grammarSearchRange = spellingSearchRange->cloneRange();
        if (!misspelledWord.isEmpty()) {
            // Stop looking at start of next misspelled word
            CharacterIterator chars(grammarSearchRange.get());
            chars.advance(misspellingOffset);
            grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION);
        }

        if (isGrammarCheckingEnabled())
            badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
    }

    // If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the
    // block rather than at a selection).
    if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
        spellingSearchRange->setStart(topNode, 0, IGNORE_EXCEPTION);
        // going until the end of the very first chunk we tested is far enough
        spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, IGNORE_EXCEPTION);

        if (unifiedTextCheckerEnabled()) {
            grammarSearchRange = spellingSearchRange->cloneRange();
            foundItem = TextCheckingHelper(spellCheckerClient(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
            if (isSpelling) {
                misspelledWord = foundItem;
                misspellingOffset = foundOffset;
            } else {
                badGrammarPhrase = foundItem;
                grammarPhraseOffset = foundOffset;
            }
        } else {
            misspelledWord = TextCheckingHelper(spellCheckerClient(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
            grammarSearchRange = spellingSearchRange->cloneRange();
            if (!misspelledWord.isEmpty()) {
                // Stop looking at start of next misspelled word
                CharacterIterator chars(grammarSearchRange.get());
                chars.advance(misspellingOffset);
                grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION);
            }

            if (isGrammarCheckingEnabled())
                badGrammarPhrase = TextCheckingHelper(spellCheckerClient(), grammarSearchRange).findFirstBadGrammar(grammarDetail, grammarPhraseOffset, false);
        }
    }

    if (!badGrammarPhrase.isEmpty()) {
        // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
        // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
        // panel, and store a marker so we draw the green squiggle later.

        ASSERT(badGrammarPhrase.length() > 0);
        ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0);

        // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
        RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
        m_frame.selection().setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
        m_frame.selection().revealSelection();

        m_frame.document()->markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription);
    } else if (!misspelledWord.isEmpty()) {
        // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
        // a marker so we draw the red squiggle later.

        RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
        m_frame.selection().setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM));
        m_frame.selection().revealSelection();

        spellCheckerClient().updateSpellingUIWithMisspelledWord(misspelledWord);
        m_frame.document()->markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling);
    }
}
コード例 #9
0
ファイル: Selection.cpp プロジェクト: Crawping/davinci
void Selection::adjustForEditableContent()
{
    if (m_base.isNull() || m_start.isNull() || m_end.isNull())
        return;

    Node* baseRoot = highestEditableRoot(m_base);
    Node* startRoot = highestEditableRoot(m_start);
    Node* endRoot = highestEditableRoot(m_end);
    
    Node* baseEditableAncestor = lowestEditableAncestor(m_base.node());
    
    // The base, start and end are all in the same region.  No adjustment necessary.
    if (baseRoot == startRoot && baseRoot == endRoot)
        return;
    
    // The selection is based in editable content.
    if (baseRoot) {
        // If the start is outside the base's editable root, cap it at the start of that root.
        // If the start is in non-editable content that is inside the base's editable root, put it
        // at the first editable position after start inside the base's editable root.
        if (startRoot != baseRoot) {
            VisiblePosition first = firstEditablePositionAfterPositionInRoot(m_start, baseRoot);
            m_start = first.deepEquivalent();
            if (m_start.isNull()) {
                ASSERT_NOT_REACHED();
                m_start = m_end;
            }
        }
        // If the end is outside the base's editable root, cap it at the end of that root.
        // If the end is in non-editable content that is inside the base's root, put it
        // at the last editable position before the end inside the base's root.
        if (endRoot != baseRoot) {
            VisiblePosition last = lastEditablePositionBeforePositionInRoot(m_end, baseRoot);
            m_end = last.deepEquivalent();
            if (m_end.isNull()) {
                ASSERT_NOT_REACHED();
                m_end = m_start;
            }
        }
    // The selection is based in non-editable content.
    } else {
        // FIXME: Non-editable pieces inside editable content should be atomic, in the same way that editable
        // pieces in non-editable content are atomic.
    
        // The selection ends in editable content or non-editable content inside a different editable ancestor, 
        // move backward until non-editable content inside the same lowest editable ancestor is reached.
        Node* endEditableAncestor = lowestEditableAncestor(m_end.node());
        if (endRoot || endEditableAncestor != baseEditableAncestor) {
            
            Position p = previousVisuallyDistinctCandidate(m_end);
            Node* shadowAncestor = endRoot ? endRoot->shadowAncestorNode() : 0;
            if (p.isNull() && endRoot && (shadowAncestor != endRoot))
                p = Position(shadowAncestor, maxDeepOffset(shadowAncestor));
            while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) {
                Node* root = editableRootForPosition(p);
                shadowAncestor = root ? root->shadowAncestorNode() : 0;
                p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
                if (p.isNull() && (shadowAncestor != root))
                    p = Position(shadowAncestor, maxDeepOffset(shadowAncestor));
            }
            VisiblePosition previous(p);
            
            if (previous.isNull()) {
                ASSERT_NOT_REACHED();
                m_base = Position();
                m_extent = Position();
                validate();
                return;
            }
            m_end = previous.deepEquivalent();
        }

        // The selection starts in editable content or non-editable content inside a different editable ancestor, 
        // move forward until non-editable content inside the same lowest editable ancestor is reached.
        Node* startEditableAncestor = lowestEditableAncestor(m_start.node());      
        if (startRoot || startEditableAncestor != baseEditableAncestor) {
            Position p = nextVisuallyDistinctCandidate(m_start);
            Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() : 0;
            if (p.isNull() && startRoot && (shadowAncestor != startRoot))
                p = Position(shadowAncestor, 0);
            while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) {
                Node* root = editableRootForPosition(p);
                shadowAncestor = root ? root->shadowAncestorNode() : 0;
                p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
                if (p.isNull() && (shadowAncestor != root))
                    p = Position(shadowAncestor, 0);
            }
            VisiblePosition next(p);
            
            if (next.isNull()) {
                ASSERT_NOT_REACHED();
                m_base = Position();
                m_extent = Position();
                validate();
                return;
            }
            m_start = next.deepEquivalent();
        }
    }
    
    // Correct the extent if necessary.
    if (baseEditableAncestor != lowestEditableAncestor(m_extent.node()))
        m_extent = m_baseIsFirst ? m_end : m_start;
}