[SCM] WebKit Debian packaging branch, debian/experimental, updated. upstream/1.3.3-10851-g50815da

mitz at apple.com mitz at apple.com
Wed Dec 22 17:46:53 UTC 2010


The following commit has been merged in the debian/experimental branch:
commit 7258a696bc6a249b346890ed3e824e26080e8fee
Author: mitz at apple.com <mitz at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Tue Nov 30 06:39:39 2010 +0000

    JavaScriptCore: WTF support for <rdar://problem/8650085> adding word-prefix search options to the text search API.
    https://bugs.webkit.org/show_bug.cgi?id=50038
    
    Reviewed by Darin Adler.
    
    * wtf/unicode/UnicodeMacrosFromICU.h: Copied additional macros from icu/unicode/utf16.h.
    
    WebCore: WebCore part of <rdar://problem/8650085> adding word-prefix search options to the text search API.
    https://bugs.webkit.org/show_bug.cgi?id=50038
    
    Reviewed by Darin Adler.
    
    Test: editing/text-iterator/findString.html
    
    * GNUmakefile.am: Added FindOptions.h.
    * WebCore.exp.in: Export the new methods that take FindOptions.
    * WebCore.gypi: Added FindOptions.h.
    * WebCore.pro: Added FindOptions.h and TextBoundaries.{cpp,h}.
    * WebCore.vcproj/WebCore.vcproj: Added FindOptions.h.
    * WebCore.xcodeproj/project.pbxproj: Added FindOptions.h and TextBoundaries.cpp.
    * editing/Editor.cpp:
    (WebCore::Editor::firstVisibleRange): Changed to use FindOptions.
    (WebCore::Editor::lastVisibleRange): Ditto.
    (WebCore::Editor::nextVisibleRange): Ditto.
    (WebCore::Editor::findString): Ditto.
    (WebCore::Editor::countMatchesForText): Ditto.
    * editing/Editor.h: Added a version of findString() that takes FindOptions. Changed
    countMatchesForText() to take FindOptions. Made nextVisibleRange() private and changed it
    and firstVisibleRange() and lastVisibleRange() to take FindOptions.
    * editing/FindOptions.h: Added.
    * editing/TextIterator.cpp:
    Augmented SearchBuffer with an optional prefix, which is not searched, but provides context
    for determining word boundaries.
    (WebCore::isSeparator): Added. Identifies a class of characters used to determine where
    “words” are embedded in a word.
    (WebCore::SearchBuffer::SearchBuffer): Changed to take FindOptions. Added initialization of
    m_options, m_prefixLength and m_needsMoreContext.
    (WebCore::SearchBuffer::append): Adjust m_prefixLength as part of the prefix gets pushed out
    of the buffer.
    (WebCore::SearchBuffer::needsMoreContext): Added.
    (WebCore::SearchBuffer::prependContext): Added.
    (WebCore::SearchBuffer::isWordStartMatch): Added.
    (WebCore::SearchBuffer::search): Changed to account for the context prefix. When searching
    only for word prefix matches, maintains enough context before a tentative match when moving
    it to the beginning of the buffer. Reject matches that are not at word starts if requested.
    Adjust m_prefixLength when pushing characters out of the buffer.
    (WebCore::findPlainText): Changed to work with FindOptions. Feed the search buffer with context
    if and as long as it requires more of it.
    * editing/TextIterator.h: Added a version of findPlainText() that takes FindOptions.
    * editing/visible_units.cpp: Moved {end,start}Of{First,Last}WordBoundary to TextBoundaries.cpp.
    * page/Page.cpp:
    (WebCore::Page::findString): Changed to work with FindOptions.
    (WebCore::Page::markAllMatchesForText): Ditto.
    * page/Page.h: Added FindOptions-based findString() and markAllMatchesForText().
    * platform/text/TextBoundaries.cpp:
    (WebCore::endOfFirstWordBoundaryContext): Moved here from visible_units.cpp.
    (WebCore::startOfLastWordBoundaryContext): Ditto.
    * platform/text/TextBoundaries.h:
    
    WebKit/mac: WebKit Mac part of <rdar://problem/8650085> adding word-prefix search options to the text search API.
    https://bugs.webkit.org/show_bug.cgi?id=50038
    Based on a patch from Darin Adler.
    
    Reviewed by Darin Adler.
    
    * WebView/WebDocumentInternal.h: Removed -markAllMatchesForText:caseSensitive:limit: and
    replaced -countMatchesForText:caseSensitive:limit:markMatches: with a WebFindOptions-based
    method. Declared a WebDocumentOptionsSearching protocol with a new -findString:options:
    method. Made WebHTMLView conform to the new protocol.
    * WebView/WebHTMLView.mm:
    (coreOptions): Added. Converts WebFindOptions to WebCore FindOptions.
    (-[WebHTMLView searchFor:direction:caseSensitive:wrap:startInSelection:]): Changed to use
    -findString:options:.
    (-[WebHTMLView countMatchesForText:options:limit:markMatches:]): Changed to use WebFindOptions.
    (-[WebHTMLView findString:options:]): Added. Calls through to WebCore::Editor::findString().
    * WebView/WebPDFView.mm:
    (-[WebPDFView countMatchesForText:options:limit:markMatches:]): Changed to use WebFindOptions.
    * WebView/WebView.mm:
    (-[WebView markAllMatchesForText:caseSensitive:highlight:limit:]): Now calls through to
    -countMatchesForText:options:highlight:limit:markMatches.
    (-[WebView countMatchesForText:caseSensitive:highlight:limit:markMatches:]): Ditto.
    (-[WebView searchFor:direction:caseSensitive:wrap:startInSelection:]): Now calls through to
    -findString:options:.
    (incrementFrame): Changed to use WebFindOptions.
    (findString): Added this helper method that performs the search using the best supported
    method for the document view.
    (-[WebView findString:options:]): Changed -searchFor::::: into this.
    (-[WebView canMarkAllTextMatches]):
    (-[WebView countMatchesForText:options:highlight:limit:markMatches:]): Updated to use
    WebFindOptions.
    (-[WebView unmarkAllTextMatches]): Updated for change to incrementFrame.
    (-[WebView rectsForTextMatches]): Ditto.
    * WebView/WebViewPrivate.h: Added WebFindOptions, -findString:options:, and WebFindOptions version
    of countMatchesForText:.
    
    WebKit2: WebKit2 part of <rdar://problem/8650085> adding word-prefix search options to the text search API.
    https://bugs.webkit.org/show_bug.cgi?id=50038
    Based on a patch from Darin Adler.
    
    Reviewed by Darin Adler.
    
    * Shared/WebFindOptions.h: Renamed FindOptions.h to this to account for WebCore’s new private
    FindOptions.h. Added and reordered FindOptions and rolled FindDirection into FindOptions.
    * UIProcess/API/C/WKAPICast.h: Removed toFindDirection().
    (WebKit::toFindOptions): Updated for new values.
    * UIProcess/API/C/WKPage.cpp:
    (WKPageFindString): Removed separate WKFindDirection.
    (WKPageCountStringMatches): Replaces caseInsensitive boolean with WKFindOptions.
    * UIProcess/API/C/WKPage.h: Removed WKFindDirection and updated WKFindOptions.
    * UIProcess/WebPageProxy.cpp:
    (WebKit::WebPageProxy::findString): Removed separate FindDirection.
    (WebKit::WebPageProxy::countStringMatches): Replaced caseInsensitive boolean with FindOptions.
    * UIProcess/WebPageProxy.h:
    * WebKit2.xcodeproj/project.pbxproj: Updated for the header rename.
    * WebProcess/WebPage/FindController.cpp:
    (WebKit::core): Added. Converts WebKit2 FindOptions to WebCore FindOptions.
    (WebKit::FindController::countStringMatches): Changed to use FindOptions.
    (WebKit::FindController::findString): Ditto.
    * WebProcess/WebPage/FindController.h:
    * WebProcess/WebPage/WebPage.cpp:
    (WebKit::WebPage::findString): Ditto.
    (WebKit::WebPage::countStringMatches): Ditto.
    * WebProcess/WebPage/WebPage.h:
    * WebProcess/WebPage/WebPage.messages.in: Ditto.
    
    WebKitTools: DumpRenderTree changes for testing the text search API.
    https://bugs.webkit.org/show_bug.cgi?id=50038
    
    Reviewed by Darin Adler.
    
    * DumpRenderTree/LayoutTestController.cpp:
    (findStringCallback):
    (LayoutTestController::staticFunctions):
    * DumpRenderTree/LayoutTestController.h:
    * DumpRenderTree/mac/LayoutTestControllerMac.mm:
    (LayoutTestController::findString):
    * MiniBrowser/mac/BrowserWindowController.m:
    (-[BrowserWindowController find:]):
    
    LayoutTests: Added text search tests, in particular testing word-prefix search.
    https://bugs.webkit.org/show_bug.cgi?id=50038
    
    Reviewed by Darin Adler.
    
    * editing/text-iterator/findString-expected.txt: Added.
    * editing/text-iterator/findString.html: Added.
    
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@72887 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog
index 8a27e02..3c838b6 100644
--- a/JavaScriptCore/ChangeLog
+++ b/JavaScriptCore/ChangeLog
@@ -1,3 +1,12 @@
+2010-11-29  Dan Bernstein  <mitz at apple.com>
+
+        Reviewed by Darin Adler.
+
+        WTF support for <rdar://problem/8650085> adding word-prefix search options to the text search API.
+        https://bugs.webkit.org/show_bug.cgi?id=50038
+
+        * wtf/unicode/UnicodeMacrosFromICU.h: Copied additional macros from icu/unicode/utf16.h.
+
 2010-11-29  Steve Falkenburg  <sfalken at apple.com>
 
         Reviewed by Darin Adler.
