#463: requestIdleCallback() implementation
This commit is contained in:
parent
b9bdadfef0
commit
1fe2d3921c
|
@ -51,13 +51,14 @@ IdleDeadline::TimeRemaining()
|
|||
}
|
||||
|
||||
RefPtr<nsPerformance> performance = mWindow->GetPerformance();
|
||||
if (!performance) {
|
||||
if (MOZ_UNLIKELY(!performance)) {
|
||||
// If there is no performance object the window is partially torn
|
||||
// down, so we can safely say that there is no time remaining.
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return std::max(mDeadline - performance->Now(), 0.0);
|
||||
DOMHighResTimeStamp remaining = std::max(mDeadline - performance->Now(), 0.0);
|
||||
return remaining;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -258,6 +258,27 @@ class nsIScriptTimeoutHandler;
|
|||
|
||||
#include "mozilla/dom/IdleDeadline.h" // issue 463
|
||||
|
||||
// Caches for Mach factor monitoring (TenFourFox issue 463).
|
||||
// These can be singletons since they're shared over the entire machine.
|
||||
// We are stricter with the minimum than with decode delay (issue 434),
|
||||
// which also uses Mach factor analysis to determine load, because most
|
||||
// calls will have a max timeout and thus the function is likely to run at
|
||||
// *some* point.
|
||||
static const int32_t MACH_FACTOR_MIN = 700;
|
||||
static const int32_t MACH_CHECK_INTERVAL = 2000;
|
||||
// This number is actually in the W3C standard, but we allow it to be
|
||||
// adjusted.
|
||||
static const int32_t CALLBACK_IDLE_INTERVAL = 50;
|
||||
static int32_t sMachFactorMin = MACH_FACTOR_MIN;
|
||||
static int32_t sIdleCallbackMachCheckInterval = MACH_CHECK_INTERVAL;
|
||||
static int32_t sIdleCallbackIdleInterval = CALLBACK_IDLE_INTERVAL;
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <mach/bootstrap.h>
|
||||
static processor_set_name_port_t sMachDefaultPset;
|
||||
static struct processor_set_load_info sMachLoadInfo;
|
||||
static host_name_port_t sMachHost;
|
||||
|
||||
static const char kStorageEnabled[] = "dom.storage.enabled";
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -1210,6 +1231,24 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
|||
Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled,
|
||||
"dom.idle-observers-api.fuzz_time.disabled",
|
||||
false);
|
||||
|
||||
// Precompute variables for Mach factor/load monitoring and idle callbacks
|
||||
// (TenFourFox issue 463).
|
||||
Preferences::AddIntVarCache(&sMachFactorMin,
|
||||
"tenfourfox.dom.requestIdleCallback.mach_factor_min",
|
||||
MACH_FACTOR_MIN);
|
||||
Preferences::AddIntVarCache(&sIdleCallbackMachCheckInterval,
|
||||
"tenfourfox.dom.requestIdleCallback.mach_check_interval",
|
||||
MACH_CHECK_INTERVAL);
|
||||
Preferences::AddIntVarCache(&sIdleCallbackIdleInterval,
|
||||
"tenfourfox.dom.requestIdleCallback.idle_interval",
|
||||
CALLBACK_IDLE_INTERVAL);
|
||||
sMachHost = mach_host_self();
|
||||
kern_return_t ret = processor_set_default(sMachHost, &sMachDefaultPset);
|
||||
if (ret != KERN_SUCCESS) {
|
||||
fprintf(stderr, "TenFourFox: Unable to initialize Mach idle monitoring: %i\n", (uint32_t)ret);
|
||||
sMachFactorMin = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (gDumpFile == nullptr) {
|
||||
|
@ -11544,8 +11583,15 @@ nsGlobalWindow::SetInterval(JSContext* aCx, const nsAString& aHandler,
|
|||
static bool
|
||||
SystemIsIdle()
|
||||
{
|
||||
// XXX: check Mach factor
|
||||
return false;
|
||||
if (!sMachDefaultPset || !sMachFactorMin) return true;
|
||||
|
||||
mach_msg_type_number_t count = PROCESSOR_SET_LOAD_INFO_COUNT;
|
||||
kern_return_t kr = processor_set_statistics(sMachDefaultPset,
|
||||
PROCESSOR_SET_LOAD_INFO,
|
||||
(processor_set_info_t)&sMachLoadInfo, &count);
|
||||
if (kr != KERN_SUCCESS) return true;
|
||||
|
||||
return (sMachLoadInfo.mach_factor > sMachFactorMin);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -11553,8 +11599,6 @@ nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
|
|||
int32_t interval,
|
||||
bool aIsInterval, int32_t *aReturn)
|
||||
{
|
||||
// XXX: see below about interval versus deadline. we abuse interval for
|
||||
// when to recheck if idle.
|
||||
return SetTimeoutOrIntervalOrIdleCallback(aHandler, interval, aIsInterval, aReturn, nullptr);
|
||||
}
|
||||
|
||||
|
@ -11586,14 +11630,28 @@ nsGlobalWindow::SetTimeoutOrIntervalOrIdleCallback(nsIScriptTimeoutHandler *aHan
|
|||
|
||||
RefPtr<nsTimeout> timeout = new nsTimeout();
|
||||
timeout->mIsInterval = aIsInterval;
|
||||
timeout->mDeadline = 0;
|
||||
timeout->mElapsed = 0;
|
||||
|
||||
// XXX: add to Interval a deadline field, and use the interval for when to check
|
||||
// if idle again. TenFourFox issue 463
|
||||
timeout->mInterval = interval;
|
||||
if (aCallback)
|
||||
if (aCallback) {
|
||||
// This is actually an idle callback. Populate the fields appropriately
|
||||
// to schedule the idle checks. TenFourFox issue 463.
|
||||
timeout->mCallback = aCallback;
|
||||
else
|
||||
timeout->mDeadline = interval;
|
||||
timeout->mScriptHandler = nullptr;
|
||||
|
||||
// If RequestIdleCallback says this is not an interval, then it must
|
||||
// want it to run right away. Otherwise schedule the idle checks.
|
||||
if (aIsInterval) {
|
||||
interval = sIdleCallbackMachCheckInterval;
|
||||
} else
|
||||
interval = 0;
|
||||
timeout->mInterval = interval;
|
||||
} else {
|
||||
timeout->mInterval = interval;
|
||||
timeout->mCallback = nullptr;
|
||||
timeout->mScriptHandler = aHandler;
|
||||
}
|
||||
|
||||
// Now clamp the actual interval we will use for the timer based on
|
||||
uint32_t nestingLevel = sNestingLevel + 1;
|
||||
|
@ -11785,13 +11843,25 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
|
|||
if (!timeout->mScriptHandler) {
|
||||
// Call the idle callback. TenFourFox issue 463.
|
||||
MOZ_ASSERT(timeout->mCallback);
|
||||
// Hold strong ref to ourselves (as below).
|
||||
nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
|
||||
|
||||
ErrorResult error;
|
||||
DOMHighResTimeStamp budget = 0.0;
|
||||
bool timedout = (bool)(timeout->mElapsed >= timeout->mDeadline);
|
||||
if (!timedout) {
|
||||
RefPtr<nsPerformance> performance = timeout->mWindow->GetPerformance();
|
||||
if (MOZ_LIKELY(performance)) {
|
||||
budget = performance->Now() + sIdleCallbackIdleInterval;
|
||||
} else {
|
||||
NS_WARNING("no performance object, timing out outstanding idle callback");
|
||||
timedout = true;
|
||||
}
|
||||
}
|
||||
RefPtr<IdleDeadline> deadline =
|
||||
new IdleDeadline(timeout->mWindow,
|
||||
/* didTimeout */ true,
|
||||
/* timeRemaining */ 0.0f);
|
||||
/* mDidTimeout */ timedout,
|
||||
/* mDeadline */ budget);
|
||||
timeout->mCallback->Call(*deadline, error, "requestIdleCallback handler");
|
||||
timeout->mCallback = nullptr;
|
||||
error.SuppressException();
|
||||
|
@ -12053,13 +12123,6 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
// The timeout is on the list to run at this depth, go ahead and
|
||||
// process it.
|
||||
|
||||
// If this timeout is an IdleCallback (TenFourFox issue 463), check
|
||||
// to see if we have hit the deadline. If so, run the timeout. If not,
|
||||
// check to see if we're idle. If so, run the timeout. If not, reschedule
|
||||
// to check again.
|
||||
//
|
||||
// -- NYI XXX --
|
||||
|
||||
// Get the script context (a strong ref to prevent it going away)
|
||||
// for this timeout and ensure the script language is enabled.
|
||||
nsCOMPtr<nsIScriptContext> scx = GetContextInternal();
|
||||
|
@ -12072,7 +12135,20 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
|
||||
// This timeout is good to run
|
||||
//++timeoutsRan;
|
||||
bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
|
||||
|
||||
bool timeout_was_cleared = false;
|
||||
bool rescheduleOk = true;
|
||||
if (timeout->mCallback) {
|
||||
// This is an idle callback (TenFourFox issue 463).
|
||||
// Check if we are idle, or if we will exceed the deadline, or
|
||||
// if we are expected to run immediately (mInterval == 0).
|
||||
timeout->mElapsed += timeout->mInterval;
|
||||
if (!timeout->mInterval || timeout->mElapsed >= timeout->mDeadline || SystemIsIdle()) {
|
||||
rescheduleOk = false;
|
||||
timeout_was_cleared = RunTimeoutHandler(timeout, scx);
|
||||
}
|
||||
} else
|
||||
timeout_was_cleared = RunTimeoutHandler(timeout, scx);
|
||||
|
||||
if (timeout_was_cleared) {
|
||||
// The running timeout's window was cleared, this means that
|
||||
|
@ -12089,7 +12165,9 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
|
|||
|
||||
// If we have a regular interval timer, we re-schedule the
|
||||
// timeout, accounting for clock drift.
|
||||
bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
|
||||
bool needsReinsertion = false;
|
||||
if (rescheduleOk)
|
||||
needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
|
||||
|
||||
// Running a timeout can cause another timeout to be deleted, so
|
||||
// we need to reset the pointer to the following timeout.
|
||||
|
@ -13926,33 +14004,41 @@ nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
|
|||
{
|
||||
MOZ_RELEASE_ASSERT(IsInnerWindow());
|
||||
AssertIsOnMainThread();
|
||||
int32_t handle = -1, timeout = 3600000; /* default 60 minutes */
|
||||
nsresult rv;
|
||||
|
||||
// uint32_t handle = ++mIdleRequestCallbackCounter;
|
||||
if (aOptions.mTimeout.WasPassed())
|
||||
timeout = aOptions.mTimeout.Value();
|
||||
|
||||
fprintf(stderr, "::RequestIdleCallback() is not yet implemented\n");
|
||||
if (SystemIsIdle()) {
|
||||
// The computer is already idle, so we will run the callback now.
|
||||
rv = SetTimeoutOrIntervalOrIdleCallback(nullptr, timeout, false, &handle,
|
||||
&aCallback);
|
||||
} else {
|
||||
fprintf(stderr, "TenFourFox: A request for idle callback is being deferred.\n");
|
||||
rv = SetTimeoutOrIntervalOrIdleCallback(nullptr, timeout, true, &handle,
|
||||
&aCallback);
|
||||
}
|
||||
if (NS_SUCCEEDED(rv))
|
||||
return handle;
|
||||
|
||||
// Plan:
|
||||
// Check if idle now. If so, set a timeout of zero so it runs right away.
|
||||
// Else set the deadline, if provided, and set an interval to recheck idle.
|
||||
// See SystemIsIdle()
|
||||
#if DEBUG
|
||||
MOZ_ASSERT(0);
|
||||
#endif
|
||||
return 0; // handle;
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsInnerWindow());
|
||||
ErrorResult ignored;
|
||||
|
||||
fprintf(stderr, "::CancelIdleCallback() is not yet implemented\n");
|
||||
// XXX: As written, this is just an alias for clearTimeout(), so
|
||||
// web scripts could call either to clear any time of timeout
|
||||
// or interval. Do we care?
|
||||
|
||||
// Plan:
|
||||
// Check if this is a timeout with a Callback. If not, fail.
|
||||
// If so, hand this to RemoveTimeout.
|
||||
#if DEBUG
|
||||
MOZ_ASSERT(0);
|
||||
#endif
|
||||
if (aHandle > 0) {
|
||||
ClearTimeoutOrInterval(aHandle, ignored);
|
||||
ignored.SuppressException();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue