Released September 16, 2024

CVE-2024-40857

CVSS 6.1 severity medium CWE-79 vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

Overview

Impact:

Description: Processing maliciously crafted web content may lead to universal cross site scripting

Researchers: Ron Masas

Key insight. The root cause is that web-archive content — which can spoof any origin and carry script — was loaded against the user’s persistent data store with normal permission and first-party-cookie treatment; the fix isolates every archive load into a throwaway non-persistent WebsiteDataStore (threaded through provisional pages, back/forward items, and cookie bookkeeping) and hard-denies permission queries once an archive is loaded.

Patch Details

diff --git a/LayoutTests/platform/ios/TestExpectations b/LayoutTests/platform/ios/TestExpectations
index 1a01d0af7fad..a4f87d04ce7a 100644
--- a/LayoutTests/platform/ios/TestExpectations
+++ b/LayoutTests/platform/ios/TestExpectations
@@ -1548,9 +1548,6 @@ webarchive/test-css-url-resources-in-stylesheets.html [ Failure ]
 webarchive/test-css-url-resources-inline-styles.html [ Failure ]
 webarchive/test-link-rel-icon.html [ Failure ]
 
-# The following two tests are expected to fail until <rdar://problem/34063313> is available on test systems.
-webarchive/loading/cache-expired-subresource.html [ Failure ]
-
 # Webarchive tests that fail due to <https://bugs.webkit.org/show_bug.cgi?id=82665>:
 http/tests/webarchive/test-css-url-encoding-shift-jis.html
 http/tests/webarchive/test-css-url-encoding-utf-8.html
diff --git a/LayoutTests/platform/ios/webarchive/loading/cache-expired-subresource-expected.txt b/LayoutTests/platform/ios/webarchive/loading/cache-expired-subresource-expected.txt
index 2bfa51ed620c..e91452ddb29c 100644
--- a/LayoutTests/platform/ios/webarchive/loading/cache-expired-subresource-expected.txt
+++ b/LayoutTests/platform/ios/webarchive/loading/cache-expired-subresource-expected.txt
@@ -1,15 +1,15 @@
 main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
-frame "<!--frame1-->" - didStartProvisionalLoadForFrame
-resources/cache-expired-subresource.webarchive - willSendRequest <NSURLRequest URL resources/cache-expired-subresource.webarchive, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
 main frame - didFinishDocumentLoadForFrame
 cache-expired-subresource.html - didFinishLoading
+frame "<!--frame1-->" - didStartProvisionalLoadForFrame
+resources/cache-expired-subresource.webarchive - willSendRequest <NSURLRequest URL resources/cache-expired-subresource.webarchive, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
 resources/cache-expired-subresource.webarchive - didReceiveResponse <NSURLResponse resources/cache-expired-subresource.webarchive, http status code 0>
 frame "<!--frame1-->" - didCommitLoadForFrame
 http://localhost/pink-bullet.png - willSendRequest <NSURLRequest URL http://localhost/pink-bullet.png, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
 frame "<!--frame1-->" - didFinishDocumentLoadForFrame
 resources/cache-expired-subresource.webarchive - didFinishLoading
-http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/pink-bullet.png, http status code 0>
+http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/pink-bullet.png, http status code 200>
 http://localhost/pink-bullet.png - didFinishLoading
 frame "<!--frame1-->" - didHandleOnloadEventsForFrame
 main frame - didHandleOnloadEventsForFrame
