Title: [195494] trunk/Source
Revision
195494
Author
[email protected]
Date
2016-01-22 16:24:02 -0800 (Fri, 22 Jan 2016)

Log Message

Add support for DataDetectors in WK (iOS).
https://bugs.webkit.org/show_bug.cgi?id=152989
rdar://problem/22855960

Reviewed by Tim Horton.

Source/WebCore:

This patch adds the logic to perform data detection and modify
the DOM by adding data detector links as appropriate.
The data detector results returned by detectContentInRange are
stored in the Frame object.

* editing/cocoa/DataDetection.h:
* editing/cocoa/DataDetection.mm:
(WebCore::resultIsURL):
(WebCore::constructURLStringForResult):
(WebCore::removeResultLinksFromAnchor):
(WebCore::searchForLinkRemovingExistingDDLinks):
(WebCore::dataDetectorTypeForCategory):
(WebCore::buildQuery):
(WebCore::DataDetection::detectContentInRange):
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::checkLoadCompleteForThisFrame):
* page/Frame.h:
(WebCore::Frame::setDataDetectionResults):
(WebCore::Frame::dataDetectionResults):
* platform/spi/cocoa/DataDetectorsCoreSPI.h:
(DDQueryOffsetCompare):

Source/WebKit2:

* UIProcess/API/Cocoa/WKWebView.mm:
(fromWKDataDetectorTypes): Changed parameter to uint64_t to
successfully compare against WKDataDetectorTypeAll.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (195493 => 195494)


--- trunk/Source/WebCore/ChangeLog	2016-01-23 00:19:02 UTC (rev 195493)
+++ trunk/Source/WebCore/ChangeLog	2016-01-23 00:24:02 UTC (rev 195494)
@@ -1,3 +1,33 @@
+2016-01-22  Enrica Casucci  <[email protected]>
+
+        Add support for DataDetectors in WK (iOS).
+        https://bugs.webkit.org/show_bug.cgi?id=152989
+        rdar://problem/22855960
+
+        Reviewed by Tim Horton.
+
+        This patch adds the logic to perform data detection and modify
+        the DOM by adding data detector links as appropriate.
+        The data detector results returned by detectContentInRange are
+        stored in the Frame object.
+
+        * editing/cocoa/DataDetection.h:
+        * editing/cocoa/DataDetection.mm:
+        (WebCore::resultIsURL):
+        (WebCore::constructURLStringForResult):
+        (WebCore::removeResultLinksFromAnchor):
+        (WebCore::searchForLinkRemovingExistingDDLinks):
+        (WebCore::dataDetectorTypeForCategory):
+        (WebCore::buildQuery):
+        (WebCore::DataDetection::detectContentInRange):
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::checkLoadCompleteForThisFrame):
+        * page/Frame.h:
+        (WebCore::Frame::setDataDetectionResults):
+        (WebCore::Frame::dataDetectionResults):
+        * platform/spi/cocoa/DataDetectorsCoreSPI.h:
+        (DDQueryOffsetCompare):
+
 2016-01-22  Daniel Bates  <[email protected]>
 
         LayoutTest http/tests/security/xssAuditor/embed-tag-in-path-unterminated.html crashing
@@ -1724,7 +1754,6 @@
         (WebCore::matchesFutureCuePseudoClass):
         (WebCore::matchesPastCuePseudoClass):
 
->>>>>>> .r195316
 2016-01-19  Chris Dumez  <[email protected]>
 
         Unreviewed, rolling out r195179.

Modified: trunk/Source/WebCore/editing/cocoa/DataDetection.h (195493 => 195494)


--- trunk/Source/WebCore/editing/cocoa/DataDetection.h	2016-01-23 00:19:02 UTC (rev 195493)
+++ trunk/Source/WebCore/editing/cocoa/DataDetection.h	2016-01-23 00:24:02 UTC (rev 195494)
@@ -30,6 +30,7 @@
 #import <wtf/RetainPtr.h>
 
 OBJC_CLASS DDActionContext;
