Title: [277267] trunk/Tools
Revision
277267
Author
cdu...@apple.com
Date
2021-05-10 09:38:00 -0700 (Mon, 10 May 2021)

Log Message

Fix potential races in AppleLanguagesTest.UpdateAppleLanguages API test
https://bugs.webkit.org/show_bug.cgi?id=225429

Reviewed by Darin Adler.

Calling [TestWKWebView performAfterReceivingAnyMessage] registers a MessageHandler which sends an
async WebUserContentController::AddUserScriptMessageHandlers IPC to the WebProcess. We want to
make sure that that message handler is registered in the WebProcess before the JS on the page
calls `webkit.messageHandlers.testHandler.postMessage()`. To address this issue, I moved the
call to `[TestWKWebView performAfterReceivingAnyMessage]` before the call to evaluateJavaScript
that registers the event listener that may call `webkit.messageHandlers.testHandler.postMessage()`.

Also make sure the PreferenceObserver has been allocated after sending the
"NSApplicationDidBecomeActiveNotification" and before proceeding with the test.

I am also adding an extra check at the end to make sure that the value of navigator.language is
correct and to make sure that WKPreferenceObserver.preferenceDidChange was called. This was done
to help diagnose flakiness issues on the bots that I cannot reproduce locally.

* TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm:
(TEST_F):

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (277266 => 277267)


--- trunk/Tools/ChangeLog	2021-05-10 16:35:17 UTC (rev 277266)
+++ trunk/Tools/ChangeLog	2021-05-10 16:38:00 UTC (rev 277267)
@@ -1,3 +1,27 @@
+2021-05-10  Chris Dumez  <cdu...@apple.com>
+
+        Fix potential races in AppleLanguagesTest.UpdateAppleLanguages API test
+        https://bugs.webkit.org/show_bug.cgi?id=225429
+
+        Reviewed by Darin Adler.
+
+        Calling [TestWKWebView performAfterReceivingAnyMessage] registers a MessageHandler which sends an
+        async WebUserContentController::AddUserScriptMessageHandlers IPC to the WebProcess. We want to
+        make sure that that message handler is registered in the WebProcess before the JS on the page
+        calls `webkit.messageHandlers.testHandler.postMessage()`. To address this issue, I moved the
+        call to `[TestWKWebView performAfterReceivingAnyMessage]` before the call to evaluateJavaScript
+        that registers the event listener that may call `webkit.messageHandlers.testHandler.postMessage()`.
+
+        Also make sure the PreferenceObserver has been allocated after sending the
+        "NSApplicationDidBecomeActiveNotification" and before proceeding with the test.
+
+        I am also adding an extra check at the end to make sure that the value of navigator.language is
+        correct and to make sure that WKPreferenceObserver.preferenceDidChange was called. This was done
+        to help diagnose flakiness issues on the bots that I cannot reproduce locally.
+
+        * TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm:
+        (TEST_F):
+
 2021-05-10  Aditya Keerthi  <akeer...@apple.com>
 
         [iPadOS] Do not present custom input peripherals when switching back to a tab with a focused element

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm (277266 => 277267)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm	2021-05-10 16:35:17 UTC (rev 277266)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm	2021-05-10 16:38:00 UTC (rev 277267)
@@ -29,6 +29,8 @@
 
 #import "PlatformUtilities.h"
 #import "TestWKWebView.h"
+#import <WebKit/PreferenceObserver.h>
+#import <wtf/ObjCRuntimeExtras.h>
 #import <wtf/cocoa/VectorCocoa.h>
 #import <wtf/text/StringBuilder.h>
 
@@ -57,7 +59,7 @@
 // On older macOSes, CFPREFS_DIRECT_MODE is disabled and the WebProcess does not see the updated AppleLanguages
 // after the AppleLanguagePreferencesChangedNotification notification.
 // FIXME: This test is currently disabled on Apple Silicon because it times out there (https://bugs.webkit.org/show_bug.cgi?id=222619).
-#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 110000 && CPU(X86_64)
+#if PLATFORM(MAC) && ENABLE(CFPREFS_DIRECT_MODE) && CPU(X86_64)
 class AppleLanguagesTest : public testing::Test {
 public:
     AppleLanguagesTest()
@@ -86,11 +88,51 @@
     String m_savedAppleLanguages;
 };
 
