Source/WebCore: WebCore part of <rdar://problem/9281695> Add text search API for...
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Aug 2011 23:36:47 +0000 (23:36 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 30 Aug 2011 23:36:47 +0000 (23:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=67230

Reviewed by Darin Adler.

Test: TestWebKitAPI/Tests/mac/DOMRangeOfString.

* WebCore.exp.in: Export Page::rangeOfString().
* dom/Range.h: Added a default ASSERT_NO_EXCEPTION to a few more member functions.
* editing/Editor.cpp:
(WebCore::Editor::findString): Moved most of the logic into rangeOfString(), which this function
now calls, passing in the current selection as the reference range and setting the selection to
the returned range.
(WebCore::Editor::rangeOfString): Added. Contains the find logic from findString().
* editing/Editor.h:
* page/Page.cpp:
(WebCore::Page::rangeOfString): Added. This function is similar to findString(), but it takes an optional
reference range and returns a Range, rather than using the current selection as the reference range and setting the
selection to the next match.
* page/Page.h:

Source/WebKit/mac: <rdar://problem/9281695> Add text search API for getting the DOM range of a text match
https://bugs.webkit.org/show_bug.cgi?id=67230

Reviewed by Darin Adler.

* WebView/WebHTMLView.mm:
* WebView/WebView.mm:
(coreOptions): Moved this function from WebHTMLView.mm to here and made
it accessible from both this file and WebHTMLView.mm.
(-[WebView DOMRangeOfString:relativeTo:options:]): Added this new API.
* WebView/WebViewInternal.h:
* WebView/WebViewPrivate.h:

Tools: Test for <rdar://problem/9281695> Add text search API for getting the DOM range of a text match
https://bugs.webkit.org/show_bug.cgi?id=67230

Reviewed by Darin Adler.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/mac/DOMRangeOfString.html: Added.
* TestWebKitAPI/Tests/mac/DOMRangeOfString.mm: Added.
(-[DOMRangeOfStringFrameLoadDelegate webView:didFinishLoadForFrame:]):
(TestWebKitAPI::TEST):

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

16 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/dom/Range.h
Source/WebCore/editing/Editor.cpp
Source/WebCore/editing/Editor.h
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebKit/mac/ChangeLog
Source/WebKit/mac/WebView/WebHTMLView.mm
Source/WebKit/mac/WebView/WebView.mm
Source/WebKit/mac/WebView/WebViewInternal.h
Source/WebKit/mac/WebView/WebViewPrivate.h
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/mac/DOMRangeOfString.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/mac/DOMRangeOfString.mm [new file with mode: 0644]

index e90138e4c445760767559241be7c555ffd1bea4f..c81cce21f6201148cbddb53486e214b23fd7bdbb 100644 (file)
@@ -1,3 +1,26 @@
+2011-08-30  Dan Bernstein  <mitz@apple.com>
+
+        WebCore part of <rdar://problem/9281695> Add text search API for getting the DOM range of a text match
+        https://bugs.webkit.org/show_bug.cgi?id=67230
+
+        Reviewed by Darin Adler.
+
+        Test: TestWebKitAPI/Tests/mac/DOMRangeOfString.
+
+        * WebCore.exp.in: Export Page::rangeOfString().
+        * dom/Range.h: Added a default ASSERT_NO_EXCEPTION to a few more member functions.
+        * editing/Editor.cpp:
+        (WebCore::Editor::findString): Moved most of the logic into rangeOfString(), which this function
+        now calls, passing in the current selection as the reference range and setting the selection to
+        the returned range.
+        (WebCore::Editor::rangeOfString): Added. Contains the find logic from findString().
+        * editing/Editor.h:
+        * page/Page.cpp:
+        (WebCore::Page::rangeOfString): Added. This function is similar to findString(), but it takes an optional
+        reference range and returns a Range, rather than using the current selection as the reference range and setting the
+        selection to the next match.
+        * page/Page.h:
+
 2011-08-30  Sam Weinig  <sam@webkit.org>
 
         Add additional convertValue overloads to JSDictionary
index 3d2e43d1268d88b0e2c737f1f703c44a86178f87..597ba3b82192c7cb666bb1b89584fedcb1e47805 100644 (file)
@@ -699,6 +699,7 @@ __ZN7WebCore4Page10findStringERKN3WTF6StringEj
 __ZN7WebCore4Page11PageClientsC1Ev
 __ZN7WebCore4Page11PageClientsD1Ev
 __ZN7WebCore4Page12setGroupNameERKN3WTF6StringE
+__ZN7WebCore4Page13rangeOfStringERKN3WTF6StringEPNS_5RangeEj
 __ZN7WebCore4Page14setMediaVolumeEf
 __ZN7WebCore4Page15addSchedulePairEN3WTF10PassRefPtrINS_12SchedulePairEEE
 __ZN7WebCore4Page15didMoveOnscreenEv
index 0b57b5b6c820c8e10ea3d4d4783faf23e9cac3c9..fef67ef06afa8a17323db4925d433d833d290bfb 100644 (file)
@@ -93,18 +93,18 @@ public:
     void detach(ExceptionCode&);
     PassRefPtr<Range> cloneRange(ExceptionCode&) const;
 
-    void setStartAfter(Node*, ExceptionCode&);
-    void setEndBefore(Node*, ExceptionCode&);
-    void setEndAfter(Node*, ExceptionCode&);
-    void selectNode(Node*, ExceptionCode&);
+    void setStartAfter(Node*, ExceptionCode& = ASSERT_NO_EXCEPTION);
+    void setEndBefore(Node*, ExceptionCode& = ASSERT_NO_EXCEPTION);
+    void setEndAfter(Node*, ExceptionCode& = ASSERT_NO_EXCEPTION);
+    void selectNode(Node*, ExceptionCode& = ASSERT_NO_EXCEPTION);
     void selectNodeContents(Node*, ExceptionCode&);
     void surroundContents(PassRefPtr<Node>, ExceptionCode&);
     void setStartBefore(Node*, ExceptionCode&);
 
     const Position startPosition() const { return m_start.toPosition(); }
     const Position endPosition() const { return m_end.toPosition(); }
-    void setStart(const Position&, ExceptionCode&);
-    void setEnd(const Position&, ExceptionCode&);
+    void setStart(const Position&, ExceptionCode& = ASSERT_NO_EXCEPTION);
+    void setEnd(const Position&, ExceptionCode& = ASSERT_NO_EXCEPTION);
 
     Node* firstNode() const;
     Node* pastLastNode() const;
index 34a182326c5c9e1d5eed6687a3ecbeb5c87cfa11..07d9e7dbfe86f97cc43bc11e30647a00d3472278 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
  *
  * Redistribution and use in source and binary forms, with or without
@@ -2913,64 +2913,75 @@ bool Editor::findString(const String& target, bool forward, bool caseFlag, bool
 
 bool Editor::findString(const String& target, FindOptions options)
 {
-    if (target.isEmpty())
+    VisibleSelection selection = m_frame->selection()->selection();
+
+    RefPtr<Range> resultRange = rangeOfString(target, selection.firstRange().get(), options);
+
+    if (!resultRange)
         return false;
 
+    m_frame->selection()->setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM));
+    m_frame->selection()->revealSelection();
+    return true;
+}
+
+PassRefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRange, FindOptions options)
+{
+    if (target.isEmpty())
+        return 0;
+
     if (m_frame->excludeFromTextSearch())
-        return false;
+        return 0;
 
-    // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge
+    // Start from an edge of the reference range, if there's a reference range that's not in shadow content. Which edge
     // is used depends on whether we're searching forward or backward, and whether startInSelection is set.
     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
-    VisibleSelection selection = m_frame->selection()->selection();
 
     bool forward = !(options & Backwards);
-    bool startInSelection = options & StartInSelection;
-    if (forward)
-        setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
-    else
-        setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
+    bool startInReferenceRange = referenceRange && (options & StartInSelection);
+    if (referenceRange) {
+        if (forward)
+            searchRange->setStart(startInReferenceRange ? referenceRange->startPosition() : referenceRange->endPosition());
+        else
+            searchRange->setEnd(startInReferenceRange ? referenceRange->endPosition() : referenceRange->startPosition());
+    }
 
-    RefPtr<Node> shadowTreeRoot = selection.nonBoundaryShadowTreeRootNode();
+    RefPtr<Node> shadowTreeRoot = referenceRange && referenceRange->startContainer() ? referenceRange->startContainer()->nonBoundaryShadowTreeRootNode() : 0;
     if (shadowTreeRoot) {
-        ExceptionCode ec = 0;
         if (forward)
-            searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount(), ec);
+            searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount());
         else