+OBJC_CLASS NSArray;
 
 namespace WebCore {
 
@@ -53,7 +54,7 @@
 #if PLATFORM(MAC)
     WEBCORE_EXPORT static RetainPtr<DDActionContext> detectItemAroundHitTestResult(const HitTestResult&, FloatRect& detectedDataBoundingBox, RefPtr<Range>& detectedDataRange);
 #endif
-    WEBCORE_EXPORT static void detectContentInRange(RefPtr<Range>& contextRange, DataDetectorTypes);
+    WEBCORE_EXPORT static NSArray *detectContentInRange(RefPtr<Range>& contextRange, DataDetectorTypes);
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/editing/cocoa/DataDetection.mm (195493 => 195494)


--- trunk/Source/WebCore/editing/cocoa/DataDetection.mm	2016-01-23 00:19:02 UTC (rev 195493)
+++ trunk/Source/WebCore/editing/cocoa/DataDetection.mm	2016-01-23 00:24:02 UTC (rev 195494)
@@ -44,6 +44,12 @@
 #import "VisibleUnits.h"
 #import "htmlediting.h"
 
+#if PLATFORM(IOS)
+const char *dataDetectorsURLScheme = "x-apple-data-detectors";
+const char *dataDetectorsAttributeTypeKey = "x-apple-data-detectors-type";
+const char *dataDetectorsAttributeResultKey = "x-apple-data-detectors-result";
+#endif
+
 namespace WebCore {
 
 #if PLATFORM(MAC)
@@ -138,9 +144,412 @@
     return detectItemAtPositionWithRange(position, contextRange, detectedDataBoundingBox, detectedDataRange);
 }
 #endif // PLATFORM(MAC)
+    
+#if PLATFORM(IOS)
+    
+static BOOL resultIsURL(DDResultRef result)
+{
+    if (!result)
+        return NO;
+    
+    static NSSet *urlTypes = [[NSSet setWithObjects: (NSString *)getDDBinderHttpURLKey(), (NSString *)getDDBinderWebURLKey(), (NSString *)getDDBinderMailURLKey(), (NSString *)getDDBinderGenericURLKey(), (NSString *)getDDBinderEmailKey(), nil] retain];
+    return [urlTypes containsObject:(NSString *)DDResultGetType(result)];
+}
 
-void DataDetection::detectContentInRange(RefPtr<Range>&, DataDetectorTypes)
+static NSString *constructURLStringForResult(DDResultRef currentResult, NSString *resultIdentifier, NSDate *referenceDate, NSTimeZone *referenceTimeZone, DataDetectorTypes detectionTypes)
 {
+    if (!DDResultHasProperties(currentResult, DDResultPropertyPassiveDisplay))
+        return nil;
+    
+    DDURLifierPhoneNumberDetectionTypes phoneTypes = (detectionTypes & DataDetectorTypePhoneNumber) ? DDURLifierPhoneNumberDetectionRegular : DDURLifierPhoneNumberDetectionNone;
+    DDResultCategory category = DDResultGetCategory(currentResult);
+    CFStringRef type = DDResultGetType(currentResult);
+    
+    if (((detectionTypes & DataDetectorTypeAddress) && (DDResultCategoryAddress == category))
+        || ((detectionTypes & DataDetectorTypeTrackingNumber) && (CFStringCompare(getDDBinderTrackingNumberKey(), type, 0) == kCFCompareEqualTo))
+        || ((detectionTypes & DataDetectorTypeFlight) && (CFStringCompare(getDDBinderFlightInformationKey(), type, 0) == kCFCompareEqualTo))
+        || ((detectionTypes & DataDetectorTypePhoneNumber) && (DDResultCategoryPhoneNumber == category))
+        || ((detectionTypes & DataDetectorTypeLink) && resultIsURL(currentResult))) {
+        
+        return DDURLStringForResult(currentResult, resultIdentifier, phoneTypes, referenceDate, referenceTimeZone);
+    }
+    if ((detectionTypes & DataDetectorTypeCalendarEvent) && (DDResultCategoryCalendarEvent == category)) {
+        if (!DDResultIsPastDate(currentResult, (CFDateRef)referenceDate, (CFTimeZoneRef)referenceTimeZone))
+            return DDURLStringForResult(currentResult, resultIdentifier, phoneTypes, referenceDate, referenceTimeZone);
+    }
+    return nil;
 }
 
+static void removeResultLinksFromAnchor(Node* node, Node* nodeParent)
+{
+    // Perform a depth-first search for anchor nodes, which have the DDURLScheme attribute set to true,
+    // take their children and insert them before the anchor,
+    // and then remove the anchor.
+    
+    if (!node)
+        return;
+    
+    BOOL nodeIsDDAnchor = is<HTMLAnchorElement>(*node) && downcast<Element>(*node).getAttribute(getDDURLScheme()) == "true";
+    
+    RefPtr<NodeList> children = node->childNodes();
+    unsigned childCount = children->length();
+    for (size_t i = 0; i < childCount; i++) {
+        Node *child = children->item(i);
+        if (is<Element>(*child))
+            removeResultLinksFromAnchor(child, node);
+    }
+    
+    if (nodeIsDDAnchor && nodeParent) {
+        children = node->childNodes();
+        childCount = children->length();
+        
+        // Iterate over the children and move them all onto the same level as this anchor.
+        // Remove the anchor afterwards.
+        for (size_t i = 0; i < childCount; i++) {
+            Node *child = children->item(0);
+            nodeParent->insertBefore(child, node, ASSERT_NO_EXCEPTION);
+        }
+        nodeParent->removeChild(node, ASSERT_NO_EXCEPTION);
+    }
+}
+
+static bool searchForLinkRemovingExistingDDLinks(Node* startNode, Node* endNode)
+{
+    Node *node = startNode;
+    while (node) {
+        if (is<HTMLAnchorElement>(*node)) {
+            if (downcast<Element>(*node).getAttribute(getDDURLScheme()) != "true")
+                return true;
+            removeResultLinksFromAnchor(node, node->parentElement());
+        }
+        
+        if (node == endNode) {
+            // If we found the end node and no link, return false unless an ancestor node is a link.
+            // The only ancestors not tested at this point are in the direct line from self's parent to the top.
+            node = startNode->parentNode();
+            while (node) {
+                if (is<HTMLAnchorElement>(*node)) {
+                    if (downcast<Element>(*node).getAttribute(getDDURLScheme()) != "true")
+                        return true;
+                    removeResultLinksFromAnchor(node, node->parentElement());
+                }
+                node = node->parentNode();
+            }
+            return false;
+        }
+        
+        RefPtr<NodeList> childNodes = node->childNodes();
+        if (childNodes->length())
+            node = childNodes->item(0);
+        else {
+            Node *newNode = node->nextSibling();
+            Node *parentNode = node;
+            while (!newNode) {
+                parentNode = parentNode->parentNode();
+                if (!parentNode)
+                    return false;
+                newNode = parentNode->nextSibling();
+            }
+            node = newNode;
+        }
+    }
+    return false;
+}
+
+static NSString *dataDetectorTypeForCategory(DDResultCategory category)
+{
+    switch (category) {
+    case DDResultCategoryPhoneNumber:
+        return @"telephone";
+    case DDResultCategoryLink:
+        return @"link";
+    case DDResultCategoryAddress:
+        return @"address";
+    case DDResultCategoryCalendarEvent:
+        return @"calendar-event";
+    case DDResultCategoryMisc:
+        return @"misc";
+    default:
+        return @"";
+    }
+}
+
+static String dataDetectorStringForPath(NSIndexPath* path)
+{
+    NSUInteger length = path.length;
+    
+    switch (length) {
+    case 0:
+        return String();
+        
+    case 1:
+        return String::format("%lu", (unsigned long)[path indexAtPosition:0]);
+        
+    case 2:
+        return String::format("%lu/%lu", (unsigned long)[path indexAtPosition:0], (unsigned long)[path indexAtPosition:1]);
+        
+    default:
+        {
+            String componentsString = String::format("%lu", (unsigned long)[path indexAtPosition:0]);
+            for (NSUInteger i = 1 ; i < length ; i++) {
+                componentsString.append("/");
+                componentsString.append(String::format("%lu", (unsigned long)[path indexAtPosition:i]));
+            }
+
+            return componentsString;
+        }
+    }
+}
+
+static void buildQuery(DDScanQueryRef scanQuery, Range* contextRange)
+{
+    // Once we're over this number of fragments, stop at the first hard break.
+    const CFIndex maxFragmentWithHardBreak = 1000;
+    // Once we're over this number of fragments, we stop at the line.
+    const CFIndex maxFragmentWithLinebreak = 5000;
+    // Once we're over this number of fragments, we stop at the space.
+    const CFIndex maxFragmentSpace = 10000;
+
+    CFCharacterSetRef whiteSpacesSet = CFCharacterSetGetPredefined(kCFCharacterSetWhitespaceAndNewline);
+    CFCharacterSetRef newLinesSet = CFCharacterSetGetPredefined(kCFCharacterSetNewline);
+    
+    RefPtr<Range> endRange;
+    CFIndex iteratorCount = 0;
+    CFIndex fragmentCount = 0;
+    
+    // Build the scan query adding separators.
+    // For each fragment the iterator increment is stored as metadata.
+    for (TextIterator iterator(contextRange); !iterator.atEnd(); iterator.advance(), iteratorCount++) {
+        size_t currentTextLength = iterator.text().length();
+        if (!currentTextLength) {
+            DDScanQueryAddSeparator(scanQuery, DDTextCoalescingTypeHardBreak);
+            if (iteratorCount > maxFragmentWithHardBreak)
+                break;
+            continue;
+        }
+        // Test for white space nodes, we're coalescing them.
+        const UniChar *currentCharPtr = iterator.text().upconvertedCharacters();
+        
+        bool containsOnlyWhiteSpace = true;
+        bool hasTab = false;
+        bool hasNewline = false;
+        int nbspCount = 0;
+        for (NSUInteger i = 0; i < currentTextLength; i++) {
+            if (!CFCharacterSetIsCharacterMember(whiteSpacesSet, *currentCharPtr)) {
+                containsOnlyWhiteSpace = false;
+                break;
+            }
+            
+            if (CFCharacterSetIsCharacterMember(newLinesSet, *currentCharPtr))
+                hasNewline = true;
+            else if (*currentCharPtr == '\t')
+                hasTab = true;
+            
+            // Multiple consecutive non breakable spaces are most likely simulated tabs.
+            if (*currentCharPtr == 0xa0) {
+                if (++nbspCount > 2)
+                    hasTab = true;
+            } else
+                nbspCount = 0;
+
+            currentCharPtr++;
+        }
+        if (containsOnlyWhiteSpace) {
+            if (hasNewline) {
+                DDScanQueryAddLineBreak(scanQuery);
+                if (iteratorCount > maxFragmentWithLinebreak)
+                    break;
+            } else {
+                DDScanQueryAddSeparator(scanQuery, hasTab ? DDTextCoalescingTypeTab : DDTextCoalescingTypeSpace);
+                if (iteratorCount > maxFragmentSpace)
+                    break;
+            }
+            continue;
+        }
+        
+        RetainPtr<CFStringRef> currentText = adoptCF(CFStringCreateWithCharacters(kCFAllocatorDefault, iterator.text().upconvertedCharacters(), iterator.text().length()));
+        DDScanQueryAddTextFragment(scanQuery, currentText.get(), CFRangeMake(0, currentTextLength), (void *)iteratorCount, (DDTextFragmentMode)0, DDTextCoalescingTypeNone);
+        fragmentCount++;
+    }
+}
+
+static inline CFComparisonResult queryOffsetCompare(DDQueryOffset o1, DDQueryOffset o2)
+{
+    if (o1.queryIndex < o2.queryIndex)
+        return kCFCompareLessThan;
+    if (o1.queryIndex > o2.queryIndex)
+        return kCFCompareGreaterThan;
+    if (o1.offset < o2.offset)
+        return kCFCompareLessThan;
+    if (o1.offset > o2.offset)
+        return kCFCompareGreaterThan;
+    return kCFCompareEqualTo;
+}
+
+NSArray *DataDetection::detectContentInRange(RefPtr<Range>& contextRange, DataDetectorTypes types)
+{
+    RetainPtr<DDScannerRef> scanner = adoptCF(DDScannerCreate(DDScannerTypeStandard, 0, nullptr));
+    RetainPtr<DDScanQueryRef> scanQuery = adoptCF(DDScanQueryCreate(NULL));
+    buildQuery(scanQuery.get(), contextRange.get());
+    
+    // FIXME: we should add a timeout to this call to make sure it doesn't take too much time.
+    if (!DDScannerScanQuery(scanner.get(), scanQuery.get()))
+        return nil;
+    
+    RetainPtr<CFArrayRef> scannerResults = adoptCF(DDScannerCopyResultsWithOptions(scanner.get(), getDDScannerCopyResultsOptionsForPassiveUse() | DDScannerCopyResultsOptionsCoalesceSignatures));
+    if (!scannerResults)
+        return nil;
+    
+    CFIndex resultCount = CFArrayGetCount(scannerResults.get());
+    if (!resultCount)
+        return nil;
+    
+    Vector<RetainPtr<DDResultRef>> allResults;
+    Vector<RetainPtr<NSIndexPath>> indexPaths;
+    NSInteger currentTopLevelIndex = 0;
+
+    // Iterate through the scanner results to find signatures and extract all the subresults while
+    // populating the array of index paths to use in the href of the anchors being created.
+    for (id resultObject in (NSArray *)scannerResults.get()) {
+        DDResultRef result = (DDResultRef)resultObject;
+        NSIndexPath *indexPath = [NSIndexPath indexPathWithIndex:currentTopLevelIndex];
+        if (CFStringCompare(DDResultGetType(result), getDDBinderSignatureBlockKey(), 0) == kCFCompareEqualTo) {
+            NSArray *subresults = (NSArray *)DDResultGetSubResults(result);
+            
+            for (NSUInteger subResultIndex = 0 ; subResultIndex < [subresults count] ; subResultIndex++) {
+                indexPaths.append([indexPath indexPathByAddingIndex:subResultIndex]);
+                allResults.append((DDResultRef)[subresults objectAtIndex:subResultIndex]);
+            }
+        } else {
+            allResults.append(result);
+            indexPaths.append(indexPath);
+        }
+        currentTopLevelIndex++;
+    }
+
+    Vector<Vector<RefPtr<Range>>> allResultRanges;
+    TextIterator iterator(contextRange.get());
+    CFIndex iteratorCount = 0;
+
+    // Iterate through the array of the expanded results to create a vector of Range objects that indicate
+    // where the DOM needs to be modified.
+    // Each result can be contained all in one text node or can span multiple text nodes.
+    for (auto& result : allResults) {
+        DDQueryRange queryRange = DDResultGetQueryRangeForURLification(result.get());
+        CFIndex iteratorTargetAdvanceCount = (CFIndex)DDScanQueryGetFragmentMetaData(scanQuery.get(), queryRange.start.queryIndex);
+        while (iteratorCount < iteratorTargetAdvanceCount) {
+            iterator.advance();
+            iteratorCount++;
+        }
+
+        Vector<RefPtr<Range>> fragmentRanges;
+        RefPtr<Range> currentRange = iterator.range();
+        CFIndex fragmentIndex = queryRange.start.queryIndex;
+        if (fragmentIndex == queryRange.end.queryIndex)
+            fragmentRanges.append(TextIterator::subrange(currentRange.get(), queryRange.start.offset, queryRange.end.offset - queryRange.start.offset));
+        else
+            fragmentRanges.append(currentRange);
+        
+        while (fragmentIndex < queryRange.end.queryIndex) {
+            fragmentIndex++;
+            iteratorTargetAdvanceCount = (CFIndex)DDScanQueryGetFragmentMetaData(scanQuery.get(), fragmentIndex);
+            while (iteratorCount < iteratorTargetAdvanceCount) {
+                iterator.advance();
+                iteratorCount++;
+            }
+            currentRange = iterator.range();
+            fragmentRanges.append(currentRange);
+        }
+        allResultRanges.append(fragmentRanges);
+    }
+    
+    CFTimeZoneRef tz = CFTimeZoneCopyDefault();
+    NSDate *referenceDate = [NSDate date];
+    Text* lastTextNodeToUpdate = nullptr;
+    String lastNodeContent;
+    size_t contentOffset = 0;
+    DDQueryOffset lastModifiedQueryOffset = {-1, 0};
+    
+    // For each result add the link.
+    // Since there could be multiple results in the same text node, the node is only modified when
+    // we are about to process a different text node.
+    resultCount = allResults.size();
+    
+    for (CFIndex resultIndex = 0; resultIndex < resultCount; resultIndex++) {
+        DDResultRef coreResult = allResults[resultIndex].get();
+        DDQueryRange queryRange = DDResultGetQueryRangeForURLification(coreResult);
+        Vector<RefPtr<Range>> resultRanges = allResultRanges[resultIndex];
+
+        // Compare the query offsets to make sure we don't go backwards
+        if (queryOffsetCompare(lastModifiedQueryOffset, queryRange.start) >= 0)
+            continue;
+
+        if (!resultRanges.size())
+            continue;
+
+        NSString *identifier = dataDetectorStringForPath(indexPaths[resultIndex].get());
+        NSString *correspondingURL = constructURLStringForResult(coreResult, identifier, referenceDate, (NSTimeZone *)tz, types);
+        
+        if (!correspondingURL || searchForLinkRemovingExistingDDLinks(&resultRanges.first()->startContainer(), &resultRanges.last()->endContainer()))
+            continue;
+        
+        lastModifiedQueryOffset = queryRange.end;
+
+        for (auto& range : resultRanges) {
+            Node* parentNode = range->startContainer().parentNode();
+            Text& currentTextNode = downcast<Text>(range->startContainer());
+            Document& document = currentTextNode.document();
+            String textNodeData;
+            
+            if (lastTextNodeToUpdate != &currentTextNode) {
+                if (lastTextNodeToUpdate)
+                    lastTextNodeToUpdate->setData(lastNodeContent);
+                contentOffset = 0;
+                if (range->startOffset() > 0)
+                    textNodeData = currentTextNode.substringData(0, range->startOffset(), ASSERT_NO_EXCEPTION);
+            } else
+                textNodeData = currentTextNode.substringData(contentOffset, range->startOffset() - contentOffset, ASSERT_NO_EXCEPTION);
+            
+            if (!textNodeData.isEmpty()) {
+                RefPtr<Node> newNode = Text::create(document, textNodeData);
+                parentNode->insertBefore(newNode, &currentTextNode, ASSERT_NO_EXCEPTION);
+                contentOffset = range->startOffset();
+            }
+            
+            // Create the actual anchor node and insert it before the current node.
+            textNodeData = currentTextNode.substringData(range->startOffset(), range->endOffset() - range->startOffset(), ASSERT_NO_EXCEPTION);
+            RefPtr<Node> newNode = Text::create(document, textNodeData);
+            parentNode->insertBefore(newNode, &currentTextNode, ASSERT_NO_EXCEPTION);
+            
+            RefPtr<HTMLAnchorElement> anchorElement = HTMLAnchorElement::create(document);
+            anchorElement->setHref(correspondingURL);
+            anchorElement->setDir("ltr");
+            RefPtr<Attr> color = downcast<Element>(parentNode)->getAttributeNode("color");
+            if (color)
+                anchorElement->setAttribute(HTMLNames::styleAttr, color->style()->cssText());
+            
+            anchorElement->Node::appendChild(newNode, ASSERT_NO_EXCEPTION);
+            parentNode->insertBefore(anchorElement, &currentTextNode, ASSERT_NO_EXCEPTION);
+            // Add a special attribute to mark this URLification as the result of data detectors.
+            anchorElement->setAttribute(QualifiedName(nullAtom, dataDetectorsURLScheme, nullAtom), "true");
+            anchorElement->setAttribute(QualifiedName(nullAtom, dataDetectorsAttributeTypeKey, nullAtom), dataDetectorTypeForCategory(DDResultGetCategory(coreResult)));
+            anchorElement->setAttribute(QualifiedName(nullAtom, dataDetectorsAttributeResultKey, nullAtom), identifier);
+            contentOffset = range->endOffset();
+            
+            lastNodeContent = currentTextNode.substringData(range->endOffset(), currentTextNode.length() - range->endOffset(), ASSERT_NO_EXCEPTION);
+            lastTextNodeToUpdate = &currentTextNode;
+        }        
+    }
+    if (lastTextNodeToUpdate)
+        lastTextNodeToUpdate->setData(lastNodeContent);
+    
+    return [getDDScannerResultClass() resultsFromCoreResults:scannerResults.get()];
+}
+
+#else
+NSArray *DataDetection::detectContentInRange(RefPtr<Range>&, DataDetectorTypes)
+{
+    return nil;
+}
+#endif
 } // namespace WebCore

Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (195493 => 195494)


