Add SPI to set a list of hosts to which to send custom header fields cross-origin
https://bugs.webkit.org/show_bug.cgi?id=197397

Patch by Alex Christensen <achristensen@webkit.org> on 2019-05-16
Reviewed by Geoff Garen.

Source/WebCore:

In r223001 I added the ability to send custom headers, but with a restriction that they will not be sent except to the origin of the main document.
We need the ability to specify what origins to send these headers to even if they are not first party requests.
We get this information in a list of strings which are the hosts to send the headers to.  Some of the strings have an asterisk at the beginning,
indicating that the headers are to be sent to all subdomains.

I repurposed some ObjC SPI that was never adopted, but I keep testing the C API that was to verify no regression.
I also added some new API tests for the new behavior.

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* loader/CustomHeaderFields.cpp: Added.
(WebCore::CustomHeaderFields::thirdPartyDomainsMatch const):
* loader/CustomHeaderFields.h: Added.
(WebCore::CustomHeaderFields::encode const):
(WebCore::CustomHeaderFields::decode):
* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::setCustomHeaderFields): Deleted.
* loader/DocumentLoader.h:
(WebCore::DocumentLoader::setCustomHeaderFields):
(WebCore::DocumentLoader::customHeaderFields const):
(WebCore::DocumentLoader::customHeaderFields): Deleted.
* loader/cache/CachedResourceLoader.cpp:
(WebCore::CachedResourceLoader::requestResource):

Source/WebKit:

* Shared/API/APIObject.h:
* Shared/Cocoa/APIObject.mm:
(API::Object::newObject):
* Shared/WebsitePoliciesData.cpp:
(WebKit::WebsitePoliciesData::decode):
* Shared/WebsitePoliciesData.h:
* SourcesCocoa.txt:
* UIProcess/API/APICustomHeaderFields.h: Added.
* UIProcess/API/APIWebsitePolicies.cpp:
(API::WebsitePolicies::WebsitePolicies):
(API::WebsitePolicies::copy const):
(API::WebsitePolicies::data):
* UIProcess/API/APIWebsitePolicies.h:
* UIProcess/API/C/WKWebsitePolicies.cpp:
(WKWebsitePoliciesCopyCustomHeaderFields):
(WKWebsitePoliciesSetCustomHeaderFields):
* UIProcess/API/Cocoa/WKWebpagePreferences.mm:
(-[WKWebpagePreferences _customHeaderFields]):
(-[WKWebpagePreferences _setCustomHeaderFields:]):
* UIProcess/API/Cocoa/WKWebpagePreferencesPrivate.h:
* UIProcess/API/Cocoa/_WKCustomHeaderFields.h: Added.
* UIProcess/API/Cocoa/_WKCustomHeaderFields.mm: Added.
(-[_WKCustomHeaderFields init]):
(-[_WKCustomHeaderFields dealloc]):
(-[_WKCustomHeaderFields fields]):
(-[_WKCustomHeaderFields setFields:]):
(-[_WKCustomHeaderFields thirdPartyDomains]):
(-[_WKCustomHeaderFields setThirdPartyDomains:]):
(-[_WKCustomHeaderFields _apiObject]):
* UIProcess/API/Cocoa/_WKCustomHeaderFieldsInternal.h: Added.
* UIProcess/API/Cocoa/_WKWebsitePolicies.h:
* UIProcess/API/Cocoa/_WKWebsitePolicies.mm:
(-[_WKWebsitePolicies customHeaderFields]): Deleted.
(-[_WKWebsitePolicies setCustomHeaderFields:]): Deleted.
* UIProcess/Cocoa/WebViewImpl.h:
* UIProcess/Cocoa/WebViewImpl.mm:
(WebKit::WebViewImpl::takeFocus):
(WebKit::WebViewImpl::accessibilityAttributeValue):
* WebKit.xcodeproj/project.pbxproj:

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm:
(TEST):
(expectLegacyHeaders):
(expectHeaders):
(-[CustomHeaderFieldsDelegate webView:decidePolicyForNavigationAction:preferences:decisionHandler:]):
(-[CustomHeaderFieldsDelegate webView:startURLSchemeTask:]):
(-[CustomHeaderFieldsDelegate _webView:decidePolicyForNavigationAction:decisionHandler:]): Deleted.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@245401 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 0bd242b..52b8265 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,34 @@
+2019-05-16  Alex Christensen  <achristensen@webkit.org>
+
+        Add SPI to set a list of hosts to which to send custom header fields cross-origin
+        https://bugs.webkit.org/show_bug.cgi?id=197397
+
+        Reviewed by Geoff Garen.
+
+        In r223001 I added the ability to send custom headers, but with a restriction that they will not be sent except to the origin of the main document.
+        We need the ability to specify what origins to send these headers to even if they are not first party requests.
+        We get this information in a list of strings which are the hosts to send the headers to.  Some of the strings have an asterisk at the beginning,
+        indicating that the headers are to be sent to all subdomains.
+
+        I repurposed some ObjC SPI that was never adopted, but I keep testing the C API that was to verify no regression.
+        I also added some new API tests for the new behavior.
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * loader/CustomHeaderFields.cpp: Added.
+        (WebCore::CustomHeaderFields::thirdPartyDomainsMatch const):
+        * loader/CustomHeaderFields.h: Added.
+        (WebCore::CustomHeaderFields::encode const):
+        (WebCore::CustomHeaderFields::decode):
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::setCustomHeaderFields): Deleted.
+        * loader/DocumentLoader.h:
+        (WebCore::DocumentLoader::setCustomHeaderFields):
+        (WebCore::DocumentLoader::customHeaderFields const):
+        (WebCore::DocumentLoader::customHeaderFields): Deleted.
+        * loader/cache/CachedResourceLoader.cpp:
+        (WebCore::CachedResourceLoader::requestResource):
+
 2019-05-16  Ali Juma  <ajuma@chromium.org>
 
         [IntersectionObserver] Regression: No initial observation when nothing else triggers rendering
diff --git a/Source/WebCore/Headers.cmake b/Source/WebCore/Headers.cmake
index 83dbea6..83427f0 100644
--- a/Source/WebCore/Headers.cmake
+++ b/Source/WebCore/Headers.cmake
@@ -654,6 +654,7 @@
     loader/CookieJar.h
     loader/CrossOriginAccessControl.h
     loader/CrossOriginPreflightResultCache.h
+    loader/CustomHeaderFields.h
     loader/DocumentLoader.h
     loader/DocumentWriter.h
     loader/EmptyClients.h
diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt
index 7c3f89c..3675e00 100644
--- a/Source/WebCore/Sources.txt
+++ b/Source/WebCore/Sources.txt
@@ -1364,6 +1364,7 @@
 loader/CrossOriginAccessControl.cpp
 loader/CrossOriginPreflightChecker.cpp
 loader/CrossOriginPreflightResultCache.cpp
+loader/CustomHeaderFields.cpp @no-unify
 loader/DocumentLoader.cpp
 loader/DocumentThreadableLoader.cpp
 loader/DocumentWriter.cpp
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index 1403a66..f69cff2 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -1872,12 +1872,14 @@
 		5C4304B0191AC908000E2BC0 /* EXTShaderTextureLOD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C4304AD191AC908000E2BC0 /* EXTShaderTextureLOD.cpp */; };
 		5C4304B1191AC908000E2BC0 /* EXTShaderTextureLOD.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C4304AE191AC908000E2BC0 /* EXTShaderTextureLOD.h */; };
 		5C4304B6191AEF46000E2BC0 /* JSEXTShaderTextureLOD.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C4304B4191AEF46000E2BC0 /* JSEXTShaderTextureLOD.h */; };
+		5C5D2385227A0652000B9BDA /* CustomHeaderFields.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C5D2383227A063A000B9BDA /* CustomHeaderFields.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5C7C88D81D0F1F4A009D2F6D /* SocketProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C7C88D71D0F1F2B009D2F6D /* SocketProvider.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5C9C2DB52241A67B00996B0B /* ContentRuleListResults.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C9C2DB32241A67300996B0B /* ContentRuleListResults.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5C9EF2F321F06190003BDC56 /* StorageSessionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C9EF2F221F06171003BDC56 /* StorageSessionProvider.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5CA1DEC61F71F1C700E71BD3 /* HTTPHeaderField.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CA1DEC41F71E68700E71BD3 /* HTTPHeaderField.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5CB37FFF1C62D2A100F20188 /* ScrollAnimatorMock.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CB37FFD1C62D27800F20188 /* ScrollAnimatorMock.h */; };
 		5CBC8DAD1AAA302200E1C803 /* MediaAccessibilitySoftLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CBC8DAB1AAA302200E1C803 /* MediaAccessibilitySoftLink.h */; };
