Commit 1793056a authored by kjd564's avatar kjd564 Committed by Commit Bot

Prevent sync XHR from being fired in a child frame when the unload handler was...

Prevent sync XHR from being fired in a child frame when the unload handler was attached from a parent document.

As of M80, sync XHR during page dismissal is not allowed by default. However, it was still possible to send a sync XHR request during dismissal by attaching an unload handler defined in the parent document to a child frame from the parent's script. This change prevents that from happening. (Note: It is still possible to schedule a sync XHR during a page dismissal, ie doing a SetTimeout in a beforeUnload handler that will do the synchronous XHR.)

This change also removes some use counters that are no longer needed.

Bug: 1044348
Change-Id: Ieab8fd018d61aecc75241c6d7d01ad6c08eff7da
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2032426
Commit-Queue: Katie Dillon <kdillon@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarHiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#742684}
parent 55e58cf6
...@@ -1581,8 +1581,6 @@ enum WebFeature { ...@@ -1581,8 +1581,6 @@ enum WebFeature {
kSuppressHistoryEntryWithoutUserGesture = 2146, kSuppressHistoryEntryWithoutUserGesture = 2146,
kPerformanceServerTiming = 2157, kPerformanceServerTiming = 2157,
kFileReaderResultBeforeCompletion = 2158, kFileReaderResultBeforeCompletion = 2158,
kSyncXhrInPageDismissal = 2159,
kAsyncXhrInPageDismissal = 2160,
kAnimationSetPlaybackRateCompensatorySeek = 2162, kAnimationSetPlaybackRateCompensatorySeek = 2162,
kDeepCombinatorInStaticProfile = 2163, kDeepCombinatorInStaticProfile = 2163,
kPseudoShadowInStaticProfile = 2164, kPseudoShadowInStaticProfile = 2164,
...@@ -2095,7 +2093,6 @@ enum WebFeature { ...@@ -2095,7 +2093,6 @@ enum WebFeature {
kV8HTMLMediaElement_CaptureStream_Method = 2729, kV8HTMLMediaElement_CaptureStream_Method = 2729,
kQuirkyLineBoxBackgroundSize = 2730, kQuirkyLineBoxBackgroundSize = 2730,
kDirectlyCompositedImage = 2731, kDirectlyCompositedImage = 2731,
kForbiddenSyncXhrInPageDismissal = 2732,
kV8HTMLVideoElement_AutoPictureInPicture_AttributeGetter = 2733, kV8HTMLVideoElement_AutoPictureInPicture_AttributeGetter = 2733,
kV8HTMLVideoElement_AutoPictureInPicture_AttributeSetter = 2734, kV8HTMLVideoElement_AutoPictureInPicture_AttributeSetter = 2734,
kAutoPictureInPictureAttribute = 2735, kAutoPictureInPictureAttribute = 2735,
......
...@@ -285,6 +285,7 @@ ...@@ -285,6 +285,7 @@
#include "third_party/blink/renderer/core/trustedtypes/trusted_html.h" #include "third_party/blink/renderer/core/trustedtypes/trusted_html.h"
#include "third_party/blink/renderer/core/xml/parser/xml_document_parser.h" #include "third_party/blink/renderer/core/xml/parser/xml_document_parser.h"
#include "third_party/blink/renderer/core/xml_names.h" #include "third_party/blink/renderer/core/xml_names.h"
#include "third_party/blink/renderer/core/xmlhttprequest/main_thread_disallow_synchronous_xhr_scope.h"
#include "third_party/blink/renderer/core/xmlns_names.h" #include "third_party/blink/renderer/core/xmlns_names.h"
#include "third_party/blink/renderer/platform/bindings/dom_data_store.h" #include "third_party/blink/renderer/platform/bindings/dom_data_store.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h" #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
...@@ -3999,6 +4000,7 @@ bool Document::DispatchBeforeUnloadEvent(ChromeClient* chrome_client, ...@@ -3999,6 +4000,7 @@ bool Document::DispatchBeforeUnloadEvent(ChromeClient* chrome_client,
if (ProcessingBeforeUnload()) if (ProcessingBeforeUnload())
return false; return false;
MainThreadDisallowSynchronousXHRScope disallow_synchronous_xhr;
auto& before_unload_event = *MakeGarbageCollected<BeforeUnloadEvent>(); auto& before_unload_event = *MakeGarbageCollected<BeforeUnloadEvent>();
before_unload_event.initEvent(event_type_names::kBeforeunload, false, true); before_unload_event.initEvent(event_type_names::kBeforeunload, false, true);
load_event_progress_ = kBeforeUnloadEventInProgress; load_event_progress_ = kBeforeUnloadEventInProgress;
...@@ -4082,6 +4084,7 @@ void Document::DispatchUnloadEvents( ...@@ -4082,6 +4084,7 @@ void Document::DispatchUnloadEvents(
SecurityOrigin* committing_origin, SecurityOrigin* committing_origin,
base::Optional<Document::UnloadEventTiming>* unload_timing) { base::Optional<Document::UnloadEventTiming>* unload_timing) {
PluginScriptForbiddenScope forbid_plugin_destructor_scripting; PluginScriptForbiddenScope forbid_plugin_destructor_scripting;
MainThreadDisallowSynchronousXHRScope disallow_synchronous_xhr;
if (parser_) if (parser_)
parser_->StopParsing(); parser_->StopParsing();
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "third_party/blink/renderer/core/page/focus_controller.h" #include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/probe/core_probes.h" #include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/core/xmlhttprequest/main_thread_disallow_synchronous_xhr_scope.h"
#include "third_party/blink/renderer/platform/instrumentation/instance_counters.h" #include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
...@@ -77,6 +78,7 @@ void Frame::Detach(FrameDetachType type) { ...@@ -77,6 +78,7 @@ void Frame::Detach(FrameDetachType type) {
// Detach() can be re-entered, so this can't simply DCHECK(IsAttached()). // Detach() can be re-entered, so this can't simply DCHECK(IsAttached()).
DCHECK(!IsDetached()); DCHECK(!IsDetached());
lifecycle_.AdvanceTo(FrameLifecycle::kDetaching); lifecycle_.AdvanceTo(FrameLifecycle::kDetaching);
MainThreadDisallowSynchronousXHRScope disallow_synchronous_xhr;
DetachImpl(type); DetachImpl(type);
......
...@@ -6,6 +6,8 @@ import("//third_party/blink/renderer/core/core.gni") ...@@ -6,6 +6,8 @@ import("//third_party/blink/renderer/core/core.gni")
blink_core_sources("xmlhttprequest") { blink_core_sources("xmlhttprequest") {
sources = [ sources = [
"main_thread_disallow_synchronous_xhr_scope.cc",
"main_thread_disallow_synchronous_xhr_scope.h",
"xml_http_request.cc", "xml_http_request.cc",
"xml_http_request.h", "xml_http_request.h",
"xml_http_request_event_target.h", "xml_http_request_event_target.h",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/xmlhttprequest/main_thread_disallow_synchronous_xhr_scope.h"
#include "base/logging.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"
namespace blink {
static unsigned g_disallow_synchronous_xhr_count = 0;
MainThreadDisallowSynchronousXHRScope::MainThreadDisallowSynchronousXHRScope() {
DCHECK(IsMainThread());
++g_disallow_synchronous_xhr_count;
}
MainThreadDisallowSynchronousXHRScope::
~MainThreadDisallowSynchronousXHRScope() {
DCHECK(IsMainThread());
--g_disallow_synchronous_xhr_count;
}
bool MainThreadDisallowSynchronousXHRScope::ShouldDisallowSynchronousXHR() {
DCHECK(IsMainThread());
return g_disallow_synchronous_xhr_count > 0;
}
} // namespace blink
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_XMLHTTPREQUEST_MAIN_THREAD_DISALLOW_SYNCHRONOUS_XHR_SCOPE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_XMLHTTPREQUEST_MAIN_THREAD_DISALLOW_SYNCHRONOUS_XHR_SCOPE_H_
#include "base/macros.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
namespace blink {
class CORE_EXPORT MainThreadDisallowSynchronousXHRScope final {
STACK_ALLOCATED();
public:
MainThreadDisallowSynchronousXHRScope();
~MainThreadDisallowSynchronousXHRScope();
static bool ShouldDisallowSynchronousXHR();
DISALLOW_COPY_AND_ASSIGN(MainThreadDisallowSynchronousXHRScope);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_XMLHTTPREQUEST_MAIN_THREAD_DISALLOW_SYNCHRONOUS_XHR_SCOPE_H_
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h" #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
#include "third_party/blink/renderer/core/url/url_search_params.h" #include "third_party/blink/renderer/core/url/url_search_params.h"
#include "third_party/blink/renderer/core/xmlhttprequest/main_thread_disallow_synchronous_xhr_scope.h"
#include "third_party/blink/renderer/core/xmlhttprequest/xml_http_request_upload.h" #include "third_party/blink/renderer/core/xmlhttprequest/xml_http_request_upload.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h"
...@@ -1100,18 +1101,6 @@ void XMLHttpRequest::CreateRequest(scoped_refptr<EncodedFormData> http_body, ...@@ -1100,18 +1101,6 @@ void XMLHttpRequest::CreateRequest(scoped_refptr<EncodedFormData> http_body,
if (async_) { if (async_) {
UseCounter::Count(&execution_context, UseCounter::Count(&execution_context,
WebFeature::kXMLHttpRequestAsynchronous); WebFeature::kXMLHttpRequestAsynchronous);
if (execution_context.IsDocument()) {
// Update histogram for usage of async xhr within pagedismissal.
auto pagedismissal = GetDocument()->PageDismissalEventBeingDispatched();
if (pagedismissal != Document::kNoDismissal) {
UseCounter::Count(&execution_context,
WebFeature::kAsyncXhrInPageDismissal);
DEFINE_STATIC_LOCAL(EnumerationHistogram,
asyncxhr_pagedismissal_histogram,
("XHR.Async.PageDismissal", 5));
asyncxhr_pagedismissal_histogram.Count(pagedismissal);
}
}
if (upload_) if (upload_)
request.SetReportUploadProgress(true); request.SetReportUploadProgress(true);
...@@ -1136,33 +1125,16 @@ void XMLHttpRequest::CreateRequest(scoped_refptr<EncodedFormData> http_body, ...@@ -1136,33 +1125,16 @@ void XMLHttpRequest::CreateRequest(scoped_refptr<EncodedFormData> http_body,
WebFeature::kXMLHttpRequestSynchronousInSameOriginSubframe); WebFeature::kXMLHttpRequestSynchronousInSameOriginSubframe);
} }
} }
// Update histogram for usage of sync xhr within pagedismissal. if (MainThreadDisallowSynchronousXHRScope::
auto pagedismissal = GetDocument()->PageDismissalEventBeingDispatched(); ShouldDisallowSynchronousXHR() &&
if (pagedismissal != Document::kNoDismissal) { !RuntimeEnabledFeatures::AllowSyncXHRInPageDismissalEnabled(
// Disallow synchronous requests on page dismissal unless enabled by &execution_context)) {
// origin trial or enterprise policy. HandleNetworkError();
if (!RuntimeEnabledFeatures::AllowSyncXHRInPageDismissalEnabled( ThrowForLoadFailureIfNeeded(exception_state,
&execution_context)) { "Synchronous XHR in page dismissal. See "
UseCounter::Count(&execution_context, "https://www.chromestatus.com/feature/"
WebFeature::kForbiddenSyncXhrInPageDismissal); "4664843055398912 for more details.");
DEFINE_STATIC_LOCAL(EnumerationHistogram, return;
forbidden_syncxhr_pagedismissal_histogram,
("XHR.Sync.PageDismissal_forbidden", 5));
forbidden_syncxhr_pagedismissal_histogram.Count(pagedismissal);
HandleNetworkError();
ThrowForLoadFailureIfNeeded(exception_state,
"Synchronous XHR in page dismissal. See "
"https://www.chromestatus.com/feature/"
"4664843055398912 for more details.");
return;
} else {
UseCounter::Count(&execution_context,
WebFeature::kSyncXhrInPageDismissal);
DEFINE_STATIC_LOCAL(EnumerationHistogram,
syncxhr_pagedismissal_histogram,
("XHR.Sync.PageDismissal", 5));
syncxhr_pagedismissal_histogram.Count(pagedismissal);
}
} }
} else { } else {
DCHECK(execution_context.IsWorkerGlobalScope()); DCHECK(execution_context.IsWorkerGlobalScope());
......
...@@ -147,7 +147,6 @@ ...@@ -147,7 +147,6 @@
{ {
name: "AllowSyncXHRInPageDismissal", name: "AllowSyncXHRInPageDismissal",
origin_trial_feature_name: "AllowSyncXHRInPageDismissal", origin_trial_feature_name: "AllowSyncXHRInPageDismissal",
status: "experimental",
}, },
{ {
name: "AnimationWorklet", name: "AnimationWorklet",
......
<html> <html>
<head> <head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script> <script>
window.onunload = function () {
try { window.onunload = parent.t.step_func(() => {
var url = '1251.html'; try {
var xhr = new XMLHttpRequest(); console.log("trying the synx xhr");
xhr.open('GET', url, false); var xhr = new XMLHttpRequest();
xhr.send(null); xhr.open("GET", "resources/1251.html", false);
window.parent.completed("PASS: sync XHR completed successfully"); xhr.send(null);
} catch (e) { assert_unreached('Sync XHR must throw during an unload event');
window.parent.completed("FAIL: sync XHR during unload failed: " + e.message); } catch (e) {
} parent.t.done();
}; }
window.onload = function () { });
window.parent.subframeLoaded();
};
</script> </script>
</head> </head>
<body> <body>
......
CONSOLE WARNING: line 8: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
Test behavior of sync XHR during unload
PASS: sync XHR completed successfully
<html>
<body>
<p>Test behavior of sync XHR during unload when the unload handler is attached
to a child iframe from the parent (crbug.com/1044348).</p>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
var t = async_test();
onload = () => {
t.step_timeout(() => {
var iframe = document.getElementById("ifr");
iframe.contentWindow.onunload = t.step_func(() => {
try {
console.log("trying the synx xhr");
var xhr = new XMLHttpRequest();
xhr.open("GET", "resources/1251.html", false);
xhr.send(null);
assert_unreached('Sync XHR must throw during an unload event');
} catch (e) {
t.done();
}
});
iframe.src = "about:blank";
}, 1000);
};
</script>
<iframe id="ifr" name="ifr" src="resources/test.html"></iframe>
</body>
</html>
<html> <html>
<body> <body>
<p>Test behavior of sync XHR during unload</p> <p>Test behavior of sync XHR during unload</p>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="ifr" name="ifr" src="resources/xmlhttprequest-in-unload-sync.html"></iframe>
<script> <script>
var consoleMessages = document.createElement("ul"); var t = async_test();
document.body.appendChild(consoleMessages);
if (window.testRunner) {
testRunner.waitUntilDone();
testRunner.dumpAsText();
}
function subframeLoaded() t.step_timeout(() => {
{ var iframe = document.getElementById("ifr");
document.getElementById('ifr').contentWindow.location.assign("data:text/html,DONE"); iframe.src = "about:blank";
} }, 1000);
function completed(msg)
{
log(msg);
if (window.testRunner)
testRunner.notifyDone();
}
function log(message)
{
var item = document.createElement("li");
item.appendChild(document.createTextNode(message));
consoleMessages.appendChild(item);
}
</script> </script>
<div id="framediv">
<iframe id="ifr" name="ifr" src="resources/xmlhttprequest-in-unload-sync.html"></iframe>
</div>
</body> </body>
</html> </html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment