mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1166452 - Add DelayedInit module for Fennec startup; r=mfinkle
This commit is contained in:
parent
be9c2c478e
commit
2544d1c7a0
175
mobile/android/modules/DelayedInit.jsm
Normal file
175
mobile/android/modules/DelayedInit.jsm
Normal file
@ -0,0 +1,175 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict"
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["DelayedInit"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "MessageLoop",
|
||||
"@mozilla.org/message-loop;1",
|
||||
"nsIMessageLoop");
|
||||
|
||||
/**
|
||||
* Use DelayedInit to schedule initializers to run some time after startup.
|
||||
* Initializers are added to a list of pending inits. Whenever the main thread
|
||||
* message loop is idle, DelayedInit will start running initializers from the
|
||||
* pending list. To prevent monopolizing the message loop, every idling period
|
||||
* has a maximum duration. When that's reached, we give up the message loop and
|
||||
* wait for the next idle.
|
||||
*
|
||||
* DelayedInit is compatible with lazy getters like those from XPCOMUtils. When
|
||||
* the lazy getter is first accessed, its corresponding initializer is run
|
||||
* automatically if it hasn't been run already. Each initializer also has a
|
||||
* maximum wait parameter that specifies a mandatory timeout; when the timeout
|
||||
* is reached, the initializer is forced to run.
|
||||
*
|
||||
* DelayedInit.schedule(() => Foo.init(), null, null, 5000);
|
||||
*
|
||||
* In the example above, Foo.init will run automatically when the message loop
|
||||
* becomes idle, or when 5000ms has elapsed, whichever comes first.
|
||||
*
|
||||
* DelayedInit.schedule(() => Foo.init(), this, "Foo", 5000);
|
||||
*
|
||||
* In the example above, Foo.init will run automatically when the message loop
|
||||
* becomes idle, when |this.Foo| is accessed, or when 5000ms has elapsed,
|
||||
* whichever comes first.
|
||||
*
|
||||
* It may be simpler to have a wrapper for DelayedInit.schedule. For example,
|
||||
*
|
||||
* function InitLater(fn, obj, name) {
|
||||
* return DelayedInit.schedule(fn, obj, name, 5000); // constant max wait
|
||||
* }
|
||||
* InitLater(() => Foo.init());
|
||||
* InitLater(() => Bar.init(), this, "Bar");
|
||||
*/
|
||||
let DelayedInit = {
|
||||
schedule: function (fn, object, name, maxWait) {
|
||||
return Impl.scheduleInit(fn, object, name, maxWait);
|
||||
},
|
||||
};
|
||||
|
||||
// Maximum duration for each idling period. Pending inits are run until this
|
||||
// duration is exceeded; then we wait for next idling period.
|
||||
const MAX_IDLE_RUN_MS = 50;
|
||||
|
||||
let Impl = {
|
||||
pendingInits: [],
|
||||
|
||||
onIdle: function () {
|
||||
let startTime = Cu.now();
|
||||
let time = startTime;
|
||||
let nextDue;
|
||||
|
||||
// Go through all the pending inits. Even if we don't run them,
|
||||
// we still need to find out when the next timeout should be.
|
||||
for (let init of this.pendingInits) {
|
||||
if (init.complete) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (time - startTime < MAX_IDLE_RUN_MS) {
|
||||
init.maybeInit();
|
||||
time = Cu.now();
|
||||
} else {
|
||||
// We ran out of time; find when the next closest due time is.
|
||||
nextDue = nextDue ? Math.min(nextDue, init.due) : init.due;
|
||||
}
|
||||
}
|
||||
|
||||
// Get rid of completed ones.
|
||||
this.pendingInits = this.pendingInits.filter((init) => !init.complete);
|
||||
|
||||
if (nextDue !== undefined) {
|
||||
// Schedule the next idle, if we still have pending inits.
|
||||
MessageLoop.postIdleTask(() => this.onIdle(),
|
||||
Math.max(0, nextDue - time));
|
||||
}
|
||||
},
|
||||
|
||||
addPendingInit: function (fn, wait) {
|
||||
let init = {
|
||||
fn: fn,
|
||||
due: Cu.now() + wait,
|
||||
complete: false,
|
||||
maybeInit: function () {
|
||||
if (this.complete) {
|
||||
return false;
|
||||
}
|
||||
this.complete = true;
|
||||
this.fn.call();
|
||||
this.fn = null;
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
if (!this.pendingInits.length) {
|
||||
// Schedule for the first idle.
|
||||
MessageLoop.postIdleTask(() => this.onIdle(), wait);
|
||||
}
|
||||
this.pendingInits.push(init);
|
||||
return init;
|
||||
},
|
||||
|
||||
scheduleInit: function (fn, object, name, wait) {
|
||||
let init = this.addPendingInit(fn, wait);
|
||||
|
||||
if (!object || !name) {
|
||||
// No lazy getter needed.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get any existing information about the property.
|
||||
let prop = Object.getOwnPropertyDescriptor(object, name) ||
|
||||
{ configurable: true, enumerable: true, writable: true };
|
||||
|
||||
if (!prop.configurable) {
|
||||
// Object.defineProperty won't work, so just perform init here.
|
||||
init.maybeInit();
|
||||
return;
|
||||
}
|
||||
|
||||
// Define proxy getter/setter that will call first initializer first,
|
||||
// before delegating the get/set to the original target.
|
||||
Object.defineProperty(object, name, {
|
||||
get: function proxy_getter() {
|
||||
init.maybeInit();
|
||||
|
||||
// If the initializer actually ran, it may have replaced our proxy
|
||||
// property with a real one, so we need to reload he property.
|
||||
let newProp = Object.getOwnPropertyDescriptor(object, name);
|
||||
if (newProp.get !== proxy_getter) {
|
||||
// Set prop if newProp doesn't refer to our proxy property.
|
||||
prop = newProp;
|
||||
} else {
|
||||
// Otherwise, reset to the original property.
|
||||
Object.defineProperty(object, name, prop);
|
||||
}
|
||||
|
||||
if (prop.get) {
|
||||
return prop.get.call(object);
|
||||
}
|
||||
return prop.value;
|
||||
},
|
||||
set: function (newVal) {
|
||||
init.maybeInit();
|
||||
|
||||
// Since our initializer already ran,
|
||||
// we can get rid of our proxy property.
|
||||
if (prop.get || prop.set) {
|
||||
Object.defineProperty(object, name, prop);
|
||||
return prop.set.call(object);
|
||||
}
|
||||
|
||||
prop.value = newVal;
|
||||
Object.defineProperty(object, name, prop);
|
||||
return newVal;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
}
|
||||
};
|
@ -9,6 +9,7 @@ EXTRA_JS_MODULES += [
|
||||
'AndroidLog.jsm',
|
||||
'ContactService.jsm',
|
||||
'dbg-browser-actors.js',
|
||||
'DelayedInit.jsm',
|
||||
'DownloadNotifications.jsm',
|
||||
'HelperApps.jsm',
|
||||
'Home.jsm',
|
||||
|
Loading…
Reference in New Issue
Block a user