diff --git a/JavaScriptCore/wtf/unicode/UnicodeMacrosFromICU.h b/JavaScriptCore/wtf/unicode/UnicodeMacrosFromICU.h
index f865ef1..ef2c8eb 100644
--- a/JavaScriptCore/wtf/unicode/UnicodeMacrosFromICU.h
+++ b/JavaScriptCore/wtf/unicode/UnicodeMacrosFromICU.h
@@ -1,4 +1,5 @@
 /*
+ *  Copyright (C) 1999-2004, International Business Machines Corporation and others.  All Rights Reserved.
  *  Copyright (C) 2006 George Staikos <staikos at kde.org>
  *  Copyright (C) 2006 Alexey Proskuryakov <ap at nypop.com>
  *  Copyright (C) 2007 Apple Computer, Inc. All rights reserved.
@@ -43,6 +44,22 @@
 #define U16_IS_SURROGATE(c) U_IS_SURROGATE(c)
 #define U16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
 
+#define U16_GET(s, start, i, length, c) { \
+    (c)=(s)[i]; \
+    if(U16_IS_SURROGATE(c)) { \
+        uint16_t __c2; \
+        if(U16_IS_SURROGATE_LEAD(c)) { \
+            if((i)+1<(length) && U16_IS_TRAIL(__c2=(s)[(i)+1])) { \
+                (c)=U16_GET_SUPPLEMENTARY((c), __c2); \
+            } \
+        } else { \
+            if((i)-1>=(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
+                (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
+            } \
+        } \
+    } \
+}
+
 #define U16_PREV(s, start, i, c) { \
     (c)=(s)[--(i)]; \
     if(U16_IS_TRAIL(c)) { \
@@ -54,6 +71,12 @@
     } \
 }
 
+#define U16_BACK_1(s, start, i) { \
+    if(U16_IS_TRAIL((s)[--(i)]) && (i)>(start) && U16_IS_LEAD((s)[(i)-1])) { \
+        --(i); \
+    } \
+}
+
 #define U16_NEXT(s, i, length, c) { \
     (c)=(s)[(i)++]; \
     if(U16_IS_LEAD(c)) { \
@@ -65,7 +88,12 @@
     } \
 }
 
+#define U16_FWD_1(s, i, length) { \
+    if(U16_IS_LEAD((s)[(i)++]) && (i)<(length) && U16_IS_TRAIL((s)[i])) { \
+        ++(i); \
+    } \
+}
+
 #define U_MASK(x) ((uint32_t)1<<(x))
 
 #endif
-
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 1df68f3..b19a638 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,13 @@
+2010-11-29  Dan Bernstein  <mitz at apple.com>
+
+        Reviewed by Darin Adler.
+
+        Added text search tests, in particular testing word-prefix search.
+        https://bugs.webkit.org/show_bug.cgi?id=50038
+
+        * editing/text-iterator/findString-expected.txt: Added.
+        * editing/text-iterator/findString.html: Added.
+
 2010-11-29  Ojan Vafai  <ojan at chromium.org>
 
         [chromium] Fix expectations for fast/block/basic/truncation-rtl.html
diff --git a/LayoutTests/editing/text-iterator/findString-expected.txt b/LayoutTests/editing/text-iterator/findString-expected.txt
new file mode 100644
index 0000000..e9cff74
--- /dev/null
+++ b/LayoutTests/editing/text-iterator/findString-expected.txt
@@ -0,0 +1,149 @@
+Searching for ‘o’ in ‘Lorem ipsum dolor sit amet’ with options []:
+PASS: Got a match at 1,2 as expected.
+PASS: Got a match at 13,14 as expected.
+PASS: Got a match at 15,16 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘o’ in ‘Lorem ipsum dolor sit amet’ with options [WrapAround]:
+PASS: Got a match at 1,2 as expected.
+PASS: Got a match at 13,14 as expected.
+PASS: Got a match at 15,16 as expected.
+PASS: Got a match at 1,2 as expected.
+
+Searching for ‘o’ in ‘Lorem ipsum dolor sit amet’ with options [Backwards]:
+PASS: Got a match at 15,16 as expected.
+PASS: Got a match at 13,14 as expected.
+PASS: Got a match at 1,2 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘o’ in ‘Lorem ipsum dolor sit amet’ with options [Backwards, WrapAround]:
+PASS: Got a match at 15,16 as expected.
+PASS: Got a match at 13,14 as expected.
+PASS: Got a match at 1,2 as expected.
+PASS: Got a match at 15,16 as expected.
+
+Searching for ‘O’ in ‘Lorem ipsum dolor sit amet’ with options []:
+PASS: Got no match as expected.
+
+Searching for ‘O’ in ‘Lorem ipsum dolor sit amet’ with options [CaseInsensitive]:
+PASS: Got a match at 1,2 as expected.
+PASS: Got a match at 13,14 as expected.
+PASS: Got a match at 15,16 as expected.
+
+Searching for ‘mount’ in ‘insurmountable mountain’ with options []:
+PASS: Got a match at 5,10 as expected.
+PASS: Got a match at 15,20 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘mount’ in ‘insurmountable mountain’ with options [AtWordStarts]:
+PASS: Got a match at 15,20 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘co’ in ‘cocoa’ with options []:
+PASS: Got a match at 0,2 as expected.
+PASS: Got a match at 2,4 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘co’ in ‘cocoa’ with options [AtWordStarts]:
+PASS: Got a match at 0,2 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘org’ in ‘webkit.org’ with options [AtWordStarts]:
+PASS: Got no match as expected.
+
+Searching for ‘.org’ in ‘webkit.org’ with options [AtWordStarts]:
+PASS: Got no match as expected.
+
+Searching for ‘rg’ in ‘webkit.org’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got no match as expected.
+
+Searching for ‘org’ in ‘webkit.org’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got a match at 7,10 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘.org’ in ‘webkit.org’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got a match at 6,10 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘t.org’ in ‘webkit.org’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got no match as expected.
+
+Searching for ‘it’ in ‘WebKit’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got no match as expected.
+
+Searching for ‘Kit’ in ‘WebKit’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got a match at 3,6 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘bKit’ in ‘WebKit’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got no match as expected.
+
+Searching for ‘equest’ in ‘XMLHTTPRequest’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got no match as expected.
+
+Searching for ‘Request’ in ‘XMLHTTPRequest’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got a match at 7,14 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘PRequest’ in ‘XMLHTTPRequest’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got no match as expected.
+
+Searching for ‘64’ in ‘LP64’ with options [AtWordStarts]:
+PASS: Got no match as expected.
+
+Searching for ‘4’ in ‘LP64’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got no match as expected.
+
+Searching for ‘64’ in ‘LP64’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got a match at 2,4 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘P64’ in ‘LP64’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
+PASS: Got no match as expected.
+
+Searching for ‘a’ in long string with options [AtWordStarts]:
+PASS: Got a match at 6146,6147 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘กร’ in ‘กรปูเลกชกชกรกรกชบงกช’ with options []:
+PASS: Got a match at 0,2 as expected.
+PASS: Got a match at 10,12 as expected.
+PASS: Got a match at 12,14 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘กร’ in ‘กรปูเลกชกชกรกรกชบงกช’ with options [AtWordStarts]:
+PASS: Got a match at 0,2 as expected.
+PASS: Got a match at 12,14 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘กช’ in ‘กรปูเลกชกชกรกรกชบงกช’ with options []:
+PASS: Got a match at 6,8 as expected.
+PASS: Got a match at 8,10 as expected.
+PASS: Got a match at 14,16 as expected.
+PASS: Got a match at 18,20 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘กช’ in ‘กรปูเลกชกชกรกรกชบงกช’ with options [AtWordStarts]:
+PASS: Got a match at 6,8 as expected.
+PASS: Got a match at 8,10 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘กร’ in long string with options []:
+PASS: Got a match at 6144,6146 as expected.
+PASS: Got a match at 6154,6156 as expected.
+PASS: Got a match at 6156,6158 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘กร’ in long string with options [AtWordStarts]:
+PASS: Got a match at 6156,6158 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘กร’ in long string with options [AtWordStarts]:
+PASS: Got a match at 6144,6146 as expected.
+PASS: Got a match at 6156,6158 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘กช’ in long string with options [AtWordStarts]:
+PASS: Got no match as expected.
+
+
diff --git a/LayoutTests/editing/text-iterator/findString.html b/LayoutTests/editing/text-iterator/findString.html
new file mode 100644
index 0000000..eb203a3
--- /dev/null
+++ b/LayoutTests/editing/text-iterator/findString.html
@@ -0,0 +1,105 @@
+<meta charset="utf-8">
+<div id="container"></div>
+<pre id="console" style="visibility: hidden;"></pre>
+<script>
+    function log(message)
+    {
+        document.getElementById("console").appendChild(document.createTextNode(message + "\n"));
+    }
+
+    function testFindString(text, target, options, expectedRanges)
+    {
+        log("Searching for \u2018" + target + "\u2019 " + (text.length <= 64 ? "in \u2018" + text + "\u2019 " : "in long string ") + "with options [" + options.join(", ") + "]:");
+
+        var container = document.getElementById("container");
+        container.innerText = text;
+        document.body.offsetTop;
+        var selection = getSelection();
+        selection.empty();
+
+        var expectedRange;
+        while (expectedRange = expectedRanges.shift()) {
+            var found = layoutTestController.findString(target, options);
+            if (found) {
+                var actualRange = [selection.baseOffset, selection.extentOffset];
+                if (expectedRange[0] !== actualRange[0] || expectedRange[1] !== actualRange[1])
+                    log("FAIL: Expected a match at " + expectedRange + " but got a match at " + actualRange + " instead.");
+                else
+                    log("PASS: Got a match at " + expectedRange + " as expected.");
+            } else if (expectedRange.length)
+                log("FAIL: Expected " + expectedRange + " but got no match.");
+            else
+                log("PASS: Got no match as expected.");
+        }
+        container.innerText = "";
+        log("");
+    }
+
+    layoutTestController.dumpAsText();
+
+    testFindString("Lorem ipsum dolor sit amet", "o", [], [[1, 2], [13, 14], [15, 16], []]);
+    testFindString("Lorem ipsum dolor sit amet", "o", ["WrapAround"], [[1, 2], [13, 14], [15, 16], [1, 2]]);
+    testFindString("Lorem ipsum dolor sit amet", "o", ["Backwards"], [[15, 16], [13, 14], [1, 2], []]);
+    testFindString("Lorem ipsum dolor sit amet", "o", ["Backwards", "WrapAround"], [[15, 16], [13, 14], [1, 2], [15, 16]]);
+    testFindString("Lorem ipsum dolor sit amet", "O", [], [[]]);
+    testFindString("Lorem ipsum dolor sit amet", "O", ["CaseInsensitive"], [[1, 2], [13, 14], [15, 16]]);
+
+    testFindString("insurmountable mountain", "mount", [], [[5, 10], [15, 20], []]);
+    testFindString("insurmountable mountain", "mount", ["AtWordStarts"], [[15, 20], []]);
+
+    testFindString("cocoa", "co", [], [[0, 2], [2, 4], []]);
+    testFindString("cocoa", "co", ["AtWordStarts"], [[0, 2], []]);
+
+    testFindString("webkit.org", "org", ["AtWordStarts"], [[]]);
+    testFindString("webkit.org", ".org", ["AtWordStarts"], [[]]);
+
+    testFindString("webkit.org", "rg", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
+    testFindString("webkit.org", "org", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[7, 10], []]);
+    testFindString("webkit.org", ".org", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[6, 10], []]);
+    testFindString("webkit.org", "t.org", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
+
+    testFindString("WebKit", "it", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
+    testFindString("WebKit", "Kit", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[3, 6], []]);
+    testFindString("WebKit", "bKit", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
+
+    testFindString("XMLHTTPRequest", "equest", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
+    testFindString("XMLHTTPRequest", "Request", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[7, 14], []]);
+    testFindString("XMLHTTPRequest", "PRequest", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
+
+    testFindString("LP64", "64", ["AtWordStarts"], [[]]);
+    testFindString("LP64", "4", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
+    testFindString("LP64", "64", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[2, 4], []]);
+    testFindString("LP64", "P64", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
+
+    const searchBufferSize = 8192;
+    const searchBufferOverlapSize = searchBufferSize / 4;
+    const searchBufferUnoverlappedSize = searchBufferSize - searchBufferOverlapSize;
+    var bufferSizedString = "X";
+    while (bufferSizedString.length < searchBufferSize)
+        bufferSizedString += bufferSizedString;
+    bufferSizedString = bufferSizedString.substring(0, searchBufferSize);
+
+    testFindString(bufferSizedString.substring(0, searchBufferUnoverlappedSize - 2) + " ba a" + bufferSizedString, "a", ["AtWordStarts"], [[searchBufferUnoverlappedSize + 2, searchBufferUnoverlappedSize + 3], []]);
+
+    var thaiWords = [
+        "\u0e01\u0e23",
+        "\u0e1b\u0e39\u0e40\u0e25",
+        "\u0e01\u0e0a",
+        "\u0e01\u0e0a\u0e01\u0e23", // thaiWords[2] + thaiWords[0]
+        "\u0e01\u0e23\u0e01\u0e0a", // thaiWords[0] + thaiWords[2]
+        "\u0e1a\u0e07\u0e01\u0e0a", // ends with thaiWords[2]
+    ];
+
+    testFindString(thaiWords.join(""), thaiWords[0], [], [[0, 2], [10, 12], [12, 14], []]);
+    testFindString(thaiWords.join(""), thaiWords[0], ["AtWordStarts"], [[0, 2], [12, 14], []]);
+
+    testFindString(thaiWords.join(""), thaiWords[2], [], [[6, 8], [8, 10], [14, 16], [18, 20], []]);
+    testFindString(thaiWords.join(""), thaiWords[2], ["AtWordStarts"], [[6, 8], [8, 10], []]);
+
+    testFindString(bufferSizedString.substring(0, searchBufferUnoverlappedSize) + thaiWords.join("") + bufferSizedString, thaiWords[0], [], [[searchBufferUnoverlappedSize, searchBufferUnoverlappedSize + 2], [searchBufferUnoverlappedSize + 10, searchBufferUnoverlappedSize + 12], [searchBufferUnoverlappedSize + 12, searchBufferUnoverlappedSize + 14], []]);
+    testFindString(bufferSizedString.substring(0, searchBufferUnoverlappedSize) + thaiWords.join("") + bufferSizedString, thaiWords[0], ["AtWordStarts"], [[searchBufferUnoverlappedSize + 12, searchBufferUnoverlappedSize + 14], []]);
+    testFindString(bufferSizedString.substring(0, searchBufferUnoverlappedSize - 1) + " " + thaiWords.join("") + bufferSizedString, thaiWords[0], ["AtWordStarts"], [[searchBufferUnoverlappedSize, searchBufferUnoverlappedSize + 2], [searchBufferUnoverlappedSize + 12, searchBufferUnoverlappedSize + 14], []]);
+    testFindString(bufferSizedString.substring(0, searchBufferUnoverlappedSize - 3) + " " + thaiWords[4] + bufferSizedString, thaiWords[2], ["AtWordStarts"], [[]]);
+
+    document.getElementById("console").style.removeProperty("visibility");
+</script>
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index 373a679..0d82a28 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,57 @@
+2010-11-29  Dan Bernstein  <mitz at apple.com>
+
+        Reviewed by Darin Adler.
+
+        WebCore part of <rdar://problem/8650085> adding word-prefix search options to the text search API.
+        https://bugs.webkit.org/show_bug.cgi?id=50038
+
+        Test: editing/text-iterator/findString.html
+
+        * GNUmakefile.am: Added FindOptions.h.
+        * WebCore.exp.in: Export the new methods that take FindOptions.
+        * WebCore.gypi: Added FindOptions.h.
+        * WebCore.pro: Added FindOptions.h and TextBoundaries.{cpp,h}.
+        * WebCore.vcproj/WebCore.vcproj: Added FindOptions.h.
+        * WebCore.xcodeproj/project.pbxproj: Added FindOptions.h and TextBoundaries.cpp.
+        * editing/Editor.cpp:
+        (WebCore::Editor::firstVisibleRange): Changed to use FindOptions.
+        (WebCore::Editor::lastVisibleRange): Ditto.
+        (WebCore::Editor::nextVisibleRange): Ditto.
+        (WebCore::Editor::findString): Ditto.
+        (WebCore::Editor::countMatchesForText): Ditto.
+        * editing/Editor.h: Added a version of findString() that takes FindOptions. Changed
+        countMatchesForText() to take FindOptions. Made nextVisibleRange() private and changed it
+        and firstVisibleRange() and lastVisibleRange() to take FindOptions.
+        * editing/FindOptions.h: Added.
+        * editing/TextIterator.cpp:
+        Augmented SearchBuffer with an optional prefix, which is not searched, but provides context
+        for determining word boundaries.
+        (WebCore::isSeparator): Added. Identifies a class of characters used to determine where
+        “words” are embedded in a word.
+        (WebCore::SearchBuffer::SearchBuffer): Changed to take FindOptions. Added initialization of
+        m_options, m_prefixLength and m_needsMoreContext.
+        (WebCore::SearchBuffer::append): Adjust m_prefixLength as part of the prefix gets pushed out
+        of the buffer.
+        (WebCore::SearchBuffer::needsMoreContext): Added.
+        (WebCore::SearchBuffer::prependContext): Added.
+        (WebCore::SearchBuffer::isWordStartMatch): Added.
+        (WebCore::SearchBuffer::search): Changed to account for the context prefix. When searching
+        only for word prefix matches, maintains enough context before a tentative match when moving
+        it to the beginning of the buffer. Reject matches that are not at word starts if requested.
+        Adjust m_prefixLength when pushing characters out of the buffer.
+        (WebCore::findPlainText): Changed to work with FindOptions. Feed the search buffer with context
+        if and as long as it requires more of it.
+        * editing/TextIterator.h: Added a version of findPlainText() that takes FindOptions.
+        * editing/visible_units.cpp: Moved {end,start}Of{First,Last}WordBoundary to TextBoundaries.cpp.
+        * page/Page.cpp:
+        (WebCore::Page::findString): Changed to work with FindOptions.
+        (WebCore::Page::markAllMatchesForText): Ditto.
+        * page/Page.h: Added FindOptions-based findString() and markAllMatchesForText().
+        * platform/text/TextBoundaries.cpp:
+        (WebCore::endOfFirstWordBoundaryContext): Moved here from visible_units.cpp.
+        (WebCore::startOfLastWordBoundaryContext): Ditto.
+        * platform/text/TextBoundaries.h:
+
 2010-11-29  Dai Mikurube  <dmikurube at google.com>
 
         Reviewed by Kent Tamura.
diff --git a/WebCore/GNUmakefile.am b/WebCore/GNUmakefile.am
index 2beafcd..2d71b11 100644
--- a/WebCore/GNUmakefile.am
+++ b/WebCore/GNUmakefile.am
@@ -1332,6 +1332,7 @@ webcore_sources += \
 	WebCore/editing/EditorDeleteAction.h \
 	WebCore/editing/Editor.h \
 	WebCore/editing/EditorInsertAction.h \
+	WebCore/editing/FindOptions.h \
 	WebCore/editing/FormatBlockCommand.cpp \
 	WebCore/editing/FormatBlockCommand.h \
 	WebCore/editing/gtk/SelectionControllerGtk.cpp \
diff --git a/WebCore/WebCore.exp.in b/WebCore/WebCore.exp.in
index e097f7c..f400c06 100644
--- a/WebCore/WebCore.exp.in
+++ b/WebCore/WebCore.exp.in
@@ -599,7 +599,7 @@ __ZN7WebCore4Node10renderRectEPb
 __ZN7WebCore4Node17stopIgnoringLeaksEv
 __ZN7WebCore4Node18startIgnoringLeaksEv
 __ZN7WebCore4Node19setNeedsStyleRecalcENS_15StyleChangeTypeE
-__ZN7WebCore4Page10findStringERKN3WTF6StringENS1_19TextCaseSensitivityENS_13FindDirectionEb
+__ZN7WebCore4Page10findStringERKN3WTF6StringEj
 __ZN7WebCore4Page11PageClientsC1Ev
 __ZN7WebCore4Page11PageClientsD1Ev
 __ZN7WebCore4Page12setGroupNameERKN3WTF6StringE
@@ -614,7 +614,7 @@ __ZN7WebCore4Page17willMoveOffscreenEv
 __ZN7WebCore4Page18removeSchedulePairEN3WTF10PassRefPtrINS_12SchedulePairEEE
 __ZN7WebCore4Page19visitedStateChangedEPNS_9PageGroupEy
 __ZN7WebCore4Page20unmarkAllTextMatchesEv
-__ZN7WebCore4Page21markAllMatchesForTextERKN3WTF6StringENS1_19TextCaseSensitivityEbj
+__ZN7WebCore4Page21markAllMatchesForTextERKN3WTF6StringEjbj
 __ZN7WebCore4Page22allVisitedStateChangedEPNS_9PageGroupE
 __ZN7WebCore4Page23clearUndoRedoOperationsEv
 __ZN7WebCore4Page27setJavaScriptURLsAreAllowedEb
@@ -661,7 +661,7 @@ __ZN7WebCore6Cursor8fromTypeENS0_4TypeE
 __ZN7WebCore6CursorD1Ev
 __ZN7WebCore6CursoraSERKS0_
 __ZN7WebCore6Editor10applyStyleEPNS_19CSSStyleDeclarationENS_10EditActionE
-__ZN7WebCore6Editor10findStringERKN3WTF6StringEbbbb
+__ZN7WebCore6Editor10findStringERKN3WTF6StringEj
 __ZN7WebCore6Editor10insertTextERKN3WTF6StringEPNS_5EventE
 __ZN7WebCore6Editor13canDHTMLPasteEv
 __ZN7WebCore6Editor13performDeleteEv
@@ -673,7 +673,7 @@ __ZN7WebCore6Editor16pasteAsPlainTextEv
 __ZN7WebCore6Editor17insertOrderedListEv
 __ZN7WebCore6Editor18confirmCompositionERKN3WTF6StringE
 __ZN7WebCore6Editor18confirmCompositionEv
-__ZN7WebCore6Editor19countMatchesForTextERKN3WTF6StringEbjb
+__ZN7WebCore6Editor19countMatchesForTextERKN3WTF6StringEjjb
 __ZN7WebCore6Editor19deleteWithDirectionENS_19SelectionController10EDirectionENS_15TextGranularityEbb
 __ZN7WebCore6Editor19insertUnorderedListEv
 __ZN7WebCore6Editor21applyStyleToSelectionEPNS_19CSSStyleDeclarationENS_10EditActionE
diff --git a/WebCore/WebCore.gypi b/WebCore/WebCore.gypi
index 5b4dd3b..b918be0 100644
--- a/WebCore/WebCore.gypi
+++ b/WebCore/WebCore.gypi
@@ -1399,6 +1399,7 @@
             'editing/EditorCommand.cpp',
             'editing/EditorDeleteAction.h',
             'editing/EditorInsertAction.h',
+            'editing/FindOptions.h',
             'editing/FormatBlockCommand.cpp',
             'editing/FormatBlockCommand.h',
             'editing/HTMLInterchange.cpp',
diff --git a/WebCore/WebCore.pro b/WebCore/WebCore.pro
index 54207b7..193df80 100644
--- a/WebCore/WebCore.pro
+++ b/WebCore/WebCore.pro
@@ -1231,6 +1231,7 @@ SOURCES += \
     platform/text/SegmentedString.cpp \
     platform/SharedBuffer.cpp \
     platform/text/String.cpp \
+    platform/text/TextBoundaries.cpp \
     platform/text/TextCodec.cpp \
     platform/text/TextCodecLatin1.cpp \
     platform/text/TextCodecUserDefined.cpp \
@@ -1712,6 +1713,7 @@ HEADERS += \
     editing/EditingBehavior.h \
     editing/EditingBoundary.h \
     editing/Editor.h \
+    editing/FindOptions.h \
     editing/FormatBlockCommand.h \
     editing/htmlediting.h \
     editing/HTMLInterchange.h \
@@ -2177,6 +2179,7 @@ HEADERS += \
     platform/text/qt/TextCodecQt.h \
     platform/text/RegularExpression.h \
     platform/text/SegmentedString.h \
+    platform/text/TextBoundaries.h \
     platform/text/TextCodec.h \
     platform/text/TextCodecLatin1.h \
     platform/text/TextCodecUserDefined.h \
diff --git a/WebCore/WebCore.vcproj/WebCore.vcproj b/WebCore/WebCore.vcproj/WebCore.vcproj
index 07182e8..288bb80 100755
--- a/WebCore/WebCore.vcproj/WebCore.vcproj
+++ b/WebCore/WebCore.vcproj/WebCore.vcproj
@@ -46583,6 +46583,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\editing\FindOptions.h"
+				>
+			</File>
+			<File
 				RelativePath="..\editing\FormatBlockCommand.cpp"
 				>
 				<FileConfiguration
diff --git a/WebCore/WebCore.xcodeproj/project.pbxproj b/WebCore/WebCore.xcodeproj/project.pbxproj
index 44bb997..eafebb4 100644
--- a/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -871,6 +871,8 @@
 		371F53E90D2704F900ECE0D5 /* CSSUnicodeRangeValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 371F53E70D2704F900ECE0D5 /* CSSUnicodeRangeValue.h */; };
 		371F53EA0D2704F900ECE0D5 /* CSSUnicodeRangeValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 371F53E80D2704F900ECE0D5 /* CSSUnicodeRangeValue.cpp */; };
 		37202199106213C600F25C4B /* FontSmoothingMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 37202198106213C600F25C4B /* FontSmoothingMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		372C00C4129611F1005C9575 /* TextBoundaries.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 372C00C3129611F1005C9575 /* TextBoundaries.cpp */; };
+		372C00D9129619F8005C9575 /* FindOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 372C00D8129619F8005C9575 /* FindOptions.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		3744570F0DB05FA500AE0992 /* SVGGlyphMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 3744570E0DB05FA500AE0992 /* SVGGlyphMap.h */; };
 		375CD232119D43C800A2A859 /* Hyphenation.h in Headers */ = {isa = PBXBuildFile; fileRef = 375CD231119D43C800A2A859 /* Hyphenation.h */; };
 		375CD23B119D44EA00A2A859 /* HyphenationMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 375CD239119D44EA00A2A859 /* HyphenationMac.mm */; };
@@ -6978,6 +6980,8 @@
 		371F53E70D2704F900ECE0D5 /* CSSUnicodeRangeValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSUnicodeRangeValue.h; sourceTree = "<group>"; };
 		371F53E80D2704F900ECE0D5 /* CSSUnicodeRangeValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSSUnicodeRangeValue.cpp; sourceTree = "<group>"; };
 		37202198106213C600F25C4B /* FontSmoothingMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontSmoothingMode.h; sourceTree = "<group>"; };
+		372C00C3129611F1005C9575 /* TextBoundaries.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextBoundaries.cpp; sourceTree = "<group>"; };
+		372C00D8129619F8005C9575 /* FindOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FindOptions.h; sourceTree = "<group>"; };
 		3744570E0DB05FA500AE0992 /* SVGGlyphMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGGlyphMap.h; sourceTree = "<group>"; };
 		375CD231119D43C800A2A859 /* Hyphenation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Hyphenation.h; sourceTree = "<group>"; };
 		375CD239119D44EA00A2A859 /* HyphenationMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HyphenationMac.mm; sourceTree = "<group>"; };
@@ -14610,6 +14614,7 @@
 				93A38B4A0D0E5808006872C2 /* EditorCommand.cpp */,
 				4BAE95B00B2FA9CE00AED8A0 /* EditorDeleteAction.h */,
 				93FDAFC90B11307400E2746F /* EditorInsertAction.h */,
+				372C00D8129619F8005C9575 /* FindOptions.h */,
 				D05CED270A40BB2C00C5AF38 /* FormatBlockCommand.cpp */,
 				D05CED280A40BB2C00C5AF38 /* FormatBlockCommand.h */,
 				93309D98099E64910056E581 /* htmlediting.cpp */,
@@ -16849,6 +16854,7 @@
 				B2C3D9FF0D006C1D00EF6F26 /* SegmentedString.h */,
 				B2C3DA000D006C1D00EF6F26 /* String.cpp */,
 				97C0784F1165D5BE003A32EF /* SuffixTree.h */,
+				372C00C3129611F1005C9575 /* TextBoundaries.cpp */,
 				B2C3DA040D006C1D00EF6F26 /* TextBoundaries.h */,
 				B2C3DA060D006C1D00EF6F26 /* TextBreakIterator.h */,
 				B2C3DA070D006C1D00EF6F26 /* TextBreakIteratorICU.cpp */,
@@ -19690,6 +19696,7 @@
 				BC5EB69F0E81DAEB00B25965 /* FillLayer.h in Headers */,
 				845E72F80FD261EE00A87D79 /* Filter.h in Headers */,
 				08C9251A0FCC7C4A00480DEC /* FilterEffect.h in Headers */,
+				372C00D9129619F8005C9575 /* FindOptions.h in Headers */,
 				A8CFF04F0A154F09000A4234 /* FixedTableLayout.h in Headers */,
 				89878566122CA064003AABDA /* Flags.h in Headers */,
 				49EECDE610503C2400099FAB /* Float32Array.h in Headers */,
@@ -23787,6 +23794,7 @@
 				F55B3DD51251F12D003EF269 /* TelephoneInputType.cpp in Sources */,
 				498770EE1242C535002226BA /* TexShader.cpp in Sources */,
 				6550B6A5099DF0270090D781 /* Text.cpp in Sources */,
+				372C00C4129611F1005C9575 /* TextBoundaries.cpp in Sources */,
 				B2AFFC970D00A5DF0030074D /* TextBoundaries.mm in Sources */,
 				B2C3DA370D006C1D00EF6F26 /* TextBreakIteratorICU.cpp in Sources */,
 				B2AFFC980D00A5DF0030074D /* TextBreakIteratorInternalICUMac.mm in Sources */,
diff --git a/WebCore/editing/Editor.cpp b/WebCore/editing/Editor.cpp
index 7a6d1cb..3a43f33 100644
--- a/WebCore/editing/Editor.cpp
+++ b/WebCore/editing/Editor.cpp
@@ -2834,39 +2834,39 @@ bool Editor::insideVisibleArea(Range* range) const
     return rectInFrameCoords.contains(resultRect);
 }
 
-PassRefPtr<Range> Editor::firstVisibleRange(const String& target, bool caseFlag)
+PassRefPtr<Range> Editor::firstVisibleRange(const String& target, FindOptions options)
 {
     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
-    RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, true, caseFlag);
+    RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, options & ~Backwards);
     ExceptionCode ec = 0;
 
     while (!insideVisibleArea(resultRange.get())) {
         searchRange->setStartAfter(resultRange->endContainer(), ec);
         if (searchRange->startContainer() == searchRange->endContainer())
             return Range::create(m_frame->document());
-        resultRange = findPlainText(searchRange.get(), target, true, caseFlag);
+        resultRange = findPlainText(searchRange.get(), target, options & ~Backwards);
     }
     
     return resultRange;
 }
 
-PassRefPtr<Range> Editor::lastVisibleRange(const String& target, bool caseFlag)
+PassRefPtr<Range> Editor::lastVisibleRange(const String& target, FindOptions options)
 {
     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
-    RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, false, caseFlag);
+    RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, options | Backwards);
     ExceptionCode ec = 0;
 
     while (!insideVisibleArea(resultRange.get())) {
         searchRange->setEndBefore(resultRange->startContainer(), ec);
         if (searchRange->startContainer() == searchRange->endContainer())
             return Range::create(m_frame->document());
-        resultRange = findPlainText(searchRange.get(), target, false, caseFlag);
+        resultRange = findPlainText(searchRange.get(), target, options | Backwards);
     }
     
     return resultRange;
 }
 
-PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, bool forward, bool caseFlag, bool wrapFlag)
+PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, FindOptions options)
 {
     if (m_frame->excludeFromTextSearch())
         return Range::create(m_frame->document());
@@ -2874,8 +2874,8 @@ PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& ta
     RefPtr<Range> resultRange = currentRange;
     RefPtr<Range> searchRange(rangeOfContents(m_frame->document()));
     ExceptionCode ec = 0;
-    
-    for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, forward, caseFlag)) {
+    bool forward = !(options & Backwards);
+    for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, options)) {
         if (resultRange->collapsed(ec)) {
             if (!resultRange->startContainer()->isInShadowTree())
                 break;
@@ -2907,13 +2907,13 @@ PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& ta
     if (insideVisibleArea(resultRange.get()))
         return resultRange;
     
-    if (!wrapFlag)
+    if (!(options & WrapAround))
         return Range::create(m_frame->document());
 
-    if (forward)
-        return firstVisibleRange(target, caseFlag);
+    if (options & Backwards)
+        return lastVisibleRange(target, options);
 
-    return lastVisibleRange(target, caseFlag);
+    return firstVisibleRange(target, options);
 }
 
 void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle)
@@ -3185,6 +3185,12 @@ RenderStyle* Editor::styleForSelectionStart(Node *&nodeToRemove) const
 // Searches from the beginning of the document if nothing is selected.
 bool Editor::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection)
 {
+    FindOptions options = (forward ? 0 : Backwards) | (caseFlag ? 0 : CaseInsensitive) | (wrapFlag ? WrapAround : 0) | (startInSelection ? StartInSelection : 0);
+    return findString(target, options);
+}
+
+bool Editor::findString(const String& target, FindOptions options)
+{
     if (target.isEmpty())
         return false;
 
@@ -3196,6 +3202,8 @@ bool Editor::findString(const String& target, bool forward, bool caseFlag, bool
     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
@@ -3210,7 +3218,7 @@ bool Editor::findString(const String& target, bool forward, bool caseFlag, bool
             searchRange->setStart(shadowTreeRoot.get(), 0, ec);
     }
 
-    RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag));
+    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.
     // 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.
@@ -3229,7 +3237,7 @@ bool Editor::findString(const String& target, bool forward, bool caseFlag, bool
                 searchRange->setStart(shadowTreeRoot.get(), 0, ec);
         }
 
-        resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
+        resultRange = findPlainText(searchRange.get(), target, options);
     }
 
     ExceptionCode exception = 0;
@@ -3242,20 +3250,20 @@ bool Editor::findString(const String& target, bool forward, bool caseFlag, bool
         else
             searchRange->setEndBefore(shadowTreeRoot->shadowParentNode(), exception);
 
-        resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
+        resultRange = findPlainText(searchRange.get(), target, options);
     }
 
     if (!insideVisibleArea(resultRange.get())) {
-        resultRange = nextVisibleRange(resultRange.get(), target, forward, caseFlag, wrapFlag);
+        resultRange = nextVisibleRange(resultRange.get(), target, options);
         if (!resultRange)
             return false;
     }
 
     // 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) && wrapFlag) {
+    if (resultRange->collapsed(exception) && options & WrapAround) {
         searchRange = rangeOfContents(m_frame->document());
-        resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
+        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
         // this should be a success case instead, so we'll just fall through in that case.
@@ -3269,7 +3277,7 @@ bool Editor::findString(const String& target, bool forward, bool caseFlag, bool
     return true;
 }
 
-unsigned Editor::countMatchesForText(const String& target, bool caseFlag, unsigned limit, bool markMatches)
+unsigned Editor::countMatchesForText(const String& target, FindOptions options, unsigned limit, bool markMatches)
 {
     if (target.isEmpty())
         return 0;
@@ -3279,7 +3287,7 @@ unsigned Editor::countMatchesForText(const String& target, bool caseFlag, unsign
     ExceptionCode exception = 0;
     unsigned matchCount = 0;
     do {
-        RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag));
+        RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options & ~Backwards));
         if (resultRange->collapsed(exception)) {
             if (!resultRange->startContainer()->isInShadowTree())
                 break;
diff --git a/WebCore/editing/Editor.h b/WebCore/editing/Editor.h
index 2b87faa..99c1c58 100644
--- a/WebCore/editing/Editor.h
+++ b/WebCore/editing/Editor.h
@@ -33,6 +33,7 @@
 #include "EditingBehavior.h"
 #include "EditorDeleteAction.h"
 #include "EditorInsertAction.h"
+#include "FindOptions.h"
 #include "SelectionController.h"
 
 #if PLATFORM(MAC) && !defined(__OBJC__)
@@ -307,7 +308,6 @@ public:
     // We should make these functions private when their callers in Frame are moved over here to Editor
     bool insideVisibleArea(const IntPoint&) const;
     bool insideVisibleArea(Range*) const;
-    PassRefPtr<Range> nextVisibleRange(Range*, const String&, bool forward, bool caseFlag, bool wrapFlag);
 
     void addToKillRing(Range*, bool prepend);
 
@@ -328,6 +328,8 @@ public:
     Node* findEventTargetFrom(const VisibleSelection& selection) const;
 
     String selectedText() const;
+    bool findString(const String&, FindOptions);
+    // FIXME: Switch callers over to the FindOptions version and retire this one.
     bool findString(const String&, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection);
 
     const VisibleSelection& mark() const; // Mark, to be used as emacs uses it.
@@ -344,7 +346,7 @@ public:
 
     RenderStyle* styleForSelectionStart(Node*& nodeToRemove) const;
 
-    unsigned countMatchesForText(const String&, bool caseFlag, unsigned limit, bool markMatches);
+    unsigned countMatchesForText(const String&, FindOptions, unsigned limit, bool markMatches);
     bool markedTextMatchesAreHighlighted() const;
     void setMarkedTextMatchesAreHighlighted(bool);
 
@@ -398,8 +400,9 @@ private:
     void confirmComposition(const String&, bool preserveSelection);
     void setIgnoreCompositionSelectionChange(bool ignore);
 
-    PassRefPtr<Range> firstVisibleRange(const String&, bool caseFlag);
-    PassRefPtr<Range> lastVisibleRange(const String&, bool caseFlag);
+    PassRefPtr<Range> firstVisibleRange(const String&, FindOptions);
+    PassRefPtr<Range> lastVisibleRange(const String&, FindOptions);
+    PassRefPtr<Range> nextVisibleRange(Range*, const String&, FindOptions);
 
     void changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle);
     void correctionPanelTimerFired(Timer<Editor>*);