@@ -28,14 +28,14 @@ layer at (0,0) size 800x600
             RenderBody {BODY} at (8,8) size 284x134
               RenderText {#text} at (0,0) size 240x19
                 text run at (0,0) width 240: "This page has a small pink circle in it"
-              RenderBR {BR} at (239,15) size 1x0
+              RenderBR {BR} at (239,0) size 1x19
               RenderImage {IMG} at (0,20) size 11x9
-              RenderBR {BR} at (11,29) size 0x0
+              RenderBR {BR} at (11,14) size 0x19
               RenderText {#text} at (0,29) size 163x19
                 text run at (0,29) width 107: "See, right there! "
                 text run at (106,29) width 57: "A circle!"
-              RenderBR {BR} at (162,44) size 1x0
-      RenderBR {BR} at (304,154) size 0x0
+              RenderBR {BR} at (162,29) size 1x19
+      RenderBR {BR} at (304,139) size 0x19
       RenderText {#text} at (0,154) size 736x39
         text run at (0,154) width 736: "This tests that loading a webarchive with a subresource that has an expired cache header does not attempt to hit the"
         text run at (0,174) width 364: "network for resource, and actually uses the archived data"
diff --git a/LayoutTests/platform/mac-wk1/TestExpectations b/LayoutTests/platform/mac-wk1/TestExpectations
index 0c0d3a7deaab..ad65b1db3d3e 100644
--- a/LayoutTests/platform/mac-wk1/TestExpectations
+++ b/LayoutTests/platform/mac-wk1/TestExpectations
@@ -1465,6 +1465,9 @@ imported/w3c/web-platform-tests/cookies/secure/set-from-ws.sub.html [ Failure ]
 
 webkit.org/b/244308 imported/w3c/web-platform-tests/cookies/attributes/attributes-ctl.sub.html [ Pass Failure ]
 
+# Only flakey in WK1
+webarchive/loading/test-loading-archive-subresource-null-mimetype.html [ Failure Pass ]
+
 # WebKit1 frames use native scrollbars causing these tests to fail.
 
 # Lack of ways to control the cross-frame scroll chaining in WK1.
diff --git a/LayoutTests/platform/mac-wk1/webarchive/loading/test-loading-archive-subresource-expected.txt b/LayoutTests/platform/mac-wk1/webarchive/loading/test-loading-archive-subresource-expected.txt
index 311f51997006..f92ab41e5c82 100644
--- a/LayoutTests/platform/mac-wk1/webarchive/loading/test-loading-archive-subresource-expected.txt
+++ b/LayoutTests/platform/mac-wk1/webarchive/loading/test-loading-archive-subresource-expected.txt
@@ -2,7 +2,10 @@ main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 frame "<!--frame1-->" - didStartProvisionalLoadForFrame
 main frame - didFinishDocumentLoadForFrame
+frame "<!--frame1-->" - didCommitLoadForFrame
+frame "<!--frame1-->" - didFinishDocumentLoadForFrame
+frame "<!--frame1-->" - didHandleOnloadEventsForFrame
 main frame - didHandleOnloadEventsForFrame
-frame "<!--frame1-->" - didFailProvisionalLoadWithError
+frame "<!--frame1-->" - didFinishLoadForFrame
 main frame - didFinishLoadForFrame
  This test assumes the webarchive is not loaded.
diff --git a/LayoutTests/platform/mac-wk2/webarchive/loading/cache-expired-subresource-expected.txt b/LayoutTests/platform/mac-wk2/webarchive/loading/cache-expired-subresource-expected.txt
new file mode 100644
index 000000000000..be965e1e74aa
--- /dev/null
+++ b/LayoutTests/platform/mac-wk2/webarchive/loading/cache-expired-subresource-expected.txt
@@ -0,0 +1,41 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+main frame - didFinishDocumentLoadForFrame
+cache-expired-subresource.html - didFinishLoading
+frame "<!--frame1-->" - didStartProvisionalLoadForFrame
+resources/cache-expired-subresource.webarchive - willSendRequest <NSURLRequest URL resources/cache-expired-subresource.webarchive, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
+resources/cache-expired-subresource.webarchive - didReceiveResponse <NSURLResponse resources/cache-expired-subresource.webarchive, http status code 0>
+frame "<!--frame1-->" - didCommitLoadForFrame
+http://localhost/pink-bullet.png - willSendRequest <NSURLRequest URL http://localhost/pink-bullet.png, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
+frame "<!--frame1-->" - didFinishDocumentLoadForFrame
+resources/cache-expired-subresource.webarchive - didFinishLoading
+http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/pink-bullet.png, http status code 200>
+http://localhost/pink-bullet.png - didFinishLoading
+frame "<!--frame1-->" - didHandleOnloadEventsForFrame
+main frame - didHandleOnloadEventsForFrame
+frame "<!--frame1-->" - didFinishLoadForFrame
+main frame - didFinishLoadForFrame
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderIFrame {IFRAME} at (0,0) size 304x154 [border: (2px inset #000000)]
+        layer at (0,0) size 300x150
+          RenderView at (0,0) size 300x150
+        layer at (0,0) size 300x150
+          RenderBlock {HTML} at (0,0) size 300x150
+            RenderBody {BODY} at (8,8) size 284x134
+              RenderText {#text} at (0,0) size 240x18
+                text run at (0,0) width 240: "This page has a small pink circle in it"
+              RenderBR {BR} at (239,0) size 1x18
+              RenderImage {IMG} at (0,18) size 11x9
+              RenderBR {BR} at (11,13) size 0x18
+              RenderText {#text} at (0,27) size 163x18
+                text run at (0,27) width 107: "See, right there! "
+                text run at (106,27) width 57: "A circle!"
+              RenderBR {BR} at (162,27) size 1x18
+      RenderBR {BR} at (304,140) size 0x18
+      RenderText {#text} at (0,154) size 736x36
+        text run at (0,154) width 736: "This tests that loading a webarchive with a subresource that has an expired cache header does not attempt to hit the"
+        text run at (0,172) width 364: "network for resource, and actually uses the archived data"
diff --git a/LayoutTests/platform/mac/TestExpectations b/LayoutTests/platform/mac/TestExpectations
index bd78906e750d..90b900ff569b 100644
--- a/LayoutTests/platform/mac/TestExpectations
+++ b/LayoutTests/platform/mac/TestExpectations
@@ -276,9 +276,6 @@ webkit.org/b/103435 fast/backgrounds/background-opaque-images-over-color.html [
 
 webkit.org/b/56685 webarchive/test-link-rel-icon.html [ Failure ]
 
-# The following two tests are expected to fail until <rdar://problem/34063313> is available on test systems.
-webarchive/loading/cache-expired-subresource.html [ Failure ]
-
 # Quota API is not supported.
 storage/storageinfo-missing-arguments.html
 storage/storageinfo-no-callbacks.html
diff --git a/LayoutTests/platform/mac/fast/loader/webarchive-encoding-respected.html b/LayoutTests/platform/mac/fast/loader/webarchive-encoding-respected.html
index dad53bfa2d98..f21906dd673b 100644
--- a/LayoutTests/platform/mac/fast/loader/webarchive-encoding-respected.html
+++ b/LayoutTests/platform/mac/fast/loader/webarchive-encoding-respected.html
@@ -1,4 +1,4 @@
-<script>
+<script><!-- webkit-test-runner [ AlwaysAllowLocalWebarchive=true ] -->
 if (window.testRunner) {
 	testRunner.dumpAsText();
 	testRunner.dumpChildFramesAsText();
diff --git a/LayoutTests/platform/mac/webarchive/loading/cache-expired-subresource-expected.txt b/LayoutTests/platform/mac/webarchive/loading/cache-expired-subresource-expected.txt
index f4f82962ac07..2cba2310b20f 100644
--- a/LayoutTests/platform/mac/webarchive/loading/cache-expired-subresource-expected.txt
+++ b/LayoutTests/platform/mac/webarchive/loading/cache-expired-subresource-expected.txt
@@ -9,7 +9,7 @@ frame "<!--frame1-->" - didCommitLoadForFrame
 http://localhost/pink-bullet.png - willSendRequest <NSURLRequest URL http://localhost/pink-bullet.png, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
 frame "<!--frame1-->" - didFinishDocumentLoadForFrame
 resources/cache-expired-subresource.webarchive - didFinishLoading
-http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/test.php, http status code 200>
+http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/pink-bullet.png, http status code 200>
 http://localhost/pink-bullet.png - didFinishLoading
 frame "<!--frame1-->" - didHandleOnloadEventsForFrame
 main frame - didHandleOnloadEventsForFrame
@@ -28,14 +28,14 @@ layer at (0,0) size 800x600
             RenderBody {BODY} at (8,8) size 284x134
               RenderText {#text} at (0,0) size 240x18
                 text run at (0,0) width 240: "This page has a small pink circle in it"
-              RenderBR {BR} at (239,14) size 1x0
+              RenderBR {BR} at (239,0) size 1x18
               RenderImage {IMG} at (0,18) size 11x9
-              RenderBR {BR} at (11,27) size 0x0
+              RenderBR {BR} at (11,13) size 0x18
               RenderText {#text} at (0,27) size 163x18
                 text run at (0,27) width 107: "See, right there! "
                 text run at (106,27) width 57: "A circle!"
-              RenderBR {BR} at (162,41) size 1x0
-      RenderBR {BR} at (304,154) size 0x0
+              RenderBR {BR} at (162,27) size 1x18
+      RenderBR {BR} at (304,140) size 0x18
       RenderText {#text} at (0,154) size 736x36
         text run at (0,154) width 736: "This tests that loading a webarchive with a subresource that has an expired cache header does not attempt to hit the"
         text run at (0,172) width 364: "network for resource, and actually uses the archived data"
diff --git a/LayoutTests/platform/wk2/webarchive/loading/javascript-url-iframe-crash-expected.txt b/LayoutTests/platform/wk2/webarchive/loading/javascript-url-iframe-crash-expected.txt
new file mode 100644
index 000000000000..aafd7085f151
--- /dev/null
+++ b/LayoutTests/platform/wk2/webarchive/loading/javascript-url-iframe-crash-expected.txt
@@ -0,0 +1,13 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+main frame - willPerformClientRedirectToURL: resources/javascript-url-iframe-crash.webarchive
+main frame - didFinishDocumentLoadForFrame
+main frame - didFinishLoadForFrame

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2024-40857
Bugzilla 268724
Component WebKit
Bug Class CrossOrigin
Severity medium
Commit a350c1e9191addf5…
Advisory Apple Advisory

Terminology

  • Web Archive (.webarchive) — A single-file snapshot of a page and its subresources whose main resource can specify an arbitrary origin/URL and can contain executable script, which is why loading one is security-sensitive.
  • UXSS (universal cross-site scripting) — A class of bug where attacker script escapes the same-origin policy and can run against or read state of arbitrary origins, bypassing per-site isolation.
  • WebsiteDataStore — The container for a browsing profile’s persistent or non-persistent state (cookies, storage, caches); createNonPersistent() yields an ephemeral, isolated store.
  • First-party for cookies — The top-level origin the network process uses to scope cookie access for a load; AddAllowedFirstPartyForCookies registers which domains a process may treat as first-party.
  • Process swap on navigation — WebKit’s mechanism to move a navigation into a fresh WebContent process to enforce isolation boundaries, forced here so the archive runs against the ephemeral store.

Root Cause Analysis

This is a universal cross-site-scripting (UXSS) / cross-origin-isolation logic bug in WebKit’s Web Archive loading path. A .webarchive (application/x-webarchive, application/x-mimearchive, multipart/related) is a self-contained snapshot whose main resource can declare an arbitrary origin/URL and can contain script; the security invariant is that loading such an archive must not let its script run against, or read the persistent state (cookies, storage, credentials, permissions) of, real origins in the user’s normal (persistent) data store. The large diff is spread across many files, but the security-relevant core is isolable: it moves web-archive loads onto an isolated, non-persistent (ephemeral) WebsiteDataStore and hardens the gate that decides when an archive may load at all. In WebPageProxy::receivedNavigationActionPolicyDecision(), when the new loadWebArchiveWithEphemeralStorageEnabled preference is on and the navigation is a Use decision for a substitute web archive or a file:// URL ending in .webarchive, the code now swaps in a freshly created non-persistent data store (WebsiteDataStore::createNonPersistent()), records the replaced persistent store, forces a process swap, and marks the load as LoadedWebArchive::Yes; navigating away restores the original store. That LoadedWebArchive flag is threaded through backForwardAddItemShared(), ProvisionalPageProxy, WebBackForwardListItem (which now carries a dataStoreForWebArchive), and NetworkProcess::AddAllowedFirstPartyForCookies so cookie/first-party bookkeeping matches the ephemeral store rather than the user’s real store. Additionally, once a web archive has been loaded, WebPageProxy::queryPermission() now returns PermissionState::Denied outright, preventing archive content from gaining camera/mic/geolocation etc. DocumentLoader::disallowWebArchive() is refactored to use the new shared MIMETypeRegistry::isWebArchiveMIMEType() helper (whose isValidValue guard also avoids feeding an over-long/invalid string to the hash set) and to key the local-webarchive allowance on the AlwaysAllowLocalWebarchive setting rather than a per-loader flag. Separately, isRequestFromClientOrUserInput is now propagated through LoadParameters and only set on the WebProcess FrameLoadRequest when the UI process says the load actually originated from client/user input, so a web-content-initiated navigation cannot masquerade as user-initiated. The pre-patch invariant violation: archive content loaded into (or sharing) the persistent data store and inheriting normal permission/first-party-cookie treatment, allowing it to act with the authority of real origins — i.e. UXSS. Note: because the enumerated source files (LoadParameters.serialization.in, UnifiedWebPreferences.yaml, the API tests) are omitted, the exact new-preference default and IPC serialization are inferred from the code that references them, not shown by the diff.

Attack Path

1. Deliver a malicious web archive

The attacker gets a crafted .webarchive onto the victim’s machine or induces a load of one (download, email attachment, or a page that triggers a substitute-data web-archive load), with a main resource that claims a sensitive origin and embeds attacker script.

2. Archive loads under the persistent store (pre-patch)

Before the fix, the archive is committed into a WebContent process using the user’s normal persistent WebsiteDataStore, so its scripted content shares cookies/storage/credentials and first-party-for-cookies treatment with real origins.

3. Script runs with borrowed authority

Because the archive’s main resource can spoof any origin, its script executes as that origin and can read cookies, localStorage, and issue authenticated requests — universal cross-site scripting against arbitrary sites.

4. Escalate via permissions

Pre-patch, archive content could also request powerful permissions (camera, microphone, geolocation) as the spoofed origin, since queryPermission did not special-case archive loads.

5. Fix cuts off the primitive

Post-patch, the archive is confined to a throwaway non-persistent data store isolated from real cookies/storage, all permission queries return Denied, and cookie/first-party bookkeeping tracks the ephemeral store, so archive script can no longer read or act with real-origin state.

Impact Assessment

This is a same-origin-policy / cross-origin isolation failure (UXSS), not a memory-corruption primitive: exploited pre-patch, malicious web-archive script runs with the authority of spoofed real origins, letting it read cookies/storage and make authenticated cross-site requests, and (before the queryPermission fix) request powerful device permissions as those origins. It is a confidentiality/integrity break confined to the WebContent process and the browsing profile rather than a path to native code execution, so no heap-grooming or ACE chain is implied by the diff. Because a web archive is a delivered file rather than a drive-by web page, exploitation typically requires getting the victim to open a crafted archive, which is why the severity is rated medium.

Audit Directions

Completeness of the archive-isolation gate

In receivedNavigationActionPolicyDecision audit every branch that decides isSubstituteWebArchive / file .webarchive: check reloads, back/forward (dataStoreForWebArchive reuse), and the didLoadWebArchive() restore path for a way to load archive content while m_replacedDataStoreForWebArchiveLoad stays null (grep LoadedWebArchive, replacedDataStoreForWebArchiveLoad, didLoadWebArchive).

MIME/type-sniffing bypasses of disallowWebArchive

Check whether an archive can reach a load without matching isWebArchiveMIMEType (e.g. null/spoofed MIME, multipart/related sub-types, GTK message/rfc822, or QUICK_LOOK) so it is treated as a normal document; grep isWebArchiveMIMEType, disallowWebArchive, allowsWebArchiveForMainFrame, alwaysAllowLocalWebarchive.

isRequestFromClientOrUserInput spoofing

Trace every setter/consumer of isRequestFromClientOrUserInput across LoadParameters/FrameLoadRequest/NavigationAction (grep isRequestFromClientOrUserInput, setIsRequestFromClientOrUserInput) for other loads that still set it unconditionally, letting web-initiated navigations pose as user-initiated and bypass local-file/archive restrictions.

Permission and state leaks under archive loads

Beyond queryPermission, look for other UIProcess features (autofill, credential/storage access, media capture, downloads) that grant capability without checking didLoadWebArchive(); grep for PermissionState, websiteDataStore() uses that assume the persistent store during an archive load.

Changed Functions

Function File Change Note
DocumentLoader::disallowWebArchive Source/WebCore/loader/DocumentLoader.cpp modified Replaces the inline MIME hash set with MIMETypeRegistry::isWebArchiveMIMEType() and gates the local-archive allowance on the AlwaysAllowLocalWebarchive setting via a new alwaysAllowLocalWebArchive path guarded by ENABLE(WEB_ARCHIVE).
MIMETypeRegistry::isWebArchiveMIMEType Source/WebCore/platform/MIMETypeRegistry.cpp added New shared helper returning whether a MIME type is a web-archive type, with an isValidValue() guard so an invalid/over-long string is rejected before contains().
WebPageProxy::receivedNavigationActionPolicyDecision Source/WebKit/UIProcess/WebPageProxy.cpp modified Core fix: for a substitute or file:// .webarchive Use decision it swaps in a non-persistent WebsiteDataStore (or reuses the back/forward item’s stored one), records the replaced store, forces a process swap, and threads LoadedWebArchive/replacedDataStore into the continuation.
WebPageProxy::continueNavigationInNewProcess Source/WebKit/UIProcess/WebPageProxy.cpp modified Gains LoadedWebArchive and replacedDataStoreForWebArchiveLoad parameters, passes the archive flag to AddAllowedFirstPartyForCookies and the replaced store to the new ProvisionalPageProxy.
WebPageProxy::backForwardAddItem / backForwardAddItemShared Source/WebKit/UIProcess/WebPageProxy.cpp modified Takes a LoadedWebArchive argument and, when Yes, stores the process’s (ephemeral) data store on the WebBackForwardListItem so back/forward reloads reuse the isolated store.
WebPageProxy::queryPermission Source/WebKit/UIProcess/WebPageProxy.cpp modified Returns PermissionState::Denied immediately when didLoadWebArchive() is true, blocking powerful-feature grants to archive content.
WebPageProxy::swapToProvisionalPage Source/WebKit/UIProcess/WebPageProxy.cpp modified Carries the provisional page’s replacedDataStoreForWebArchiveLoad onto the committed page so the persistent store can be restored later.
WebPageProxy::loadRequestWithNavigationShared / loadFile Source/WebKit/UIProcess/WebPageProxy.cpp modified Set loadParameters.isRequestFromClientOrUserInput from navigation/app-initiated state so only genuinely client/user-initiated loads carry that flag.
ProvisionalPageProxy::ProvisionalPageProxy / backForwardAddItem Source/WebKit/UIProcess/ProvisionalPageProxy.cpp modified New replacedDataStoreForWebArchiveLoad member; forwards LoadedWebArchive::Yes/No to backForwardAddItemShared based on it.
FrameLoader::loadWithNavigationAction / loadDifferentDocumentItem Source/WebCore/loader/FrameLoader.cpp modified Propagates isRequestFromClientOrUserInput onto the DocumentLoader and preserves the app-initiated bit when rebuilding the navigation action for original-URL loads.
WebPage::loadRequest Source/WebKit/WebProcess/WebPage/WebPage.cpp modified Only calls frameLoadRequest.setIsRequestFromClientOrUserInput() when loadParameters.isRequestFromClientOrUserInput is true, instead of unconditionally, so web-initiated loads are not mislabeled as user input.
WebBackForwardListItem (dataStoreForWebArchive) Source/WebKit/Shared/WebBackForwardListItem.h modified Adds m_dataStoreForWebArchive with getter/setter so the ephemeral store associated with an archive load survives on the history item.

Regression Test

The fix ships this proof-of-concept / regression test:

diff --git a/LayoutTests/platform/ios/TestExpectations b/LayoutTests/platform/ios/TestExpectations
index 1a01d0af7fad..a4f87d04ce7a 100644
--- a/LayoutTests/platform/ios/TestExpectations
+++ b/LayoutTests/platform/ios/TestExpectations
@@ -1548,9 +1548,6 @@ webarchive/test-css-url-resources-in-stylesheets.html [ Failure ]
 webarchive/test-css-url-resources-inline-styles.html [ Failure ]
 webarchive/test-link-rel-icon.html [ Failure ]
 
-# The following two tests are expected to fail until <rdar://problem/34063313> is available on test systems.
-webarchive/loading/cache-expired-subresource.html [ Failure ]
-
 # Webarchive tests that fail due to <https://bugs.webkit.org/show_bug.cgi?id=82665>:
 http/tests/webarchive/test-css-url-encoding-shift-jis.html
 http/tests/webarchive/test-css-url-encoding-utf-8.html
diff --git a/LayoutTests/platform/ios/webarchive/loading/cache-expired-subresource-expected.txt b/LayoutTests/platform/ios/webarchive/loading/cache-expired-subresource-expected.txt
index 2bfa51ed620c..e91452ddb29c 100644
--- a/LayoutTests/platform/ios/webarchive/loading/cache-expired-subresource-expected.txt
+++ b/LayoutTests/platform/ios/webarchive/loading/cache-expired-subresource-expected.txt
@@ -1,15 +1,15 @@
 main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
-frame "<!--frame1-->" - didStartProvisionalLoadForFrame
-resources/cache-expired-subresource.webarchive - willSendRequest <NSURLRequest URL resources/cache-expired-subresource.webarchive, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
 main frame - didFinishDocumentLoadForFrame
 cache-expired-subresource.html - didFinishLoading
+frame "<!--frame1-->" - didStartProvisionalLoadForFrame
+resources/cache-expired-subresource.webarchive - willSendRequest <NSURLRequest URL resources/cache-expired-subresource.webarchive, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
 resources/cache-expired-subresource.webarchive - didReceiveResponse <NSURLResponse resources/cache-expired-subresource.webarchive, http status code 0>
 frame "<!--frame1-->" - didCommitLoadForFrame
 http://localhost/pink-bullet.png - willSendRequest <NSURLRequest URL http://localhost/pink-bullet.png, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
 frame "<!--frame1-->" - didFinishDocumentLoadForFrame
 resources/cache-expired-subresource.webarchive - didFinishLoading
-http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/pink-bullet.png, http status code 0>
+http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/pink-bullet.png, http status code 200>
 http://localhost/pink-bullet.png - didFinishLoading
 frame "<!--frame1-->" - didHandleOnloadEventsForFrame
 main frame - didHandleOnloadEventsForFrame
@@ -28,14 +28,14 @@ layer at (0,0) size 800x600
             RenderBody {BODY} at (8,8) size 284x134
               RenderText {#text} at (0,0) size 240x19
                 text run at (0,0) width 240: "This page has a small pink circle in it"
-              RenderBR {BR} at (239,15) size 1x0
+              RenderBR {BR} at (239,0) size 1x19
               RenderImage {IMG} at (0,20) size 11x9
-              RenderBR {BR} at (11,29) size 0x0
+              RenderBR {BR} at (11,14) size 0x19
               RenderText {#text} at (0,29) size 163x19
                 text run at (0,29) width 107: "See, right there! "
                 text run at (106,29) width 57: "A circle!"
-              RenderBR {BR} at (162,44) size 1x0
-      RenderBR {BR} at (304,154) size 0x0
+              RenderBR {BR} at (162,29) size 1x19
+      RenderBR {BR} at (304,139) size 0x19
       RenderText {#text} at (0,154) size 736x39
         text run at (0,154) width 736: "This tests that loading a webarchive with a subresource that has an expired cache header does not attempt to hit the"
         text run at (0,174) width 364: "network for resource, and actually uses the archived data"
diff --git a/LayoutTests/platform/mac-wk1/TestExpectations b/LayoutTests/platform/mac-wk1/TestExpectations
index 0c0d3a7deaab..ad65b1db3d3e 100644
--- a/LayoutTests/platform/mac-wk1/TestExpectations
+++ b/LayoutTests/platform/mac-wk1/TestExpectations
@@ -1465,6 +1465,9 @@ imported/w3c/web-platform-tests/cookies/secure/set-from-ws.sub.html [ Failure ]
 
 webkit.org/b/244308 imported/w3c/web-platform-tests/cookies/attributes/attributes-ctl.sub.html [ Pass Failure ]
 
+# Only flakey in WK1
+webarchive/loading/test-loading-archive-subresource-null-mimetype.html [ Failure Pass ]
+
 # WebKit1 frames use native scrollbars causing these tests to fail.
 
 # Lack of ways to control the cross-frame scroll chaining in WK1.
diff --git a/LayoutTests/platform/mac-wk1/webarchive/loading/test-loading-archive-subresource-expected.txt b/LayoutTests/platform/mac-wk1/webarchive/loading/test-loading-archive-subresource-expected.txt
index 311f51997006..f92ab41e5c82 100644
--- a/LayoutTests/platform/mac-wk1/webarchive/loading/test-loading-archive-subresource-expected.txt
+++ b/LayoutTests/platform/mac-wk1/webarchive/loading/test-loading-archive-subresource-expected.txt
@@ -2,7 +2,10 @@ main frame - didStartProvisionalLoadForFrame
 main frame - didCommitLoadForFrame
 frame "<!--frame1-->" - didStartProvisionalLoadForFrame
 main frame - didFinishDocumentLoadForFrame
+frame "<!--frame1-->" - didCommitLoadForFrame
+frame "<!--frame1-->" - didFinishDocumentLoadForFrame
+frame "<!--frame1-->" - didHandleOnloadEventsForFrame
 main frame - didHandleOnloadEventsForFrame
-frame "<!--frame1-->" - didFailProvisionalLoadWithError
+frame "<!--frame1-->" - didFinishLoadForFrame
 main frame - didFinishLoadForFrame
  This test assumes the webarchive is not loaded.
diff --git a/LayoutTests/platform/mac-wk2/webarchive/loading/cache-expired-subresource-expected.txt b/LayoutTests/platform/mac-wk2/webarchive/loading/cache-expired-subresource-expected.txt
new file mode 100644
index 000000000000..be965e1e74aa
--- /dev/null
+++ b/LayoutTests/platform/mac-wk2/webarchive/loading/cache-expired-subresource-expected.txt
@@ -0,0 +1,41 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+main frame - didFinishDocumentLoadForFrame
+cache-expired-subresource.html - didFinishLoading
+frame "<!--frame1-->" - didStartProvisionalLoadForFrame
+resources/cache-expired-subresource.webarchive - willSendRequest <NSURLRequest URL resources/cache-expired-subresource.webarchive, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
+resources/cache-expired-subresource.webarchive - didReceiveResponse <NSURLResponse resources/cache-expired-subresource.webarchive, http status code 0>
+frame "<!--frame1-->" - didCommitLoadForFrame
+http://localhost/pink-bullet.png - willSendRequest <NSURLRequest URL http://localhost/pink-bullet.png, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
+frame "<!--frame1-->" - didFinishDocumentLoadForFrame
+resources/cache-expired-subresource.webarchive - didFinishLoading
+http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/pink-bullet.png, http status code 200>
+http://localhost/pink-bullet.png - didFinishLoading
+frame "<!--frame1-->" - didHandleOnloadEventsForFrame
+main frame - didHandleOnloadEventsForFrame
+frame "<!--frame1-->" - didFinishLoadForFrame
+main frame - didFinishLoadForFrame
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderIFrame {IFRAME} at (0,0) size 304x154 [border: (2px inset #000000)]
+        layer at (0,0) size 300x150
+          RenderView at (0,0) size 300x150
+        layer at (0,0) size 300x150
+          RenderBlock {HTML} at (0,0) size 300x150
+            RenderBody {BODY} at (8,8) size 284x134
+              RenderText {#text} at (0,0) size 240x18
+                text run at (0,0) width 240: "This page has a small pink circle in it"
+              RenderBR {BR} at (239,0) size 1x18
+              RenderImage {IMG} at (0,18) size 11x9
+              RenderBR {BR} at (11,13) size 0x18
+              RenderText {#text} at (0,27) size 163x18
+                text run at (0,27) width 107: "See, right there! "
+                text run at (106,27) width 57: "A circle!"
+              RenderBR {BR} at (162,27) size 1x18
+      RenderBR {BR} at (304,140) size 0x18
+      RenderText {#text} at (0,154) size 736x36
+        text run at (0,154) width 736: "This tests that loading a webarchive with a subresource that has an expired cache header does not attempt to hit the"
+        text run at (0,172) width 364: "network for resource, and actually uses the archived data"
diff --git a/LayoutTests/platform/mac/TestExpectations b/LayoutTests/platform/mac/TestExpectations
index bd78906e750d..90b900ff569b 100644
--- a/LayoutTests/platform/mac/TestExpectations
+++ b/LayoutTests/platform/mac/TestExpectations
@@ -276,9 +276,6 @@ webkit.org/b/103435 fast/backgrounds/background-opaque-images-over-color.html [
 
 webkit.org/b/56685 webarchive/test-link-rel-icon.html [ Failure ]
 
-# The following two tests are expected to fail until <rdar://problem/34063313> is available on test systems.
-webarchive/loading/cache-expired-subresource.html [ Failure ]
-
 # Quota API is not supported.
 storage/storageinfo-missing-arguments.html
 storage/storageinfo-no-callbacks.html
diff --git a/LayoutTests/platform/mac/fast/loader/webarchive-encoding-respected.html b/LayoutTests/platform/mac/fast/loader/webarchive-encoding-respected.html
index dad53bfa2d98..f21906dd673b 100644
--- a/LayoutTests/platform/mac/fast/loader/webarchive-encoding-respected.html
+++ b/LayoutTests/platform/mac/fast/loader/webarchive-encoding-respected.html
@@ -1,4 +1,4 @@
-<script>
+<script><!-- webkit-test-runner [ AlwaysAllowLocalWebarchive=true ] -->
 if (window.testRunner) {
 	testRunner.dumpAsText();
 	testRunner.dumpChildFramesAsText();
diff --git a/LayoutTests/platform/mac/webarchive/loading/cache-expired-subresource-expected.txt b/LayoutTests/platform/mac/webarchive/loading/cache-expired-subresource-expected.txt
index f4f82962ac07..2cba2310b20f 100644
--- a/LayoutTests/platform/mac/webarchive/loading/cache-expired-subresource-expected.txt
+++ b/LayoutTests/platform/mac/webarchive/loading/cache-expired-subresource-expected.txt
@@ -9,7 +9,7 @@ frame "<!--frame1-->" - didCommitLoadForFrame
 http://localhost/pink-bullet.png - willSendRequest <NSURLRequest URL http://localhost/pink-bullet.png, main document URL cache-expired-subresource.html, http method GET> redirectResponse (null)
 frame "<!--frame1-->" - didFinishDocumentLoadForFrame
 resources/cache-expired-subresource.webarchive - didFinishLoading
-http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/test.php, http status code 200>
+http://localhost/pink-bullet.png - didReceiveResponse <NSURLResponse http://localhost/pink-bullet.png, http status code 200>
 http://localhost/pink-bullet.png - didFinishLoading
 frame "<!--frame1-->" - didHandleOnloadEventsForFrame
 main frame - didHandleOnloadEventsForFrame
@@ -28,14 +28,14 @@ layer at (0,0) size 800x600
             RenderBody {BODY} at (8,8) size 284x134
               RenderText {#text} at (0,0) size 240x18
                 text run at (0,0) width 240: "This page has a small pink circle in it"
-              RenderBR {BR} at (239,14) size 1x0
+              RenderBR {BR} at (239,0) size 1x18
               RenderImage {IMG} at (0,18) size 11x9
-              RenderBR {BR} at (11,27) size 0x0
+              RenderBR {BR} at (11,13) size 0x18
               RenderText {#text} at (0,27) size 163x18
                 text run at (0,27) width 107: "See, right there! "
                 text run at (106,27) width 57: "A circle!"
-              RenderBR {BR} at (162,41) size 1x0
-      RenderBR {BR} at (304,154) size 0x0
+              RenderBR {BR} at (162,27) size 1x18
+      RenderBR {BR} at (304,140) size 0x18
       RenderText {#text} at (0,154) size 736x36
         text run at (0,154) width 736: "This tests that loading a webarchive with a subresource that has an expired cache header does not attempt to hit the"
         text run at (0,172) width 364: "network for resource, and actually uses the archived data"
diff --git a/LayoutTests/platform/wk2/webarchive/loading/javascript-url-iframe-crash-expected.txt b/LayoutTests/platform/wk2/webarchive/loading/javascript-url-iframe-crash-expected.txt
new file mode 100644
index 000000000000..aafd7085f151
--- /dev/null
+++ b/LayoutTests/platform/wk2/webarchive/loading/javascript-url-iframe-crash-expected.txt
@@ -0,0 +1,13 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+main frame - willPerformClientRedirectToURL: resources/javascript-url-iframe-crash.webarchive
+main frame - didFinishDocumentLoadForFrame
+main frame - didFinishLoadForFrame
+main frame - didCancelClientRedirectForFrame
+frame "<!--frame1-->" - didFinishDocumentLoadForFrame
+frame "<!--frame1-->" - didHandleOnloadEventsForFrame
+main frame - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+Loading this webarchive with a "non-empty javascript URL iframe" should not crash.
+
diff --git a/LayoutTests/platform/wk2/webarchive/loading/object-expected.txt b/LayoutTests/platform/wk2/webarchive/loading/object-expected.txt
new file mode 100644
index 000000000000..f446855424c5
--- /dev/null
+++ b/LayoutTests/platform/wk2/webarchive/loading/object-expected.txt
@@ -0,0 +1,16 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+main frame - willPerformClientRedirectToURL: resources/object.webarchive
+main frame - didFinishDocumentLoadForFrame
+main frame - didFinishLoadForFrame
+main frame - didCancelClientRedirectForFrame
+main frame - didFinishDocumentLoadForFrame
+frame "<!--frame1-->" - didStartProvisionalLoadForFrame
+frame "<!--frame1-->" - didCommitLoadForFrame
+frame "<!--frame1-->" - didFinishDocumentLoadForFrame
+frame "<!--frame1-->" - didHandleOnloadEventsForFrame
+main frame - didHandleOnloadEventsForFrame
+frame "<!--frame1-->" - didFinishLoadForFrame
+main frame - didFinishLoadForFrame
+Loading this webarchive with an object should not crash.
+
diff --git a/LayoutTests/platform/wk2/webarchive/loading/test-loading-top-archive-expected.txt b/LayoutTests/platform/wk2/webarchive/loading/test-loading-top-archive-expected.txt
new file mode 100644
index 000000000000..bb86d5046bb6
--- /dev/null
+++ b/LayoutTests/platform/wk2/webarchive/loading/test-loading-top-archive-expected.txt
@@ -0,0 +1,10 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+main frame - willPerformClientRedirectToURL: resources/top.webarchive
+main frame - didFinishDocumentLoadForFrame
+main frame - didFinishLoadForFrame
+main frame - didCancelClientRedirectForFrame
+main frame - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+main frame - didFinishLoadForFrame
+hello, world.
diff --git a/LayoutTests/platform/wk2/webarchive/loading/video-in-webarchive-expected.txt b/LayoutTests/platform/wk2/webarchive/loading/video-in-webarchive-expected.txt
index 02daa557c635..88d0906af149 100644
--- a/LayoutTests/platform/wk2/webarchive/loading/video-in-webarchive-expected.txt
+++ b/LayoutTests/platform/wk2/webarchive/loading/video-in-webarchive-expected.txt
@@ -3,9 +3,7 @@ main frame - didCommitLoadForFrame
 main frame - willPerformClientRedirectToURL: resources/video-in-webarchive.webarchive
 main frame - didFinishDocumentLoadForFrame
 main frame - didFinishLoadForFrame
-main frame - didStartProvisionalLoadForFrame
 main frame - didCancelClientRedirectForFrame
-main frame - didCommitLoadForFrame
 main frame - didFinishDocumentLoadForFrame
 frame "<!--frame1-->" - didStartProvisionalLoadForFrame
 main frame - didHandleOnloadEventsForFrame
diff --git a/LayoutTests/webarchive/loading/cache-expired-subresource.html b/LayoutTests/webarchive/loading/cache-expired-subresource.html
index 0daf00b0e776..645ed3ae18b6 100644
--- a/LayoutTests/webarchive/loading/cache-expired-subresource.html
+++ b/LayoutTests/webarchive/loading/cache-expired-subresource.html
@@ -1,4 +1,4 @@
-<html>
+<html><!-- webkit-test-runner [ AlwaysAllowLocalWebarchive=true ] -->
 <script>
     if (window.testRunner) {
         testRunner.dumpResourceLoadCallbacks();
diff --git a/LayoutTests/webarchive/loading/javascript-url-iframe-crash.html b/LayoutTests/webarchive/loading/javascript-url-iframe-crash.html
index 2d6d7137d504..d6fe97c4b484 100644
--- a/LayoutTests/webarchive/loading/javascript-url-iframe-crash.html
+++ b/LayoutTests/webarchive/loading/javascript-url-iframe-crash.html
@@ -1,4 +1,4 @@
-<html>
+<html><!-- webkit-test-runner [ AlwaysAllowLocalWebarchive=true ] -->
 <script>
     if (window.testRunner) {
         testRunner.dumpAsText();
diff --git a/LayoutTests/webarchive/loading/mainresource-null-mimetype-crash.html b/LayoutTests/webarchive/loading/mainresource-null-mimetype-crash.html
index 5757fe819291..725647f9f8bd 100644
--- a/LayoutTests/webarchive/loading/mainresource-null-mimetype-crash.html
+++ b/LayoutTests/webarchive/loading/mainresource-null-mimetype-crash.html
@@ -1,4 +1,4 @@
-<html>
+<html><!-- webkit-test-runner [ AlwaysAllowLocalWebarchive=true ] -->
 <script>
     if (window.testRunner) {
         testRunner.dumpAsText();
diff --git a/LayoutTests/webarchive/loading/missing-data.html b/LayoutTests/webarchive/loading/missing-data.html
index 6d295248c39a..c26dd6330efd 100644
--- a/LayoutTests/webarchive/loading/missing-data.html
+++ b/LayoutTests/webarchive/loading/missing-data.html
@@ -1,4 +1,4 @@
-<script>
+<script><!-- webkit-test-runner [ AlwaysAllowLocalWebarchive=true ] -->
     if (window.testRunner) {
         testRunner.waitUntilDone();
         testRunner.dumpAsText();
... (truncated)