Need a mechanism to override navigator.userAgent
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 26 Jan 2019 06:50:58 +0000 (06:50 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 26 Jan 2019 06:50:58 +0000 (06:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193762
<rdar://problem/47504939>

Reviewed by Brent Fulgham.

Source/WebCore:

Added the ability to specify user agent string just for navigator.userAgent via DocumentLoader.

* loader/DocumentLoader.h:
(WebCore::DocumentLoader::setCustomJavaScriptUserAgent):
(WebCore::DocumentLoader::customJavaScriptUserAgent const):
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::userAgentForJavaScript const):
* loader/FrameLoader.h:
* page/Navigator.cpp:
(WebCore::Navigator::userAgent const):

Source/WebKit:

This patch adds a new _WKWebsitePolicies SPI to specify the user agent string returned by
navigator.userAgent without affecting the user agent string used to send network requests.

Tests: WebKit.WebsitePoliciesCustomJavaScriptUserAgent
       WebKit.WebsitePoliciesCustomUserAgents

* Shared/WebsitePoliciesData.cpp:
(WebKit::WebsitePoliciesData::encode const):
(WebKit::WebsitePoliciesData::decode):
(WebKit::WebsitePoliciesData::applyToDocumentLoader):
* Shared/WebsitePoliciesData.h:
* UIProcess/API/APIWebsitePolicies.cpp:
(API::WebsitePolicies::data):
* UIProcess/API/APIWebsitePolicies.h:
* UIProcess/API/Cocoa/_WKWebsitePolicies.h:
* UIProcess/API/Cocoa/_WKWebsitePolicies.mm:
(-[_WKWebsitePolicies setCustomJavaScriptUserAgent:]):
(-[_WKWebsitePolicies customJavaScriptUserAgent]):

Tools:

Added test cases for _WKWebsitePolicies.customJavaScriptUserAgent.

* TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm:
(-[CustomJavaScriptUserAgentDelegate _webView:decidePolicyForNavigationAction:userInfo:decisionHandler:]):
(-[CustomJavaScriptUserAgentDelegate webView:didFinishNavigation:]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@240541 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/loader/DocumentLoader.h
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/FrameLoader.h
Source/WebCore/page/Navigator.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebsitePoliciesData.cpp
Source/WebKit/Shared/WebsitePoliciesData.h
Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp
Source/WebKit/UIProcess/API/APIWebsitePolicies.h
Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.h
Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm

index 0473ff1..67b72bb 100644 (file)
@@ -1,3 +1,22 @@
+2019-01-25  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Need a mechanism to override navigator.userAgent
+        https://bugs.webkit.org/show_bug.cgi?id=193762
+        <rdar://problem/47504939>
+
+        Reviewed by Brent Fulgham.
+
+        Added the ability to specify user agent string just for navigator.userAgent via DocumentLoader.
+
+        * loader/DocumentLoader.h:
+        (WebCore::DocumentLoader::setCustomJavaScriptUserAgent):
+        (WebCore::DocumentLoader::customJavaScriptUserAgent const):
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::userAgentForJavaScript const):
+        * loader/FrameLoader.h:
+        * page/Navigator.cpp:
+        (WebCore::Navigator::userAgent const):
+
 2019-01-25  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: provide a way to edit page settings on a remote target
index 804be9b..f7c5457 100644 (file)
@@ -270,7 +270,10 @@ public:
 
     void setCustomUserAgent(const String& customUserAgent) { m_customUserAgent = customUserAgent; }
     const String& customUserAgent() const { return m_customUserAgent; }
-        
+
+    void setCustomJavaScriptUserAgent(const String& customJavaScriptUserAgent) { m_customJavaScriptUserAgent = customJavaScriptUserAgent; }
+    const String& customJavaScriptUserAgent() const { return m_customJavaScriptUserAgent; }
+
     void setCustomNavigatorPlatform(const String& customNavigatorPlatform) { m_customNavigatorPlatform = customNavigatorPlatform; }
     const String& customNavigatorPlatform() const { return m_customNavigatorPlatform; }
 
@@ -543,6 +546,7 @@ private:
     HashMap<String, Vector<std::pair<String, uint32_t>>> m_pendingContentExtensionDisplayNoneSelectors;
 #endif
     String m_customUserAgent;
+    String m_customJavaScriptUserAgent;
     String m_customNavigatorPlatform;
     bool m_userContentExtensionsEnabled { true };
     bool m_deviceOrientationEventEnabled { true };
index ebf950b..43d7c64 100644 (file)
@@ -2705,6 +2705,20 @@ String FrameLoader::userAgent(const URL& url) const
 
     return m_client.userAgent(url);
 }
