mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1534012 - Use a low priority ThrottledEventQueue for postMessages during page load r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D27386 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
c729145871
commit
87884c39a6
@ -298,6 +298,45 @@ bool TabGroup::IsBackground() const {
|
||||
return mForegroundCount == 0;
|
||||
}
|
||||
|
||||
nsresult TabGroup::QueuePostMessageEvent(
|
||||
already_AddRefed<nsIRunnable>&& aRunnable) {
|
||||
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
|
||||
if (!mPostMessageEventQueue) {
|
||||
nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
|
||||
mPostMessageEventQueue = ThrottledEventQueue::Create(
|
||||
target, nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS);
|
||||
nsresult rv = mPostMessageEventQueue->SetIsPaused(false);
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
}
|
||||
|
||||
// Ensure the queue is enabled
|
||||
if (mPostMessageEventQueue->IsPaused()) {
|
||||
nsresult rv = mPostMessageEventQueue->SetIsPaused(false);
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
}
|
||||
|
||||
if (mPostMessageEventQueue) {
|
||||
mPostMessageEventQueue->Dispatch(std::move(aRunnable),
|
||||
NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void TabGroup::FlushPostMessageEvents() {
|
||||
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
|
||||
if (mPostMessageEventQueue) {
|
||||
nsresult rv = mPostMessageEventQueue->SetIsPaused(true);
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
while ((event = mPostMessageEventQueue->GetEvent())) {
|
||||
Dispatch(TaskCategory::Other, event.forget());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TabGroup::Count(bool aActiveOnly) const {
|
||||
if (!aActiveOnly) {
|
||||
return mDocGroups.Count();
|
||||
|
@ -23,6 +23,7 @@ class nsPIDOMWindowOuter;
|
||||
|
||||
namespace mozilla {
|
||||
class AbstractThread;
|
||||
class ThrottledEventQueue;
|
||||
namespace dom {
|
||||
class Document;
|
||||
class BrowserChild;
|
||||
@ -143,6 +144,10 @@ class TabGroup final : public SchedulerGroup,
|
||||
// can be throttled.
|
||||
static bool HasOnlyThrottableTabs();
|
||||
|
||||
nsresult QueuePostMessageEvent(already_AddRefed<nsIRunnable>&& aRunnable);
|
||||
|
||||
void FlushPostMessageEvents();
|
||||
|
||||
private:
|
||||
virtual AbstractThread* AbstractMainThreadForImpl(
|
||||
TaskCategory aCategory) override;
|
||||
@ -166,6 +171,10 @@ class TabGroup final : public SchedulerGroup,
|
||||
uint32_t mForegroundCount;
|
||||
|
||||
static LinkedList<TabGroup>* sTabGroups;
|
||||
|
||||
// A queue to store postMessage events during page load, the queue will be
|
||||
// flushed once the page is loaded
|
||||
RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -2482,6 +2482,18 @@ void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
|
||||
}
|
||||
|
||||
void nsPIDOMWindowInner::SetActiveLoadingState(bool aIsLoading) {
|
||||
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
|
||||
if (!aIsLoading) {
|
||||
Document* doc = GetExtantDoc();
|
||||
if (doc) {
|
||||
if (doc->IsTopLevelContentDocument()) {
|
||||
mozilla::dom::TabGroup* tabGroup = doc->GetDocGroup()->GetTabGroup();
|
||||
tabGroup->FlushPostMessageEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
|
||||
mTimeoutManager->SetLoading(aIsLoading);
|
||||
}
|
||||
|
@ -6057,6 +6057,19 @@ void nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx,
|
||||
return;
|
||||
}
|
||||
|
||||
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
|
||||
if (mDoc) {
|
||||
Document* doc = mDoc->GetTopLevelContentDocument();
|
||||
if (doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
|
||||
// As long as the top level is loading, we can dispatch events to the
|
||||
// queue because the queue will be flushed eventually
|
||||
mozilla::dom::TabGroup* tabGroup = TabGroup();
|
||||
aError = tabGroup->QueuePostMessageEvent(event.forget());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aError = Dispatch(TaskCategory::Other, event.forget());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
<script>
|
||||
|
||||
let runnable1 = {
|
||||
run() {
|
||||
window.opener.callOrder.push("Runnable1");
|
||||
}
|
||||
}
|
||||
|
||||
let runnable2 = {
|
||||
run() {
|
||||
window.opener.callOrder.push("Runnable2");
|
||||
}
|
||||
}
|
||||
|
||||
let runnable3 = {
|
||||
run() {
|
||||
window.opener.callOrder.push("Runnable3");
|
||||
}
|
||||
}
|
||||
|
||||
window.onmessage = function () {
|
||||
window.opener.callOrder.push("PostMessage");
|
||||
if (window.loadCount == 1) {
|
||||
window.loadCount += 1;
|
||||
location.reload();
|
||||
} else {
|
||||
window.opener.onDone();
|
||||
}
|
||||
};
|
||||
|
||||
// Pushed to normal queue
|
||||
SpecialPowers.Services.tm.dispatchToMainThread(runnable1);
|
||||
// Pushed to idle queue
|
||||
window.postMessage("bar", "*");
|
||||
// Pushed to normal queue
|
||||
SpecialPowers.Services.tm.dispatchToMainThread(runnable2);
|
||||
// Pushed to normal queue
|
||||
SpecialPowers.Services.tm.dispatchToMainThread(runnable3);
|
||||
</script>
|
@ -3,6 +3,7 @@ support-files =
|
||||
child_ip_address.html
|
||||
file_crossdomainprops_inner.html
|
||||
file_location.html
|
||||
file_separate_post_message_queue.html
|
||||
framed_location.html
|
||||
idn_child.html
|
||||
innerWidthHeight_script.html
|
||||
@ -19,3 +20,4 @@ support-files =
|
||||
[test_location_setters.html]
|
||||
[test_setting_document.domain_idn.html]
|
||||
[test_setting_document.domain_to_shortened_ipaddr.html]
|
||||
[test_separate_post_message_queue.html]
|
||||
|
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for using separate event queue for post messages during page load behaviors</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var tab;
|
||||
var callOrder = [];
|
||||
function onDone() {
|
||||
tab.close();
|
||||
isDeeply(callOrder, ["Runnable1", "Runnable2", "Runnable3", "PostMessage",
|
||||
"Runnable1", "Runnable2", "Runnable3", "PostMessage"], "Runnables should be fired prior to PostMessage");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set":[["dom.separate_event_queue_for_post_message.enabled", true]]}, function () {
|
||||
tab = window.open('file_separate_post_message_queue.html');
|
||||
tab.loadCount = 1;
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -22,6 +22,7 @@
|
||||
#include "mozilla/dom/PopupBlocker.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/dom/DocGroup.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
|
@ -726,6 +726,12 @@ VARCACHE_PREF(
|
||||
bool, false
|
||||
)
|
||||
|
||||
VARCACHE_PREF(
|
||||
"dom.separate_event_queue_for_post_message.enabled",
|
||||
dom_separate_event_queue_for_post_message_enabled,
|
||||
bool, true
|
||||
)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Extension prefs
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1,4 +1,5 @@
|
||||
[sandboxed-iframe.html]
|
||||
prefs: [dom.separate_event_queue_for_post_message.enabled:false] # Bug in WPT https://github.com/web-platform-tests/wpt/issues/16540
|
||||
disabled:
|
||||
if (os == "mac"): https://bugzilla.mozilla.org/show_bug.cgi?id=1433190
|
||||
[Blob URL parses correctly]
|
||||
|
@ -128,7 +128,7 @@ function assert_iframe_with_csp(t, url, csp, shouldBlock, urlId, blockedURI) {
|
||||
// Delay the check until after the postMessage has a chance to execute.
|
||||
setTimeout(t.step_func_done(function () {
|
||||
assert_equals(loaded[urlId], undefined);
|
||||
}), 1);
|
||||
}), 500);
|
||||
assert_throws("SecurityError", () => {
|
||||
var x = i.contentWindow.location.href;
|
||||
});
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
|
@ -89,6 +89,10 @@ bool ThreadEventQueue<InnerQueueT>::PutEventInternal(
|
||||
aPriority = EventQueuePriority::Input;
|
||||
} else if (prio == nsIRunnablePriority::PRIORITY_MEDIUMHIGH) {
|
||||
aPriority = EventQueuePriority::MediumHigh;
|
||||
} else if (prio == nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS) {
|
||||
aPriority = EventQueuePriority::DeferredTimers;
|
||||
} else if (prio == nsIRunnablePriority::PRIORITY_IDLE) {
|
||||
aPriority = EventQueuePriority::Idle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,11 +173,14 @@ class ThrottledEventQueue::Inner final : public nsISupports {
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
// We only check the name of an executor runnable when we know there is
|
||||
// something in the queue, so this should never fail.
|
||||
event = mEventQueue.PeekEvent(lock);
|
||||
MOZ_ALWAYS_TRUE(event);
|
||||
// It is possible that mEventQueue wasn't empty when the executor
|
||||
// was added to the queue, but someone processed events from mEventQueue
|
||||
// before the executor, this is why mEventQueue is empty here
|
||||
if (!event) {
|
||||
aName.AssignLiteral("no runnables left in the ThrottledEventQueue");
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (nsCOMPtr<nsINamed> named = do_QueryInterface(event)) {
|
||||
@ -265,6 +268,11 @@ class ThrottledEventQueue::Inner final : public nsISupports {
|
||||
return mEventQueue.Count(lock);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIRunnable> GetEvent() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mEventQueue.GetEvent(nullptr, lock);
|
||||
}
|
||||
|
||||
void AwaitIdle() const {
|
||||
// Any thread, except the main thread or our base target. Blocking the
|
||||
// main thread is forbidden. Blocking the base target is guaranteed to
|
||||
@ -372,6 +380,11 @@ bool ThrottledEventQueue::IsEmpty() const { return mInner->IsEmpty(); }
|
||||
|
||||
uint32_t ThrottledEventQueue::Length() const { return mInner->Length(); }
|
||||
|
||||
// Get the next runnable from the queue
|
||||
already_AddRefed<nsIRunnable> ThrottledEventQueue::GetEvent() {
|
||||
return mInner->GetEvent();
|
||||
}
|
||||
|
||||
void ThrottledEventQueue::AwaitIdle() const { return mInner->AwaitIdle(); }
|
||||
|
||||
nsresult ThrottledEventQueue::SetIsPaused(bool aIsPaused) {
|
||||
|
@ -77,6 +77,8 @@ class ThrottledEventQueue final : public nsISerialEventTarget {
|
||||
// Determine how many events are pending in the queue.
|
||||
uint32_t Length() const;
|
||||
|
||||
already_AddRefed<nsIRunnable> GetEvent();
|
||||
|
||||
// Block the current thread until the queue is empty. This may not be called
|
||||
// on the main thread or the base target. The ThrottledEventQueue must not be
|
||||
// paused.
|
||||
|
@ -25,5 +25,7 @@ interface nsIRunnablePriority : nsISupports
|
||||
const unsigned short PRIORITY_INPUT = 1;
|
||||
const unsigned short PRIORITY_HIGH = 2;
|
||||
const unsigned short PRIORITY_MEDIUMHIGH = 3;
|
||||
const unsigned short PRIORITY_IDLE = 4;
|
||||
const unsigned short PRIORITY_DEFERRED_TIMERS = 5;
|
||||
readonly attribute unsigned long priority;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user