-            searchRange->setStart(shadowTreeRoot.get(), 0, ec);
+            searchRange->setStart(shadowTreeRoot.get(), 0);
     }
 
     RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options));
-    // If we started in the selection and the found range exactly matches the existing selection, find again.
+    // If we started in the reference range and the found range exactly matches the reference range, find again.
     // Build a selection with the found range to remove collapsed whitespace.
     // Compare ranges instead of selection objects to ignore the way that the current selection was made.
-    if (startInSelection && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), selection.toNormalizedRange().get())) {
+    if (startInReferenceRange && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), referenceRange)) {
         searchRange = rangeOfContents(m_frame->document());
         if (forward)
-            setStart(searchRange.get(), selection.visibleEnd());
+            searchRange->setStart(referenceRange->endPosition());
         else
-            setEnd(searchRange.get(), selection.visibleStart());
+            searchRange->setEnd(referenceRange->startPosition());
 
         if (shadowTreeRoot) {
-            ExceptionCode ec = 0;
             if (forward)
-                searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount(), ec);
+                searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount());
             else
-                searchRange->setStart(shadowTreeRoot.get(), 0, ec);
+                searchRange->setStart(shadowTreeRoot.get(), 0);
         }
 
         resultRange = findPlainText(searchRange.get(), target, options);
     }
 