diff --git a/WebCore/editing/FindOptions.h b/WebCore/editing/FindOptions.h
new file mode 100644
index 0000000..ae4aecf
--- /dev/null
+++ b/WebCore/editing/FindOptions.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef FindOptions_h
+#define FindOptions_h
+
+namespace WebCore {
+
+enum FindOptionFlag {
+    CaseInsensitive = 1 << 0,
+    AtWordStarts = 1 << 1,
+    // When combined with AtWordStarts, accepts a match in the middle of a word if the match begins with
+    // an uppercase letter followed by a lowercase or non-letter. Accepts several other intra-word matches.
+    TreatMedialCapitalAsWordStart = 1 << 2,
+    Backwards = 1 << 3,
+    WrapAround = 1 << 4,
+    StartInSelection = 1 << 5
+};
+
+typedef unsigned FindOptions;
+
+} // namespace WebCore
+
+#endif // FindOptions_h
diff --git a/WebCore/editing/TextIterator.cpp b/WebCore/editing/TextIterator.cpp
index 2ea16fb..6248d41 100644
--- a/WebCore/editing/TextIterator.cpp
+++ b/WebCore/editing/TextIterator.cpp
@@ -38,6 +38,8 @@
 #include "RenderTableRow.h"
 #include "RenderTextControl.h"
 #include "RenderTextFragment.h"
+#include "TextBoundaries.h"
+#include "TextBreakIterator.h"
 #include "VisiblePosition.h"
 #include "visible_units.h"
 
@@ -56,14 +58,18 @@ using namespace HTMLNames;
 // Buffer that knows how to compare with a search target.
 // Keeps enough of the previous text to be able to search in the future, but no more.
 // Non-breaking spaces are always equal to normal spaces.
