Send more context about text selection to telephone number scanner
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 Jun 2014 17:52:18 +0000 (17:52 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 Jun 2014 17:52:18 +0000 (17:52 +0000)
<rdar://problem/16874385> and https://bugs.webkit.org/show_bug.cgi?id=133684

Reviewed by Enrica Casucci.

* dom/Range.cpp:
(WebCore::rangesOverlap):Add a utility to check if two ranges overlap.
* dom/Range.h:

* editing/Editor.cpp:
(WebCore::Editor::scanSelectionForTelephoneNumbers): Create a range that is wider than the
  actual selection to search for phone numbers that are not completely selected yet.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@169750 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/dom/Range.cpp
Source/WebCore/dom/Range.h
Source/WebCore/editing/Editor.cpp

index 8e8e245..9d0e509 100644 (file)
@@ -1,3 +1,18 @@
+2014-06-10  Brady Eidson  <beidson@apple.com>
+
+        Send more context about text selection to telephone number scanner
+        <rdar://problem/16874385> and https://bugs.webkit.org/show_bug.cgi?id=133684
+
+        Reviewed by Enrica Casucci.
+
+        * dom/Range.cpp:
+        (WebCore::rangesOverlap):Add a utility to check if two ranges overlap.
+        * dom/Range.h:
+        
+        * editing/Editor.cpp:
+        (WebCore::Editor::scanSelectionForTelephoneNumbers): Create a range that is wider than the 
+          actual selection to search for phone numbers that are not completely selected yet.
+
 2014-06-10  Alex Christensen  <achristensen@webkit.org>
 
         [iOS] Another unreviewed build fix after r169746.
index b0ee0ea..64a72a8 100644 (file)
@@ -1976,6 +1976,35 @@ bool areRangesEqual(const Range* a, const Range* b)
     return a->startPosition() == b->startPosition() && a->endPosition() == b->endPosition();
 }
 
+bool rangesOverlap(const Range* a, const Range* b)
+{
+    if (!a || !b)
+        return false;
+
+    if (a == b)
+        return true;
+
+    if (a->commonAncestorContainer(ASSERT_NO_EXCEPTION)->ownerDocument() != b->commonAncestorContainer(ASSERT_NO_EXCEPTION)->ownerDocument())
+        return false;
+
+    short startToStart = a->compareBoundaryPoints(Range::START_TO_START, b, ASSERT_NO_EXCEPTION);
+    short endToEnd = a->compareBoundaryPoints(Range::END_TO_END, b, ASSERT_NO_EXCEPTION);
+
+    // First range contains the second range.
+    if (startToStart <= 0 && endToEnd >= 0)
+        return true;
+
+    // End of first range is inside second range.
+    if (a->compareBoundaryPoints(Range::END_TO_START, b, ASSERT_NO_EXCEPTION) >= 0 && endToEnd <= 0)
+        return true;
+
+    // Start of first range is inside second range.
+    if (startToStart >= 0 && a->compareBoundaryPoints(Range::START_TO_END, b, ASSERT_NO_EXCEPTION) <= 0)
+        return true;
+
+    return false;
+}
+
 PassRefPtr<Range> rangeOfContents(Node& node)
 {
     RefPtr<Range> range = Range::create(node.document());
index 877b860..c40ede3 100644 (file)
@@ -189,6 +189,7 @@ private:
 PassRefPtr<Range> rangeOfContents(Node&);
 
 bool areRangesEqual(const Range*, const Range*);
+bool rangesOverlap(const Range*, const Range*);
 
 } // namespace
 
index f8b137d..856b81c 100644 (file)
@@ -3364,23 +3364,45 @@ void Editor::scanSelectionForTelephoneNumbers()
 
     Vector<RefPtr<Range>> markedRanges;
 
-    RefPtr<Range> selectedRange = m_frame.selection().toNormalizedRange();
-    if (!selectedRange || (selectedRange->startContainer() == selectedRange->endContainer() && selectedRange->startOffset() == selectedRange->endOffset())) {
+    FrameSelection& frameSelection = m_frame.selection();
+    if (!frameSelection.isRange()) {
         client()->selectedTelephoneNumberRangesChanged(markedRanges);
         return;
     }
 
+    // Extend the range a few characters in each direction to detect incompletely selected phone numbers.
+    static const int charactersToExtend = 15;
+    Position base = frameSelection.selection().base();
+    Position extent = frameSelection.selection().extent();
+    for (int i = 0; i < charactersToExtend; ++i) {
+        base = base.previous(Character);
+        extent = extent.next(Character);
+    }
+
+    FrameSelection extendedSelection;
+    extendedSelection.setBase(base);
+    extendedSelection.setExtent(extent);
+    RefPtr<Range> extendedRange = extendedSelection.toNormalizedRange();
+
     // FIXME: This won't work if a phone number spans multiple chunks of text from the perspective of the TextIterator
     // (By a style change, image, line break, etc.)
     // One idea to handle this would be a model like text search that uses a rotating window.
-    for (TextIterator textChunk(selectedRange.get()); !textChunk.atEnd(); textChunk.advance()) {
+    for (TextIterator textChunk(extendedRange.get()); !textChunk.atEnd(); textChunk.advance()) {
         // TextIterator is supposed to never returns a Range that spans multiple Nodes.
         ASSERT(textChunk.range()->startContainer() == textChunk.range()->endContainer());
 
         scanRangeForTelephoneNumbers(*textChunk.range(), textChunk.text(), markedRanges);
     }
 
-    client()->selectedTelephoneNumberRangesChanged(markedRanges);
+    // Only consider ranges with a detected telephone number if they overlap with the actual selection range.
+    Vector<RefPtr<Range>> extendedMarkedRanges;
+    RefPtr<Range> selectedRange = frameSelection.toNormalizedRange();
+    for (auto& range : markedRanges) {
+        if (rangesOverlap(range.get(), selectedRange.get()))
+            extendedMarkedRanges.append(range);
+    }
+
+    client()->selectedTelephoneNumberRangesChanged(extendedMarkedRanges);
 }
 
 void Editor::scanRangeForTelephoneNumbers(Range& range, const StringView& stringView, Vector<RefPtr<Range>>& markedRanges)