+		5CBD59592280E926002B22AA /* CustomHeaderFields.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C5D2386227A077C000B9BDA /* CustomHeaderFields.cpp */; };
 		5CD9F5661AA0F73C00DA45FF /* DFABytecode.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C39305D1AA0F6A90029C816 /* DFABytecode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5CD9F5671AA0F74200DA45FF /* DFABytecodeCompiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C39305F1AA0F6A90029C816 /* DFABytecodeCompiler.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5CD9F5681AA0F74600DA45FF /* DFABytecodeInterpreter.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C3930611AA0F6A90029C816 /* DFABytecodeInterpreter.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -8832,6 +8834,8 @@
 		5C5381B11D87D45700E2EBE6 /* URLSearchParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = URLSearchParams.h; sourceTree = "<group>"; };
 		5C5381B31D87E08100E2EBE6 /* JSURLSearchParams.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSURLSearchParams.cpp; sourceTree = "<group>"; };
 		5C5381B41D87E08100E2EBE6 /* JSURLSearchParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSURLSearchParams.h; sourceTree = "<group>"; };
+		5C5D2383227A063A000B9BDA /* CustomHeaderFields.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomHeaderFields.h; sourceTree = "<group>"; };
+		5C5D2386227A077C000B9BDA /* CustomHeaderFields.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CustomHeaderFields.cpp; sourceTree = "<group>"; };
 		5C668E641E7C6C3500D32B3B /* SocketStreamHandleImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SocketStreamHandleImpl.cpp; sourceTree = "<group>"; };
 		5C688AA01D380509000B54FA /* ThreadableWebSocketChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadableWebSocketChannel.cpp; sourceTree = "<group>"; };
 		5C688AA21D38126F000B54FA /* SocketProvider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SocketProvider.cpp; sourceTree = "<group>"; };
@@ -25011,6 +25015,8 @@
 				41ABE67A1D0580D5006D862D /* CrossOriginPreflightChecker.h */,
 				E1C415DD0F655D7C0092D2FB /* CrossOriginPreflightResultCache.cpp */,
 				E1C415D90F655D6F0092D2FB /* CrossOriginPreflightResultCache.h */,
+				5C5D2386227A077C000B9BDA /* CustomHeaderFields.cpp */,
+				5C5D2383227A063A000B9BDA /* CustomHeaderFields.h */,
 				93E227DB0AF589AD00D48324 /* DocumentLoader.cpp */,
 				656D371E0ADBA5DE00A4554D /* DocumentLoader.h */,
 				0B9056150F2578BE0095FF6A /* DocumentThreadableLoader.cpp */,
@@ -28685,6 +28691,7 @@
 				93D437A01D57B19A00AB85EA /* CustomElementReactionQueue.h in Headers */,
 				9BD4E91B1C462CFC005065BC /* CustomElementRegistry.h in Headers */,
 				62CD325A1157E57C0063B0A7 /* CustomEvent.h in Headers */,
+				5C5D2385227A0652000B9BDA /* CustomHeaderFields.h in Headers */,
 				4B1E13E721790D660042CF98 /* CustomPaintCanvas.h in Headers */,
 				4B7AE4932177B56F00C59959 /* CustomPaintImage.h in Headers */,
 				A8CB413E0E8633FD0032C4F0 /* DashArray.h in Headers */,
@@ -32609,6 +32616,7 @@
 				07AFF4231EFB144900B545B3 /* CoreAudioCaptureSourceIOS.mm in Sources */,
 				46C696CC1E7205FC00597937 /* CPUMonitor.cpp in Sources */,
 				1ABA76CA11D20E50004C201C /* CSSPropertyNames.cpp in Sources */,
+				5CBD59592280E926002B22AA /* CustomHeaderFields.cpp in Sources */,
 				BE23480C18A9870B00E4B6E8 /* DataCue.cpp in Sources */,
 				4463CF682212FA68001A8577 /* DataDetectorsCoreSoftLink.mm in Sources */,
 				E58B45BB20AD07DD00991025 /* DataListButtonElement.cpp in Sources */,
diff --git a/Source/WebCore/loader/CustomHeaderFields.cpp b/Source/WebCore/loader/CustomHeaderFields.cpp
new file mode 100644
index 0000000..deee438
--- /dev/null
+++ b/Source/WebCore/loader/CustomHeaderFields.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "CustomHeaderFields.h"
+
+#include "HTTPHeaderField.h"
+#include "RegistrableDomain.h"
+
+namespace WebCore {
+
+bool CustomHeaderFields::thirdPartyDomainsMatch(const URL& url) const
+{
+    if (thirdPartyDomains.isEmpty())
+        return false;
+
+    auto registrableDomainLength = RegistrableDomain(url).string().length();
+    for (const auto& domainOrPattern : thirdPartyDomains) {
+        if (domainOrPattern == url.host())
+            return true;
+        if (domainOrPattern.startsWith("*.")
+            && url.host().endsWith(StringView(domainOrPattern).substring(1))
+            && domainOrPattern.length() > registrableDomainLength)
+            return true;
+    }
+
+    return false;
+}
+
+}
diff --git a/Source/WebCore/loader/CustomHeaderFields.h b/Source/WebCore/loader/CustomHeaderFields.h
new file mode 100644
index 0000000..660ff0a
--- /dev/null
+++ b/Source/WebCore/loader/CustomHeaderFields.h
@@ -0,0 +1,67 @@
+/*
+ * 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. ``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
+ * 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.
+ */
+
+#pragma once
+
+#include "HTTPHeaderField.h"
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+struct WEBCORE_EXPORT CustomHeaderFields {
+    Vector<HTTPHeaderField> fields;
+    Vector<String> thirdPartyDomains;
+
+    bool thirdPartyDomainsMatch(const URL&) const;
+
+    template<class Encoder> void encode(Encoder&) const;
+    template<class Decoder> static Optional<CustomHeaderFields> decode(Decoder&);
+};
+
+template<class Encoder>
+void CustomHeaderFields::encode(Encoder& encoder) const
+{
+    encoder << fields;
+    encoder << thirdPartyDomains;
+}
+
+template<class Decoder>
+Optional<CustomHeaderFields> CustomHeaderFields::decode(Decoder& decoder)
+{
+    Optional<Vector<HTTPHeaderField>> fields;
+    decoder >> fields;
+    if (!fields)
+        return WTF::nullopt;
+    
+    Optional<Vector<String>> thirdPartyDomains;
+    decoder >> thirdPartyDomains;
+    if (!thirdPartyDomains)
+        return WTF::nullopt;
+    
+    return {{ WTFMove(*fields), WTFMove(*thirdPartyDomains) }};
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/loader/DocumentLoader.cpp b/Source/WebCore/loader/DocumentLoader.cpp
index fceea1b..10cfdd9 100644
--- a/Source/WebCore/loader/DocumentLoader.cpp
+++ b/Source/WebCore/loader/DocumentLoader.cpp
@@ -38,6 +38,7 @@
 #include "CachedResourceLoader.h"
 #include "ContentExtensionError.h"
 #include "ContentSecurityPolicy.h"
+#include "CustomHeaderFields.h"
 #include "DOMWindow.h"
 #include "Document.h"
 #include "DocumentParser.h"
@@ -53,7 +54,6 @@
 #include "FrameTree.h"
 #include "HTMLFormElement.h"
 #include "HTMLFrameOwnerElement.h"
-#include "HTTPHeaderField.h"
 #include "HTTPHeaderNames.h"
 #include "HistoryItem.h"
 #include "HistoryController.h"
@@ -1322,11 +1322,6 @@
 }
 #endif
 