--- trunk/Source/WebCore/loader/FrameLoader.cpp	2016-01-23 00:19:02 UTC (rev 195493)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp	2016-01-23 00:24:02 UTC (rev 195494)
@@ -2279,14 +2279,14 @@
                 m_client.dispatchDidFailLoad(error);
                 loadingEvent = AXObjectCache::AXLoadingFailed;
             } else {
-                m_client.dispatchDidFinishLoad();
-                loadingEvent = AXObjectCache::AXLoadingFinished;
 #if ENABLE(DATA_DETECTION)
                 if (m_frame.settings().dataDetectorTypes() != DataDetectorTypeNone) {
                     RefPtr<Range> documentRange = makeRange(firstPositionInNode(m_frame.document()->documentElement()), lastPositionInNode(m_frame.document()->documentElement()));
-                    DataDetection::detectContentInRange(documentRange, m_frame.settings().dataDetectorTypes());
+                    m_frame.setDataDetectionResults(DataDetection::detectContentInRange(documentRange, m_frame.settings().dataDetectorTypes()));
                 }
 #endif
+                m_client.dispatchDidFinishLoad();
+                loadingEvent = AXObjectCache::AXLoadingFinished;
             }
 
             // Notify accessibility.

Modified: trunk/Source/WebCore/page/Frame.h (195493 => 195494)


