Bug 1114345 - Don't pop up slow script dialog after waking up laptop (r=bholley)

This commit is contained in:
Bill McCloskey 2015-01-21 16:13:21 -08:00
parent 7843b2875a
commit 67a97e37f5
2 changed files with 53 additions and 7 deletions

View File

@ -1307,7 +1307,18 @@ WatchdogMain(void *arg)
// Don't request an interrupt callback unless the current script has
// been running long enough that we might show the slow script dialog.
// Triggering the callback from off the main thread can be expensive.
PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC;
// We want to avoid showing the slow script dialog if the user's laptop
// goes to sleep in the middle of running a script. To ensure this, we
// invoke the interrupt callback after only half the timeout has
// elapsed. The callback simply records the fact that it was called in
// the mSlowScriptSecondHalf flag. Then we wait another (timeout/2)
// seconds and invoke the callback again. This time around it sees
// mSlowScriptSecondHalf is set and so it shows the slow script
// dialog. If the computer is put to sleep during one of the (timeout/2)
// periods, the script still has the other (timeout/2) seconds to
// finish.
PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2;
if (manager->IsRuntimeActive() &&
manager->TimeSinceLastRuntimeStateChange() >= usecs)
{
@ -1378,10 +1389,12 @@ XPCJSRuntime::InterruptCallback(JSContext *cx)
{
XPCJSRuntime *self = XPCJSRuntime::Get();
// If this is the first time the interrupt callback has fired since we last
// returned to the event loop, mark the checkpoint.
// Normally we record mSlowScriptCheckpoint when we start to process an
// event. However, we can run JS outside of event handlers. This code takes
// care of that case.
if (self->mSlowScriptCheckpoint.IsNull()) {
self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
self->mSlowScriptSecondHalf = false;
return true;
}
@ -1403,9 +1416,18 @@ XPCJSRuntime::InterruptCallback(JSContext *cx)
int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10);
// If there's no limit, or we're within the limit, let it go.
if (limit == 0 || duration.ToSeconds() < limit)
if (limit == 0 || duration.ToSeconds() < limit / 2.0)
return true;
// In order to guard against time changes or laptops going to sleep, we
// don't trigger the slow script warning until (limit/2) seconds have
// elapsed twice.
if (!self->mSlowScriptSecondHalf) {
self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
self->mSlowScriptSecondHalf = true;
return true;
}
//
// This has gone on long enough! Time to take action. ;-)
//
@ -3197,7 +3219,8 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
mUnprivilegedJunkScope(this->Runtime(), nullptr),
mPrivilegedJunkScope(this->Runtime(), nullptr),
mCompilationScope(this->Runtime(), nullptr),
mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite())
mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()),
mSlowScriptSecondHalf(false)
{
// these jsids filled in later when we have a JSContext to work with.
mStrIDs[0] = JSID_VOID;

View File

@ -624,8 +624,14 @@ public:
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
void OnProcessNextEvent() { mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes(); }
void OnAfterProcessNextEvent() { mSlowScriptCheckpoint = mozilla::TimeStamp(); }
void OnProcessNextEvent() {
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
mSlowScriptSecondHalf = false;
}
void OnAfterProcessNextEvent() {
mSlowScriptCheckpoint = mozilla::TimeStamp();
mSlowScriptSecondHalf = false;
}
nsTArray<nsXPCWrappedJS*>& WrappedJSToReleaseArray() { return mWrappedJSToReleaseArray; }
@ -669,6 +675,23 @@ private:
JS::PersistentRootedObject mCompilationScope;
nsRefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
// If we spend too much time running JS code in an event handler, then we
// want to show the slow script UI. The timeout T is controlled by prefs. We
// invoke the interrupt callback once after T/2 seconds and set
// mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the
// interrupt callback again. Since mSlowScriptSecondHalf is now true, it
// shows the slow script UI. The reason we invoke the callback twice is to
// ensure that putting the computer to sleep while running a script doesn't
// cause the UI to be shown. If the laptop goes to sleep during one of the
// timeout periods, the script still has the other T/2 seconds to complete
// before the slow script UI is shown.
bool mSlowScriptSecondHalf;
// mSlowScriptCheckpoint is set to the time when:
// 1. We started processing the current event, or
// 2. mSlowScriptSecondHalf was set to true
// (whichever comes later). We use it to determine whether the interrupt
// callback needs to do anything.
mozilla::TimeStamp mSlowScriptCheckpoint;
friend class Watchdog;