-void DocumentLoader::setCustomHeaderFields(Vector<HTTPHeaderField>&& fields)
-{
-    m_customHeaderFields = WTFMove(fields);
-}
-
 bool DocumentLoader::isLoadingInAPISense() const
 {
     // Once a frame has loaded, we no longer need to consider subresources,
diff --git a/Source/WebCore/loader/DocumentLoader.h b/Source/WebCore/loader/DocumentLoader.h
index b1445e7..8144382 100644
--- a/Source/WebCore/loader/DocumentLoader.h
+++ b/Source/WebCore/loader/DocumentLoader.h
@@ -76,10 +76,10 @@
 class CachedRawResource;
 class CachedResourceLoader;
 class ContentFilter;
+struct CustomHeaderFields;
 class FormState;
 class Frame;
 class FrameLoader;
-class HTTPHeaderField;
 class IconLoader;
 class Page;
 class PreviewConverter;
@@ -371,8 +371,8 @@
     void finishedLoadingApplicationManifest(ApplicationManifestLoader&);
 #endif
 
-    WEBCORE_EXPORT void setCustomHeaderFields(Vector<HTTPHeaderField>&& fields);
-    const Vector<HTTPHeaderField>& customHeaderFields() { return m_customHeaderFields; }
+    void setCustomHeaderFields(Vector<CustomHeaderFields>&& fields) { m_customHeaderFields = WTFMove(fields); }
+    const Vector<CustomHeaderFields>& customHeaderFields() const { return m_customHeaderFields; }
 
     void setAllowsWebArchiveForMainFrame(bool allowsWebArchiveForMainFrame) { m_allowsWebArchiveForMainFrame = allowsWebArchiveForMainFrame; }
     bool allowsWebArchiveForMainFrame() const { return m_allowsWebArchiveForMainFrame; }
@@ -564,7 +564,7 @@
     HashMap<std::unique_ptr<ApplicationManifestLoader>, uint64_t> m_applicationManifestLoaders;
 #endif
 
-    Vector<HTTPHeaderField> m_customHeaderFields;
+    Vector<CustomHeaderFields> m_customHeaderFields;
     
     bool m_subresourceLoadersArePageCacheAcceptable { false };
     ShouldOpenExternalURLsPolicy m_shouldOpenExternalURLsPolicy { ShouldOpenExternalURLsPolicy::ShouldNotAllow };
diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.cpp b/Source/WebCore/loader/cache/CachedResourceLoader.cpp
index 5b0d4e0..843a9d1 100644
--- a/Source/WebCore/loader/cache/CachedResourceLoader.cpp
+++ b/Source/WebCore/loader/cache/CachedResourceLoader.cpp
@@ -43,6 +43,7 @@
 #include "ContentRuleListResults.h"
 #include "ContentSecurityPolicy.h"
 #include "CrossOriginAccessControl.h"
+#include "CustomHeaderFields.h"
 #include "DOMWindow.h"
 #include "DiagnosticLoggingClient.h"
 #include "DiagnosticLoggingKeys.h"
@@ -858,9 +859,11 @@
             sameOriginRequest = document()->topDocument().securityOrigin().isSameSchemeHostPort(requestedOrigin.get())
                 && document()->securityOrigin().isSameSchemeHostPort(requestedOrigin.get());
         }
-        if (sameOriginRequest) {
-            for (auto& field : m_documentLoader->customHeaderFields())
-                request.resourceRequest().setHTTPHeaderField(field.name(), field.value());
+        for (auto& fields : m_documentLoader->customHeaderFields()) {
+            if (sameOriginRequest || fields.thirdPartyDomainsMatch(url)) {
+                for (auto& field : fields.fields)
+                    request.resourceRequest().setHTTPHeaderField(field.name(), field.value());
+            }
         }
     }
 
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index cecd952..59b4370 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,50 @@
+2019-05-16  Alex Christensen  <achristensen@webkit.org>
+
+        Add SPI to set a list of hosts to which to send custom header fields cross-origin
+        https://bugs.webkit.org/show_bug.cgi?id=197397
+
+        Reviewed by Geoff Garen.
+
+        * Shared/API/APIObject.h:
+        * Shared/Cocoa/APIObject.mm:
+        (API::Object::newObject):
+        * Shared/WebsitePoliciesData.cpp:
+        (WebKit::WebsitePoliciesData::decode):
+        * Shared/WebsitePoliciesData.h:
+        * SourcesCocoa.txt:
+        * UIProcess/API/APICustomHeaderFields.h: Added.
+        * UIProcess/API/APIWebsitePolicies.cpp:
+        (API::WebsitePolicies::WebsitePolicies):
+        (API::WebsitePolicies::copy const):
+        (API::WebsitePolicies::data):
+        * UIProcess/API/APIWebsitePolicies.h:
+        * UIProcess/API/C/WKWebsitePolicies.cpp:
+        (WKWebsitePoliciesCopyCustomHeaderFields):
+        (WKWebsitePoliciesSetCustomHeaderFields):
+        * UIProcess/API/Cocoa/WKWebpagePreferences.mm:
+        (-[WKWebpagePreferences _customHeaderFields]):
+        (-[WKWebpagePreferences _setCustomHeaderFields:]):
+        * UIProcess/API/Cocoa/WKWebpagePreferencesPrivate.h:
+        * UIProcess/API/Cocoa/_WKCustomHeaderFields.h: Added.
+        * UIProcess/API/Cocoa/_WKCustomHeaderFields.mm: Added.
+        (-[_WKCustomHeaderFields init]):
+        (-[_WKCustomHeaderFields dealloc]):
+        (-[_WKCustomHeaderFields fields]):
+        (-[_WKCustomHeaderFields setFields:]):
+        (-[_WKCustomHeaderFields thirdPartyDomains]):
+        (-[_WKCustomHeaderFields setThirdPartyDomains:]):
+        (-[_WKCustomHeaderFields _apiObject]):
+        * UIProcess/API/Cocoa/_WKCustomHeaderFieldsInternal.h: Added.
+        * UIProcess/API/Cocoa/_WKWebsitePolicies.h:
+        * UIProcess/API/Cocoa/_WKWebsitePolicies.mm:
+        (-[_WKWebsitePolicies customHeaderFields]): Deleted.
+        (-[_WKWebsitePolicies setCustomHeaderFields:]): Deleted.
+        * UIProcess/Cocoa/WebViewImpl.h:
+        * UIProcess/Cocoa/WebViewImpl.mm:
+        (WebKit::WebViewImpl::takeFocus):
+        (WebKit::WebViewImpl::accessibilityAttributeValue):
+        * WebKit.xcodeproj/project.pbxproj:
+
 2019-05-16  Ryan Walklin  <ryan@testtoast.com>
 
         [WPE] Rendering on a HiDPI display looks scaled up instead of rendered at 2x
diff --git a/Source/WebKit/Shared/API/APIObject.h b/Source/WebKit/Shared/API/APIObject.h
index 2ae85fc..de208c4 100644
--- a/Source/WebKit/Shared/API/APIObject.h
+++ b/Source/WebKit/Shared/API/APIObject.h
@@ -111,6 +111,7 @@
         ContentRuleListStore,
         ContextMenuListener,
         CookieManager,
+        CustomHeaderFields,
         InternalDebugFeature,
         Download,
         ExperimentalFeature,
diff --git a/Source/WebKit/Shared/Cocoa/APIObject.mm b/Source/WebKit/Shared/Cocoa/APIObject.mm
index 457fd0c..ee98ca3 100644
--- a/Source/WebKit/Shared/Cocoa/APIObject.mm
+++ b/Source/WebKit/Shared/Cocoa/APIObject.mm
@@ -70,6 +70,7 @@
 #import "_WKAttachmentInternal.h"
 #import "_WKAutomationSessionInternal.h"
 #import "_WKContentRuleListActionInternal.h"
+#import "_WKCustomHeaderFieldsInternal.h"
 #import "_WKDownloadInternal.h"
 #import "_WKExperimentalFeatureInternal.h"
 #import "_WKFrameHandleInternal.h"
@@ -312,6 +313,10 @@
         wrapper = [WKContentRuleListStore alloc];
         break;
 
+    case Type::CustomHeaderFields:
+        wrapper = [_WKCustomHeaderFields alloc];
+        break;
+
     case Type::UserContentWorld:
         wrapper = [_WKUserContentWorld alloc];
         break;
diff --git a/Source/WebKit/Shared/WebsitePoliciesData.cpp b/Source/WebKit/Shared/WebsitePoliciesData.cpp
index 5a54860..f53776a 100644
--- a/Source/WebKit/Shared/WebsitePoliciesData.cpp
+++ b/Source/WebKit/Shared/WebsitePoliciesData.cpp
@@ -28,6 +28,7 @@
 
 #include "ArgumentCoders.h"
 #include "WebProcess.h"
+#include <WebCore/CustomHeaderFields.h>
 #include <WebCore/DocumentLoader.h>
 #include <WebCore/Frame.h>
 #include <WebCore/Page.h>
@@ -77,7 +78,7 @@
     if (!allowedAutoplayQuirks)
         return WTF::nullopt;
     
