/* * Copyright (C) 2019 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "DeprecatedGlobalValues.h" #import "PlatformUtilities.h" #import "Test.h" #import "TestWKWebView.h" #import #import #import #import #import #import #import #import #import #import typedef void(^ItemCallback)(_WKTextManipulationItem *); typedef void(^ItemListCallback)(NSArray<_WKTextManipulationItem *> *); @interface TextManipulationDelegate : NSObject <_WKTextManipulationDelegate> - (void)_webView:(WKWebView *)webView didFindTextManipulationItems:(NSArray<_WKTextManipulationItem *> *)items; @property (nonatomic, readonly, copy) NSArray<_WKTextManipulationItem *> *items; @property (strong) ItemCallback itemCallback; @property (strong) ItemListCallback itemListCallback; @end @implementation TextManipulationDelegate { RetainPtr _items; } - (instancetype)init { if (!(self = [super init])) return nil; _items = adoptNS([[NSMutableArray alloc] init]); return self; } - (void)_webView:(WKWebView *)webView didFindTextManipulationItems:(NSArray<_WKTextManipulationItem *> *)items { [_items addObjectsFromArray:items]; if (self.itemListCallback) self.itemListCallback(items); if (self.itemCallback) { for (_WKTextManipulationItem *item in items) self.itemCallback(item); } } - (NSArray<_WKTextManipulationItem *> *)items { return _items.get(); } @end @interface LegacyTextManipulationDelegate : NSObject <_WKTextManipulationDelegate> - (void)_webView:(WKWebView *)webView didFindTextManipulationItem:(_WKTextManipulationItem *)item; @property (nonatomic, readonly, copy) NSArray<_WKTextManipulationItem *> *items; @property (strong) ItemCallback itemCallback; @end @implementation LegacyTextManipulationDelegate { RetainPtr _items; } - (instancetype)init { if (!(self = [super init])) return nil; _items = adoptNS([[NSMutableArray alloc] init]); return self; } - (void)_webView:(WKWebView *)webView didFindTextManipulationItem:(_WKTextManipulationItem *)item { [_items addObject:item]; } - (NSArray<_WKTextManipulationItem *> *)items { return _items.get(); } @end namespace TestWebKitAPI { TEST(TextManipulation, StartTextManipulationExitEarlyWithoutDelegate) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView synchronouslyLoadHTMLString:@"" "hello
world
WebKit
"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_EQ([delegate items].count, 0UL); } TEST(TextManipulation, StartTextManipulationFindSimpleParagraphs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "hello
world
WebKit
"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 3UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello", items[0].tokens[0].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("world", items[1].tokens[0].content.UTF8String); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_STREQ("WebKit", items[2].tokens[0].content.UTF8String); } TEST(TextManipulation, StartTextManipulationFindMultipleParagraphsInSingleTextNode) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "
hello\nworld\nWebKit
"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 5UL); EXPECT_STREQ("hello", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("\n", items[0].tokens[1].content.UTF8String); EXPECT_STREQ("world", items[0].tokens[2].content.UTF8String); EXPECT_STREQ("\n", items[0].tokens[3].content.UTF8String); EXPECT_STREQ("WebKit", items[0].tokens[4].content.UTF8String); } TEST(TextManipulation, StartTextManipulationFindParagraphsWithMultipleTokens) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "hello, world
WebKit
"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("hello, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("world", items[0].tokens[1].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 2UL); EXPECT_STREQ("Web", items[1].tokens[0].content.UTF8String); EXPECT_STREQ("Kit", items[1].tokens[1].content.UTF8String); } TEST(TextManipulation, StartTextManipulationFindAttributeContent) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"hey" "
hello\"fruit\""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 4UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hey", items[0].tokens[0].content.UTF8String); EXPECT_FALSE(items[0].tokens[0].isExcluded); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("this is greet", items[1].tokens[0].content.UTF8String); EXPECT_FALSE(items[1].tokens[0].isExcluded); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_STREQ("fruit", items[2].tokens[0].content.UTF8String); EXPECT_FALSE(items[2].tokens[0].isExcluded); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_STREQ("hello", items[3].tokens[0].content.UTF8String); EXPECT_FALSE(items[3].tokens[0].isExcluded); } TEST(TextManipulation, StartTextManipulationSupportsLegacyDelegateCallback) { auto delegate = adoptNS([[LegacyTextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

hello, world

WebKit

"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); __block auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("hello, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("world", items[0].tokens[1].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("WebKit", items[1].tokens[0].content.UTF8String); } TEST(TextManipulation, StartTextManipulationFindNewlyInsertedParagraph) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

hello

"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); __block auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello", items[0].tokens[0].content.UTF8String); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 3) done = true; }; [webView stringByEvaluatingJavaScript:@"document.body.appendChild(document.createElement('div')).innerHTML = 'world
WebKit';"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items.count, 3UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("world", items[1].tokens[0].content.UTF8String); EXPECT_EQ(items[2].tokens.count, 2UL); EXPECT_STREQ("Web", items[2].tokens[0].content.UTF8String); EXPECT_STREQ("Kit", items[2].tokens[1].content.UTF8String); } TEST(TextManipulation, StartTextManipulationFindNewlyDisplayedParagraph) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "

hello

" "
" "" "" "
" "" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); __block auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello", items[0].tokens[0].content.UTF8String); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { done = true; }; [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('span.hidden').forEach((span) => span.classList.remove('hidden'));"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[1].tokens.count, 2UL); EXPECT_STREQ("Web", items[1].tokens[0].content.UTF8String); EXPECT_STREQ("Kit", items[1].tokens[1].content.UTF8String); // This has to happen separately in order to have a deterministic ordering. done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { done = true; }; [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('div.hidden').forEach((div) => div.classList.remove('hidden'));"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items.count, 3UL); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_STREQ("hey", items[2].tokens[0].content.UTF8String); } TEST(TextManipulation, StartTextManipulationFindSameParagraphWithNewContent) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

hello

"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello", items[0].tokens[0].content.UTF8String); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { done = true; }; [webView stringByEvaluatingJavaScript:@"b = document.createElement('b');" "b.textContent = ' world';" "document.querySelector('p').appendChild(b); ''"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ(" world", items[1].tokens[0].content.UTF8String); } TEST(TextManipulation, StartTextManipulationApplySingleExcluionRuleForElement) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "Here's some code:function F() { }."]; RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); [configuration setExclusionRules:@[ adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:(BOOL)YES forElement:@"code"]).get(), ]]; done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 5UL); auto* tokens = items[0].tokens; EXPECT_STREQ("Here's some code:", tokens[0].content.UTF8String); EXPECT_FALSE(tokens[0].isExcluded); EXPECT_STREQ("function ", tokens[1].content.UTF8String); EXPECT_TRUE(tokens[1].isExcluded); EXPECT_STREQ("F", tokens[2].content.UTF8String); EXPECT_TRUE(tokens[2].isExcluded); EXPECT_STREQ("() { }", tokens[3].content.UTF8String); EXPECT_TRUE(tokens[3].isExcluded); EXPECT_STREQ(".", tokens[4].content.UTF8String); EXPECT_FALSE(tokens[4].isExcluded); } TEST(TextManipulation, StartTextManipulationApplyInclusionExclusionRulesForAttributes) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "hello, world"]; RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); [configuration setExclusionRules:@[ adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:(BOOL)YES forAttribute:@"data-exclude" value:@"yes"]).get(), adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:(BOOL)NO forAttribute:@"data-exclude" value:@"no"]).get(), ]]; done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("world", items[0].tokens[0].content.UTF8String); EXPECT_FALSE(items[0].tokens[0].isExcluded); } TEST(TextManipulation, StartTextManipulationApplyInclusionExclusionRulesForClass) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "Message: hello, world"]; auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); [configuration setExclusionRules:@[ adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:YES forClass:@"exclude"]).get(), ]]; done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("Message: ", items[0].tokens[0].content.UTF8String); EXPECT_FALSE(items[0].tokens[0].isExcluded); } TEST(TextManipulation, StartTextManipulationApplyInclusionExclusionRulesForClassAndAttribute) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "Message: hello, world"]; auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); [configuration setExclusionRules:@[ adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:YES forAttribute:@"data-exclude" value:@"yes"]).get(), adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:NO forAttribute:@"data-exclude" value:@"no"]).get(), adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:YES forClass:@"exclude"]).get(), ]]; done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello, ", items[0].tokens[0].content.UTF8String); EXPECT_FALSE(items[0].tokens[0].isExcluded); } TEST(TextManipulation, StartTextManipulationBreaksParagraphInBetweenListItems) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " " "" "" "
  • One
  • Two-three
" "

" "
  • Four
  • Five-six
" "

" "
  1. Seven
  2. Eight
" "

" "
  • Nine
  • Ten
  • " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); NSArray<_WKTextManipulationItem *> *items = [delegate items]; EXPECT_EQ(items.count, 7UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("One", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 2UL); EXPECT_WK_STREQ("Two", items[1].tokens[0].content); EXPECT_WK_STREQ("-three", items[1].tokens[1].content); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_WK_STREQ("Four", items[2].tokens[0].content); EXPECT_EQ(items[3].tokens.count, 2UL); EXPECT_WK_STREQ("Five", items[3].tokens[0].content); EXPECT_WK_STREQ("-six", items[3].tokens[1].content); EXPECT_EQ(items[4].tokens.count, 2UL); EXPECT_WK_STREQ("Seven", items[4].tokens[0].content); EXPECT_WK_STREQ("Eight", items[4].tokens[1].content); EXPECT_EQ(items[5].tokens.count, 1UL); EXPECT_WK_STREQ("Nine", items[5].tokens[0].content); EXPECT_EQ(items[6].tokens.count, 1UL); EXPECT_WK_STREQ("Ten", items[6].tokens[0].content); } TEST(TextManipulation, StartTextManipulationIncludesFullyClippedText) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " " "" "" "
    Hello world
    " "
    " "

    More text

    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); NSArray<_WKTextManipulationItem *> *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_WK_STREQ("Hello", items[0].tokens[0].content); EXPECT_WK_STREQ(" world", items[0].tokens[1].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("More text", items[1].tokens[0].content); } TEST(TextManipulation, StartTextManipulationFindsInsertedClippedText) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " " "" "" "
    hello, world
    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 2) done = true; }; [webView stringByEvaluatingJavaScript:@"var beforeElement = document.createElement('span');" "beforeElement.innerHTML='before';" "document.querySelector('div').before(beforeElement);"]; TestWebKitAPI::Util::run(&done); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 3) done = true; }; [webView stringByEvaluatingJavaScript:@"var afterElement = document.createElement('p');" "afterElement.innerHTML='after';" "document.querySelector('div').after(afterElement)"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello, world", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("before", items[1].tokens[0].content); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_WK_STREQ("after", items[2].tokens[0].content); } TEST(TextManipulation, StartTextManipulationTreatsInlineBlockLinksAndButtonsAndSpansAsParagraphs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " " "" "" " " "

    " " ThreeFour" " Five" " Six" " End" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); NSArray<_WKTextManipulationItem *> *items = [delegate items]; EXPECT_EQ(items.count, 7UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_EQ(items[4].tokens.count, 1UL); EXPECT_EQ(items[5].tokens.count, 1UL); EXPECT_EQ(items[6].tokens.count, 1UL); EXPECT_WK_STREQ("One", items[0].tokens[0].content); EXPECT_WK_STREQ("Two", items[1].tokens[0].content); EXPECT_WK_STREQ("Three", items[2].tokens[0].content); EXPECT_WK_STREQ("Four", items[3].tokens[0].content); EXPECT_WK_STREQ("Five", items[4].tokens[0].content); EXPECT_WK_STREQ("Six", items[5].tokens[0].content); EXPECT_WK_STREQ("End", items[6].tokens[0].content); } TEST(TextManipulation, StartTextManipulationTreatsLinksInNavigationElementsAsParagraphs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " " "" "" "
    " " " "
    " " " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); NSArray<_WKTextManipulationItem *> *items = [delegate items]; EXPECT_EQ(items.count, 4UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_WK_STREQ("Foo", items[0].tokens[0].content); EXPECT_WK_STREQ("Bar", items[1].tokens[0].content); EXPECT_WK_STREQ("Baz", items[2].tokens[0].content); EXPECT_WK_STREQ("Garply", items[3].tokens[0].content); } TEST(TextManipulation, StartTextManipulationTreatsNestedInlineBlockListItemsAndLinksAsParagraphs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " " "" "" "
      " "
    1. OneTwoThreeFour
    2. " "
    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 4UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_WK_STREQ("One", items[0].tokens[0].content); EXPECT_WK_STREQ("Two", items[1].tokens[0].content); EXPECT_WK_STREQ("Three", items[2].tokens[0].content); EXPECT_WK_STREQ("Four", items[3].tokens[0].content); } TEST(TextManipulation, StartTextManipulationExtractsUserInfo) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " This is a test" "

    First

    " "
    Second
    " " Third" "
    Fourth
    " " " ""]; [webView waitForNextPresentationUpdate]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 5UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_EQ(items[4].tokens.count, 1UL); EXPECT_WK_STREQ("This is a test", items[0].tokens[0].content); EXPECT_WK_STREQ("First", items[1].tokens[0].content); EXPECT_WK_STREQ("Second", items[2].tokens[0].content); EXPECT_WK_STREQ("Third", items[3].tokens[0].content); EXPECT_WK_STREQ("Fourth", items[4].tokens[0].content); { auto userInfo = items[0].tokens[0].userInfo; EXPECT_WK_STREQ("TestWebKitAPI.resources", [(NSURL *)userInfo[_WKTextManipulationTokenUserInfoDocumentURLKey] lastPathComponent]); EXPECT_WK_STREQ("TITLE", (NSString *)userInfo[_WKTextManipulationTokenUserInfoTagNameKey]); EXPECT_FALSE([userInfo[_WKTextManipulationTokenUserInfoVisibilityKey] boolValue]); } { auto userInfo = items[1].tokens[0].userInfo; EXPECT_WK_STREQ("TestWebKitAPI.resources", [(NSURL *)userInfo[_WKTextManipulationTokenUserInfoDocumentURLKey] lastPathComponent]); EXPECT_WK_STREQ("P", (NSString *)userInfo[_WKTextManipulationTokenUserInfoTagNameKey]); EXPECT_FALSE([userInfo[_WKTextManipulationTokenUserInfoVisibilityKey] boolValue]); } { auto userInfo = items[2].tokens[0].userInfo; EXPECT_WK_STREQ("TestWebKitAPI.resources", [(NSURL *)userInfo[_WKTextManipulationTokenUserInfoDocumentURLKey] lastPathComponent]); EXPECT_WK_STREQ("DIV", (NSString *)userInfo[_WKTextManipulationTokenUserInfoTagNameKey]); EXPECT_WK_STREQ("button", (NSString *)userInfo[_WKTextManipulationTokenUserInfoRoleAttributeKey]); EXPECT_FALSE([userInfo[_WKTextManipulationTokenUserInfoVisibilityKey] boolValue]); } { auto userInfo = items[3].tokens[0].userInfo; EXPECT_WK_STREQ("TestWebKitAPI.resources", [(NSURL *)userInfo[_WKTextManipulationTokenUserInfoDocumentURLKey] lastPathComponent]); EXPECT_WK_STREQ("SPAN", (NSString *)userInfo[_WKTextManipulationTokenUserInfoTagNameKey]); EXPECT_FALSE([userInfo[_WKTextManipulationTokenUserInfoVisibilityKey] boolValue]); } { auto userInfo = items[4].tokens[0].userInfo; EXPECT_WK_STREQ("TestWebKitAPI.resources", [(NSURL *)userInfo[_WKTextManipulationTokenUserInfoDocumentURLKey] lastPathComponent]); EXPECT_WK_STREQ("DIV", (NSString *)userInfo[_WKTextManipulationTokenUserInfoTagNameKey]); EXPECT_TRUE([userInfo[_WKTextManipulationTokenUserInfoVisibilityKey] boolValue]); } } TEST(TextManipulation, StartTextManipulationExtractsValuesFromButtonInputs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "" "" "" "" "" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("One", items[0].tokens[0].content); EXPECT_WK_STREQ("Two", items[1].tokens[0].content); } TEST(TextManipulation, StartTextManipulationExtractsValuesFromTextInputs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "" "" "" "" "" "" "" "" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("One", items[0].tokens[0].content); EXPECT_WK_STREQ("Two", items[1].tokens[0].content); } TEST(TextManipulation, StartTextManipulationDoesNotExtractUserModifiedText) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_EQ([delegate items].count, 0UL); Vector> items; done = false; [delegate setItemCallback:[&] (_WKTextManipulationItem *item) { items.append(retainPtr(item)); done = true; }]; [webView stringByEvaluatingJavaScript:@"document.getElementById('one').focus();" "document.execCommand('InsertText', true, 'foo');" "document.getElementById('two').value = 'bar';"]; Util::run(&done); EXPECT_EQ(items.size(), 1U); auto tokens = [items[0] tokens]; EXPECT_EQ(tokens.count, 1UL); EXPECT_WK_STREQ("bar", tokens.firstObject.content); } TEST(TextManipulation, StartTextManipulationExtractsVisibleLineBreaksInTextAsExcludedTokens) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " one two" " " " three four " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 4UL); EXPECT_STREQ("\n ", items[0].tokens[0].content.UTF8String); EXPECT_TRUE(items[0].tokens[0].isExcluded); EXPECT_STREQ("one", items[0].tokens[1].content.UTF8String); EXPECT_FALSE(items[0].tokens[1].isExcluded); EXPECT_STREQ("\n ", items[0].tokens[2].content.UTF8String); EXPECT_TRUE(items[0].tokens[2].isExcluded); EXPECT_STREQ("two", items[0].tokens[3].content.UTF8String); EXPECT_FALSE(items[0].tokens[3].isExcluded); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("three four", items[1].tokens[0].content.UTF8String); } TEST(TextManipulation, StartTextManipulationExtractsPrivateUseCharactersAsExcludedTokens) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"foobarbaz"]; auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); auto* item = items.firstObject; EXPECT_EQ(item.tokens.count, 5UL); EXPECT_WK_STREQ("foo", item.tokens[0].content); EXPECT_FALSE(item.tokens[0].isExcluded); EXPECT_WK_STREQ("", item.tokens[1].content); EXPECT_TRUE(item.tokens[1].isExcluded); EXPECT_WK_STREQ("bar", item.tokens[2].content); EXPECT_FALSE(item.tokens[2].isExcluded); EXPECT_WK_STREQ("", item.tokens[3].content); EXPECT_TRUE(item.tokens[3].isExcluded); EXPECT_WK_STREQ("baz", item.tokens[4].content); EXPECT_FALSE(item.tokens[4].isExcluded); } TEST(TextManipulation, StartTextManipulationExtractsValuesByNode) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "onetwo threefour" " five" " six seven" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 5UL); EXPECT_WK_STREQ("one", items[0].tokens[0].content); EXPECT_WK_STREQ("two", items[0].tokens[1].content); EXPECT_WK_STREQ(" \n ", items[0].tokens[2].content); EXPECT_WK_STREQ("three", items[0].tokens[3].content); EXPECT_WK_STREQ("four", items[0].tokens[4].content); EXPECT_EQ(items[1].tokens.count, 5UL); EXPECT_WK_STREQ("\n", items[1].tokens[0].content); EXPECT_WK_STREQ("five", items[1].tokens[1].content); EXPECT_WK_STREQ(" six", items[1].tokens[2].content); EXPECT_WK_STREQ(" ", items[1].tokens[3].content); EXPECT_WK_STREQ("seven", items[1].tokens[4].content); } TEST(TextManipulation, StartTextManipulationExcludesTextRenderedAsIcons) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "" "" "" "onetwothreefour" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); auto item = items.firstObject; EXPECT_EQ(item.tokens.count, 4UL); EXPECT_WK_STREQ(item.tokens[0].content, "one"); EXPECT_FALSE(item.tokens[0].isExcluded); EXPECT_WK_STREQ(item.tokens[1].content, "two"); EXPECT_TRUE(item.tokens[1].isExcluded); EXPECT_WK_STREQ(item.tokens[2].content, "three"); EXPECT_FALSE(item.tokens[2].isExcluded); EXPECT_WK_STREQ(item.tokens[3].content, "four"); EXPECT_FALSE(item.tokens[3].isExcluded); } TEST(TextManipulation, StartTextManipulationAvoidCrashWhenExtractingOrphanedPositions) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hello world

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello world", items[0].tokens[0].content); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { done = true; }; [webView objectByEvaluatingJavaScript:@"(() => {" " const objectElement = document.createElement('object');" " document.body.appendChild(objectElement);" " document.body.scrollTop;" " objectElement.remove();" " const text = document.createTextNode('testing');" " const container = document.createElement('div');" " container.appendChild(text);" " document.body.appendChild(container);" "})();"]; TestWebKitAPI::Util::run(&done); } TEST(TextManipulation, RemovedElements) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hello world

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello world", items[0].tokens[0].content); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { done = true; }; [webView objectByEvaluatingJavaScript:@"(() => {" " for (let i = 0; i < 1024; ++i) {" " const objectElement = document.createElement('object');" " document.body.appendChild(objectElement);" " objectElement.remove();" " }" " const text = document.createTextNode('testing');" " const container = document.createElement('div');" " container.appendChild(text);" " document.body.appendChild(container);" "})();"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("testing", items[1].tokens[0].content); } TEST(TextManipulation, StartTextManipulationExtractsHeadingElementsAsSeparateItems) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" " " "
    " "

    Hello world

    " "

    This is a heading

    " " " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("Hello world", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("This is a heading", items[1].tokens[0].content); } TEST(TextManipulation, StartTextManipulationIgnoresSpaces) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "Hello" "
     
    " "World"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("Hello", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("World", items[1].tokens[0].content); } TEST(TextManipulation, StartTextManipulationExtractsTableCellsAsSeparateItems) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "" " " "" "" " " " " " " " Hello" " World" " " " " " " " " " " "
    FooBarBaz
    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 5UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("Hello", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("World", items[1].tokens[0].content); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_WK_STREQ("Foo", items[2].tokens[0].content); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_WK_STREQ("Bar", items[3].tokens[0].content); EXPECT_EQ(items[4].tokens.count, 1UL); EXPECT_WK_STREQ("Baz", items[4].tokens[0].content); } struct Token { NSString *identifier; NSString *content; }; template static RetainPtr<_WKTextManipulationItem> createItem(NSString *itemIdentifier, const Token (&tokens)[length]) { RetainPtr wkTokens = adoptNS([[NSMutableArray alloc] init]); for (size_t i = 0; i < length; i++) { RetainPtr<_WKTextManipulationToken> token = adoptNS([[_WKTextManipulationToken alloc] init]); [token setIdentifier: tokens[i].identifier]; [token setContent: tokens[i].content]; [wkTokens addObject:token.get()]; } return adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:itemIdentifier tokens:wkTokens.get()]); } TEST(TextManipulation, CompleteTextManipulationReplaceSimpleSingleParagraph) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    helllo, wooorld

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("helllo, wooorld", items[0].tokens[0].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello, world" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hello, world

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, LegacyCompleteTextManipulationReplaceSimpleSingleParagraph) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    helllo, wooorld

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("helllo, wooorld", items[0].tokens[0].content.UTF8String); done = false; [webView _completeTextManipulation:(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello, world" }, }) completion:^(BOOL success) { EXPECT_TRUE(success); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hello, world

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationDisgardsTokens) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    hello, world. WebKit

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 4UL); EXPECT_STREQ("hello, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("world", items[0].tokens[1].content.UTF8String); EXPECT_STREQ(". ", items[0].tokens[2].content.UTF8String); EXPECT_STREQ("WebKit", items[0].tokens[3].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello, " }, { items[0].tokens[3].identifier, @"WebKit" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hello, WebKit

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationReplaceTwoSimpleParagraphs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hello

    world"]; auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello", items[0].tokens[0].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("world", items[1].tokens[0].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Hello" }}).get(), createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"World" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    Hello

    World", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationReplaceMultipleSimpleParagraphs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    helllo, wooorld

    hey, Kits is cuuute

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("helllo, wooorld", items[0].tokens[0].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 4UL); EXPECT_STREQ("hey, ", items[1].tokens[0].content.UTF8String); EXPECT_STREQ("Kits", items[1].tokens[1].content.UTF8String); EXPECT_STREQ(" is ", items[1].tokens[2].content.UTF8String); EXPECT_STREQ("cuuute", items[1].tokens[3].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"Hello, " }, { items[1].tokens[1].identifier, @"kittens" }, { items[1].tokens[2].identifier, @" are " }, { items[1].tokens[3].identifier, @"cute" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    helllo, wooorld

    Hello, kittens are cute

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello, world." }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    Hello, world.

    Hello, kittens are cute

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationReplaceMultipleSimpleParagraphsAtOnce) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    helllo, wooorld

    hey, Kits is cuuute

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("helllo, wooorld", items[0].tokens[0].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 4UL); EXPECT_STREQ("hey, ", items[1].tokens[0].content.UTF8String); EXPECT_STREQ("Kits", items[1].tokens[1].content.UTF8String); EXPECT_STREQ(" is ", items[1].tokens[2].content.UTF8String); EXPECT_STREQ("cuuute", items[1].tokens[3].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"Hello, " }, { items[1].tokens[1].identifier, @"kittens" }, { items[1].tokens[2].identifier, @" are " }, { items[1].tokens[3].identifier, @"cute" }, }), (_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello, world." }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    Hello, world.

    Hello, kittens are cute

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationReplaceMultipleSimpleParagraphsSeparatedByBR) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    helllo, wooorld
    webKit

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("helllo, wooorld", items[0].tokens[0].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("webKit", items[1].tokens[0].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello, World" }, }), (_WKTextManipulationItem *)createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"WebKit" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    Hello, World
    WebKit

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationReplaceParagraphsSeparatedByWrappedBR) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    earth, hey
    webKit

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("earth, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("hey", items[0].tokens[1].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("webKit", items[1].tokens[0].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[1].identifier, @"Hello, " }, { items[0].tokens[0].identifier, @"World" }, }), (_WKTextManipulationItem *)createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"WebKit" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    Hello, World
    WebKit

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationFailWhenBRIsInserted) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    helllo, worrld

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("helllo, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("worrld", items[0].tokens[1].content.UTF8String); [webView stringByEvaluatingJavaScript:@"document.querySelector('b').before(document.createElement('br'))"]; done = false; __block auto item = createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello, " }, { items[0].tokens[1].identifier, @"World" }, }); [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorContentChanged); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], item.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    helllo,
    worrld

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationAvoidCrashingWhenContentIsRemoved) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadTestPageNamed:@"simple"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); auto *tokens = items[0].tokens; EXPECT_EQ(tokens.count, 1UL); __block bool done = false; [webView performAfterReceivingMessage:@"DoneRemovingParagraph" action:^{ done = true; }]; [webView stringByEvaluatingJavaScript: @"const paragraph = document.createElement('p');" "paragraph.textContent = 'Hello world';" "document.body.appendChild(paragraph);" "setTimeout(() => { paragraph.remove(); webkit.messageHandlers.testHandler.postMessage('DoneRemovingParagraph') })"]; TestWebKitAPI::Util::run(&done); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { tokens[0].identifier, @"Simple HTML file!" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("Simple HTML file!", [webView stringByEvaluatingJavaScript:@"document.body.textContent"]); } TEST(TextManipulation, CompleteTextManipulationShouldPreserveImagesAsExcludedTokens) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"
    hello, world
    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); auto *tokens = items[0].tokens; EXPECT_EQ(tokens.count, 3UL); EXPECT_STREQ("hello, ", tokens[0].content.UTF8String); EXPECT_FALSE(tokens[0].isExcluded); EXPECT_STREQ("[]", tokens[1].content.UTF8String); EXPECT_TRUE(tokens[1].isExcluded); EXPECT_STREQ(" world", tokens[2].content.UTF8String); EXPECT_FALSE(tokens[2].isExcluded); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { tokens[0].identifier, @"this is " }, { tokens[1].identifier, nil }, { tokens[2].identifier, @" a test" } })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("
    this is a test
    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationShouldPreserveSVGAsExcludedTokens) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "
    hello" "" "webkit
    " "

    world

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); auto *tokens = items[0].tokens; EXPECT_EQ(tokens.count, 3UL); EXPECT_STREQ("hello", tokens[0].content.UTF8String); EXPECT_FALSE(tokens[0].isExcluded); EXPECT_STREQ("[]", tokens[1].content.UTF8String); EXPECT_TRUE(tokens[1].isExcluded); EXPECT_STREQ("webkit", tokens[2].content.UTF8String); EXPECT_FALSE(tokens[2].isExcluded); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("world", items[1].tokens[0].content.UTF8String); EXPECT_FALSE(items[1].tokens[0].isExcluded); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { tokens[0].identifier, @"hey" }, { tokens[1].identifier, nil }, { tokens[2].identifier, @"WebKit" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("
    heyWebKit

    world

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationShouldPreserveOrderOfBlockImage) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "
    helllo world
    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("helllo world", items[0].tokens[0].content.UTF8String); EXPECT_TRUE(!items[0].tokens[0].isExcluded); done = false; [webView _completeTextManipulationForItems:@[ (_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello, world" } }), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("" "
    hello, world
    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationShouldReplaceAttributeContent) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"hey" "
    hello\"fruit\"
    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 4UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hey", items[0].tokens[0].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("this is greet", items[1].tokens[0].content.UTF8String); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_STREQ("fruit", items[2].tokens[0].content.UTF8String); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_STREQ("hello", items[3].tokens[0].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[ (_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello" } }), (_WKTextManipulationItem *)createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"This is a greeting" } }), (_WKTextManipulationItem *)createItem(items[2].identifier, { { items[2].tokens[0].identifier, @"Apple" } }), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("Hello
    hello" "\"Apple\"
    ", [webView stringByEvaluatingJavaScript:@"document.documentElement.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationShouldReplaceContentFollowedAfterImageInCSSTable) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "
    " "
    hello world
    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello world", items[0].tokens[0].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[ (_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello, world" } }), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("
    " "
    hello, world
    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationShouldReplaceTextContentWithMultipleTokens) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "This is a test" "" "Text" "image" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 6UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_EQ(items[4].tokens.count, 1UL); EXPECT_EQ(items[5].tokens.count, 1UL); EXPECT_WK_STREQ("This is a test", items[0].tokens[0].content); EXPECT_WK_STREQ("Hello world", items[1].tokens[0].content); EXPECT_WK_STREQ("Should not be replaced", items[2].tokens[0].content); EXPECT_WK_STREQ("label", items[3].tokens[0].content); EXPECT_WK_STREQ("image", items[4].tokens[0].content); EXPECT_WK_STREQ("Text", items[5].tokens[0].content); auto replacementItems = retainPtr(@[ createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Replacement" }, { items[0].tokens[0].identifier, @"title" } }).get(), createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"Replacement" }, { items[1].tokens[0].identifier, @"option" } }).get(), createItem(items[2].identifier, { { items[2].tokens[0].identifier, @"Failed replacement" }, { @"12345", @"option" } }).get(), createItem(items[3].identifier, { { items[3].tokens[0].identifier, @"Replacement" }, { items[3].tokens[0].identifier, @"label" } }).get(), createItem(items[4].identifier, { { items[4].tokens[0].identifier, @"Replacement" }, { items[4].tokens[0].identifier, @"image" } }).get(), ]); done = false; [webView _completeTextManipulationForItems:replacementItems.get() completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorInvalidToken); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], [replacementItems objectAtIndex:2]); done = true; }]; TestWebKitAPI::Util::run(&done); NSArray *options = [webView objectByEvaluatingJavaScript:@"Array.from(document.querySelector('select').options).map(option => option.textContent)"]; EXPECT_WK_STREQ("Replacement option", options.firstObject); EXPECT_WK_STREQ("Should not be replaced", options.lastObject); EXPECT_WK_STREQ("Replacement title", [webView stringByEvaluatingJavaScript:@"document.title"]); EXPECT_WK_STREQ("Replacement label", [webView objectByEvaluatingJavaScript:@"document.querySelector('span').getAttribute('aria-label')"]); EXPECT_WK_STREQ("Replacement image", [webView objectByEvaluatingJavaScript:@"document.querySelector('img').getAttribute('alt')"]); } TEST(TextManipulation, CompleteTextManipulationShouldReplaceContentsAroundParagraphWithJustImage) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"
    heeey
    woorld"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("heeey", items[0].tokens[0].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("woorld", items[1].tokens[0].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[ (_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello" } }), (_WKTextManipulationItem *)createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"world" } }), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("
    hello
    world", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationShouldBatchItemCallback) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@""]; [webView stringByEvaluatingJavaScript:@"html = ''; for (let i = 0; i < 2000; ++i) html += `

    hello ${i}

    `; document.body.innerHTML = html;"]; done = false; __block unsigned itemListCallbackCount = 0; delegate.get().itemListCallback = ^(NSArray<_WKTextManipulationItem *> *items) { itemListCallbackCount++; }; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_GE(itemListCallbackCount, 2UL); auto *items = [delegate items]; EXPECT_EQ(items.count, 2000UL); for (unsigned i = 0; i < 2000; ++i) { EXPECT_EQ(items[i].tokens.count, 1UL); EXPECT_WK_STREQ(makeString("hello ", i), items[i].tokens[0].content.UTF8String); } } TEST(TextManipulation, CompleteTextManipulationReordersContent) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    cats, I are

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 4UL); EXPECT_STREQ("cats", items[0].tokens[0].content.UTF8String); EXPECT_STREQ(", ", items[0].tokens[1].content.UTF8String); EXPECT_STREQ("I", items[0].tokens[2].content.UTF8String); EXPECT_STREQ(" are", items[0].tokens[3].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[2].identifier, @"I" }, { items[0].tokens[3].identifier, @"'m a " }, { items[0].tokens[0].identifier, @"cat" }, }).get()] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    I'm a cat

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationCanSplitContent) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    hello world WebKit

    "]; [webView stringByEvaluatingJavaScript:@"paragraph.firstChild.addEventListener('click', () => window.didClick = true)"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("hello world", items[0].tokens[0].content.UTF8String); EXPECT_STREQ(" WebKit", items[0].tokens[1].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello" }, { items[0].tokens[1].identifier, @" WebKit " }, { items[0].tokens[0].identifier, @"world" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hello WebKit world

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"didClick = false; paragraph.firstChild.click(); didClick"].boolValue); EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"didClick = false; paragraph.lastChild.click(); didClick"].boolValue); } TEST(TextManipulation, CompleteTextManipulationCanMergeContent) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hello world WebKit

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 3UL); EXPECT_STREQ("hello ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("world", items[0].tokens[1].content.UTF8String); EXPECT_STREQ(" WebKit", items[0].tokens[2].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello " }, { items[0].tokens[2].identifier, @"world" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hello world

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationFailWhenItemIdentifierIsDuplicated) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hello, world

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("hello, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("world", items[0].tokens[1].content.UTF8String); done = false; auto firstItem = createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"done" } }); __block auto secondItem = createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"bad" } }); [webView _completeTextManipulationForItems:@[firstItem.get(), secondItem.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorInvalidItem); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], secondItem.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    done

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationCanHandleSubsetOfItemsToFail) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hey, dude

    this is bad

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("hey, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("dude", items[0].tokens[1].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 2UL); EXPECT_STREQ("this is ", items[1].tokens[0].content.UTF8String); EXPECT_STREQ("bad", items[1].tokens[1].content.UTF8String); done = false; __block auto firstItem = createItem(items[0].identifier, { { items[1].tokens[0].identifier, @"bad" } }); auto secondItem = createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"good" } }); [webView _completeTextManipulationForItems:@[firstItem.get(), secondItem.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorInvalidToken); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], firstItem.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hey, dude

    good

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationFailWhenContentIsChanged) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "

    what time are they now?

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 3UL); EXPECT_STREQ("what ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("time", items[0].tokens[1].content.UTF8String); EXPECT_STREQ(" are they now?", items[0].tokens[2].content.UTF8String); [webView stringByEvaluatingJavaScript:@"document.querySelector('em').nextSibling.data = ' is it now in London?'"]; done = false; __block auto item = createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"What " }, { items[0].tokens[1].identifier, @"time" }, { items[0].tokens[2].identifier, @" is it now?" }, }); [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorContentChanged); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], item.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    what time is it now in London?

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationFailWhenContentIsRemoved) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hello, world

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello, world", items[0].tokens[0].content.UTF8String); [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = 'new content'"]; done = false; __block auto item = createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hey" }, }); [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorContentChanged); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], item.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("new content", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationFailWhenContentIsAdded) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hello, world

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_STREQ("hello, world", items[0].tokens[0].content.UTF8String); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 3) done = true; }; [webView stringByEvaluatingJavaScript:@"document.querySelector('p').innerHTML = 'hello, world bye';" "document.body.appendChild(document.createElement('div')).innerHTML = 'end'"]; TestWebKitAPI::Util::run(&done); done = false; __block auto item = createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Hello, World" }}); [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorContentChanged); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], item.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hello, world \n bye

    end
    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationSuccedsWhenContentOutOfParagraphIsAdded) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hello world

    "]; RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello world", items[0].tokens[0].content); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 2) done = true; }; [webView stringByEvaluatingJavaScript:@"var element = document.createElement('span');" "element.innerHTML='inserted';" "document.querySelector('u').before(element)"]; TestWebKitAPI::Util::run(&done); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Hello World" }}).get(), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    Hello Worldinserted

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationFailWhenDocumentHasBeenNavigatedAway) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadTestPageNamed:@"simple"]; [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = '

    hey, earth

    '"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("hey, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("earth", items[0].tokens[1].content.UTF8String); [webView synchronouslyLoadTestPageNamed:@"copy-html"]; [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = '

    hey, earth

    '"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); done = false; __block auto item = createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"hello, " }, { items[0].tokens[1].identifier, @"world" }, }); [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorInvalidItem); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], item.get()); done = true; }]; TestWebKitAPI::Util::run(&done); } TEST(TextManipulation, CompleteTextManipulationFailWhenExclusionIsViolated) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadTestPageNamed:@"simple"]; [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = '

    hi, WebKitten bye

    '"]; RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); [configuration setExclusionRules:@[ adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:(BOOL)YES forElement:@"em"]).get(), ]]; done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 3UL); EXPECT_STREQ("hi, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("WebKitten", items[0].tokens[1].content.UTF8String); EXPECT_STREQ(" bye", items[0].tokens[2].content.UTF8String); done = false; __block auto item = createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello," }, { items[0].tokens[1].identifier, @"WebKit" }, { items[0].tokens[2].identifier, @"Bye" }, }); [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorExclusionViolation); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], item.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hi, WebKitten bye

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationFailWhenExcludedContentAppearsMoreThanOnce) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadTestPageNamed:@"simple"]; [webView stringByEvaluatingJavaScript:@"document.body.innerHTML = '

    hi, WebKitten bye

    '"]; RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); [configuration setExclusionRules:@[ adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:(BOOL)YES forElement:@"em"]).get(), ]]; done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 3UL); EXPECT_STREQ("hi, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("WebKitten", items[0].tokens[1].content.UTF8String); EXPECT_STREQ(" bye", items[0].tokens[2].content.UTF8String); done = false; __block auto item = createItem(items[0].identifier, { { items[0].tokens[1].identifier, nil }, { items[0].tokens[0].identifier, @"Hello," }, { items[0].tokens[1].identifier, nil }, { items[0].tokens[2].identifier, @"Bye" }, }); [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorExclusionViolation); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], item.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    hi, WebKitten bye

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationPreservesExcludedContent) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hi, WebKitten bye

    "]; RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); [configuration setExclusionRules:@[ adoptNS([[_WKTextManipulationExclusionRule alloc] initExclusion:(BOOL)YES forElement:@"em"]).get(), ]]; done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 3UL); EXPECT_STREQ("hi, ", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("WebKitten", items[0].tokens[1].content.UTF8String); EXPECT_STREQ(" bye", items[0].tokens[2].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello, " }, { items[0].tokens[1].identifier, nil }, { items[0].tokens[2].identifier, @" Bye" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    Hello, WebKitten Bye

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationDoesNotCreateMoreTextManipulationItems) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    Foo bar baz

    "]; auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); auto* firstItem = items.firstObject; EXPECT_EQ(firstItem.tokens.count, 3UL); __block BOOL foundNewItemAfterCompletingTextManipulation = false; [delegate setItemCallback:^(_WKTextManipulationItem *) { foundNewItemAfterCompletingTextManipulation = true; }]; done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(firstItem.identifier, { { firstItem.tokens[0].identifier, @"bar " }, { firstItem.tokens[1].identifier, @"garply" }, { firstItem.tokens[2].identifier, @" foo" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); [webView waitForNextPresentationUpdate]; EXPECT_FALSE(foundNewItemAfterCompletingTextManipulation); EXPECT_WK_STREQ("

    bar garply foo

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationCorrectParagraphRange) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"
    "]; RetainPtr<_WKTextManipulationConfiguration> configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("holle", items[0].tokens[0].content); EXPECT_WK_STREQ("wdrlo", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"hello" }}).get(), createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"world" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("
    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationCanMergeContentAndPreserveLineBreaks) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "one two " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 4UL); EXPECT_STREQ("one", items[0].tokens[0].content.UTF8String); EXPECT_STREQ(" \n ", items[0].tokens[1].content.UTF8String); EXPECT_STREQ("two", items[0].tokens[2].content.UTF8String); EXPECT_STREQ(" \n", items[0].tokens[3].content.UTF8String); auto replacementItems = retainPtr(@[ createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"ONE" }, { items[0].tokens[1].identifier, items[0].tokens[1].content }, { items[0].tokens[2].identifier, @"TWO" }, { items[0].tokens[3].identifier, items[0].tokens[3].content } }).get(), ]); done = false; [webView _completeTextManipulationForItems:replacementItems.get() completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("ONE \n TWO \n", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationIgnoreWhiteSpacesBetweenParagraphs) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" ""]; auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("world", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Hello" }}).get(), createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"World" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); } TEST(TextManipulation, CompleteTextManipulationShouldPreserveNodesAfterParagraphRange) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "

    helloworld

    from

    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_STREQ("hello", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("world", items[0].tokens[1].content.UTF8String); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_STREQ("from", items[1].tokens[0].content.UTF8String); auto replacementItems = retainPtr(@[ createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello" }, { items[0].tokens[1].identifier, @"World" } }).get(), createItem(items[1].identifier, { { items[1].tokens[0].identifier, @"From WebKit" } }).get(), ]); done = false; [webView _completeTextManipulationForItems:replacementItems.get() completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    HelloWorld

    From WebKit

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationSPreserveNodesBeforeParagraphRange) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "



    helloworld
    from

    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 3UL); EXPECT_STREQ("hello", items[0].tokens[0].content.UTF8String); EXPECT_STREQ("world", items[0].tokens[1].content.UTF8String); EXPECT_STREQ("from", items[0].tokens[2].content.UTF8String); auto replacementItems = retainPtr(@[ createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Hello" }, { items[0].tokens[1].identifier, @"World" }, { items[0].tokens[2].identifier, @"From WebKit" } }).get() ]); done = false; [webView _completeTextManipulationForItems:replacementItems.get() completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("



    HelloWorld
    From WebKit

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, InsertingContentIntoAlreadyManipulatedContentCreatesTextManipulationItem) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    hey dude

    "]; auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); auto* firstItem = items.firstObject; EXPECT_EQ(firstItem.tokens.count, 2UL); EXPECT_STREQ("hey", firstItem.tokens[0].content.UTF8String); EXPECT_STREQ(" dude", firstItem.tokens[1].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(firstItem.identifier, { { firstItem.tokens[0].identifier, @"hello," }, { firstItem.tokens[1].identifier, @" world" }, })] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; [delegate setItemCallback:^(_WKTextManipulationItem *) { done = true; }]; [webView stringByEvaluatingJavaScript:@"span = document.createElement('span'); span.textContent = ' WebKit!'; document.querySelector('b').after(span);"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items.count, 2UL); EXPECT_WK_STREQ("

    hello, WebKit! world

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationInButtonsAndTextFields) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@""]; auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]); done = false; [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); auto* firstItem = items.firstObject; auto* lastItem = items.lastObject; EXPECT_EQ(firstItem.tokens.count, 1UL); EXPECT_EQ(lastItem.tokens.count, 1UL); EXPECT_STREQ("hello1", firstItem.tokens[0].content.UTF8String); EXPECT_STREQ("hello2", lastItem.tokens[0].content.UTF8String); done = false; [webView _completeTextManipulationForItems:@[ createItem(firstItem.identifier, {{ firstItem.tokens[0].identifier, @"world1" }}).get(), createItem(lastItem.identifier, {{ lastItem.tokens[0].identifier, @"world2" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("world1", [webView stringByEvaluatingJavaScript:@"document.querySelector('input[type=text]').value"]); EXPECT_WK_STREQ("world2", [webView stringByEvaluatingJavaScript:@"document.querySelector('input[type=submit]').value"]); } TEST(TextManipulation, CompleteTextManipulationForNewlyDisplayedParagraph) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "
    " "webkit!" "" "hello world" "" "" "
    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_WK_STREQ("webkit!", items[0].tokens[0].content); EXPECT_WK_STREQ("hello world", items[0].tokens[1].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"WebKit!" }, { items[0].tokens[1].identifier, @"Hello World" }}).get(), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { done = true; }; [webView stringByEvaluatingJavaScript:@"document.querySelector('i').removeAttribute('class');"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("bye", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"Bye" }}).get(), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("WebKit!Hello WorldBye", [webView stringByEvaluatingJavaScript:@"document.querySelector('div').innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationForNewlyDisplayedText) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "" "hello world" "" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello world", items[0].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Hello World" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("Hello World", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 2) done = true; }; NSString *scriptString = @"document.querySelector('a').innerHTML=''; document.querySelector('a').innerHTML='hello world again';"; [webView evaluateJavaScript:scriptString completionHandler:nil]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("hello world again", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"Hello World Again" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("Hello World Again", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationForManipulatedTextWithNewContent) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "hello world" "

    hello webkit

    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello world", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("hello webkit", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Hello World" }}).get(), createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"Hello WebKit" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("Hello World

    Hello WebKit

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 4) done = true; }; [webView stringByEvaluatingJavaScript:@"document.querySelector('span').innerHTML='hello world again';"]; [webView stringByEvaluatingJavaScript:@"document.querySelector('p').childNodes[0].nodeValue='hello webkit again';"]; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_WK_STREQ("hello world again", items[2].tokens[0].content); EXPECT_EQ(items[3].tokens.count, 1UL); EXPECT_WK_STREQ("hello webkit again", items[3].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[2].identifier, {{ items[2].tokens[0].identifier, @"Hello World Again" }}).get(), createItem(items[3].identifier, {{ items[3].tokens[0].identifier, @"Hello WebKit Again" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("Hello World Again

    Hello WebKit Again

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationForTitleElement) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 0UL); done = false; [webView stringByEvaluatingJavaScript:@"document.title = 'page title'"]; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 1) done = true; }; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("page title", items[0].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Page Title" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("Page Title", [webView stringByEvaluatingJavaScript:@"document.title"]); EXPECT_WK_STREQ("Page Title", [webView stringByEvaluatingJavaScript:@"document.documentElement.innerHTML"]); done = false; [webView stringByEvaluatingJavaScript:@"document.title = 'new page title'"]; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { if (items.count == 2) done = true; }; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("new page title", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"New Page Title" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("New Page Title", [webView stringByEvaluatingJavaScript:@"document.title"]); EXPECT_WK_STREQ("New Page Title", [webView stringByEvaluatingJavaScript:@"document.documentElement.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationAvoidExtractingManipulatedTextAfterManipulation) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"

    foo
    bar

    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("foo", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ("bar", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"FOO" }}).get(), createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"BAR" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("

    FOO
    BAR

    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); [webView objectByEvaluatingJavaScript:@"document.querySelector('p').style.display = 'none'"]; [webView objectByEvaluatingJavaScript:@"document.querySelector('p').style.display = ''"]; [webView stringByEvaluatingJavaScript:@"var element = document.createElement('span');" "element.innerHTML='end';" "document.querySelector('p').after(element)"]; done = false; delegate.get().itemCallback = ^(_WKTextManipulationItem *item) { done = true; }; TestWebKitAPI::Util::run(&done); EXPECT_EQ(items.count, 3UL); EXPECT_EQ(items[2].tokens.count, 1UL); EXPECT_WK_STREQ("end", items[2].tokens[0].content); } TEST(TextManipulation, CompleteTextManipulationAddsOverflowHiddenToAvoidBreakingLayout) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "" " " "" "" " " " A Hello world" " " "" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("A", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ(" Hello world", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"This is a long translation" }}).get(), createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @" Foo bar" }}).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("This is a long translation Foo bar", [webView stringByEvaluatingJavaScript:@"document.querySelector('a').textContent.trim()"]); EXPECT_WK_STREQ("hidden", [webView stringByEvaluatingJavaScript:@"getComputedStyle(document.querySelector('span')).overflowX"]); EXPECT_WK_STREQ("auto", [webView stringByEvaluatingJavaScript:@"getComputedStyle(document.querySelector('span')).overflowY"]); } TEST(TextManipulation, CompleteTextManipulationParagraphsContainCollapsedSpaces) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "" "" "" " hello" " world" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello", items[0].tokens[0].content); EXPECT_EQ(items[1].tokens.count, 1UL); EXPECT_WK_STREQ(" world", items[1].tokens[0].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Hello" }}).get(), createItem(items[1].identifier, {{ items[1].tokens[0].identifier, @"World" }}).get(), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("HelloWorld", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationParagraphContainsCollapsedSpaces) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "
    " "webkit" " and " "apple" "
    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 3UL); EXPECT_WK_STREQ("webkit", items[0].tokens[0].content); EXPECT_WK_STREQ(" and ", items[0].tokens[1].content); EXPECT_WK_STREQ("apple", items[0].tokens[2].content); done = false; [webView evaluateJavaScript:@"document.getElementById('div').style.width='9ch';" completionHandler:^(id result, NSError *) { done = true; }]; done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"WebKit" }, { items[0].tokens[1].identifier, @" And " }, { items[0].tokens[2].identifier, @"Apple" } }).get() ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationShouldReplaceContentIgnoredByEditing) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "
    " "images" "link" "" "" "
    " ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_WK_STREQ("images", items[0].tokens[0].content); EXPECT_WK_STREQ("link", items[0].tokens[1].content); done = false; [webView _completeTextManipulationForItems:@[ createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Images" }, { items[0].tokens[1].identifier, @"Link" } }).get(), ] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("
    ImagesLink
    ", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationShouldOnlyChangeNodesInParagraphRange) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "" "" "" "zero two four" "one" "three" ""]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto *items = [delegate items]; EXPECT_EQ(items.count, 2UL); EXPECT_EQ(items[0].tokens.count, 2UL); EXPECT_WK_STREQ("zero", items[0].tokens[0].content); EXPECT_WK_STREQ(" \n", items[0].tokens[1].content); EXPECT_EQ(items[1].tokens.count, 3UL); EXPECT_WK_STREQ("two four", items[1].tokens[0].content); EXPECT_WK_STREQ("one", items[1].tokens[1].content); EXPECT_WK_STREQ("three", items[1].tokens[2].content); done = false; [webView _completeTextManipulationForItems:@[(_WKTextManipulationItem *)createItem(items[1].identifier, { { items[1].tokens[1].identifier, @"one" }, { items[1].tokens[0].identifier, @"two" }, { items[1].tokens[2].identifier, @"three" }, { items[1].tokens[0].identifier, @"four" } }).get()] completion:^(NSArray *errors) { EXPECT_EQ(errors, nil); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("zero \nonetwothreefour", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationParagraphBecomesHidden) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"" "" "hello"]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 1UL); EXPECT_WK_STREQ("hello", items[0].tokens[0].content); done = false; [webView evaluateJavaScript:@"document.querySelector('span').classList.add('hidden');" completionHandler:^(id result, NSError *) { EXPECT_NULL(result); done = true; }]; done = false; __block auto item = createItem(items[0].identifier, {{ items[0].tokens[0].identifier, @"Hello" }}); [webView _completeTextManipulationForItems:@[item.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 1UL); EXPECT_EQ(errors.firstObject.domain, _WKTextManipulationItemErrorDomain); EXPECT_EQ(errors.firstObject.code, _WKTextManipulationItemErrorContentChanged); EXPECT_EQ(errors.firstObject.userInfo[_WKTextManipulationItemErrorItemKey], item.get()); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("hello", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); } TEST(TextManipulation, CompleteTextManipulationSkipsEmptyContainers) { auto delegate = adoptNS([[TextManipulationDelegate alloc] init]); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); [webView _setTextManipulationDelegate:delegate.get()]; [webView synchronouslyLoadHTMLString:@"
    Guten
    " "
    "]; done = false; [webView _startTextManipulationsWithConfiguration:nil completion:^{ done = true; }]; TestWebKitAPI::Util::run(&done); auto items = [delegate items]; EXPECT_EQ(items.count, 1UL); EXPECT_EQ(items[0].tokens.count, 3UL); EXPECT_WK_STREQ("Guten", items[0].tokens[0].content); EXPECT_WK_STREQ("[]", items[0].tokens[1].content); EXPECT_WK_STREQ("Tag", items[0].tokens[2].content); auto replacement = createItem(items[0].identifier, { { items[0].tokens[0].identifier, @"Good" }, { items[0].tokens[2].identifier, @"Day" } }); done = false; [webView _completeTextManipulationForItems:@[replacement.get()] completion:^(NSArray *errors) { EXPECT_EQ(errors.count, 0UL); done = true; }]; TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ("SECTION", [webView stringByEvaluatingJavaScript:@"document.getElementById('target').parentElement.tagName"]); NSArray *textContents = [webView objectByEvaluatingJavaScript:@"Array.from(document.links).map(a => a.textContent)"]; EXPECT_EQ(textContents.count, 2U); EXPECT_WK_STREQ("Good", textContents.firstObject); EXPECT_WK_STREQ("Day", textContents.lastObject); } TEST(TextManipulation, TextManipulationTokenDebugDescription) { auto token = adoptNS([[_WKTextManipulationToken alloc] init]); [token setIdentifier:@"foo_is_the_identifier"]; [token setContent:@"bar_is_the_content"]; NSString *description = [token description]; EXPECT_TRUE([description containsString:@"foo_is_the_identifier"]); EXPECT_FALSE([description containsString:@"bar_is_the_content"]); NSString *debugDescription = [token debugDescription]; EXPECT_TRUE([debugDescription containsString:@"foo_is_the_identifier"]); EXPECT_TRUE([debugDescription containsString:@"bar_is_the_content"]); } TEST(TextManipulation, TextManipulationTokenNotEqualToNil) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@"A"]; [tokenA setContent:@"A"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:nil includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:nil includingContentEquality:NO]); [tokenA setIdentifier:nil]; [tokenA setContent:nil]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:nil includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:nil includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenEqualityWithEqualIdentifiers) { auto token1 = adoptNS([[_WKTextManipulationToken alloc] init]); [token1 setIdentifier:@"A"]; auto token2 = adoptNS([[_WKTextManipulationToken alloc] init]); [token2 setIdentifier:@"A"]; EXPECT_TRUE([token1 isEqualToTextManipulationToken:token2.get() includingContentEquality:YES]); EXPECT_TRUE([token1 isEqualToTextManipulationToken:token2.get() includingContentEquality:NO]); // Same identifiers, different content. [token1 setContent:@"1"]; [token2 setContent:@"2"]; EXPECT_FALSE([token1 isEqualToTextManipulationToken:token2.get() includingContentEquality:YES]); EXPECT_TRUE([token1 isEqualToTextManipulationToken:token2.get() includingContentEquality:NO]); // Same identifiers, different exclusion. [token1 setExcluded:NO]; [token2 setExcluded:YES]; [token1 setContent:nil]; [token2 setContent:nil]; EXPECT_FALSE([token1 isEqualToTextManipulationToken:token2.get() includingContentEquality:YES]); EXPECT_FALSE([token1 isEqualToTextManipulationToken:token2.get() includingContentEquality:NO]); // Same identifiers, different exclusion and different content. [token1 setContent:@"1"]; [token2 setContent:@"2"]; EXPECT_FALSE([token1 isEqualToTextManipulationToken:token2.get() includingContentEquality:YES]); EXPECT_FALSE([token1 isEqualToTextManipulationToken:token2.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenEqualityWithDifferentIdentifiers) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@"A"]; auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"B"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); // Different identifiers, same content. [tokenA setContent:@"content"]; [tokenB setContent:@"content"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); // Different identifiers, same exclusion. [tokenA setContent:nil]; [tokenB setContent:nil]; [tokenA setExcluded:YES]; [tokenB setExcluded:YES]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); // Different identifiers, same content and same exclusion. [tokenA setContent:@"content"]; [tokenB setContent:@"content"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenEqualityWithNilIdentifiers) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); EXPECT_NULL([tokenA identifier]); auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"B"]; auto tokenC = adoptNS([[_WKTextManipulationToken alloc] init]); EXPECT_NULL([tokenC identifier]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:NO]); // Equal content. [tokenA setContent:@"content"]; [tokenB setContent:@"content"]; [tokenC setContent:@"content"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:NO]); // Different content. [tokenA setContent:@"contentA"]; [tokenB setContent:@"contentB"]; [tokenC setContent:@"contentC"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenEqualityWithEmptyIdentifiers) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@""]; auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"B"]; auto tokenC = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenC setIdentifier:@""]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:NO]); // Equal content. [tokenA setContent:@"content"]; [tokenB setContent:@"content"]; [tokenC setContent:@"content"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:NO]); // Different content. [tokenA setContent:@"contentA"]; [tokenB setContent:@"contentB"]; [tokenC setContent:@"contentC"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenWithNilContent) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@"A"]; auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"A"]; EXPECT_NULL([tokenA content]); EXPECT_NULL([tokenB content]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); [tokenB setContent:@""]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); [tokenB setContent:@"B"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenWithEmptyContent) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@"A"]; [tokenA setContent:@""]; auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"A"]; [tokenB setContent:@""]; EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); [tokenB setContent:nil]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); [tokenB setContent:@"B"]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenWithIdenticalContent) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@"A"]; [tokenA setContent:@"content"]; auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"A"]; [tokenB setContent:@"content"]; EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenWithPointerEqualContent) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@"A"]; auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"A"]; NSString *contentString = @"content"; [tokenA setContent:contentString]; [tokenB setContent:contentString]; EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenWithTrailingSpace) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@"A"]; [tokenA setContent:@"content"]; auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"A"]; [tokenB setContent:@"content "]; EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationTokenEqualToSelf) { auto token = adoptNS([[_WKTextManipulationToken alloc] init]); [token setIdentifier:@"A"]; [token setContent:@"content"]; EXPECT_TRUE([token isEqualToTextManipulationToken:token.get() includingContentEquality:YES]); EXPECT_TRUE([token isEqualToTextManipulationToken:token.get() includingContentEquality:NO]); EXPECT_TRUE([token isEqual:token.get()]); } TEST(TextManipulation, TextManipulationTokenNSObjectEqualityWithOtherToken) { auto tokenA = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenA setIdentifier:@"A"]; [tokenA setContent:@"content"]; auto tokenB = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenB setIdentifier:@"A"]; [tokenB setContent:@"content"]; auto tokenC = adoptNS([[_WKTextManipulationToken alloc] init]); [tokenC setIdentifier:@"A"]; [tokenC setContent:@"content "]; EXPECT_TRUE([tokenA isEqualToTextManipulationToken:tokenB.get() includingContentEquality:YES]); EXPECT_TRUE([tokenA isEqual:tokenB.get()]); EXPECT_FALSE([tokenA isEqualToTextManipulationToken:tokenC.get() includingContentEquality:YES]); EXPECT_FALSE([tokenA isEqual:tokenC.get()]); } TEST(TextManipulation, TextManipulationTokenNSObjectEqualityWithNonToken) { auto token = adoptNS([[_WKTextManipulationToken alloc] init]); [token setIdentifier:@"A"]; [token setContent:@"content"]; NSString *string = @"content"; EXPECT_FALSE([token isEqual:string]); EXPECT_FALSE([token isEqual:nil]); } static RetainPtr<_WKTextManipulationToken> createTextManipulationToken(NSString *identifier, BOOL excluded, NSString *content) { auto token = adoptNS([[_WKTextManipulationToken alloc] init]); [token setIdentifier:identifier]; [token setExcluded:excluded]; [token setContent:content]; return token; } TEST(TextManipulation, TextManipulationItemDebugDescription) { auto tokenA = createTextManipulationToken(@"public_identifier_A", NO, @"private_content_A"); auto tokenB = createTextManipulationToken(@"public_identifier_B", NO, @"private_content_B"); auto item = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"public_item_identifier" tokens:@[ tokenA.get(), tokenB.get() ]]); NSString *debugDescription = [item debugDescription]; EXPECT_TRUE([debugDescription containsString:@"public_identifier_A"]); EXPECT_TRUE([debugDescription containsString:@"public_identifier_B"]); EXPECT_TRUE([debugDescription containsString:@"private_content_A"]); EXPECT_TRUE([debugDescription containsString:@"private_content_B"]); EXPECT_TRUE([debugDescription containsString:@"public_item_identifier"]); NSString *description = [item description]; EXPECT_TRUE([description containsString:@"public_identifier_A"]); EXPECT_TRUE([description containsString:@"public_identifier_B"]); EXPECT_FALSE([description containsString:@"private_content_A"]); EXPECT_FALSE([description containsString:@"private_content_B"]); EXPECT_TRUE([description containsString:@"public_item_identifier"]); } TEST(TextManipulation, TextManipulationItemEqualityToNilItem) { auto item = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ ]]); EXPECT_FALSE([item isEqualToTextManipulationItem:nil includingContentEquality:YES]); EXPECT_FALSE([item isEqualToTextManipulationItem:nil includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualityToSelf) { auto token = createTextManipulationToken(@"A", NO, @"token"); auto item = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"B" tokens:@[ token.get() ]]); EXPECT_TRUE([item isEqualToTextManipulationItem:item.get() includingContentEquality:YES]); EXPECT_TRUE([item isEqualToTextManipulationItem:item.get() includingContentEquality:NO]); EXPECT_TRUE([item isEqual:item.get()]); } TEST(TextManipulation, TextManipulationItemBasicEquality) { auto token1 = createTextManipulationToken(@"1", NO, @"token1"); auto token2 = createTextManipulationToken(@"1", NO, @"token1"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ token1.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ token2.get() ]]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemBasicEqualityWithMultipleTokens) { auto tokenA1 = createTextManipulationToken(@"1", NO, @"token1"); auto tokenA2 = createTextManipulationToken(@"2", NO, @"token2"); auto tokenB1 = createTextManipulationToken(@"1", NO, @"token1"); auto tokenB2 = createTextManipulationToken(@"2", NO, @"token2"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenA1.get(), tokenA2.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenB1.get(), tokenB2.get() ]]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualitySimilarTokensWithDifferentContent) { auto token1 = createTextManipulationToken(@"1", NO, @"token1"); auto token2 = createTextManipulationToken(@"1", NO, @"token2"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ token1.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ token2.get() ]]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualityWithOutOfOrderTokens) { auto tokenA1 = createTextManipulationToken(@"1", NO, @"token1"); auto tokenA2 = createTextManipulationToken(@"2", NO, @"token2"); auto tokenB1 = createTextManipulationToken(@"1", NO, @"token1"); auto tokenB2 = createTextManipulationToken(@"2", NO, @"token2"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenA1.get(), tokenA2.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenB2.get(), tokenB1.get() ]]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualityWithPointerEqualTokens) { auto token1 = createTextManipulationToken(@"1", NO, @"token1"); auto token2 = createTextManipulationToken(@"2", NO, @"token2"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ token1.get(), token2.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ token1.get(), token2.get() ]]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualityWithPointerEqualTokenArrays) { auto token1 = createTextManipulationToken(@"1", NO, @"token1"); auto token2 = createTextManipulationToken(@"2", NO, @"token2"); NSArray<_WKTextManipulationToken *> *tokens = @[ token1.get(), token2.get() ]; auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:tokens]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:tokens]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualityWithMismatchedTokenCounts) { auto tokenA1 = createTextManipulationToken(@"1", NO, @"token1"); auto tokenA2 = createTextManipulationToken(@"2", NO, @"token2"); auto tokenA3 = createTextManipulationToken(@"3", NO, @"token3"); auto tokenB1 = createTextManipulationToken(@"1", NO, @"token1"); auto tokenB2 = createTextManipulationToken(@"2", NO, @"token2"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenA1.get(), tokenA2.get(), tokenA3.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenB1.get(), tokenB2.get() ]]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); EXPECT_FALSE([itemB isEqualToTextManipulationItem:itemA.get() includingContentEquality:YES]); EXPECT_FALSE([itemB isEqualToTextManipulationItem:itemA.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualityWithDifferentTokenIdentifiers) { auto tokenA = createTextManipulationToken(@"A", NO, @"token"); auto tokenB = createTextManipulationToken(@"B", NO, @"token"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenA.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenB.get() ]]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualityWithNilIdentifiers) { auto token = createTextManipulationToken(@"A", NO, @"token"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:nil tokens:@[ token.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:nil tokens:@[ token.get() ]]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemEqualityWithDifferentTokenExclusions) { auto tokenA = createTextManipulationToken(@"1", NO, @"token"); auto tokenB = createTextManipulationToken(@"1", YES, @"token"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenA.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenB.get() ]]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); } TEST(TextManipulation, TextManipulationItemNSObjectEqualityWithOtherItem) { auto tokenA1 = createTextManipulationToken(@"1", NO, @"token1"); auto tokenA2 = createTextManipulationToken(@"2", NO, @"token2"); auto tokenB1 = createTextManipulationToken(@"1", NO, @"token1"); auto tokenB2 = createTextManipulationToken(@"2", NO, @"token2"); auto itemA = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenA1.get(), tokenA2.get() ]]); auto itemB = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ tokenB1.get(), tokenB2.get() ]]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); EXPECT_TRUE([itemA isEqual:itemB.get()]); [tokenB2 setContent:@"something else"]; EXPECT_FALSE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:YES]); EXPECT_TRUE([itemA isEqualToTextManipulationItem:itemB.get() includingContentEquality:NO]); EXPECT_FALSE([itemA isEqual:itemB.get()]); } TEST(TextManipulation, TextManipulationItemNSObjectEqualityWithNonToken) { auto token = createTextManipulationToken(@"1", NO, @"token1"); auto item = adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:@"A" tokens:@[ token.get() ]]); NSString *string = @"content"; EXPECT_FALSE([token isEqual:string]); EXPECT_FALSE([token isEqual:nil]); } } // namespace TestWebKitAPI