-    ExceptionCode exception = 0;
-
     // If nothing was found in the shadow tree, search in main content following the shadow tree.
-    if (resultRange->collapsed(exception) && shadowTreeRoot) {
+    if (resultRange->collapsed() && shadowTreeRoot) {
         searchRange = rangeOfContents(m_frame->document());
         if (forward)
-            searchRange->setStartAfter(shadowTreeRoot->shadowAncestorNode(), exception);
+            searchRange->setStartAfter(shadowTreeRoot->shadowAncestorNode());
         else
-            searchRange->setEndBefore(shadowTreeRoot->shadowAncestorNode(), exception);
+            searchRange->setEndBefore(shadowTreeRoot->shadowAncestorNode());
 
         resultRange = findPlainText(searchRange.get(), target, options);
     }
@@ -2978,25 +2989,20 @@ bool Editor::findString(const String& target, FindOptions options)
     if (!insideVisibleArea(resultRange.get())) {
         resultRange = nextVisibleRange(resultRange.get(), target, options);
         if (!resultRange)
-            return false;
+            return 0;
     }
 
     // If we didn't find anything and we're wrapping, search again in the entire document (this will
     // redundantly re-search the area already searched in some cases).
-    if (resultRange->collapsed(exception) && options & WrapAround) {
+    if (resultRange->collapsed() && options & WrapAround) {
         searchRange = rangeOfContents(m_frame->document());
         resultRange = findPlainText(searchRange.get(), target, options);
         // We used to return false here if we ended up with the same range that we started with
-        // (e.g., the selection was already the only instance of this text). But we decided that
+        // (e.g., the reference range was already the only instance of this text). But we decided that
         // this should be a success case instead, so we'll just fall through in that case.
     }
 
-    if (resultRange->collapsed(exception))
-        return false;
-
-    m_frame->selection()->setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM));
-    m_frame->selection()->revealSelection();
-    return true;
+    return resultRange->collapsed() ? 0 : resultRange.release();
 }
 
 static bool isFrameInRange(Frame* frame, Range* range)
index a7e1f3b929e825befe6c64e4e45d07c21a3fad0a..77ef8b0d9bc85f616035b1c7be3e0d1a365a45e2 100644 (file)
@@ -335,6 +335,8 @@ public:
     // FIXME: Switch callers over to the FindOptions version and retire this one.
     bool findString(const String&, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection);
 
+    PassRefPtr<Range> rangeOfString(const String&, Range*, FindOptions);
+
     const VisibleSelection& mark() const; // Mark, to be used as emacs uses it.
     void setMark(const VisibleSelection&);
 