+
+String FrameLoader::userAgentForJavaScript(const URL& url) const
+{
+    if (auto* documentLoader = m_frame.mainFrame().loader().activeDocumentLoader()) {
+        auto& customJavaScriptUserAgent = documentLoader->customJavaScriptUserAgent();
+        if (!customJavaScriptUserAgent.isEmpty())
+            return customJavaScriptUserAgent;
+        auto& customUserAgent = documentLoader->customUserAgent();
+        if (!customUserAgent.isEmpty())
+            return customUserAgent;
+    }
+
+    return m_client.userAgent(url);
+}
     
 String FrameLoader::navigatorPlatform() const
 {
index 0554aad..89dcb98 100644 (file)
@@ -234,6 +234,7 @@ public:
 
     void dispatchOnloadEvents();
     String userAgent(const URL&) const;
+    String userAgentForJavaScript(const URL&) const;
     String navigatorPlatform() const;
 
     void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld&);
index a1aaf8f..91e3f4d 100644 (file)
@@ -92,7 +92,7 @@ const String& Navigator::userAgent() const
     if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
         ResourceLoadObserver::shared().logNavigatorAPIAccessed(*frame->document(), ResourceLoadStatistics::NavigatorAPI::UserAgent);
     if (m_userAgent.isNull())
-        m_userAgent = frame->loader().userAgent(frame->document()->url());
+        m_userAgent = frame->loader().userAgentForJavaScript(frame->document()->url());
     return m_userAgent;
 }
     
index d2d0fe2..7f1f3bd 100644 (file)
@@ -1,3 +1,30 @@
+2019-01-25  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Need a mechanism to override navigator.userAgent
+        https://bugs.webkit.org/show_bug.cgi?id=193762
+        <rdar://problem/47504939>
+
+        Reviewed by Brent Fulgham.
+
+        This patch adds a new _WKWebsitePolicies SPI to specify the user agent string returned by
+        navigator.userAgent without affecting the user agent string used to send network requests.
+
+        Tests: WebKit.WebsitePoliciesCustomJavaScriptUserAgent
+               WebKit.WebsitePoliciesCustomUserAgents
+
+        * Shared/WebsitePoliciesData.cpp:
+        (WebKit::WebsitePoliciesData::encode const):
+        (WebKit::WebsitePoliciesData::decode):
+        (WebKit::WebsitePoliciesData::applyToDocumentLoader):
+        * Shared/WebsitePoliciesData.h:
+        * UIProcess/API/APIWebsitePolicies.cpp:
+        (API::WebsitePolicies::data):
+        * UIProcess/API/APIWebsitePolicies.h:
+        * UIProcess/API/Cocoa/_WKWebsitePolicies.h:
+        * UIProcess/API/Cocoa/_WKWebsitePolicies.mm:
+        (-[_WKWebsitePolicies setCustomJavaScriptUserAgent:]):
+        (-[_WKWebsitePolicies customJavaScriptUserAgent]):
+
 2019-01-25  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: provide a way to edit page settings on a remote target
index fb8e240..1d1181b 100644 (file)
@@ -44,6 +44,7 @@ void WebsitePoliciesData::encode(IPC::Encoder& encoder) const
     encoder << popUpPolicy;
     encoder << websiteDataStoreParameters;
     encoder << customUserAgent;
+    encoder << customJavaScriptUserAgent;
     encoder << customNavigatorPlatform;
 }
 
@@ -89,6 +90,11 @@ Optional<WebsitePoliciesData> WebsitePoliciesData::decode(IPC::Decoder& decoder)
     if (!customUserAgent)
         return WTF::nullopt;
 
+    Optional<String> customJavaScriptUserAgent;
+    decoder >> customJavaScriptUserAgent;
+    if (!customJavaScriptUserAgent)
+        return WTF::nullopt;
+
     Optional<String> customNavigatorPlatform;
     decoder >> customNavigatorPlatform;
     if (!customNavigatorPlatform)
@@ -103,6 +109,7 @@ Optional<WebsitePoliciesData> WebsitePoliciesData::decode(IPC::Decoder& decoder)
         WTFMove(*popUpPolicy),
         WTFMove(*websiteDataStoreParameters),
         WTFMove(*customUserAgent),
+        WTFMove(*customJavaScriptUserAgent),
         WTFMove(*customNavigatorPlatform),
     } };
 }