--- trunk/Source/WebCore/page/Frame.h	2016-01-23 00:19:02 UTC (rev 195493)
+++ trunk/Source/WebCore/page/Frame.h	2016-01-23 00:24:02 UTC (rev 195494)
@@ -47,10 +47,13 @@
 #include "FrameWin.h"
 #endif
 
+#if PLATFORM(COCOA)
+OBJC_CLASS NSArray;
+#endif
+
 #if PLATFORM(IOS)
 OBJC_CLASS DOMCSSStyleDeclaration;
 OBJC_CLASS DOMNode;
-OBJC_CLASS NSArray;
 OBJC_CLASS NSString;
 #endif
 
@@ -185,6 +188,11 @@
         WEBCORE_EXPORT float frameScaleFactor() const;
 
         void deviceOrPageScaleFactorChanged();
+        
+#if ENABLE(DATA_DETECTION)
+        void setDataDetectionResults(NSArray *results) { m_dataDetectionResults = results; }
+        NSArray *dataDetectionResults() const { return m_dataDetectionResults.get(); }
+#endif
 
 #if PLATFORM(IOS)
         const ViewportArguments& viewportArguments() const;
@@ -292,6 +300,9 @@
         const std::unique_ptr<EventHandler> m_eventHandler;
         const std::unique_ptr<AnimationController> m_animationController;
 
