Title: [273904] trunk
Revision
273904
Author
cdu...@apple.com
Date
2021-03-04 11:02:27 -0800 (Thu, 04 Mar 2021)

Log Message

[macOS][WK2] Changing the system language does not update navigator.language
https://bugs.webkit.org/show_bug.cgi?id=222619

Reviewed by Per Arne Vollan.

Source/WebKit:

* Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceMain.mm:
(WebKit::setAppleLanguagesPreference):
Fix a memory leak for newArguments.

* WebProcess/cocoa/WebProcessCocoa.mm:
(WebKit::WebProcess::platformInitializeWebProcess):
If CFPREFS_DIRECT_MODE is not enabled, we need to listen for AppleLanguagePreferencesChangedNotification
inside the WebProcess. This used to happen implicitly inside WTF::platformUserPreferredLanguages() but
it is now explicit since we don't want/need it when using CFPREFS_DIRECT_MODE.

(WebKit::setPreferenceValue):
- If preference is AppleLanguages, set it for the volatile domain to match what we do in XPCServiceMain.mm.
  This is needed because the preference in the volatile domain seems to take precedence.
- Call WTF::languageDidChange() when the AppleLanguages preference gets updated so that
  language change listeners get notified of the language change (e.g. we fire a languagechange
  event at the Window).

Source/WebKitLegacy/mac:

We need to listen for AppleLanguagePreferencesChangedNotification on mac WK1.
This used to happen implicitly inside WTF::platformUserPreferredLanguages() but
it is now explicit since we don't want/need it when using CFPREFS_DIRECT_MODE in WK2.

* WebView/WebView.mm:
(-[WebView _commonInitializationWithFrameName:groupName:]):

Source/WTF:

Update WTF::languageDidChange() to clear preferredLanguages() on Cocoa platforms
so that we get updated values from the system the next time
platformUserPreferredLanguages() is called.

platformUserPreferredLanguages() used to implicitly register a AppleLanguagePreferencesChangedNotification
listener. We've now made this registering opt-in by moving it to a separate
listenForLanguageChangeNotifications() function. This function is getting called
on Mac WK1 and WK2 when CFPREFS_DIRECT_MODE is disabled (legacy).
When CFPREFS_DIRECT_MODE is enabled in WK2, we don't want/need to listen for this
notification because the AppleLanguages preference gets pushed by the UIProcess
down to the WebProcesses. Even though we could listen for this notification,
the WebProcess would not have the latest AppleLanguages preference when receiving
the notification. This would cause us to fire the languagechange event at the
Window too early and navigator.language would keep returning the old language.
For WK2 with CFPREFS_DIRECT_MODE enabled, we now explicitly call
WTF::languageDidChange() when the "AppleLanguages" preference gets sync'd from
the UIProcess instead.

* wtf/Language.cpp:
(WTF::languageDidChange):
(WTF::platformLanguageDidChange):
* wtf/Language.h:
* wtf/cf/LanguageCF.cpp:
(WTF::languagePreferencesDidChange):
(WTF::platformLanguageDidChange):
(WTF::listenForLanguageChangeNotifications):
(WTF::platformUserPreferredLanguages):

Tools:

Add API test coverage.

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

Modified Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (273903 => 273904)


--- trunk/Source/WTF/ChangeLog	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WTF/ChangeLog	2021-03-04 19:02:27 UTC (rev 273904)
@@ -1,5 +1,40 @@
 2021-03-04  Chris Dumez  <cdu...@apple.com>
 