-    Optional<Vector<WebCore::HTTPHeaderField>> customHeaderFields;
+    Optional<Vector<WebCore::CustomHeaderFields>> customHeaderFields;
     decoder >> customHeaderFields;
     if (!customHeaderFields)
         return WTF::nullopt;
diff --git a/Source/WebKit/Shared/WebsitePoliciesData.h b/Source/WebKit/Shared/WebsitePoliciesData.h
index 6a02fca..040bf06 100644
--- a/Source/WebKit/Shared/WebsitePoliciesData.h
+++ b/Source/WebKit/Shared/WebsitePoliciesData.h
@@ -32,8 +32,8 @@
 #include "WebsiteMetaViewportPolicy.h"
 #include "WebsitePopUpPolicy.h"
 #include "WebsiteSimulatedMouseEventsDispatchPolicy.h"
+#include <WebCore/CustomHeaderFields.h>
 #include <WebCore/DeviceOrientationOrMotionPermissionState.h>
-#include <WebCore/HTTPHeaderField.h>
 #include <wtf/OptionSet.h>
 
 namespace IPC {
@@ -56,7 +56,7 @@
 #if ENABLE(DEVICE_ORIENTATION)
     WebCore::DeviceOrientationOrMotionPermissionState deviceOrientationAndMotionAccessState;
 #endif
-    Vector<WebCore::HTTPHeaderField> customHeaderFields;
+    Vector<WebCore::CustomHeaderFields> customHeaderFields;
     WebsitePopUpPolicy popUpPolicy { WebsitePopUpPolicy::Default };
     Optional<WebsiteDataStoreParameters> websiteDataStoreParameters;
     String customUserAgent;
diff --git a/Source/WebKit/SourcesCocoa.txt b/Source/WebKit/SourcesCocoa.txt
index b81088e..d4a7723 100644
--- a/Source/WebKit/SourcesCocoa.txt
+++ b/Source/WebKit/SourcesCocoa.txt
@@ -242,6 +242,7 @@
 UIProcess/API/Cocoa/_WKAutomationSessionConfiguration.mm
 UIProcess/API/Cocoa/_WKContentRuleListAction.mm
 UIProcess/API/Cocoa/_WKContextMenuElementInfo.mm
+UIProcess/API/Cocoa/_WKCustomHeaderFields.mm @no-unify
 UIProcess/API/Cocoa/_WKDownload.mm
 UIProcess/API/Cocoa/_WKElementAction.mm
 UIProcess/API/Cocoa/_WKErrorRecoveryAttempting.mm
diff --git a/Source/WebKit/UIProcess/API/APICustomHeaderFields.h b/Source/WebKit/UIProcess/API/APICustomHeaderFields.h
new file mode 100644
index 0000000..1c4c2cd
--- /dev/null
+++ b/Source/WebKit/UIProcess/API/APICustomHeaderFields.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "APIObject.h"
+#include <WebCore/CustomHeaderFields.h>
+
+namespace API {
+
+class CustomHeaderFields final : public ObjectImpl<Object::Type::CustomHeaderFields> {
+public:
+    template<typename... Args> static Ref<CustomHeaderFields> create(Args&&... args)
+    {
+        return adoptRef(*new CustomHeaderFields(std::forward<Args>(args)...));
+    }
+
+    CustomHeaderFields() = default;
+
+    const Vector<WebCore::HTTPHeaderField>& fields() const { return m_fields.fields; }
+    void setFields(Vector<WebCore::HTTPHeaderField>&& fields) { m_fields.fields = WTFMove(fields); }
+
+    const Vector<WTF::String> thirdPartyDomains() const { return m_fields.thirdPartyDomains; }
+    void setThirdPartyDomains(Vector<WTF::String>&& domains) { m_fields.thirdPartyDomains = WTFMove(domains); }
+
+    const WebCore::CustomHeaderFields& coreFields() const { return m_fields; }
+
+private:
+    CustomHeaderFields(const WebCore::CustomHeaderFields& fields)
+        : m_fields(fields) { }
+
+    WebCore::CustomHeaderFields m_fields;
+};
+
+} // namespace API
diff --git a/Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp b/Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp
index 99e830c..5cfeb08 100644
--- a/Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp
+++ b/Source/WebKit/UIProcess/API/APIWebsitePolicies.cpp
@@ -33,10 +33,11 @@
 
 WebsitePolicies::WebsitePolicies() = default;
 
-WebsitePolicies::WebsitePolicies(bool contentBlockersEnabled, OptionSet<WebKit::WebsiteAutoplayQuirk> allowedAutoplayQuirks, WebKit::WebsiteAutoplayPolicy autoplayPolicy, Vector<WebCore::HTTPHeaderField>&& customHeaderFields, WebKit::WebsitePopUpPolicy popUpPolicy, RefPtr<WebsiteDataStore>&& websiteDataStore)
+WebsitePolicies::WebsitePolicies(bool contentBlockersEnabled, OptionSet<WebKit::WebsiteAutoplayQuirk> allowedAutoplayQuirks, WebKit::WebsiteAutoplayPolicy autoplayPolicy, Vector<WebCore::HTTPHeaderField>&& legacyCustomHeaderFields, Vector<WebCore::CustomHeaderFields>&& customHeaderFields, WebKit::WebsitePopUpPolicy popUpPolicy, RefPtr<WebsiteDataStore>&& websiteDataStore)
     : m_contentBlockersEnabled(contentBlockersEnabled)
     , m_allowedAutoplayQuirks(allowedAutoplayQuirks)
     , m_autoplayPolicy(autoplayPolicy)
+    , m_legacyCustomHeaderFields(WTFMove(legacyCustomHeaderFields))
     , m_customHeaderFields(WTFMove(customHeaderFields))
     , m_popUpPolicy(popUpPolicy)
     , m_websiteDataStore(WTFMove(websiteDataStore))
@@ -60,10 +61,17 @@
     policies->setMetaViewportPolicy(m_metaViewportPolicy);
     policies->setMediaSourcePolicy(m_mediaSourcePolicy);
     policies->setSimulatedMouseEventsDispatchPolicy(m_simulatedMouseEventsDispatchPolicy);
-    Vector<WebCore::HTTPHeaderField> customHeaderFields;
+    
+    Vector<WebCore::HTTPHeaderField> legacyCustomHeaderFields;
+    legacyCustomHeaderFields.reserveInitialCapacity(m_legacyCustomHeaderFields.size());
+    for (auto& field : m_legacyCustomHeaderFields)
+        legacyCustomHeaderFields.uncheckedAppend(field);
+    policies->setLegacyCustomHeaderFields(WTFMove(legacyCustomHeaderFields));
+
+    Vector<WebCore::CustomHeaderFields> customHeaderFields;
     customHeaderFields.reserveInitialCapacity(m_customHeaderFields.size());
     for (auto& field : m_customHeaderFields)
-        customHeaderFields.append(WebCore::HTTPHeaderField(field));
+        customHeaderFields.uncheckedAppend(field);
     policies->setCustomHeaderFields(WTFMove(customHeaderFields));
     policies->setAllowSiteSpecificQuirksToOverrideCompatibilityMode(m_allowSiteSpecificQuirksToOverrideCompatibilityMode);
     policies->setApplicationNameForUserAgentWithModernCompatibility(m_applicationNameForUserAgentWithModernCompatibility);
@@ -81,6 +89,14 @@
 
 WebKit::WebsitePoliciesData WebsitePolicies::data()
 {
+    bool hasLegacyCustomHeaderFields = legacyCustomHeaderFields().size();
+    Vector<WebCore::CustomHeaderFields> customHeaderFields;
+    customHeaderFields.reserveInitialCapacity(this->customHeaderFields().size() + hasLegacyCustomHeaderFields);
+    for (auto& field : this->customHeaderFields())
+        customHeaderFields.uncheckedAppend(field);
+    if (hasLegacyCustomHeaderFields)
+        customHeaderFields.uncheckedAppend({ legacyCustomHeaderFields(), { }});
+
     return {
         contentBlockersEnabled(),
         allowedAutoplayQuirks(),
@@ -88,7 +104,7 @@
 #if ENABLE(DEVICE_ORIENTATION)
         deviceOrientationAndMotionAccessState(),
 #endif
-        customHeaderFields(),
+        WTFMove(customHeaderFields),
         popUpPolicy(),
         m_websiteDataStore ? Optional<WebKit::WebsiteDataStoreParameters> { m_websiteDataStore->websiteDataStore().parameters() } : WTF::nullopt,
         m_customUserAgent,
diff --git a/Source/WebKit/UIProcess/API/APIWebsitePolicies.h b/Source/WebKit/UIProcess/API/APIWebsitePolicies.h
index 4d49269..50ff7ae 100644
--- a/Source/WebKit/UIProcess/API/APIWebsitePolicies.h
+++ b/Source/WebKit/UIProcess/API/APIWebsitePolicies.h
@@ -33,6 +33,7 @@
 #include "WebsiteMetaViewportPolicy.h"
 #include "WebsitePopUpPolicy.h"
 #include "WebsiteSimulatedMouseEventsDispatchPolicy.h"
+#include <WebCore/CustomHeaderFields.h>
 #include <WebCore/DeviceOrientationOrMotionPermissionState.h>
 #include <WebCore/HTTPHeaderField.h>
 #include <wtf/OptionSet.h>
@@ -68,9 +69,11 @@
     void setDeviceOrientationAndMotionAccessState(WebCore::DeviceOrientationOrMotionPermissionState state) { m_deviceOrientationAndMotionAccessState = state; }
 #endif
 
-    const Vector<WebCore::HTTPHeaderField>& customHeaderFields() const { return m_customHeaderFields; }
-    Vector<WebCore::HTTPHeaderField>&& takeCustomHeaderFields() { return WTFMove(m_customHeaderFields); }
-    void setCustomHeaderFields(Vector<WebCore::HTTPHeaderField>&& fields) { m_customHeaderFields = WTFMove(fields); }
+    const Vector<WebCore::HTTPHeaderField>& legacyCustomHeaderFields() const { return m_legacyCustomHeaderFields; }
+    void setLegacyCustomHeaderFields(Vector<WebCore::HTTPHeaderField>&& fields) { m_legacyCustomHeaderFields = WTFMove(fields); }
+
+    const Vector<WebCore::CustomHeaderFields>& customHeaderFields() const { return m_customHeaderFields; }
+    void setCustomHeaderFields(Vector<WebCore::CustomHeaderFields>&& fields) { m_customHeaderFields = WTFMove(fields); }
 
     WebKit::WebsitePopUpPolicy popUpPolicy() const { return m_popUpPolicy; }
     void setPopUpPolicy(WebKit::WebsitePopUpPolicy policy) { m_popUpPolicy = policy; }
@@ -108,7 +111,7 @@
     void setApplicationNameForUserAgentWithModernCompatibility(const WTF::String& applicationName) { m_applicationNameForUserAgentWithModernCompatibility = applicationName; }
 
 private:
-    WebsitePolicies(bool contentBlockersEnabled, OptionSet<WebKit::WebsiteAutoplayQuirk>, WebKit::WebsiteAutoplayPolicy, Vector<WebCore::HTTPHeaderField>&&, WebKit::WebsitePopUpPolicy, RefPtr<WebsiteDataStore>&&);
+    WebsitePolicies(bool contentBlockersEnabled, OptionSet<WebKit::WebsiteAutoplayQuirk>, WebKit::WebsiteAutoplayPolicy, Vector<WebCore::HTTPHeaderField>&&, Vector<WebCore::CustomHeaderFields>&&, WebKit::WebsitePopUpPolicy, RefPtr<WebsiteDataStore>&&);
 
     bool m_contentBlockersEnabled { true };
     OptionSet<WebKit::WebsiteAutoplayQuirk> m_allowedAutoplayQuirks;
@@ -116,7 +119,8 @@
 #if ENABLE(DEVICE_ORIENTATION)
     WebCore::DeviceOrientationOrMotionPermissionState m_deviceOrientationAndMotionAccessState { WebCore::DeviceOrientationOrMotionPermissionState::Prompt };
 #endif
-    Vector<WebCore::HTTPHeaderField> m_customHeaderFields;
+    Vector<WebCore::HTTPHeaderField> m_legacyCustomHeaderFields;
+    Vector<WebCore::CustomHeaderFields> m_customHeaderFields;
     WebKit::WebsitePopUpPolicy m_popUpPolicy { WebKit::WebsitePopUpPolicy::Default };
     RefPtr<WebsiteDataStore> m_websiteDataStore;
     WTF::String m_customUserAgent;
diff --git a/Source/WebKit/UIProcess/API/C/WKWebsitePolicies.cpp b/Source/WebKit/UIProcess/API/C/WKWebsitePolicies.cpp
index 64dbef8..aea4b8b 100644
--- a/Source/WebKit/UIProcess/API/C/WKWebsitePolicies.cpp
+++ b/Source/WebKit/UIProcess/API/C/WKWebsitePolicies.cpp
@@ -59,7 +59,7 @@
 WK_EXPORT WKDictionaryRef WKWebsitePoliciesCopyCustomHeaderFields(WKWebsitePoliciesRef websitePolicies)
 {
     HashMap<WTF::String, RefPtr<API::Object>> fields;
-    for (const auto& field : toImpl(websitePolicies)->customHeaderFields())
+    for (const auto& field : toImpl(websitePolicies)->legacyCustomHeaderFields())
         fields.add(field.name(), API::String::create(field.value()));
     return toAPI(API::Dictionary::create(WTFMove(fields)).ptr());
 }
@@ -76,7 +76,7 @@
         if (field && startsWithLettersIgnoringASCIICase(field->name(), "x-"))
             fields.uncheckedAppend(WTFMove(*field));
     }
-    toImpl(websitePolicies)->setCustomHeaderFields(WTFMove(fields));
+    toImpl(websitePolicies)->setLegacyCustomHeaderFields(WTFMove(fields));
 }
 
 void WKWebsitePoliciesSetAllowedAutoplayQuirks(WKWebsitePoliciesRef websitePolicies, WKWebsiteAutoplayQuirk allowedQuirks)
diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebpagePreferences.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebpagePreferences.mm
index 6a36b1b..4c99139 100644
--- a/Source/WebKit/UIProcess/API/Cocoa/WKWebpagePreferences.mm
+++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebpagePreferences.mm
@@ -26,9 +26,11 @@
 #import "config.h"
 #import "WKWebpagePreferences.h"
 
+#import "APICustomHeaderFields.h"
 #import "WKWebpagePreferencesInternal.h"
 #import "WKWebsiteDataStoreInternal.h"
 #import "WebCompatibilityMode.h"
+#import "_WKCustomHeaderFieldsInternal.h"
 #import "_WKWebsitePoliciesInternal.h"
 #import <wtf/RetainPtr.h>
 
@@ -214,26 +216,22 @@
     }
 }
 