index 6d1f298cca18ee9c8aa72bc774595bab99c62f32..b18fd392e9d10dfd5603dbd65f5ae1329bc19c7d 100644 (file)
@@ -499,6 +499,34 @@ bool Page::findString(const String& target, FindOptions options)
     return false;
 }
 
+PassRefPtr<Range> Page::rangeOfString(const String& target, Range* referenceRange, FindOptions options)
+{
+    if (target.isEmpty() || !mainFrame())
+        return 0;
+
+    if (referenceRange && referenceRange->ownerDocument()->page() != this)
+        return 0;
+
+    bool shouldWrap = options & WrapAround;
+    Frame* frame = referenceRange ? referenceRange->ownerDocument()->frame() : mainFrame();
+    Frame* startFrame = frame;
+    do {
+        if (RefPtr<Range> resultRange = frame->editor()->rangeOfString(target, frame == startFrame ? referenceRange : 0, (options & ~WrapAround) | StartInSelection))
+            return resultRange.release();
+
+        frame = incrementFrame(frame, !(options & Backwards), shouldWrap);
+    } while (frame && frame != startFrame);
+
+    // Search contents of startFrame, on the other side of the reference range that we did earlier.
+    // We cheat a bit and just search again with wrap on.
+    if (shouldWrap && referenceRange) {
+        if (RefPtr<Range> resultRange = startFrame->editor()->rangeOfString(target, referenceRange, options | WrapAround | StartInSelection))
+            return resultRange.release();
+    }
+
+    return 0;
+}
+
 unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit)
 {
     return markAllMatchesForText(target, caseSensitivity == TextCaseInsensitive ? CaseInsensitive : 0, shouldHighlight, limit);
index 9b20c817d7b27a456d7c3221399ebdb18216d3ed..efd13033336e9d1e37b7b0e4d2b42f16787efca9 100644 (file)
@@ -75,6 +75,7 @@ namespace WebCore {
     class PageGroup;
     class PluginData;
     class ProgressTracker;
+    class Range;
     class RenderTheme;
     class VisibleSelection;
     class ScrollableArea;
@@ -210,6 +211,9 @@ namespace WebCore {
         bool findString(const String&, FindOptions);
         // FIXME: Switch callers over to the FindOptions version and retire this one.
         bool findString(const String&, TextCaseSensitivity, FindDirection, bool shouldWrap);
+
+        PassRefPtr<Range> rangeOfString(const String&, Range*, FindOptions);
+
         unsigned markAllMatchesForText(const String&, FindOptions, bool shouldHighlight, unsigned);
         // FIXME: Switch callers over to the FindOptions version and retire this one.
         unsigned markAllMatchesForText(const String&, TextCaseSensitivity, bool shouldHighlight, unsigned);
index 70491bfd2f2d2ac3c7d6aec319c1a081cf17bfcf..29c4ab866089d003a28fb93f0fecd67054f4e44f 100644 (file)
@@ -1,3 +1,18 @@
+2011-08-30  Dan Bernstein  <mitz@apple.com>
+
+        <rdar://problem/9281695> Add text search API for getting the DOM range of a text match
+        https://bugs.webkit.org/show_bug.cgi?id=67230
+
+        Reviewed by Darin Adler.
+
+        * WebView/WebHTMLView.mm:
+        * WebView/WebView.mm:
+        (coreOptions): Moved this function from WebHTMLView.mm to here and made
+        it accessible from both this file and WebHTMLView.mm.
+        (-[WebView DOMRangeOfString:relativeTo:options:]): Added this new API.
+        * WebView/WebViewInternal.h:
+        * WebView/WebViewPrivate.h:
+
 2011-08-30  Beth Dakin  <bdakin@apple.com>
 
         https://bugs.webkit.org/show_bug.cgi?id=67150
index c0b39d2d05330e63a3706d6beb9d272a464ed521..412d6cc79dddbe948be65c0f7a9af9cd10f123f9 100644 (file)
@@ -534,16 +534,6 @@ static NSCellStateValue kit(TriState state)
     return NSOffState;
 }
 
-static FindOptions coreOptions(WebFindOptions options)
-{
-    return (options & WebFindOptionsCaseInsensitive ? CaseInsensitive : 0)
-        | (options & WebFindOptionsAtWordStarts ? AtWordStarts : 0)
-        | (options & WebFindOptionsTreatMedialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0)
-        | (options & WebFindOptionsBackwards ? Backwards : 0)
-        | (options & WebFindOptionsWrapAround ? WrapAround : 0)
-        | (options & WebFindOptionsStartInSelection ? StartInSelection : 0);
-}
-
 @implementation WebHTMLViewPrivate
 
 + (void)initialize
index ad235026deeeb581d10ab4e298e30c905ca4260a..52e1d88db13660398fbf29d9968d4b12b22303a6 100644 (file)
@@ -374,6 +374,16 @@ static const char webViewIsOpen[] = "At least one WebView is still open.";
 - (id)initWithTarget:(id)target defaultTarget:(id)defaultTarget catchExceptions:(BOOL)catchExceptions;
 @end
 
+FindOptions coreOptions(WebFindOptions options)
+{
+    return (options & WebFindOptionsCaseInsensitive ? CaseInsensitive : 0)
+        | (options & WebFindOptionsAtWordStarts ? AtWordStarts : 0)
+        | (options & WebFindOptionsTreatMedialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0)
+        | (options & WebFindOptionsBackwards ? Backwards : 0)
+        | (options & WebFindOptionsWrapAround ? WrapAround : 0)
+        | (options & WebFindOptionsStartInSelection ? StartInSelection : 0);
+}
+
 @interface WebView (WebFileInternal)
 - (float)_deviceScaleFactor;
 - (BOOL)_isLoading;
@@ -4547,6 +4557,14 @@ static BOOL findString(NSView <WebDocumentSearching> *searchView, NSString *stri
     return NO;
 }
 
+- (DOMRange *)DOMRangeOfString:(NSString *)string relativeTo:(DOMRange *)previousRange options:(WebFindOptions)options
+{
+    if (!_private->page)
+        return nil;
+
+    return kit(_private->page->rangeOfString(string, core(previousRange), coreOptions(options)).get());
+}
+
 - (void)setHoverFeedbackSuspended:(BOOL)newValue
 {
     if (_private->hoverFeedbackSuspended == newValue)
index 523d804e57766d33b9f3a346c598a809016d530f..a107b8f92a43c045bda339ec53a2f847c05ebdff 100644 (file)
@@ -34,6 +34,7 @@
 #import "WebTypesInternal.h"
 
 #ifdef __cplusplus
+#import <WebCore/FindOptions.h>
 #import <WebCore/WebCoreKeyboardUIMode.h>
 
 #include <wtf/Forward.h>
@@ -56,6 +57,8 @@ namespace WebCore {
 
 #ifdef __cplusplus
 
+WebCore::FindOptions coreOptions(WebFindOptions options);
+
 @interface WebView (WebViewEditingExtras)
 - (BOOL)_shouldChangeSelectedDOMRange:(DOMRange *)currentRange toDOMRange:(DOMRange *)proposedRange affinity:(NSSelectionAffinity)selectionAffinity stillSelecting:(BOOL)flag;
 @end
index f7eae4c88360d10719672b6c2b605e5dd61da59d..523c1055e0e268c5e1e9bb2351a59175d44ccfc2 100644 (file)
@@ -117,6 +117,7 @@ typedef NSUInteger WebFindOptions;
 - (void)unscheduleFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode;
 
 - (BOOL)findString:(NSString *)string options:(WebFindOptions)options;
+- (DOMRange *)DOMRangeOfString:(NSString *)string relativeTo:(DOMRange *)previousRange options:(WebFindOptions)options;
 
 - (void)setMainFrameDocumentReady:(BOOL)mainFrameDocumentReady;
 
index b44ee2b9dcd444650d0a8d48fd6fd226032fd235..cb0bc3c4195604c12e486bff73813ba1045784ad 100644 (file)
@@ -1,3 +1,16 @@
+2011-08-30  Dan Bernstein  <mitz@apple.com>
+
+        Test for <rdar://problem/9281695> Add text search API for getting the DOM range of a text match
+        https://bugs.webkit.org/show_bug.cgi?id=67230
+
+        Reviewed by Darin Adler.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/mac/DOMRangeOfString.html: Added.
+        * TestWebKitAPI/Tests/mac/DOMRangeOfString.mm: Added.
+        (-[DOMRangeOfStringFrameLoadDelegate webView:didFinishLoadForFrame:]):
+        (TestWebKitAPI::TEST):
+
 2011-08-30  Aaron Colwell  <acolwell@chromium.org>
 
         Add MediaSource API to HTMLMediaElement
index 7249238852fcb4fcd933890cb9b4215aa43348e7..1124d2991a8516669efb4b82247e425888cd8c91 100644 (file)
@@ -19,6 +19,8 @@
                33BE5AF9137B5AAE00705813 /* MouseMoveAfterCrash_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33BE5AF8137B5AAE00705813 /* MouseMoveAfterCrash_Bundle.cpp */; };
                33E79E06137B5FD900E32D99 /* mouse-move-listener.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 33E79E05137B5FCE00E32D99 /* mouse-move-listener.html */; };
                37200B9213A16230007A4FAD /* VectorReverse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37200B9113A16230007A4FAD /* VectorReverse.cpp */; };
+               37DC678D140D7C5000ABCCDB /* DOMRangeOfString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DC678B140D7C5000ABCCDB /* DOMRangeOfString.mm */; };
+               37DC6791140D7D7600ABCCDB /* DOMRangeOfString.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 37DC678F140D7D3A00ABCCDB /* DOMRangeOfString.html */; };
                4BFDFFA71314776C0061F24B /* HitTestResultNodeHandle_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDFFA61314776C0061F24B /* HitTestResultNodeHandle_Bundle.cpp */; };
                4BFDFFA9131477770061F24B /* HitTestResultNodeHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDFFA8131477770061F24B /* HitTestResultNodeHandle.cpp */; };
                BC131885117114B600B69727 /* PlatformUtilitiesMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC131884117114B600B69727 /* PlatformUtilitiesMac.mm */; };
                                C0ADBE9612FCA79B00D2C129 /* simple-form.html in Copy Resources */,
                                C01A23F21266156700C9ED55 /* spacebar-scrolling.html in Copy Resources */,
                                BC2D006412AA04CE00E732A3 /* file-with-anchor.html in Copy Resources */,