+#if ENABLE(DATA_DETECTION)
+        RetainPtr<NSArray> m_dataDetectionResults;
+#endif
 #if PLATFORM(IOS)
         void betterApproximateNode(const IntPoint& testPoint, NodeQualifier, Node*& best, Node* failedNode, IntPoint& bestPoint, IntRect& bestRect, const IntRect& testRect);
         bool hitTestResultAtViewportLocation(const FloatPoint& viewportLocation, HitTestResult&, IntPoint& center);

Modified: trunk/Source/WebCore/platform/spi/cocoa/DataDetectorsCoreSPI.h (195493 => 195494)


--- trunk/Source/WebCore/platform/spi/cocoa/DataDetectorsCoreSPI.h	2016-01-23 00:19:02 UTC (rev 195493)
+++ trunk/Source/WebCore/platform/spi/cocoa/DataDetectorsCoreSPI.h	2016-01-23 00:24:02 UTC (rev 195494)
@@ -23,10 +23,16 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#import "SoftLinking.h"
+
 #if USE(APPLE_INTERNAL_SDK)
 
+#import <DataDetectorsCore/DDBinderKeys_Private.h>
+#import <DataDetectorsCore/DDScannerResult.h>
 #import <DataDetectorsCore/DataDetectorsCore.h>