-- (NSDictionary<NSString *, NSString *> *)_customHeaderFields
+- (NSArray<_WKCustomHeaderFields *> *)_customHeaderFields
 {
     const auto& fields = _websitePolicies->customHeaderFields();
-    auto dictionary = adoptNS([[NSMutableDictionary alloc] initWithCapacity:fields.size()]);
+    NSMutableArray *array = [[[NSMutableArray alloc] initWithCapacity:fields.size()] autorelease];
     for (const auto& field : fields)
-        [dictionary setObject:field.value() forKey:field.name()];
-    return dictionary.autorelease();
+        [array addObject:wrapper(API::CustomHeaderFields::create(field))];
+    return array;
 }
 
-- (void)_setCustomHeaderFields:(NSDictionary<NSString *, NSString *> *)fields
+- (void)_setCustomHeaderFields:(NSArray<_WKCustomHeaderFields *> *)fields
 {
-    Vector<WebCore::HTTPHeaderField> parsedFields;
-    parsedFields.reserveInitialCapacity(fields.count);
-
-    for (NSString *name in fields) {
-        auto field = WebCore::HTTPHeaderField::create(name, [fields objectForKey:name]);
-        if (field && startsWithLettersIgnoringASCIICase(field->name(), "x-"))
-            parsedFields.uncheckedAppend(WTFMove(*field));
-    }
-    _websitePolicies->setCustomHeaderFields(WTFMove(parsedFields));
+    Vector<WebCore::CustomHeaderFields> vector;
+    vector.reserveInitialCapacity(fields.count);
+    for (_WKCustomHeaderFields *element in fields)
+        vector.uncheckedAppend(static_cast<API::CustomHeaderFields&>([element _apiObject]).coreFields());
+    _websitePolicies->setCustomHeaderFields(WTFMove(vector));
 }
 
 - (WKWebsiteDataStore *)_websiteDataStore
diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebpagePreferencesPrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKWebpagePreferencesPrivate.h
index 0bd0fac..2d5e249 100644
--- a/Source/WebKit/UIProcess/API/Cocoa/WKWebpagePreferencesPrivate.h
+++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebpagePreferencesPrivate.h
@@ -55,12 +55,14 @@
     _WKWebsiteDeviceOrientationAndMotionAccessPolicyDeny,
 } WK_API_AVAILABLE(macos(10.14), ios(12.0));
 
+@class _WKCustomHeaderFields;
+
 @interface WKWebpagePreferences (WKPrivate)
 
 @property (nonatomic, setter=_setContentBlockersEnabled:) BOOL _contentBlockersEnabled;
 @property (nonatomic, setter=_setAllowedAutoplayQuirks:) _WKWebsiteAutoplayQuirk _allowedAutoplayQuirks;
 @property (nonatomic, setter=_setAutoplayPolicy:) _WKWebsiteAutoplayPolicy _autoplayPolicy;
-@property (nonatomic, copy, setter=_setCustomHeaderFields:) NSDictionary<NSString *, NSString *> *_customHeaderFields;
+@property (nonatomic, copy, setter=_setCustomHeaderFields:) NSArray<_WKCustomHeaderFields *> *_customHeaderFields;
 @property (nonatomic, setter=_setPopUpPolicy:) _WKWebsitePopUpPolicy _popUpPolicy;
 @property (nonatomic, strong, setter=_setWebsiteDataStore:) WKWebsiteDataStore *_websiteDataStore;
 @property (nonatomic, copy, setter=_setCustomUserAgent:) NSString *_customUserAgent;
diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFields.h b/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFields.h
new file mode 100644
index 0000000..24355b0
--- /dev/null
+++ b/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFields.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <WebKit/WKFoundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+WK_CLASS_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA))
+@interface _WKCustomHeaderFields : NSObject
+
+@property (nonatomic, copy) NSDictionary<NSString *, NSString *> *fields;
+@property (nonatomic, copy) NSArray<NSString *> *thirdPartyDomains;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFields.mm b/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFields.mm
new file mode 100644
index 0000000..ddef71f
--- /dev/null
+++ b/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFields.mm
@@ -0,0 +1,92 @@
+/*
+ * 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 "_WKCustomHeaderFields.h"
+
+#import "_WKCustomHeaderFieldsInternal.h"
+#import <wtf/BlockPtr.h>
+
+@implementation _WKCustomHeaderFields
+
+- (instancetype)init
+{
+    if (!(self = [super init]))
+        return nil;
+    
+    API::Object::constructInWrapper<API::CustomHeaderFields>(self);
+    return self;
+}
+
+- (void)dealloc
+{
+    _fields->API::CustomHeaderFields::~CustomHeaderFields();
+    [super dealloc];
+}
+
+- (NSDictionary<NSString *, NSString *> *)fields
+{
+    auto& vector = _fields->fields();
+    NSMutableDictionary<NSString *, NSString *> *dictionary = [NSMutableDictionary dictionaryWithCapacity:vector.size()];
+    for (auto& field : vector)
+        [dictionary setObject:field.value() forKey:field.name()];
+    return dictionary;
+}
+
+- (void)setFields:(NSDictionary<NSString *, NSString *> *)fields
+{
+    Vector<WebCore::HTTPHeaderField> vector;
+    vector.reserveInitialCapacity(fields.count);
+    [fields enumerateKeysAndObjectsUsingBlock:makeBlockPtr([&](id key, id value, BOOL* stop) {
+        if (auto field = WebCore::HTTPHeaderField::create((NSString *)key, (NSString *)value); field && startsWithLettersIgnoringASCIICase(field->name(), "x-"))
+            vector.uncheckedAppend(WTFMove(*field));
+    }).get()];
+    _fields->setFields(WTFMove(vector));
+}
+
+- (NSArray<NSString *> *)thirdPartyDomains
+{
+    auto& domains = _fields->thirdPartyDomains();
+    NSMutableArray *array = [NSMutableArray arrayWithCapacity:domains.size()];
+    for (auto& domain : domains)
+        [array addObject:domain];
+    return array;
+}
+
+- (void)setThirdPartyDomains:(NSArray<NSString *> *)thirdPartyDomains
+{
+    Vector<String> domains;
+    domains.reserveInitialCapacity(thirdPartyDomains.count);
+    for (NSString *domain in thirdPartyDomains)
+        domains.uncheckedAppend(domain);
+    _fields->setThirdPartyDomains(WTFMove(domains));
+}
+
+- (API::Object&)_apiObject
+{
+    return *_fields;
+}
+
+@end
diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFieldsInternal.h b/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFieldsInternal.h
new file mode 100644
index 0000000..05ef77a
--- /dev/null
+++ b/Source/WebKit/UIProcess/API/Cocoa/_WKCustomHeaderFieldsInternal.h
@@ -0,0 +1,42 @@
+/*
+ * 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 "APICustomHeaderFields.h"
+#import "WKObject.h"
+#import "_WKCustomHeaderFields.h"
+
+namespace WebKit {
+
+template<> struct WrapperTraits<API::CustomHeaderFields> {
+    using WrapperClass = _WKCustomHeaderFields;
+};
+
+}
+
+@interface _WKCustomHeaderFields () <WKObject> {
+@package
+    API::ObjectStorage<API::CustomHeaderFields> _fields;
+}
+@end
diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm b/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm
index c57bd9d..1435549 100644
--- a/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm
+++ b/Source/WebKit/UIProcess/API/Cocoa/_WKWebsitePolicies.mm
@@ -97,12 +97,24 @@
 
 - (NSDictionary<NSString *, NSString *> *)customHeaderFields
 {
-    return [_webpagePreferences _customHeaderFields];
+    auto& fields = static_cast<API::WebsitePolicies&>([_webpagePreferences _apiObject]).legacyCustomHeaderFields();
+    auto dictionary = [NSMutableDictionary dictionaryWithCapacity:fields.size()];
+    for (const auto& field : fields)
+        [dictionary setObject:field.value() forKey:field.name()];
+    return dictionary;
 }
 
 - (void)setCustomHeaderFields:(NSDictionary<NSString *, NSString *> *)fields
 {
-    [_webpagePreferences _setCustomHeaderFields:fields];
+    auto websitePolicies = static_cast<API::WebsitePolicies&>([_webpagePreferences _apiObject]);
+    Vector<WebCore::HTTPHeaderField> parsedFields;
+    parsedFields.reserveInitialCapacity(fields.count);
+    for (NSString *name in fields) {
+        auto field = WebCore::HTTPHeaderField::create(name, [fields objectForKey:name]);
+        if (field && startsWithLettersIgnoringASCIICase(field->name(), "x-"))
+            parsedFields.uncheckedAppend(WTFMove(*field));
+    }
+    websitePolicies.setLegacyCustomHeaderFields(WTFMove(parsedFields));
 }
 
 - (WKWebsiteDataStore *)websiteDataStore
diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj
index 26c05c7..17ecd2e 100644
--- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj
+++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj
@@ -1078,6 +1078,7 @@
 		5C5CEC33220912B300D6BBB0 /* AuxiliaryProcessMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C5CEC31220912AF00D6BBB0 /* AuxiliaryProcessMain.cpp */; };
 		5C5CEC34220912B400D6BBB0 /* AuxiliaryProcessMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C5CEC31220912AF00D6BBB0 /* AuxiliaryProcessMain.cpp */; };
 		5C5CEC35220912B400D6BBB0 /* AuxiliaryProcessMain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C5CEC31220912AF00D6BBB0 /* AuxiliaryProcessMain.cpp */; };
+		5C5D238C227A2CDA000B9BDA /* _WKCustomHeaderFields.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C5D2389227A1892000B9BDA /* _WKCustomHeaderFields.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5C62FDF91EFC271C00CE072E /* WKURLSchemeTaskPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C62FDF81EFC263C00CE072E /* WKURLSchemeTaskPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5C7FB47021E97DC5009E3241 /* WebCookieJar.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C7FB46F21E97C0C009E3241 /* WebCookieJar.h */; };
 		5C8BC797218CBB4800813886 /* SafeBrowsing.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5C8BC796218CB58A00813886 /* SafeBrowsing.xcassets */; };