@@ -111,6 +118,7 @@ void WebsitePoliciesData::applyToDocumentLoader(WebsitePoliciesData&& websitePol
 {
     documentLoader.setCustomHeaderFields(WTFMove(websitePolicies.customHeaderFields));
     documentLoader.setCustomUserAgent(websitePolicies.customUserAgent);
+    documentLoader.setCustomJavaScriptUserAgent(websitePolicies.customJavaScriptUserAgent);
     documentLoader.setCustomNavigatorPlatform(websitePolicies.customNavigatorPlatform);
     documentLoader.setDeviceOrientationEventEnabled(websitePolicies.deviceOrientationEventEnabled);
     
index fcaed27..506cda9 100644 (file)
@@ -54,8 +54,9 @@ struct WebsitePoliciesData {
     WebsitePopUpPolicy popUpPolicy { WebsitePopUpPolicy::Default };
     Optional<WebsiteDataStoreParameters> websiteDataStoreParameters;
     String customUserAgent;
+    String customJavaScriptUserAgent;
     String customNavigatorPlatform;
-    
+
     void encode(IPC::Encoder&) const;
     static Optional<WebsitePoliciesData> decode(IPC::Decoder&);
 };
index 8447696..52a1636 100644 (file)
@@ -56,7 +56,8 @@ WebKit::WebsitePoliciesData WebsitePolicies::data()
     Optional<WebKit::WebsiteDataStoreParameters> parameters;
     if (m_websiteDataStore)
         parameters = m_websiteDataStore->websiteDataStore().parameters();
-    return { contentBlockersEnabled(), deviceOrientationEventEnabled(), allowedAutoplayQuirks(), autoplayPolicy(), customHeaderFields(), popUpPolicy(), WTFMove(parameters), m_customUserAgent, m_customNavigatorPlatform };
+    return { contentBlockersEnabled(), deviceOrientationEventEnabled(), allowedAutoplayQuirks(), autoplayPolicy(),
+        customHeaderFields(), popUpPolicy(), WTFMove(parameters), m_customUserAgent, m_customJavaScriptUserAgent, m_customNavigatorPlatform };
 }
 
 }
index 718c755..ee7fd25 100644 (file)
@@ -74,7 +74,10 @@ public:
 
     void setCustomUserAgent(const WTF::String& customUserAgent) { m_customUserAgent = customUserAgent; }
     const WTF::String& customUserAgent() const { return m_customUserAgent; }
-    
+
+    void setCustomJavaScriptUserAgent(const WTF::String& customJavaScriptUserAgent) { m_customJavaScriptUserAgent = customJavaScriptUserAgent; }
+    const WTF::String& customJavaScriptUserAgent() const { return m_customJavaScriptUserAgent; }
+
     void setCustomNavigatorPlatform(const WTF::String& customNavigatorPlatform) { m_customNavigatorPlatform = customNavigatorPlatform; }
     const WTF::String& customNavigatorPlatform() const { return m_customNavigatorPlatform; }
 
@@ -89,6 +92,7 @@ private:
     WebKit::WebsitePopUpPolicy m_popUpPolicy { WebKit::WebsitePopUpPolicy::Default };
     RefPtr<WebsiteDataStore> m_websiteDataStore;
     WTF::String m_customUserAgent;
+    WTF::String m_customJavaScriptUserAgent;
     WTF::String m_customNavigatorPlatform;
 };
 
index cb7c2d0..3f5688c 100644 (file)
@@ -59,6 +59,7 @@ WK_CLASS_AVAILABLE(macosx(10.12.3), ios(10.3))
 @property (nonatomic) _WKWebsitePopUpPolicy popUpPolicy WK_API_AVAILABLE(macosx(10.14), ios(12.0));
 @property (nonatomic, strong) WKWebsiteDataStore *websiteDataStore WK_API_AVAILABLE(macosx(10.13.4), ios(11.3));
 @property (nonatomic, copy) NSString *customUserAgent WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic, copy) NSString *customJavaScriptUserAgent WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @property (nonatomic, copy) NSString *customNavigatorPlatform WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @property (nonatomic) BOOL deviceOrientationEventEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
index 3928e39..fda88b0 100644 (file)
     return _websitePolicies->customUserAgent();
 }
 
+- (void)setCustomJavaScriptUserAgent:(NSString *)customUserAgent
+{
+    _websitePolicies->setCustomJavaScriptUserAgent(customUserAgent);
+}
+
+- (NSString *)customJavaScriptUserAgent
+{
+    return _websitePolicies->customJavaScriptUserAgent();
+}
+
 - (void)setCustomNavigatorPlatform:(NSString *)customNavigatorPlatform
 {
     _websitePolicies->setCustomNavigatorPlatform(customNavigatorPlatform);
index 1dceff2..df3606a 100644 (file)
@@ -1,3 +1,17 @@
+2019-01-25  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Need a mechanism to override navigator.userAgent
+        https://bugs.webkit.org/show_bug.cgi?id=193762
+        <rdar://problem/47504939>
+
+        Reviewed by Brent Fulgham.
+
+        Added test cases for _WKWebsitePolicies.customJavaScriptUserAgent.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm:
+        (-[CustomJavaScriptUserAgentDelegate _webView:decidePolicyForNavigationAction:userInfo:decisionHandler:]):
+        (-[CustomJavaScriptUserAgentDelegate webView:didFinishNavigation:]):
+
 2019-01-25  Dean Jackson  <dino@apple.com>
 
         REGRESSION: Some USDz from 3rd party websites don't go directly to AR QL
index 825ed10..5e4c7c5 100644 (file)
@@ -1046,6 +1046,9 @@ onload = () => {
         fetch("test://www.webkit.org/fetchResource.html");
     }, 0);
 }