+        [macOS][WK2] Changing the system language does not update navigator.language
+        https://bugs.webkit.org/show_bug.cgi?id=222619
+
+        Reviewed by Per Arne Vollan.
+
+        Update WTF::languageDidChange() to clear preferredLanguages() on Cocoa platforms
+        so that we get updated values from the system the next time
+        platformUserPreferredLanguages() is called.
+
+        platformUserPreferredLanguages() used to implicitly register a AppleLanguagePreferencesChangedNotification
+        listener. We've now made this registering opt-in by moving it to a separate
+        listenForLanguageChangeNotifications() function. This function is getting called
+        on Mac WK1 and WK2 when CFPREFS_DIRECT_MODE is disabled (legacy).
+        When CFPREFS_DIRECT_MODE is enabled in WK2, we don't want/need to listen for this
+        notification because the AppleLanguages preference gets pushed by the UIProcess
+        down to the WebProcesses. Even though we could listen for this notification,
+        the WebProcess would not have the latest AppleLanguages preference when receiving
+        the notification. This would cause us to fire the languagechange event at the
+        Window too early and navigator.language would keep returning the old language.
+        For WK2 with CFPREFS_DIRECT_MODE enabled, we now explicitly call
+        WTF::languageDidChange() when the "AppleLanguages" preference gets sync'd from
+        the UIProcess instead.
+
+        * wtf/Language.cpp:
+        (WTF::languageDidChange):
+        (WTF::platformLanguageDidChange):
+        * wtf/Language.h:
+        * wtf/cf/LanguageCF.cpp:
+        (WTF::languagePreferencesDidChange):
+        (WTF::platformLanguageDidChange):
+        (WTF::listenForLanguageChangeNotifications):
+        (WTF::platformUserPreferredLanguages):
+
+2021-03-04  Chris Dumez  <cdu...@apple.com>
+
         Set ownership of IOSurfaces from the GPUProcess instead of the WebProcess
         https://bugs.webkit.org/show_bug.cgi?id=222391
         <rdar://74748353>

Modified: trunk/Source/WTF/wtf/Language.cpp (273903 => 273904)


--- trunk/Source/WTF/wtf/Language.cpp	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WTF/wtf/Language.cpp	2021-03-04 19:02:27 UTC (rev 273904)
@@ -62,6 +62,7 @@
 
 void languageDidChange()
 {
+    platformLanguageDidChange();
     for (auto& observer : copyToVector(observerMap())) {
         if (observerMap().contains(observer.key))
             observer.value(observer.key);
@@ -183,4 +184,10 @@
     return localeName;
 }
 
+#if !PLATFORM(COCOA)
+void platformLanguageDidChange()
+{
 }
+#endif
+
+}

Modified: trunk/Source/WTF/wtf/Language.h (273903 => 273904)


--- trunk/Source/WTF/wtf/Language.h	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WTF/wtf/Language.h	2021-03-04 19:02:27 UTC (rev 273904)
@@ -41,6 +41,8 @@
 WTF_EXPORT_PRIVATE void overrideUserPreferredLanguages(const Vector<String>&);
 WTF_EXPORT_PRIVATE size_t indexOfBestMatchingLanguageInList(const String& language, const Vector<String>& languageList, bool& exactMatch);
 WTF_EXPORT_PRIVATE Vector<String> platformUserPreferredLanguages();
+WTF_EXPORT_PRIVATE void platformLanguageDidChange();
+
 // Called from platform specific code when the user's preferred language(s) change.
 WTF_EXPORT_PRIVATE void languageDidChange();
 
@@ -52,6 +54,7 @@
 
 #if PLATFORM(COCOA)
 bool canMinimizeLanguages();
+WTF_EXPORT_PRIVATE void listenForLanguageChangeNotifications();
 RetainPtr<CFArrayRef> minimizedLanguagesFromLanguages(CFArrayRef);
 #endif
 

Modified: trunk/Source/WTF/wtf/cf/LanguageCF.cpp (273903 => 273904)


--- trunk/Source/WTF/wtf/cf/LanguageCF.cpp	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WTF/wtf/cf/LanguageCF.cpp	2021-03-04 19:02:27 UTC (rev 273904)
@@ -40,6 +40,13 @@
 
 static Lock preferredLanguagesMutex;
 
+#if PLATFORM(MAC)
+static void languagePreferencesDidChange(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef)
+{
+    languageDidChange();
+}
+#endif
+
 static Vector<String>& preferredLanguages()
 {
     static LazyNeverDestroyed<Vector<String>> languages;
@@ -83,27 +90,26 @@
 
 }
 
-#if PLATFORM(MAC)
-static void languagePreferencesDidChange(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef)
+void platformLanguageDidChange()
 {
     {
         auto locker = holdLock(preferredLanguagesMutex);
         preferredLanguages().clear();
     }
-    
-    languageDidChange();
 }
-#endif
 
-Vector<String> platformUserPreferredLanguages()
+void listenForLanguageChangeNotifications()
 {
 #if PLATFORM(MAC)
     static dispatch_once_t onceToken;
-    dispatch_once(&onceToken, ^ {
+    dispatch_once(&onceToken, ^{
         CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), nullptr, &languagePreferencesDidChange, CFSTR("AppleLanguagePreferencesChangedNotification"), nullptr, CFNotificationSuspensionBehaviorCoalesce);
     });
 #endif
+}
 