-// Case folding is also done if <isCaseSensitive> is false.
+// Case folding is also done if the CaseInsensitive option is specified.
+// Matches are further filtered if the AtWordStarts option is specified, although some
+// matches inside a word are permitted if TreatMedialCapitalAsWordStart is specified as well.
 class SearchBuffer : public Noncopyable {
 public:
-    SearchBuffer(const String& target, bool isCaseSensitive);
+    SearchBuffer(const String& target, FindOptions);
     ~SearchBuffer();
 
     // Returns number of characters appended; guaranteed to be in the range [1, length].
     size_t append(const UChar*, size_t length);
+    bool needsMoreContext() const;
+    void prependContext(const UChar*, size_t length);
     void reachedBreak();
 
     // Result is the size in characters of what was found.
@@ -75,11 +81,16 @@ public:
 
 private:
     bool isBadMatch(const UChar*, size_t length) const;
+    bool isWordStartMatch(size_t start, size_t length) const;
 
     String m_target;
+    FindOptions m_options;
+
     Vector<UChar> m_buffer;
     size_t m_overlap;
+    size_t m_prefixLength;
     bool m_atBreak;
+    bool m_needsMoreContext;
 
     bool m_targetRequiresKanaWorkaround;
     Vector<UChar> m_normalizedTarget;
@@ -92,7 +103,7 @@ private:
     size_t length() const;
 
     String m_target;
-    bool m_isCaseSensitive;
+    FindOptions m_options;
 
     Vector<UChar> m_buffer;
     Vector<bool> m_isCharacterStartBuffer;
@@ -1828,9 +1839,46 @@ static void normalizeCharacters(const UChar* characters, unsigned length, Vector
     ASSERT(status == U_STRING_NOT_TERMINATED_WARNING);
 }
 
-inline SearchBuffer::SearchBuffer(const String& target, bool isCaseSensitive)
+static bool isNonLatin1Separator(UChar32 character)
+{
+    ASSERT_ARG(character, character >= 256);
+
+    return U_GET_GC_MASK(character) & (U_GC_S_MASK | U_GC_P_MASK | U_GC_Z_MASK | U_GC_CF_MASK);
+}
+
+static inline bool isSeparator(UChar32 character)
+{
+    static const bool latin1SeparatorTable[256] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // space ! " # $ % & ' ( ) * + , - . /
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, //                         : ; < = > ?
+        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //   @
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, //                         [ \ ] ^ _
+        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //   `
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, //                           { | } ~
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+
+    if (character < 256)
+        return latin1SeparatorTable[character];
+
+    return isNonLatin1Separator(character);
+}
+
+inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
     : m_target(target)
+    , m_options(options)
+    , m_prefixLength(0)
     , m_atBreak(true)
+    , m_needsMoreContext(options & AtWordStarts)
     , m_targetRequiresKanaWorkaround(containsKanaLetters(m_target))
 {
     ASSERT(!m_target.isEmpty());
@@ -1852,7 +1900,7 @@ inline SearchBuffer::SearchBuffer(const String& target, bool isCaseSensitive)
     UStringSearch* searcher = WebCore::searcher();
     UCollator* collator = usearch_getCollator(searcher);
 
-    UCollationStrength strength = isCaseSensitive ? UCOL_TERTIARY : UCOL_PRIMARY;
+    UCollationStrength strength = m_options & CaseInsensitive ? UCOL_PRIMARY : UCOL_TERTIARY;
     if (ucol_getStrength(collator) != strength) {
         ucol_setStrength(collator, strength);
         usearch_reset(searcher);
@@ -1878,9 +1926,11 @@ inline size_t SearchBuffer::append(const UChar* characters, size_t length)
 
     if (m_atBreak) {
         m_buffer.shrink(0);
+        m_prefixLength = 0;
         m_atBreak = false;
     } else if (m_buffer.size() == m_buffer.capacity()) {
         memcpy(m_buffer.data(), m_buffer.data() + m_buffer.size() - m_overlap, m_overlap * sizeof(UChar));
+        m_prefixLength -= min(m_prefixLength, m_buffer.size() - m_overlap);
         m_buffer.shrink(m_overlap);
     }
 
@@ -1892,6 +1942,35 @@ inline size_t SearchBuffer::append(const UChar* characters, size_t length)
     return usableLength;
 }
 
+inline bool SearchBuffer::needsMoreContext() const
+{
+    return m_needsMoreContext;
+}
+
+inline void SearchBuffer::prependContext(const UChar* characters, size_t length)
+{
+    ASSERT(m_needsMoreContext);
+    ASSERT(m_prefixLength == m_buffer.size());
+
+    if (!length)
+        return;
+
+    m_atBreak = false;
+
+    size_t wordBoundaryContextStart = length;
+    if (wordBoundaryContextStart) {
+        U16_BACK_1(characters, 0, wordBoundaryContextStart);
+        wordBoundaryContextStart = startOfLastWordBoundaryContext(characters, wordBoundaryContextStart);
+    }
+
+    size_t usableLength = min(m_buffer.capacity() - m_prefixLength, length - wordBoundaryContextStart);
+    m_buffer.prepend(characters + length - usableLength, usableLength);
+    m_prefixLength += usableLength;
+
+    if (wordBoundaryContextStart || m_prefixLength == m_buffer.capacity())
+        m_needsMoreContext = false;
+}
+
 inline bool SearchBuffer::atBreak() const
 {
     return m_atBreak;
@@ -1962,6 +2041,55 @@ inline bool SearchBuffer::isBadMatch(const UChar* match, size_t matchLength) con
     }
 }
 
+inline bool SearchBuffer::isWordStartMatch(size_t start, size_t length) const
+{
+    ASSERT(m_options & AtWordStarts);
+
+    if (!start)
+        return true;
+
+    if (m_options & TreatMedialCapitalAsWordStart) {
+        int size = m_buffer.size();
+        int offset = start;
+        UChar32 firstCharacter;
+        U16_GET(m_buffer.data(), 0, offset, size, firstCharacter);
+        UChar32 previousCharacter;
+        U16_PREV(m_buffer.data(), 0, offset, previousCharacter);
+
+        if (isSeparator(firstCharacter)) {
+            // The start of a separator run is a word start (".org" in "webkit.org").
+            if (!isSeparator(previousCharacter))
+                return true;
+        } else if (isASCIIUpper(firstCharacter)) {
+            // The start of an uppercase run is a word start ("Kit" in "WebKit").
+            if (!isASCIIUpper(previousCharacter))
+                return true;
+            // The last character of an uppercase run followed by a non-separator, non-digit
+            // is a word start ("Request" in "XMLHTTPRequest").
+            offset = start;
+            U16_FWD_1(m_buffer.data(), offset, size);
+            UChar32 nextCharacter = 0;
+            if (offset < size)
+                U16_GET(m_buffer.data(), 0, offset, size, nextCharacter);
+            if (!isASCIIUpper(nextCharacter) && !isASCIIDigit(nextCharacter) && !isSeparator(nextCharacter))
+                return true;
+        } else if (isASCIIDigit(firstCharacter)) {
+            // The start of a digit run is a word start ("2" in "WebKit2").
+            if (!isASCIIDigit(previousCharacter))
+                return true;
+        } else if (isSeparator(previousCharacter) || isASCIIDigit(previousCharacter)) {
+            // The start of a non-separator, non-uppercase, non-digit run is a word start,
+            // except after an uppercase. ("org" in "webkit.org", but not "ore" in "WebCore").
+            return true;
+        }
+    }
+
+    size_t wordBreakSearchStart = start + length;
+    while (wordBreakSearchStart > start)
+        wordBreakSearchStart = findNextWordFromIndex(m_buffer.data(), m_buffer.size(), wordBreakSearchStart, false /* backwards */);
+    return wordBreakSearchStart == start;
+}
+
 inline size_t SearchBuffer::search(size_t& start)
 {
     size_t size = m_buffer.size();
@@ -1979,7 +2107,10 @@ inline size_t SearchBuffer::search(size_t& start)
     usearch_setText(searcher, m_buffer.data(), size, &status);
     ASSERT(status == U_ZERO_ERROR);
 
-    int matchStart = usearch_first(searcher, &status);
+    usearch_setOffset(searcher, m_prefixLength, &status);
+    ASSERT(status == U_ZERO_ERROR);
+
+    int matchStart = usearch_next(searcher, &status);
     ASSERT(status == U_ZERO_ERROR);
 
 nextMatch:
@@ -1992,8 +2123,18 @@ nextMatch:
     // The same match may appear later, matching more characters,
     // possibly including a combining character that's not yet in the buffer.
     if (!m_atBreak && static_cast<size_t>(matchStart) >= size - m_overlap) {
-        memcpy(m_buffer.data(), m_buffer.data() + size - m_overlap, m_overlap * sizeof(UChar));
-        m_buffer.shrink(m_overlap);
+        size_t overlap = m_overlap;
+        if (m_options & AtWordStarts) {
+            // Ensure that there is sufficient context before matchStart the next time around for
+            // determining if it is at a word boundary.
+            int wordBoundaryContextStart = matchStart;
+            U16_BACK_1(m_buffer.data(), 0, wordBoundaryContextStart);
+            wordBoundaryContextStart = startOfLastWordBoundaryContext(m_buffer.data(), wordBoundaryContextStart);
+            overlap = min(size - 1, max(overlap, size - wordBoundaryContextStart));
+        }
+        memcpy(m_buffer.data(), m_buffer.data() + size - overlap, overlap * sizeof(UChar));
+        m_prefixLength -= min(m_prefixLength, size - overlap);
+        m_buffer.shrink(overlap);
         return 0;
     }
 
@@ -2001,7 +2142,7 @@ nextMatch:
     ASSERT(matchStart + matchedLength <= size);
 
     // If this match is "bad", move on to the next match.
-    if (isBadMatch(m_buffer.data() + matchStart, matchedLength)) {
+    if (isBadMatch(m_buffer.data() + matchStart, matchedLength) || m_options & AtWordStarts && !isWordStartMatch(matchStart, matchedLength)) {
         matchStart = usearch_next(searcher, &status);
         ASSERT(status == U_ZERO_ERROR);
         goto nextMatch;
@@ -2009,6 +2150,7 @@ nextMatch:
 
     size_t newSize = size - (matchStart + 1);
     memmove(m_buffer.data(), m_buffer.data() + matchStart + 1, newSize * sizeof(UChar));
+    m_prefixLength -= min<size_t>(m_prefixLength, matchStart + 1);
     m_buffer.shrink(newSize);
 
     start = size - matchStart;
@@ -2017,9 +2159,9 @@ nextMatch:
 
 #else // !ICU_UNICODE
 
-inline SearchBuffer::SearchBuffer(const String& target, bool isCaseSensitive)
-    : m_target(isCaseSensitive ? target : target.foldCase())
-    , m_isCaseSensitive(isCaseSensitive)
+inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
+    : m_target(options & CaseInsensitive ? target.foldCase() : target)
+    , m_options(options)
     , m_buffer(m_target.length())
     , m_isCharacterStartBuffer(m_target.length())
     , m_isBufferFull(false)
@@ -2058,7 +2200,7 @@ inline void SearchBuffer::append(UChar c, bool isStart)
 inline size_t SearchBuffer::append(const UChar* characters, size_t length)
 {
     ASSERT(length);
-    if (m_isCaseSensitive) {
+    if (!(m_options & CaseInsensitive)) {
         append(characters[0], true);
         return 1;
     }
@@ -2078,6 +2220,16 @@ inline size_t SearchBuffer::append(const UChar* characters, size_t length)
     return 1;
 }
 
+inline bool SearchBuffer::needsMoreContext() const
+{
+    return false;
+}
+
+void prependContext(const UChar*, size_t)
+{
+    ASSERT_NOT_REACHED();
+}
+
 inline size_t SearchBuffer::search(size_t& start)
 {
     if (!m_isBufferFull)
@@ -2332,12 +2484,24 @@ static PassRefPtr<Range> collapsedToBoundary(const Range* range, bool forward)
     return result.release();
 }
 
-static size_t findPlainText(CharacterIterator& it, const String& target, bool forward, bool caseSensitive, size_t& matchStart)
+static size_t findPlainText(CharacterIterator& it, const String& target, FindOptions options, size_t& matchStart)
 {
     matchStart = 0;
     size_t matchLength = 0;
 
-    SearchBuffer buffer(target, caseSensitive);
+    SearchBuffer buffer(target, options);
+
+    if (buffer.needsMoreContext()) {
+        RefPtr<Range> startRange = it.range();
+        RefPtr<Range> beforeStartRange = startRange->ownerDocument()->createRange();
+        ExceptionCode ec = 0;
+        beforeStartRange->setEnd(startRange->startContainer(), startRange->startOffset(), ec);
+        for (SimplifiedBackwardsTextIterator backwardsIterator(beforeStartRange.get()); !backwardsIterator.atEnd(); backwardsIterator.advance()) {
+            buffer.prependContext(backwardsIterator.characters(), backwardsIterator.length());
+            if (!buffer.needsMoreContext())
+                break;
+        }
+    }
 
     while (!it.atEnd()) {
         it.advance(buffer.append(it.characters(), it.length()));
@@ -2351,7 +2515,7 @@ tryAgain:
             matchLength = newMatchLength;
             // If searching forward, stop on the first match.
             // If searching backward, don't stop, so we end up with the last match.
-            if (forward)
+            if (!(options & Backwards))
                 break;
             goto tryAgain;
         }
@@ -2366,14 +2530,19 @@ tryAgain:
 
 PassRefPtr<Range> findPlainText(const Range* range, const String& target, bool forward, bool caseSensitive)
 {
+    return findPlainText(range, target, (forward ? 0 : Backwards) | (caseSensitive ? 0 : CaseInsensitive));
+}
+
+PassRefPtr<Range> findPlainText(const Range* range, const String& target, FindOptions options)
+{
     // First, find the text.
     size_t matchStart;
     size_t matchLength;
     {
         CharacterIterator findIterator(range, TextIteratorEntersTextControls);
-        matchLength = findPlainText(findIterator, target, forward, caseSensitive, matchStart);
+        matchLength = findPlainText(findIterator, target, options, matchStart);
         if (!matchLength)
-            return collapsedToBoundary(range, forward);
+            return collapsedToBoundary(range, !(options & Backwards));
     }
 
     // Then, find the document position of the start and the end of the text.
diff --git a/WebCore/editing/TextIterator.h b/WebCore/editing/TextIterator.h
index 1bd8828..8b61afe 100644
--- a/WebCore/editing/TextIterator.h
+++ b/WebCore/editing/TextIterator.h
@@ -26,6 +26,7 @@
 #ifndef TextIterator_h
 #define TextIterator_h
 
+#include "FindOptions.h"
 #include "InlineTextBox.h"
 #include "Range.h"
 #include <wtf/Vector.h>
@@ -58,7 +59,9 @@ inline bool isCollapsibleWhitespace(UChar c)
 }
 
 String plainText(const Range*, TextIteratorBehavior defaultBehavior = TextIteratorDefaultBehavior);
-UChar* plainTextToMallocAllocatedBuffer(const Range*, unsigned& bufferLength, bool isDisplayString, TextIteratorBehavior defaultBehavior = TextIteratorDefaultBehavior);
+UChar* plainTextToMallocAllocatedBuffer(const Range*, unsigned& bufferLength, bool isDisplayString, TextIteratorBehavior = TextIteratorDefaultBehavior);
+PassRefPtr<Range> findPlainText(const Range*, const String&, FindOptions);
+// FIXME: Switch callers over to the FindOptions version and retire this one.
 PassRefPtr<Range> findPlainText(const Range*, const String&, bool forward, bool caseSensitive);
 
 class BitStack {
diff --git a/WebCore/editing/visible_units.cpp b/WebCore/editing/visible_units.cpp
index 2c7b87c..d7343bf 100644
--- a/WebCore/editing/visible_units.cpp
+++ b/WebCore/editing/visible_units.cpp
@@ -45,30 +45,6 @@ namespace WebCore {
 using namespace HTMLNames;
 using namespace WTF::Unicode;
 
-static int endOfFirstWordBoundaryContext(const UChar* characters, int length)
-{
-    for (int i = 0; i < length; ) {
-        int first = i;
-        UChar32 ch;
-        U16_NEXT(characters, i, length, ch);
-        if (!requiresContextForWordBoundary(ch))
-            return first;
-    }
-    return length;
-}
-
-static int startOfLastWordBoundaryContext(const UChar* characters, int length)
-{
-    for (int i = length; i > 0; ) {
-        int last = i;
-        UChar32 ch;
-        U16_PREV(characters, 0, i, ch);
-        if (!requiresContextForWordBoundary(ch))
-            return last;
-    }
-    return 0;
-}
-
 enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext };
 
 typedef unsigned (*BoundarySearchFunction)(const UChar*, unsigned length, unsigned offset, BoundarySearchContextAvailability, bool& needMoreContext);
diff --git a/WebCore/page/Page.cpp b/WebCore/page/Page.cpp
index be3df9a..e22762e 100644
--- a/WebCore/page/Page.cpp
+++ b/WebCore/page/Page.cpp
@@ -507,25 +507,31 @@ static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag)
 
 bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, FindDirection direction, bool shouldWrap)
 {
+    return findString(target, (caseSensitivity == TextCaseInsensitive ? CaseInsensitive : 0) | (direction == FindDirectionBackward ? Backwards : 0) | (shouldWrap ? WrapAround : 0));
+}
+
+bool Page::findString(const String& target, FindOptions options)
+{
     if (target.isEmpty() || !mainFrame())
         return false;
 
+    bool shouldWrap = options & WrapAround;
     Frame* frame = focusController()->focusedOrMainFrame();
     Frame* startFrame = frame;
     do {
-        if (frame->editor()->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) {
+        if (frame->editor()->findString(target, (options & ~WrapAround) | StartInSelection)) {
             if (frame != startFrame)
                 startFrame->selection()->clear();
             focusController()->setFocusedFrame(frame);
             return true;
         }
-        frame = incrementFrame(frame, direction == FindDirectionForward, shouldWrap);
+        frame = incrementFrame(frame, !(options & Backwards), shouldWrap);
     } while (frame && frame != startFrame);
 
     // Search contents of startFrame, on the other side of the selection that we did earlier.
     // We cheat a bit and just research with wrap on
     if (shouldWrap && !startFrame->selection()->isNone()) {
-        bool found = startFrame->editor()->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true);
+        bool found = startFrame->editor()->findString(target, options | WrapAround | StartInSelection);
         focusController()->setFocusedFrame(frame);
         return found;
     }
@@ -535,6 +541,11 @@ bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity,
 
 unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit)
 {
+    return markAllMatchesForText(target, caseSensitivity == TextCaseInsensitive ? CaseInsensitive : 0, shouldHighlight, limit);
+}
+
+unsigned int Page::markAllMatchesForText(const String& target, FindOptions options, bool shouldHighlight, unsigned limit)
+{
     if (target.isEmpty() || !mainFrame())
         return 0;
 
@@ -543,7 +554,7 @@ unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivi
     Frame* frame = mainFrame();
     do {
         frame->editor()->setMarkedTextMatchesAreHighlighted(shouldHighlight);
-        matches += frame->editor()->countMatchesForText(target, caseSensitivity == TextCaseSensitive, limit ? (limit - matches) : 0, true);
+        matches += frame->editor()->countMatchesForText(target, options, limit ? (limit - matches) : 0, true);
         frame = incrementFrame(frame, true, false);
     } while (frame);
 
diff --git a/WebCore/page/Page.h b/WebCore/page/Page.h
index e074814..a938932 100644
--- a/WebCore/page/Page.h
+++ b/WebCore/page/Page.h
@@ -22,6 +22,7 @@
 #define Page_h
 
 #include "FrameLoaderTypes.h"
+#include "FindOptions.h"
 #include "PlatformString.h"
 #include "ViewportArguments.h"
 #include <wtf/Forward.h>
@@ -198,8 +199,12 @@ namespace WebCore {
         void setTabKeyCyclesThroughElements(bool b) { m_tabKeyCyclesThroughElements = b; }
         bool tabKeyCyclesThroughElements() const { return m_tabKeyCyclesThroughElements; }
 
+        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);
-        unsigned int markAllMatchesForText(const String&, TextCaseSensitivity, bool shouldHighlight, unsigned);
+        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);
         void unmarkAllTextMatches();
 
 #if PLATFORM(MAC)
diff --git a/WebCore/platform/text/TextBoundaries.cpp b/WebCore/platform/text/TextBoundaries.cpp
index 8eaffca..fbb261b 100644
--- a/WebCore/platform/text/TextBoundaries.cpp
+++ b/WebCore/platform/text/TextBoundaries.cpp
@@ -36,6 +36,32 @@ using namespace Unicode;
 
 namespace WebCore {
 
+int endOfFirstWordBoundaryContext(const UChar* characters, int length)
+{
+    for (int i = 0; i < length; ) {
+        int first = i;
+        UChar32 ch;
+        U16_NEXT(characters, i, length, ch);
+        if (!requiresContextForWordBoundary(ch))
+            return first;
+    }
+    return length;
+}
+
+int startOfLastWordBoundaryContext(const UChar* characters, int length)
+{
+    for (int i = length; i > 0; ) {
+        int last = i;
+        UChar32 ch;
+        U16_PREV(characters, 0, i, ch);
+        if (!requiresContextForWordBoundary(ch))
+            return last;
+    }
+    return 0;
+}
+
+#if !PLATFORM(BREWMP) && !PLATFORM(MAC) && !PLATFORM(QT)
+
 int findNextWordFromIndex(const UChar* chars, int len, int position, bool forward)
 {
     TextBreakIterator* it = wordBreakIterator(chars, len);
@@ -76,4 +102,6 @@ void findWordBoundary(const UChar* chars, int len, int position, int* start, int
     *start = textBreakPrevious(it);
 }
 
+#endif // !PLATFORM(BREWMP) && !PLATFORM(MAC) && !PLATFORM(QT)
+
 } // namespace WebCore
diff --git a/WebCore/platform/text/TextBoundaries.h b/WebCore/platform/text/TextBoundaries.h
index 7eb9cab..870ab62 100644
--- a/WebCore/platform/text/TextBoundaries.h
+++ b/WebCore/platform/text/TextBoundaries.h
@@ -35,6 +35,9 @@ namespace WebCore {
         return WTF::Unicode::hasLineBreakingPropertyComplexContext(ch);
     }
 
+    int endOfFirstWordBoundaryContext(const UChar* characters, int length);
+    int startOfLastWordBoundaryContext(const UChar* characters, int length);
+
     void findWordBoundary(const UChar*, int len, int position, int* start, int* end);
     int findNextWordFromIndex(const UChar*, int len, int position, bool forward);
 
diff --git a/WebKit/mac/ChangeLog b/WebKit/mac/ChangeLog
index fe5c984..d0c7a64 100644
--- a/WebKit/mac/ChangeLog
+++ b/WebKit/mac/ChangeLog
@@ -1,3 +1,41 @@
+2010-11-29  Dan Bernstein  <mitz at apple.com>
+
+        Reviewed by Darin Adler.
+
+        WebKit Mac part of <rdar://problem/8650085> adding word-prefix search options to the text search API.
+        https://bugs.webkit.org/show_bug.cgi?id=50038
+        Based on a patch from Darin Adler.
+
+        * WebView/WebDocumentInternal.h: Removed -markAllMatchesForText:caseSensitive:limit: and
+        replaced -countMatchesForText:caseSensitive:limit:markMatches: with a WebFindOptions-based
+        method. Declared a WebDocumentOptionsSearching protocol with a new -findString:options:
+        method. Made WebHTMLView conform to the new protocol.
+        * WebView/WebHTMLView.mm:
+        (coreOptions): Added. Converts WebFindOptions to WebCore FindOptions.
+        (-[WebHTMLView searchFor:direction:caseSensitive:wrap:startInSelection:]): Changed to use
+        -findString:options:.
+        (-[WebHTMLView countMatchesForText:options:limit:markMatches:]): Changed to use WebFindOptions.
+        (-[WebHTMLView findString:options:]): Added. Calls through to WebCore::Editor::findString().
+        * WebView/WebPDFView.mm:
+        (-[WebPDFView countMatchesForText:options:limit:markMatches:]): Changed to use WebFindOptions.
+        * WebView/WebView.mm:
+        (-[WebView markAllMatchesForText:caseSensitive:highlight:limit:]): Now calls through to
+        -countMatchesForText:options:highlight:limit:markMatches.
+        (-[WebView countMatchesForText:caseSensitive:highlight:limit:markMatches:]): Ditto.
+        (-[WebView searchFor:direction:caseSensitive:wrap:startInSelection:]): Now calls through to
+        -findString:options:.
+        (incrementFrame): Changed to use WebFindOptions.
+        (findString): Added this helper method that performs the search using the best supported
+        method for the document view.
+        (-[WebView findString:options:]): Changed -searchFor::::: into this.
+        (-[WebView canMarkAllTextMatches]):
+        (-[WebView countMatchesForText:options:highlight:limit:markMatches:]): Updated to use
+        WebFindOptions.
+        (-[WebView unmarkAllTextMatches]): Updated for change to incrementFrame.
+        (-[WebView rectsForTextMatches]): Ditto.
+        * WebView/WebViewPrivate.h: Added WebFindOptions, -findString:options:, and WebFindOptions version
+        of countMatchesForText:.
+
 2010-11-29  Jeremy Moskovich  <jeremy at chromium.org>
 
         Reviewed by David Hyatt.
diff --git a/WebKit/mac/WebView/WebDocumentInternal.h b/WebKit/mac/WebView/WebDocumentInternal.h
index 0f63d75..0b86ba8 100644
--- a/WebKit/mac/WebView/WebDocumentInternal.h
+++ b/WebKit/mac/WebView/WebDocumentInternal.h
@@ -28,6 +28,7 @@
 
 #import <WebKit/WebDocumentPrivate.h>
 #import <WebKit/WebHTMLView.h>
+#import <WebKit/WebViewPrivate.h>
 
 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
 #define WebNSUInteger unsigned int
@@ -61,12 +62,14 @@
 @protocol WebMultipleTextMatches <NSObject>
 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue;
 - (BOOL)markedTextMatchesAreHighlighted;
-- (WebNSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(WebNSUInteger)limit;
-- (WebNSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches;
+- (WebNSUInteger)countMatchesForText:(NSString *)string options:(WebFindOptions)options limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches;
 - (void)unmarkAllTextMatches;
 - (NSArray *)rectsForTextMatches;
 @end
 
+ at protocol WebDocumentOptionsSearching <NSObject>
+- (BOOL)findString:(NSString *)string options:(WebFindOptions)options;
+ at end
 
 /* Used to save and restore state in the view, typically when going back/forward */
 @protocol _WebDocumentViewState <NSObject>
@@ -76,7 +79,7 @@
 - (void)setViewState:(id)statePList;
 @end
 
- at interface WebHTMLView (WebDocumentInternalProtocols) <WebDocumentElement, WebMultipleTextMatches>
+ at interface WebHTMLView (WebDocumentInternalProtocols) <WebDocumentElement, WebMultipleTextMatches, WebDocumentOptionsSearching>
 @end
 
 #undef WebNSUInteger
diff --git a/WebKit/mac/WebView/WebHTMLView.mm b/WebKit/mac/WebView/WebHTMLView.mm
index 07e4876..db78c37 100644
--- a/WebKit/mac/WebView/WebHTMLView.mm
+++ b/WebKit/mac/WebView/WebHTMLView.mm
@@ -525,6 +525,16 @@ 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
@@ -6198,10 +6208,7 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
 
 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
 {
-    if (![string length])
-        return NO;
-    Frame* coreFrame = core([self _frame]);
-    return coreFrame && coreFrame->editor()->findString(string, forward, caseFlag, wrapFlag, startInSelection);
+    return [self findString:string options:(forward ? 0 : WebFindOptionsBackwards) | (caseFlag ? 0 : WebFindOptionsCaseInsensitive) | (startInSelection ? WebFindOptionsStartInSelection : 0)];
 }
 
 @end
@@ -6221,17 +6228,12 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
     return [[[WebElementDictionary alloc] initWithHitTestResult:coreFrame->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease];
 }
 
-- (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(NSUInteger)limit
-{
-    return [self countMatchesForText:string caseSensitive:caseFlag limit:limit markMatches:YES];
-}
-
-- (NSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(NSUInteger)limit markMatches:(BOOL)markMatches
+- (NSUInteger)countMatchesForText:(NSString *)string options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches
 {
     Frame* coreFrame = core([self _frame]);
     if (!coreFrame)
         return 0;
-    return coreFrame->editor()->countMatchesForText(string, caseFlag, limit, markMatches);
+    return coreFrame->editor()->countMatchesForText(string, coreOptions(options), limit, markMatches);
 }
 
 - (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
@@ -6276,6 +6278,14 @@ static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnde
     return result;
 }
 
+- (BOOL)findString:(NSString *)string options:(WebFindOptions)options
+{
+    if (![string length])
+        return NO;
+    Frame* coreFrame = core([self _frame]);
+    return coreFrame && coreFrame->editor()->findString(string, coreOptions(options));
+}
+
 @end
 
 // This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once.
diff --git a/WebKit/mac/WebView/WebPDFView.mm b/WebKit/mac/WebView/WebPDFView.mm
index 70fceb6..35d44f7 100644
--- a/WebKit/mac/WebView/WebPDFView.mm
+++ b/WebKit/mac/WebView/WebPDFView.mm
@@ -626,19 +626,13 @@ static BOOL _PDFSelectionsAreEqual(PDFSelection *selectionA, PDFSelection *selec
     return NO;
 }
 
-- (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(NSUInteger)limit
-{
-    return [self countMatchesForText:string caseSensitive:caseFlag limit:limit markMatches:YES];
-}
-
-- (NSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(NSUInteger)limit markMatches:(BOOL)markMatches
+- (NSUInteger)countMatchesForText:(NSString *)string options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches
 {
     PDFSelection *previousMatch = nil;
-    PDFSelection *nextMatch = nil;
     NSMutableArray *matches = [[NSMutableArray alloc] initWithCapacity:limit];
     
     for (;;) {
-        nextMatch = [self _nextMatchFor:string direction:YES caseSensitive:caseFlag wrap:NO fromSelection:previousMatch startInSelection:NO];
+        PDFSelection *nextMatch = [self _nextMatchFor:string direction:YES caseSensitive:!(options & WebFindOptionsCaseInsensitive) wrap:NO fromSelection:previousMatch startInSelection:NO];
         if (!nextMatch)
             break;
         
diff --git a/WebKit/mac/WebView/WebView.mm b/WebKit/mac/WebView/WebView.mm
index 0498554..c57bf38 100644
--- a/WebKit/mac/WebView/WebView.mm
+++ b/WebKit/mac/WebView/WebView.mm
@@ -2645,6 +2645,21 @@ static PassOwnPtr<Vector<String> > toStringVector(NSArray* patterns)
     return coreFrame->pageScaleFactor();
 }
 
+- (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit
+{
+    return [self countMatchesForText:string options:(caseFlag ? 0 : WebFindOptionsCaseInsensitive) highlight:highlight limit:limit markMatches:YES];
+}
+
+- (NSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit markMatches:(BOOL)markMatches
+{
+    return [self countMatchesForText:string options:(caseFlag ? 0 : WebFindOptionsCaseInsensitive) highlight:highlight limit:limit markMatches:markMatches];
+}
+
+- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
+{
+    return [self findString:string options:((forward ? 0 : WebFindOptionsBackwards) | (caseFlag ? 0 : WebFindOptionsCaseInsensitive) | (wrapFlag ? WebFindOptionsWrapAround : 0) | (startInSelection ? WebFindOptionsStartInSelection : 0))];
+}
+
 @end
 
 @implementation _WebSafeForwarder
@@ -3824,12 +3839,12 @@ static bool needsWebViewInitThreadWorkaround()
     [super setNextKeyView:view];
 }
 
-static WebFrame *incrementFrame(WebFrame *frame, BOOL forward, BOOL wrapFlag)
+static WebFrame *incrementFrame(WebFrame *frame, WebFindOptions options = 0)
 {
     Frame* coreFrame = core(frame);
-    return kit(forward
-        ? coreFrame->tree()->traverseNextWithWrap(wrapFlag)
-        : coreFrame->tree()->traversePreviousWithWrap(wrapFlag));
+    return kit((options & WebFindOptionsBackwards)
+        ? coreFrame->tree()->traversePreviousWithWrap(options & WebFindOptionsWrapAround)
+        : coreFrame->tree()->traverseNextWithWrap(options & WebFindOptionsWrapAround));
 }
 
 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
@@ -4255,7 +4270,16 @@ static WebFrame *incrementFrame(WebFrame *frame, BOOL forward, BOOL wrapFlag)
         core(self)->removeSchedulePair(SchedulePair::create(runLoop, (CFStringRef)mode));
 }
 
-- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
+static BOOL findString(NSView <WebDocumentSearching> *searchView, NSString *string, WebFindOptions options)
+{
+    if ([searchView conformsToProtocol:@protocol(WebDocumentOptionsSearching)])
+        return [(NSView <WebDocumentOptionsSearching> *)searchView findString:string options:options];
+    if ([searchView conformsToProtocol:@protocol(WebDocumentIncrementalSearching)])
+        return [(NSView <WebDocumentIncrementalSearching> *)searchView searchFor:string direction:!(options & WebFindOptionsBackwards) caseSensitive:!(options & WebFindOptionsCaseInsensitive) wrap:!!(options & WebFindOptionsWrapAround) startInSelection:!!(options & WebFindOptionsStartInSelection)];
+    return [searchView searchFor:string direction:!(options & WebFindOptionsBackwards) caseSensitive:!(options & WebFindOptionsCaseInsensitive) wrap:!!(options & WebFindOptionsWrapAround)];
+}
+
+- (BOOL)findString:(NSString *)string options:(WebFindOptions)options
 {
     if (_private->closed)
         return NO;
@@ -4267,7 +4291,7 @@ static WebFrame *incrementFrame(WebFrame *frame, BOOL forward, BOOL wrapFlag)
     NSView <WebDocumentSearching> *startSearchView = nil;
     WebFrame *frame = startFrame;
     do {
-        WebFrame *nextFrame = incrementFrame(frame, forward, wrapFlag);
+        WebFrame *nextFrame = incrementFrame(frame, options);
         
         BOOL onlyOneFrame = (frame == nextFrame);
         ASSERT(!onlyOneFrame || frame == startFrame);
@@ -4279,18 +4303,13 @@ static WebFrame *incrementFrame(WebFrame *frame, BOOL forward, BOOL wrapFlag)
             if (frame == startFrame)
                 startSearchView = searchView;
             
-            BOOL foundString;
             // In some cases we have to search some content twice; see comment later in this method.
-            // We can avoid ever doing this in the common one-frame case by passing YES for wrapFlag 
+            // We can avoid ever doing this in the common one-frame case by passing the wrap option through 
             // here, and then bailing out before we get to the code that would search again in the
             // same content.
-            BOOL wrapOnThisPass = wrapFlag && onlyOneFrame;
-            if ([searchView conformsToProtocol:@protocol(WebDocumentIncrementalSearching)])
-                foundString = [(NSView <WebDocumentIncrementalSearching> *)searchView searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapOnThisPass startInSelection:startInSelection];
-            else
-                foundString = [searchView searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapOnThisPass];
-            
-            if (foundString) {
+            WebFindOptions optionsForThisPass = onlyOneFrame ? options : (options & ~WebFindOptionsWrapAround);
+
+            if (findString(searchView, string, optionsForThisPass)) {
                 if (frame != startFrame)
                     [startFrame _clearSelection];
                 [[self window] makeFirstResponder:searchView];
@@ -4303,18 +4322,13 @@ static WebFrame *incrementFrame(WebFrame *frame, BOOL forward, BOOL wrapFlag)
         frame = nextFrame;
     } while (frame && frame != startFrame);
     
-    // If there are multiple frames and wrapFlag is true and we've visited each one without finding a result, we still need to search in the 
+    // If there are multiple frames and WebFindOptionsWrapAround is set and we've visited each one without finding a result, we still need to search in the 
     // first-searched frame up to the selection. However, the API doesn't provide a way to search only up to a particular point. The only 
-    // way to make sure the entire frame is searched is to pass YES for the wrapFlag. When there are no matches, this will search again
+    // way to make sure the entire frame is searched is to pass WebFindOptionsWrapAround. When there are no matches, this will search
     // some content that we already searched on the first pass. In the worst case, we could search the entire contents of this frame twice.
     // To fix this, we'd need to add a mechanism to specify a range in which to search.
-    if (wrapFlag && startSearchView) {
-        BOOL foundString;
-        if ([startSearchView conformsToProtocol:@protocol(WebDocumentIncrementalSearching)])
-            foundString = [(NSView <WebDocumentIncrementalSearching> *)startSearchView searchFor:string direction:forward caseSensitive:caseFlag wrap:YES startInSelection:startInSelection];
-        else
-            foundString = [startSearchView searchFor:string direction:forward caseSensitive:caseFlag wrap:YES];
-        if (foundString) {
+    if ((options & WebFindOptionsWrapAround) && startSearchView) {
+        if (findString(startSearchView, string, options)) {
             [[self window] makeFirstResponder:startSearchView];
             return YES;
         }
@@ -4496,21 +4510,13 @@ static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue jsValu
         if (view && ![view conformsToProtocol:@protocol(WebMultipleTextMatches)])
             return NO;
         
-        frame = incrementFrame(frame, YES, NO);
+        frame = incrementFrame(frame);
     } while (frame);
     
     return YES;
 }
 
-- (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit
-{
-    if (_private->closed)
-        return 0;
-
-    return [self countMatchesForText:string caseSensitive:caseFlag highlight:highlight limit:limit markMatches:YES];
-}
-
-- (NSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit markMatches:(BOOL)markMatches
+- (NSUInteger)countMatchesForText:(NSString *)string options:(WebFindOptions)options highlight:(BOOL)highlight limit:(NSUInteger)limit markMatches:(BOOL)markMatches
 {
     if (_private->closed)
         return 0;
@@ -4524,14 +4530,14 @@ static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue jsValu
                 [(NSView <WebMultipleTextMatches>*)view setMarkedTextMatchesAreHighlighted:highlight];
         
             ASSERT(limit == 0 || matchCount < limit);
-            matchCount += [(NSView <WebMultipleTextMatches>*)view countMatchesForText:string caseSensitive:caseFlag limit:limit == 0 ? 0 : limit - matchCount markMatches:markMatches];
+            matchCount += [(NSView <WebMultipleTextMatches>*)view countMatchesForText:string options:options limit:(limit == 0 ? 0 : limit - matchCount) markMatches:markMatches];
 
             // Stop looking if we've reached the limit. A limit of 0 means no limit.
             if (limit > 0 && matchCount >= limit)
                 break;
         }
         
-        frame = incrementFrame(frame, YES, NO);
+        frame = incrementFrame(frame);
     } while (frame);
     
     return matchCount;
@@ -4548,7 +4554,7 @@ static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue jsValu
         if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)])
             [(NSView <WebMultipleTextMatches>*)view unmarkAllTextMatches];
         
-        frame = incrementFrame(frame, YES, NO);
+        frame = incrementFrame(frame);
     } while (frame);
 }
 
@@ -4586,7 +4592,7 @@ static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue jsValu
             [pool drain];
         }
         
-        frame = incrementFrame(frame, YES, NO);
+        frame = incrementFrame(frame);
     } while (frame);
     
     return result;
diff --git a/WebKit/mac/WebView/WebViewPrivate.h b/WebKit/mac/WebView/WebViewPrivate.h
index af594d9..5b995e9 100644
--- a/WebKit/mac/WebView/WebViewPrivate.h
+++ b/WebKit/mac/WebView/WebViewPrivate.h
@@ -70,7 +70,7 @@ extern NSString *WebElementIsContentEditableKey; // NSNumber indicating whether
 extern NSString *WebElementMediaURLKey;          // NSURL of the media element
 
 // other WebElementDictionary keys
-extern NSString *WebElementLinkIsLiveKey;        // NSNumber of BOOL indictating whether the link is live or not
+extern NSString *WebElementLinkIsLiveKey;        // NSNumber of BOOL indicating whether the link is live or not
 extern NSString *WebElementIsInScrollBarKey;
 
 // One of the subviews of the WebView entered compositing mode.
@@ -96,6 +96,16 @@ typedef enum {
     WebInjectInTopFrameOnly
 } WebUserContentInjectedFrames;
 
+enum {
+    WebFindOptionsCaseInsensitive = 1 << 0,
+    WebFindOptionsAtWordStarts = 1 << 1,
+    WebFindOptionsTreatMedialCapitalAsWordStart = 1 << 2,
+    WebFindOptionsBackwards = 1 << 3,
+    WebFindOptionsWrapAround = 1 << 4,
+    WebFindOptionsStartInSelection = 1 << 5
+};
+typedef NSUInteger WebFindOptions;
+
 @interface WebController : NSTreeController {
     IBOutlet WebView *webView;
 }
@@ -114,18 +124,7 @@ typedef enum {
 - (void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode;
 - (void)unscheduleFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode;
 
-/*!
- at method searchFor:direction:caseSensitive:wrap:startInSelection:
- @abstract Searches a document view for a string and highlights the string if it is found.
- Starts the search from the current selection.  Will search across all frames.
- @param string The string to search for.
- @param forward YES to search forward, NO to seach backwards.
- @param caseFlag YES to for case-sensitive search, NO for case-insensitive search.
- @param wrapFlag YES to wrap around, NO to avoid wrapping.
- @param startInSelection YES to begin search in the selected text (useful for incremental searching), NO to begin search after the selected text.
- @result YES if found, NO if not found.
- */
-- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection;
+- (BOOL)findString:(NSString *)string options:(WebFindOptions)options;
 
 - (void)setMainFrameDocumentReady:(BOOL)mainFrameDocumentReady;
 
@@ -182,8 +181,7 @@ typedef enum {
 // whether or not they implement the protocol. For now we'll just deal with HTML.
 // These methods are still in flux; don't rely on them yet.
 - (BOOL)canMarkAllTextMatches;
-- (WebNSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(WebNSUInteger)limit;
-- (WebNSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches;
+- (WebNSUInteger)countMatchesForText:(NSString *)string options:(WebFindOptions)options highlight:(BOOL)highlight limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches;
 - (void)unmarkAllTextMatches;
 - (NSArray *)rectsForTextMatches;
 
@@ -545,6 +543,24 @@ Could be worth adding to the API.
 - (void)_scaleWebView:(float)scale atOrigin:(NSPoint)origin;
 - (float)_viewScaleFactor;
 
+// Deprecated. Use the methods in pending public above instead.
+- (WebNSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(WebNSUInteger)limit;
+- (WebNSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches;
+
+/*!
+ @method searchFor:direction:caseSensitive:wrap:startInSelection:
+ @abstract Searches a document view for a string and highlights the string if it is found.
+ Starts the search from the current selection.  Will search across all frames.
+ @param string The string to search for.
+ @param forward YES to search forward, NO to seach backwards.
+ @param caseFlag YES to for case-sensitive search, NO for case-insensitive search.
+ @param wrapFlag YES to wrap around, NO to avoid wrapping.
+ @param startInSelection YES to begin search in the selected text (useful for incremental searching), NO to begin search after the selected text.
+ @result YES if found, NO if not found.
+ */
+// Deprecated. Use findString.
+- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection;
+
 @end
 
 @interface WebView (WebViewPrintingPrivate)
diff --git a/WebKit2/ChangeLog b/WebKit2/ChangeLog
index 54866be..704f731 100644
--- a/WebKit2/ChangeLog
+++ b/WebKit2/ChangeLog
@@ -1,3 +1,35 @@
+2010-11-29  Dan Bernstein  <mitz at apple.com>
+
+        Reviewed by Darin Adler.
+
+        WebKit2 part of <rdar://problem/8650085> adding word-prefix search options to the text search API.
+        https://bugs.webkit.org/show_bug.cgi?id=50038
+        Based on a patch from Darin Adler.
+
+        * Shared/WebFindOptions.h: Renamed FindOptions.h to this to account for WebCore’s new private
+        FindOptions.h. Added and reordered FindOptions and rolled FindDirection into FindOptions.
+        * UIProcess/API/C/WKAPICast.h: Removed toFindDirection().
+        (WebKit::toFindOptions): Updated for new values.
+        * UIProcess/API/C/WKPage.cpp:
+        (WKPageFindString): Removed separate WKFindDirection.
+        (WKPageCountStringMatches): Replaces caseInsensitive boolean with WKFindOptions.
+        * UIProcess/API/C/WKPage.h: Removed WKFindDirection and updated WKFindOptions.
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::findString): Removed separate FindDirection.
+        (WebKit::WebPageProxy::countStringMatches): Replaced caseInsensitive boolean with FindOptions.
+        * UIProcess/WebPageProxy.h:
+        * WebKit2.xcodeproj/project.pbxproj: Updated for the header rename.
+        * WebProcess/WebPage/FindController.cpp:
+        (WebKit::core): Added. Converts WebKit2 FindOptions to WebCore FindOptions.
+        (WebKit::FindController::countStringMatches): Changed to use FindOptions.
+        (WebKit::FindController::findString): Ditto.
+        * WebProcess/WebPage/FindController.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::findString): Ditto.
+        (WebKit::WebPage::countStringMatches): Ditto.
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in: Ditto.
+
 2010-11-29  Brent Fulgham  <bfulgham at webkit.org>
 
         Unreviewed build fix.
diff --git a/WebKit2/Shared/FindOptions.h b/WebKit2/Shared/FindOptions.h
deleted file mode 100644
index efe671e..0000000
--- a/WebKit2/Shared/FindOptions.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef FindOptions_h
-#define FindOptions_h
-
-namespace WebKit {
-
-enum FindDirection { 
-    FindDirectionForward, 
-    FindDirectionBackward
-};
-
-enum FindOptions {
-    FindOptionsCaseInsensitive = 1 << 0,
-    FindOptionsWrapAround = 1 << 1,
-    FindOptionsShowOverlay = 1 << 2,
-    FindOptionsShowFindIndicator = 1 << 3,
-};
-
-} // namespace WebKit
-
-#endif // FindOptions_h
diff --git a/WebKit2/Shared/WebFindOptions.h b/WebKit2/Shared/WebFindOptions.h
new file mode 100644
index 0000000..9c2a899
--- /dev/null
+++ b/WebKit2/Shared/WebFindOptions.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef WebFindOptions_h
+#define WebFindOptions_h
+
+namespace WebKit {
+
+enum FindOptions {
+    FindOptionsCaseInsensitive = 1 << 0,
+    FindOptionsAtWordStarts = 1 << 1,
+    FindOptionsTreatMedialCapitalAsWordStart = 1 << 2,
+    FindOptionsBackwards = 1 << 3,
+    FindOptionsWrapAround = 1 << 4,
+    FindOptionsShowOverlay = 1 << 5,
+    FindOptionsShowFindIndicator = 1 << 6
+};
+
+} // namespace WebKit
+
+#endif // WebFindOptions_h
diff --git a/WebKit2/UIProcess/API/C/WKAPICast.h b/WebKit2/UIProcess/API/C/WKAPICast.h
index d06996d..0045984 100644
--- a/WebKit2/UIProcess/API/C/WKAPICast.h
+++ b/WebKit2/UIProcess/API/C/WKAPICast.h
@@ -27,12 +27,12 @@
 #define WKAPICast_h
 
 #include "CacheModel.h"
-#include "FindOptions.h"
 #include "FontSmoothingLevel.h"
 #include "WKContext.h"
 #include "WKPage.h"
 #include "WKPreferencesPrivate.h"
 #include "WKSharedAPICast.h"
+#include "WebFindOptions.h"
 #include <WebCore/FrameLoaderTypes.h>
 
 namespace WebKit {
@@ -124,25 +124,18 @@ inline WKCacheModel toAPI(CacheModel cacheModel)
     return kWKCacheModelDocumentViewer;
 }
 
-inline FindDirection toFindDirection(WKFindDirection wkFindDirection)
-{
-    switch (wkFindDirection) {
-    case kWKFindDirectionForward:
-        return FindDirectionForward;
-    case kWKFindDirectionBackward:
-        return FindDirectionBackward;
-    }
-
-    ASSERT_NOT_REACHED();
-    return FindDirectionForward;
-}
-
 inline FindOptions toFindOptions(WKFindOptions wkFindOptions)
 {
     unsigned findOptions = 0;
 
     if (wkFindOptions & kWKFindOptionsCaseInsensitive)
         findOptions |= FindOptionsCaseInsensitive;
+    if (wkFindOptions & kWKFindOptionsAtWordStarts)
+        findOptions |= FindOptionsAtWordStarts;
+    if (wkFindOptions & kWKFindOptionsTreatMedialCapitalAsWordStart)
+        findOptions |= FindOptionsTreatMedialCapitalAsWordStart;
+    if (wkFindOptions & kWKFindOptionsBackwards)
+        findOptions |= FindOptionsBackwards;
     if (wkFindOptions & kWKFindOptionsWrapAround)
         findOptions |= FindOptionsWrapAround;
     if (wkFindOptions & kWKFindOptionsShowOverlay)
diff --git a/WebKit2/UIProcess/API/C/WKPage.cpp b/WebKit2/UIProcess/API/C/WKPage.cpp
index 1d4420f..9d4ba91 100644
--- a/WebKit2/UIProcess/API/C/WKPage.cpp
+++ b/WebKit2/UIProcess/API/C/WKPage.cpp
@@ -230,9 +230,9 @@ double WKPageGetViewScaleFactor(WKPageRef pageRef)
     return toImpl(pageRef)->viewScaleFactor();
 }
 
-void WKPageFindString(WKPageRef pageRef, WKStringRef string, WKFindDirection findDirection, WKFindOptions findOptions, unsigned maxMatchCount)
+void WKPageFindString(WKPageRef pageRef, WKStringRef string, WKFindOptions options, unsigned maxMatchCount)
 {
-    toImpl(pageRef)->findString(toImpl(string)->string(), toFindDirection(findDirection), toFindOptions(findOptions), maxMatchCount);
+    toImpl(pageRef)->findString(toImpl(string)->string(), toFindOptions(options), maxMatchCount);
 }
 
 void WKPageHideFindUI(WKPageRef pageRef)
@@ -240,9 +240,9 @@ void WKPageHideFindUI(WKPageRef pageRef)
     toImpl(pageRef)->hideFindUI();
 }
 
-void WKPageCountStringMatches(WKPageRef pageRef, WKStringRef string, bool caseInsensitive, unsigned maxMatchCount)
+void WKPageCountStringMatches(WKPageRef pageRef, WKStringRef string, WKFindOptions options, unsigned maxMatchCount)
 {
-    toImpl(pageRef)->countStringMatches(toImpl(string)->string(), caseInsensitive, maxMatchCount);
+    toImpl(pageRef)->countStringMatches(toImpl(string)->string(), toFindOptions(options), maxMatchCount);
 }
 
 void WKPageSetPageContextMenuClient(WKPageRef pageRef, const WKPageContextMenuClient* wkClient)
diff --git a/WebKit2/UIProcess/API/C/WKPage.h b/WebKit2/UIProcess/API/C/WKPage.h
index 1b4eb44..9512520 100644
--- a/WebKit2/UIProcess/API/C/WKPage.h
+++ b/WebKit2/UIProcess/API/C/WKPage.h
@@ -274,22 +274,19 @@ WK_EXPORT double WKPageGetViewScaleFactor(WKPageRef page);
 
 // Find.
 enum {
-    kWKFindDirectionForward,
-    kWKFindDirectionBackward
-};
-typedef uint32_t WKFindDirection;
-
-enum {
     kWKFindOptionsCaseInsensitive = 1 << 0,
-    kWKFindOptionsWrapAround = 1 << 1,
-    kWKFindOptionsShowOverlay = 1 << 2,
-    kWKFindOptionsShowFindIndicator = 1 << 3
+    kWKFindOptionsAtWordStarts = 1 << 1,
+    kWKFindOptionsTreatMedialCapitalAsWordStart = 1 << 2,
+    kWKFindOptionsBackwards = 1 << 3,
+    kWKFindOptionsWrapAround = 1 << 4,
+    kWKFindOptionsShowOverlay = 1 << 5,
+    kWKFindOptionsShowFindIndicator = 1 << 6
 };
 typedef uint32_t WKFindOptions;
 
-WK_EXPORT void WKPageFindString(WKPageRef page, WKStringRef string, WKFindDirection findDirection, WKFindOptions findOptions, unsigned maxMatchCount);
+WK_EXPORT void WKPageFindString(WKPageRef page, WKStringRef string, WKFindOptions findOptions, unsigned maxMatchCount);
 WK_EXPORT void WKPageHideFindUI(WKPageRef page);
-WK_EXPORT void WKPageCountStringMatches(WKPageRef page, WKStringRef string, bool caseInsensitive, unsigned maxMatchCount);
+WK_EXPORT void WKPageCountStringMatches(WKPageRef page, WKStringRef string, WKFindOptions findOptions, unsigned maxMatchCount);
 
 WK_EXPORT void WKPageSetPageContextMenuClient(WKPageRef page, const WKPageContextMenuClient* client);
 WK_EXPORT void WKPageSetPageFindClient(WKPageRef page, const WKPageFindClient* client);
diff --git a/WebKit2/UIProcess/WebPageProxy.cpp b/WebKit2/UIProcess/WebPageProxy.cpp
index b0e9974..d72b361 100644
--- a/WebKit2/UIProcess/WebPageProxy.cpp
+++ b/WebKit2/UIProcess/WebPageProxy.cpp
@@ -607,9 +607,9 @@ void WebPageProxy::scaleWebView(double scale, const IntPoint& origin)
     process()->send(Messages::WebPage::ScaleWebView(scale, origin), m_pageID);
 }
 
-void WebPageProxy::findString(const String& string, FindDirection findDirection, FindOptions findOptions, unsigned maxMatchCount)
+void WebPageProxy::findString(const String& string, FindOptions options, unsigned maxMatchCount)
 {
-    process()->send(Messages::WebPage::FindString(string, findDirection, findOptions, maxMatchCount), m_pageID);
+    process()->send(Messages::WebPage::FindString(string, options, maxMatchCount), m_pageID);
 }
 
 void WebPageProxy::hideFindUI()
@@ -617,9 +617,9 @@ void WebPageProxy::hideFindUI()
     process()->send(Messages::WebPage::HideFindUI(), m_pageID);
 }
 
-void WebPageProxy::countStringMatches(const String& string, bool caseInsensitive, unsigned maxMatchCount)
+void WebPageProxy::countStringMatches(const String& string, FindOptions options, unsigned maxMatchCount)
 {
-    process()->send(Messages::WebPage::CountStringMatches(string, caseInsensitive, maxMatchCount), m_pageID);
+    process()->send(Messages::WebPage::CountStringMatches(string, options, maxMatchCount), m_pageID);
 }
     
 void WebPageProxy::runJavaScriptInMainFrame(const String& script, PassRefPtr<ScriptReturnValueCallback> prpCallback)
diff --git a/WebKit2/UIProcess/WebPageProxy.h b/WebKit2/UIProcess/WebPageProxy.h
index ca8039b..ccc8588 100644
--- a/WebKit2/UIProcess/WebPageProxy.h
+++ b/WebKit2/UIProcess/WebPageProxy.h
@@ -28,7 +28,6 @@
 
 #include "APIObject.h"
 #include "DrawingAreaProxy.h"
-#include "FindOptions.h"
 #include "GenericCallback.h"
 #include "SharedMemory.h"
 #include "WKBase.h"
@@ -36,6 +35,7 @@
 #include "WebContextMenuItemData.h"
 #include "WebEvent.h"
 #include "WebFindClient.h"
+#include "WebFindOptions.h"
 #include "WebFormClient.h"
 #include "WebFrameProxy.h"
 #include "WebHistoryClient.h"
@@ -208,9 +208,9 @@ public:
     double viewScaleFactor() const { return m_viewScaleFactor; }
 
     // Find.
-    void findString(const String&, FindDirection, FindOptions, unsigned maxMatchCount);
+    void findString(const String&, FindOptions, unsigned maxMatchCount);
     void hideFindUI();
-    void countStringMatches(const String&, bool caseInsensitive, unsigned maxMatchCount);
+    void countStringMatches(const String&, FindOptions, unsigned maxMatchCount);
 
     void runJavaScriptInMainFrame(const String&, PassRefPtr<ScriptReturnValueCallback>);
     void getRenderTreeExternalRepresentation(PassRefPtr<RenderTreeExternalRepresentationCallback>);
diff --git a/WebKit2/WebKit2.pro b/WebKit2/WebKit2.pro
index aa4c1d6..fa7f880 100644
--- a/WebKit2/WebKit2.pro
+++ b/WebKit2/WebKit2.pro
@@ -245,6 +245,7 @@ HEADERS += \
     Shared/WebError.h \
     Shared/WebEvent.h \
     Shared/WebEventConversion.h \
+    Shared/WebFindOptions.h \
     Shared/WebNavigationDataStore.h \
     Shared/WebNumber.h \
     Shared/WebPageCreationParameters.h \
diff --git a/WebKit2/WebKit2.xcodeproj/project.pbxproj b/WebKit2/WebKit2.xcodeproj/project.pbxproj
index 3261170..71c4e30 100644
--- a/WebKit2/WebKit2.xcodeproj/project.pbxproj
+++ b/WebKit2/WebKit2.xcodeproj/project.pbxproj
@@ -140,7 +140,7 @@
 		1A8EFA711252B84100F7067F /* PluginProxyMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A8EFA6F1252B84100F7067F /* PluginProxyMessages.h */; };
 		1A8EFDFA1253CAA200F7067F /* DataReference.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A8EFDF91253CAA200F7067F /* DataReference.h */; };
 		1A8EFDFE1253CB6E00F7067F /* DataReference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A8EFDFD1253CB6E00F7067F /* DataReference.cpp */; };
-		1A90C1EE1264FD50003E44D4 /* FindOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A90C1ED1264FD50003E44D4 /* FindOptions.h */; };
+		1A90C1EE1264FD50003E44D4 /* WebFindOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A90C1ED1264FD50003E44D4 /* WebFindOptions.h */; };
 		1A90C1F41264FD71003E44D4 /* FindController.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A90C1F21264FD71003E44D4 /* FindController.h */; };
 		1A90C1F51264FD71003E44D4 /* FindController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A90C1F31264FD71003E44D4 /* FindController.cpp */; };
 		1A90C23712650717003E44D4 /* PageOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A90C23512650717003E44D4 /* PageOverlay.h */; };
@@ -700,7 +700,7 @@
 		1A8EFA6F1252B84100F7067F /* PluginProxyMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginProxyMessages.h; sourceTree = "<group>"; };
 		1A8EFDF91253CAA200F7067F /* DataReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataReference.h; sourceTree = "<group>"; };
 		1A8EFDFD1253CB6E00F7067F /* DataReference.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataReference.cpp; sourceTree = "<group>"; };
-		1A90C1ED1264FD50003E44D4 /* FindOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FindOptions.h; sourceTree = "<group>"; };
+		1A90C1ED1264FD50003E44D4 /* WebFindOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebFindOptions.h; sourceTree = "<group>"; };
 		1A90C1F21264FD71003E44D4 /* FindController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FindController.h; sourceTree = "<group>"; };
 		1A90C1F31264FD71003E44D4 /* FindController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FindController.cpp; sourceTree = "<group>"; };
 		1A90C23512650717003E44D4 /* PageOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageOverlay.h; sourceTree = "<group>"; };
@@ -1385,7 +1385,6 @@
 				1A2D956D12848564001EB962 /* ChildProcess.h */,
 				1A6F9F8E11E13EFC00DB1371 /* CommandLine.h */,
 				0FB659221208B4DB0044816C /* DrawingAreaBase.h */,
-				1A90C1ED1264FD50003E44D4 /* FindOptions.h */,
 				762B7481120BBA0100819339 /* FontSmoothingLevel.h */,
 				BC64696D11DBE603006455B0 /* ImmutableArray.cpp */,
 				BC64696E11DBE603006455B0 /* ImmutableArray.h */,
@@ -1417,6 +1416,7 @@
 				BC032DAF10F4380F0058C15A /* WebEvent.h */,
 				BC032DB010F4380F0058C15A /* WebEventConversion.cpp */,
 				BC032DB110F4380F0058C15A /* WebEventConversion.h */,
+				1A90C1ED1264FD50003E44D4 /* WebFindOptions.h */,
 				C0337DD2127A2A0E008FF4F4 /* WebKeyboardEvent.cpp */,
 				C0337DAF127A28D0008FF4F4 /* WebMouseEvent.cpp */,
 				BCF69F981176CED600471A52 /* WebNavigationDataStore.h */,
@@ -2378,7 +2378,7 @@
 				BC5744F012638FB3006F0F12 /* WebPopupItem.h in Headers */,
 				BC57450C1263B155006F0F12 /* WKBundleNodeHandlePrivate.h in Headers */,
 				1AC41AC71263C88300054E94 /* BinarySemaphore.h in Headers */,
-				1A90C1EE1264FD50003E44D4 /* FindOptions.h in Headers */,
+				1A90C1EE1264FD50003E44D4 /* WebFindOptions.h in Headers */,
 				1A90C1F41264FD71003E44D4 /* FindController.h in Headers */,
 				1A90C23712650717003E44D4 /* PageOverlay.h in Headers */,
 				BC574E631267D080006F0F12 /* WebPopupMenuProxy.h in Headers */,
diff --git a/WebKit2/WebProcess/WebPage/FindController.cpp b/WebKit2/WebProcess/WebPage/FindController.cpp
index 7fdcb0f..f1a251c 100644
--- a/WebKit2/WebProcess/WebPage/FindController.cpp
+++ b/WebKit2/WebProcess/WebPage/FindController.cpp
@@ -40,6 +40,15 @@ using namespace WebCore;
 
 namespace WebKit {
 
+static WebCore::FindOptions core(FindOptions options)
+{
+    return (options & FindOptionsCaseInsensitive ? CaseInsensitive : 0)
+        | (options & FindOptionsAtWordStarts ? AtWordStarts : 0)
+        | (options & FindOptionsTreatMedialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0)
+        | (options & FindOptionsBackwards ? Backwards : 0)
+        | (options & FindOptionsWrapAround ? WrapAround : 0);
+}
+
 FindController::FindController(WebPage* webPage)
     : m_webPage(webPage)
     , m_findPageOverlay(0)
@@ -51,9 +60,9 @@ FindController::~FindController()
 {
 }
 
-void FindController::countStringMatches(const String& string, bool caseInsensitive, unsigned maxMatchCount)
+void FindController::countStringMatches(const String& string, FindOptions options, unsigned maxMatchCount)
 {
-    unsigned matchCount = m_webPage->corePage()->markAllMatchesForText(string, caseInsensitive ? TextCaseInsensitive : TextCaseSensitive, false, maxMatchCount);
+    unsigned matchCount = m_webPage->corePage()->markAllMatchesForText(string, core(options), false, maxMatchCount);
     m_webPage->corePage()->unmarkAllTextMatches();
 
     m_webPage->send(Messages::WebPageProxy::DidCountStringMatches(string, matchCount));
@@ -69,14 +78,11 @@ static Frame* frameWithSelection(Page* page)
     return 0;
 }
 
-void FindController::findString(const String& string, FindDirection findDirection, FindOptions findOptions, unsigned maxMatchCount)
+void FindController::findString(const String& string, FindOptions options, unsigned maxMatchCount)
 {
     m_webPage->corePage()->unmarkAllTextMatches();
 
-    TextCaseSensitivity caseSensitivity = findOptions & FindOptionsCaseInsensitive ? TextCaseInsensitive : TextCaseSensitive;
-    bool found = m_webPage->corePage()->findString(string, caseSensitivity,
-                                                   findDirection == FindDirectionForward ? WebCore::FindDirectionForward : WebCore::FindDirectionBackward,
-                                                   findOptions & FindOptionsWrapAround);
+    bool found = m_webPage->corePage()->findString(string, core(options));
 
     Frame* selectedFrame = frameWithSelection(m_webPage->corePage());
 
@@ -91,10 +97,10 @@ void FindController::findString(const String& string, FindDirection findDirectio
 
         m_webPage->send(Messages::WebPageProxy::DidFailToFindString(string));
     } else {
-        shouldShowOverlay = findOptions & FindOptionsShowOverlay;
+        shouldShowOverlay = options & FindOptionsShowOverlay;
 
         if (shouldShowOverlay) {
-            unsigned matchCount = m_webPage->corePage()->markAllMatchesForText(string, caseSensitivity, false, maxMatchCount + 1);
+            unsigned matchCount = m_webPage->corePage()->markAllMatchesForText(string, core(options), false, maxMatchCount + 1);
 
             // Check if we have more matches than allowed.
             if (matchCount > maxMatchCount) {
@@ -105,7 +111,7 @@ void FindController::findString(const String& string, FindDirection findDirectio
             m_webPage->send(Messages::WebPageProxy::DidFindString(string, matchCount));
         }
 
-        if (!(findOptions & FindOptionsShowFindIndicator) || !updateFindIndicator(selectedFrame, shouldShowOverlay)) {
+        if (!(options & FindOptionsShowFindIndicator) || !updateFindIndicator(selectedFrame, shouldShowOverlay)) {
             // Either we shouldn't show the find indicator, or we couldn't update it.
             hideFindIndicator();
         }
diff --git a/WebKit2/WebProcess/WebPage/FindController.h b/WebKit2/WebProcess/WebPage/FindController.h
index 14bfbb0..110a7a4 100644
--- a/WebKit2/WebProcess/WebPage/FindController.h
+++ b/WebKit2/WebProcess/WebPage/FindController.h
@@ -26,8 +26,8 @@
 #ifndef FindController_h
 #define FindController_h
 
-#include "FindOptions.h"
 #include "PageOverlay.h"
+#include "WebFindOptions.h"
 #include <wtf/Forward.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/Vector.h>
@@ -48,9 +48,9 @@ public:
     explicit FindController(WebPage*);
     virtual ~FindController();
 
-    void findString(const String&, FindDirection, FindOptions, unsigned maxMatchCount);
+    void findString(const String&, FindOptions, unsigned maxMatchCount);
     void hideFindUI();
-    void countStringMatches(const String&, bool caseInsensitive, unsigned maxMatchCount);
+    void countStringMatches(const String&, FindOptions, unsigned maxMatchCount);
     
     void hideFindIndicator();
 
diff --git a/WebKit2/WebProcess/WebPage/WebPage.cpp b/WebKit2/WebProcess/WebPage/WebPage.cpp
index 4883e3a..207ed35 100644
--- a/WebKit2/WebProcess/WebPage/WebPage.cpp
+++ b/WebKit2/WebProcess/WebPage/WebPage.cpp
@@ -998,9 +998,9 @@ void WebPage::setActivePopupMenu(WebPopupMenu* menu)
     m_activePopupMenu = menu;
 }
 
-void WebPage::findString(const String& string, uint32_t findDirection, uint32_t findOptions, uint32_t maxMatchCount)
+void WebPage::findString(const String& string, uint32_t options, uint32_t maxMatchCount)
 {
-    m_findController.findString(string, static_cast<FindDirection>(findDirection), static_cast<FindOptions>(findOptions), maxMatchCount);
+    m_findController.findString(string, static_cast<FindOptions>(options), maxMatchCount);
 }
 
 void WebPage::hideFindUI()
@@ -1008,9 +1008,9 @@ void WebPage::hideFindUI()
     m_findController.hideFindUI();
 }
 
-void WebPage::countStringMatches(const String& string, bool caseInsensitive, uint32_t maxMatchCount)
+void WebPage::countStringMatches(const String& string, uint32_t options, uint32_t maxMatchCount)
 {
-    m_findController.countStringMatches(string, caseInsensitive, maxMatchCount);
+    m_findController.countStringMatches(string, static_cast<FindOptions>(options), maxMatchCount);
 }
 
 void WebPage::didChangeSelectedIndexForActivePopupMenu(int32_t newIndex)
diff --git a/WebKit2/WebProcess/WebPage/WebPage.h b/WebKit2/WebProcess/WebPage/WebPage.h
index dc5a717..ed2264d 100644
--- a/WebKit2/WebProcess/WebPage/WebPage.h
+++ b/WebKit2/WebProcess/WebPage/WebPage.h
@@ -290,9 +290,9 @@ private:
     void reapplyEditCommand(uint64_t commandID);
     void didRemoveEditCommand(uint64_t commandID);
 
-    void findString(const String&, uint32_t findDirection, uint32_t findOptions, uint32_t maxMatchCount);
+    void findString(const String&, uint32_t findOptions, uint32_t maxMatchCount);
     void hideFindUI();
-    void countStringMatches(const String&, bool caseInsensitive, uint32_t maxMatchCount);
+    void countStringMatches(const String&, uint32_t findOptions, uint32_t maxMatchCount);
 
 #if PLATFORM(QT)
     void findZoomableAreaForPoint(const WebCore::IntPoint&);
diff --git a/WebKit2/WebProcess/WebPage/WebPage.messages.in b/WebKit2/WebProcess/WebPage/WebPage.messages.in
index 3f39487..ef86ad1 100644
--- a/WebKit2/WebProcess/WebPage/WebPage.messages.in
+++ b/WebKit2/WebProcess/WebPage/WebPage.messages.in
@@ -78,9 +78,9 @@ messages -> WebPage {
     ScaleWebView(double scale, WebCore::IntPoint origin)
 
     # Find.
-    FindString(WTF::String string, uint32_t findDirection, uint32_t findOptions, unsigned maxMatchCount)
+    FindString(WTF::String string, uint32_t findOptions, unsigned maxMatchCount)
     HideFindUI()
-    CountStringMatches(WTF::String string, bool caseInsensitive, unsigned maxMatchCount)
+    CountStringMatches(WTF::String string, uint32_t findOptions, unsigned maxMatchCount)
 
     # Popup menu.
     DidChangeSelectedIndexForActivePopupMenu(int32_t newIndex);
diff --git a/WebKit2/win/WebKit2.vcproj b/WebKit2/win/WebKit2.vcproj
index c69122f..42df544 100755
--- a/WebKit2/win/WebKit2.vcproj
+++ b/WebKit2/win/WebKit2.vcproj
@@ -552,6 +552,10 @@
 				>
 			</File>
 			<File
+				RelativePath="..\Shared\WebFindOptions.h"
+				>
+			</File>
+			<File
 				RelativePath="..\Shared\WebKeyboardEvent.cpp"
 				>
 			</File>
diff --git a/WebKitTools/ChangeLog b/WebKitTools/ChangeLog
index 77ea2af..92298e6 100644
--- a/WebKitTools/ChangeLog
+++ b/WebKitTools/ChangeLog
@@ -1,3 +1,19 @@
+2010-11-29  Dan Bernstein  <mitz at apple.com>
+
+        Reviewed by Darin Adler.
+
+        DumpRenderTree changes for testing the text search API.
+        https://bugs.webkit.org/show_bug.cgi?id=50038
+
+        * DumpRenderTree/LayoutTestController.cpp:
+        (findStringCallback):
+        (LayoutTestController::staticFunctions):
+        * DumpRenderTree/LayoutTestController.h:
+        * DumpRenderTree/mac/LayoutTestControllerMac.mm:
+        (LayoutTestController::findString):
+        * MiniBrowser/mac/BrowserWindowController.m:
+        (-[BrowserWindowController find:]):
+
 2010-11-29  Johnny Ding  <jnd at chromium.org>
 
         Unreviewed: Add myself to the list of Committers.
diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/LayoutTestController.cpp
index 5623c5e..a22579a 100644
--- a/WebKitTools/DumpRenderTree/LayoutTestController.cpp
+++ b/WebKitTools/DumpRenderTree/LayoutTestController.cpp
@@ -442,6 +442,22 @@ static JSValueRef execCommandCallback(JSContextRef context, JSObjectRef function
     return JSValueMakeUndefined(context);
 }
 
+static JSValueRef findStringCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    // Has Mac implementation.
+    if (argumentCount < 2)
+        return JSValueMakeUndefined(context);
+
+    JSRetainPtr<JSStringRef> target(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+    ASSERT(!*exception);
+
+    JSObjectRef options = JSValueToObject(context, arguments[1], exception);
+    ASSERT(!*exception);
+
+    LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+    return JSValueMakeBoolean(context, controller->findString(context, target.get(), options));
+}
+
 static JSValueRef counterValueForElementByIdCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
     if (argumentCount < 1)
@@ -1916,6 +1932,7 @@ JSStaticFunction* LayoutTestController::staticFunctions()
         { "evaluateInWebInspector", evaluateInWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "evaluateScriptInIsolatedWorld", evaluateScriptInIsolatedWorldCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "execCommand", execCommandCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "findString", findStringCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "counterValueForElementById", counterValueForElementByIdCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "grantDesktopNotificationPermission", grantDesktopNotificationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 
         { "hasSpellingMarker", hasSpellingMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.h b/WebKitTools/DumpRenderTree/LayoutTestController.h
index 391f7f5..2e9c856 100644
--- a/WebKitTools/DumpRenderTree/LayoutTestController.h
+++ b/WebKitTools/DumpRenderTree/LayoutTestController.h
@@ -52,11 +52,12 @@ public:
     bool callShouldCloseOnWebView();
     JSStringRef copyDecodedHostName(JSStringRef name);
     JSStringRef copyEncodedHostName(JSStringRef name);
+    JSRetainPtr<JSStringRef> counterValueForElementById(JSStringRef id);
     void disableImageLoading();
     void dispatchPendingLoadRequests();
     void display();
     void execCommand(JSStringRef name, JSStringRef value);
-    JSRetainPtr<JSStringRef> counterValueForElementById(JSStringRef id);
+    bool findString(JSContextRef, JSStringRef, JSObjectRef optionsArray);
     bool isCommandEnabled(JSStringRef name);
     void keepWebHistory();
     JSValueRef computedStyleIncludingVisitedInfo(JSContextRef, JSValueRef);
diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
index e3f2141..be3eb09 100644
--- a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
+++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm
@@ -583,6 +583,42 @@ void LayoutTestController::execCommand(JSStringRef name, JSStringRef value)
     [[mainFrame webView] _executeCoreCommandByName:nameNS value:valueNS];
 }
 
+bool LayoutTestController::findString(JSContextRef context, JSStringRef target, JSObjectRef optionsArray)
+{
+    WebFindOptions options = 0;
+
+    JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
+    JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
+    if (!JSValueIsNumber(context, lengthValue))
+        return false;
+
+    RetainPtr<CFStringRef> targetCFString(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, target));
+
+    size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
+    for (size_t i = 0; i < length; ++i) {
+        JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
+        if (!JSValueIsString(context, value))
+            continue;
+
+        JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
+
+        if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
+            options |= WebFindOptionsCaseInsensitive;
+        else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
+            options |= WebFindOptionsAtWordStarts;
+        else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
+            options |= WebFindOptionsTreatMedialCapitalAsWordStart;
+        else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
+            options |= WebFindOptionsBackwards;
+        else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
+            options |= WebFindOptionsWrapAround;
+        else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection"))
+            options |= WebFindOptionsStartInSelection;
+    }
+
+    return [[mainFrame webView] findString:(NSString *)targetCFString.get() options:options];
+}
+
 void LayoutTestController::setCacheModel(int cacheModel)
 {
     [[WebPreferences standardPreferences] setCacheModel:cacheModel];
diff --git a/WebKitTools/MiniBrowser/mac/BrowserWindowController.m b/WebKitTools/MiniBrowser/mac/BrowserWindowController.m
index 4f2b63f..ff6246e 100644
--- a/WebKitTools/MiniBrowser/mac/BrowserWindowController.m
+++ b/WebKitTools/MiniBrowser/mac/BrowserWindowController.m
@@ -690,8 +690,7 @@ static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKF
 {
     WKStringRef string = WKStringCreateWithCFString((CFStringRef)[sender stringValue]);
 
-    WKPageFindString(_webView.pageRef, string, kWKFindDirectionForward, 
-                     kWKFindOptionsCaseInsensitive | kWKFindOptionsWrapAround | kWKFindOptionsShowFindIndicator | kWKFindOptionsShowOverlay, 100);
+    WKPageFindString(_webView.pageRef, string, kWKFindOptionsCaseInsensitive | kWKFindOptionsWrapAround | kWKFindOptionsShowFindIndicator | kWKFindOptionsShowOverlay, 100);
 }
 
 @end

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list