+onmessage = (event) => {
+    window.subframeUserAgent = event.data;
+}
 </script>
 )TESTRESOURCE";
 
@@ -1058,6 +1061,7 @@ onload = () => {
     setTimeout(() => {
         fetch("test://www.apple.com/fetchResource.html");
     }, 0);
+    top.postMessage(navigator.userAgent, '*');
 }
 </script>
 )TESTRESOURCE";
@@ -1100,6 +1104,96 @@ TEST(WebKit, WebsitePoliciesCustomUserAgent)
     loadCount = 0;
 }
 
+@interface CustomJavaScriptUserAgentDelegate : NSObject <WKNavigationDelegate>
+@property (nonatomic) BOOL setCustomUserAgent;
+@end
+
+@implementation CustomJavaScriptUserAgentDelegate
+
+- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction userInfo:(id <NSSecureCoding>)userInfo decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
+{
+    _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
+    if (navigationAction.targetFrame.mainFrame) {
+        [websitePolicies setCustomJavaScriptUserAgent:@"Foo Custom JavaScript UserAgent"];
+        if (_setCustomUserAgent)
+            [websitePolicies setCustomUserAgent:@"Foo Custom Request UserAgent"];
+    }
+
+    decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
+}
+
+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
+{
+    finishedNavigation = true;
+}
+
+@end
+
+TEST(WebKit, WebsitePoliciesCustomJavaScriptUserAgent)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+
+    auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
+    [schemeHandler addMappingFromURLString:@"test://www.webkit.org/main.html" toData:customUserAgentMainFrameTestBytes];
+    [schemeHandler addMappingFromURLString:@"test://www.apple.com/subframe.html" toData:customUserAgentSubFrameTestBytes];
+    [schemeHandler setTaskHandler:[](id <WKURLSchemeTask> task) {
+        auto* userAgentString = [task.request valueForHTTPHeaderField:@"User-Agent"];
+        EXPECT_TRUE([userAgentString hasPrefix:@"Mozilla/5.0 (Macintosh;"]);
+        EXPECT_TRUE([userAgentString hasSuffix:@"(KHTML, like Gecko)"]);
+    }];
+    [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    auto delegate = adoptNS([[CustomJavaScriptUserAgentDelegate alloc] init]);
+    [webView setNavigationDelegate:delegate.get()];
+
+    loadCount = 0;
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.webkit.org/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&finishedNavigation);
+    finishedNavigation = false;
+
+    while (loadCount != 9U)
+        TestWebKitAPI::Util::spinRunLoop();
+
+    EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"navigator.userAgent"] UTF8String]);
+    EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"subframeUserAgent"] UTF8String]);
+}
+
+TEST(WebKit, WebsitePoliciesCustomUserAgents)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+
+    auto schemeHandler = adoptNS([[DataMappingSchemeHandler alloc] init]);
+    [schemeHandler addMappingFromURLString:@"test://www.webkit.org/main.html" toData:customUserAgentMainFrameTestBytes];
+    [schemeHandler addMappingFromURLString:@"test://www.apple.com/subframe.html" toData:customUserAgentSubFrameTestBytes];
+    [schemeHandler setTaskHandler:[](id <WKURLSchemeTask> task) {
+        EXPECT_STREQ("Foo Custom Request UserAgent", [[task.request valueForHTTPHeaderField:@"User-Agent"] UTF8String]);
+    }];
+    [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    auto delegate = adoptNS([[CustomJavaScriptUserAgentDelegate alloc] init]);
+    delegate.get().setCustomUserAgent = YES;
+    [webView setNavigationDelegate:delegate.get()];
+
+    loadCount = 0;
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"test://www.webkit.org/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&finishedNavigation);
+    finishedNavigation = false;
+
+    while (loadCount != 9U)
+        TestWebKitAPI::Util::spinRunLoop();
+
+    EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"navigator.userAgent"] UTF8String]);
+    EXPECT_STREQ("Foo Custom JavaScript UserAgent", [[webView stringByEvaluatingJavaScript:@"subframeUserAgent"] UTF8String]);
+}
+
 @interface CustomNavigatorPlatformDelegate : NSObject <WKNavigationDelegate> {
 }
 @end