+                               37DC6791140D7D7600ABCCDB /* DOMRangeOfString.html in Copy Resources */,
                                1ADBEFE3130C6AA100D61D19 /* simple-accelerated-compositing.html in Copy Resources */,
                        );
                        name = "Copy Resources";
                33BE5AF8137B5AAE00705813 /* MouseMoveAfterCrash_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MouseMoveAfterCrash_Bundle.cpp; sourceTree = "<group>"; };
                33E79E05137B5FCE00E32D99 /* mouse-move-listener.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "mouse-move-listener.html"; sourceTree = "<group>"; };
                37200B9113A16230007A4FAD /* VectorReverse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VectorReverse.cpp; path = WTF/VectorReverse.cpp; sourceTree = "<group>"; };
+               37DC678B140D7C5000ABCCDB /* DOMRangeOfString.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DOMRangeOfString.mm; sourceTree = "<group>"; };
+               37DC678F140D7D3A00ABCCDB /* DOMRangeOfString.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DOMRangeOfString.html; sourceTree = "<group>"; };
                4BFDFFA61314776C0061F24B /* HitTestResultNodeHandle_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HitTestResultNodeHandle_Bundle.cpp; sourceTree = "<group>"; };
                4BFDFFA8131477770061F24B /* HitTestResultNodeHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HitTestResultNodeHandle.cpp; sourceTree = "<group>"; };
                8DD76FA10486AA7600D96B5E /* TestWebKitAPI */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = TestWebKitAPI; sourceTree = BUILT_PRODUCTS_DIR; };
                        isa = PBXGroup;
                        children = (
                                C07E6CB013FD737C0038B22B /* Resources */,
+                               37DC678B140D7C5000ABCCDB /* DOMRangeOfString.mm */,
                                C07E6CAE13FD67650038B22B /* DynamicDeviceScaleFactor.mm */,
                        );
                        path = mac;
                C07E6CB013FD737C0038B22B /* Resources */ = {
                        isa = PBXGroup;
                        children = (
+                               37DC678F140D7D3A00ABCCDB /* DOMRangeOfString.html */,
                                C07E6CB113FD738A0038B22B /* devicePixelRatio.html */,
                        );
                        name = Resources;
                                C08587BF13FE956C001EF4E5 /* WebKitAgnosticTest.mm in Sources */,
                                C08587FC13FEC39B001EF4E5 /* InstanceMethodSwizzler.mm in Sources */,
                                C085880013FEC3A6001EF4E5 /* InstanceMethodSwizzler.mm in Sources */,
+                               37DC678D140D7C5000ABCCDB /* DOMRangeOfString.mm in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/Tools/TestWebKitAPI/Tests/mac/DOMRangeOfString.html b/Tools/TestWebKitAPI/Tests/mac/DOMRangeOfString.html
new file mode 100644 (file)
index 0000000..539cc0a
--- /dev/null
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body>
+    There is only a single needle in this stack of hay.
+    <iframe src="data:text/html,
+        There are no feathers in here.
+    "></iframe>
+</body>
+</html>
diff --git a/Tools/TestWebKitAPI/Tests/mac/DOMRangeOfString.mm b/Tools/TestWebKitAPI/Tests/mac/DOMRangeOfString.mm
new file mode 100644 (file)
index 0000000..66bfb27
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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.
+ */
+
+#import "PlatformUtilities.h"
+#import <WebKit/WebViewPrivate.h>
+#import <WebKit/DOM.h>
+#import <wtf/RetainPtr.h>
+
+@interface DOMRangeOfStringFrameLoadDelegate : NSObject {
+}
+@end
+
+static bool didFinishLoad;
+
+@implementation DOMRangeOfStringFrameLoadDelegate
+
+- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
+{
+    didFinishLoad = true;
+}
+
+@end
+
+namespace TestWebKitAPI {
+
+TEST(WebKit1, DOMRangeOfString)
+{
+    RetainPtr<WebView> webView(AdoptNS, [[WebView alloc] initWithFrame:NSZeroRect frameName:nil groupName:nil]);
+    RetainPtr<DOMRangeOfStringFrameLoadDelegate> frameLoadDelegate(AdoptNS, [DOMRangeOfStringFrameLoadDelegate new]);
+
+    webView.get().frameLoadDelegate = frameLoadDelegate.get();
+    [[webView.get() mainFrame] loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"DOMRangeOfString" withExtension:@"html"]]];
+
+    Util::run(&didFinishLoad);
+
+    DOMRange *resultRange = [webView.get() DOMRangeOfString:@"needles" relativeTo:nil options:0];
+    EXPECT_EQ(nil, resultRange);
+
+    DOMRange *needleRange = [webView.get() DOMRangeOfString:@"needle" relativeTo:nil options:0];
+    EXPECT_EQ(28, needleRange.startOffset);
+
+    resultRange = [webView.get() DOMRangeOfString:@"stack" relativeTo:needleRange options:0];
+    EXPECT_EQ(43, resultRange.startOffset);
+
+    resultRange = [webView.get() DOMRangeOfString:@"stack" relativeTo:needleRange options:WebFindOptionsBackwards];
+    EXPECT_EQ(nil, resultRange);
+
+    RetainPtr<WebView> otherWebView(AdoptNS, [[WebView alloc] initWithFrame:NSZeroRect frameName:nil groupName:nil]);
+    DOMRange *foreignRange = [[[otherWebView.get() mainFrame] DOMDocument] createRange];
+    resultRange = [webView.get() DOMRangeOfString:@"needle" relativeTo:foreignRange options:0];
+    EXPECT_EQ(nil, resultRange);
+
+    resultRange = [webView.get() DOMRangeOfString:@"here" relativeTo:needleRange options:0];
+    EXPECT_EQ(1, resultRange.startOffset);
+
+    resultRange = [webView.get() DOMRangeOfString:@"here" relativeTo:resultRange options:0];
+    EXPECT_EQ(25, resultRange.startOffset);
+
+    resultRange = [webView.get() DOMRangeOfString:@"here" relativeTo:resultRange options:WebFindOptionsWrapAround];
+    EXPECT_EQ(6, resultRange.startOffset);
+}
+
+} // namespace TestWebKitAPI