+Vector<String> platformUserPreferredLanguages()
+{
     auto locker = holdLock(preferredLanguagesMutex);
     Vector<String>& userPreferredLanguages = preferredLanguages();
 

Modified: trunk/Source/WebKit/ChangeLog (273903 => 273904)


--- trunk/Source/WebKit/ChangeLog	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WebKit/ChangeLog	2021-03-04 19:02:27 UTC (rev 273904)
@@ -1,5 +1,29 @@
 2021-03-04  Chris Dumez  <cdu...@apple.com>
 
+        [macOS][WK2] Changing the system language does not update navigator.language
+        https://bugs.webkit.org/show_bug.cgi?id=222619
+
+        Reviewed by Per Arne Vollan.
+
+        * Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceMain.mm:
+        (WebKit::setAppleLanguagesPreference):
+        Fix a memory leak for newArguments.
+
+        * WebProcess/cocoa/WebProcessCocoa.mm:
+        (WebKit::WebProcess::platformInitializeWebProcess):
+        If CFPREFS_DIRECT_MODE is not enabled, we need to listen for AppleLanguagePreferencesChangedNotification
+        inside the WebProcess. This used to happen implicitly inside WTF::platformUserPreferredLanguages() but
+        it is now explicit since we don't want/need it when using CFPREFS_DIRECT_MODE.
+
+        (WebKit::setPreferenceValue):
+        - If preference is AppleLanguages, set it for the volatile domain to match what we do in XPCServiceMain.mm.
+          This is needed because the preference in the volatile domain seems to take precedence.
+        - Call WTF::languageDidChange() when the AppleLanguages preference gets updated so that
+          language change listeners get notified of the language change (e.g. we fire a languagechange
+          event at the Window).
+
+2021-03-04  Chris Dumez  <cdu...@apple.com>
+
         Set ownership of IOSurfaces from the GPUProcess instead of the WebProcess
         https://bugs.webkit.org/show_bug.cgi?id=222391
         <rdar://problem/74748353>

Modified: trunk/Source/WebKit/Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceMain.mm (273903 => 273904)


--- trunk/Source/WebKit/Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceMain.mm	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WebKit/Shared/EntryPointUtilities/Cocoa/XPCService/XPCServiceMain.mm	2021-03-04 19:02:27 UTC (rev 273904)
@@ -48,7 +48,7 @@
     if (xpc_object_t languages = xpc_dictionary_get_value(bootstrap.get(), "OverrideLanguages")) {
         @autoreleasepool {
             NSDictionary *existingArguments = [[NSUserDefaults standardUserDefaults] volatileDomainForName:NSArgumentDomain];
-            NSMutableDictionary *newArguments = [existingArguments mutableCopy];
+            auto newArguments = adoptNS([existingArguments mutableCopy]);
             RetainPtr<NSMutableArray> newLanguages = adoptNS([[NSMutableArray alloc] init]);
             xpc_array_apply(languages, ^(size_t index, xpc_object_t value) {
                 [newLanguages addObject:[NSString stringWithCString:xpc_string_get_string_ptr(value) encoding:NSUTF8StringEncoding]];
@@ -55,7 +55,7 @@
                 return true;
             });
             [newArguments setValue:newLanguages.get() forKey:@"AppleLanguages"];
-            [[NSUserDefaults standardUserDefaults] setVolatileDomain:newArguments forName:NSArgumentDomain];
+            [[NSUserDefaults standardUserDefaults] setVolatileDomain:newArguments.get() forName:NSArgumentDomain];
         }
     }
 }

Modified: trunk/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm (273903 => 273904)


--- trunk/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm	2021-03-04 19:02:27 UTC (rev 273904)
@@ -81,7 +81,6 @@
 #import <algorithm>
 #import <dispatch/dispatch.h>
 #import <objc/runtime.h>
-#import <pal/cocoa/MediaToolboxSoftLink.h>
 #import <pal/spi/cf/CFNetworkSPI.h>
 #import <pal/spi/cf/CFUtilitiesSPI.h>
 #import <pal/spi/cg/CoreGraphicsSPI.h>
@@ -94,6 +93,7 @@
 #import <pal/spi/mac/NSApplicationSPI.h>
 #import <stdio.h>
 #import <wtf/FileSystem.h>
+#import <wtf/Language.h>
 #import <wtf/ProcessPrivilege.h>
 #import <wtf/SoftLinking.h>
 #import <wtf/cocoa/Entitlements.h>