+static IMP sharedInstanceMethodOriginal = nil;
+static IMP preferenceDidChangeMethodOriginal = nil;
+static bool preferenceObserverSharedInstanceCalled = false;
+static bool preferenceObserverPreferenceDidChangeCalled = false;
+
+static WKPreferenceObserver *sharedInstanceMethodOverride(id self, SEL selector)
+{
+    WKPreferenceObserver *observer = wtfCallIMP<WKPreferenceObserver *>(sharedInstanceMethodOriginal, self, selector);
+    preferenceObserverSharedInstanceCalled = true;
+    return observer;
+}
+
+static WKPreferenceObserver *preferenceDidChangeMethodOverride(id self, SEL selector, NSString *domain, NSString *key, NSString *encodedValue)
+{
+    NSLog(@"preferenceDidChangeMethodOverride: domain=%@, key=%@, encodedValue=%@", domain, key, encodedValue);
+    if ([key isEqualToString:@"AppleLanguages"])
+        preferenceObserverPreferenceDidChangeCalled = true;
+    WKPreferenceObserver *observer = wtfCallIMP<WKPreferenceObserver *>(preferenceDidChangeMethodOriginal, self, selector, domain, key, encodedValue);
+    return observer;
+}
+
 TEST_F(AppleLanguagesTest, UpdateAppleLanguages)
 {
+    preferenceObserverSharedInstanceCalled = false;
+    Method sharedInstanceMethod = class_getClassMethod(objc_getClass("WKPreferenceObserver"), @selector(sharedInstance));
+    ASSERT(sharedInstanceMethod);
+    sharedInstanceMethodOriginal = method_setImplementation(sharedInstanceMethod, (IMP)sharedInstanceMethodOverride);
+    ASSERT(sharedInstanceMethodOriginal);
+    Method preferenceDidChangeMethod = class_getInstanceMethod(objc_getClass("WKPreferenceObserver"), @selector(preferenceDidChange:key:encodedValue:));
+    ASSERT(preferenceDidChangeMethod);
+    preferenceDidChangeMethodOriginal = method_setImplementation(preferenceDidChangeMethod, (IMP)preferenceDidChangeMethodOverride);
+    ASSERT(preferenceDidChangeMethodOriginal);
+
     // Tests uses "en-GB" language initially.
     system([NSString stringWithFormat:@"defaults write NSGlobalDomain AppleLanguages '(\"en-GB\")'"].UTF8String);
 
+    auto getLanguageFromNSUserDefaults = [] {
+        auto languages = makeVector<String>([[NSUserDefaults standardUserDefaults] valueForKey:@"AppleLanguages"]);
+        return languages.isEmpty() ? emptyString() : languages[0];
+    };
+    unsigned timeout = 0;
+    while (getLanguageFromNSUserDefaults() != "en-GB" && ++timeout < 100)
+        TestWebKitAPI::Util::sleep(0.1);
+    EXPECT_WK_STREQ(@"en-GB", getLanguageFromNSUserDefaults());
+
     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
     [webView synchronouslyLoadTestPageNamed:@"simple"];
@@ -97,6 +139,12 @@
 
     // We only listen for preference changes when the application is active.
     [[NSNotificationCenter defaultCenter] postNotificationName:NSApplicationDidBecomeActiveNotification object:NSApp userInfo:nil];
+    timeout = 0;
+    while (!preferenceObserverSharedInstanceCalled && ++timeout < 100)
+        TestWebKitAPI::Util::sleep(0.1);
+    EXPECT_TRUE(preferenceObserverSharedInstanceCalled);
+    if (!preferenceObserverSharedInstanceCalled)
+        return;
 
     auto preferredLanguage = [&] {
         return [webView stringByEvaluatingJavaScript:@"navigator.language"];
@@ -104,13 +152,6 @@
     EXPECT_WK_STREQ(@"en-GB", preferredLanguage());
 
     __block bool done = false;
-    [webView evaluateJavaScript:@"_onlanguagechange_ = () => { webkit.messageHandlers.testHandler.postMessage(navigator.language); }; true;" completionHandler:^(id value, NSError *error) {
-        EXPECT_TRUE(!error);
-        done = true;
-    }];
-    TestWebKitAPI::Util::run(&done);
-
-    done = false;
     __block bool didChangeLanguage = false;
     [webView performAfterReceivingAnyMessage:^(NSString *newLanguage) {
         EXPECT_WK_STREQ(@"en-US", newLanguage);
@@ -118,10 +159,16 @@
         done = true;
     }];
 
+    [webView evaluateJavaScript:@"_onlanguagechange_ = () => { webkit.messageHandlers.testHandler.postMessage(navigator.language); }; true;" completionHandler:^(id value, NSError *error) {
+        EXPECT_TRUE(!error);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
     // Switch system language from "en-GB" to "en-US". Make sure that we fire a languagechange event at the Window and that navigator.language
-    // now reports "en-gb".
+    // now reports "en-GB".
     system([NSString stringWithFormat:@"defaults write NSGlobalDomain AppleLanguages '(\"en-US\")'"].UTF8String);
-    CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), CFSTR("AppleLanguagePreferencesChangedNotification"), nullptr, nullptr, true);
 
     // Implement our own timeout because we would fail to reset the language if we let the test actually time out.
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
@@ -130,5 +177,10 @@
 
     TestWebKitAPI::Util::run(&done);
     EXPECT_TRUE(didChangeLanguage);
+    EXPECT_TRUE(preferenceObserverPreferenceDidChangeCalled);
+    EXPECT_WK_STREQ(@"en-US", preferredLanguage());
+    EXPECT_WK_STREQ(@"en-US", getLanguageFromNSUserDefaults());
+
+    method_setImplementation(sharedInstanceMethod, (IMP)sharedInstanceMethodOriginal);
 }
 #endif
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to