<rdar://problem/8970549> WebFindOptionsAtWordStarts fails with Japanese words
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 24 Apr 2011 03:56:07 +0000 (03:56 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 24 Apr 2011 03:56:07 +0000 (03:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=59288

Reviewed by Simon Fraser.

Source/WebCore:

* platform/text/mac/TextBoundaries.mm:
(WebCore::wordStringTokenizer): Added this helper function.
(WebCore::findNextWordFromIndex): Changed to use a CFStringTokenizer with kCFStringTokenizerUnitWord,
whose behavior is not locale-dependent.

LayoutTests:

* editing/text-iterator/findString-expected.txt:
* editing/text-iterator/findString.html:

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

LayoutTests/ChangeLog
LayoutTests/editing/text-iterator/findString-expected.txt
LayoutTests/editing/text-iterator/findString.html
Source/WebCore/ChangeLog
Source/WebCore/platform/text/mac/TextBoundaries.mm

index 1b86624..f44ef25 100644 (file)
@@ -1,3 +1,13 @@
+2011-04-23  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Simon Fraser.
+
+        <rdar://problem/8970549> WebFindOptionsAtWordStarts fails with Japanese words
+        https://bugs.webkit.org/show_bug.cgi?id=59288
+
+        * editing/text-iterator/findString-expected.txt:
+        * editing/text-iterator/findString.html:
+
 2011-04-23  Dominic Cooney  <dominicc@chromium.org>
 
         Reviewed by Dimitri Glazkov.
index ac9482f..f90c199 100644 (file)
@@ -49,6 +49,7 @@ 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 a match at 7,10 as expected.
 PASS: Got no match as expected.
 
 Searching for ‘.org’ in ‘webkit.org’ with options [AtWordStarts]:
@@ -102,6 +103,16 @@ PASS: Got no match as expected.
 Searching for ‘P64’ in ‘LP64’ with options [AtWordStarts, TreatMedialCapitalAsWordStart]:
 PASS: Got no match as expected.
 
+Searching for ‘動戦士’ in ‘起動戦士’ with options [AtWordStarts]:
+PASS: Got no match as expected.
+
+Searching for ‘戦士’ in ‘起動戦士’ with options [AtWordStarts]:
+PASS: Got a match at 2,4 as expected.
+PASS: Got no match as expected.
+
+Searching for ‘士’ in ‘起動戦士’ with options [AtWordStarts]:
+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.
index a147d26..cba5ade 100644 (file)
@@ -50,7 +50,7 @@
     testFindString("cocoa", "co", [], [[0, 2], [2, 4], []]);
     testFindString("cocoa", "co", ["AtWordStarts"], [[0, 2], []]);
 
-    testFindString("webkit.org", "org", ["AtWordStarts"], [[]]);
+    testFindString("webkit.org", "org", ["AtWordStarts"], [[7, 10], []]);
     testFindString("webkit.org", ".org", ["AtWordStarts"], [[6, 10], []]);
 
     testFindString("webkit.org", "rg", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
     testFindString("LP64", "64", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[2, 4], []]);
     testFindString("LP64", "P64", ["AtWordStarts", "TreatMedialCapitalAsWordStart"], [[]]);
 
+    testFindString("\u8d77\u52d5\u6226\u58eb", "\u52d5\u6226\u58eb", ["AtWordStarts"], [[]]);
+    testFindString("\u8d77\u52d5\u6226\u58eb", "\u6226\u58eb", ["AtWordStarts"], [[2, 4], []]);
+    testFindString("\u8d77\u52d5\u6226\u58eb", "\u58eb", ["AtWordStarts"], [[]]);
+
     const searchBufferSize = 8192;
     const searchBufferOverlapSize = searchBufferSize / 4;
     const searchBufferUnoverlappedSize = searchBufferSize - searchBufferOverlapSize;
index a800848..74e70e7 100644 (file)
@@ -1,3 +1,15 @@
+2011-04-23  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Simon Fraser.
+
+        <rdar://problem/8970549> WebFindOptionsAtWordStarts fails with Japanese words
+        https://bugs.webkit.org/show_bug.cgi?id=59288
+
+        * platform/text/mac/TextBoundaries.mm:
+        (WebCore::wordStringTokenizer): Added this helper function.
+        (WebCore::findNextWordFromIndex): Changed to use a CFStringTokenizer with kCFStringTokenizerUnitWord,
+        whose behavior is not locale-dependent.
+
 2011-04-23  Dominic Cooney  <dominicc@chromium.org>
 
         Reviewed by Dimitri Glazkov.
index bd7ddf8..cca981d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004, 2006, 2011 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *    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 COMPUTER, INC. ``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 COMPUTER, INC. OR
- * 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. 
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #import "config.h"
 #import "TextBoundaries.h"
 
-using namespace WTF::Unicode;
+#import <wtf/RetainPtr.h>
 
 namespace WebCore {
 
@@ -42,15 +42,37 @@ void findWordBoundary(const UChar* chars, int len, int position, int* start, int
     *end = range.location + range.length;
 }
 
+static CFStringTokenizerRef wordStringTokenizer(CFStringRef string)
+{
+    static CFStringTokenizerRef stringTokenizer;
+    if (stringTokenizer)
+        CFStringTokenizerSetString(stringTokenizer, string, CFRangeMake(0, CFStringGetLength(string)));
+    else
+        stringTokenizer = CFStringTokenizerCreate(kCFAllocatorDefault, string, CFRangeMake(0, CFStringGetLength(string)), kCFStringTokenizerUnitWord, 0);
+
+    return stringTokenizer;
+}
+
 int findNextWordFromIndex(const UChar* chars, int len, int position, bool forward)
-{   
-    NSString* string = [[NSString alloc] initWithCharactersNoCopy:const_cast<unichar*>(chars)
-        length:len freeWhenDone:NO];
-    NSAttributedString* attr = [[NSAttributedString alloc] initWithString:string];
-    int result = [attr nextWordFromIndex:position forward:forward];
-    [attr release];
-    [string release];
-    return result;
+{
+    RetainPtr<CFStringRef> string(AdoptCF, CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, chars, len, kCFAllocatorNull));
+    CFStringTokenizerRef stringTokenizer = wordStringTokenizer(string.get());
+
+    CFIndex lastTokenStart = 0;
+    while (true) {
+        CFStringTokenizerTokenType tokenType = CFStringTokenizerAdvanceToNextToken(stringTokenizer);
+        if (tokenType == kCFStringTokenizerTokenNone)
+            return forward ? len : lastTokenStart;
+
+        CFRange tokenRange = CFStringTokenizerGetCurrentTokenRange(stringTokenizer);
+
+        if (!forward && tokenRange.location >= position)
+            return lastTokenStart;
+        if (forward && tokenRange.location + tokenRange.length > position)
+            return tokenRange.location + tokenRange.length;
+
+        lastTokenStart = tokenRange.location;
+    }
 }
 
 }