@@ -144,6 +144,7 @@
 #endif
 
 #import <pal/cocoa/AVFoundationSoftLink.h>
+#import <pal/cocoa/MediaToolboxSoftLink.h>
 
 #if HAVE(CATALYST_USER_INTERFACE_IDIOM_AND_SCALE_FACTOR)
 // FIXME: This is only for binary compatibility with versions of UIKit in macOS 11 that are missing the change in <rdar://problem/68524148>.
@@ -357,6 +358,10 @@
     __CFRunLoopSetOptionsReason(__CFRunLoopOptionsEnableAppNap, CFSTR("Finished checkin as application - enable app nap"));
 #endif
 
+#if !ENABLE(CFPREFS_DIRECT_MODE)
+    WTF::listenForLanguageChangeNotifications();
+#endif
+
 #if TARGET_OS_IPHONE
     // Priority decay on iOS 9 is impacting page load time so we fix the priority of the WebProcess' main thread (rdar://problem/22003112).
     pthread_set_fixedpriority_self();
@@ -1096,10 +1101,20 @@
         CFPreferencesSetValue(key.createCFString().get(), (__bridge CFPropertyListRef)value, kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
 #if ASSERT_ENABLED
         id valueAfterSetting = [[NSUserDefaults standardUserDefaults] objectForKey:key];
-        ASSERT(valueAfterSetting == value || [valueAfterSetting isEqual:value]);
+        ASSERT(valueAfterSetting == value || [valueAfterSetting isEqual:value] || key == "AppleLanguages");
 #endif
     } else
         CFPreferencesSetValue(key.createCFString().get(), (__bridge CFPropertyListRef)value, domain.createCFString().get(), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
+
+    if (key == "AppleLanguages") {
+        // We need to set AppleLanguages for the volatile domain, similarly to what we do in XPCServiceMain.mm.
+        NSDictionary *existingArguments = [[NSUserDefaults standardUserDefaults] volatileDomainForName:NSArgumentDomain];
+        auto newArguments = adoptNS([existingArguments mutableCopy]);
+        [newArguments setValue:value forKey:@"AppleLanguages"];
+        [[NSUserDefaults standardUserDefaults] setVolatileDomain:newArguments.get() forName:NSArgumentDomain];
+
+        WTF::languageDidChange();
+    }
 }
 
 void WebProcess::notifyPreferencesChanged(const String& domain, const String& key, const Optional<String>& encodedValue)

Modified: trunk/Source/WebKitLegacy/mac/ChangeLog (273903 => 273904)


--- trunk/Source/WebKitLegacy/mac/ChangeLog	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WebKitLegacy/mac/ChangeLog	2021-03-04 19:02:27 UTC (rev 273904)
@@ -1,3 +1,17 @@
+2021-03-04  Chris Dumez  <cdu...@apple.com>
+
+        [macOS][WK2] Changing the system language does not update navigator.language
+        https://bugs.webkit.org/show_bug.cgi?id=222619
+
+        Reviewed by Per Arne Vollan.
+
+        We need to listen for AppleLanguagePreferencesChangedNotification on mac WK1.
+        This used to happen implicitly inside WTF::platformUserPreferredLanguages() but
+        it is now explicit since we don't want/need it when using CFPREFS_DIRECT_MODE in WK2.
+
+        * WebView/WebView.mm:
+        (-[WebView _commonInitializationWithFrameName:groupName:]):
+
 2021-03-03  Chris Dumez  <cdu...@apple.com>
 
         Unreviewed, reverting r273851.

Modified: trunk/Source/WebKitLegacy/mac/WebView/WebView.mm (273903 => 273904)


--- trunk/Source/WebKitLegacy/mac/WebView/WebView.mm	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Source/WebKitLegacy/mac/WebView/WebView.mm	2021-03-04 19:02:27 UTC (rev 273904)
@@ -248,6 +248,7 @@
 #import <wtf/BlockPtr.h>
 #import <wtf/FileSystem.h>
 #import <wtf/HashTraits.h>
+#import <wtf/Language.h>
 #import <wtf/MainThread.h>
 #import <wtf/ProcessPrivilege.h>
 #import <wtf/RAMSize.h>
@@ -1680,7 +1681,9 @@
 #endif
 
     [WebViewVisualIdentificationOverlay installForWebViewIfNeeded:self kind:@"WebView" deprecated:YES];