@@ -1092,6 +1093,7 @@
 		5CB2378C1DF0DE6E00117AA3 /* _WKWebsitePolicies.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CB237891DF0DD4300117AA3 /* _WKWebsitePolicies.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5CB2378E1DF0E0D300117AA3 /* _WKWebsitePoliciesInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CB2378D1DF0E0C200117AA3 /* _WKWebsitePoliciesInternal.h */; };
 		5CBC9B8E1C652CA000A8FDCF /* NetworkDataTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CBC9B891C6524A500A8FDCF /* NetworkDataTask.h */; };
+		5CBD595C2280EDF4002B22AA /* _WKCustomHeaderFields.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C5D2388227A1892000B9BDA /* _WKCustomHeaderFields.mm */; };
 		5CD286511E7235990094FDC8 /* WKContentRuleListStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CD2864D1E722F440094FDC8 /* WKContentRuleListStore.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		5CD286531E7235AA0094FDC8 /* _WKUserContentFilterPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CD286491E722F440094FDC8 /* _WKUserContentFilterPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5CD286541E7235B10094FDC8 /* WKContentRuleList.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CD2864A1E722F440094FDC8 /* WKContentRuleList.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -3528,6 +3530,10 @@
 		5C5CEC31220912AF00D6BBB0 /* AuxiliaryProcessMain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AuxiliaryProcessMain.cpp; path = Cocoa/AuxiliaryProcessMain.cpp; sourceTree = "<group>"; };
 		5C5CEC382209583200D6BBB0 /* DaemonEntryPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DaemonEntryPoint.h; sourceTree = "<group>"; };
 		5C5CEC392209583200D6BBB0 /* DaemonEntryPoint.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DaemonEntryPoint.mm; sourceTree = "<group>"; };
+		5C5D2387227A1891000B9BDA /* _WKCustomHeaderFieldsInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKCustomHeaderFieldsInternal.h; sourceTree = "<group>"; };
+		5C5D2388227A1892000B9BDA /* _WKCustomHeaderFields.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _WKCustomHeaderFields.mm; sourceTree = "<group>"; };
+		5C5D2389227A1892000B9BDA /* _WKCustomHeaderFields.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKCustomHeaderFields.h; sourceTree = "<group>"; };
+		5C5D238A227A1D9B000B9BDA /* APICustomHeaderFields.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APICustomHeaderFields.h; sourceTree = "<group>"; };
 		5C62FDF81EFC263C00CE072E /* WKURLSchemeTaskPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKURLSchemeTaskPrivate.h; sourceTree = "<group>"; };
 		5C6CE6D01F59BC460007C6CB /* PageClientImplCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PageClientImplCocoa.mm; sourceTree = "<group>"; };
 		5C6CE6D31F59EA350007C6CB /* PageClientImplCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageClientImplCocoa.h; sourceTree = "<group>"; };
@@ -6141,6 +6147,9 @@
 				5C4609E422430E4D009943C2 /* _WKContentRuleListActionInternal.h */,
 				1A5704F61BE01FF400874AF1 /* _WKContextMenuElementInfo.h */,
 				1A5704F51BE01FF400874AF1 /* _WKContextMenuElementInfo.mm */,
+				5C5D2389227A1892000B9BDA /* _WKCustomHeaderFields.h */,
+				5C5D2388227A1892000B9BDA /* _WKCustomHeaderFields.mm */,
+				5C5D2387227A1891000B9BDA /* _WKCustomHeaderFieldsInternal.h */,
 				83891B681A68BEBC0030F386 /* _WKDiagnosticLoggingDelegate.h */,
 				A1A4FE5718DCE9FA00B5EA8A /* _WKDownload.h */,
 				A1A4FE5818DCE9FA00B5EA8A /* _WKDownload.mm */,
@@ -7705,6 +7714,7 @@
 				7C3A06A51AAB903E009D74BA /* APIContentRuleListStore.cpp */,
 				7C3A06A61AAB903E009D74BA /* APIContentRuleListStore.h */,
 				076E884D1A13CADF005E90FC /* APIContextMenuClient.h */,
+				5C5D238A227A1D9B000B9BDA /* APICustomHeaderFields.h */,
 				7A821F4F1E2F7A5C00604577 /* APICustomProtocolManagerClient.h */,
 				83891B621A68B3420030F386 /* APIDiagnosticLoggingClient.h */,
 				1F7D36C018DA513F00D9D659 /* APIDownloadClient.h */,
@@ -9018,6 +9028,7 @@
 				5C4609E7224317B4009943C2 /* _WKContentRuleListAction.h in Headers */,
 				5C4609E8224317BB009943C2 /* _WKContentRuleListActionInternal.h in Headers */,
 				1A5704F81BE01FF400874AF1 /* _WKContextMenuElementInfo.h in Headers */,
+				5C5D238C227A2CDA000B9BDA /* _WKCustomHeaderFields.h in Headers */,
 				83891B691A68BEBC0030F386 /* _WKDiagnosticLoggingDelegate.h in Headers */,
 				A1A4FE5A18DCE9FA00B5EA8A /* _WKDownload.h in Headers */,
 				A1A4FE6118DD54A400B5EA8A /* _WKDownloadDelegate.h in Headers */,
@@ -10957,6 +10968,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				5CBD595C2280EDF4002B22AA /* _WKCustomHeaderFields.mm in Sources */,
 				99E7189A21F79D9E0055E975 /* _WKTouchEventGenerator.mm in Sources */,
 				2D92A784212B6AB100F493FD /* ActivityAssertion.cpp in Sources */,
 				2D92A77B212B6A7100F493FD /* ArgumentCoders.cpp in Sources */,
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index 912093f..80a3627 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,18 @@
+2019-05-16  Alex Christensen  <achristensen@webkit.org>
+
+        Add SPI to set a list of hosts to which to send custom header fields cross-origin
+        https://bugs.webkit.org/show_bug.cgi?id=197397
+
+        Reviewed by Geoff Garen.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm:
+        (TEST):
+        (expectLegacyHeaders):
+        (expectHeaders):
+        (-[CustomHeaderFieldsDelegate webView:decidePolicyForNavigationAction:preferences:decisionHandler:]):
+        (-[CustomHeaderFieldsDelegate webView:startURLSchemeTask:]):
+        (-[CustomHeaderFieldsDelegate _webView:decidePolicyForNavigationAction:decisionHandler:]): Deleted.
+
 2019-05-15  Aakash Jain  <aakash_jain@apple.com>
 
         Replace double-quotes with single quotes in steps.py
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm
index 134319f..beb312a 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebsitePolicies.mm
@@ -28,16 +28,19 @@
 #import "PlatformUtilities.h"
 #import "TestNavigationDelegate.h"
 #import "TestWKWebView.h"
+#import <WebKit/WKMutableDictionary.h>
 #import <WebKit/WKNavigationDelegatePrivate.h>
 #import <WebKit/WKPagePrivate.h>
 #import <WebKit/WKPreferencesPrivate.h>
 #import <WebKit/WKPreferencesRefPrivate.h>
+#import <WebKit/WKString.h>
 #import <WebKit/WKUIDelegatePrivate.h>
 #import <WebKit/WKURLSchemeTaskPrivate.h>
 #import <WebKit/WKUserContentControllerPrivate.h>
 #import <WebKit/WKWebViewPrivate.h>
 #import <WebKit/WKWebsiteDataStorePrivate.h>
 #import <WebKit/WKWebsitePolicies.h>
+#import <WebKit/_WKCustomHeaderFields.h>
 #import <WebKit/_WKUserContentExtensionStorePrivate.h>
 #import <WebKit/_WKWebsiteDataStoreConfiguration.h>
 #import <WebKit/_WKWebsitePolicies.h>
