Prevent synchronous XHR in beforeunload / unload event handlers
https://bugs.webkit.org/show_bug.cgi?id=204912
<rdar://problem/57676394>
Reviewed by Darin Adler.
Source/WebCore:
Prevent synchronous XHR in beforeunload / unload event handlers. They are terrible for performance
and the Beacon API (or Fetch keepalive) are more efficient & supported alternatives.
In particular, this would cause hangs when trying to navigate away from a site or when closing
attempt, which would result in terrible user experience.
Chrome and Edge have expressed public support for this. Chrome has actually been testing this behavior
for a while now:
https://www.chromestatus.com/feature/4664843055398912
I added this new behavior behind an experimental feature flag, enabled by default.
Tests: http/tests/xmlhttprequest/sync-xhr-in-beforeunload.html
http/tests/xmlhttprequest/sync-xhr-in-unload.html
* loader/DocumentThreadableLoader.cpp:
(WebCore::DocumentThreadableLoader::DocumentThreadableLoader):
* loader/FrameLoader.cpp:
(WebCore::PageLevelForbidScope::PageLevelForbidScope):
(WebCore::ForbidPromptsScope::ForbidPromptsScope):
(WebCore::ForbidPromptsScope::~ForbidPromptsScope):
(WebCore::ForbidSynchronousLoadsScope::ForbidSynchronousLoadsScope):
(WebCore::ForbidSynchronousLoadsScope::~ForbidSynchronousLoadsScope):
(WebCore::FrameLoader::dispatchUnloadEvents):
(WebCore::FrameLoader::dispatchBeforeUnloadEvent):
* page/Page.cpp:
(WebCore::Page::forbidSynchronousLoads):
(WebCore::Page::allowSynchronousLoads):
(WebCore::Page::areSynchronousLoadsAllowed):
* page/Page.h:
LayoutTests:
Add layout test coverage.
* http/tests/xmlhttprequest/resources/sync-xhr-in-beforeunload-window.html: Added.
* http/tests/xmlhttprequest/resources/sync-xhr-in-unload-window.html: Added.
* http/tests/xmlhttprequest/sync-xhr-in-beforeunload-expected.txt: Added.
* http/tests/xmlhttprequest/sync-xhr-in-beforeunload.html: Added.
* http/tests/xmlhttprequest/sync-xhr-in-unload-expected.txt: Added.
* http/tests/xmlhttprequest/sync-xhr-in-unload.html: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@253213 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/loader/DocumentThreadableLoader.cpp b/Source/WebCore/loader/DocumentThreadableLoader.cpp
index f5e5515..3d26893 100644
--- a/Source/WebCore/loader/DocumentThreadableLoader.cpp
+++ b/Source/WebCore/loader/DocumentThreadableLoader.cpp
@@ -55,6 +55,7 @@
#include "RuntimeApplicationChecks.h"
#include "RuntimeEnabledFeatures.h"
#include "SecurityOrigin.h"
+#include "Settings.h"
#include "SharedBuffer.h"
#include "SubresourceIntegrity.h"
#include "SubresourceLoader.h"
@@ -131,6 +132,11 @@
// Setting a referrer header is only supported in the async code path.
ASSERT(m_async || m_referrer.isEmpty());
+ if (document.settings().disallowSyncXHRDuringPageDismissalEnabled() && !m_async && (!document.page() || !document.page()->areSynchronousLoadsAllowed())) {
+ logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, request.url(), "Synchronous loads are not allowed at this time"));
+ return;
+ }
+
// Referrer and Origin headers should be set after the preflight if any.
ASSERT(!request.hasHTTPReferrer() && !request.hasHTTPOrigin());