-#endif
+
+    WTF::listenForLanguageChangeNotifications();
+#endif // PLATFORM(MAC)
 }
 
 - (id)_initWithFrame:(NSRect)f frameName:(NSString *)frameName groupName:(NSString *)groupName

Modified: trunk/Tools/ChangeLog (273903 => 273904)


--- trunk/Tools/ChangeLog	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Tools/ChangeLog	2021-03-04 19:02:27 UTC (rev 273904)
@@ -1,3 +1,15 @@
+2021-03-04  Chris Dumez  <cdu...@apple.com>
+
+        [macOS][WK2] Changing the system language does not update navigator.language
+        https://bugs.webkit.org/show_bug.cgi?id=222619
+
+        Reviewed by Per Arne Vollan.
+
+        Add API test coverage.
+
+        * TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm:
+        (TEST):
+
 2021-03-04  Alex Christensen  <achristen...@webkit.org>
 
         Introduce "websocket", "fetch", and "other" resource types to WKContentRuleList

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm (273903 => 273904)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm	2021-03-04 18:41:02 UTC (rev 273903)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit/OverrideAppleLanguagesPreference.mm	2021-03-04 19:02:27 UTC (rev 273904)
@@ -29,6 +29,8 @@
 
 #import "PlatformUtilities.h"
 #import "TestWKWebView.h"
+#import <wtf/cocoa/VectorCocoa.h>
+#import <wtf/text/StringBuilder.h>
 
 TEST(WebKit, OverrideAppleLanguagesPreference)
 {
@@ -51,3 +53,74 @@
 }
 
 #endif // WK_HAVE_C_SPI
+
+// On older macOSes, CFPREFS_DIRECT_MODE is disabled and the WebProcess does not see the updated AppleLanguages
+// after the AppleLanguagePreferencesChangedNotification notification.
+#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 110000
+class AppleLanguagesTest : public testing::Test {
+public:
+    AppleLanguagesTest()
+    {
+        // Save current system language to restore it later.
+        auto task = adoptNS([[NSTask alloc] init]);
+        [task setLaunchPath:@"/usr/bin/defaults"];
+        [task setArguments:@[@"read", @"NSGlobalDomain", @"AppleLanguages"]];
+        auto pipe = adoptNS([[NSPipe alloc] init]);
+        [task setStandardOutput:pipe.get()];
+        auto fileHandle = [pipe fileHandleForReading];
+        [task launch];
+        NSData *data = "" readDataToEndOfFile];
+        m_savedAppleLanguages = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+        m_savedAppleLanguages.replace("\n", "");
+        m_savedAppleLanguages.replace(" ", "");
+    }
+
+    ~AppleLanguagesTest()
+    {
+        // Restore previous system language.
+        system([NSString stringWithFormat:@"defaults write NSGlobalDomain AppleLanguages '%@'", (NSString *)m_savedAppleLanguages].UTF8String);
+    }
+
+private:
+    String m_savedAppleLanguages;
+};
+
+TEST_F(AppleLanguagesTest, UpdateAppleLanguages)
+{
+    // Tests uses "en-US" language initially.
+    system([NSString stringWithFormat:@"defaults write NSGlobalDomain AppleLanguages '(\"en-US\")'"].UTF8String);
+
+    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"];
+
+    // We only listen for preference changes when the application is active.
+    [[NSNotificationCenter defaultCenter] postNotificationName:NSApplicationDidBecomeActiveNotification object:NSApp userInfo:nil];
+
+    auto preferredLanguage = [&] {
+        return [webView stringByEvaluatingJavaScript:@"navigator.language"];
+    };
+    EXPECT_WK_STREQ(@"en-us", 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);
+
+    __block bool didChangeLanguage = false;
+    [webView performAfterReceivingMessage:@"en-gb" action:^{ didChangeLanguage = true; }];
+    [webView performAfterReceivingMessage:@"en-us" action:^{
+        EXPECT_TRUE(false); // navigator.language was wrong when the languagechange event fired.
+        didChangeLanguage = true;
+    }];
+
+    // Switch system language from "en-US" to "en-GB". Make sure that we fire a languagechange event at the Window and that navigator.language
+    // now reports "en-gb".
+    system([NSString stringWithFormat:@"defaults write NSGlobalDomain AppleLanguages '(\"en-GB\")'"].UTF8String);
+    CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), CFSTR("AppleLanguagePreferencesChangedNotification"), nullptr, nullptr, true);
+
+    TestWebKitAPI::Util::run(&didChangeLanguage);
+}
+#endif
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to