@@ -907,9 +910,9 @@
 
 TEST(WebKit, InvalidCustomHeaders)
 {
-    auto websitePolicies = adoptNS([[_WKWebsitePolicies alloc] init]);
-    [websitePolicies setCustomHeaderFields:@{@"invalidheader" : @"", @"noncustom" : @"header", @"    x-Custom ":@"  Needs Canonicalization\t ", @"x-other" : @"other value"}];
-    NSDictionary<NSString *, NSString *> *canonicalized = [websitePolicies customHeaderFields];
+    auto customHeaderFields = adoptNS([[_WKCustomHeaderFields alloc] init]);
+    [customHeaderFields setFields:@{@"invalidheader" : @"", @"noncustom" : @"header", @"    x-Custom ":@"  Needs Canonicalization\t ", @"x-other" : @"other value"}];
+    NSDictionary<NSString *, NSString *> *canonicalized = [customHeaderFields fields];
     EXPECT_EQ(canonicalized.count, 2u);
     EXPECT_STREQ([canonicalized objectForKey:@"x-Custom"].UTF8String, "Needs Canonicalization");
     EXPECT_STREQ([canonicalized objectForKey:@"x-other"].UTF8String, "other value");
@@ -919,8 +922,9 @@
 static bool secondTestDone;
 static bool thirdTestDone;
 static bool fourthTestDone;
+static bool fifthTestDone;
 
-static void expectHeaders(id <WKURLSchemeTask> task, bool expected)
+static void expectLegacyHeaders(id <WKURLSchemeTask> task, bool expected)
 {
     NSURLRequest *request = task.request;
     if (expected) {
@@ -932,6 +936,18 @@
     }
 }
 
+static void expectHeaders(id <WKURLSchemeTask> task, bool expected)
+{
+    NSURLRequest *request = task.request;
+    if (expected) {
+        EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key3"] UTF8String], "value3");
+        EXPECT_STREQ([[request valueForHTTPHeaderField:@"X-key4"] UTF8String], "value4");
+    } else {
+        EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key3"] == nil);
+        EXPECT_TRUE([request valueForHTTPHeaderField:@"X-key4"] == nil);
+    }
+}
+
 static void respond(id <WKURLSchemeTask>task, NSString *html = nil)
 {
     [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:html.length textEncodingName:nil] autorelease]];
@@ -944,61 +960,92 @@
 
 @implementation CustomHeaderFieldsDelegate
 
-IGNORE_WARNINGS_BEGIN("deprecated-implementations")
-- (void)_webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy, _WKWebsitePolicies *))decisionHandler
-IGNORE_WARNINGS_END
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences *))decisionHandler
 {
-    _WKWebsitePolicies *websitePolicies = [[[_WKWebsitePolicies alloc] init] autorelease];
-    [websitePolicies setCustomHeaderFields:@{@"X-key1": @"value1", @"X-key2": @"value2"}];
+    auto legacyHeaderFieldDictionary = adoptWK(WKMutableDictionaryCreate());
+    WKDictionarySetItem(legacyHeaderFieldDictionary.get(), adoptWK(WKStringCreateWithUTF8CString("X-key1")).get(), adoptWK(WKStringCreateWithUTF8CString("value1")).get());
+    WKDictionarySetItem(legacyHeaderFieldDictionary.get(), adoptWK(WKStringCreateWithUTF8CString("X-key2")).get(), adoptWK(WKStringCreateWithUTF8CString("value2")).get());
+    WKWebsitePoliciesSetCustomHeaderFields((WKWebsitePoliciesRef)preferences, legacyHeaderFieldDictionary.get());
+
+    _WKCustomHeaderFields *headerFields = [[[_WKCustomHeaderFields alloc] init] autorelease];
+    [headerFields setFields:@{@"X-key3": @"value3", @"X-key4": @"value4"}];
+    [headerFields setThirdPartyDomains:@[
+        @"*.hostwithasterisk.com",
+        @"hostwithoutasterisk.com",
+        @"*.com" // should be ignored.
+    ]];
+    
+    [preferences _setCustomHeaderFields:@[headerFields]];
+    
     if ([navigationAction.request.URL.path isEqualToString:@"/mainresource"]) {
         dispatch_async(dispatch_get_main_queue(), ^{
-            decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
+            decisionHandler(WKNavigationActionPolicyAllow, preferences);
         });
     } else
-        decisionHandler(WKNavigationActionPolicyAllow, websitePolicies);
+        decisionHandler(WKNavigationActionPolicyAllow, preferences);
 }
 
 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
 {
     NSString *path = urlSchemeTask.request.URL.path;
     if ([path isEqualToString:@"/mainresource"]) {
-        expectHeaders(urlSchemeTask, true);
+        expectLegacyHeaders(urlSchemeTask, true);
         respond(urlSchemeTask, @"<script>fetch('subresource').then(function(response){fetch('test://differentsecurityorigin/crossoriginsubresource',{mode:'no-cors'})})</script>");
     } else if ([path isEqualToString:@"/subresource"]) {
-        expectHeaders(urlSchemeTask, true);
+        expectLegacyHeaders(urlSchemeTask, true);
         respond(urlSchemeTask);
     } else if ([path isEqualToString:@"/crossoriginsubresource"]) {
-        expectHeaders(urlSchemeTask, false);
+        expectLegacyHeaders(urlSchemeTask, false);
         respond(urlSchemeTask);
         firstTestDone = true;
     } else if ([path isEqualToString:@"/mainresourcewithiframe"]) {
-        expectHeaders(urlSchemeTask, true);
+        expectLegacyHeaders(urlSchemeTask, true);
         respond(urlSchemeTask, @"<iframe src='test://iframeorigin/iframemainresource'></iframe>");
     } else if ([path isEqualToString:@"/iframemainresource"]) {
-        expectHeaders(urlSchemeTask, false);
+        expectLegacyHeaders(urlSchemeTask, false);
         respond(urlSchemeTask, @"<script>fetch('iframesubresource').then(function(response){fetch('test://mainframeorigin/originaloriginsubresource',{mode:'no-cors'})})</script>");
     } else if ([path isEqualToString:@"/iframesubresource"]) {
-        expectHeaders(urlSchemeTask, false);
+        expectLegacyHeaders(urlSchemeTask, false);
         respond(urlSchemeTask);
     } else if ([path isEqualToString:@"/originaloriginsubresource"]) {
-        expectHeaders(urlSchemeTask, false);
+        expectLegacyHeaders(urlSchemeTask, false);
         respond(urlSchemeTask);
         secondTestDone = true;
     } else if ([path isEqualToString:@"/nestedtop"]) {
-        expectHeaders(urlSchemeTask, true);
+        expectLegacyHeaders(urlSchemeTask, true);
         respond(urlSchemeTask, @"<iframe src='test://otherorigin/nestedmid'></iframe>");
     } else if ([path isEqualToString:@"/nestedmid"]) {
-        expectHeaders(urlSchemeTask, false);
+        expectLegacyHeaders(urlSchemeTask, false);
         respond(urlSchemeTask, @"<iframe src='test://toporigin/nestedbottom'></iframe>");
     } else if ([path isEqualToString:@"/nestedbottom"]) {
-        expectHeaders(urlSchemeTask, true);
+        expectLegacyHeaders(urlSchemeTask, true);
         respond(urlSchemeTask);
         thirdTestDone = true;
     } else if ([path isEqualToString:@"/requestfromaboutblank"]) {
-        expectHeaders(urlSchemeTask, true);
+        expectLegacyHeaders(urlSchemeTask, true);
         respond(urlSchemeTask);
         fourthTestDone = true;
-    } else
+    } else if ([path isEqualToString:@"/testcustomheaderfieldhosts"]) {
+        expectHeaders(urlSchemeTask, true);
+        NSString *html = @"<script>fetch('test://a.b.c.sub.hostwithasterisk.com/hosttest1',{mode:'no-cors'})"
+            ".then(function(response){fetch('test://subhostwithasterisk.com/hosttest2',{mode:'no-cors'})})"
+            ".then(function(response){fetch('test://hostwithoutasterisk.com/hosttest3',{mode:'no-cors'})})"
+            ".then(function(response){fetch('test://a.b.c.sub.hostwithoutasterisk.com/hosttest4',{mode:'no-cors'})})</script>";
+        respond(urlSchemeTask, html);
+    } else if ([path isEqualToString:@"/hosttest1"]) {
+        expectHeaders(urlSchemeTask, true);
+        respond(urlSchemeTask);
+    } else if ([path isEqualToString:@"/hosttest2"]) {
+        expectHeaders(urlSchemeTask, false);
+        respond(urlSchemeTask);
+    } else if ([path isEqualToString:@"/hosttest3"]) {
+        expectHeaders(urlSchemeTask, true);
+        respond(urlSchemeTask);
+    } else if ([path isEqualToString:@"/hosttest4"]) {
+        expectHeaders(urlSchemeTask, false);
+        respond(urlSchemeTask);
+        fifthTestDone = true;
+    } else if ([path isEqualToString:@"/testcustomheaderfieldhosts"])
         EXPECT_TRUE(false);
 }
 
@@ -1023,6 +1070,9 @@
 
     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://toporigin/nestedtop"]]];
     TestWebKitAPI::Util::run(&thirdTestDone);
+
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test://host/testcustomheaderfieldhosts"]]];
+    TestWebKitAPI::Util::run(&fifthTestDone);
 }
 
 static unsigned loadCount;