-
+#if PLATFORM(IOS)
+#import <DataDetectorsCore/DDURLifier.h>
+#endif
 #else // !USE(APPLE_INTERNAL_SDK)
 
 typedef enum {
@@ -38,8 +44,65 @@
 enum {
     DDScannerCopyResultsOptionsNone = 0,
     DDScannerCopyResultsOptionsNoOverlap = 1 << 0,
+    DDScannerCopyResultsOptionsCoalesceSignatures = 1 << 1,
 };
 
+enum {
+    DDURLifierPhoneNumberDetectionNone = 0,
+    DDURLifierPhoneNumberDetectionRegular = 1 << 1,
+    DDURLifierPhoneNumberDetectionQuotedShorts = 1 << 2,
+    DDURLifierPhoneNumberDetectionUnquotedShorts = 1 << 3
+};
+typedef NSUInteger DDURLifierPhoneNumberDetectionTypes;
+
+typedef enum __DDTextCoalescingType {
+    DDTextCoalescingTypeNone = 0,
+    DDTextCoalescingTypeSpace = 1,
+    DDTextCoalescingTypeTab = 2,
+    DDTextCoalescingTypeLineBreak = 3,
+    DDTextCoalescingTypeHardBreak = 4,
+} DDTextCoalescingType;
+
+typedef enum {
+    DDResultCategoryUnknown = 0,
+    DDResultCategoryLink = 1,
+    DDResultCategoryPhoneNumber = 2,
+    DDResultCategoryAddress = 3,
+    DDResultCategoryCalendarEvent = 4,
+    DDResultCategoryMisc = 5,
+} DDResultCategory;
+
+typedef enum __DDTextFragmentType {
+    DDTextFragmentTypeTrimWhiteSpace =  0x1,
+    DDTextFragmentTypeIgnoreCRLF =  0x2,
+} DDTextFragmentMode;
+
+extern CFStringRef const DDBinderHttpURLKey;
+extern CFStringRef const DDBinderWebURLKey;
+extern CFStringRef const DDBinderMailURLKey;
+extern CFStringRef const DDBinderGenericURLKey;
+extern CFStringRef const DDBinderEmailKey;
+extern CFStringRef const DDBinderTrackingNumberKey;
+extern CFStringRef const DDBinderFlightInformationKey;
+extern CFStringRef const DDBinderSignatureBlockKey;
+extern NSString * const DDURLScheme;
+
+@interface DDScannerResult : NSObject <NSCoding, NSSecureCoding>
++ (NSArray *)resultsFromCoreResults:(CFArrayRef)coreResults;
+@end
+
+#define DDResultPropertyPassiveDisplay   (1 << 0)
+
+typedef struct __DDQueryOffset {
+    CFIndex queryIndex;
+    CFIndex offset;
+} DDQueryOffset;
+
+typedef struct __DDQueryRange {
+    DDQueryOffset start;
+    DDQueryOffset end;
+} DDQueryRange;
+
 #endif // !USE(APPLE_INTERNAL_SDK)
 
 typedef struct __DDResult *DDResultRef;
@@ -48,13 +111,56 @@
 
 typedef CFIndex DDScannerCopyResultsOptions;
 typedef CFIndex DDScannerOptions;
+extern const DDScannerCopyResultsOptions DDScannerCopyResultsOptionsForPassiveUse;
 
 extern "C" {
 
 DDScannerRef DDScannerCreate(DDScannerType, DDScannerOptions, CFErrorRef*);
+DDScanQueryRef DDScanQueryCreate(CFAllocatorRef);
 DDScanQueryRef DDScanQueryCreateFromString(CFAllocatorRef, CFStringRef, CFRange);
 Boolean DDScannerScanQuery(DDScannerRef, DDScanQueryRef);
 CFArrayRef DDScannerCopyResultsWithOptions(DDScannerRef, DDScannerCopyResultsOptions);
 CFRange DDResultGetRange(DDResultRef);
+CFStringRef DDResultGetType(DDResultRef);
+DDResultCategory DDResultGetCategory(DDResultRef);
+Boolean DDResultIsPastDate(DDResultRef, CFDateRef referenceDate, CFTimeZoneRef referenceTimeZone);
+void DDScanQueryAddTextFragment(DDScanQueryRef, CFStringRef, CFRange, void *identifier, DDTextFragmentMode, DDTextCoalescingType);
+void DDScanQueryAddSeparator(DDScanQueryRef, DDTextCoalescingType);
+void DDScanQueryAddLineBreak(DDScanQueryRef);
+void *DDScanQueryGetFragmentMetaData(DDScanQueryRef, CFIndex queryIndex);
+bool DDResultHasProperties(DDResultRef, CFIndex propertySet);
+CFArrayRef DDResultGetSubResults(DDResultRef);
+DDQueryRange DDResultGetQueryRangeForURLification(DDResultRef);
 
+#if PLATFORM(IOS)
+SOFT_LINK_PRIVATE_FRAMEWORK_OPTIONAL(DataDetectorsCore)
+SOFT_LINK_CLASS(DataDetectorsCore, DDScannerResult)
+SOFT_LINK(DataDetectorsCore, DDScannerCreate, DDScannerRef, (DDScannerType type, DDScannerOptions options, CFErrorRef * errorRef), (type, options, errorRef))
+SOFT_LINK(DataDetectorsCore, DDScannerScanQuery, Boolean, (DDScannerRef scanner, DDScanQueryRef query), (scanner, query))
+SOFT_LINK(DataDetectorsCore, DDScanQueryCreate, DDScanQueryRef, (CFAllocatorRef allocator), (allocator))
+SOFT_LINK(DataDetectorsCore, DDScannerCopyResultsWithOptions, CFArrayRef, (DDScannerRef scanner, DDScannerCopyResultsOptions options), (scanner, options))
+SOFT_LINK(DataDetectorsCore, DDResultGetRange, CFRange, (DDResultRef result), (result))
+SOFT_LINK(DataDetectorsCore, DDResultGetType, CFStringRef, (DDResultRef result), (result))
+SOFT_LINK(DataDetectorsCore, DDResultGetCategory, DDResultCategory, (DDResultRef result), (result))
+SOFT_LINK(DataDetectorsCore, DDResultIsPastDate, Boolean, (DDResultRef result, CFDateRef referenceDate, CFTimeZoneRef referenceTimeZone), (result, referenceDate, referenceTimeZone))
+SOFT_LINK(DataDetectorsCore, DDScanQueryAddTextFragment, void, (DDScanQueryRef query, CFStringRef fragment, CFRange range, void *identifier, DDTextFragmentMode mode, DDTextCoalescingType type), (query, fragment, range, identifier, mode, type))
+SOFT_LINK(DataDetectorsCore, DDScanQueryAddSeparator, void, (DDScanQueryRef query, DDTextCoalescingType type), (query, type))
+SOFT_LINK(DataDetectorsCore, DDScanQueryAddLineBreak, void, (DDScanQueryRef query), (query))
+SOFT_LINK(DataDetectorsCore, DDScanQueryGetFragmentMetaData, void *, (DDScanQueryRef query, CFIndex queryIndex), (query, queryIndex))
+SOFT_LINK(DataDetectorsCore, DDResultHasProperties, bool, (DDResultRef result, CFIndex propertySet), (result, propertySet))
+SOFT_LINK(DataDetectorsCore, DDResultGetSubResults, CFArrayRef, (DDResultRef result), (result))
+SOFT_LINK(DataDetectorsCore, DDResultGetQueryRangeForURLification, DDQueryRange, (DDResultRef result), (result))
+SOFT_LINK(DataDetectorsCore, DDURLStringForResult, NSString *, (DDResultRef currentResult, NSString * resultIdentifier, DDURLifierPhoneNumberDetectionTypes includingTelGroups, NSDate * referenceDate, NSTimeZone * referenceTimeZone), (currentResult, resultIdentifier, includingTelGroups, referenceDate, referenceTimeZone))
+SOFT_LINK_POINTER(DataDetectorsCore, DDBinderHttpURLKey, CFStringRef)
+SOFT_LINK_POINTER(DataDetectorsCore, DDBinderWebURLKey, CFStringRef)
+SOFT_LINK_POINTER(DataDetectorsCore, DDBinderMailURLKey, CFStringRef)
+SOFT_LINK_POINTER(DataDetectorsCore, DDBinderGenericURLKey, CFStringRef)
+SOFT_LINK_POINTER(DataDetectorsCore, DDBinderEmailKey, CFStringRef)
+SOFT_LINK_POINTER(DataDetectorsCore, DDBinderTrackingNumberKey, CFStringRef)
+SOFT_LINK_POINTER(DataDetectorsCore, DDBinderFlightInformationKey, CFStringRef)
+SOFT_LINK_POINTER(DataDetectorsCore, DDBinderSignatureBlockKey, CFStringRef)
+SOFT_LINK_POINTER(DataDetectorsCore, DDURLScheme, NSString *)
+SOFT_LINK_CONSTANT(DataDetectorsCore, DDScannerCopyResultsOptionsForPassiveUse, DDScannerCopyResultsOptions)
+#endif
+
 }

Modified: trunk/Source/WebKit2/ChangeLog (195493 => 195494)


--- trunk/Source/WebKit2/ChangeLog	2016-01-23 00:19:02 UTC (rev 195493)
+++ trunk/Source/WebKit2/ChangeLog	2016-01-23 00:24:02 UTC (rev 195494)
@@ -1,3 +1,15 @@
+2016-01-22  Enrica Casucci  <[email protected]>
+
+        Add support for DataDetectors in WK (iOS).
+        https://bugs.webkit.org/show_bug.cgi?id=152989
+        rdar://problem/22855960
+
+        Reviewed by Tim Horton.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (fromWKDataDetectorTypes): Changed parameter to uint64_t to
+        successfully compare against WKDataDetectorTypeAll.
+
 2016-01-17  Ada Chan  <[email protected]>
 
         Add a mode parameter to MediaControllerInterface::supportsFullscreen() and ChromeClient::supportsVideoFullscreen().

Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm (195493 => 195494)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm	2016-01-23 00:19:02 UTC (rev 195493)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm	2016-01-23 00:24:02 UTC (rev 195494)
@@ -315,7 +315,7 @@
 #endif
 
 #if ENABLE(DATA_DETECTION)
-static WebCore::DataDetectorTypes fromWKDataDetectorTypes(uint32_t types)
+static WebCore::DataDetectorTypes fromWKDataDetectorTypes(uint64_t types)
 {
     if (static_cast<WKDataDetectorTypes>(types) == WKDataDetectorTypeNone)
         return WebCore::DataDetectorTypeNone;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to