Add SPI to allow the client to set the user-agent at main frame level, from the UIProcess
https://bugs.webkit.org/show_bug.cgi?id=192509
<rdar://problem/46500832>
Reviewed by Alex Christensen.
Source/WebCore:
* loader/DocumentLoader.h:
(WebCore::DocumentLoader::setCustomUserAgent):
(WebCore::DocumentLoader::customUserAgent const):
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::userAgent const):
Source/WebKit:
Add SPI to allow the client to set the user-agent at main frame level, from the UIProcess instead of doing
it at resource-level from the injected bundle.
The custom user-agent string can now be set on _WKWebsitePolicies during the
decidePolicyForNavigationAction for the main feame, and will impact this main resource load as well as its
future subresource loads.
* 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 setCustomUserAgent:]):
(-[_WKWebsitePolicies customUserAgent]):
* UIProcess/Cocoa/NavigationState.mm:
(WebKit::NavigationState::NavigationClient::decidePolicyForNavigationAction):
Tools:
Add API test coverage.
* TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm:
(-[DataMappingSchemeHandler addMappingFromURLString:toData:]):
(-[DataMappingSchemeHandler webView:startURLSchemeTask:]):
(-[DataMappingSchemeHandler webView:stopURLSchemeTask:]):
(-[CustomUserAgentDelegate _webView:decidePolicyForNavigationAction:userInfo:decisionHandler:]):
(-[CustomUserAgentDelegate webView:didFinishNavigation:]):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@239046 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index b968cee..625b5f6 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,17 @@
+2018-12-10 Chris Dumez <cdumez@apple.com>
+
+ Add SPI to allow the client to set the user-agent at main frame level, from the UIProcess
+ https://bugs.webkit.org/show_bug.cgi?id=192509
+ <rdar://problem/46500832>
+
+ Reviewed by Alex Christensen.
+
+ * loader/DocumentLoader.h:
+ (WebCore::DocumentLoader::setCustomUserAgent):
+ (WebCore::DocumentLoader::customUserAgent const):
+ * loader/FrameLoader.cpp:
+ (WebCore::FrameLoader::userAgent const):
+
2018-12-10 Adrian Perez de Castro <aperez@igalia.com>
[GLib] FileSystem::moveFile() should fall back to copying
diff --git a/Source/WebCore/loader/DocumentLoader.h b/Source/WebCore/loader/DocumentLoader.h
index a62ff44..501a527 100644
--- a/Source/WebCore/loader/DocumentLoader.h
+++ b/Source/WebCore/loader/DocumentLoader.h
@@ -264,6 +264,9 @@
AutoplayPolicy autoplayPolicy() const { return m_autoplayPolicy; }
void setAutoplayPolicy(AutoplayPolicy policy) { m_autoplayPolicy = policy; }
+ void setCustomUserAgent(const String& customUserAgent) { m_customUserAgent = customUserAgent; }
+ const String& customUserAgent() const { return m_customUserAgent; }
+
OptionSet<AutoplayQuirk> allowedAutoplayQuirks() const { return m_allowedAutoplayQuirks; }
void setAllowedAutoplayQuirks(OptionSet<AutoplayQuirk> allowedQuirks) { m_allowedAutoplayQuirks = allowedQuirks; }
@@ -532,6 +535,7 @@
HashMap<String, RefPtr<StyleSheetContents>> m_pendingNamedContentExtensionStyleSheets;
HashMap<String, Vector<std::pair<String, uint32_t>>> m_pendingContentExtensionDisplayNoneSelectors;
#endif
+ String m_customUserAgent;
bool m_userContentExtensionsEnabled { true };
AutoplayPolicy m_autoplayPolicy { AutoplayPolicy::Default };
OptionSet<AutoplayQuirk> m_allowedAutoplayQuirks;
diff --git a/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp
index a857e8f..8336522 100644
--- a/Source/WebCore/loader/FrameLoader.cpp
+++ b/Source/WebCore/loader/FrameLoader.cpp
@@ -2664,6 +2664,12 @@
String FrameLoader::userAgent(const URL& url) const
{
+ if (auto* documentLoader = m_frame.mainFrame().loader().activeDocumentLoader()) {
+ auto& customUserAgent = documentLoader->customUserAgent();
+ if (!customUserAgent.isEmpty())
+ return customUserAgent;
+ }
+
return m_client.userAgent(url);
}
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index bc0c269..ecd96cc 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,33 @@
+2018-12-10 Chris Dumez <cdumez@apple.com>
+
+ Add SPI to allow the client to set the user-agent at main frame level, from the UIProcess
+ https://bugs.webkit.org/show_bug.cgi?id=192509
+ <rdar://problem/46500832>
+
+ Reviewed by Alex Christensen.
+
+ Add SPI to allow the client to set the user-agent at main frame level, from the UIProcess instead of doing
+ it at resource-level from the injected bundle.
+
+ The custom user-agent string can now be set on _WKWebsitePolicies during the
+ decidePolicyForNavigationAction for the main feame, and will impact this main resource load as well as its
+ future subresource loads.
+
+ * 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 setCustomUserAgent:]):
+ (-[_WKWebsitePolicies customUserAgent]):
+ * UIProcess/Cocoa/NavigationState.mm:
+ (WebKit::NavigationState::NavigationClient::decidePolicyForNavigationAction):
+
2018-12-10 Alex Christensen <achristensen@webkit.org>
Safe browsing warning should layout buttons vertically in narrow WKWebViews
diff --git a/Source/WebKit/Shared/WebsitePoliciesData.cpp b/Source/WebKit/Shared/WebsitePoliciesData.cpp
index 6073b2f..df6d3ae 100644
--- a/Source/WebKit/Shared/WebsitePoliciesData.cpp
+++ b/Source/WebKit/Shared/WebsitePoliciesData.cpp
@@ -42,6 +42,7 @@
encoder << customHeaderFields;
encoder << popUpPolicy;
encoder << websiteDataStoreParameters;
+ encoder << customUserAgent;
}
std::optional<WebsitePoliciesData> WebsitePoliciesData::decode(IPC::Decoder& decoder)
@@ -75,6 +76,11 @@
decoder >> websiteDataStoreParameters;
if (!websiteDataStoreParameters)
return std::nullopt;
+
+ std::optional<String> customUserAgent;
+ decoder >> customUserAgent;
+ if (!customUserAgent)
+ return std::nullopt;
return { {
WTFMove(*contentBlockersEnabled),
@@ -83,12 +89,14 @@
WTFMove(*customHeaderFields),
WTFMove(*popUpPolicy),
WTFMove(*websiteDataStoreParameters),
+ WTFMove(*customUserAgent),
} };
}
void WebsitePoliciesData::applyToDocumentLoader(WebsitePoliciesData&& websitePolicies, WebCore::DocumentLoader& documentLoader)
{
documentLoader.setCustomHeaderFields(WTFMove(websitePolicies.customHeaderFields));
+ documentLoader.setCustomUserAgent(websitePolicies.customUserAgent);
// Only setUserContentExtensionsEnabled if it hasn't already been disabled by reloading without content blockers.
if (documentLoader.userContentExtensionsEnabled())
diff --git a/Source/WebKit/Shared/WebsitePoliciesData.h b/Source/WebKit/Shared/WebsitePoliciesData.h
index 447df65..7944c46 100644
--- a/Source/WebKit/Shared/WebsitePoliciesData.h
+++ b/Source/WebKit/Shared/WebsitePoliciesData.h
@@ -52,6 +52,7 @@
Vector<WebCore::HTTPHeaderField> customHeaderFields;
WebsitePopUpPolicy popUpPolicy { WebsitePopUpPolicy::Default };
std::optional<WebsiteDataStoreParameters> websiteDataStoreParameters;
+ String customUserAgent;
void encode(IPC::Encoder&) const;
static std::optional<WebsitePoliciesData> decode(IPC::Decoder&);
diff --git a/Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp b/Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp
index 740b6b5..f441442 100644
--- a/Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp
+++ b/Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp
@@ -56,7 +56,7 @@
std::optional<WebKit::WebsiteDataStoreParameters> parameters;
if (m_websiteDataStore)
parameters = m_websiteDataStore->websiteDataStore().parameters();
- return { contentBlockersEnabled(), allowedAutoplayQuirks(), autoplayPolicy(), customHeaderFields(), popUpPolicy(), WTFMove(parameters) };
+ return { contentBlockersEnabled(), allowedAutoplayQuirks(), autoplayPolicy(), customHeaderFields(), popUpPolicy(), WTFMove(parameters), m_customUserAgent };
}
}
diff --git a/Source/WebKit/UIProcess/API/APIWebsitePolicies.h b/Source/WebKit/UIProcess/API/APIWebsitePolicies.h
index 95ae1a1..5f66abe 100644
--- a/Source/WebKit/UIProcess/API/APIWebsitePolicies.h
+++ b/Source/WebKit/UIProcess/API/APIWebsitePolicies.h
@@ -69,6 +69,9 @@
WebKit::WebsitePoliciesData data();
+ void setCustomUserAgent(const WTF::String& customUserAgent) { m_customUserAgent = customUserAgent; }
+ const WTF::String& customUserAgent() const { return m_customUserAgent; }
+
private:
WebsitePolicies(bool contentBlockersEnabled, OptionSet<WebKit::WebsiteAutoplayQuirk>, WebKit::WebsiteAutoplayPolicy, Vector<WebCore::HTTPHeaderField>&&, WebKit::WebsitePopUpPolicy, RefPtr<WebsiteDataStore>&&);
@@ -78,6 +81,7 @@
Vector<WebCore::HTTPHeaderField> m_customHeaderFields;
WebKit::WebsitePopUpPolicy m_popUpPolicy { WebKit::WebsitePopUpPolicy::Default };
RefPtr<WebsiteDataStore> m_websiteDataStore;
+ WTF::String m_customUserAgent;
};
} // namespace API
diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.h b/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.h
index fe95705d..ead5019 100644
--- a/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.h
+++ b/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.h
@@ -57,6 +57,7 @@
@property (nonatomic, copy) NSDictionary<NSString *, NSString *> *customHeaderFields WK_API_AVAILABLE(macosx(10.13.4), ios(11.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));
@end
diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm b/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm
index 375f08d..03bd177 100644
--- a/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm
+++ b/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm
@@ -183,6 +183,16 @@
_websitePolicies->setWebsiteDataStore(websiteDataStore->_websiteDataStore.get());
}
+- (void)setCustomUserAgent:(NSString *)customUserAgent
+{
+ _websitePolicies->setCustomUserAgent(customUserAgent);
+}
+
+- (NSString *)customUserAgent
+{
+ return _websitePolicies->customUserAgent();
+}
+
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; contentBlockersEnabled = %d>", NSStringFromClass(self.class), self, self.contentBlockersEnabled];
diff --git a/Source/WebKit/UIProcess/Cocoa/NavigationState.mm b/Source/WebKit/UIProcess/Cocoa/NavigationState.mm
index 6f82d54..eab71ad 100644
--- a/Source/WebKit/UIProcess/Cocoa/NavigationState.mm
+++ b/Source/WebKit/UIProcess/Cocoa/NavigationState.mm
@@ -547,6 +547,8 @@
if (subframeNavigation)
[NSException raise:NSInvalidArgumentException format:@"_WKWebsitePolicies.websiteDataStore must be nil for subframe navigations."];
}
+ if (!apiWebsitePolicies->customUserAgent().isNull() && subframeNavigation)
+ [NSException raise:NSInvalidArgumentException format:@"_WKWebsitePolicies.customUserAgent must be nil for subframe navigations."];
}
switch (actionPolicy) {
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index 7592afc..04c14c2 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,20 @@
+2018-12-10 Chris Dumez <cdumez@apple.com>
+
+ Add SPI to allow the client to set the user-agent at main frame level, from the UIProcess
+ https://bugs.webkit.org/show_bug.cgi?id=192509
+ <rdar://problem/46500832>
+
+ Reviewed by Alex Christensen.
+
+ Add API test coverage.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm:
+ (-[DataMappingSchemeHandler addMappingFromURLString:toData:]):
+ (-[DataMappingSchemeHandler webView:startURLSchemeTask:]):
+ (-[DataMappingSchemeHandler webView:stopURLSchemeTask:]):
+ (-[CustomUserAgentDelegate _webView:decidePolicyForNavigationAction:userInfo:decisionHandler:]):
+ (-[CustomUserAgentDelegate webView:didFinishNavigation:]):
+
2018-12-10 Wenson Hsieh <wenson_hsieh@apple.com>
[iOS] Caret is obscured by finger when dragging over an editable element
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm
index f146b79..88c4af4 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm
@@ -38,8 +38,10 @@
#import <WebKit/_WKUserContentExtensionStorePrivate.h>
#import <WebKit/_WKWebsiteDataStoreConfiguration.h>
#import <WebKit/_WKWebsitePolicies.h>
+#import <wtf/HashMap.h>
#import <wtf/MainThread.h>
#import <wtf/RetainPtr.h>
+#import <wtf/text/StringHash.h>
#import <wtf/text/WTFString.h>
#if PLATFORM(IOS_FAMILY)
@@ -54,6 +56,7 @@
static bool doneCompiling;
static bool receivedAlert;
+static bool finishedNavigation;
#if PLATFORM(MAC)
static std::optional<_WKAutoplayEvent> receivedAutoplayEvent;
@@ -859,6 +862,118 @@
TestWebKitAPI::Util::run(&thirdTestDone);
}
+static unsigned loadCount;
+
+@interface DataMappingSchemeHandler : NSObject <WKURLSchemeHandler> {
+ HashMap<String, RetainPtr<NSData *>> _dataMappings;
+}
+- (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data;
+@end
+
+@implementation DataMappingSchemeHandler
+
+- (void)addMappingFromURLString:(NSString *)urlString toData:(const char*)data
+{
+ _dataMappings.set(urlString, [NSData dataWithBytesNoCopy:(void*)data length:strlen(data) freeWhenDone:NO]);
+}
+
+- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
+{
+ NSURL *finalURL = task.request.URL;
+
+ ++loadCount;
+ EXPECT_STREQ("Foo Custom UserAgent", [[task.request valueForHTTPHeaderField:@"User-Agent"] UTF8String]);
+
+ auto response = adoptNS([[NSURLResponse alloc] initWithURL:finalURL MIMEType:@"text/html" expectedContentLength:1 textEncodingName:nil]);
+ [task didReceiveResponse:response.get()];
+
+ if (auto data = _dataMappings.get([finalURL absoluteString]))
+ [task didReceiveData:data.get()];
+ else
+ [task didReceiveData:[@"Hello" dataUsingEncoding:NSUTF8StringEncoding]];
+ [task didFinish];
+}
+
+- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
+{
+}
+
+@end
+
+@interface CustomUserAgentDelegate : NSObject <WKNavigationDelegate> {
+}
+@end
+
+@implementation CustomUserAgentDelegate
+
+- (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 setCustomUserAgent:@"Foo Custom UserAgent"];
+
+ decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
+}
+
+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
+{
+ finishedNavigation = true;
+}
+
+@end
+
+static const char* customUserAgentMainFrameTestBytes = R"TESTRESOURCE(
+<script src="test://www.webkit.org/script.js"></script>
+<img src="test://www.webkit.org/image.png"></img>
+<iframe src="test://www.apple.com/subframe.html"></iframe>
+<script>
+onload = () => {
+ setTimeout(() => {
+ fetch("test://www.webkit.org/fetchResource.html");
+ }, 0);
+}
+</script>
+)TESTRESOURCE";
+
+static const char* customUserAgentSubFrameTestBytes = R"TESTRESOURCE(
+<script src="test://www.apple.com/script.js"></script>
+<img src="test://www.apple.com/image.png"></img>
+<iframe src="test://www.apple.com/subframe2.html"></iframe>
+<script>
+onload = () => {
+ setTimeout(() => {
+ fetch("test://www.apple.com/fetchResource.html");
+ }, 0);
+}
+</script>
+)TESTRESOURCE";
+
+TEST(WebKit, WebsitePoliciesCustomUserAgent)
+{
+ 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];
+ [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"test"];
+
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+ auto delegate = adoptNS([[CustomUserAgentDelegate 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();
+ loadCount = 0;
+}
+
@interface PopUpPoliciesDelegate : NSObject <WKNavigationDelegate, WKUIDelegatePrivate>
@property (nonatomic, copy) _WKWebsitePopUpPolicy(^popUpPolicyForURL)(NSURL *);
@end