Support new emoji group candidates
[WebKit-https.git] / Source / WTF / wtf / text / TextBreakIterator.cpp
1 /*
2  * (C) 1999 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2015 Apple Inc. All rights reserved.
4  * Copyright (C) 2007-2009 Torch Mobile, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23 #include "TextBreakIterator.h"
24
25 #include "LineBreakIteratorPoolICU.h"
26 #include "UTextProviderLatin1.h"
27 #include "UTextProviderUTF16.h"
28 #include <mutex>
29 #include <wtf/Atomics.h>
30 #include <wtf/text/StringView.h>
31
32 // FIXME: This needs a better name
33 #define ADDITIONAL_EMOJI_SUPPORT (PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100))
34
35 namespace WTF {
36
37 // Iterator initialization
38
39 static TextBreakIterator* initializeIterator(UBreakIteratorType type, const char* locale = currentTextBreakLocaleID())
40 {
41     UErrorCode openStatus = U_ZERO_ERROR;
42     TextBreakIterator* iterator = reinterpret_cast<TextBreakIterator*>(ubrk_open(type, locale, 0, 0, &openStatus));
43     ASSERT_WITH_MESSAGE(U_SUCCESS(openStatus), "ICU could not open a break iterator: %s (%d)", u_errorName(openStatus), openStatus);
44     return iterator;
45 }
46
47 #if !PLATFORM(IOS)
48
49 static TextBreakIterator* initializeIteratorWithRules(const char* breakRules)
50 {
51     UParseError parseStatus;
52     UErrorCode openStatus = U_ZERO_ERROR;
53     unsigned length = strlen(breakRules);
54     auto upconvertedCharacters = StringView(reinterpret_cast<const LChar*>(breakRules), length).upconvertedCharacters();
55     TextBreakIterator* iterator = reinterpret_cast<TextBreakIterator*>(ubrk_openRules(upconvertedCharacters, length, 0, 0, &parseStatus, &openStatus));
56     ASSERT_WITH_MESSAGE(U_SUCCESS(openStatus), "ICU could not open a break iterator: %s (%d)", u_errorName(openStatus), openStatus);
57     return iterator;
58 }
59
60 #endif
61
62
63 // Iterator text setting
64
65 static TextBreakIterator* setTextForIterator(TextBreakIterator& iterator, StringView string)
66 {
67     if (string.is8Bit()) {
68         UTextWithBuffer textLocal;
69         textLocal.text = UTEXT_INITIALIZER;
70         textLocal.text.extraSize = sizeof(textLocal.buffer);
71         textLocal.text.pExtra = textLocal.buffer;
72
73         UErrorCode openStatus = U_ZERO_ERROR;
74         UText* text = openLatin1UTextProvider(&textLocal, string.characters8(), string.length(), &openStatus);
75         if (U_FAILURE(openStatus)) {
76             LOG_ERROR("uTextOpenLatin1 failed with status %d", openStatus);
77             return nullptr;
78         }
79
80         UErrorCode setTextStatus = U_ZERO_ERROR;
81         ubrk_setUText(reinterpret_cast<UBreakIterator*>(&iterator), text, &setTextStatus);
82         if (U_FAILURE(setTextStatus)) {
83             LOG_ERROR("ubrk_setUText failed with status %d", setTextStatus);
84             return nullptr;
85         }
86
87         utext_close(text);
88     } else {
89         UErrorCode setTextStatus = U_ZERO_ERROR;
90         ubrk_setText(reinterpret_cast<UBreakIterator*>(&iterator), string.characters16(), string.length(), &setTextStatus);
91         if (U_FAILURE(setTextStatus))
92             return nullptr;
93     }
94
95     return &iterator;
96 }
97
98 static TextBreakIterator* setContextAwareTextForIterator(TextBreakIterator& iterator, StringView string, const UChar* priorContext, unsigned priorContextLength)
99 {
100     if (string.is8Bit()) {
101         UTextWithBuffer textLocal;
102         textLocal.text = UTEXT_INITIALIZER;
103         textLocal.text.extraSize = sizeof(textLocal.buffer);
104         textLocal.text.pExtra = textLocal.buffer;
105
106         UErrorCode openStatus = U_ZERO_ERROR;
107         UText* text = openLatin1ContextAwareUTextProvider(&textLocal, string.characters8(), string.length(), priorContext, priorContextLength, &openStatus);
108         if (U_FAILURE(openStatus)) {
109             LOG_ERROR("openLatin1ContextAwareUTextProvider failed with status %d", openStatus);
110             return nullptr;
111         }
112
113         UErrorCode setTextStatus = U_ZERO_ERROR;
114         ubrk_setUText(reinterpret_cast<UBreakIterator*>(&iterator), text, &setTextStatus);
115         if (U_FAILURE(setTextStatus)) {
116             LOG_ERROR("ubrk_setUText failed with status %d", setTextStatus);
117             return nullptr;
118         }
119
120         utext_close(text);
121     } else {
122         UText textLocal = UTEXT_INITIALIZER;
123
124         UErrorCode openStatus = U_ZERO_ERROR;
125         UText* text = openUTF16ContextAwareUTextProvider(&textLocal, string.characters16(), string.length(), priorContext, priorContextLength, &openStatus);
126         if (U_FAILURE(openStatus)) {
127             LOG_ERROR("openUTF16ContextAwareUTextProvider failed with status %d", openStatus);
128             return 0;
129         }
130
131         UErrorCode setTextStatus = U_ZERO_ERROR;
132         ubrk_setUText(reinterpret_cast<UBreakIterator*>(&iterator), text, &setTextStatus);
133         if (U_FAILURE(setTextStatus)) {
134             LOG_ERROR("ubrk_setUText failed with status %d", setTextStatus);
135             return nullptr;
136         }
137
138         utext_close(text);
139     }
140
141     return &iterator;
142 }
143
144
145 // Static iterators
146
147 TextBreakIterator* wordBreakIterator(StringView string)
148 {
149     static TextBreakIterator* staticWordBreakIterator = initializeIterator(UBRK_WORD);
150     if (!staticWordBreakIterator)
151         return nullptr;
152
153     return setTextForIterator(*staticWordBreakIterator, string);
154 }
155
156 TextBreakIterator* sentenceBreakIterator(StringView string)
157 {
158     static TextBreakIterator* staticSentenceBreakIterator = initializeIterator(UBRK_SENTENCE);
159     if (!staticSentenceBreakIterator)
160         return nullptr;
161
162     return setTextForIterator(*staticSentenceBreakIterator, string);
163 }
164
165 TextBreakIterator* cursorMovementIterator(StringView string)
166 {
167     // FIXME: These rules need to be updated for additional gender-based emoji support.
168 #if !PLATFORM(IOS)
169     // This rule set is based on character-break iterator rules of ICU 4.0
170     // <http://source.icu-project.org/repos/icu/icu/tags/release-4-0/source/data/brkitr/char.txt>.
171     // The major differences from the original ones are listed below:
172     // * Replaced '[\p{Grapheme_Cluster_Break = SpacingMark}]' with '[\p{General_Category = Spacing Mark} - $Extend]' for ICU 3.8 or earlier;
173     // * Removed rules that prevent a cursor from moving after prepend characters (Bug 24342);
174     // * Added rules that prevent a cursor from moving after virama signs of Indic languages except Tamil (Bug 15790), and;
175     // * Added rules that prevent a cursor from moving before Japanese half-width katakara voiced marks.
176     // * Added rules for regional indicator symbols.
177     static const char* kRules =
178         "$CR      = [\\p{Grapheme_Cluster_Break = CR}];"
179         "$LF      = [\\p{Grapheme_Cluster_Break = LF}];"
180         "$Control = [\\p{Grapheme_Cluster_Break = Control}];"
181         "$VoiceMarks = [\\uFF9E\\uFF9F];" // Japanese half-width katakana voiced marks
182         "$Extend  = [\\p{Grapheme_Cluster_Break = Extend} $VoiceMarks - [\\u0E30 \\u0E32 \\u0E45 \\u0EB0 \\u0EB2]];"
183         "$SpacingMark = [[\\p{General_Category = Spacing Mark}] - $Extend];"
184         "$L       = [\\p{Grapheme_Cluster_Break = L}];"
185         "$V       = [\\p{Grapheme_Cluster_Break = V}];"
186         "$T       = [\\p{Grapheme_Cluster_Break = T}];"
187         "$LV      = [\\p{Grapheme_Cluster_Break = LV}];"
188         "$LVT     = [\\p{Grapheme_Cluster_Break = LVT}];"
189         "$Hin0    = [\\u0905-\\u0939];" // Devanagari Letter A,...,Ha
190         "$HinV    = \\u094D;" // Devanagari Sign Virama
191         "$Hin1    = [\\u0915-\\u0939];" // Devanagari Letter Ka,...,Ha
192         "$Ben0    = [\\u0985-\\u09B9];" // Bengali Letter A,...,Ha
193         "$BenV    = \\u09CD;" // Bengali Sign Virama
194         "$Ben1    = [\\u0995-\\u09B9];" // Bengali Letter Ka,...,Ha
195         "$Pan0    = [\\u0A05-\\u0A39];" // Gurmukhi Letter A,...,Ha
196         "$PanV    = \\u0A4D;" // Gurmukhi Sign Virama
197         "$Pan1    = [\\u0A15-\\u0A39];" // Gurmukhi Letter Ka,...,Ha
198         "$Guj0    = [\\u0A85-\\u0AB9];" // Gujarati Letter A,...,Ha
199         "$GujV    = \\u0ACD;" // Gujarati Sign Virama
200         "$Guj1    = [\\u0A95-\\u0AB9];" // Gujarati Letter Ka,...,Ha
201         "$Ori0    = [\\u0B05-\\u0B39];" // Oriya Letter A,...,Ha
202         "$OriV    = \\u0B4D;" // Oriya Sign Virama
203         "$Ori1    = [\\u0B15-\\u0B39];" // Oriya Letter Ka,...,Ha
204         "$Tel0    = [\\u0C05-\\u0C39];" // Telugu Letter A,...,Ha
205         "$TelV    = \\u0C4D;" // Telugu Sign Virama
206         "$Tel1    = [\\u0C14-\\u0C39];" // Telugu Letter Ka,...,Ha
207         "$Kan0    = [\\u0C85-\\u0CB9];" // Kannada Letter A,...,Ha
208         "$KanV    = \\u0CCD;" // Kannada Sign Virama
209         "$Kan1    = [\\u0C95-\\u0CB9];" // Kannada Letter A,...,Ha
210         "$Mal0    = [\\u0D05-\\u0D39];" // Malayalam Letter A,...,Ha
211         "$MalV    = \\u0D4D;" // Malayalam Sign Virama
212         "$Mal1    = [\\u0D15-\\u0D39];" // Malayalam Letter A,...,Ha
213         "$RI      = [\\U0001F1E6-\\U0001F1FF];" // Emoji regional indicators
214         "$ZWJ     = \\u200D;" // Zero width joiner
215         "$EmojiVar = [\\uFE0F];" // Emoji-style variation selector
216 #if ADDITIONAL_EMOJI_SUPPORT
217         "$EmojiForSeqs = [\\u2764 \\U0001F441 \\U0001F466-\\U0001F469 \\U0001F48B \\U0001F5E8];" // Emoji that participate in ZWJ sequences
218         "$EmojiForMods = [\\u261D \\u26F9 \\u270A-\\u270D \\U0001F385 \\U0001F3C3-\\U0001F3C4 \\U0001F3CA \\U0001F3CB \\U0001F442-\\U0001F443 \\U0001F446-\\U0001F450 \\U0001F466-\\U0001F469 \\U0001F46E-\\U0001F478 \\U0001F47C \\U0001F481-\\U0001F483 \\U0001F485-\\U0001F487 \\U0001F4AA \\U0001F575 \\U0001F590 \\U0001F595 \\U0001F596 \\U0001F645-\\U0001F647 \\U0001F64B-\\U0001F64F \\U0001F6A3 \\U0001F6B4-\\U0001F6B6 \\U0001F6C0 \\U0001F918] ;" // Emoji that take Fitzpatrick modifiers
219 #else
220         "$EmojiForSeqs = [\\u2764 \\U0001F466-\\U0001F469 \\U0001F48B];" // Emoji that participate in ZWJ sequences
221         "$EmojiForMods = [\\u261D \\u270A-\\u270C \\U0001F385 \\U0001F3C3-\\U0001F3C4 \\U0001F3C7 \\U0001F3CA \\U0001F442-\\U0001F443 \\U0001F446-\\U0001F450 \\U0001F466-\\U0001F469 \\U0001F46E-\\U0001F478 \\U0001F47C \\U0001F481-\\U0001F483 \\U0001F485-\\U0001F487 \\U0001F4AA \\U0001F596 \\U0001F645-\\U0001F647 \\U0001F64B-\\U0001F64F \\U0001F6A3 \\U0001F6B4-\\U0001F6B6 \\U0001F6C0] ;" // Emoji that take Fitzpatrick modifiers
222 #endif
223         "$EmojiMods = [\\U0001F3FB-\\U0001F3FF];" // Fitzpatrick modifiers
224         "!!chain;"
225 #if ADDITIONAL_EMOJI_SUPPORT
226         "!!RINoChain;"
227 #endif
228         "!!forward;"
229         "$CR $LF;"
230         "$L ($L | $V | $LV | $LVT);"
231         "($LV | $V) ($V | $T);"
232         "($LVT | $T) $T;"
233 #if ADDITIONAL_EMOJI_SUPPORT
234         "$RI $RI $Extend* / $RI;"
235         "$RI $RI $Extend*;"
236         "[^$Control $CR $LF] $Extend;"
237         "[^$Control $CR $LF] $SpacingMark;"
238 #else
239         "[^$Control $CR $LF] $Extend;"
240         "[^$Control $CR $LF] $SpacingMark;"
241         "$RI $RI / $RI;"
242         "$RI $RI;"
243 #endif
244         "$Hin0 $HinV $Hin1;" // Devanagari Virama (forward)
245         "$Ben0 $BenV $Ben1;" // Bengali Virama (forward)
246         "$Pan0 $PanV $Pan1;" // Gurmukhi Virama (forward)
247         "$Guj0 $GujV $Guj1;" // Gujarati Virama (forward)
248         "$Ori0 $OriV $Ori1;" // Oriya Virama (forward)
249         "$Tel0 $TelV $Tel1;" // Telugu Virama (forward)
250         "$Kan0 $KanV $Kan1;" // Kannada Virama (forward)
251         "$Mal0 $MalV $Mal1;" // Malayalam Virama (forward)
252         "$ZWJ $EmojiForSeqs;" // Don't break in emoji ZWJ sequences
253         "$EmojiForMods $EmojiVar? $EmojiMods;" // Don't break between relevant emoji (possibly with variation selector) and Fitzpatrick modifier
254         "!!reverse;"
255         "$LF $CR;"
256         "($L | $V | $LV | $LVT) $L;"
257         "($V | $T) ($LV | $V);"
258         "$T ($LVT | $T);"
259 #if ADDITIONAL_EMOJI_SUPPORT
260         "$Extend* $RI $RI / $Extend* $RI $RI;"
261         "$Extend* $RI $RI;"
262         "$Extend      [^$Control $CR $LF];"
263         "$SpacingMark [^$Control $CR $LF];"
264 #else
265         "$Extend      [^$Control $CR $LF];"
266         "$SpacingMark [^$Control $CR $LF];"
267         "$RI $RI / $RI $RI;"
268         "$RI $RI;"
269 #endif
270         "$Hin1 $HinV $Hin0;" // Devanagari Virama (backward)
271         "$Ben1 $BenV $Ben0;" // Bengali Virama (backward)
272         "$Pan1 $PanV $Pan0;" // Gurmukhi Virama (backward)
273         "$Guj1 $GujV $Guj0;" // Gujarati Virama (backward)
274         "$Ori1 $OriV $Ori0;" // Gujarati Virama (backward)
275         "$Tel1 $TelV $Tel0;" // Telugu Virama (backward)
276         "$Kan1 $KanV $Kan0;" // Kannada Virama (backward)
277         "$Mal1 $MalV $Mal0;" // Malayalam Virama (backward)
278         "$EmojiForSeqs $ZWJ;" // Don't break in emoji ZWJ sequences
279         "$EmojiMods $EmojiVar? $EmojiForMods;" // Don't break between relevant emoji (possibly with variation selector) and Fitzpatrick modifier
280 #if ADDITIONAL_EMOJI_SUPPORT
281         "!!safe_reverse;"
282         "$RI $RI+;"
283         "[$EmojiVar $EmojiMods]+ $EmojiForMods;"
284         "!!safe_forward;"
285         "$RI $RI+;"
286         "$EmojiForMods [$EmojiVar $EmojiMods]+;";
287 #else
288         "[$EmojiVar $EmojiMods]+ $EmojiForMods;"
289         "$EmojiForMods [$EmojiVar $EmojiMods]+;"
290         "!!safe_reverse;"
291         "!!safe_forward;";
292 #endif
293     static TextBreakIterator* staticCursorMovementIterator = initializeIteratorWithRules(kRules);
294 #else // PLATFORM(IOS)
295     // Use the special Thai character break iterator for all locales
296     static TextBreakIterator* staticCursorMovementIterator = initializeIterator(UBRK_CHARACTER, "th");
297 #endif // !PLATFORM(IOS)
298
299     if (!staticCursorMovementIterator)
300         return nullptr;
301
302     return setTextForIterator(*staticCursorMovementIterator, string);
303 }
304
305 TextBreakIterator* acquireLineBreakIterator(StringView string, const AtomicString& locale, const UChar* priorContext, unsigned priorContextLength, LineBreakIteratorMode mode, bool isCJK)
306 {
307     TextBreakIterator* iterator = LineBreakIteratorPool::sharedPool().take(locale, mode, isCJK);
308     if (!iterator)
309         return nullptr;
310
311     return setContextAwareTextForIterator(*iterator, string, priorContext, priorContextLength);
312 }
313
314 void releaseLineBreakIterator(TextBreakIterator* iterator)
315 {
316     ASSERT_ARG(iterator, iterator);
317
318     LineBreakIteratorPool::sharedPool().put(iterator);
319 }
320
321 static const char* uax14Prologue =
322     "!!chain;"
323     "!!LBCMNoChain;"
324     "!!lookAheadHardBreak;";
325
326 static const char* uax14AssignmentsBefore =
327     // explicitly enumerate $CJ since ICU versions prior to 49 don't support :LineBreak=Conditional_Japanese_Starter:
328     "$CJ = ["
329 #if (U_ICU_VERSION_MAJOR_NUM >= 4) && (U_ICU_VERSION_MINOR_NUM >= 9)
330     ":LineBreak=Conditional_Japanese_Starter:"
331 #else
332     "\\u3041\\u3043\\u3045\\u3047\\u3049\\u3063\\u3083\\u3085\\u3087\\u308E\\u3095\\u3096\\u30A1\\u30A3\\u30A5\\u30A7"
333     "\\u30A9\\u30C3\\u30E3\\u30E5\\u30E7\\u30EE\\u30F5\\u30F6\\u30FC"
334     "\\u31F0\\u31F1\\u31F2\\u31F3\\u31F4\\u31F5\\u31F6\\u31F7\\u31F8\\u31F9\\u31FA\\u31FB\\u31FC\\u31FD\\u31FE\\u31FF"
335     "\\uFF67\\uFF68\\uFF69\\uFF6A\\uFF6B\\uFF6C\\uFF6D\\uFF6E\\uFF6F\\uFF70"
336 #endif
337     "];";
338
339 static const char* uax14AssignmentsCustomLooseCJK =
340     "$BA_SUB = [\\u2010\\u2013];"
341     "$EX_SUB = [\\u0021\\u003F\\uFF01\\uFF1F];"
342     "$ID_SUB = '';"
343     "$IN_SUB = [\\u2025\\u2026];"
344     "$IS_SUB = [\\u003A\\u003B];"
345     "$NS_SUB = [\\u203C\\u2047\\u2048\\u2049\\u3005\\u301C\\u303B\\u309D\\u309E\\u30A0\\u30FB\\u30FD\\u30FE\\uFF1A\\uFF1B\\uFF65];"
346     "$PO_SUB = [\\u0025\\u00A2\\u00B0\\u2030\\u2032\\u2033\\u2103\\uFF05\\uFFE0];"
347     "$PR_SUB = [\\u0024\\u00A3\\u00A5\\u20AC\\u2116\\uFF04\\uFFE1\\uFFE5];"
348     "$ID_ADD = [$CJ $BA_SUB $EX_SUB $IN_SUB $IS_SUB $NS_SUB $PO_SUB $PR_SUB];"
349     "$NS_ADD = '';";
350
351 static const char* uax14AssignmentsCustomLooseNonCJK =
352     "$BA_SUB = '';"
353     "$EX_SUB = '';"
354     "$ID_SUB = '';"
355     "$IN_SUB = [\\u2025\\u2026];"
356     "$IS_SUB = '';"
357     "$NS_SUB = [\\u3005\\u303B\\u309D\\u309E\\u30FD\\u30FE];"
358     "$PO_SUB = '';"
359     "$PR_SUB = '';"
360     "$ID_ADD = [$CJ $IN_SUB $NS_SUB];"
361     "$NS_ADD = '';";
362
363 static const char* uax14AssignmentsCustomNormalCJK =
364     "$BA_SUB = [\\u2010\\u2013];"
365     "$EX_SUB = '';"
366     "$IN_SUB = '';"
367     "$ID_SUB = '';"
368     "$IS_SUB = '';"
369     "$NS_SUB = [\\u301C\\u30A0];"
370     "$PO_SUB = '';"
371     "$PR_SUB = '';"
372     "$ID_ADD = [$CJ $BA_SUB $NS_SUB];"
373     "$NS_ADD = '';";
374
375 static const char* uax14AssignmentsCustomNormalNonCJK =
376     "$BA_SUB = '';"
377     "$EX_SUB = '';"
378     "$ID_SUB = '';"
379     "$IN_SUB = '';"
380     "$IS_SUB = '';"
381     "$NS_SUB = '';"
382     "$PO_SUB = '';"
383     "$PR_SUB = '';"
384     "$ID_ADD = [$CJ];"
385     "$NS_ADD = '';";
386
387 static const char* uax14AssignmentsCustomStrictCJK =
388     "$BA_SUB = '';"
389     "$EX_SUB = '';"
390     "$ID_SUB = '';"
391     "$IN_SUB = '';"
392     "$IS_SUB = '';"
393     "$NS_SUB = '';"
394     "$PO_SUB = '';"
395     "$PR_SUB = '';"
396     "$ID_ADD = '';"
397     "$NS_ADD = [$CJ];";
398
399 #define uax14AssignmentsCustomStrictNonCJK      uax14AssignmentsCustomStrictCJK
400 #define uax14AssignmentsCustomDefaultCJK        uax14AssignmentsCustomNormalCJK
401 #define uax14AssignmentsCustomDefaultNonCJK     uax14AssignmentsCustomStrictNonCJK
402
403 static const char* uax14AssignmentsAfter =
404     "$AI = [:LineBreak = Ambiguous:];"
405     "$AL = [:LineBreak = Alphabetic:];"
406     "$BA = [[:LineBreak = Break_After:] - $BA_SUB];"
407     "$BB = [:LineBreak = Break_Before:];"
408     "$BK = [:LineBreak = Mandatory_Break:];"
409     "$B2 = [:LineBreak = Break_Both:];"
410     "$CB = [:LineBreak = Contingent_Break:];"
411     "$CL = [:LineBreak = Close_Punctuation:];"
412     "$CM = [:LineBreak = Combining_Mark:];"
413     "$CP = [:LineBreak = Close_Parenthesis:];"
414     "$CR = [:LineBreak = Carriage_Return:];"
415     "$EX = [[:LineBreak = Exclamation:] - $EX_SUB];"
416     "$GL = [:LineBreak = Glue:];"
417 #if (U_ICU_VERSION_MAJOR_NUM >= 4) && (U_ICU_VERSION_MINOR_NUM >= 9)
418     "$HL = [:LineBreak = Hebrew_Letter:];"
419 #else
420     "$HL = [[:Hebrew:] & [:Letter:]];"
421 #endif
422     "$HY = [:LineBreak = Hyphen:];"
423     "$H2 = [:LineBreak = H2:];"
424     "$H3 = [:LineBreak = H3:];"
425     "$ID = [[[[:LineBreak = Ideographic:] - $CJ] $ID_ADD] - $ID_SUB];"
426     "$IN = [[:LineBreak = Inseparable:] - $IN_SUB];"
427     "$IS = [[:LineBreak = Infix_Numeric:] - $IS_SUB];"
428     "$JL = [:LineBreak = JL:];"
429     "$JV = [:LineBreak = JV:];"
430     "$JT = [:LineBreak = JT:];"
431     "$LF = [:LineBreak = Line_Feed:];"
432     "$NL = [:LineBreak = Next_Line:];"
433     "$NS = [[[[:LineBreak = Nonstarter:] - $CJ] $NS_ADD] - $NS_SUB];"
434     "$NU = [:LineBreak = Numeric:];"
435     "$OP = [:LineBreak = Open_Punctuation:];"
436     "$PO = [[:LineBreak = Postfix_Numeric:] - $PO_SUB];"
437     "$PR = [[:LineBreak = Prefix_Numeric:] - $PR_SUB];"
438     "$QU = [:LineBreak = Quotation:];"
439     "$RI = [\\U0001F1E6-\\U0001F1FF];"
440     "$SA = [:LineBreak = Complex_Context:];"
441     "$SG = [:LineBreak = Surrogate:];"
442     "$SP = [:LineBreak = Space:];"
443     "$SY = [:LineBreak = Break_Symbols:];"
444     "$WJ = [:LineBreak = Word_Joiner:];"
445     "$XX = [:LineBreak = Unknown:];"
446     "$ZW = [:LineBreak = ZWSpace:];"
447     "$ZWJ = \\u200D;"
448     "$EmojiVar = \\uFE0F;"
449 #if ADDITIONAL_EMOJI_SUPPORT
450     "$EmojiForSeqs = [\\u2764 \\U0001F441 \\U0001F466-\\U0001F469 \\U0001F48B \\U0001F5E8];"
451     "$EmojiForMods = [\\u261D \\u26F9 \\u270A-\\u270D \\U0001F385 \\U0001F3C3-\\U0001F3C4 \\U0001F3CA \\U0001F3CB \\U0001F442-\\U0001F443 \\U0001F446-\\U0001F450 \\U0001F466-\\U0001F469 \\U0001F46E-\\U0001F478 \\U0001F47C \\U0001F481-\\U0001F483 \\U0001F485-\\U0001F487 \\U0001F4AA \\U0001F575 \\U0001F590 \\U0001F595 \\U0001F596 \\U0001F645-\\U0001F647 \\U0001F64B-\\U0001F64F \\U0001F6A3 \\U0001F6B4-\\U0001F6B6 \\U0001F6C0 \\U0001F918] ;" // Emoji that take Fitzpatrick modifiers
452 #else
453     "$EmojiForSeqs = [\\u2764 \\U0001F466-\\U0001F469 \\U0001F48B];"
454     "$EmojiForMods = [\\u261D \\u270A-\\u270C \\U0001F385 \\U0001F3C3-\\U0001F3C4 \\U0001F3C7 \\U0001F3CA \\U0001F442-\\U0001F443 \\U0001F446-\\U0001F450 \\U0001F466-\\U0001F469 \\U0001F46E-\\U0001F478 \\U0001F47C \\U0001F481-\\U0001F483 \\U0001F485-\\U0001F487 \\U0001F4AA \\U0001F596 \\U0001F645-\\U0001F647 \\U0001F64B-\\U0001F64F \\U0001F6A3 \\U0001F6B4-\\U0001F6B6 \\U0001F6C0] ;" // Emoji that take Fitzpatrick modifiers
455 #endif
456     "$EmojiMods = [\\U0001F3FB-\\U0001F3FF];"
457     "$dictionary = [:LineBreak = Complex_Context:];"
458     "$ALPlus = [$AL $AI $SA $SG $XX];"
459     "$ALcm = $ALPlus $CM*;"
460     "$BAcm = $BA $CM*;"
461     "$BBcm = $BB $CM*;"
462     "$B2cm = $B2 $CM*;"
463     "$CLcm = $CL $CM*;"
464     "$CPcm = $CP $CM*;"
465     "$EXcm = $EX $CM*;"
466     "$GLcm = $GL $CM*;"
467     "$HLcm = $HL $CM*;"
468     "$HYcm = $HY $CM*;"
469     "$H2cm = $H2 $CM*;"
470     "$H3cm = $H3 $CM*;"
471     "$IDcm = $ID $CM*;"
472     "$INcm = $IN $CM*;"
473     "$IScm = $IS $CM*;"
474     "$JLcm = $JL $CM*;"
475     "$JVcm = $JV $CM*;"
476     "$JTcm = $JT $CM*;"
477     "$NScm = $NS $CM*;"
478     "$NUcm = $NU $CM*;"
479     "$OPcm = $OP $CM*;"
480     "$POcm = $PO $CM*;"
481     "$PRcm = $PR $CM*;"
482     "$QUcm = $QU $CM*;"
483     "$RIcm = $QU $CM*;"
484     "$SYcm = $SY $CM*;"
485     "$WJcm = $WJ $CM*;";
486
487 static const char* uax14Forward =
488     "!!forward;"
489     "$CAN_CM = [^$SP $BK $CR $LF $NL $ZW $CM];"
490     "$CANT_CM = [$SP $BK $CR $LF $NL $ZW $CM];"
491     "$AL_FOLLOW_NOCM = [$BK $CR $LF $NL $ZW $SP];"
492     "$AL_FOLLOW_CM = [$CL $CP $EX $HL $IS $SY $WJ $GL $OP $QU $BA $HY $NS $IN $NU $ALPlus];"
493     "$AL_FOLLOW = [$AL_FOLLOW_NOCM $AL_FOLLOW_CM];"
494     "$LB4Breaks = [$BK $CR $LF $NL];"
495     "$LB4NonBreaks = [^$BK $CR $LF $NL];"
496     "$LB8Breaks = [$LB4Breaks $ZW];"
497     "$LB8NonBreaks = [[$LB4NonBreaks] - [$ZW]];"
498     "$LB18NonBreaks = [$LB8NonBreaks - [$SP]];"
499     "$LB18Breaks = [$LB8Breaks $SP];"
500     "$LB20NonBreaks = [$LB18NonBreaks - $CB];"
501     "$ALPlus $CM+;"
502     "$BA $CM+;"
503     "$BB $CM+;"
504     "$B2 $CM+;"
505     "$CL $CM+;"
506     "$CP $CM+;"
507     "$EX $CM+;"
508     "$GL $CM+;"
509     "$HL $CM+;"
510     "$HY $CM+;"
511     "$H2 $CM+;"
512     "$H3 $CM+;"
513     "$ID $CM+;"
514     "$IN $CM+;"
515     "$IS $CM+;"
516     "$JL $CM+;"
517     "$JV $CM+;"
518     "$JT $CM+;"
519     "$NS $CM+;"
520     "$NU $CM+;"
521     "$OP $CM+;"
522     "$PO $CM+;"
523     "$PR $CM+;"
524     "$QU $CM+;"
525     "$SY $CM+;"
526     "$WJ $CM+;"
527     "$CR $LF {100};"
528     "$LB4NonBreaks? $LB4Breaks {100};"
529     "$CAN_CM $CM* $LB4Breaks {100};"
530     "$CM+ $LB4Breaks {100};"
531     "$LB4NonBreaks [$SP $ZW];"
532     "$CAN_CM $CM* [$SP $ZW];"
533     "$CM+ [$SP $ZW];"
534     "$EmojiForSeqs $EmojiVar? $EmojiMods? $ZWJ $EmojiForSeqs;"
535     "$CAN_CM $CM+;"
536     "$CM+;"
537     "$CAN_CM $CM* $WJcm;"
538     "$LB8NonBreaks $WJcm;"
539     "$CM+ $WJcm;"
540     "$WJcm $CANT_CM;"
541     "$WJcm $CAN_CM $CM*;"
542     "$GLcm $CAN_CM $CM*;"
543     "$GLcm $CANT_CM;"
544     "[[$LB8NonBreaks] - [$SP $BA $HY]] $CM* $GLcm;"
545     "$CM+ GLcm;"
546     "$LB8NonBreaks $CL;"
547     "$CAN_CM $CM* $CL;"
548     "$CM+ $CL;"
549     "$LB8NonBreaks $CP;"
550     "$CAN_CM $CM* $CP;"
551     "$CM+ $CP;"
552     "$LB8NonBreaks $EX;"
553     "$CAN_CM $CM* $EX;"
554     "$CM+ $EX;"
555     "$LB8NonBreaks $IS;"
556     "$CAN_CM $CM* $IS;"
557     "$CM+ $IS;"
558     "$LB8NonBreaks $SY;"
559     "$CAN_CM $CM* $SY;"
560     "$CM+ $SY;"
561     "$OPcm $SP* $CAN_CM $CM*;"
562     "$OPcm $SP* $CANT_CM;"
563     "$OPcm $SP+ $CM+ $AL_FOLLOW?;"
564     "$QUcm $SP* $OPcm;"
565     "($CLcm | $CPcm) $SP* $NScm;"
566     "$B2cm $SP* $B2cm;"
567     "$LB18NonBreaks $CM* $QUcm;"
568     "$CM+ $QUcm;"
569     "$QUcm .?;"
570     "$QUcm $LB18NonBreaks $CM*;"
571     "$LB20NonBreaks $CM* ($BAcm | $HYcm | $NScm); "
572     "$BBcm [^$CB];"
573     "$BBcm $LB20NonBreaks $CM*;"
574     "$HLcm ($HYcm | $BAcm) [^$CB]?;"
575     "($ALcm | $HLcm) $INcm;"
576     "$CM+ $INcm;"
577     "$IDcm $INcm;"
578     "$INcm $INcm;"
579     "$NUcm $INcm;"
580     "$IDcm $POcm;"
581     "$ALcm $NUcm;"
582     "$HLcm $NUcm;"
583     "$CM+ $NUcm;"
584     "$NUcm $ALcm;"
585     "$NUcm $HLcm;"
586     "$PRcm $IDcm;"
587     "$PRcm ($ALcm | $HLcm);"
588     "$POcm ($ALcm | $HLcm);"
589     "($PRcm | $POcm)? ($OPcm | $HYcm)? $NUcm ($NUcm | $SYcm | $IScm)* ($CLcm | $CPcm)? ($PRcm | $POcm)?;"
590     "$JLcm ($JLcm | $JVcm | $H2cm | $H3cm);"
591     "($JVcm | $H2cm) ($JVcm | $JTcm);"
592     "($JTcm | $H3cm) $JTcm;"
593     "($JLcm | $JVcm | $JTcm | $H2cm | $H3cm) $INcm;"
594     "($JLcm | $JVcm | $JTcm | $H2cm | $H3cm) $POcm;"
595     "$PRcm ($JLcm | $JVcm | $JTcm | $H2cm | $H3cm);"
596     "($ALcm | $HLcm) ($ALcm | $HLcm);"
597     "$CM+ ($ALcm | $HLcm);"
598     "$IScm ($ALcm | $HLcm);"
599     "($ALcm | $HLcm | $NUcm) $OPcm;"
600     "$CM+ $OPcm;"
601     "$CPcm ($ALcm | $HLcm | $NUcm);"
602 #if ADDITIONAL_EMOJI_SUPPORT
603     "$RIcm $RIcm;"
604 #endif
605     "$EmojiForMods $EmojiVar? $EmojiMods;";
606
607 static const char* uax14Reverse =
608     "!!reverse;"
609     "$CM+ $ALPlus;"
610     "$CM+ $BA;"
611     "$CM+ $BB;"
612     "$CM+ $B2;"
613     "$CM+ $CL;"
614     "$CM+ $CP;"
615     "$CM+ $EX;"
616     "$CM+ $GL;"
617     "$CM+ $HL;"
618     "$CM+ $HY;"
619     "$CM+ $H2;"
620     "$CM+ $H3;"
621     "$CM+ $ID;"
622     "$CM+ $IN;"
623     "$CM+ $IS;"
624     "$CM+ $JL;"
625     "$CM+ $JV;"
626     "$CM+ $JT;"
627     "$CM+ $NS;"
628     "$CM+ $NU;"
629     "$CM+ $OP;"
630     "$CM+ $PO;"
631     "$CM+ $PR;"
632     "$CM+ $QU;"
633 #if ADDITIONAL_EMOJI_SUPPORT
634     "$CM+ $RI;"
635 #endif
636     "$CM+ $SY;"
637     "$CM+ $WJ;"
638     "$CM+;"
639     "$AL_FOLLOW $CM+ / ([$BK $CR $LF $NL $ZW {eof}] | $SP+ $CM+ $SP | $SP+ $CM* ([^$OP $CM $SP] | [$AL {eof}]));"
640     "[$PR] / $CM+ [$BK $CR $LF $NL $ZW $SP {eof}];"
641     "$LB4Breaks [$LB4NonBreaks-$CM];"
642     "$LB4Breaks $CM+ $CAN_CM;"
643     "$LF $CR;"
644     "[$SP $ZW] [$LB4NonBreaks-$CM];"
645     "[$SP $ZW] $CM+ $CAN_CM;"
646     "$EmojiForSeqs $ZWJ $EmojiMods? $EmojiVar? $EmojiForSeqs;"
647     "$CM+ $CAN_CM;"
648     "$CM* $WJ $CM* $CAN_CM;"
649     "$CM* $WJ [$LB8NonBreaks-$CM];"
650     "$CANT_CM $CM* $WJ;"
651     "$CM* $CAN_CM $CM* $WJ;"
652     "$CM* $GL $CM* [$LB8NonBreaks-[$CM $SP $BA $HY]];"
653     "$CANT_CM $CM* $GL;"
654     "$CM* $CAN_CM $CM* $GL;"
655     "$CL $CM+ $CAN_CM;"
656     "$CP $CM+ $CAN_CM;"
657     "$EX $CM+ $CAN_CM;"
658     "$IS $CM+ $CAN_CM;"
659     "$SY $CM+ $CAN_CM;"
660     "$CL [$LB8NonBreaks-$CM];"
661     "$CP [$LB8NonBreaks-$CM];"
662     "$EX [$LB8NonBreaks-$CM];"
663     "$IS [$LB8NonBreaks-$CM];"
664     "$SY [$LB8NonBreaks-$CM];"
665     "[$CL $CP $EX $IS $SY] $CM+ $SP+ $CM* $OP; "
666     "$CM* $CAN_CM $SP* $CM* $OP;"
667     "$CANT_CM $SP* $CM* $OP;"
668     "$AL_FOLLOW? $CM+ $SP $SP* $CM* $OP;"
669     "$AL_FOLLOW_NOCM $CM+ $SP+ $CM* $OP;"
670     "$CM* $AL_FOLLOW_CM $CM+ $SP+ $CM* $OP;"
671     "$SY $CM $SP+ $OP;"
672     "$CM* $OP $SP* $CM* $QU;"
673     "$CM* $NS $SP* $CM* ($CL | $CP);"
674     "$CM* $B2 $SP* $CM* $B2;"
675     "$CM* $QU $CM* $CAN_CM;"
676     "$CM* $QU $LB18NonBreaks;"
677     "$CM* $CAN_CM $CM* $QU;"
678     "$CANT_CM $CM* $QU;"
679     "$CM* ($BA | $HY | $NS) $CM* [$LB20NonBreaks-$CM];"
680     "$CM* [$LB20NonBreaks-$CM] $CM* $BB;"
681     "[^$CB] $CM* $BB;"
682     "[^$CB] $CM* ($HY | $BA) $CM* $HL;"
683     "$CM* $IN $CM* ($ALPlus | $HL);"
684     "$CM* $IN $CM* $ID;"
685     "$CM* $IN $CM* $IN;"
686     "$CM* $IN $CM* $NU;"
687     "$CM* $PO $CM* $ID;"
688     "$CM* $NU $CM* ($ALPlus | $HL);"
689     "$CM* ($ALPlus | $HL) $CM* $NU;"
690     "$CM* $ID $CM* $PR;"
691     "$CM* ($ALPlus | $HL) $CM* $PR;"
692     "$CM* ($ALPlus | $HL) $CM* $PO;"
693     "($CM* ($PR | $PO))? ($CM* ($CL | $CP))? ($CM* ($NU | $IS | $SY))* $CM* $NU ($CM* ($OP | $HY))? ($CM* ($PR | $PO))?;"
694     "$CM* ($H3 | $H2 | $JV | $JL) $CM* $JL;"
695     "$CM* ($JT | $JV) $CM* ($H2 | $JV);"
696     "$CM* $JT $CM* ($H3 | $JT);"
697     "$CM* $IN $CM* ($H3 | $H2 | $JT | $JV | $JL);"
698     "$CM* $PO $CM* ($H3 | $H2 | $JT | $JV | $JL);"
699     "$CM* ($H3 | $H2 | $JT | $JV | $JL) $CM* $PR;"
700     "$CM* ($ALPlus | $HL) $CM* ($ALPlus | $HL);"
701     "$CM* ($ALPlus | $HL) $CM* $IS;"
702     "$CM* $OP $CM* ($ALPlus | $HL | $NU);"
703     "$CM* ($ALPlus | $HL | $NU) $CM* $CP;"
704 #if ADDITIONAL_EMOJI_SUPPORT
705     "$CM* $RI $CM* $RI;"
706 #endif
707     "$EmojiMods $EmojiVar? $EmojiForMods;";
708
709 static const char* uax14SafeForward =
710     "!!safe_forward;"
711     "[$CM $OP $QU $CL $CP $B2 $PR $HY $BA $SP $dictionary]+ [^$CM $OP $QU $CL $CP $B2 $PR $HY $BA $dictionary];"
712     "$dictionary $dictionary;";
713
714 static const char* uax14SafeReverse =
715     "!!safe_reverse;"
716     "$CM+ [^$CM $BK $CR $LF $NL $ZW $SP];"
717     "$CM+ $SP / .;"
718     "$SP+ $CM* $OP;"
719     "$SP+ $CM* $QU;"
720     "$SP+ $CM* ($CL | $CP);"
721     "$SP+ $CM* $B2;"
722     "$CM* ($HY | $BA) $CM* $HL;"
723     "($CM* ($IS | $SY))+ $CM* $NU;"
724     "($CL | $CP) $CM* ($NU | $IS | $SY);"
725     "$dictionary $dictionary;";
726
727 static String mapLineIteratorModeToRules(LineBreakIteratorMode mode, bool isCJK)
728 {
729     StringBuilder rulesBuilder;
730     rulesBuilder.append(uax14Prologue);
731     rulesBuilder.append(uax14AssignmentsBefore);
732     switch (mode) {
733     case LineBreakIteratorModeUAX14:
734         rulesBuilder.append(isCJK ? uax14AssignmentsCustomDefaultCJK : uax14AssignmentsCustomDefaultNonCJK);
735         break;
736     case LineBreakIteratorModeUAX14Loose:
737         rulesBuilder.append(isCJK ? uax14AssignmentsCustomLooseCJK : uax14AssignmentsCustomLooseNonCJK);
738         break;
739     case LineBreakIteratorModeUAX14Normal:
740         rulesBuilder.append(isCJK ? uax14AssignmentsCustomNormalCJK : uax14AssignmentsCustomNormalNonCJK);
741         break;
742     case LineBreakIteratorModeUAX14Strict:
743         rulesBuilder.append(isCJK ? uax14AssignmentsCustomStrictCJK : uax14AssignmentsCustomStrictNonCJK);
744         break;
745     }
746     rulesBuilder.append(uax14AssignmentsAfter);
747     rulesBuilder.append(uax14Forward);
748     rulesBuilder.append(uax14Reverse);
749     rulesBuilder.append(uax14SafeForward);
750     rulesBuilder.append(uax14SafeReverse);
751     return rulesBuilder.toString();
752 }
753
754 // Recognize BCP47 compliant primary language values of 'zh', 'ja', 'ko'
755 // (in any combination of case), optionally followed by subtags. Don't
756 // recognize 3-letter variants 'chi'/'zho', 'jpn', or 'kor' since BCP47
757 // requires use of shortest language tag.
758 bool isCJKLocale(const AtomicString& locale)
759 {
760     size_t length = locale.length();
761     if (length < 2)
762         return false;
763     auto c1 = locale[0];
764     auto c2 = locale[1];
765     auto c3 = length == 2 ? 0 : locale[2];
766     if (!c3 || c3 == '-' || c3 == '_' || c3 == '@') {
767         if (c1 == 'z' || c1 == 'Z')
768             return c2 == 'h' || c2 == 'H';
769         if (c1 == 'j' || c1 == 'J')
770             return c2 == 'a' || c2 == 'A';
771         if (c1 == 'k' || c1 == 'K')
772             return c2 == 'o' || c2 == 'O';
773     }
774     return false;
775 }
776
777 TextBreakIterator* openLineBreakIterator(const AtomicString& locale, LineBreakIteratorMode mode, bool isCJK)
778 {
779     UBreakIterator* ubrkIter;
780     UErrorCode openStatus = U_ZERO_ERROR;
781     bool localeIsEmpty = locale.isEmpty();
782     if (mode == LineBreakIteratorModeUAX14)
783         ubrkIter = ubrk_open(UBRK_LINE, localeIsEmpty ? currentTextBreakLocaleID() : locale.string().utf8().data(), 0, 0, &openStatus);
784     else {
785         UParseError parseStatus;
786         auto rules = mapLineIteratorModeToRules(mode, isCJK);
787         ubrkIter = ubrk_openRules(StringView(rules).upconvertedCharacters(), rules.length(), 0, 0, &parseStatus, &openStatus);
788     }
789     // locale comes from a web page and it can be invalid, leading ICU
790     // to fail, in which case we fall back to the default locale.
791     if (!localeIsEmpty && U_FAILURE(openStatus)) {
792         openStatus = U_ZERO_ERROR;
793         ubrkIter = ubrk_open(UBRK_LINE, currentTextBreakLocaleID(), 0, 0, &openStatus);
794     }
795
796     if (U_FAILURE(openStatus)) {
797         LOG_ERROR("ubrk_open failed with status %d", openStatus);
798         return nullptr;
799     }
800
801     return reinterpret_cast<TextBreakIterator*>(ubrkIter);
802 }
803
804 void closeLineBreakIterator(TextBreakIterator*& iterator)
805 {
806     UBreakIterator* ubrkIter = reinterpret_cast<UBreakIterator*>(iterator);
807     ASSERT(ubrkIter);
808     ubrk_close(ubrkIter);
809     iterator = nullptr;
810 }
811
812 static TextBreakIterator* nonSharedCharacterBreakIterator;
813
814 static inline bool compareAndSwapNonSharedCharacterBreakIterator(TextBreakIterator* expected, TextBreakIterator* newValue)
815 {
816     return WTF::weakCompareAndSwap(&nonSharedCharacterBreakIterator, expected, newValue);
817 }
818
819 NonSharedCharacterBreakIterator::NonSharedCharacterBreakIterator(StringView string)
820 {
821     m_iterator = nonSharedCharacterBreakIterator;
822
823     bool createdIterator = m_iterator && compareAndSwapNonSharedCharacterBreakIterator(m_iterator, nullptr);
824     if (!createdIterator)
825         m_iterator = initializeIterator(UBRK_CHARACTER);
826     if (!m_iterator)
827         return;
828
829     m_iterator = setTextForIterator(*m_iterator, string);
830 }
831
832 NonSharedCharacterBreakIterator::~NonSharedCharacterBreakIterator()
833 {
834     if (!compareAndSwapNonSharedCharacterBreakIterator(0, m_iterator))
835         ubrk_close(reinterpret_cast<UBreakIterator*>(m_iterator));
836 }
837
838 NonSharedCharacterBreakIterator::NonSharedCharacterBreakIterator(NonSharedCharacterBreakIterator&& other)
839     : m_iterator(nullptr)
840 {
841     std::swap(m_iterator, other.m_iterator);
842 }
843
844
845 // Iterator implemenation.
846
847 int textBreakFirst(TextBreakIterator* iterator)
848 {
849     return ubrk_first(reinterpret_cast<UBreakIterator*>(iterator));
850 }
851
852 int textBreakLast(TextBreakIterator* iterator)
853 {
854     return ubrk_last(reinterpret_cast<UBreakIterator*>(iterator));
855 }
856
857 int textBreakNext(TextBreakIterator* iterator)
858 {
859     return ubrk_next(reinterpret_cast<UBreakIterator*>(iterator));
860 }
861
862 int textBreakPrevious(TextBreakIterator* iterator)
863 {
864     return ubrk_previous(reinterpret_cast<UBreakIterator*>(iterator));
865 }
866
867 int textBreakPreceding(TextBreakIterator* iterator, int pos)
868 {
869     return ubrk_preceding(reinterpret_cast<UBreakIterator*>(iterator), pos);
870 }
871
872 int textBreakFollowing(TextBreakIterator* iterator, int pos)
873 {
874     return ubrk_following(reinterpret_cast<UBreakIterator*>(iterator), pos);
875 }
876
877 int textBreakCurrent(TextBreakIterator* iterator)
878 {
879     return ubrk_current(reinterpret_cast<UBreakIterator*>(iterator));
880 }
881
882 bool isTextBreak(TextBreakIterator* iterator, int position)
883 {
884     return ubrk_isBoundary(reinterpret_cast<UBreakIterator*>(iterator), position);
885 }
886
887 bool isWordTextBreak(TextBreakIterator* iterator)
888 {
889     int ruleStatus = ubrk_getRuleStatus(reinterpret_cast<UBreakIterator*>(iterator));
890     return ruleStatus != UBRK_WORD_NONE;
891 }
892
893 unsigned numGraphemeClusters(const String& s)
894 {
895     unsigned stringLength = s.length();
896     
897     if (!stringLength)
898         return 0;
899
900     // The only Latin-1 Extended Grapheme Cluster is CR LF
901     if (s.is8Bit() && !s.contains('\r'))
902         return stringLength;
903
904     NonSharedCharacterBreakIterator it(s);
905     if (!it)
906         return stringLength;
907
908     unsigned num = 0;
909     while (textBreakNext(it) != TextBreakDone)
910         ++num;
911     return num;
912 }
913
914 unsigned numCharactersInGraphemeClusters(const StringView& s, unsigned numGraphemeClusters)
915 {
916     unsigned stringLength = s.length();
917
918     if (!stringLength)
919         return 0;
920
921     // The only Latin-1 Extended Grapheme Cluster is CR LF
922     if (s.is8Bit() && !s.contains('\r'))
923         return std::min(stringLength, numGraphemeClusters);
924
925     NonSharedCharacterBreakIterator it(s);
926     if (!it)
927         return std::min(stringLength, numGraphemeClusters);
928
929     for (unsigned i = 0; i < numGraphemeClusters; ++i) {
930         if (textBreakNext(it) == TextBreakDone)
931             return stringLength;
932     }
933     return textBreakCurrent(it);
934 }
935
936 } // namespace WTF