CVE-2024-44309
Overview
Impact:
Description: Processing maliciously crafted web content may lead to a cross site scripting attack. Apple is aware of a report that this issue may have been actively exploited on Intel-based Mac systems.
Researchers: Clément Lecigne and Benoît Sevens of Google’s Threat Analysis Group
Key insight. Cookie first-party access was a binary allow-or-terminate decision that could not cleanly deny a cross-site cookie request; a tri-state Allow/Disallow/Terminate lets the NetworkProcess refuse disallowed cookie access without leaking it or crashing – closing an in-the-wild XSS vector.
Patch Details
diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
index 3ce50b762262..1dd61fa378fb 100644
--- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
+++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
@@ -503,7 +503,7 @@ void NetworkConnectionToWebProcess::didReceiveInvalidMessage(IPC::Connection&, I
void NetworkConnectionToWebProcess::createSocketChannel(const ResourceRequest& request, const String& protocol, WebSocketIdentifier identifier, WebPageProxyIdentifier webPageProxyID, std::optional<FrameIdentifier> frameID, std::optional<PageIdentifier> pageID, const ClientOrigin& clientOrigin, bool hadMainFrameMainResourcePrivateRelayed, bool allowPrivacyProxy, OptionSet<AdvancedPrivacyProtections> advancedPrivacyProtections, ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking, WebCore::StoredCredentialsPolicy storedCredentialsPolicy)
{
- MESSAGE_CHECK(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, request.firstPartyForCookies()));
+ MESSAGE_CHECK(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, request.firstPartyForCookies()) != NetworkProcess::AllowCookieAccess::Terminate);
ASSERT(!m_networkSocketChannels.contains(identifier));
if (auto channel = NetworkSocketChannel::create(*this, m_sessionID, request, protocol, identifier, webPageProxyID, frameID, pageID, clientOrigin, hadMainFrameMainResourcePrivateRelayed, allowPrivacyProxy, advancedPrivacyProtections, shouldRelaxThirdPartyCookieBlocking, storedCredentialsPolicy))
@@ -553,11 +553,11 @@ RefPtr<ServiceWorkerFetchTask> NetworkConnectionToWebProcess::createFetchTask(Ne
void NetworkConnectionToWebProcess::scheduleResourceLoad(NetworkResourceLoadParameters&& loadParameters, std::optional<NetworkResourceLoadIdentifier> existingLoaderToResume)
{
- bool hasCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, loadParameters.request.firstPartyForCookies());
- if (UNLIKELY(!hasCookieAccess))
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, loadParameters.request.firstPartyForCookies());
+ if (UNLIKELY(allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow))
RELEASE_LOG_ERROR(Loading, "scheduleResourceLoad: Web process does not have cookie access to url %" SENSITIVE_LOG_STRING " for request %" SENSITIVE_LOG_STRING, loadParameters.request.firstPartyForCookies().string().utf8().data(), loadParameters.request.url().string().utf8().data());
- MESSAGE_CHECK(hasCookieAccess);
+ MESSAGE_CHECK(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate);
CONNECTION_RELEASE_LOG(Loading, "scheduleResourceLoad: (parentPID=%d, pageProxyID=%" PRIu64 ", webPageID=%" PRIu64 ", frameID=%" PRIu64 ", resourceID=%" PRIu64 ", existingLoaderToResume=%" PRIu64 ")", loadParameters.parentPID, loadParameters.webPageProxyID.toUInt64(), loadParameters.webPageID ? loadParameters.webPageID->toUInt64() : 0, loadParameters.webFrameID.object().toUInt64(), loadParameters.identifier.toUInt64(), valueOrDefault(existingLoaderToResume).toUInt64());
@@ -786,7 +786,10 @@ void NetworkConnectionToWebProcess::registerURLSchemesAsCORSEnabled(Vector<Strin
void NetworkConnectionToWebProcess::cookiesForDOM(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, FrameIdentifier frameID, PageIdentifier pageID, IncludeSecureCookies includeSecureCookies, ApplyTrackingPrevention applyTrackingPrevention, ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking, CompletionHandler<void(String cookieString, bool secureCookiesAccessed)>&& completionHandler)
{
- MESSAGE_CHECK_COMPLETION(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty), completionHandler({ }, false));
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty);
+ MESSAGE_CHECK_COMPLETION(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate, completionHandler({ }, false));
+ if (allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow)
+ return completionHandler({ }, false);
auto* networkStorageSession = storageSession();
if (!networkStorageSession)
@@ -803,7 +806,10 @@ void NetworkConnectionToWebProcess::cookiesForDOM(const URL& firstParty, const S
void NetworkConnectionToWebProcess::setCookiesFromDOM(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, WebCore::FrameIdentifier frameID, PageIdentifier pageID, ApplyTrackingPrevention applyTrackingPrevention, const String& cookieString, ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking)
{
- MESSAGE_CHECK(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty));
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty);
+ MESSAGE_CHECK(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate);
+ if (allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow)
+ return;
auto* networkStorageSession = storageSession();
if (!networkStorageSession)
@@ -824,7 +830,10 @@ void NetworkConnectionToWebProcess::cookiesEnabledSync(const URL& firstParty, co
void NetworkConnectionToWebProcess::cookiesEnabled(const URL& firstParty, const URL& url, std::optional<FrameIdentifier> frameID, std::optional<PageIdentifier> pageID, ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking, CompletionHandler<void(bool)>&& completionHandler)
{
- MESSAGE_CHECK_COMPLETION(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty), completionHandler(false));
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty);
+ MESSAGE_CHECK_COMPLETION(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate, completionHandler(false));
+ if (allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow)
+ return completionHandler(false);
auto* networkStorageSession = storageSession();
if (!networkStorageSession) {
@@ -838,7 +847,10 @@ void NetworkConnectionToWebProcess::cookiesEnabled(const URL& firstParty, const
void NetworkConnectionToWebProcess::cookieRequestHeaderFieldValue(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<FrameIdentifier> frameID, std::optional<PageIdentifier> pageID, IncludeSecureCookies includeSecureCookies, ApplyTrackingPrevention applyTrackingPrevention, ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking, CompletionHandler<void(String, bool)>&& completionHandler)
{
- MESSAGE_CHECK_COMPLETION(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty), completionHandler({ }, false));
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty);
+ MESSAGE_CHECK_COMPLETION(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate, completionHandler({ }, false));
+ if (allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow)
+ return completionHandler({ }, false);
auto* networkStorageSession = storageSession();
if (!networkStorageSession)
@@ -849,7 +861,10 @@ void NetworkConnectionToWebProcess::cookieRequestHeaderFieldValue(const URL& fir
void NetworkConnectionToWebProcess::getRawCookies(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<FrameIdentifier> frameID, std::optional<PageIdentifier> pageID, ApplyTrackingPrevention applyTrackingPrevention, ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking, CompletionHandler<void(Vector<WebCore::Cookie>&&)>&& completionHandler)
{
- MESSAGE_CHECK_COMPLETION(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty), completionHandler({ }));
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty);
+ MESSAGE_CHECK_COMPLETION(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate, completionHandler({ }));
+ if (allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow)
+ return completionHandler({ });
auto* networkStorageSession = storageSession();
if (!networkStorageSession)
@@ -878,7 +893,10 @@ void NetworkConnectionToWebProcess::deleteCookie(const URL& url, const String& c
void NetworkConnectionToWebProcess::cookiesForDOMAsync(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<WebCore::FrameIdentifier> frameID, std::optional<WebCore::PageIdentifier> pageID, IncludeSecureCookies includeSecureCookies, ApplyTrackingPrevention applyTrackingPrevention, ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking, WebCore::CookieStoreGetOptions&& options, CompletionHandler<void(std::optional<Vector<WebCore::Cookie>>&&)>&& completionHandler)
{
- MESSAGE_CHECK_COMPLETION(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty), completionHandler(std::nullopt));
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty);
+ MESSAGE_CHECK_COMPLETION(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate, completionHandler(std::nullopt));
+ if (allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow)
+ return completionHandler(std::nullopt);
auto* networkStorageSession = storageSession();
if (!networkStorageSession)
@@ -895,7 +913,10 @@ void NetworkConnectionToWebProcess::cookiesForDOMAsync(const URL& firstParty, co
void NetworkConnectionToWebProcess::setCookieFromDOMAsync(const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, std::optional<WebCore::FrameIdentifier> frameID, std::optional<WebCore::PageIdentifier> pageID, ApplyTrackingPrevention applyTrackingPrevention, WebCore::Cookie&& cookie, ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking, CompletionHandler<void(bool)>&& completionHandler)
{
- MESSAGE_CHECK_COMPLETION(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty), completionHandler(false));
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty);
+ MESSAGE_CHECK_COMPLETION(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate, completionHandler(false));
+ if (allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow)
+ return completionHandler(false);
auto* networkStorageSession = storageSession();
if (!networkStorageSession)
@@ -915,7 +936,10 @@ void NetworkConnectionToWebProcess::domCookiesForHost(const URL& url, Completion
{
auto host = url.host().toString();
MESSAGE_CHECK_COMPLETION(HashSet<String>::isValidValue(host), completionHandler({ }));
- MESSAGE_CHECK_COMPLETION(m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, url), completionHandler({ }));
+ auto allowCookieAccess = m_networkProcess->allowsFirstPartyForCookies(m_webProcessIdentifier, url);
+ MESSAGE_CHECK_COMPLETION(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate, completionHandler({ }));
+ if (allowCookieAccess != NetworkProcess::AllowCookieAccess::Allow)
+ return completionHandler({ });
auto* networkStorageSession = storageSession();
if (!networkStorageSession)
@@ -1440,7 +1464,8 @@ void NetworkConnectionToWebProcess::establishSWContextConnection(WebPageProxyIde
{
auto* session = networkSession();
if (auto* swServer = session ? session->swServer() : nullptr) {
- MESSAGE_CHECK_COMPLETION(session->networkProcess().allowsFirstPartyForCookies(webProcessIdentifier(), registrableDomain), completionHandler());
+ auto allowCookieAccess = session->networkProcess().allowsFirstPartyForCookies(webProcessIdentifier(), registrableDomain);
+ MESSAGE_CHECK_COMPLETION(allowCookieAccess != NetworkProcess::AllowCookieAccess::Terminate, completionHandler());
m_swContextConnection = makeUnique<WebSWServerToContextConnection>(*this, webPageProxyID, WTFMove(registrableDomain), serviceWorkerPageIdentifier, *swServer);
}
completionHandler();
diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.cpp b/Source/WebKit/NetworkProcess/NetworkProcess.cpp
index eb014c672222..1f3dcced7272 100644
--- a/Source/WebKit/NetworkProcess/NetworkProcess.cpp
+++ b/Source/WebKit/NetworkProcess/NetworkProcess.cpp
@@ -458,48 +458,49 @@ void NetworkProcess::webProcessWillLoadWebArchive(WebCore::ProcessIdentifier pro
}).iterator->value.first = LoadedWebArchive::Yes;
}
-bool NetworkProcess::allowsFirstPartyForCookies(WebCore::ProcessIdentifier processIdentifier, const URL& firstParty)
+auto NetworkProcess::allowsFirstPartyForCookies(WebCore::ProcessIdentifier processIdentifier, const URL& firstParty) -> AllowCookieAccess
{
- // FIXME: This should probably not be necessary. If about:blank is the first party for cookies,
- // we should set it to be the inherited origin then remove this exception.
- if (firstParty.isAboutBlank())
- return true;
+ auto allowCookieAccess = allowsFirstPartyForCookies(processIdentifier, RegistrableDomain { firstParty });
+ if (allowCookieAccess == NetworkProcess::AllowCookieAccess::Terminate) {
+ // FIXME: This should probably not be necessary. If about:blank is the first party for cookies,
+ // we should set it to be the inherited origin then remove this exception.
+ if (firstParty.isAboutBlank())
+ return AllowCookieAccess::Disallow;
- if (firstParty.isNull())
- return true; // FIXME: This shouldn't be allowed.
+ if (firstParty.isNull())
+ return AllowCookieAccess::Disallow; // FIXME: This shouldn't be allowed.
+ }
- return allowsFirstPartyForCookies(processIdentifier, RegistrableDomain { firstParty });
+ return allowCookieAccess;
}
-bool NetworkProcess::allowsFirstPartyForCookies(WebCore::ProcessIdentifier processIdentifier, const RegistrableDomain& firstPartyDomain)
+auto NetworkProcess::allowsFirstPartyForCookies(WebCore::ProcessIdentifier processIdentifier, const RegistrableDomain& firstPartyDomain) -> AllowCookieAccess
{
// FIXME: This shouldn't be needed but it is hit sometimes at least with PDFs.
- if (firstPartyDomain.isEmpty())
- return true;
-
+ auto terminateOrDisallow = firstPartyDomain.isEmpty() ? AllowCookieAccess::Disallow : AllowCookieAccess::Terminate;
if (!decltype(m_allowedFirstPartiesForCookies)::isValidKey(processIdentifier)) {
ASSERT_NOT_REACHED();
- return false;
+ return terminateOrDisallow;
}
auto iterator = m_allowedFirstPartiesForCookies.find(processIdentifier);
if (iterator == m_allowedFirstPartiesForCookies.end()) {
ASSERT_NOT_REACHED();
- return false;
+ return terminateOrDisallow;
}
if (iterator->value.first == LoadedWebArchive::Yes)
- return true;
+ return AllowCookieAccess::Allow;
auto& set = iterator->value.second;
if (!std::remove_reference_t<decltype(set)>::isValidValue(firstPartyDomain)) {
ASSERT_NOT_REACHED();
- return false;
+ return terminateOrDisallow;
}
auto result = set.contains(firstPartyDomain);
- ASSERT(result);
Background
Advisory: Apple Security Advisory
| Attribute | Value |
|---|---|
| CVE | CVE-2024-44309 |
| Bugzilla | 283095 |
| Component | WebKit |
| Bug Class | CrossOrigin |
| Severity | medium |
| Commit | 2815b4e298299328… |
| Advisory | Apple Advisory |
Terminology
- First-party-for-cookies check — NetworkProcess-side authorization deciding whether a given web process may access cookies for a first-party URL.
- MESSAGE_CHECK — An IPC guard that terminates the connection when its condition fails.
- AllowCookieAccess (tri-state) — New enum {Allow, Disallow, Terminate} separating a legitimate denial from a protocol violation.
- In-the-wild / KEV — Apple reported active exploitation on Intel Macs; the CVE is on the CISA Known Exploited Vulnerabilities catalog.
Root Cause Analysis
NetworkConnectionToWebProcess gates a web process’s cookie operations (cookiesForDOM, cookiesEnabled(Sync), createFetchTask, registerURLSchemesAsCORSEnabled, invalid-message handling) on m_networkProcess->allowsFirstPartyForCookies(webProcessIdentifier, firstParty). Pre-patch that returned a plain bool and every call site did MESSAGE_CHECK(hasCookieAccess): a false result was treated as a protocol violation that TERMINATES the web process connection. That conflated two distinct situations – ’this process legitimately may not access cookies for this first party’ (should be denied quietly, returning empty) versus ’this message is malformed/malicious’ (should terminate). Because the only outcomes were allow or terminate, the first-party-for-cookies enforcement could not simply deny a cross-site cookie request; combined with how the result was consumed this left a cookie-management flaw that, per Apple, was exploited in the wild for cross-site scripting on Intel Macs (the precise XSS chain – cross-site cookie read/injection feeding script execution – is not shown by the diff and is inference). The fix replaces the bool with a tri-state NetworkProcess::AllowCookieAccess { Allow, Disallow, Terminate }: MESSAGE_CHECK now only fires (terminating) on the Terminate result, and each cookie path additionally returns early/empty when the result is not Allow (if (allowCookieAccess != Allow) return ...). The restored invariant is that a disallowed first-party cookie request is denied (no data, no cross-site access) without either leaking cookies or crashing, and only genuinely invalid requests terminate the connection.
Attack Path
1. Drive cookie requests from a web process
Issue cookie operations (cookiesForDOM/cookiesEnabled/fetch) for a first-party URL the process should not have cookie access to.
2. Exploit the binary check
Pre-patch the allow/terminate-only decision mishandles the ‘disallowed’ case, failing to cleanly deny the cross-site cookie request.
3. Obtain cross-site cookie access
[inference] The mishandling lets the process read/influence cookies for another site’s first party.
4. Escalate to XSS
[inference] The cross-site cookie manipulation is leveraged into script execution in another origin – the in-the-wild XSS Apple reported on Intel Macs.
Impact Assessment
An exploited-in-the-wild cookie-management/access-control flaw in the NetworkProcess first-party-for-cookies enforcement, described by Apple as leading to cross-site scripting. It is an isolation/authorization bug (not memory corruption); the diff shows the binary allow/terminate decision replaced by a tri-state that can properly deny cross-site cookie access, while the exact XSS chain is inferred. Given active exploitation it is high-impact despite the medium engine rating; enforcement runs in the NetworkProcess.
Audit Directions
Other bool authorization checks
grep NetworkProcess for allow*/canAccess* predicates returning bool consumed only via MESSAGE_CHECK, where ‘deny’ and ’terminate’ are conflated.
Cookie access paths
Audit every cookiesForDOM/cookiesEnabled/setCookie/fetch path to confirm a non-Allow result denies without exposing cookies.
First-party derivation
Review how firstPartyForCookies is derived and validated per web process under site isolation to prevent cross-site requests.
Changed Functions
| Function | File | Change | Note |
|---|---|---|---|
NetworkConnectionToWebProcess::cookiesForDOM / cookiesEnabled / cookiesEnabledSync / createFetchTask / registerURLSchemesAsCORSEnabled / didReceiveInvalidMessage |
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp |
modified | Consume allowsFirstPartyForCookies as the tri-state AllowCookieAccess: MESSAGE_CHECK only on Terminate, and deny (return empty/early) when the result is not Allow, so a disallowed first party is refused without leaking cookies or crashing. |
NetworkProcess::allowsFirstPartyForCookies (AllowCookieAccess) |
Source/WebKit/NetworkProcess/NetworkProcess.cpp |
modified | Returns Allow/Disallow/Terminate instead of a bool so callers can distinguish ‘deny’ from ’terminate’. |
Regression Test
The fix ships this proof-of-concept / regression test:
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm
index 77ae695483e9..f61602dc70e8 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm
+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/IPCTestingAPI.mm
@@ -376,6 +376,39 @@ TEST(IPCTestingAPI, DecodesReplyArgumentsForAsyncMessage)
EXPECT_STREQ([alertMessage UTF8String], "[{\"type\":\"bool\",\"value\":false}]");
}
+TEST(IPCTestingAPI, EmptyFirstPartyForCookiesCookieRequestHeaderFieldValue)
+{
+ RetainPtr webView = createWebViewWithIPCTestingAPI();
+ RetainPtr delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]);
+ [webView setUIDelegate:delegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>document.cookie='a=b';</script>" baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
+ auto sendMessage = @"const connection = IPC.connectionForProcessTarget('Networking');"
+ "const result = connection.sendSyncMessage("
+ " 0,"
+ " IPC.messages.NetworkConnectionToWebProcess_CookieRequestHeaderFieldValue.name,"
+ " 1000,"
+ " ["
+ " {type: 'String', value: null},"
+ " {type: 'uint8_t', value: 1},"
+ " {type: 'uint8_t', value: 1},"
+ " {type: 'uint8_t', value: 1},"
+ " {type: 'String', value: location.href},"
+ " {type: 'uint8_t', value: 1},"
+ " {type: 'FrameID', value: IPC.frameID},"
+ " {type: 'uint8_t', value: 1},"
+ " {type: 'uint64_t', value: IPC.pageID},"
+ " {type: 'uint8_t', value: 1},"
+ " {type: 'uint8_t', value: 0},"
+ " {type: 'uint8_t', value: 1},"
+ " ]"
+ ");";
+ [webView evaluateJavaScript:sendMessage completionHandler:nil];
+ while (![webView objectByEvaluatingJavaScript:@"result"])
+ TestWebKitAPI::Util::spinRunLoop();
+ EXPECT_STREQ([[webView stringByEvaluatingJavaScript:@"result.arguments[0].value"] UTF8String], "<null>");
+}
+
TEST(IPCTestingAPI, DescribesArguments)
{
auto webView = createWebViewWithIPCTestingAPI();