[iOS] Expose SPI to access the current sentence boundary and selection state
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / EditorStateTests.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if WK_API_ENABLED
29
30 #import "EditingTestHarness.h"
31 #import "PlatformUtilities.h"
32 #import "TestWKWebView.h"
33 #import <WebKit/WKWebViewPrivate.h>
34 #import <wtf/Vector.h>
35
36 #if PLATFORM(IOS_FAMILY)
37 #import "UIKitSPI.h"
38 #import <UIKit/UIKit.h>
39 #endif
40
41 static void* const SelectionAttributesObservationContext = (void*)&SelectionAttributesObservationContext;
42
43 @interface SelectionChangeObserver : NSObject
44 - (instancetype)initWithWebView:(TestWKWebView *)webView;
45 @property (nonatomic, readonly) TestWKWebView *webView;
46 @property (nonatomic, readonly) _WKSelectionAttributes currentSelectionAttributes;
47 @end
48
49 @implementation SelectionChangeObserver {
50     RetainPtr<TestWKWebView> _webView;
51     Vector<_WKSelectionAttributes> _observedSelectionAttributes;
52 }
53
54 - (instancetype)initWithWebView:(TestWKWebView *)webView
55 {
56     if (!(self = [super init]))
57         return nil;
58
59     _webView = webView;
60     [_webView addObserver:self forKeyPath:@"_selectionAttributes" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:SelectionAttributesObservationContext];
61     return self;
62 }
63
64 - (TestWKWebView *)webView
65 {
66     return _webView.get();
67 }
68
69 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
70 {
71     if (context == SelectionAttributesObservationContext) {
72         if (!_observedSelectionAttributes.isEmpty())
73             EXPECT_EQ(_observedSelectionAttributes.last(), [change[NSKeyValueChangeOldKey] unsignedIntValue]);
74         _observedSelectionAttributes.append([change[NSKeyValueChangeNewKey] unsignedIntValue]);
75         return;
76     }
77     [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
78 }
79
80 - (_WKSelectionAttributes)currentSelectionAttributes
81 {
82     return _observedSelectionAttributes.isEmpty() ? _WKSelectionAttributeNoSelection : _observedSelectionAttributes.last();
83 }
84
85 @end
86
87 namespace TestWebKitAPI {
88
89 static RetainPtr<EditingTestHarness> setUpEditorStateTestHarness()
90 {
91     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
92     auto testHarness = adoptNS([[EditingTestHarness alloc] initWithWebView:webView.get()]);
93     [webView synchronouslyLoadTestPageNamed:@"editor-state-test-harness"];
94     return testHarness;
95 }
96
97 TEST(EditorStateTests, TypingAttributesBold)
98 {
99     auto testHarness = setUpEditorStateTestHarness();
100
101     [testHarness insertHTML:@"<b>first</b>" andExpectEditorStateWith:@{ @"bold": @YES }];
102     [testHarness toggleBold];
103     [testHarness insertText:@" second" andExpectEditorStateWith:@{ @"bold": @NO }];
104     [testHarness insertHTML:@"<span style='font-weight: 700'> third</span>" andExpectEditorStateWith:@{ @"bold": @YES }];
105     [testHarness insertHTML:@"<span style='font-weight: 300'> fourth</span>" andExpectEditorStateWith:@{ @"bold": @NO }];
106     [testHarness insertHTML:@"<span style='font-weight: 800'> fifth</span>" andExpectEditorStateWith:@{ @"bold": @YES }];
107     [testHarness insertHTML:@"<span style='font-weight: 400'> sixth</span>" andExpectEditorStateWith:@{ @"bold": @NO }];
108     [testHarness insertHTML:@"<span style='font-weight: 900'> seventh</span>" andExpectEditorStateWith:@{ @"bold": @YES }];
109     [testHarness toggleBold];
110     [testHarness insertText:@" eighth" andExpectEditorStateWith:@{ @"bold": @NO }];
111     [testHarness insertHTML:@"<strong> ninth</strong>" andExpectEditorStateWith:@{ @"bold": @YES }];
112     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"bold": @YES }];
113     [testHarness deleteBackwardAndExpectEditorStateWith:@{ @"bold": @YES }];
114     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"bold": @YES }];
115     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"bold": @NO }];
116     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"bold": @YES }];
117     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"bold": @NO }];
118     [testHarness selectAllAndExpectEditorStateWith:@{ @"bold": @YES }];
119     EXPECT_WK_STREQ("first second third fourth fifth sixth seventh eighth ninth", [[testHarness webView] stringByEvaluatingJavaScript:@"getSelection().toString()"]);
120 }
121
122 TEST(EditorStateTests, TypingAttributesItalic)
123 {
124     auto testHarness = setUpEditorStateTestHarness();
125
126     [testHarness insertHTML:@"<i>first</i>" andExpectEditorStateWith:@{ @"italic": @YES }];
127     [testHarness toggleItalic];
128     [testHarness insertText:@" second" andExpectEditorStateWith:@{ @"italic": @NO }];
129     [testHarness insertHTML:@"<span style='font-style: italic'> third</span>" andExpectEditorStateWith:@{ @"italic": @YES }];
130     [testHarness toggleItalic];
131     [testHarness insertText:@" fourth" andExpectEditorStateWith:@{ @"italic": @NO }];
132     [testHarness toggleItalic];
133     [testHarness insertText:@" fifth" andExpectEditorStateWith:@{ @"italic": @YES }];
134     [testHarness insertHTML:@"<span style='font-style: normal'> sixth</span>" andExpectEditorStateWith:@{ @"italic": @NO }];
135     [testHarness insertHTML:@"<span style='font-style: oblique'> seventh</span>" andExpectEditorStateWith:@{ @"italic": @YES }];
136     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"italic": @YES }];
137     [testHarness deleteBackwardAndExpectEditorStateWith:@{ @"italic": @YES }];
138     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"italic": @YES }];
139     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"italic": @NO }];
140     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"italic": @YES }];
141     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"italic": @NO }];
142
143     [testHarness selectAllAndExpectEditorStateWith:@{ @"italic": @YES }];
144     EXPECT_WK_STREQ("first second third fourth fifth sixth seventh", [[testHarness webView] stringByEvaluatingJavaScript:@"getSelection().toString()"]);
145 }
146
147 TEST(EditorStateTests, TypingAttributesUnderline)
148 {
149     auto testHarness = setUpEditorStateTestHarness();
150
151     [testHarness insertHTML:@"<u>first</u>" andExpectEditorStateWith:@{ @"underline": @YES }];
152     [testHarness toggleUnderline];
153     [testHarness insertText:@" second" andExpectEditorStateWith:@{ @"underline": @NO }];
154     [testHarness insertHTML:@"<span style='text-decoration: underline'> third</span>" andExpectEditorStateWith:@{ @"underline": @YES }];
155     [testHarness insertHTML:@"<span style='text-decoration: line-through'> fourth</span>" andExpectEditorStateWith:@{ @"underline": @NO }];
156     [testHarness insertHTML:@"<span style='text-decoration: underline overline line-through'> fifth</span>" andExpectEditorStateWith:@{ @"underline": @YES }];
157     [testHarness insertHTML:@"<span style='text-decoration: none'> sixth</span>" andExpectEditorStateWith:@{ @"underline": @NO }];
158     [testHarness toggleUnderline];
159     [testHarness insertText:@" seventh" andExpectEditorStateWith:@{ @"underline": @YES }];
160     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"underline": @YES }];
161     [testHarness deleteBackwardAndExpectEditorStateWith:@{ @"underline": @YES }];
162     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"underline": @YES }];
163     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"underline": @NO }];
164     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"underline": @YES }];
165     [testHarness moveWordBackwardAndExpectEditorStateWith:@{ @"underline": @NO }];
166
167     [testHarness selectAllAndExpectEditorStateWith:@{ @"underline": @YES }];
168     EXPECT_WK_STREQ("first second third fourth fifth sixth seventh", [[testHarness webView] stringByEvaluatingJavaScript:@"getSelection().toString()"]);
169 }
170
171 TEST(EditorStateTests, TypingAttributesTextAlignmentAbsoluteAlignmentOptions)
172 {
173     auto testHarness = setUpEditorStateTestHarness();
174     TestWKWebView *webView = [testHarness webView];
175
176     [webView stringByEvaluatingJavaScript:@"document.body.style.direction = 'ltr'"];
177
178     [testHarness insertHTML:@"<div style='text-align: right;'>right</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
179     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
180
181     [testHarness insertText:@"justified" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
182     [testHarness alignJustifiedAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentJustified) }];
183     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentJustified) }];
184
185     [testHarness alignCenterAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
186     [testHarness insertText:@"center" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
187     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
188
189     [testHarness insertHTML:@"<span id='left'>left</span>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
190     [webView stringByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(left.childNodes[0], 0, left.childNodes[0], 6)"];
191     [testHarness alignLeftAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
192
193     [testHarness selectAllAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
194     EXPECT_WK_STREQ("right\njustified\ncenter\nleft", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]);
195 }
196
197 TEST(EditorStateTests, TypingAttributesTextAlignmentStartEnd)
198 {
199     auto testHarness = setUpEditorStateTestHarness();
200     TestWKWebView *webView = [testHarness webView];
201
202     [webView stringByEvaluatingJavaScript:@"document.styleSheets[0].insertRule('.start { text-align: start; }')"];
203     [webView stringByEvaluatingJavaScript:@"document.styleSheets[0].insertRule('.end { text-align: end; }')"];
204     [webView stringByEvaluatingJavaScript:@"document.body.style.direction = 'rtl'"];
205
206     [testHarness insertHTML:@"<div class='start'>rtl start</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
207     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
208
209     [testHarness insertHTML:@"<div class='end'>rtl end</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
210     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
211
212     [[testHarness webView] stringByEvaluatingJavaScript:@"document.body.style.direction = 'ltr'"];
213     [testHarness insertHTML:@"<div class='start'>ltr start</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
214     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
215
216     [testHarness insertHTML:@"<div class='end'>ltr end</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
217     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
218 }
219
220 TEST(EditorStateTests, TypingAttributesTextAlignmentDirectionalText)
221 {
222     auto testHarness = setUpEditorStateTestHarness();
223     [[testHarness webView] stringByEvaluatingJavaScript:@"document.body.setAttribute('dir', 'auto')"];
224
225     [testHarness insertHTML:@"<div>מקור השם עברית</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
226     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
227     [testHarness insertHTML:@"<div dir='ltr'>מקור השם עברית</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
228     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
229     [testHarness insertHTML:@"<div dir='rtl'>מקור השם עברית</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
230     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
231
232     [testHarness insertHTML:@"<div dir='auto'>This is English text</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
233     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
234     [testHarness insertHTML:@"<div dir='rtl'>This is English text</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
235     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentRight) }];
236     [testHarness insertHTML:@"<div dir='ltr'>This is English text</div>" andExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
237     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentLeft) }];
238 }
239
240 TEST(EditorStateTests, TypingAttributesTextColor)
241 {
242     auto testHarness = setUpEditorStateTestHarness();
243
244     [testHarness setForegroundColor:@"rgb(255, 0, 0)"];
245     [testHarness insertText:@"red" andExpectEditorStateWith:@{ @"text-color": @"rgb(255, 0, 0)" }];
246
247     [testHarness insertHTML:@"<span style='color: rgb(0, 255, 0)'>green</span>" andExpectEditorStateWith:@{ @"text-color": @"rgb(0, 255, 0)" }];
248     [testHarness insertParagraphAndExpectEditorStateWith:@{ @"text-color": @"rgb(0, 255, 0)" }];
249
250     [testHarness setForegroundColor:@"rgb(0, 0, 255)"];
251     [testHarness insertText:@"blue" andExpectEditorStateWith:@{ @"text-color": @"rgb(0, 0, 255)" }];
252 }
253
254 TEST(EditorStateTests, TypingAttributesMixedStyles)
255 {
256     auto testHarness = setUpEditorStateTestHarness();
257
258     [testHarness alignCenterAndExpectEditorStateWith:@{ @"text-alignment": @(NSTextAlignmentCenter) }];
259     [testHarness setForegroundColor:@"rgb(128, 128, 128)"];
260     [testHarness toggleBold];
261     [testHarness toggleItalic];
262     [testHarness toggleUnderline];
263     NSDictionary *expectedAttributes = @{
264         @"bold": @YES,
265         @"italic": @YES,
266         @"underline": @YES,
267         @"text-color": @"rgb(128, 128, 128)",
268         @"text-alignment": @(NSTextAlignmentCenter)
269     };
270     BOOL containsProperties = [testHarness latestEditorStateContains:expectedAttributes];
271     EXPECT_TRUE(containsProperties);
272     if (!containsProperties)
273         NSLog(@"Expected %@ to contain %@", [testHarness latestEditorState], expectedAttributes);
274 }
275
276 TEST(EditorStateTests, TypingAttributeLinkColor)
277 {
278     auto testHarness = setUpEditorStateTestHarness();
279     [testHarness insertHTML:@"<a href='https://www.apple.com/'>This is a link</a>" andExpectEditorStateWith:@{ @"text-color": @"rgb(0, 0, 238)" }];
280     [testHarness selectAllAndExpectEditorStateWith:@{ @"text-color": @"rgb(0, 0, 238)" }];
281     EXPECT_WK_STREQ("https://www.apple.com/", [[testHarness webView] stringByEvaluatingJavaScript:@"document.querySelector('a').href"]);
282 }
283
284 #if PLATFORM(IOS_FAMILY)
285
286 static void checkContentViewHasTextWithFailureDescription(TestWKWebView *webView, BOOL expectedToHaveText, NSString *description)
287 {
288     BOOL hasText = webView.textInputContentView.hasText;
289     if (expectedToHaveText)
290         EXPECT_TRUE(hasText);
291     else
292         EXPECT_FALSE(hasText);
293
294     if (expectedToHaveText != hasText)
295         NSLog(@"Expected -[%@ hasText] to be %@, but observed: %@ (%@)", [webView.textInputContentView class], expectedToHaveText ? @"YES" : @"NO", hasText ? @"YES" : @"NO", description);
296 }
297
298 TEST(EditorStateTests, ContentViewHasTextInContentEditableElement)
299 {
300     auto testHarness = setUpEditorStateTestHarness();
301     TestWKWebView *webView = [testHarness webView];
302
303     checkContentViewHasTextWithFailureDescription(webView, NO, @"before inserting any content");
304     [testHarness insertHTML:@"<img src='icon.png'></img>"];
305     checkContentViewHasTextWithFailureDescription(webView, NO, @"after inserting an image element");
306     [testHarness insertText:@"A"];
307     checkContentViewHasTextWithFailureDescription(webView, YES, @"after inserting text");
308     [testHarness selectAll];
309     checkContentViewHasTextWithFailureDescription(webView, YES, @"after selecting everything");
310     [testHarness deleteBackwards];
311     checkContentViewHasTextWithFailureDescription(webView, NO, @"after deleting everything");
312     [testHarness insertParagraph];
313     checkContentViewHasTextWithFailureDescription(webView, YES, @"after inserting a newline");
314     [testHarness deleteBackwards];
315     checkContentViewHasTextWithFailureDescription(webView, NO, @"after deleting the newline");
316     [testHarness insertText:@"B"];
317     checkContentViewHasTextWithFailureDescription(webView, YES, @"after inserting text again");
318     [webView stringByEvaluatingJavaScript:@"document.body.blur()"];
319     [webView waitForNextPresentationUpdate];
320     checkContentViewHasTextWithFailureDescription(webView, NO, @"after losing focus");
321 }
322
323 TEST(EditorStateTests, ContentViewHasTextInTextarea)
324 {
325     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
326     auto testHarness = adoptNS([[EditingTestHarness alloc] initWithWebView:webView.get()]);
327     [webView synchronouslyLoadHTMLString:@"<textarea id='textarea'></textarea>"];
328     [webView stringByEvaluatingJavaScript:@"textarea.focus()"];
329     [webView waitForNextPresentationUpdate];
330
331     checkContentViewHasTextWithFailureDescription(webView.get(), NO, @"before inserting any content");
332     [testHarness insertText:@"A"];
333     checkContentViewHasTextWithFailureDescription(webView.get(), YES, @"after inserting text");
334     [testHarness selectAll];
335     checkContentViewHasTextWithFailureDescription(webView.get(), YES, @"after selecting everything");
336     [testHarness deleteBackwards];
337     checkContentViewHasTextWithFailureDescription(webView.get(), NO, @"after deleting everything");
338     [testHarness insertParagraph];
339     checkContentViewHasTextWithFailureDescription(webView.get(), YES, @"after inserting a newline");
340     [testHarness deleteBackwards];
341     checkContentViewHasTextWithFailureDescription(webView.get(), NO, @"after deleting the newline");
342     [testHarness insertText:@"B"];
343     checkContentViewHasTextWithFailureDescription(webView.get(), YES, @"after inserting text again");
344     [webView stringByEvaluatingJavaScript:@"textarea.blur()"];
345     [webView waitForNextPresentationUpdate];
346     checkContentViewHasTextWithFailureDescription(webView.get(), NO, @"after losing focus");
347 }
348
349 TEST(EditorStateTests, CaretColorInContentEditable)
350 {
351     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
352     [webView synchronouslyLoadHTMLString:@"<body style=\"caret-color: red;\" contenteditable=\"true\"></body>"];
353     [webView stringByEvaluatingJavaScript:@"document.body.focus()"];
354     UIView<UITextInputTraits_Private> *textInput = (UIView<UITextInputTraits_Private> *) [webView textInputContentView];
355     UIColor *insertionPointColor = textInput.insertionPointColor;
356     UIColor *redColor = [UIColor redColor];
357     auto colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
358     auto cgInsertionPointColor = adoptCF(CGColorCreateCopyByMatchingToColorSpace(colorSpace.get(), kCGRenderingIntentDefault, insertionPointColor.CGColor, NULL));
359     auto cgRedColor = adoptCF(CGColorCreateCopyByMatchingToColorSpace(colorSpace.get(), kCGRenderingIntentDefault, redColor.CGColor, NULL));
360     EXPECT_TRUE(CGColorEqualToColor(cgInsertionPointColor.get(), cgRedColor.get()));
361 }
362
363 TEST(EditorStateTests, ObserveSelectionAttributeChanges)
364 {
365     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
366     auto editor = adoptNS([[EditingTestHarness alloc] initWithWebView:webView.get()]);
367     [webView _setEditable:YES];
368     [webView synchronouslyLoadHTMLString:@"<body></body>"];
369
370     auto observer = adoptNS([[SelectionChangeObserver alloc] initWithWebView:webView.get()]);
371
372     [webView evaluateJavaScript:@"document.body.focus()" completionHandler:nil];
373     [webView waitForNextPresentationUpdate];
374     EXPECT_EQ(_WKSelectionAttributeIsCaret | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
375
376     [editor insertText:@"Hello"];
377     EXPECT_EQ(_WKSelectionAttributeIsCaret, [observer currentSelectionAttributes]);
378
379     [editor insertText:@"."];
380     EXPECT_EQ(_WKSelectionAttributeIsCaret | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
381
382     [editor moveBackward];
383     EXPECT_EQ(_WKSelectionAttributeIsCaret, [observer currentSelectionAttributes]);
384
385     [editor moveForward];
386     EXPECT_EQ(_WKSelectionAttributeIsCaret | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
387
388     [editor deleteBackwards];
389     EXPECT_EQ(_WKSelectionAttributeIsCaret, [observer currentSelectionAttributes]);
390
391     [editor insertParagraph];
392     EXPECT_EQ(_WKSelectionAttributeIsCaret | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
393
394     [editor selectAll];
395     EXPECT_EQ(_WKSelectionAttributeIsRange | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
396
397     [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
398     [webView waitForNextPresentationUpdate];
399     EXPECT_EQ(_WKSelectionAttributeNoSelection, [observer currentSelectionAttributes]);
400 }
401
402 #endif // PLATFORM(IOS_FAMILY)
403
404 } // namespace TestWebKitAPI
405
406 #endif // WK_API_ENABLED